├── .github └── FUNDING.yml ├── Runtime.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── Runtime ├── Platform.swift ├── Property.swift ├── Protocol.swift ├── DebugDescriptions.swift ├── Ivar.swift ├── Object.swift ├── OrderedSet.swift ├── Method.swift ├── Struct.swift ├── Runtime.swift ├── RootObject.swift ├── ClassBuilder.swift ├── Types.swift ├── Class.swift └── Pointer.swift ├── RuntimeTests ├── Info.plist ├── Person.swift └── RuntimeTests.swift ├── .gitignore └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [NSExceptional] 4 | -------------------------------------------------------------------------------- /Runtime.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Runtime/Platform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Platform.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Platform { 12 | static let is64Bit = MemoryLayout.size == MemoryLayout.size 13 | } 14 | -------------------------------------------------------------------------------- /Runtime/Property.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Property.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Property: Hashable, Equatable { 12 | public let name: String 13 | public let getter: Method 14 | public let setter: Method! 15 | 16 | public var type: Type { 17 | return self.getter.returnType 18 | } 19 | 20 | public var hashValue: Int { 21 | return self.name.hashValue 22 | } 23 | 24 | public static func ==(lhs: Property, rhs: Property) -> Bool { 25 | return lhs.name == rhs.name 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /RuntimeTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | DerivedData 17 | .idea/ 18 | 19 | *.xcbkptlist 20 | *.xccheckout 21 | *.hmap 22 | *.ipa 23 | 24 | *.swp 25 | *.lock 26 | *.xcuserstate 27 | 28 | Luna.xcworkspace/xcuserdata/tantan.xcuserdatad/UserInterfaceState.xcuserstate 29 | 30 | .AppleDouble 31 | .LSOverride 32 | 33 | # Icon must end with two \r 34 | Icon 35 | 36 | # Thumbnails 37 | ._* 38 | 39 | # Files that might appear on external disk 40 | .Spotlight-V100 41 | .Trashes 42 | 43 | # Directories potentially created on remote AFP share 44 | .AppleDB 45 | .AppleDesktop 46 | Network Trash Folder 47 | Temporary Items 48 | .apdisk -------------------------------------------------------------------------------- /Runtime/Protocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Protocol.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Protocol: Hashable { 12 | public let name: String 13 | public let methods: [Method] 14 | 15 | public init(name: String, methods: [Method]) { 16 | self.name = name 17 | self.methods = methods 18 | } 19 | 20 | public var hashValue: Int { 21 | return self.name.hashValue 22 | } 23 | 24 | public static func ==(lhs: Protocol, rhs: Protocol) -> Bool { 25 | return lhs.name == rhs.name 26 | } 27 | } 28 | 29 | public extension Protocol { 30 | static var protocolList: [Protocol] = [] 31 | 32 | public static func named(_ name: String) -> Protocol! { 33 | return Protocol.protocolList.filter { $0.name == name }.first 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Runtime/DebugDescriptions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DebugDescriptions.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/21/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Class: CustomDebugStringConvertible, CustomStringConvertible, CustomPlaygroundQuickLookable { 12 | public var customPlaygroundQuickLook: PlaygroundQuickLook { 13 | return .text(self.name) 14 | } 15 | 16 | public var description: String { 17 | return self.name 18 | } 19 | 20 | public var debugDescription: String { 21 | var me = self 22 | return """ 23 | { 24 | isa: \(self.isa?.name ?? "nil"), 25 | superclass: \(self.superclass?.name ?? "nil"), 26 | methods: \(self.methods.count), 27 | properties: \(self.properties.count), 28 | protocols: \(self.protocols.count), 29 | } 30 | """ 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Runtime/Ivar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Ivar.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Ivar: Hashable { 12 | public typealias Stub = (name: String, type: Type) 13 | 14 | public let name: String 15 | public let type: Type 16 | public let offset: Int 17 | 18 | public var hashValue: Int { 19 | return self.name.hashValue 20 | } 21 | 22 | public static func ==(lhs: Ivar, rhs: Ivar) -> Bool { 23 | return lhs.name == rhs.name 24 | } 25 | } 26 | 27 | extension Ivar { 28 | static func make(from stubs: [Stub], _ offset: Int) -> [Ivar] { 29 | var offsett = offset 30 | return stubs.map { stub -> Ivar in 31 | defer { offsett += stub.type.size } 32 | return Ivar(name: stub.name, type: stub.type, offset: offsett) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Runtime/Object.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Object.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Object { 12 | var isa: Class! 13 | 14 | public var getClass: Class { 15 | return self.isa 16 | } 17 | 18 | public func respondsTo(_ selector: SEL) -> Bool { 19 | return self.isa.getMethodIMP(selector) != nil 20 | } 21 | } 22 | 23 | extension Pointer: CustomStringConvertible, CustomDebugStringConvertible { 24 | public var debugDescription: String { return self.description } 25 | public var description: String { 26 | if Pointee.self is Object.Type { 27 | let this: id = ~self 28 | if this|.respondsTo("description") { 29 | return msgSend(this, "description") 30 | } else { 31 | return "<\(this|.getClass) \(self.raw.debugDescription)>" 32 | } 33 | } 34 | 35 | return self.raw.debugDescription 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Runtime/OrderedSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OrderedSet.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/23/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class OrderedSet: ExpressibleByArrayLiteral { 12 | var array: [T] = [] 13 | var set: Set = [] 14 | 15 | init() { } 16 | 17 | /// - Returns: true if the element was not already in the set, false otherwise 18 | @discardableResult 19 | func add(_ element: T) -> Bool { 20 | if self.set.insert(element).inserted { 21 | array.append(element) 22 | return true 23 | } 24 | 25 | return false 26 | } 27 | 28 | /// - Returns: true if all elements were not already in the set, false otherwise 29 | @discardableResult 30 | func addAll(_ elements: [T]) -> Bool { 31 | var allIn = true 32 | for e in elements { 33 | if !self.add(e) { 34 | allIn = false 35 | } 36 | } 37 | 38 | return allIn 39 | } 40 | 41 | required init(arrayLiteral elements: T...) { 42 | self.addAll(elements) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Runtime/Method.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Method.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Method: Hashable { 12 | public typealias Signauture = (returnType: Type, argumentTypes: [Type]) 13 | 14 | public let name: String 15 | public var imp: IMP 16 | public let returnType: Type 17 | public let argumentTypes: [Type] 18 | 19 | public var signature: Signauture { 20 | return (self.returnType, self.argumentTypes) 21 | } 22 | 23 | public init(_ name: String, returns: Type = .void, args: [Type] = [], _ imp: @escaping IMP) { 24 | self.name = name 25 | self.returnType = returns 26 | self.argumentTypes = args 27 | self.imp = imp 28 | } 29 | 30 | convenience public init(name: String, signature: Signauture = (.void, []), _ imp: @escaping IMP) { 31 | self.init(name, returns: signature.returnType, args: signature.argumentTypes, imp) 32 | } 33 | 34 | public var hashValue: Int { 35 | return self.name.hashValue 36 | } 37 | 38 | public static func ==(lhs: Method, rhs: Method) -> Bool { 39 | return lhs.name == rhs.name 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Runtime/Struct.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Struct.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// As of now, purely for metadata purposes. 12 | /// Provides no way to create and use structs. 13 | /// It may make more sense just to use standard Swift structs 14 | /// alongside instances of `Struct` for reflection purposes. 15 | public class Struct { 16 | let name: String 17 | let instanceSize: Int 18 | 19 | let ivars: [Ivar] 20 | let methods: [Method] 21 | let properties: [Property] 22 | 23 | public init(name: String, instanceSize: Int, ivars: [Ivar.Stub] = [], methods: [Method] = [], properties: [Property] = []) { 24 | self.name = name 25 | self.instanceSize = instanceSize 26 | self.ivars = Ivar.make(from: ivars, 0) 27 | self.methods = methods 28 | self.properties = properties 29 | } 30 | } 31 | 32 | public extension Struct { 33 | static var structList: [Struct] = [] 34 | 35 | public static func named(_ name: String) -> Struct! { 36 | return Struct.structList.filter { $0.name == name }.first 37 | } 38 | } 39 | 40 | public extension Struct { 41 | public var describedAsTuple: String { 42 | return "(" + self.ivars.map({ ivar in 43 | if !ivar.name.isEmpty { 44 | return ivar.name + ": " + ivar.type.description 45 | } else { 46 | return ivar.type.description 47 | } 48 | }).joined(separator: ", ") 49 | } 50 | 51 | public var typeEncoding: String { 52 | return "" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Runtime/Runtime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Runtime.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias id = Pointer 12 | public typealias SEL = String 13 | public typealias IMP = (_ self: id, _ _cmd: SEL, _ args: Any) -> Any 14 | 15 | @inline(__always) func prepareMsg(_ target: id, _ _cmd: SEL, super: Bool) -> IMP { 16 | let isClass = Class.isClass(target) 17 | var cls: Class! 18 | if isClass { 19 | cls = `super` ? target|.isa.superclass! : target|.isa 20 | } else { 21 | cls = `super` ? target|.getClass.superclass! : target|.getClass 22 | } 23 | 24 | var imp: IMP! 25 | repeat { 26 | imp = cls.getMethodIMP(_cmd) 27 | cls = cls.superclass 28 | } while imp == nil && cls != nil 29 | 30 | guard imp != nil else { 31 | let isClass = Class.isClass(target) 32 | let invocation = (isClass ? "+" : "-") + "[\(target|.getClass.name) \(_cmd)]" 33 | fatalError("Unrecognized selector sent to instance \(target.raw): " + invocation) 34 | } 35 | 36 | return imp 37 | } 38 | 39 | /// Dynamically calls a method on a `Foo` instance given a method name (like objc_msgSend) 40 | public func msgSend(super: Bool = false, _ target: id, _ _cmd: SEL, _ args: Any = ()) -> T { 41 | let imp = prepareMsg(target, _cmd, super: `super`) 42 | return imp(target, _cmd, args) as! T 43 | } 44 | 45 | /// Convenience for `Void` or discardable results 46 | public func _msgSend(super: Bool = false, _ target: id, _ _cmd: SEL, _ args: Any = ()) { 47 | let imp = prepareMsg(target, _cmd, super: `super`) 48 | _ = imp(target, _cmd, args) 49 | } 50 | -------------------------------------------------------------------------------- /Runtime/RootObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootObject.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/19/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// New classes are defined as static instances within an extension on Class itself. 12 | /// Methods, ivars, properties, etc for these new classes are defined as below. 13 | public struct RootObject { 14 | static let `class` = Class( 15 | isa: RootObject_meta.class, 16 | superclass: nil, 17 | name: "Object", 18 | ivars: [("isa", .pointer(.class("self"))), ("_retainCount", .integer)], 19 | methods: [_init, retain, retainCount] 20 | ) 21 | 22 | static var _init = Method("init", returns: .object("self")) { this, _cmd, args in 23 | func init$(_ this: id, _ _cmd: SEL) -> id { 24 | print("\(this|.getClass).init(): \(this)") 25 | return this 26 | } 27 | 28 | return init$(this, _cmd) 29 | } 30 | 31 | static var retain = Method("retain", returns: .object("self")) { this, _cmd, args in 32 | func retain$(_ this: id, _ _cmd: SEL) -> id { 33 | let newCount: Int = (this|"_retainCount") + 1 34 | this |= (newCount, "_retainCount") 35 | return this 36 | } 37 | 38 | return retain$(this, _cmd) 39 | } 40 | 41 | static var release = Method("release") { this, _cmd, args in 42 | func release$(_ this: id, _ _cmd: SEL) { 43 | let newCount: Int = (this|"_retainCount") - 1 44 | this |= (newCount, "_retainCount") 45 | if newCount < 1 { 46 | if newCount < 0 { 47 | fatalError("Over-released object at \(this.raw.debugDescription)") 48 | } 49 | 50 | this.free() 51 | } 52 | } 53 | 54 | release$(this, _cmd) 55 | return () 56 | } 57 | 58 | static var retainCount = Method("retainCount", returns: .integer) { this, _cmd, args in 59 | func retainCount$(_ this: id, _ _cmd: SEL) { 60 | return this|"_retainCount" 61 | } 62 | 63 | retainCount$(this, _cmd) 64 | return () 65 | } 66 | } 67 | 68 | public struct RootObject_meta { 69 | static let `class` = Class( 70 | isa: nil, 71 | superclass: nil, 72 | name: "Object.meta" 73 | ) 74 | } 75 | -------------------------------------------------------------------------------- /Runtime/ClassBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClassBuilder.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/21/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class ClassBuilder { 12 | public var superclass: Class? 13 | public var name: String 14 | public var extraBytes: Int 15 | 16 | private var final = false 17 | private var methods: OrderedSet = [] 18 | private var classMethods: OrderedSet = [] 19 | private var ivars: OrderedSet = [] 20 | private var properties: OrderedSet = [] 21 | private var classProperties: OrderedSet = [] 22 | private var protocols: OrderedSet = [] 23 | 24 | /// - Warning: returns nil if a class with the given name already exists. 25 | public init?(name: String, superclass: Class? = RootObject.class, extraBytes: Int = 0) { 26 | if Class.named(name) != nil { 27 | return nil 28 | } 29 | 30 | self.superclass = superclass 31 | self.name = name 32 | self.extraBytes = extraBytes 33 | } 34 | 35 | /// Actually creates the class and adds it to the runtime. 36 | /// 37 | /// The class you are constructing cannot be used until it is finalized. 38 | /// - Returns: The newly created class. 39 | public func finalize() -> Class { 40 | let metaclass = Class( 41 | isa: nil, 42 | superclass: self.superclass?.isa, 43 | name: self.name + ".meta", 44 | ivars: [], 45 | methods: self.classMethods.array, 46 | properties: self.classProperties.array, 47 | protocols: [] 48 | ) 49 | let cls = Class( 50 | isa: metaclass, 51 | superclass: self.superclass, 52 | name: self.name, 53 | ivars: self.ivars.array.map({ $0.stub }), 54 | methods: self.methods.array, 55 | properties: self.properties.array, 56 | protocols: self.protocols.array, 57 | extraBytes: self.extraBytes 58 | ) 59 | 60 | self.final = true 61 | Class.classList.append(cls) 62 | return cls 63 | } 64 | 65 | public func add(_ methods: [Method], toClass: Bool = false) { 66 | if toClass { 67 | self.classMethods.addAll(methods) 68 | } else { 69 | self.methods.addAll(methods) 70 | } 71 | } 72 | 73 | public func add(_ ivars: [IvarStub]) { 74 | self.ivars.addAll(ivars) 75 | } 76 | 77 | public func add(_ properties: [Property], toClass: Bool = false) { 78 | if toClass { 79 | self.classProperties.addAll(properties) 80 | } else { 81 | self.properties.addAll(properties) 82 | } 83 | } 84 | 85 | public func add(_ protocols: [Protocol]) { 86 | self.protocols .addAll(protocols) 87 | } 88 | 89 | public struct IvarStub: Hashable { 90 | let name: String 91 | let type: Type 92 | 93 | fileprivate var stub: Ivar.Stub { return (name, type) } 94 | 95 | public var hashValue: Int { 96 | return self.name.hashValue 97 | } 98 | 99 | public static func ==(lhs: IvarStub, rhs: IvarStub) -> Bool { 100 | return lhs.name == rhs.name 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Runtime/Types.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Types.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public indirect enum Type { 12 | case void 13 | case pointer(Type) 14 | case integer 15 | case float 16 | case bool 17 | case string 18 | case tuple(String) 19 | case optional(Type) 20 | case object(String) 21 | case `struct`(String) 22 | case `class`(String) 23 | 24 | public var description: String { 25 | switch self { 26 | case .void: 27 | return "Void" 28 | case .pointer(let type): 29 | return "Pointer<\(type)>" 30 | case .integer: 31 | return "Integer" 32 | case .float: 33 | return "Float" 34 | case .bool: 35 | return "Bool" 36 | case .string: 37 | return "String" 38 | case .tuple(let structName): 39 | return structName // TDOO: struct.describedAsTuple 40 | case .optional(let type): 41 | return type.description + "?" 42 | case .object(let name): 43 | return name 44 | case .struct(let name): 45 | return name 46 | case .class(let name): 47 | return name 48 | } 49 | } 50 | 51 | public var encoding: String { 52 | switch self { 53 | case .void: 54 | return "v" 55 | case .pointer(let type): 56 | return "^\(type.encoding)" 57 | case .integer: 58 | return Platform.is64Bit ? "q" : "i" 59 | case .float: 60 | return Platform.is64Bit ? "d" : "f" 61 | case .bool: 62 | return "C" 63 | case .string: 64 | return "{11_StringCore}" 65 | case .tuple(let structName): 66 | return Type.struct(structName).encoding 67 | case .optional(let type): 68 | return "?\(type.description.count)\(type.description)" 69 | case .object(_): 70 | return "@" 71 | case .struct(let name): 72 | return "{\(name.count)" + name + "}" 73 | case .class(_): 74 | return "#" 75 | } 76 | } 77 | 78 | public var size: Int { 79 | switch self { 80 | case .void: 81 | return 0 82 | case .pointer(_): fallthrough 83 | case .integer: 84 | return MemoryLayout.size 85 | case .float: 86 | return Platform.is64Bit ? MemoryLayout.size : MemoryLayout.size 87 | case .bool: 88 | return MemoryLayout.size 89 | case .string: 90 | return MemoryLayout.size 91 | case .tuple(let structName): 92 | return Type.struct(structName).size 93 | case .optional(let type): 94 | switch type { 95 | case .pointer(let ptrType): 96 | return ptrType.size 97 | default: 98 | return type.size + 1 99 | } 100 | case .object(let className): 101 | return Class.named(className).instanceSize 102 | case .struct(let name): 103 | return Struct.named(name).instanceSize 104 | case .class(_): 105 | return 0 // You'll never need to know the size of a class in practice 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /RuntimeTests/Person.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Person.swift 3 | // RuntimeTests 4 | // 5 | // Created by Tanner on 10/19/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | @testable import Runtime 10 | 11 | infix operator |= 12 | infix operator | 13 | 14 | /// Example subclass of RootObject. 15 | struct Person { 16 | static let `class` = Class( 17 | isa: Person_meta.class, 18 | superclass: RootObject.class, 19 | name: "Person", 20 | ivars: [ 21 | (name: "_name", type: .string), 22 | (name: "_age", type: .integer) 23 | ], 24 | methods: [_init, name, setName_, age, setAge_, description], 25 | properties: [ 26 | Property(name: "name", getter: name, setter: setName_), 27 | Property(name: "age", getter: age, setter: setAge_), 28 | ], 29 | protocols: [] 30 | ) 31 | 32 | static var _init = Method("init", returns: .object("self")) { this, _cmd, args in 33 | func init$(_ this: id, _ _cmd: SEL) -> id { 34 | _msgSend(this, "setName_", ("Bob")) 35 | _msgSend(this, "setAge_", (18)) 36 | print("init override: \(this)") 37 | 38 | return msgSend(super: true, this, _cmd) 39 | } 40 | 41 | return init$(this, _cmd) 42 | } 43 | 44 | static var name = Method("name", returns: .string) { this, _cmd, args in 45 | func name$(_ this: id, _ _cmd: SEL) -> String { 46 | return this|"_name" 47 | } 48 | 49 | return name$(this, _cmd) 50 | } 51 | 52 | static var setName_ = Method("setName_", args: [.string]) { this, _cmd, args in 53 | func setName$(_ this: id, _ _cmd: SEL, _ name: String) { 54 | this |= (name, "_name") 55 | } 56 | 57 | let args = (args as! (String)) 58 | setName$(this, _cmd, args) 59 | return () 60 | } 61 | 62 | static var age = Method("age", returns: .string) { this, _cmd, args in 63 | func age$(_ this: id, _ _cmd: SEL) -> Int { 64 | return this|"_age" 65 | } 66 | 67 | return age$(this, _cmd) 68 | } 69 | 70 | static var setAge_ = Method("setAge_", args: [.string]) { this, _cmd, args in 71 | func setName$(_ this: id, _ _cmd: SEL, _ age: Int) { 72 | this |= (age, "_age") 73 | } 74 | 75 | let args = (args as! (Int)) 76 | setName$(this, _cmd, args) 77 | return () 78 | } 79 | 80 | static var description = Method("description", returns: .string) { this, _cmd, args in 81 | func description$(_ this: id, _ _cmd: SEL) -> String { 82 | let name: String = msgSend(this, "name") 83 | let age: Int = msgSend(this, "age") 84 | return """ 85 | <\(this|.getClass) \(this.raw.debugDescription)> { 86 | name: \(name), 87 | age: \(age) 88 | } 89 | """ 90 | } 91 | 92 | return description$(this, _cmd) 93 | } 94 | } 95 | 96 | private struct Person_meta { 97 | static let `class` = Class( 98 | isa: nil, 99 | superclass: nil, 100 | name: "Person.meta", 101 | ivars: [], 102 | methods: [], 103 | properties: [], 104 | protocols: [] 105 | ) 106 | } 107 | 108 | /// For debugging purposes 109 | /// unsafeBitCast(this, to: UnsafePointer.self).pointee 110 | struct person_ { 111 | let isa: Class 112 | let _retainCount: Int 113 | let _name: String 114 | let _age: Int 115 | } 116 | -------------------------------------------------------------------------------- /RuntimeTests/RuntimeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RuntimeTests.swift 3 | // RuntimeTests 4 | // 5 | // Created by Tanner on 10/19/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Runtime 11 | 12 | class Tests: XCTestCase { 13 | typealias id = Runtime.id 14 | 15 | override func setUp() { 16 | // Runtime initialization 17 | _ = RootObject.class 18 | _ = Person.class 19 | } 20 | 21 | func testPerson() { 22 | let bob: id = msgSend(Person.class.createInstance(), "init") 23 | let name: String = msgSend(bob, "name") 24 | let age: Int = msgSend(bob, "age") 25 | let description: String = msgSend(bob, "description") 26 | XCTAssertEqual("Bob", name) 27 | XCTAssertEqual(18, age) 28 | XCTAssert(description.hasPrefix("\(bob)")) 29 | } 30 | 31 | func testPointerAssumptions() { 32 | class Foo { 33 | struct Layout { 34 | let magic: (isa: Int, refCount: Int) 35 | let count: Int 36 | } 37 | let count = 0x72656E6E6174 38 | public init() {} 39 | } 40 | 41 | var instance = Foo() 42 | let bitcast = unsafeBitCast(instance, to: Pointer.self) 43 | let unsafe = withUnsafePointer(to: &instance, { $0 }) 44 | 45 | XCTAssertEqual(bitcast|.count, unsafe.pointee.count) 46 | } 47 | 48 | func testClassObjectReferencing() { 49 | var cls = Class.named("Object")! 50 | XCTAssert(cls === RootObject.class) 51 | 52 | let ptr: Pointer = |cls 53 | let unsafePtr = withUnsafePointer(to: &cls, { $0 }) 54 | 55 | XCTAssertEqual(cls.name, ptr|.name) 56 | XCTAssertEqual(ptr.raw.debugDescription, unsafePtr.debugDescription) 57 | 58 | var asObject: id = ~cls 59 | asObject += 16 60 | XCTAssertEqual(cls.name, ptr|.name) 61 | 62 | let ref = cls.ref 63 | XCTAssertEqual(asObject, ref) 64 | XCTAssert(ref|.isa === RootObject_meta.class) 65 | } 66 | 67 | func testCreateClass() { 68 | XCTAssertNil(Class.named("Foo")) 69 | var counter = 0 70 | 71 | let builder = ClassBuilder(name: "Foo")! 72 | let initializer = Method("init", returns: .object("self")) { this, _cmd, args in 73 | func init$(_ this: id, _ _cmd: SEL) -> id { 74 | let this: id = msgSend(super: true, this, _cmd) 75 | counter += 1 76 | return this 77 | } 78 | 79 | return init$(this, _cmd) 80 | } 81 | let method = Method("getClassName", returns: .string) { this, _cmd, args in 82 | func getClassName$(_ this: id, _ _cmd: SEL) -> String { 83 | return this|.getClass.name 84 | } 85 | 86 | return getClassName$(this, _cmd) 87 | } 88 | builder.add([initializer, method]) 89 | 90 | let classMethod = Method("instanceCount", returns: .integer) { this, _cmd, args in 91 | func instanceCount$(_ this: id, _ _cmd: SEL) -> Int { 92 | return counter 93 | } 94 | 95 | return instanceCount$(this, _cmd) 96 | } 97 | builder.add([classMethod], toClass: true) 98 | let created = builder.finalize() 99 | let asObject = created.ref // TODO: Make this not necessary 100 | 101 | XCTAssert(created === Class.named("Foo")) 102 | XCTAssertEqual(0, msgSend(asObject, "instanceCount")) 103 | 104 | _msgSend(created.createInstance(), "init") 105 | XCTAssertEqual(1, msgSend(asObject, "instanceCount")) 106 | } 107 | } 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /Runtime/Class.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Class.swift 3 | // Runtime 4 | // 5 | // Created by Tanner on 10/18/17. 6 | // Copyright © 2017 Tanner Bennett. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Class { 12 | public let isa: Class! // metaclass, nil for metaclasses 13 | let magic: UInt = 0xAAAABBBBCCCCDDDD // for debugging purposes, will be removed 14 | public let superclass: Class! // nil if no superclass 15 | public let name: String 16 | public let instanceSize: Int 17 | 18 | /// Instance class property-backing variables 19 | let ivars: [Ivar] 20 | /// Instance or class methods 21 | let methods: [SEL: Method] 22 | /// Instance or class properties 23 | let properties: [Property] 24 | /// Protocols conformed to by instances; not applicable to classes 25 | let protocols: [Protocol] 26 | 27 | private static let __swobjSize = 16 28 | /// Used to refer to the class as an object. 29 | /// Temporary workaround since class objects themselves 30 | /// already have an `isa` provided by Swift. 31 | public var ref: id { 32 | var ptr: id = ~self 33 | ptr += Class.__swobjSize 34 | return ptr 35 | } 36 | 37 | public init(isa: Class!, superclass: Class?, name: String, 38 | ivars stubs: [Ivar.Stub] = [], methods: [Method] = [], properties: [Property] = [], protocols: [Protocol] = [], extraBytes: Int = 0) { 39 | let offset = superclass?.instanceSize ?? Type.pointer(.class("")).size 40 | 41 | self.isa = isa 42 | self.superclass = superclass 43 | self.name = name 44 | self.instanceSize = offset + stubs.map { return $0.type.size }.reduce(0, +) + extraBytes 45 | 46 | self.ivars = Ivar.make(from: stubs, offset) 47 | self.properties = properties 48 | self.protocols = protocols 49 | self.methods = { 50 | var dict: [SEL: Method] = [:] 51 | for method in methods { 52 | dict[method.name] = method 53 | } 54 | 55 | return dict 56 | }() 57 | 58 | Class.classList.append(self) 59 | } 60 | 61 | public func createInstance() -> id { 62 | let instance = Pointer.calloc(self.instanceSize) 63 | instance.pointee.isa = self 64 | return instance 65 | } 66 | 67 | public func method(named: String) -> Method! { 68 | return self.methods[named] 69 | } 70 | 71 | public func ivar(named name: String) -> Ivar! { 72 | return self.ivars.filter { $0.name == name }.first 73 | } 74 | 75 | public func property(named name: String) -> Property! { 76 | return self.properties.filter { $0.name == name }.first 77 | } 78 | 79 | public func conforms(to protocol: String) -> Bool { 80 | return !self.protocols.filter { $0.name == name }.isEmpty 81 | } 82 | 83 | public func getIvarOffset(_ name: String) -> Int! { 84 | return self.ivar(named: name)?.offset 85 | } 86 | 87 | public func getMethodIMP(_ sel: SEL) -> IMP? { 88 | return self.method(named: sel)?.imp 89 | } 90 | 91 | public static func isClass(_ object: id) -> Bool { 92 | if object|.isa == nil { 93 | // Metaclass.isa -> nil 94 | return true 95 | } else if object|.isa.isa == nil { 96 | // Class.isa -> metaclass.isa -> nil 97 | return true 98 | } else { 99 | // Object.isa -> Class.isa -> metaclass.isa -> nil 100 | return false 101 | } 102 | } 103 | } 104 | 105 | public extension Class { 106 | static var classList: [Class] = [] 107 | 108 | public static func named(_ name: String) -> Class! { 109 | return Class.classList.filter { $0.name == name }.first 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Runtime/Pointer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Pointer.swift 3 | // MirrorKit.swift 4 | // 5 | // Created by Tanner on 8/25/17. 6 | // 7 | 8 | import Foundation 9 | 10 | extension UnsafeMutableRawPointer { 11 | @inline(__always) 12 | init(to thing: inout T) { 13 | self = withUnsafeMutablePointer(to: &thing) { UnsafeMutableRawPointer($0) } 14 | } 15 | } 16 | 17 | prefix operator ~ 18 | public prefix func ~(thing: T) -> U { 19 | return unsafeBitCast(thing, to: U.self) 20 | } 21 | 22 | infix operator |= 23 | /// Shorthand ivar set 24 | public func |=(pointer: id, change: (value: T, ivar: String)) { 25 | let offset = pointer|.getClass.getIvarOffset(change.ivar)! 26 | let pointer: Pointer = ~pointer + offset 27 | pointer.pointee = change.value 28 | } 29 | 30 | infix operator | 31 | /// Shorthand ivar get 32 | public func |(pointer: id, ivar: String) -> T { 33 | let offset = pointer|.getClass.getIvarOffset(ivar)! 34 | let ivarPtr: Pointer = ~(pointer + offset) 35 | return ivarPtr.pointee 36 | } 37 | 38 | postfix operator | 39 | /// Shorthand for .pointee 40 | public postfix func |(pointer: Pointer) -> T { 41 | return pointer.pointee 42 | } 43 | 44 | prefix operator | 45 | /// Shorthand for .pointee 46 | @inline(__always) 47 | public prefix func |(pointee: inout T) -> Pointer { 48 | return Pointer(to: &pointee) 49 | } 50 | 51 | public struct Pointer: Strideable, Hashable, Equatable { 52 | 53 | // MARK: Public 54 | 55 | public let raw: UnsafeMutableRawPointer 56 | public var pointee: Pointee { 57 | get { return self.read() } 58 | nonmutating set { return self.write(newValue) } 59 | } 60 | 61 | // Get a Pointer to some variable 62 | public init(to thing: inout T) { 63 | self.raw = UnsafeMutableRawPointer(to: &thing) 64 | } 65 | 66 | // Convert some variable, which is already a "pointer" itself, into a Pointer 67 | public init(from pointer: inout Any) { 68 | self.raw = ~pointer 69 | } 70 | 71 | public func read(byteOffset: Int = 0) -> T { 72 | return self.raw.load(fromByteOffset: byteOffset, as: T.self) 73 | } 74 | 75 | public func write(_ value: T, byteOffset: Int = 0) { 76 | self.raw.storeBytes(of: value, toByteOffset: byteOffset, as: T.self) 77 | } 78 | 79 | // MARK: Memory management 80 | 81 | public static func alloc(_ count: Int) -> Pointer { 82 | return Pointer(raw: UnsafeMutablePointer.allocate(capacity: count)) 83 | } 84 | 85 | public static func calloc(_ count: Int) -> Pointer { 86 | #if os(Linux) 87 | return Pointer(raw: Glibc.calloc(count, 1)) 88 | #else 89 | return Pointer(raw: Darwin.calloc(count, 1)) 90 | #endif 91 | } 92 | 93 | public func free() { 94 | #if os(Linux) 95 | Glibc.free(self.raw) 96 | #else 97 | Darwin.free(self.raw) 98 | #endif 99 | } 100 | 101 | public func free(_ count: Int) { 102 | self.raw.deallocate(bytes: count, alignedTo: MemoryLayout.alignment) 103 | } 104 | 105 | // MARK: Private 106 | 107 | init(raw pointer: UnsafeMutableRawPointer) { 108 | self.raw = pointer 109 | } 110 | 111 | // MARK: Strideable 112 | 113 | public func distance(to other: Pointer) -> Int { 114 | return self.raw.distance(to: other.raw) 115 | } 116 | 117 | public func advanced(by n: Int) -> Pointer { 118 | return Pointer(raw: self.raw.advanced(by: n)) 119 | } 120 | 121 | // MARK: Hashable 122 | 123 | public var hashValue: Int { 124 | return self.raw.hashValue 125 | } 126 | 127 | // MARK: Convenience 128 | 129 | public static func +(lhs: Pointer, rhs: Int) -> Pointer { 130 | return lhs.advanced(by: rhs) 131 | } 132 | 133 | public static func -(lhs: Pointer, rhs: Int) -> Pointer { 134 | return lhs.advanced(by: -rhs) 135 | } 136 | 137 | public static func +=(lhs: inout Pointer, rhs: Int) { 138 | lhs = lhs.advanced(by: rhs) 139 | } 140 | 141 | public static func -=(lhs: inout Pointer, rhs: Int) { 142 | lhs = lhs.advanced(by: -rhs) 143 | } 144 | 145 | public static postfix func ++(pointer: inout Pointer) { 146 | pointer = pointer.advanced(by: 1) 147 | } 148 | 149 | public static postfix func --(pointer: inout Pointer) { 150 | pointer = pointer.advanced(by: -1) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Runtime 2 | 3 | An Objective-C simulator written in Swift. 4 | 5 | ## Goals 6 | 7 | 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. 8 | 9 | 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. 10 | 11 | ## Features 12 | 13 | - Dynamic method dispatch 14 | - Method swizzling / replacing 15 | - Creating entire classes at runtime 16 | - Non-fragile ivars 17 | 18 | See `Person.Swift` for an examples of everything mentioned in the readme. 19 | 20 | ## Overview 21 | 22 | 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. 23 | 24 | ### Defining classes 25 | 26 | 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. 27 | 28 | ```swift 29 | struct Person { 30 | static let `class` = Class( 31 | isa: Person_meta.class, 32 | superclass: RootObject.class, 33 | name: "Person", 34 | ivars: [ 35 | (name: "_name", type: .string), 36 | (name: "_age", type: .integer) 37 | ], 38 | methods: [_init, name, setName_, age, setAge_, description], 39 | properties: [ 40 | Property(name: "name", getter: name, setter: setName_), 41 | Property(name: "age", getter: age, setter: setAge_), 42 | ], 43 | protocols: [] 44 | ) 45 | 46 | // Methods go here as static vars 47 | 48 | static var _init = Method("init", returns: .object("this")) { this, _cmd, args in 49 | func init$(_ this: id, _ _cmd: SEL) -> id { 50 | _msgSend(this, "setName_", ("Bob")) 51 | _msgSend(this, "setAge_", (18)) 52 | 53 | return msgSend(super: true, this, _cmd) 54 | } 55 | 56 | return init$(this, _cmd) 57 | } 58 | 59 | static var name = Method("name", returns: .string) ... 60 | ... 61 | } 62 | 63 | private struct Person_meta { 64 | static let `class` = Class( 65 | isa: nil, 66 | superclass: nil, 67 | name: "Person.meta", 68 | ... 69 | ) 70 | } 71 | ``` 72 | 73 | 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. 74 | 75 | `isa:` should be the class's metaclass (or `nil` if the class is a metaclass itself). `superclass:` should be the superclass. 76 | 77 | #### The Metaclass 78 | 79 | 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`. 80 | 81 | Each metaclass can be looked up by using `Class.named("Foo").isa` or directly by name with `Class.named("Foo.meta")`. 82 | 83 | #### Methods 84 | ###### Declaration 85 | 86 | `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. 87 | 88 | ###### Method.init() structure 89 | 90 | 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. 91 | 92 | ###### IMP arguments 93 | 94 | 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. 95 | 96 | ###### Implementation conventions 97 | 98 | 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: 99 | 100 | ```swift 101 | static var add__ = Method(…) { this, _cmd, args in 102 | // Actual implementation and type signature of method 103 | func add__$(_ this: id, _ _cmd: SEL, a: Int, b: Int) -> Int { 104 | return a + b 105 | } 106 | 107 | // Cast out arguments and call method 108 | let args = args as! (Int, Int) 109 | return add__$(this, _cmd, args.0, args.1) 110 | } 111 | ``` 112 | 113 | Arguments must be cast from `Any` to their actual types as a tuple before being used. 114 | 115 | ###### Overriding methods 116 | 117 | 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`: 118 | 119 | ```swift 120 | static var _init = Method("init", ...) { this, _cmd, args in 121 | func init$(_ this: id, _ _cmd: SEL) -> id { 122 | return msgSend(super: true, this, _cmd) 123 | print("init override: \(this)") 124 | } 125 | 126 | return init$(this, _cmd) 127 | } 128 | ``` 129 | 130 | ###### Init 131 | 132 | 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`. 133 | 134 | > 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. 135 | 136 | 137 | #### Instance variables 138 | 139 | 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. 140 | 141 | > Metaclasses can not have any instance variables; trying to use ivars on a metaclass is undefined behavior. 142 | 143 | #### Properties 144 | 145 | Properties take a name and one or two implementations. A property's `type` comes from its `getter`. 146 | 147 | -- 148 | 149 | ### Creating objects 150 | 151 | Instances of objects are allocated by calling `class.createInstance()`, i.e.: 152 | 153 | ```swift 154 | let instance1 = Person.class.createInstance() 155 | let instance2 = Class.named("Person").createInstance() 156 | ``` 157 | 158 | ### Calling methods 159 | 160 | 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. 161 | 162 | ```swift 163 | let bob: id = msgSend(Person.class.createInstance(), "init") 164 | let name: String = msgSend(bob, "name") 165 | let age: Int = msgSend(bob, "age") 166 | let description: String = msgSend(bob, "description") 167 | ``` 168 | 169 | ### Accessing ivars 170 | 171 | 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: 172 | 173 | ```swift 174 | let offset = this|.getClass.getIvarOffset("_someInt")! 175 | let pointer: Pointer = ~pointer + offset 176 | let ivarValue = pointer.pointee 177 | ``` 178 | 179 | `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. 180 | 181 | The above is still pretty convoluted and heavily repeated, so I've provided yet another operator which returns `ivarValue` above: 182 | 183 | ```swift 184 | let ivarValue: Int = this|"_someInt" 185 | ``` 186 | 187 | 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`: 188 | 189 | ```swift 190 | this |= (5, "_foo") 191 | ``` 192 | 193 | -- 194 | 195 | ### Type system "gotchas" 196 | 197 | #### You're stuck with `id` 198 | 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`). 199 | 200 | 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: 201 | 202 | ```swift 203 | struct Vehicle { 204 | let _super: Object 205 | let _capacity: Int 206 | ... 207 | 208 | static let `class` = Class(isa: ...) 209 | } 210 | 211 | /// Vehicle subclass 212 | struct Car { 213 | let _super: Vehicle 214 | let make: String 215 | let model: String 216 | let year: Int 217 | ... 218 | 219 | static let `class` = Class(isa: ...) 220 | } 221 | ``` 222 | 223 | Now, you could possibly do the following: 224 | 225 | ```swift 226 | let fiesta: Pointer = msgSend( 227 | Car.class.createInstance(), 228 | "init", 229 | ("Ford", "Fiesta", 2014, ...) 230 | ) 231 | fiesta.year = 2017 232 | ``` 233 | 234 | 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. 235 | 236 | #### Using `Class`es as objects 237 | 238 | `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`: 239 | 240 | ```swift 241 | _msgSend(Person.class.ref, "someClassMethod") 242 | ``` 243 | 244 | In general, use `class.ref` whenever you wish to treat a `Class` as an object. 245 | 246 | ### Other caveats 247 | 248 | `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: 249 | 250 | ```swift 251 | func runtimeInit() { 252 | // Runtime initialization 253 | _ = RootObject.class 254 | _ = Person.class 255 | ... 256 | } 257 | ``` 258 | 259 | 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! 260 | 261 | --- 262 | 263 | ## To-do 264 | 265 | - More tests 266 | - Zeroing deallocated references 267 | - Suggestions welcome! 268 | -------------------------------------------------------------------------------- /Runtime.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C35AEBBE1F987493008F2988 /* RuntimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C35AEBBD1F987493008F2988 /* RuntimeTests.swift */; }; 11 | C35AEBC01F987493008F2988 /* libRuntime.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C3F59BC81F981B41002494E4 /* libRuntime.a */; }; 12 | C37F4B741F9B279E000732E0 /* DebugDescriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C37F4B731F9B279E000732E0 /* DebugDescriptions.swift */; }; 13 | C3BEFCC11F994C7F005AD638 /* Person.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BEFCC01F994C7F005AD638 /* Person.swift */; }; 14 | C3BEFCC31F994CB9005AD638 /* RootObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BEFCC21F994CB9005AD638 /* RootObject.swift */; }; 15 | C3D55DF81F9C4535006DE12F /* ClassBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D55DF71F9C4535006DE12F /* ClassBuilder.swift */; }; 16 | C3D55DFA1F9E4642006DE12F /* OrderedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3D55DF91F9E4642006DE12F /* OrderedSet.swift */; }; 17 | C3F59BD51F981B8D002494E4 /* Class.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BD41F981B8D002494E4 /* Class.swift */; }; 18 | C3F59BD91F981C20002494E4 /* Pointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BD81F981C20002494E4 /* Pointer.swift */; }; 19 | C3F59BDB1F981C4D002494E4 /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BDA1F981C4D002494E4 /* Object.swift */; }; 20 | C3F59BDD1F981D8D002494E4 /* Ivar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BDC1F981D8D002494E4 /* Ivar.swift */; }; 21 | C3F59BDF1F981D93002494E4 /* Method.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BDE1F981D93002494E4 /* Method.swift */; }; 22 | C3F59BE11F981D9C002494E4 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE01F981D9C002494E4 /* Property.swift */; }; 23 | C3F59BE31F981DAD002494E4 /* Protocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE21F981DAD002494E4 /* Protocol.swift */; }; 24 | C3F59BE51F981E6F002494E4 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE41F981E6F002494E4 /* Types.swift */; }; 25 | C3F59BE71F98229C002494E4 /* Struct.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE61F98229C002494E4 /* Struct.swift */; }; 26 | C3F59BE91F9827BD002494E4 /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BE81F9827BD002494E4 /* Platform.swift */; }; 27 | C3F59BEB1F982B80002494E4 /* Runtime.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F59BEA1F982B80002494E4 /* Runtime.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | C35AEBC11F987493008F2988 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = C3F59BC01F981B41002494E4 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = C3F59BC71F981B41002494E4; 36 | remoteInfo = Runtime; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXCopyFilesBuildPhase section */ 41 | C3F59BC61F981B41002494E4 /* CopyFiles */ = { 42 | isa = PBXCopyFilesBuildPhase; 43 | buildActionMask = 2147483647; 44 | dstPath = "include/$(PRODUCT_NAME)"; 45 | dstSubfolderSpec = 16; 46 | files = ( 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXCopyFilesBuildPhase section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | C35AEBBB1F987493008F2988 /* RuntimeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RuntimeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | C35AEBBD1F987493008F2988 /* RuntimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeTests.swift; sourceTree = ""; }; 55 | C35AEBBF1F987493008F2988 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | C37F4B731F9B279E000732E0 /* DebugDescriptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugDescriptions.swift; sourceTree = ""; }; 57 | C3BEFCC01F994C7F005AD638 /* Person.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Person.swift; sourceTree = ""; }; 58 | C3BEFCC21F994CB9005AD638 /* RootObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootObject.swift; sourceTree = ""; }; 59 | C3D55DF71F9C4535006DE12F /* ClassBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassBuilder.swift; sourceTree = ""; }; 60 | C3D55DF91F9E4642006DE12F /* OrderedSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedSet.swift; sourceTree = ""; }; 61 | C3F59BC81F981B41002494E4 /* libRuntime.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRuntime.a; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | C3F59BD41F981B8D002494E4 /* Class.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Class.swift; sourceTree = ""; }; 63 | C3F59BD81F981C20002494E4 /* Pointer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pointer.swift; sourceTree = ""; }; 64 | C3F59BDA1F981C4D002494E4 /* Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Object.swift; sourceTree = ""; }; 65 | C3F59BDC1F981D8D002494E4 /* Ivar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ivar.swift; sourceTree = ""; }; 66 | C3F59BDE1F981D93002494E4 /* Method.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Method.swift; sourceTree = ""; }; 67 | C3F59BE01F981D9C002494E4 /* Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; 68 | C3F59BE21F981DAD002494E4 /* Protocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocol.swift; sourceTree = ""; }; 69 | C3F59BE41F981E6F002494E4 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = ""; }; 70 | C3F59BE61F98229C002494E4 /* Struct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Struct.swift; sourceTree = ""; }; 71 | C3F59BE81F9827BD002494E4 /* Platform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.swift; sourceTree = ""; }; 72 | C3F59BEA1F982B80002494E4 /* Runtime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Runtime.swift; sourceTree = ""; }; 73 | /* End PBXFileReference section */ 74 | 75 | /* Begin PBXFrameworksBuildPhase section */ 76 | C35AEBB81F987493008F2988 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | C35AEBC01F987493008F2988 /* libRuntime.a in Frameworks */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | C3F59BC51F981B41002494E4 /* Frameworks */ = { 85 | isa = PBXFrameworksBuildPhase; 86 | buildActionMask = 2147483647; 87 | files = ( 88 | ); 89 | runOnlyForDeploymentPostprocessing = 0; 90 | }; 91 | /* End PBXFrameworksBuildPhase section */ 92 | 93 | /* Begin PBXGroup section */ 94 | C35AEBBC1F987493008F2988 /* RuntimeTests */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | C35AEBBD1F987493008F2988 /* RuntimeTests.swift */, 98 | C35AEBBF1F987493008F2988 /* Info.plist */, 99 | C3BEFCC01F994C7F005AD638 /* Person.swift */, 100 | ); 101 | path = RuntimeTests; 102 | sourceTree = ""; 103 | }; 104 | C3F59BBF1F981B41002494E4 = { 105 | isa = PBXGroup; 106 | children = ( 107 | C3F59BCA1F981B41002494E4 /* Runtime */, 108 | C35AEBBC1F987493008F2988 /* RuntimeTests */, 109 | C3F59BC91F981B41002494E4 /* Products */, 110 | ); 111 | sourceTree = ""; 112 | }; 113 | C3F59BC91F981B41002494E4 /* Products */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | C3F59BC81F981B41002494E4 /* libRuntime.a */, 117 | C35AEBBB1F987493008F2988 /* RuntimeTests.xctest */, 118 | ); 119 | name = Products; 120 | sourceTree = ""; 121 | }; 122 | C3F59BCA1F981B41002494E4 /* Runtime */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | C3F59BEA1F982B80002494E4 /* Runtime.swift */, 126 | C3F59BE81F9827BD002494E4 /* Platform.swift */, 127 | C3F59BE41F981E6F002494E4 /* Types.swift */, 128 | C3F59BD81F981C20002494E4 /* Pointer.swift */, 129 | C3F59BD41F981B8D002494E4 /* Class.swift */, 130 | C3D55DF71F9C4535006DE12F /* ClassBuilder.swift */, 131 | C3F59BE61F98229C002494E4 /* Struct.swift */, 132 | C3F59BDC1F981D8D002494E4 /* Ivar.swift */, 133 | C3F59BDE1F981D93002494E4 /* Method.swift */, 134 | C3F59BE01F981D9C002494E4 /* Property.swift */, 135 | C3F59BE21F981DAD002494E4 /* Protocol.swift */, 136 | C3F59BDA1F981C4D002494E4 /* Object.swift */, 137 | C3BEFCC21F994CB9005AD638 /* RootObject.swift */, 138 | C37F4B731F9B279E000732E0 /* DebugDescriptions.swift */, 139 | C3D55DF91F9E4642006DE12F /* OrderedSet.swift */, 140 | ); 141 | path = Runtime; 142 | sourceTree = ""; 143 | }; 144 | /* End PBXGroup section */ 145 | 146 | /* Begin PBXNativeTarget section */ 147 | C35AEBBA1F987493008F2988 /* RuntimeTests */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = C35AEBC51F987493008F2988 /* Build configuration list for PBXNativeTarget "RuntimeTests" */; 150 | buildPhases = ( 151 | C35AEBB71F987493008F2988 /* Sources */, 152 | C35AEBB81F987493008F2988 /* Frameworks */, 153 | C35AEBB91F987493008F2988 /* Resources */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | C35AEBC21F987493008F2988 /* PBXTargetDependency */, 159 | ); 160 | name = RuntimeTests; 161 | productName = RuntimeTests; 162 | productReference = C35AEBBB1F987493008F2988 /* RuntimeTests.xctest */; 163 | productType = "com.apple.product-type.bundle.unit-test"; 164 | }; 165 | C3F59BC71F981B41002494E4 /* Runtime */ = { 166 | isa = PBXNativeTarget; 167 | buildConfigurationList = C3F59BD11F981B41002494E4 /* Build configuration list for PBXNativeTarget "Runtime" */; 168 | buildPhases = ( 169 | C3F59BC41F981B41002494E4 /* Sources */, 170 | C3F59BC51F981B41002494E4 /* Frameworks */, 171 | C3F59BC61F981B41002494E4 /* CopyFiles */, 172 | ); 173 | buildRules = ( 174 | ); 175 | dependencies = ( 176 | ); 177 | name = Runtime; 178 | productName = Runtime; 179 | productReference = C3F59BC81F981B41002494E4 /* libRuntime.a */; 180 | productType = "com.apple.product-type.library.static"; 181 | }; 182 | /* End PBXNativeTarget section */ 183 | 184 | /* Begin PBXProject section */ 185 | C3F59BC01F981B41002494E4 /* Project object */ = { 186 | isa = PBXProject; 187 | attributes = { 188 | LastSwiftUpdateCheck = 0900; 189 | LastUpgradeCheck = 0910; 190 | ORGANIZATIONNAME = "Tanner Bennett"; 191 | TargetAttributes = { 192 | C35AEBBA1F987493008F2988 = { 193 | CreatedOnToolsVersion = 9.0; 194 | ProvisioningStyle = Automatic; 195 | }; 196 | C3F59BC71F981B41002494E4 = { 197 | CreatedOnToolsVersion = 9.0; 198 | LastSwiftMigration = 0900; 199 | ProvisioningStyle = Automatic; 200 | }; 201 | }; 202 | }; 203 | buildConfigurationList = C3F59BC31F981B41002494E4 /* Build configuration list for PBXProject "Runtime" */; 204 | compatibilityVersion = "Xcode 8.0"; 205 | developmentRegion = en; 206 | hasScannedForEncodings = 0; 207 | knownRegions = ( 208 | en, 209 | ); 210 | mainGroup = C3F59BBF1F981B41002494E4; 211 | productRefGroup = C3F59BC91F981B41002494E4 /* Products */; 212 | projectDirPath = ""; 213 | projectRoot = ""; 214 | targets = ( 215 | C3F59BC71F981B41002494E4 /* Runtime */, 216 | C35AEBBA1F987493008F2988 /* RuntimeTests */, 217 | ); 218 | }; 219 | /* End PBXProject section */ 220 | 221 | /* Begin PBXResourcesBuildPhase section */ 222 | C35AEBB91F987493008F2988 /* Resources */ = { 223 | isa = PBXResourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | /* End PBXResourcesBuildPhase section */ 230 | 231 | /* Begin PBXSourcesBuildPhase section */ 232 | C35AEBB71F987493008F2988 /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | C3BEFCC11F994C7F005AD638 /* Person.swift in Sources */, 237 | C35AEBBE1F987493008F2988 /* RuntimeTests.swift in Sources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | C3F59BC41F981B41002494E4 /* Sources */ = { 242 | isa = PBXSourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | C3F59BE91F9827BD002494E4 /* Platform.swift in Sources */, 246 | C3F59BE11F981D9C002494E4 /* Property.swift in Sources */, 247 | C3BEFCC31F994CB9005AD638 /* RootObject.swift in Sources */, 248 | C3F59BDF1F981D93002494E4 /* Method.swift in Sources */, 249 | C37F4B741F9B279E000732E0 /* DebugDescriptions.swift in Sources */, 250 | C3F59BD51F981B8D002494E4 /* Class.swift in Sources */, 251 | C3F59BE71F98229C002494E4 /* Struct.swift in Sources */, 252 | C3F59BE51F981E6F002494E4 /* Types.swift in Sources */, 253 | C3F59BEB1F982B80002494E4 /* Runtime.swift in Sources */, 254 | C3D55DFA1F9E4642006DE12F /* OrderedSet.swift in Sources */, 255 | C3F59BE31F981DAD002494E4 /* Protocol.swift in Sources */, 256 | C3D55DF81F9C4535006DE12F /* ClassBuilder.swift in Sources */, 257 | C3F59BDB1F981C4D002494E4 /* Object.swift in Sources */, 258 | C3F59BD91F981C20002494E4 /* Pointer.swift in Sources */, 259 | C3F59BDD1F981D8D002494E4 /* Ivar.swift in Sources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | /* End PBXSourcesBuildPhase section */ 264 | 265 | /* Begin PBXTargetDependency section */ 266 | C35AEBC21F987493008F2988 /* PBXTargetDependency */ = { 267 | isa = PBXTargetDependency; 268 | target = C3F59BC71F981B41002494E4 /* Runtime */; 269 | targetProxy = C35AEBC11F987493008F2988 /* PBXContainerItemProxy */; 270 | }; 271 | /* End PBXTargetDependency section */ 272 | 273 | /* Begin XCBuildConfiguration section */ 274 | C35AEBC31F987493008F2988 /* Debug */ = { 275 | isa = XCBuildConfiguration; 276 | buildSettings = { 277 | CODE_SIGN_STYLE = Automatic; 278 | DEVELOPMENT_TEAM = S6N2F22V2Z; 279 | INFOPLIST_FILE = RuntimeTests/Info.plist; 280 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 281 | PRODUCT_BUNDLE_IDENTIFIER = com.nsexceptional.RuntimeTests; 282 | PRODUCT_NAME = "$(TARGET_NAME)"; 283 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 284 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 285 | SWIFT_VERSION = 4.0; 286 | TARGETED_DEVICE_FAMILY = "1,2"; 287 | }; 288 | name = Debug; 289 | }; 290 | C35AEBC41F987493008F2988 /* Release */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | CODE_SIGN_STYLE = Automatic; 294 | DEVELOPMENT_TEAM = S6N2F22V2Z; 295 | INFOPLIST_FILE = RuntimeTests/Info.plist; 296 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 297 | PRODUCT_BUNDLE_IDENTIFIER = com.nsexceptional.RuntimeTests; 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 300 | SWIFT_VERSION = 4.0; 301 | TARGETED_DEVICE_FAMILY = "1,2"; 302 | }; 303 | name = Release; 304 | }; 305 | C3F59BCF1F981B41002494E4 /* Debug */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ALWAYS_SEARCH_USER_PATHS = NO; 309 | CLANG_ANALYZER_NONNULL = YES; 310 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 312 | CLANG_CXX_LIBRARY = "libc++"; 313 | CLANG_ENABLE_MODULES = YES; 314 | CLANG_ENABLE_OBJC_ARC = YES; 315 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 316 | CLANG_WARN_BOOL_CONVERSION = YES; 317 | CLANG_WARN_COMMA = YES; 318 | CLANG_WARN_CONSTANT_CONVERSION = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INFINITE_RECURSION = YES; 324 | CLANG_WARN_INT_CONVERSION = YES; 325 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 329 | CLANG_WARN_STRICT_PROTOTYPES = YES; 330 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 331 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 332 | CLANG_WARN_UNREACHABLE_CODE = YES; 333 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 334 | CODE_SIGN_IDENTITY = "iPhone Developer"; 335 | COPY_PHASE_STRIP = NO; 336 | DEBUG_INFORMATION_FORMAT = dwarf; 337 | ENABLE_STRICT_OBJC_MSGSEND = YES; 338 | ENABLE_TESTABILITY = YES; 339 | GCC_C_LANGUAGE_STANDARD = gnu11; 340 | GCC_DYNAMIC_NO_PIC = NO; 341 | GCC_NO_COMMON_BLOCKS = YES; 342 | GCC_OPTIMIZATION_LEVEL = 0; 343 | GCC_PREPROCESSOR_DEFINITIONS = ( 344 | "DEBUG=1", 345 | "$(inherited)", 346 | ); 347 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 349 | GCC_WARN_UNDECLARED_SELECTOR = YES; 350 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 351 | GCC_WARN_UNUSED_FUNCTION = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 354 | MTL_ENABLE_DEBUG_INFO = YES; 355 | ONLY_ACTIVE_ARCH = YES; 356 | SDKROOT = iphoneos; 357 | }; 358 | name = Debug; 359 | }; 360 | C3F59BD01F981B41002494E4 /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_ANALYZER_NONNULL = YES; 365 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 366 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 367 | CLANG_CXX_LIBRARY = "libc++"; 368 | CLANG_ENABLE_MODULES = YES; 369 | CLANG_ENABLE_OBJC_ARC = YES; 370 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 371 | CLANG_WARN_BOOL_CONVERSION = YES; 372 | CLANG_WARN_COMMA = YES; 373 | CLANG_WARN_CONSTANT_CONVERSION = YES; 374 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 375 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 376 | CLANG_WARN_EMPTY_BODY = YES; 377 | CLANG_WARN_ENUM_CONVERSION = YES; 378 | CLANG_WARN_INFINITE_RECURSION = YES; 379 | CLANG_WARN_INT_CONVERSION = YES; 380 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 381 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 382 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 383 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 384 | CLANG_WARN_STRICT_PROTOTYPES = YES; 385 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 386 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 387 | CLANG_WARN_UNREACHABLE_CODE = YES; 388 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 389 | CODE_SIGN_IDENTITY = "iPhone Developer"; 390 | COPY_PHASE_STRIP = NO; 391 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 392 | ENABLE_NS_ASSERTIONS = NO; 393 | ENABLE_STRICT_OBJC_MSGSEND = YES; 394 | GCC_C_LANGUAGE_STANDARD = gnu11; 395 | GCC_NO_COMMON_BLOCKS = YES; 396 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 397 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 398 | GCC_WARN_UNDECLARED_SELECTOR = YES; 399 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 400 | GCC_WARN_UNUSED_FUNCTION = YES; 401 | GCC_WARN_UNUSED_VARIABLE = YES; 402 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 403 | MTL_ENABLE_DEBUG_INFO = NO; 404 | SDKROOT = iphoneos; 405 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 406 | VALIDATE_PRODUCT = YES; 407 | }; 408 | name = Release; 409 | }; 410 | C3F59BD21F981B41002494E4 /* Debug */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | CLANG_ENABLE_MODULES = YES; 414 | CODE_SIGN_STYLE = Automatic; 415 | DEVELOPMENT_TEAM = S6N2F22V2Z; 416 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 417 | OTHER_LDFLAGS = "-ObjC"; 418 | PRODUCT_NAME = "$(TARGET_NAME)"; 419 | SKIP_INSTALL = YES; 420 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 421 | SWIFT_VERSION = 3.0; 422 | TARGETED_DEVICE_FAMILY = "1,2"; 423 | }; 424 | name = Debug; 425 | }; 426 | C3F59BD31F981B41002494E4 /* Release */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | CLANG_ENABLE_MODULES = YES; 430 | CODE_SIGN_STYLE = Automatic; 431 | DEVELOPMENT_TEAM = S6N2F22V2Z; 432 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 433 | OTHER_LDFLAGS = "-ObjC"; 434 | PRODUCT_NAME = "$(TARGET_NAME)"; 435 | SKIP_INSTALL = YES; 436 | SWIFT_VERSION = 3.0; 437 | TARGETED_DEVICE_FAMILY = "1,2"; 438 | }; 439 | name = Release; 440 | }; 441 | /* End XCBuildConfiguration section */ 442 | 443 | /* Begin XCConfigurationList section */ 444 | C35AEBC51F987493008F2988 /* Build configuration list for PBXNativeTarget "RuntimeTests" */ = { 445 | isa = XCConfigurationList; 446 | buildConfigurations = ( 447 | C35AEBC31F987493008F2988 /* Debug */, 448 | C35AEBC41F987493008F2988 /* Release */, 449 | ); 450 | defaultConfigurationIsVisible = 0; 451 | defaultConfigurationName = Release; 452 | }; 453 | C3F59BC31F981B41002494E4 /* Build configuration list for PBXProject "Runtime" */ = { 454 | isa = XCConfigurationList; 455 | buildConfigurations = ( 456 | C3F59BCF1F981B41002494E4 /* Debug */, 457 | C3F59BD01F981B41002494E4 /* Release */, 458 | ); 459 | defaultConfigurationIsVisible = 0; 460 | defaultConfigurationName = Release; 461 | }; 462 | C3F59BD11F981B41002494E4 /* Build configuration list for PBXNativeTarget "Runtime" */ = { 463 | isa = XCConfigurationList; 464 | buildConfigurations = ( 465 | C3F59BD21F981B41002494E4 /* Debug */, 466 | C3F59BD31F981B41002494E4 /* Release */, 467 | ); 468 | defaultConfigurationIsVisible = 0; 469 | defaultConfigurationName = Release; 470 | }; 471 | /* End XCConfigurationList section */ 472 | }; 473 | rootObject = C3F59BC01F981B41002494E4 /* Project object */; 474 | } 475 | --------------------------------------------------------------------------------