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

Swift funtime

Swift funtime

An exploration of the new (?) runtime and how the Objective-C runtime API can be used in Swift. Talk given at mobiconf 2014.

Avatar for Boris Bügling

Boris Bügling

October 02, 2014
Tweet

More Decks by Boris Bügling

Other Decks in Technology

Transcript

  1. AGENDA ▸ How do you even Swift? ▸ What is

    a Swift object? ▸ Objective-C runtime in the age of Swift ▸ Swift runtime ▸ (some Q&A)
  2. ▸ Optionals ▸ Tuples ▸ Generics ▸ Pattern matching ▸

    Operator overloading ▸ Namespaces ▸ Type inference ▸ ...
  3. ▸ The Swift Programming Language by  AND ALSO ▸

    Swift by Tutorials: A Hands-On Approach ▸ Your First Swift App ▸ Functional Programming in Swift
  4. SWIFT COMMAND LINE TOOLS $ xcrun swift Welcome to Swift!

    Type :help for assistance. $ xcrun swiftc Foo.swift ## will compile an executable $ xcrun swift-stdlib-tool ## assembles libraries for an application bundle $ xcrun swift-demangle _TtCSs29_NativeDictionaryStorageOwner _TtCSs29_NativeDictionaryStorageOwner ---> Swift._NativeDictionaryStorageOwner
  5. ▸ behaves like any old Objective-C object ▸ instance variables

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

    ▸ ivars have no type encoding ▸ methods are not ObjC methods ▸ not interoperable with ObjC
  7. PLAYGROUND! import ObjectiveC.runtime but Playground execution failed: Error in auto-import:

    failed to get module 'runtime' from AST context (rdar://problem/18482380)
  8. !"

  9. class MySwiftClass { var foo = "bar"; init() { }

    } import Foundation import ObjectiveC.runtime var ivar = class_getInstanceVariable(MySwiftClass().dynamicType, "foo") var value : AnyObject = object_getIvar(MySwiftClass(), ivar)! Segmentation fault: 11
  10. #import <Foundation/Foundation.h> #import <objc/runtime.h> @interface MyClass : NSObject @property (nonatomic,

    retain) NSString* foo; @end #pragma mark - @implementation MyClass -(instancetype)init { self = [super init]; if (self) { self.foo = @"bar"; } return self; } @end #pragma mark - int main(int argc, char *argv[]) { @autoreleasepool { MyClass* object = [MyClass new]; Ivar ivar = class_getInstanceVariable(object.class, "_foo"); id value = object_getIvar(object, ivar); NSLog(@"%@", value); return 0; } }
  11. THERE IS HOPE /// How children of this value should

    be presented in the IDE. enum MirrorDisposition { case Struct case Class case Enum case Tuple [...] } /// A protocol that provides a reflection interface to an underlying value. protocol MirrorType { [...] /// Get the number of logical children this value has. var count: Int { get } subscript (i: Int) -> (String, MirrorType) { get } /// Get a string description of this value. var summary: String { get } [...] }
  12. // From: https://gist.github.com/peebsjs/9288f79322ed3119ece4 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 } //Example struct MyPoint { let x: Float let y: Float } let point = MyPoint(x: 1, y: 2) println(point --> "x") println(point --> "y")
  13. import Foundation import ObjectiveC.runtime extension NSString { func swizzle_description() ->

    NSString { return "!" } } var myString = "foobar" as NSString println(myString.description) var originalMethod = class_getInstanceMethod(NSString.self, "description") var swizzledMethod = class_getInstanceMethod(NSString.self, "swizzle_description") method_exchangeImplementations(originalMethod, swizzledMethod) println(myString.description)
  14. OR REPLACING METHODS import Foundation import ObjectiveC.runtime let myString =

    "foobar" as NSString println(myString.description) 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)
  15. CLASSES struct objc_class { Class isa; #if !__OBJC2__ Class super_class

    OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;
  16. OBJECTS ▸ struct magic ▸ contains refCount and isa ▸

    methods are in virtual table, like in C++ CLASSES ▸ have mangled names, which contain the module name
  17. NAME MANGLING another C++ concept _TFV4test1eCfMS0_FT_S0_ ---> test.e.init (test.e.Type)() ->

    test.e _TMLCCC4test1a1b1c ---> lazy cache variable for type metadata for test.a.b.c _TMmCCC4test1a1b1c ---> metaclass for test.a.b.c _TMnCC4test1a1b ---> nominal type descriptor for test.a.b _TTWOV4test1e1fSs9EquatableFS2_oi2eeUS2___fMQPS2_FTS3_S3__Sb ---> protocol witness for Swift. Equatable.== infix <A : Swift.Equatable>(Swift.Equatable.Self.Type) (Swift.Equatable.Self, Swift.Equatable.Self) -> Swift.Bool in conformance test.e.f : Swift.Equatable _TWoFC4test1aCfMS0_FT_S0_ ---> witness table offset for test.a.__allocating_init (test.a.Type)() -> test.a _TWoFCCC4test1a1b1c1dfS2_FT1zS0_1xS1_1vFT1xSi_Si_OVS_1e1f ---> witness table offset for test.a.b.c.d (test.a.b.c)(z : test.a, x : test.a.b, v : (x : Swift.Int) -> Swift.Int) -> test.e.f
  18. 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
  19. SWROUTE ▸ PoC of function hooking in Swift ▸ Uses

    rd_route, a Mach specific injection library for C
  20. #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; }; 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; }
  21. Foo.app boris$ find . -type f ./Frameworks/libswiftCore.dylib ./Frameworks/libswiftCoreGraphics.dylib ./Frameworks/libswiftCoreImage.dylib ./Frameworks/libswiftDarwin.dylib

    ./Frameworks/libswiftDispatch.dylib ./Frameworks/libswiftFoundation.dylib ./Frameworks/libswiftObjectiveC.dylib ./Frameworks/libswiftUIKit.dylib ./Info.plist ./PkgInfo ./Foo
  22. SPEED ▸ less dynamic dispatch ▸ omits _cmd - freeing

    one register ▸ usually no pointer aliasing int *ptrA = malloc(100 * sizeof(*ptrA)); int *ptrB = ptrA;
  23. class BankAccount { var balance: Double = 0.0 func deposit(amount:

    Double) { balance += amount } } let account = BankAccount() account.deposit(100) let depositor = BankAccount.deposit depositor(account)(100) BankAccount.deposit(account)(100)