Repository: NSExceptional/Runtime Branch: master Commit: 1bb33a438f3b Files: 23 Total size: 62.5 KB Directory structure: gitextract_savhqszy/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── README.md ├── Runtime/ │ ├── Class.swift │ ├── ClassBuilder.swift │ ├── DebugDescriptions.swift │ ├── Ivar.swift │ ├── Method.swift │ ├── Object.swift │ ├── OrderedSet.swift │ ├── Platform.swift │ ├── Pointer.swift │ ├── Property.swift │ ├── Protocol.swift │ ├── RootObject.swift │ ├── Runtime.swift │ ├── Struct.swift │ └── Types.swift ├── Runtime.xcodeproj/ │ ├── project.pbxproj │ └── project.xcworkspace/ │ └── contents.xcworkspacedata └── RuntimeTests/ ├── Info.plist ├── Person.swift └── RuntimeTests.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [NSExceptional] ================================================ FILE: .gitignore ================================================ # Xcode .DS_Store build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 !default.xcworkspace xcuserdata profile *.moved-aside DerivedData .idea/ *.xcbkptlist *.xccheckout *.hmap *.ipa *.swp *.lock *.xcuserstate Luna.xcworkspace/xcuserdata/tantan.xcuserdatad/UserInterfaceState.xcuserstate .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ================================================ FILE: README.md ================================================ # Runtime An Objective-C simulator written in Swift. ## Goals With few exceptions, this project aims to simulate, in Swift, how Objective-C works under the hood (i.e. calls to `objc_msgSend`, inserted ARC functions, literal class-refs in class method calls, etc), as opposed to mirroring Objective-C style code and dynamism which Swift can accomplish already via `@objc` classes. This project could theoretically be used as a dynamic runtime backend for a transpiled progamming language, and as such, this framework and its conventions were crafted with this idea in mind. Many of the constructs used here may seem to lack type-safety, but everything is perfectly safe if the code is generated by some other, more type-safe language. In short, this code is not meant to be written by hand if used for anything serious. ## Features - Dynamic method dispatch - Method swizzling / replacing - Creating entire classes at runtime - Non-fragile ivars See `Person.Swift` for an examples of everything mentioned in the readme. ## Overview Runtime metadata types provided by this framework mirrors that of the public Objective-C runtime interface as closely as possible, declaring types such as `Class`, `Ivar`, `Method`, etc, all of which provide about as much information as their Objective-C counterparts. ### Defining classes A base class, `RootObject`, is provided for other classes to inherit from if they wish. New classes are defined by declaring a `struct` type to enclose the `Class` object in, with the class object itself being declared as a `static let`, followed by method variables. ```swift struct Person { static let `class` = Class( isa: Person_meta.class, superclass: RootObject.class, name: "Person", ivars: [ (name: "_name", type: .string), (name: "_age", type: .integer) ], methods: [_init, name, setName_, age, setAge_, description], properties: [ Property(name: "name", getter: name, setter: setName_), Property(name: "age", getter: age, setter: setAge_), ], protocols: [] ) // Methods go here as static vars static var _init = Method("init", returns: .object("this")) { this, _cmd, args in func init$(_ this: id, _ _cmd: SEL) -> id { _msgSend(this, "setName_", ("Bob")) _msgSend(this, "setAge_", (18)) return msgSend(super: true, this, _cmd) } return init$(this, _cmd) } static var name = Method("name", returns: .string) ... ... } private struct Person_meta { static let `class` = Class( isa: nil, superclass: nil, name: "Person.meta", ... ) } ``` It is good practice to declare a struct for the class itself and another for the metaclass, as above, to reduce ambiguity between class members and instance members (methods, properties, etc). The metaclass stores class members. `isa:` should be the class's metaclass (or `nil` if the class is a metaclass itself). `superclass:` should be the superclass. #### The Metaclass Metaclasses inherit from the super-metaclass, not the superclass. It is convention to declare the compile-time variable like `MyClass_meta` and name it `"MyClass.meta"`. So, `Person` inherits from `Object.class`, and `Person_meta` inherits from `Object_meta.class`. Each metaclass can be looked up by using `Class.named("Foo").isa` or directly by name with `Class.named("Foo.meta")`. #### Methods ###### Declaration `Method`s should be defined as `static var/let` as well (as opposed to right inside the `methods:` argument to the `.class` initializer as I have done with `properties:`), in case you need to reference the method as an argument to a `Property` at compile-time. Declaring them inline also makes the initializer very hard to parse visually since method declarations are typically no less than 7 or 8 lines. ###### Method.init() structure The `Method` initializer takes the name of the method, the return and argument types (`Type`) an implementation (`IMP`). The return and argument types default to `.void` and `[]`. For initializers, it is convention to return `.object("self")` where you would use `instancetype` in Objective-C. You could use `.object("anything you want")`, but I find that `"self"` makes the most sense here. In cases where you return another object of a fixed type, use `.object("ClassName")`. This runtime aims to provide as much metadata for method type signatures as Objective-C does for property type signatures. ###### IMP arguments Like Objective-C, all methods take two fixed arguments: `this` in place of `self`, and `_cmd`. However, due to limitations in the Swift type system, all method `IMP`s must return the same thing, `Any`, and without using assembly, they must all take `Any` as the variable arguments, even if a method takes no other arguments. An `IMP` is invoked by passing `this`, `_cmd`, and `args` where `args` is a tuple of the non-fixed arguments to the method. ###### Implementation conventions To counteract the lack of type safety and enhance readability, I find it helpful to declare a function within the scope of the method `IMP` named with a traling `$` to represent the actual type signature of the method (and to hold the non-trivial implementation), like so: ```swift static var add__ = Method(…) { this, _cmd, args in // Actual implementation and type signature of method func add__$(_ this: id, _ _cmd: SEL, a: Int, b: Int) -> Int { return a + b } // Cast out arguments and call method let args = args as! (Int, Int) return add__$(this, _cmd, args.0, args.1) } ``` Arguments must be cast from `Any` to their actual types as a tuple before being used. ###### Overriding methods To override a method, simply give your subclass another method with the same name as the method you wish to override. If you need to call the `super` implementation, simply pass `super: true` to your call to `msgSend`: ```swift static var _init = Method("init", ...) { this, _cmd, args in func init$(_ this: id, _ _cmd: SEL) -> id { return msgSend(super: true, this, _cmd) print("init override: \(this)") } return init$(this, _cmd) } ``` ###### Init If you're familiar with Swift, you may know that Swift doesn't allow you to use `self` before all ivars have been initialized. With some exceptions, the same is true here. That said, all ivars are initialized to `0` or `nil`, so it is not necessary to initialize primitive integral types to `nil` or `0`. > Technically, if a class has no stored complex Swift structures in it (such as `String`), it should be safe to use prior to ivar initialization. I plan to make a wrapper for `String` and `Array`, etc, to counteract these edge cases. #### Instance variables Ivars are passed to the `Class` initializer as a tuple of their name and type. Their offset is detremined at runtime, and as a result, classes do not have fragile ivars. > Metaclasses can not have any instance variables; trying to use ivars on a metaclass is undefined behavior. #### Properties Properties take a name and one or two implementations. A property's `type` comes from its `getter`. -- ### Creating objects Instances of objects are allocated by calling `class.createInstance()`, i.e.: ```swift let instance1 = Person.class.createInstance() let instance2 = Class.named("Person").createInstance() ``` ### Calling methods Like Objective-C, this runtime uses dynamic dispatch via the `msgSend` and `_msgSend` functions. `_msgSend` only exists as a shortcut for void-returning methods, or cases where you want to discard the return value. ```swift let bob: id = msgSend(Person.class.createInstance(), "init") let name: String = msgSend(bob, "name") let age: Int = msgSend(bob, "age") let description: String = msgSend(bob, "description") ``` ### Accessing ivars Ivar access works similarly to how it works in Objective-C. You must retrieve the offset from the runtime and add it to `this` to access the ivar. A lot of casting is involved, and I've provided some operators to ease the pain: ```swift let offset = this|.getClass.getIvarOffset("_someInt")! let pointer: Pointer = ~pointer + offset let ivarValue = pointer.pointee ``` `this|` is shorthand for `this.pointee`. `~pointer` is shorthand for `unsafeBitCast(pointer, to: T.self)`. Note that the runtime uses its own `Pointer` type, which allows `+` to offset it by bytes at at time. The above is still pretty convoluted and heavily repeated, so I've provided yet another operator which returns `ivarValue` above: ```swift let ivarValue: Int = this|"_someInt" ``` In general, `|` provides some form of dereferencing an object pointer. Here is another operator which can be used to set an ivar `_foo` to `5`: ```swift this |= (5, "_foo") ``` -- ### Type system "gotchas" #### You're stuck with `id` Since new classes are weakly defined as runtime metadata and not as concrete types in Swift code, you cannot declare a `Pointer` to a custom type directly. That is, all object references are typed as `Pointer` aka `id`, as defined by `Object.swift` (not to be confused with `RootObject`, which is akin to `NSObject`). If you really want to declare a `Pointer` for example, you could declare members on your `Vehicle ` struct like so, alongside the `static let class` declaration: ```swift struct Vehicle { let _super: Object let _capacity: Int ... static let `class` = Class(isa: ...) } /// Vehicle subclass struct Car { let _super: Vehicle let make: String let model: String let year: Int ... static let `class` = Class(isa: ...) } ``` Now, you could possibly do the following: ```swift let fiesta: Pointer = msgSend( Car.class.createInstance(), "init", ("Ford", "Fiesta", 2014, ...) ) fiesta.year = 2017 ``` Be sure to continue to declare all ivars and methods inside the `Class` variable. Statically declaring the layout like this is only useful for extra type-safety and direct ivar access if you wish to bypass non-fragile ivar lookup. #### Using `Class`es as objects `Class` instances could only be made possible by making `Class` a Swift `class` and not a `struct`, due to limitations in Swift's type system and several abstractions Swift imposes on the user. Therefore, they do not have the same underlying structure as `Object` does (that is, `Class` does not start with the `isa` defined by the `Object` declaration). To call a class method on a class, pass `.ref` as `this`: ```swift _msgSend(Person.class.ref, "someClassMethod") ``` In general, use `class.ref` whenever you wish to treat a `Class` as an object. ### Other caveats `Class` objects will not be available via `Class.named(_:)` until they have been accessed statically. You should "load" these classes manually by accessing all classes you define, like so: ```swift func runtimeInit() { // Runtime initialization _ = RootObject.class _ = Person.class ... } ``` Ideally this shouldn't be necessary, or should be easier. Please submit a pull request if you have suggestions on how to make this easier or unnecessary! --- ## To-do - More tests - Zeroing deallocated references - Suggestions welcome! ================================================ FILE: Runtime/Class.swift ================================================ // // Class.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation public class Class { public let isa: Class! // metaclass, nil for metaclasses let magic: UInt = 0xAAAABBBBCCCCDDDD // for debugging purposes, will be removed public let superclass: Class! // nil if no superclass public let name: String public let instanceSize: Int /// Instance class property-backing variables let ivars: [Ivar] /// Instance or class methods let methods: [SEL: Method] /// Instance or class properties let properties: [Property] /// Protocols conformed to by instances; not applicable to classes let protocols: [Protocol] private static let __swobjSize = 16 /// Used to refer to the class as an object. /// Temporary workaround since class objects themselves /// already have an `isa` provided by Swift. public var ref: id { var ptr: id = ~self ptr += Class.__swobjSize return ptr } public init(isa: Class!, superclass: Class?, name: String, ivars stubs: [Ivar.Stub] = [], methods: [Method] = [], properties: [Property] = [], protocols: [Protocol] = [], extraBytes: Int = 0) { let offset = superclass?.instanceSize ?? Type.pointer(.class("")).size self.isa = isa self.superclass = superclass self.name = name self.instanceSize = offset + stubs.map { return $0.type.size }.reduce(0, +) + extraBytes self.ivars = Ivar.make(from: stubs, offset) self.properties = properties self.protocols = protocols self.methods = { var dict: [SEL: Method] = [:] for method in methods { dict[method.name] = method } return dict }() Class.classList.append(self) } public func createInstance() -> id { let instance = Pointer.calloc(self.instanceSize) instance.pointee.isa = self return instance } public func method(named: String) -> Method! { return self.methods[named] } public func ivar(named name: String) -> Ivar! { return self.ivars.filter { $0.name == name }.first } public func property(named name: String) -> Property! { return self.properties.filter { $0.name == name }.first } public func conforms(to protocol: String) -> Bool { return !self.protocols.filter { $0.name == name }.isEmpty } public func getIvarOffset(_ name: String) -> Int! { return self.ivar(named: name)?.offset } public func getMethodIMP(_ sel: SEL) -> IMP? { return self.method(named: sel)?.imp } public static func isClass(_ object: id) -> Bool { if object|.isa == nil { // Metaclass.isa -> nil return true } else if object|.isa.isa == nil { // Class.isa -> metaclass.isa -> nil return true } else { // Object.isa -> Class.isa -> metaclass.isa -> nil return false } } } public extension Class { static var classList: [Class] = [] public static func named(_ name: String) -> Class! { return Class.classList.filter { $0.name == name }.first } } ================================================ FILE: Runtime/ClassBuilder.swift ================================================ // // ClassBuilder.swift // Runtime // // Created by Tanner on 10/21/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation public class ClassBuilder { public var superclass: Class? public var name: String public var extraBytes: Int private var final = false private var methods: OrderedSet = [] private var classMethods: OrderedSet = [] private var ivars: OrderedSet = [] private var properties: OrderedSet = [] private var classProperties: OrderedSet = [] private var protocols: OrderedSet = [] /// - Warning: returns nil if a class with the given name already exists. public init?(name: String, superclass: Class? = RootObject.class, extraBytes: Int = 0) { if Class.named(name) != nil { return nil } self.superclass = superclass self.name = name self.extraBytes = extraBytes } /// Actually creates the class and adds it to the runtime. /// /// The class you are constructing cannot be used until it is finalized. /// - Returns: The newly created class. public func finalize() -> Class { let metaclass = Class( isa: nil, superclass: self.superclass?.isa, name: self.name + ".meta", ivars: [], methods: self.classMethods.array, properties: self.classProperties.array, protocols: [] ) let cls = Class( isa: metaclass, superclass: self.superclass, name: self.name, ivars: self.ivars.array.map({ $0.stub }), methods: self.methods.array, properties: self.properties.array, protocols: self.protocols.array, extraBytes: self.extraBytes ) self.final = true Class.classList.append(cls) return cls } public func add(_ methods: [Method], toClass: Bool = false) { if toClass { self.classMethods.addAll(methods) } else { self.methods.addAll(methods) } } public func add(_ ivars: [IvarStub]) { self.ivars.addAll(ivars) } public func add(_ properties: [Property], toClass: Bool = false) { if toClass { self.classProperties.addAll(properties) } else { self.properties.addAll(properties) } } public func add(_ protocols: [Protocol]) { self.protocols .addAll(protocols) } public struct IvarStub: Hashable { let name: String let type: Type fileprivate var stub: Ivar.Stub { return (name, type) } public var hashValue: Int { return self.name.hashValue } public static func ==(lhs: IvarStub, rhs: IvarStub) -> Bool { return lhs.name == rhs.name } } } ================================================ FILE: Runtime/DebugDescriptions.swift ================================================ // // DebugDescriptions.swift // Runtime // // Created by Tanner on 10/21/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation extension Class: CustomDebugStringConvertible, CustomStringConvertible, CustomPlaygroundQuickLookable { public var customPlaygroundQuickLook: PlaygroundQuickLook { return .text(self.name) } public var description: String { return self.name } public var debugDescription: String { var me = self return """ { isa: \(self.isa?.name ?? "nil"), superclass: \(self.superclass?.name ?? "nil"), methods: \(self.methods.count), properties: \(self.properties.count), protocols: \(self.protocols.count), } """ } } ================================================ FILE: Runtime/Ivar.swift ================================================ // // Ivar.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation public struct Ivar: Hashable { public typealias Stub = (name: String, type: Type) public let name: String public let type: Type public let offset: Int public var hashValue: Int { return self.name.hashValue } public static func ==(lhs: Ivar, rhs: Ivar) -> Bool { return lhs.name == rhs.name } } extension Ivar { static func make(from stubs: [Stub], _ offset: Int) -> [Ivar] { var offsett = offset return stubs.map { stub -> Ivar in defer { offsett += stub.type.size } return Ivar(name: stub.name, type: stub.type, offset: offsett) } } } ================================================ FILE: Runtime/Method.swift ================================================ // // Method.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation public class Method: Hashable { public typealias Signauture = (returnType: Type, argumentTypes: [Type]) public let name: String public var imp: IMP public let returnType: Type public let argumentTypes: [Type] public var signature: Signauture { return (self.returnType, self.argumentTypes) } public init(_ name: String, returns: Type = .void, args: [Type] = [], _ imp: @escaping IMP) { self.name = name self.returnType = returns self.argumentTypes = args self.imp = imp } convenience public init(name: String, signature: Signauture = (.void, []), _ imp: @escaping IMP) { self.init(name, returns: signature.returnType, args: signature.argumentTypes, imp) } public var hashValue: Int { return self.name.hashValue } public static func ==(lhs: Method, rhs: Method) -> Bool { return lhs.name == rhs.name } } ================================================ FILE: Runtime/Object.swift ================================================ // // Object.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation public struct Object { var isa: Class! public var getClass: Class { return self.isa } public func respondsTo(_ selector: SEL) -> Bool { return self.isa.getMethodIMP(selector) != nil } } extension Pointer: CustomStringConvertible, CustomDebugStringConvertible { public var debugDescription: String { return self.description } public var description: String { if Pointee.self is Object.Type { let this: id = ~self if this|.respondsTo("description") { return msgSend(this, "description") } else { return "<\(this|.getClass) \(self.raw.debugDescription)>" } } return self.raw.debugDescription } } ================================================ FILE: Runtime/OrderedSet.swift ================================================ // // OrderedSet.swift // Runtime // // Created by Tanner on 10/23/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation class OrderedSet: ExpressibleByArrayLiteral { var array: [T] = [] var set: Set = [] init() { } /// - Returns: true if the element was not already in the set, false otherwise @discardableResult func add(_ element: T) -> Bool { if self.set.insert(element).inserted { array.append(element) return true } return false } /// - Returns: true if all elements were not already in the set, false otherwise @discardableResult func addAll(_ elements: [T]) -> Bool { var allIn = true for e in elements { if !self.add(e) { allIn = false } } return allIn } required init(arrayLiteral elements: T...) { self.addAll(elements) } } ================================================ FILE: Runtime/Platform.swift ================================================ // // Platform.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation struct Platform { static let is64Bit = MemoryLayout.size == MemoryLayout.size } ================================================ FILE: Runtime/Pointer.swift ================================================ // // Pointer.swift // MirrorKit.swift // // Created by Tanner on 8/25/17. // import Foundation extension UnsafeMutableRawPointer { @inline(__always) init(to thing: inout T) { self = withUnsafeMutablePointer(to: &thing) { UnsafeMutableRawPointer($0) } } } prefix operator ~ public prefix func ~(thing: T) -> U { return unsafeBitCast(thing, to: U.self) } infix operator |= /// Shorthand ivar set public func |=(pointer: id, change: (value: T, ivar: String)) { let offset = pointer|.getClass.getIvarOffset(change.ivar)! let pointer: Pointer = ~pointer + offset pointer.pointee = change.value } infix operator | /// Shorthand ivar get public func |(pointer: id, ivar: String) -> T { let offset = pointer|.getClass.getIvarOffset(ivar)! let ivarPtr: Pointer = ~(pointer + offset) return ivarPtr.pointee } postfix operator | /// Shorthand for .pointee public postfix func |(pointer: Pointer) -> T { return pointer.pointee } prefix operator | /// Shorthand for .pointee @inline(__always) public prefix func |(pointee: inout T) -> Pointer { return Pointer(to: &pointee) } public struct Pointer: Strideable, Hashable, Equatable { // MARK: Public public let raw: UnsafeMutableRawPointer public var pointee: Pointee { get { return self.read() } nonmutating set { return self.write(newValue) } } // Get a Pointer to some variable public init(to thing: inout T) { self.raw = UnsafeMutableRawPointer(to: &thing) } // Convert some variable, which is already a "pointer" itself, into a Pointer public init(from pointer: inout Any) { self.raw = ~pointer } public func read(byteOffset: Int = 0) -> T { return self.raw.load(fromByteOffset: byteOffset, as: T.self) } public func write(_ value: T, byteOffset: Int = 0) { self.raw.storeBytes(of: value, toByteOffset: byteOffset, as: T.self) } // MARK: Memory management public static func alloc(_ count: Int) -> Pointer { return Pointer(raw: UnsafeMutablePointer.allocate(capacity: count)) } public static func calloc(_ count: Int) -> Pointer { #if os(Linux) return Pointer(raw: Glibc.calloc(count, 1)) #else return Pointer(raw: Darwin.calloc(count, 1)) #endif } public func free() { #if os(Linux) Glibc.free(self.raw) #else Darwin.free(self.raw) #endif } public func free(_ count: Int) { self.raw.deallocate(bytes: count, alignedTo: MemoryLayout.alignment) } // MARK: Private init(raw pointer: UnsafeMutableRawPointer) { self.raw = pointer } // MARK: Strideable public func distance(to other: Pointer) -> Int { return self.raw.distance(to: other.raw) } public func advanced(by n: Int) -> Pointer { return Pointer(raw: self.raw.advanced(by: n)) } // MARK: Hashable public var hashValue: Int { return self.raw.hashValue } // MARK: Convenience public static func +(lhs: Pointer, rhs: Int) -> Pointer { return lhs.advanced(by: rhs) } public static func -(lhs: Pointer, rhs: Int) -> Pointer { return lhs.advanced(by: -rhs) } public static func +=(lhs: inout Pointer, rhs: Int) { lhs = lhs.advanced(by: rhs) } public static func -=(lhs: inout Pointer, rhs: Int) { lhs = lhs.advanced(by: -rhs) } public static postfix func ++(pointer: inout Pointer) { pointer = pointer.advanced(by: 1) } public static postfix func --(pointer: inout Pointer) { pointer = pointer.advanced(by: -1) } } ================================================ FILE: Runtime/Property.swift ================================================ // // Property.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation public struct Property: Hashable, Equatable { public let name: String public let getter: Method public let setter: Method! public var type: Type { return self.getter.returnType } public var hashValue: Int { return self.name.hashValue } public static func ==(lhs: Property, rhs: Property) -> Bool { return lhs.name == rhs.name } } ================================================ FILE: Runtime/Protocol.swift ================================================ // // Protocol.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation public class Protocol: Hashable { public let name: String public let methods: [Method] public init(name: String, methods: [Method]) { self.name = name self.methods = methods } public var hashValue: Int { return self.name.hashValue } public static func ==(lhs: Protocol, rhs: Protocol) -> Bool { return lhs.name == rhs.name } } public extension Protocol { static var protocolList: [Protocol] = [] public static func named(_ name: String) -> Protocol! { return Protocol.protocolList.filter { $0.name == name }.first } } ================================================ FILE: Runtime/RootObject.swift ================================================ // // RootObject.swift // Runtime // // Created by Tanner on 10/19/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation /// New classes are defined as static instances within an extension on Class itself. /// Methods, ivars, properties, etc for these new classes are defined as below. public struct RootObject { static let `class` = Class( isa: RootObject_meta.class, superclass: nil, name: "Object", ivars: [("isa", .pointer(.class("self"))), ("_retainCount", .integer)], methods: [_init, retain, retainCount] ) static var _init = Method("init", returns: .object("self")) { this, _cmd, args in func init$(_ this: id, _ _cmd: SEL) -> id { print("\(this|.getClass).init(): \(this)") return this } return init$(this, _cmd) } static var retain = Method("retain", returns: .object("self")) { this, _cmd, args in func retain$(_ this: id, _ _cmd: SEL) -> id { let newCount: Int = (this|"_retainCount") + 1 this |= (newCount, "_retainCount") return this } return retain$(this, _cmd) } static var release = Method("release") { this, _cmd, args in func release$(_ this: id, _ _cmd: SEL) { let newCount: Int = (this|"_retainCount") - 1 this |= (newCount, "_retainCount") if newCount < 1 { if newCount < 0 { fatalError("Over-released object at \(this.raw.debugDescription)") } this.free() } } release$(this, _cmd) return () } static var retainCount = Method("retainCount", returns: .integer) { this, _cmd, args in func retainCount$(_ this: id, _ _cmd: SEL) { return this|"_retainCount" } retainCount$(this, _cmd) return () } } public struct RootObject_meta { static let `class` = Class( isa: nil, superclass: nil, name: "Object.meta" ) } ================================================ FILE: Runtime/Runtime.swift ================================================ // // Runtime.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation public typealias id = Pointer public typealias SEL = String public typealias IMP = (_ self: id, _ _cmd: SEL, _ args: Any) -> Any @inline(__always) func prepareMsg(_ target: id, _ _cmd: SEL, super: Bool) -> IMP { let isClass = Class.isClass(target) var cls: Class! if isClass { cls = `super` ? target|.isa.superclass! : target|.isa } else { cls = `super` ? target|.getClass.superclass! : target|.getClass } var imp: IMP! repeat { imp = cls.getMethodIMP(_cmd) cls = cls.superclass } while imp == nil && cls != nil guard imp != nil else { let isClass = Class.isClass(target) let invocation = (isClass ? "+" : "-") + "[\(target|.getClass.name) \(_cmd)]" fatalError("Unrecognized selector sent to instance \(target.raw): " + invocation) } return imp } /// Dynamically calls a method on a `Foo` instance given a method name (like objc_msgSend) public func msgSend(super: Bool = false, _ target: id, _ _cmd: SEL, _ args: Any = ()) -> T { let imp = prepareMsg(target, _cmd, super: `super`) return imp(target, _cmd, args) as! T } /// Convenience for `Void` or discardable results public func _msgSend(super: Bool = false, _ target: id, _ _cmd: SEL, _ args: Any = ()) { let imp = prepareMsg(target, _cmd, super: `super`) _ = imp(target, _cmd, args) } ================================================ FILE: Runtime/Struct.swift ================================================ // // Struct.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation /// As of now, purely for metadata purposes. /// Provides no way to create and use structs. /// It may make more sense just to use standard Swift structs /// alongside instances of `Struct` for reflection purposes. public class Struct { let name: String let instanceSize: Int let ivars: [Ivar] let methods: [Method] let properties: [Property] public init(name: String, instanceSize: Int, ivars: [Ivar.Stub] = [], methods: [Method] = [], properties: [Property] = []) { self.name = name self.instanceSize = instanceSize self.ivars = Ivar.make(from: ivars, 0) self.methods = methods self.properties = properties } } public extension Struct { static var structList: [Struct] = [] public static func named(_ name: String) -> Struct! { return Struct.structList.filter { $0.name == name }.first } } public extension Struct { public var describedAsTuple: String { return "(" + self.ivars.map({ ivar in if !ivar.name.isEmpty { return ivar.name + ": " + ivar.type.description } else { return ivar.type.description } }).joined(separator: ", ") } public var typeEncoding: String { return "" } } ================================================ FILE: Runtime/Types.swift ================================================ // // Types.swift // Runtime // // Created by Tanner on 10/18/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import Foundation public indirect enum Type { case void case pointer(Type) case integer case float case bool case string case tuple(String) case optional(Type) case object(String) case `struct`(String) case `class`(String) public var description: String { switch self { case .void: return "Void" case .pointer(let type): return "Pointer<\(type)>" case .integer: return "Integer" case .float: return "Float" case .bool: return "Bool" case .string: return "String" case .tuple(let structName): return structName // TDOO: struct.describedAsTuple case .optional(let type): return type.description + "?" case .object(let name): return name case .struct(let name): return name case .class(let name): return name } } public var encoding: String { switch self { case .void: return "v" case .pointer(let type): return "^\(type.encoding)" case .integer: return Platform.is64Bit ? "q" : "i" case .float: return Platform.is64Bit ? "d" : "f" case .bool: return "C" case .string: return "{11_StringCore}" case .tuple(let structName): return Type.struct(structName).encoding case .optional(let type): return "?\(type.description.count)\(type.description)" case .object(_): return "@" case .struct(let name): return "{\(name.count)" + name + "}" case .class(_): return "#" } } public var size: Int { switch self { case .void: return 0 case .pointer(_): fallthrough case .integer: return MemoryLayout.size case .float: return Platform.is64Bit ? MemoryLayout.size : MemoryLayout.size case .bool: return MemoryLayout.size case .string: return MemoryLayout.size case .tuple(let structName): return Type.struct(structName).size case .optional(let type): switch type { case .pointer(let ptrType): return ptrType.size default: return type.size + 1 } case .object(let className): return Class.named(className).instanceSize case .struct(let name): return Struct.named(name).instanceSize case .class(_): return 0 // You'll never need to know the size of a class in practice } } } ================================================ FILE: Runtime.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 48; objects = { /* Begin PBXBuildFile section */ C35AEBBE1F987493008F2988 /* RuntimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C35AEBBD1F987493008F2988 /* RuntimeTests.swift */; }; C35AEBC01F987493008F2988 /* libRuntime.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C3F59BC81F981B41002494E4 /* libRuntime.a */; }; C37F4B741F9B279E000732E0 /* DebugDescriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C37F4B731F9B279E000732E0 /* DebugDescriptions.swift */; }; C3BEFCC11F994C7F005AD638 /* Person.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BEFCC01F994C7F005AD638 /* Person.swift */; }; C3BEFCC31F994CB9005AD638 /* RootObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BEFCC21F994CB9005AD638 /* RootObject.swift */; }; C3D55DF81F9C4535006DE12F /* ClassBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D55DF71F9C4535006DE12F /* ClassBuilder.swift */; }; C3D55DFA1F9E4642006DE12F /* OrderedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D55DF91F9E4642006DE12F /* OrderedSet.swift */; }; C3F59BD51F981B8D002494E4 /* Class.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BD41F981B8D002494E4 /* Class.swift */; }; C3F59BD91F981C20002494E4 /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BD81F981C20002494E4 /* Pointer.swift */; }; C3F59BDB1F981C4D002494E4 /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BDA1F981C4D002494E4 /* Object.swift */; }; C3F59BDD1F981D8D002494E4 /* Ivar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BDC1F981D8D002494E4 /* Ivar.swift */; }; C3F59BDF1F981D93002494E4 /* Method.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BDE1F981D93002494E4 /* Method.swift */; }; C3F59BE11F981D9C002494E4 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE01F981D9C002494E4 /* Property.swift */; }; C3F59BE31F981DAD002494E4 /* Protocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE21F981DAD002494E4 /* Protocol.swift */; }; C3F59BE51F981E6F002494E4 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE41F981E6F002494E4 /* Types.swift */; }; C3F59BE71F98229C002494E4 /* Struct.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE61F98229C002494E4 /* Struct.swift */; }; C3F59BE91F9827BD002494E4 /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE81F9827BD002494E4 /* Platform.swift */; }; C3F59BEB1F982B80002494E4 /* Runtime.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BEA1F982B80002494E4 /* Runtime.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ C35AEBC11F987493008F2988 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = C3F59BC01F981B41002494E4 /* Project object */; proxyType = 1; remoteGlobalIDString = C3F59BC71F981B41002494E4; remoteInfo = Runtime; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ C3F59BC61F981B41002494E4 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "include/$(PRODUCT_NAME)"; dstSubfolderSpec = 16; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ C35AEBBB1F987493008F2988 /* RuntimeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RuntimeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C35AEBBD1F987493008F2988 /* RuntimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeTests.swift; sourceTree = ""; }; C35AEBBF1F987493008F2988 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C37F4B731F9B279E000732E0 /* DebugDescriptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugDescriptions.swift; sourceTree = ""; }; C3BEFCC01F994C7F005AD638 /* Person.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Person.swift; sourceTree = ""; }; C3BEFCC21F994CB9005AD638 /* RootObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootObject.swift; sourceTree = ""; }; C3D55DF71F9C4535006DE12F /* ClassBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassBuilder.swift; sourceTree = ""; }; C3D55DF91F9E4642006DE12F /* OrderedSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedSet.swift; sourceTree = ""; }; C3F59BC81F981B41002494E4 /* libRuntime.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRuntime.a; sourceTree = BUILT_PRODUCTS_DIR; }; C3F59BD41F981B8D002494E4 /* Class.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Class.swift; sourceTree = ""; }; C3F59BD81F981C20002494E4 /* Pointer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pointer.swift; sourceTree = ""; }; C3F59BDA1F981C4D002494E4 /* Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Object.swift; sourceTree = ""; }; C3F59BDC1F981D8D002494E4 /* Ivar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ivar.swift; sourceTree = ""; }; C3F59BDE1F981D93002494E4 /* Method.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Method.swift; sourceTree = ""; }; C3F59BE01F981D9C002494E4 /* Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; C3F59BE21F981DAD002494E4 /* Protocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocol.swift; sourceTree = ""; }; C3F59BE41F981E6F002494E4 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; C3F59BE61F98229C002494E4 /* Struct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Struct.swift; sourceTree = ""; }; C3F59BE81F9827BD002494E4 /* Platform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.swift; sourceTree = ""; }; C3F59BEA1F982B80002494E4 /* Runtime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Runtime.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ C35AEBB81F987493008F2988 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( C35AEBC01F987493008F2988 /* libRuntime.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; C3F59BC51F981B41002494E4 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ C35AEBBC1F987493008F2988 /* RuntimeTests */ = { isa = PBXGroup; children = ( C35AEBBD1F987493008F2988 /* RuntimeTests.swift */, C35AEBBF1F987493008F2988 /* Info.plist */, C3BEFCC01F994C7F005AD638 /* Person.swift */, ); path = RuntimeTests; sourceTree = ""; }; C3F59BBF1F981B41002494E4 = { isa = PBXGroup; children = ( C3F59BCA1F981B41002494E4 /* Runtime */, C35AEBBC1F987493008F2988 /* RuntimeTests */, C3F59BC91F981B41002494E4 /* Products */, ); sourceTree = ""; }; C3F59BC91F981B41002494E4 /* Products */ = { isa = PBXGroup; children = ( C3F59BC81F981B41002494E4 /* libRuntime.a */, C35AEBBB1F987493008F2988 /* RuntimeTests.xctest */, ); name = Products; sourceTree = ""; }; C3F59BCA1F981B41002494E4 /* Runtime */ = { isa = PBXGroup; children = ( C3F59BEA1F982B80002494E4 /* Runtime.swift */, C3F59BE81F9827BD002494E4 /* Platform.swift */, C3F59BE41F981E6F002494E4 /* Types.swift */, C3F59BD81F981C20002494E4 /* Pointer.swift */, C3F59BD41F981B8D002494E4 /* Class.swift */, C3D55DF71F9C4535006DE12F /* ClassBuilder.swift */, C3F59BE61F98229C002494E4 /* Struct.swift */, C3F59BDC1F981D8D002494E4 /* Ivar.swift */, C3F59BDE1F981D93002494E4 /* Method.swift */, C3F59BE01F981D9C002494E4 /* Property.swift */, C3F59BE21F981DAD002494E4 /* Protocol.swift */, C3F59BDA1F981C4D002494E4 /* Object.swift */, C3BEFCC21F994CB9005AD638 /* RootObject.swift */, C37F4B731F9B279E000732E0 /* DebugDescriptions.swift */, C3D55DF91F9E4642006DE12F /* OrderedSet.swift */, ); path = Runtime; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ C35AEBBA1F987493008F2988 /* RuntimeTests */ = { isa = PBXNativeTarget; buildConfigurationList = C35AEBC51F987493008F2988 /* Build configuration list for PBXNativeTarget "RuntimeTests" */; buildPhases = ( C35AEBB71F987493008F2988 /* Sources */, C35AEBB81F987493008F2988 /* Frameworks */, C35AEBB91F987493008F2988 /* Resources */, ); buildRules = ( ); dependencies = ( C35AEBC21F987493008F2988 /* PBXTargetDependency */, ); name = RuntimeTests; productName = RuntimeTests; productReference = C35AEBBB1F987493008F2988 /* RuntimeTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; C3F59BC71F981B41002494E4 /* Runtime */ = { isa = PBXNativeTarget; buildConfigurationList = C3F59BD11F981B41002494E4 /* Build configuration list for PBXNativeTarget "Runtime" */; buildPhases = ( C3F59BC41F981B41002494E4 /* Sources */, C3F59BC51F981B41002494E4 /* Frameworks */, C3F59BC61F981B41002494E4 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = Runtime; productName = Runtime; productReference = C3F59BC81F981B41002494E4 /* libRuntime.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ C3F59BC01F981B41002494E4 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0900; LastUpgradeCheck = 0910; ORGANIZATIONNAME = "Tanner Bennett"; TargetAttributes = { C35AEBBA1F987493008F2988 = { CreatedOnToolsVersion = 9.0; ProvisioningStyle = Automatic; }; C3F59BC71F981B41002494E4 = { CreatedOnToolsVersion = 9.0; LastSwiftMigration = 0900; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = C3F59BC31F981B41002494E4 /* Build configuration list for PBXProject "Runtime" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = C3F59BBF1F981B41002494E4; productRefGroup = C3F59BC91F981B41002494E4 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( C3F59BC71F981B41002494E4 /* Runtime */, C35AEBBA1F987493008F2988 /* RuntimeTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ C35AEBB91F987493008F2988 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ C35AEBB71F987493008F2988 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C3BEFCC11F994C7F005AD638 /* Person.swift in Sources */, C35AEBBE1F987493008F2988 /* RuntimeTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; C3F59BC41F981B41002494E4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C3F59BE91F9827BD002494E4 /* Platform.swift in Sources */, C3F59BE11F981D9C002494E4 /* Property.swift in Sources */, C3BEFCC31F994CB9005AD638 /* RootObject.swift in Sources */, C3F59BDF1F981D93002494E4 /* Method.swift in Sources */, C37F4B741F9B279E000732E0 /* DebugDescriptions.swift in Sources */, C3F59BD51F981B8D002494E4 /* Class.swift in Sources */, C3F59BE71F98229C002494E4 /* Struct.swift in Sources */, C3F59BE51F981E6F002494E4 /* Types.swift in Sources */, C3F59BEB1F982B80002494E4 /* Runtime.swift in Sources */, C3D55DFA1F9E4642006DE12F /* OrderedSet.swift in Sources */, C3F59BE31F981DAD002494E4 /* Protocol.swift in Sources */, C3D55DF81F9C4535006DE12F /* ClassBuilder.swift in Sources */, C3F59BDB1F981C4D002494E4 /* Object.swift in Sources */, C3F59BD91F981C20002494E4 /* Pointer.swift in Sources */, C3F59BDD1F981D8D002494E4 /* Ivar.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ C35AEBC21F987493008F2988 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C3F59BC71F981B41002494E4 /* Runtime */; targetProxy = C35AEBC11F987493008F2988 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ C35AEBC31F987493008F2988 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S6N2F22V2Z; INFOPLIST_FILE = RuntimeTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.nsexceptional.RuntimeTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; C35AEBC41F987493008F2988 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S6N2F22V2Z; INFOPLIST_FILE = RuntimeTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.nsexceptional.RuntimeTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; C3F59BCF1F981B41002494E4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; C3F59BD01F981B41002494E4 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; }; C3F59BD21F981B41002494E4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S6N2F22V2Z; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; C3F59BD31F981B41002494E4 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S6N2F22V2Z; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C35AEBC51F987493008F2988 /* Build configuration list for PBXNativeTarget "RuntimeTests" */ = { isa = XCConfigurationList; buildConfigurations = ( C35AEBC31F987493008F2988 /* Debug */, C35AEBC41F987493008F2988 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C3F59BC31F981B41002494E4 /* Build configuration list for PBXProject "Runtime" */ = { isa = XCConfigurationList; buildConfigurations = ( C3F59BCF1F981B41002494E4 /* Debug */, C3F59BD01F981B41002494E4 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C3F59BD11F981B41002494E4 /* Build configuration list for PBXNativeTarget "Runtime" */ = { isa = XCConfigurationList; buildConfigurations = ( C3F59BD21F981B41002494E4 /* Debug */, C3F59BD31F981B41002494E4 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = C3F59BC01F981B41002494E4 /* Project object */; } ================================================ FILE: Runtime.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: RuntimeTests/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleVersion 1 ================================================ FILE: RuntimeTests/Person.swift ================================================ // // Person.swift // RuntimeTests // // Created by Tanner on 10/19/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // @testable import Runtime infix operator |= infix operator | /// Example subclass of RootObject. struct Person { static let `class` = Class( isa: Person_meta.class, superclass: RootObject.class, name: "Person", ivars: [ (name: "_name", type: .string), (name: "_age", type: .integer) ], methods: [_init, name, setName_, age, setAge_, description], properties: [ Property(name: "name", getter: name, setter: setName_), Property(name: "age", getter: age, setter: setAge_), ], protocols: [] ) static var _init = Method("init", returns: .object("self")) { this, _cmd, args in func init$(_ this: id, _ _cmd: SEL) -> id { _msgSend(this, "setName_", ("Bob")) _msgSend(this, "setAge_", (18)) print("init override: \(this)") return msgSend(super: true, this, _cmd) } return init$(this, _cmd) } static var name = Method("name", returns: .string) { this, _cmd, args in func name$(_ this: id, _ _cmd: SEL) -> String { return this|"_name" } return name$(this, _cmd) } static var setName_ = Method("setName_", args: [.string]) { this, _cmd, args in func setName$(_ this: id, _ _cmd: SEL, _ name: String) { this |= (name, "_name") } let args = (args as! (String)) setName$(this, _cmd, args) return () } static var age = Method("age", returns: .string) { this, _cmd, args in func age$(_ this: id, _ _cmd: SEL) -> Int { return this|"_age" } return age$(this, _cmd) } static var setAge_ = Method("setAge_", args: [.string]) { this, _cmd, args in func setName$(_ this: id, _ _cmd: SEL, _ age: Int) { this |= (age, "_age") } let args = (args as! (Int)) setName$(this, _cmd, args) return () } static var description = Method("description", returns: .string) { this, _cmd, args in func description$(_ this: id, _ _cmd: SEL) -> String { let name: String = msgSend(this, "name") let age: Int = msgSend(this, "age") return """ <\(this|.getClass) \(this.raw.debugDescription)> { name: \(name), age: \(age) } """ } return description$(this, _cmd) } } private struct Person_meta { static let `class` = Class( isa: nil, superclass: nil, name: "Person.meta", ivars: [], methods: [], properties: [], protocols: [] ) } /// For debugging purposes /// unsafeBitCast(this, to: UnsafePointer.self).pointee struct person_ { let isa: Class let _retainCount: Int let _name: String let _age: Int } ================================================ FILE: RuntimeTests/RuntimeTests.swift ================================================ // // RuntimeTests.swift // RuntimeTests // // Created by Tanner on 10/19/17. // Copyright © 2017 Tanner Bennett. All rights reserved. // import XCTest @testable import Runtime class Tests: XCTestCase { typealias id = Runtime.id override func setUp() { // Runtime initialization _ = RootObject.class _ = Person.class } func testPerson() { let bob: id = msgSend(Person.class.createInstance(), "init") let name: String = msgSend(bob, "name") let age: Int = msgSend(bob, "age") let description: String = msgSend(bob, "description") XCTAssertEqual("Bob", name) XCTAssertEqual(18, age) XCTAssert(description.hasPrefix("\(bob)")) } func testPointerAssumptions() { class Foo { struct Layout { let magic: (isa: Int, refCount: Int) let count: Int } let count = 0x72656E6E6174 public init() {} } var instance = Foo() let bitcast = unsafeBitCast(instance, to: Pointer.self) let unsafe = withUnsafePointer(to: &instance, { $0 }) XCTAssertEqual(bitcast|.count, unsafe.pointee.count) } func testClassObjectReferencing() { var cls = Class.named("Object")! XCTAssert(cls === RootObject.class) let ptr: Pointer = |cls let unsafePtr = withUnsafePointer(to: &cls, { $0 }) XCTAssertEqual(cls.name, ptr|.name) XCTAssertEqual(ptr.raw.debugDescription, unsafePtr.debugDescription) var asObject: id = ~cls asObject += 16 XCTAssertEqual(cls.name, ptr|.name) let ref = cls.ref XCTAssertEqual(asObject, ref) XCTAssert(ref|.isa === RootObject_meta.class) } func testCreateClass() { XCTAssertNil(Class.named("Foo")) var counter = 0 let builder = ClassBuilder(name: "Foo")! let initializer = Method("init", returns: .object("self")) { this, _cmd, args in func init$(_ this: id, _ _cmd: SEL) -> id { let this: id = msgSend(super: true, this, _cmd) counter += 1 return this } return init$(this, _cmd) } let method = Method("getClassName", returns: .string) { this, _cmd, args in func getClassName$(_ this: id, _ _cmd: SEL) -> String { return this|.getClass.name } return getClassName$(this, _cmd) } builder.add([initializer, method]) let classMethod = Method("instanceCount", returns: .integer) { this, _cmd, args in func instanceCount$(_ this: id, _ _cmd: SEL) -> Int { return counter } return instanceCount$(this, _cmd) } builder.add([classMethod], toClass: true) let created = builder.finalize() let asObject = created.ref // TODO: Make this not necessary XCTAssert(created === Class.named("Foo")) XCTAssertEqual(0, msgSend(asObject, "instanceCount")) _msgSend(created.createInstance(), "init") XCTAssertEqual(1, msgSend(asObject, "instanceCount")) } }