-
Notifications
You must be signed in to change notification settings - Fork 395
/
ObjCRuntime.java
353 lines (268 loc) · 13 KB
/
ObjCRuntime.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
* Copyright (C) 2012 RoboVM AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.robovm.objc;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.robovm.rt.VM;
import org.robovm.rt.bro.Bro;
import org.robovm.rt.bro.Runtime;
import org.robovm.rt.bro.Struct;
import org.robovm.rt.bro.annotation.Bridge;
import org.robovm.rt.bro.annotation.ByVal;
import org.robovm.rt.bro.annotation.Library;
import org.robovm.rt.bro.annotation.Pointer;
import org.robovm.rt.bro.annotation.StructRet;
import org.robovm.rt.bro.ptr.BytePtr;
/**
*
*/
@Library("objc")
public class ObjCRuntime {
private static final Map<Class<?>, Integer> structSizes = new HashMap<Class<?>, Integer>();
static {
Bro.bind(ObjCRuntime.class);
}
public static void bind() {
Class<?> caller = VM.getStackClasses(0, 1)[0];
bind(caller);
}
public static void bind(Class<?> c) {
for (Method method : c.getDeclaredMethods()) {
Bridge bridge = method.getAnnotation(Bridge.class);
if (bridge != null && (bridge.symbol() == null || "".equals(bridge.symbol()))) {
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length >= 2) {
// An ObjC method takes at least 2 parameters (self, selector)
// or (super, selector).
if (paramTypes[1] == Selector.class) {
String symbol = null;
if (paramTypes[0] == ObjCSuper.class) {
symbol = "objc_msgSendSuper";
} else if (ObjCObject.class.isAssignableFrom(paramTypes[0])) {
// self should be an instance of ObjCObject
symbol = "objc_msgSend";
} else if (paramTypes[0] == long.class) {
// Also allow @Pointer long as type of self
Annotation[] paramAnnos = method.getParameterAnnotations()[0];
for (Annotation a : paramAnnos) {
if (a.annotationType() == Pointer.class) {
symbol = "objc_msgSend";
break;
}
}
}
if (symbol != null) {
// So this is a bridge to an ObjC method. If the method
// returns a struct by value we may have to use the
// special stret versions of objc_msgSend/objc_msgSendSuper.
if (isStret(method)) {
symbol += "_stret";
}
long f = Runtime.resolveBridge("objc", symbol, method);
VM.bindBridgeMethod(method, f);
}
}
}
}
}
Bro.bind(c);
}
static boolean isStret(Method method) {
Class<?> returnType = method.getReturnType();
if (returnType.getSuperclass() == Struct.class
&& (method.getAnnotation(ByVal.class) != null
|| returnType.getAnnotation(ByVal.class) != null)) {
int structSize = getStructSize(returnType);
if (Bro.IS_X86 && Bro.IS_32BIT) {
if (structSize > 2 && structSize != 4 && structSize != 8) {
// On x86 stret has to be used for all structs except
// of size 1, 2, 4 and 8 bytes.
return true;
}
} else if (Bro.IS_X86 && Bro.IS_64BIT) {
if (structSize > 16) {
return true;
}
} else if (Bro.IS_ARM && Bro.IS_32BIT) {
if (structSize > 4) {
// On ARM stret has to be used for structs
// larger than 4 bytes
return true;
}
} else if (Bro.IS_ARM && Bro.IS_64BIT) {
// iOS ARM64 doesn't have objc_msgSend_stret
return false;
} else {
throw new Error("Unsupported architecture");
}
}
return false;
}
static synchronized int getStructSize(Class<?> cls) {
Integer size = structSizes.get(cls);
if (size == null) {
try {
Method sizeOf = cls.getMethod("sizeOf");
size = (Integer) sizeOf.invoke(null);
} catch (Exception e) {
throw new Error(e);
}
structSizes.put(cls, size);
}
return size;
}
/* selector */
@Bridge
public static native BytePtr sel_getName(Selector sel);
@Bridge
public static native Selector sel_registerName(BytePtr str);
/* objc */
@Bridge
public static native @Pointer long objc_getClass(@Pointer long name);
@Bridge
public static native @Pointer long objc_getMetaClass(@Pointer long name);
@Bridge
public static native @Pointer long objc_allocateClassPair(@Pointer long superclass, @Pointer long name, @Pointer long extraBytes);
@Bridge
public static native void objc_registerClassPair(@Pointer long cls);
@Bridge
public static native @Pointer long objc_getProtocol(@Pointer long name);
@Bridge
public static native @Pointer long objc_allocateProtocol(@Pointer long name);
@Bridge
public static native void objc_registerProtocol(@Pointer long protocol);
@Bridge
public static native void objc_setAssociatedObject(@Pointer long object, @Pointer long key, @Pointer long value, int policy);
@Bridge
public static native @Pointer long objc_getAssociatedObject(@Pointer long object, @Pointer long key);
/* object */
@Bridge
public static native @Pointer long object_getClass(@Pointer long object);
@Bridge
public static native @Pointer long object_setClass(@Pointer long object, @Pointer long cls);
@Bridge
public static native @Pointer long object_getClassName(@Pointer long object);
@Bridge
public static native @Pointer long object_dispose(@Pointer long object);
@Bridge
public static native @Pointer long object_getIvar(@Pointer long object, @Pointer long ivar);
@Bridge
public static native void object_setIvar(@Pointer long object, @Pointer long ivar, @Pointer long value);
/* class */
@Bridge
public static native @Pointer long class_getSuperclass(@Pointer long cls);
@Bridge
public static native @Pointer long class_isMetaClass(@Pointer long cls);
@Bridge
public static native @Pointer long class_getName(@Pointer long cls);
@Bridge
public static native @Pointer long class_getInstanceMethod(@Pointer long aClass, @Pointer long aSelector);
@Bridge
public static native @Pointer long class_getClassMethod(@Pointer long aClass, @Pointer long aSelector);
@Bridge
public static native boolean class_addProtocol(@Pointer long cls, @Pointer long protocol);
@Bridge
public static native boolean class_conformsToProtocol(@Pointer long cls, @Pointer long protocol);
@Bridge
public static native boolean class_addMethod(@Pointer long cls, @Pointer long name, @Pointer long imp, @Pointer long types);
@Bridge
public static native @Pointer long class_replaceMethod(@Pointer long cls, @Pointer long name, @Pointer long imp, @Pointer long types);
@Bridge
public static native @Pointer long class_getMethodImplementation(@Pointer long cls, @Pointer long name);
@Bridge
public static native boolean class_addIvar(@Pointer long cls, @Pointer long name, int size, byte alignment, @Pointer long types);
@Bridge
public static native @Pointer long class_getInstanceVariable(@Pointer long cls, @Pointer long name);
@Bridge
public static native @Pointer long class_getClassVariable(@Pointer long cls, @Pointer long name);
@Bridge
public static native @Pointer long class_getIvarLayout(@Pointer long cls);
@Bridge
public static native void class_setIvarLayout(@Pointer long cls, @Pointer long layout);
@Bridge
public static native @Pointer long class_getProperty(@Pointer long cls, @Pointer long name);
@Bridge
public static native boolean class_respondsToSelector(@Pointer long cls, @Pointer long sel);
/* protocol */
@Bridge
public static native @Pointer long protocol_getName(@Pointer long protocol);
@Bridge
public static native void protocol_addProtocol(@Pointer long protocol, @Pointer long addition);
@Bridge
public static native boolean protocol_conformsToProtocol(@Pointer long protocol, @Pointer long other);
@Bridge
public static native @Pointer long protocol_getProperty(@Pointer long protocol, @Pointer long name, boolean isRequiredProperty, boolean isInstanceProperty);
/* ivar */
@Bridge
public static native @Pointer long ivar_getName(@Pointer long ivar);
@Bridge
public static native @Pointer long ivar_getTypeEncoding(@Pointer long ivar);
@Bridge
public static native int ivar_getOffset(@Pointer long ivar);
/* method */
@Bridge
public static native @Pointer long method_getName(@Pointer long m);
@Bridge
public static native @Pointer long method_copyReturnType(@Pointer long m);
@Bridge
public static native @Pointer long method_copyArgumentType(@Pointer long m, int index);
@Bridge
public static native int method_getNumberOfArguments(@Pointer long m);
@Bridge
public static native @Pointer long method_getTypeEncoding(@Pointer long method);
@Bridge
public static native @Pointer long method_getImplementation(@Pointer long method);
@Bridge
public static native @Pointer long method_setImplementation(@Pointer long m, @Pointer long imp);
@Bridge
public static native void method_exchangeImplementations(@Pointer long m1, @Pointer long m2);
/* property */
@Bridge
public static native @Pointer long property_getName(@Pointer long property);
@Bridge
public static native BytePtr property_getAttributes(@Pointer long property);
@Bridge
public static native BytePtr property_copyAttributeValue(@Pointer long property, BytePtr attributeName);
/* message */
@Bridge(symbol = "objc_msgSend")
public static native @Pointer long ptr_objc_msgSend(@Pointer long receiver, @Pointer long selector);
@Bridge(symbol = "objc_msgSendSuper")
public static native @Pointer long ptr_objc_msgSendSuper(@Pointer long zuper, @Pointer long selector);
@Bridge(symbol = "objc_msgSend")
public static native void void_objc_msgSend(@Pointer long receiver, @Pointer long selector);
@Bridge(symbol = "objc_msgSendSuper")
public static native void void_objc_msgSendSuper(@Pointer long zuper, @Pointer long selector);
@Bridge(symbol = "objc_msgSend")
public static native boolean boolean_objc_msgSend(@Pointer long receiver, @Pointer long selector);
@Bridge(symbol = "objc_msgSend")
public static native void void_objc_msgSend_ptr(@Pointer long receiver, @Pointer long selector, @Pointer long ptr);
@Bridge(symbol = "objc_msgSend")
public static native void void_objc_msgSend_boolean(@Pointer long receiver, @Pointer long selector, boolean b);
@Bridge(symbol = "objc_msgSend")
public static native int int_objc_msgSend(@Pointer long receiver, @Pointer long selector);
@Bridge(symbol = "objc_msgSend")
public static native @Pointer long ptr_objc_msgSend_ptr(@Pointer long receiver, @Pointer long selector, @Pointer long ptr);
@Bridge(symbol = "objc_msgSend")
public static native @Pointer long ptr_objc_msgSend_int(@Pointer long receiver, @Pointer long selector, int i);
@Bridge(symbol = "objc_msgSend")
public static native @Pointer long ptr_objc_msgSend_long(@Pointer long receiver, @Pointer long selector, long l);
@Bridge(symbol = "objc_msgSend")
public static native char char_objc_msgSend_int(@Pointer long receiver, @Pointer long selector, int i);
@Bridge(symbol = "objc_msgSend_stret", optional = true) // Optional since it's not available on iOS ARM64
public static native void objc_msgSend_stret(@StructRet @Pointer long ret, @Pointer long receiver, @Pointer long selector);
}