Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift funtime

Swift funtime

A look at accomplishing the things we loved in the Objective-C runtime with Swift. The talk was given at Swift Summit. 🗻

Boris Bügling

March 22, 2015
Tweet

More Decks by Boris Bügling

Other Decks in Programming

Transcript

  1. !

  2. ▸ behaves like any old Objective-C object ▸ instance variables

    are properties ▸ fully interopable with ObjC
  3. ▸ has SwiftObject as superclass ▸ instance variables are ivars

    only ▸ ivars have no type encoding ▸ methods are not ObjC methods ▸ not interoperable with ObjC
  4. func info<T>(x: T) { println("\(x) is a \(_stdlib_getDemangledTypeName(x))") } let

    array = [0, 1, 2] // 'as AnyObject' => ! info(array) // is a Swift.Array import Foundation let objc_array: AnyObject = [0, 1, 2] as AnyObject info(objc_array) // is a Swift._NSSwiftArrayImpl // comparing different array types => compiler error as well //let equal = objc_array == array
  5. Y DID WE ! THE OBJECTIVE-" RUNTIME? ▸ Dynamic Introspection

    ! ▸ Change Behaviour "# ▸ Analyse private API $
  6. var propertyCount : UInt32 = 0 var properties : UnsafeMutablePointer<objc_property_t>

    = class_copyPropertyList(myClass, &propertyCount) for i in 0..<propertyCount { println("Property: " + String.fromCString(property_getName(properties[Int(i)]))!) }
  7. THERE IS HOPE // Excerpt from the standard library ///

    How children of this value should be presented in the IDE. enum MirrorDisposition { case Struct case Class case Enum [...] } /// A protocol that provides a reflection interface to an underlying value. protocol MirrorType { [...] }
  8. infix operator --> {} func --> (instance: Any, key: String)

    -> Any? { let mirror = reflect(instance) for index in 0 ..< mirror.count { let (childKey, childMirror) = mirror[index] if childKey == key { return childMirror.value } } return nil }
  9. struct MyPoint { let x: Float let y: Float }

    let point = MyPoint(x: 1, y: 2) println(point --> "x") // Optional(1.0) println(point --> "y") // Optional(2.0)
  10. let myString = "foobar" as NSString println(myString.description) // foobar let

    myBlock : @objc_block (AnyObject!) -> String = { (sself : AnyObject!) -> (String) in "✋" } let myIMP = imp_implementationWithBlock(unsafeBitCast(myBlock, AnyObject.self)) let method = class_getInstanceMethod(myString.dynamicType, "description") method_setImplementation(method, myIMP) println(myString.description) // ✋
  11. SWROUTE ▸ PoC of function hooking in Swift ▸ Uses

    rd_route, a Mach specific injection library for C
  12. #include <stdint.h> #define kObjectFieldOffset sizeof(uintptr_t) struct swift_func_object { uintptr_t *original_type_ptr;

    #if defined(__x86_64__) uintptr_t *unknown0; #else uintptr_t *unknown0, *unknown1; #endif uintptr_t function_address; uintptr_t *self; };
  13. uintptr_t _rd_get_func_impl(void *func) { struct swift_func_object *obj = (struct swift_func_object

    *) *(uintptr_t *)(func + kObjectFieldOffset); return obj->function_address; }
  14. MEMORY LAYOUT ▸ 8 bytes => Pointer to _TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_ ▸

    8 bytes => Pointer to struct _TPA__TTRXFo_dSidSi_dSi_XFo_iTSiSi__iSi_ ---> partial apply forwarder for reabstraction thunk helper [...]
  15. MEMORY LAYOUT ▸ 16 bytes => Swift object ▸ 8

    bytes => Pointer to _TF6memory3addFTSiSi_Si Function pointer !
  16. struct f_trampoline { var trampoline_ptr: COpaquePointer var function_obj_ptr: UnsafeMutablePointer<function_obj> }

    struct function_obj { var some_ptr_0: COpaquePointer var some_ptr_1: COpaquePointer var function_ptr: COpaquePointer }
  17. @asmname("floor") func my_floor(dbl: Double) -> Double println(my_floor(6.7)) let handle =

    dlopen(nil, RTLD_NOW) let pointer = COpaquePointer(dlsym(handle, "ceil")) typealias FunctionType = (Double) -> Double
  18. struct f_trampoline { [...] } struct function_obj { [...] }

    let orig = unsafeBitCast(my_floor, f_trampoline.self) let new = f_trampoline(prototype: orig, new_fp: pointer) let my_ceil = unsafeBitCast(new, FunctionType.self) println(my_ceil(6.7))
  19. func greeting() { println("Hello from Swift") } let t =

    unsafeBitCast(greeting, f_trampoline.self) let fp = CFunctionPointer<()->()> (t.function_obj_ptr.memory.function_ptr) executeFunction(fp) Hello from Swift Program ended with exit code: 0
  20. $ xcrun swiftc f.swift $ ./swift-dump.rb f // Code generated

    from `f` import Foundation class MyClass { var someVar: Int = 0 func someFuncWithAReallyLongNameLol() -> () {} }
  21. $ xcrun nm -g f|grep TFC 0000000100000c50 T __TFC1f7MyClass30someFuncWithAReallyLongNameLolfS0_FT_T_ 0000000100000d30

    T __TFC1f7MyClassCfMS0_FT_S0_ 0000000100000c70 T __TFC1f7MyClassD 0000000100000d10 T __TFC1f7MyClasscfMS0_FT_S0_ 0000000100000c60 T __TFC1f7MyClassd 0000000100000ca0 T __TFC1f7MyClassg7someVarSi 0000000100000ce0 T __TFC1f7MyClassm7someVarSi 0000000100000cc0 T __TFC1f7MyClasss7someVarSi
  22. HOW ARE EMOJI FORMED? $ echo 'class ! {}'|xcrun swiftc

    -emit-library -o test - $ nm -g test ... 0000000000000db0 T __TFC4testX4ypIhD ... $ xcrun swift-demangle __TFC4testX4ypIhD _TFC4testX4ypIhD ---> test.!.__deallocating_deinit X4 ypIh ~ xn--yp8h
  23. WHAT HAVE WE LEARNED? ▸ import ObjectiveC.runtime ☺ ▸ Introspection

    somewhat exists " ▸ Changing behaviour is hard # ▸ Reverse engineering is still fine $