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 AltConf 2015.

Avatar for Boris Bügling

Boris Bügling

June 10, 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) { print("\(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. !

  6. SWIFT 2.0 ./foo.swift:3:17: error: 'CFunctionPointer' is unavailable: use a function

    type '@convention(c) (T) -> U' typealias foo = CFunctionPointer<() -> ()>
  7. C FUNCTION POINTERS SWIFT 1.X CFunctionPointer<(UnsafeMutablePointer<Void>, Float) -> Int>.self SWIFT

    2.X typealias CFunction = @convention(c) (UnsafeMutablePointer<Void>, Float) -> Int CFunction.self
  8. Y DID WE ! THE OBJECTIVE-" RUNTIME? ▸ Dynamic Introspection

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

    = class_copyPropertyList(myClass, &propertyCount) for i in 0..<propertyCount { print("Property: " + String.fromCString(property_getName(properties[Int(i)]))!) }
  10. 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 { [...] }
  11. 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 }
  12. struct MyPoint { let x: Float let y: Float }

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

    myBlock : @convention(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) print(myString.description) // ✋
  14. SWROUTE ▸ PoC of function hooking in Swift ▸ Uses

    rd_route, a Mach specific injection library for C
  15. #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; };
  16. 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; }
  17. 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 [...]
  18. MEMORY LAYOUT ▸ 16 bytes => Swift object ▸ 8

    bytes => Pointer to _TF6memory3addFTSiSi_Si Function pointer !
  19. 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 }
  20. @asmname("floor") func my_floor(dbl: Double) -> Double print(my_floor(6.7)) let handle =

    dlopen(nil, RTLD_NOW) let pointer = COpaquePointer(dlsym(handle, "ceil")) typealias FunctionType = (Double) -> Double
  21. 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) print(my_ceil(6.7))
  22. void executeFunction(void(*f)(void)) { f(); } typealias CFunc = @convention(c) ()->()

    @asmname("executeFunction") func executeFunction(fp: CFunc)
  23. func greeting() { print("Hello from Swift") } typealias CFunc =

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

    from `f` import Foundation class MyClass { var someVar: Int = 0 func someFuncWithAReallyLongNameLol() -> () {} }
  25. $ 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
  26. performSelector? import Foundation import ObjectiveC.runtime @asmname("objc_msgSend") func sendPerformSelector(NSObject, Selector, Selector)

    -> NSString extension NSObject { public func performSelector2(selector : Selector) -> NSString { return sendPerformSelector(self, "performSelector:", selector) } } let string : NSString = "Fuck yeah, Swift!" println(string.performSelector2("description"))
  27. 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
  28. WHAT HAVE WE LEARNED? ▸ import ObjectiveC.runtime ☺ ▸ Introspection

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