Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8c3cc34

Browse files
committed
Initial commit
0 parents  commit8c3cc34

22 files changed

+1918
-0
lines changed

‎.gitignore‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
NetworkTrashFolder
47+
TemporaryItems
48+
.apdisk

‎README.md‎

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
#Runtime
2+
3+
An Objective-C simulator written in Swift.
4+
5+
##Goals
6+
7+
With few exceptions, this project aims to simulate what Objective-C code is translated to in assembly (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 do already by using`@objc` classes.
8+
9+
This project could theoretically be used as a dynamic runtime backend for a transpiled progamming language to transpile to, and as such, this framework and its conventions were crafted with that in mind. Many of the constructs here may seem like anything but type-safe, but it's all perfectly safe if the code is generated by some other, more type-safe language.
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+
```objc
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+
privatestruct 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+
staticvar add__=Method() { this, _cmd, argsin
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= argsas! (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+
staticvar _init=Method("init",...) { this, _cmd, argsin
121+
funcinit$(_ this: id,_ _cmd: SEL)->id {
122+
returnmsgSend(super:true, this, _cmd)
123+
print("init override:\(this)")
124+
}
125+
126+
returninit$(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<Int>=~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<Object>` 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<Vehicle>` for example, you could declare members on your`Vehicle` struct like so, alongside the`static let class` declaration:
201+
202+
```swift
203+
structVehicle {
204+
let _super: Object
205+
let _capacity:Int
206+
...
207+
208+
staticlet `class`=Class(isa:...)
209+
}
210+
211+
/// Vehicle subclass
212+
structCar {
213+
let _super: Vehicle
214+
let make:String
215+
let model:String
216+
let year:Int
217+
...
218+
219+
staticlet `class`=Class(isa:...)
220+
}
221+
```
222+
223+
Now, you could possibly do the following:
224+
225+
```swift
226+
let fiesta: Pointer<Car>=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+
In general, use `class.ref` whenever you wish to treat a `Class` as an object.
244+
245+
---
246+
247+
##To-do
248+
249+
- More tests
250+
- Zeroing deallocated references
251+
- Suggestions welcome!

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp