111
|
1 /* Test the Modern GNU Objective-C Runtime API.
|
|
2
|
|
3 This is test 'class', covering all functions starting with 'class'.
|
|
4 Tests calling the functions with a meta class as argument are covered
|
|
5 in the separate file, gnu-api-2-class-meta.m. */
|
|
6
|
|
7 /* { dg-do run } */
|
|
8 /* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */
|
|
9 /* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
|
|
10
|
|
11 /* To get the modern GNU Objective-C Runtime API, you include
|
|
12 objc/runtime.h. */
|
|
13 #include <objc/runtime.h>
|
|
14 #include <stdlib.h>
|
|
15 #include <stdio.h>
|
|
16 #include <string.h>
|
|
17
|
|
18 @interface MyRootClass
|
|
19 { Class isa; }
|
|
20 + alloc;
|
|
21 - init;
|
|
22 + initialize;
|
|
23 @end
|
|
24
|
|
25 @implementation MyRootClass
|
|
26 + alloc { return class_createInstance (self, 0); }
|
|
27 - init { return self; }
|
|
28 + initialize { return self; }
|
|
29 @end
|
|
30
|
|
31 @protocol MyProtocol
|
|
32 - (id) variable;
|
|
33 @end
|
|
34
|
|
35 @protocol MySecondProtocol
|
|
36 - (id) setVariable: (id)value;
|
|
37 @end
|
|
38
|
|
39 @interface MySubClass : MyRootClass <MyProtocol>
|
|
40 { id variable_ivar; }
|
|
41 - (void) setVariable: (id)value;
|
|
42 - (id) variable;
|
|
43 @end
|
|
44
|
|
45 @implementation MySubClass
|
|
46 - (void) setVariable: (id)value { variable_ivar = value; }
|
|
47 - (id) variable { return variable_ivar; }
|
|
48 @end
|
|
49
|
|
50 @interface MyOtherSubClass : MySubClass
|
|
51 @end
|
|
52
|
|
53 @implementation MyOtherSubClass
|
|
54 @end
|
|
55
|
|
56 @interface DifferentClass : MyRootClass
|
|
57 - (id) myClass;
|
|
58 - (id) self;
|
|
59 @end
|
|
60
|
|
61 @implementation DifferentClass
|
|
62 - (id) myClass { return object_getClass (self); }
|
|
63 - (id) self { return self; }
|
|
64 @end
|
|
65
|
|
66 @interface MySubClass (MySelf)
|
|
67 - (id) mySelf;
|
|
68 @end
|
|
69
|
|
70 /* Hack to calculate the log2 of a byte alignment. */
|
|
71 unsigned char
|
|
72 log_2_of (unsigned int x)
|
|
73 {
|
|
74 unsigned char result = 0;
|
|
75
|
|
76 /* We count how many times we need to divide by 2 before we reach 1.
|
|
77 This algorithm is good enough for the small numbers (such as 8,
|
|
78 16 or 64) that we have to deal with. */
|
|
79 while (x > 1)
|
|
80 {
|
|
81 x = x / 2;
|
|
82 result++;
|
|
83 }
|
|
84
|
|
85 return result;
|
|
86 }
|
|
87
|
|
88 int main(int argc, void **args)
|
|
89 {
|
|
90 /* Functions are tested in alphabetical order. */
|
|
91
|
|
92 printf ("Testing class_addIvar ()...\n");
|
|
93 {
|
|
94 Class new_class = objc_allocateClassPair (objc_getClass ("MySubClass"), "MySubSubClass", 0);
|
|
95
|
|
96 if (new_class == Nil)
|
|
97 abort ();
|
|
98
|
|
99 if (! class_addIvar (new_class, "variable2_ivar", sizeof (id),
|
|
100 log_2_of (__alignof__ (id)), @encode (id)))
|
|
101 abort ();
|
|
102
|
|
103 if (! class_addIvar (new_class, "variable3_ivar", sizeof (unsigned char),
|
|
104 log_2_of (__alignof__ (unsigned char)), @encode (unsigned char)))
|
|
105 abort ();
|
|
106
|
|
107 if (! class_addIvar (new_class, "variable4_ivar", sizeof (unsigned long),
|
|
108 log_2_of (__alignof__ (unsigned long)), @encode (unsigned long)))
|
|
109 abort ();
|
|
110
|
|
111 objc_registerClassPair (new_class);
|
|
112
|
|
113 {
|
|
114 MySubClass *o = [[(Class)objc_getClass ("MySubSubClass") alloc] init];
|
|
115 Ivar variable2 = class_getInstanceVariable (objc_getClass ("MySubSubClass"), "variable2_ivar");
|
|
116 Ivar variable3 = class_getInstanceVariable (objc_getClass ("MySubSubClass"), "variable3_ivar");
|
|
117 Ivar variable4 = class_getInstanceVariable (objc_getClass ("MySubSubClass"), "variable4_ivar");
|
|
118
|
|
119 if (variable2 == NULL || variable3 == NULL || variable4 == NULL)
|
|
120 abort ();
|
|
121
|
|
122 if (strcmp (ivar_getName (variable2), "variable2_ivar") != 0)
|
|
123 abort ();
|
|
124
|
|
125 if (strcmp (ivar_getName (variable3), "variable3_ivar") != 0)
|
|
126 abort ();
|
|
127
|
|
128 if (strcmp (ivar_getName (variable4), "variable4_ivar") != 0)
|
|
129 abort ();
|
|
130
|
|
131 {
|
|
132 unsigned char *var3 = (unsigned char *)((char *)o + ivar_getOffset (variable3));
|
|
133 unsigned long *var4 = (unsigned long *)((char *)o + ivar_getOffset (variable4));
|
|
134
|
|
135 object_setIvar (o, variable2, new_class);
|
|
136 *var3 = 230;
|
|
137 *var4 = 89000L;
|
|
138
|
|
139 if (object_getIvar (o, variable2) != new_class)
|
|
140 abort ();
|
|
141
|
|
142 if (*var3 != 230)
|
|
143 abort ();
|
|
144
|
|
145 if (*var4 != 89000L)
|
|
146 abort ();
|
|
147 }
|
|
148 }
|
|
149 }
|
|
150
|
|
151 printf ("Testing class_addMethod ()...\n");
|
|
152 {
|
|
153 Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass2", 0);
|
|
154 Method method1 = class_getInstanceMethod (objc_getClass ("MySubClass"), @selector (setVariable:));
|
|
155 Method method2 = class_getInstanceMethod (objc_getClass ("MySubClass"), @selector (variable));
|
|
156
|
|
157 if (new_class == Nil)
|
|
158 abort ();
|
|
159
|
|
160 if (! class_addIvar (new_class, "variable_ivar", sizeof (id),
|
|
161 log_2_of (__alignof__ (id)), @encode (id)))
|
|
162 abort ();
|
|
163
|
|
164 if (! class_addMethod (new_class, @selector (setVariable:), method_getImplementation (method1),
|
|
165 method_getTypeEncoding (method1)))
|
|
166 abort ();
|
|
167
|
|
168 if (! class_addMethod (new_class, @selector (variable), method_getImplementation (method2),
|
|
169 method_getTypeEncoding (method2)))
|
|
170 abort ();
|
|
171
|
|
172 /* Test that if the method already exists in the class,
|
|
173 class_addMethod() returns NO. */
|
|
174 if (class_addMethod (new_class, @selector (variable), method_getImplementation (method2),
|
|
175 method_getTypeEncoding (method2)))
|
|
176 abort ();
|
|
177
|
|
178 objc_registerClassPair (new_class);
|
|
179
|
|
180 /* Now, MySubClass2 is basically the same as MySubClass! We'll
|
|
181 use the variable and setVariable: methods on it. */
|
|
182 {
|
|
183 MySubClass *o = (MySubClass *)[[(Class)objc_getClass ("MySubClass2") alloc] init];
|
|
184
|
|
185 [o setVariable: o];
|
|
186
|
|
187 if ([o variable] != o)
|
|
188 abort ();
|
|
189 }
|
|
190
|
|
191 /* Now, try that if you take an existing class and try to add an
|
|
192 already existing method, class_addMethod returns NO. This is
|
|
193 subtly different from before, when 'new_class' was still in
|
|
194 construction. Now it's a real class and the libobjc internals
|
|
195 differ between the two cases. */
|
|
196 if (class_addMethod (new_class, @selector (variable), method_getImplementation (method2),
|
|
197 method_getTypeEncoding (method2)))
|
|
198 abort ();
|
|
199 }
|
|
200
|
|
201 printf ("Testing class_addProtocol ()...\n");
|
|
202 {
|
|
203 if (!class_addProtocol (objc_getClass ("MySubClass"), @protocol (MySecondProtocol)))
|
|
204 abort ();
|
|
205
|
|
206 if (!class_conformsToProtocol (objc_getClass ("MySubClass"), @protocol (MyProtocol)))
|
|
207 abort ();
|
|
208
|
|
209 if (!class_conformsToProtocol (objc_getClass ("MySubClass"), @protocol (MySecondProtocol)))
|
|
210 abort ();
|
|
211 }
|
|
212
|
|
213 printf ("Testing class_conformsToProtocol ()...\n");
|
|
214 {
|
|
215 if (class_conformsToProtocol (objc_getClass ("MyRootClass"), @protocol (MyProtocol)))
|
|
216 abort ();
|
|
217
|
|
218 if (!class_conformsToProtocol (objc_getClass ("MySubClass"), @protocol (MyProtocol)))
|
|
219 abort ();
|
|
220
|
|
221 /* Test that class_conformsToProtocol checks the class, but not
|
|
222 superclasses. */
|
|
223 if (class_conformsToProtocol (objc_getClass ("MyOtherSubClass"), @protocol (MyProtocol)))
|
|
224 abort ();
|
|
225 }
|
|
226
|
|
227 printf ("Testing class_copyIvarList ()...\n");
|
|
228 {
|
|
229 unsigned int count;
|
|
230 Ivar * list = class_copyIvarList (objc_getClass ("MySubClass"), &count);
|
|
231
|
|
232 if (count != 1)
|
|
233 abort ();
|
|
234
|
|
235 if (strcmp (ivar_getName (list[0]), "variable_ivar") != 0)
|
|
236 abort ();
|
|
237
|
|
238 if (list[1] != NULL)
|
|
239 abort ();
|
|
240 }
|
|
241
|
|
242 printf ("Testing class_copyIvarList () on class with no instance variables...\n");
|
|
243 {
|
|
244 unsigned int count;
|
|
245 Ivar * list = class_copyIvarList (objc_getClass ("MyOtherSubClass"),
|
|
246 &count);
|
|
247
|
|
248 if (count != 0)
|
|
249 abort ();
|
|
250
|
|
251 if (list != NULL)
|
|
252 abort ();
|
|
253 }
|
|
254
|
|
255 printf ("Testing class_copyMethodList ()...\n");
|
|
256 {
|
|
257 unsigned int count;
|
|
258 Method * list = class_copyMethodList (objc_getClass ("MySubClass"), &count);
|
|
259
|
|
260 if (count != 2)
|
|
261 abort ();
|
|
262
|
|
263 if (! ((strcmp (sel_getName (method_getName (list[0])), "variable") == 0
|
|
264 && strcmp (sel_getName (method_getName (list[1])), "setVariable:") == 0)
|
|
265 || (strcmp (sel_getName (method_getName (list[0])), "setVariable:") == 0
|
|
266 && strcmp (sel_getName (method_getName (list[1])), "variable") == 0)))
|
|
267 abort ();
|
|
268
|
|
269 if (list[2] != NULL)
|
|
270 abort ();
|
|
271 }
|
|
272
|
|
273 /* TODO: Test new ABI (when available). */
|
|
274 printf ("Testing class_copyPropertyList ()...\n");
|
|
275 {
|
|
276 unsigned int count;
|
|
277 objc_property_t * list = class_copyPropertyList (objc_getClass ("MySubClass"), &count);
|
|
278
|
|
279 if (count != 0 || list != NULL)
|
|
280 abort ();
|
|
281 }
|
|
282
|
|
283 printf ("Testing class_copyProtocolList ()...\n");
|
|
284 {
|
|
285 unsigned int count;
|
|
286 Protocol ** list = class_copyProtocolList (objc_getClass ("MySubClass"), &count);
|
|
287
|
|
288 /* Remember that we added MySecondProtocol in the test above. */
|
|
289 if (count != 2)
|
|
290 abort ();
|
|
291
|
|
292 if (! ((strcmp (protocol_getName (list[0]), "MyProtocol") == 0
|
|
293 && strcmp (protocol_getName (list[1]), "MySecondProtocol") == 0)
|
|
294 || (strcmp (protocol_getName (list[0]), "MySecondProtocol") == 0
|
|
295 && strcmp (protocol_getName (list[1]), "MyProtocol") == 0)))
|
|
296 abort ();
|
|
297
|
|
298 if (list[2] != NULL)
|
|
299 abort ();
|
|
300 }
|
|
301
|
|
302 printf ("Testing class_createInstance ()...\n");
|
|
303 {
|
|
304 MySubClass *object = [[MySubClass alloc] init];
|
|
305
|
|
306 [object setVariable: object];
|
|
307 if ([object variable] != object)
|
|
308 abort ();
|
|
309 }
|
|
310
|
|
311 printf ("Testing class_getClassMethod ()...\n");
|
|
312 {
|
|
313 Method method = class_getClassMethod (objc_getClass ("MySubClass"),
|
|
314 @selector(alloc));
|
|
315
|
|
316 if (method == NULL)
|
|
317 abort ();
|
|
318
|
|
319 if (strcmp (sel_getName (method_getName (method)), "alloc") != 0)
|
|
320 abort ();
|
|
321
|
|
322 if (class_getClassMethod (objc_getClass ("MySubClass"),
|
|
323 @selector(variable)))
|
|
324 abort ();
|
|
325 }
|
|
326
|
|
327 printf ("Testing class_getClassVariable ()...\n");
|
|
328 {
|
|
329 if (class_getClassVariable (objc_getClass ("MySubClass"), "variable_ivar"))
|
|
330 abort ();
|
|
331 }
|
|
332
|
|
333 printf ("Testing class_getInstanceMethod ()...\n");
|
|
334 {
|
|
335 Method method = class_getInstanceMethod (objc_getClass ("MySubClass"),
|
|
336 @selector(variable));
|
|
337
|
|
338 if (method == NULL)
|
|
339 abort ();
|
|
340
|
|
341 if (strcmp (sel_getName (method_getName (method)), "variable") != 0)
|
|
342 abort ();
|
|
343
|
|
344 if (class_getInstanceMethod (objc_getClass ("MySubClass"),
|
|
345 @selector(alloc)))
|
|
346 abort ();
|
|
347 }
|
|
348
|
|
349 printf ("Testing class_getInstanceSize ()...\n");
|
|
350 {
|
|
351 if (class_getInstanceSize (objc_getClass ("MyRootClass")) != sizeof (struct objc_object))
|
|
352 abort ();
|
|
353 }
|
|
354
|
|
355 printf ("Testing class_getInstanceVariable ()...\n");
|
|
356 {
|
|
357 Ivar variable = class_getInstanceVariable (objc_getClass ("MySubClass"), "variable_ivar");
|
|
358
|
|
359 if (variable == NULL)
|
|
360 abort ();
|
|
361
|
|
362 if (strcmp (ivar_getName (variable), "variable_ivar") != 0)
|
|
363 abort ();
|
|
364
|
|
365 if (class_getInstanceVariable (objc_getClass ("MySubClass"), "variable_ivar_no"))
|
|
366 abort ();
|
|
367 }
|
|
368
|
|
369 printf ("Testing class_getIvarLayout ()...\n");
|
|
370 {
|
|
371 if (class_getIvarLayout (objc_getClass ("MyRootClass")) != NULL)
|
|
372 abort ();
|
|
373 }
|
|
374
|
|
375 printf ("Testing class_getMethodImplementation ()...\n");
|
|
376 {
|
|
377 MySubClass *object = [[MySubClass alloc] init];
|
|
378 IMP imp = class_getMethodImplementation (objc_getClass ("MySubClass"),
|
|
379 @selector(variable));
|
|
380
|
|
381 if (imp == NULL)
|
|
382 abort ();
|
|
383
|
|
384 [object setVariable: object];
|
|
385
|
|
386 if ((*imp)(object, @selector(variable)) != object)
|
|
387 abort ();
|
|
388 }
|
|
389
|
|
390 /* This function does not exist with the GNU runtime. */
|
|
391 /* printf ("Testing class_getMethodImplementation_stret ()...\n"); */
|
|
392
|
|
393 printf ("Testing class_getName ()...\n");
|
|
394 {
|
|
395 if (strcmp (class_getName (objc_getClass ("MyRootClass")),
|
|
396 "MyRootClass") != 0)
|
|
397 abort ();
|
|
398 }
|
|
399
|
|
400 /* TODO: Test new ABI (when available). */
|
|
401 printf ("Testing class_getProperty ()...\n");
|
|
402 {
|
|
403 if (class_getProperty (objc_getClass ("MyRootClass"), "property") != NULL)
|
|
404 abort ();
|
|
405 }
|
|
406
|
|
407 printf ("Testing class_getSuperclass ()...\n");
|
|
408 {
|
|
409 MySubClass *object = [[MySubClass alloc] init];
|
|
410 if (class_getSuperclass (object_getClass (object)) != objc_getClass ("MyRootClass"))
|
|
411 abort ();
|
|
412
|
|
413 /* Test that it works on a newly created, but not registered, class. */
|
|
414 {
|
|
415 Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass3", 0);
|
|
416
|
|
417 if (class_getSuperclass (new_class) != objc_getClass ("MyRootClass"))
|
|
418 abort ();
|
|
419 }
|
|
420 }
|
|
421
|
|
422 printf ("Testing class_getVersion ()...\n");
|
|
423 {
|
|
424 if (class_getVersion (objc_getClass ("MySubClass")) != 0)
|
|
425 abort ();
|
|
426 }
|
|
427
|
|
428 printf ("Testing class_getWeakIvarLayout ()...\n");
|
|
429 {
|
|
430 if (class_getWeakIvarLayout (objc_getClass ("MyRootClass")) != NULL)
|
|
431 abort ();
|
|
432 }
|
|
433
|
|
434 printf ("Testing class_isMetaClass ()...\n");
|
|
435 {
|
|
436 MySubClass *object = [[MySubClass alloc] init];
|
|
437 if (class_isMetaClass (object_getClass (object))
|
|
438 || ! class_isMetaClass (object_getClass (object_getClass (object))))
|
|
439 abort ();
|
|
440 }
|
|
441
|
|
442 printf ("Testing class_replaceMethod ()...\n");
|
|
443 {
|
|
444 Method new_method = class_getInstanceMethod (objc_getClass ("DifferentClass"),
|
|
445 @selector (myClass));
|
|
446 Method old_method = class_getInstanceMethod (objc_getClass ("MySubClass"),
|
|
447 @selector (variable));
|
|
448 const char *new_types = method_getTypeEncoding (new_method);
|
|
449 IMP new_imp = method_getImplementation (new_method);
|
|
450 const char *old_types = method_getTypeEncoding (old_method);
|
|
451 IMP old_imp = class_replaceMethod (objc_getClass ("MySubClass"), @selector (variable),
|
|
452 method_getImplementation (new_method),
|
|
453 method_getTypeEncoding (new_method));
|
|
454 MySubClass *o = [[MySubClass alloc] init];
|
|
455
|
|
456 [o setVariable: o];
|
|
457
|
|
458 /* Try the new method implementation. */
|
|
459 if ([o variable] != objc_getClass ("MySubClass"))
|
|
460 abort ();
|
|
461
|
|
462 /* Put the original method back. */
|
|
463 class_replaceMethod (objc_getClass ("MySubClass"), @selector (variable),
|
|
464 old_imp, old_types);
|
|
465
|
|
466 /* Test it's back to what it was. */
|
|
467 if ([o variable] != o)
|
|
468 abort ();
|
|
469
|
|
470 {
|
|
471 DifferentClass *o = [[DifferentClass alloc] init];
|
|
472
|
|
473 /* Finally, try adding a new method. */
|
|
474 class_replaceMethod (objc_getClass ("DifferentClass"), @selector (mySelf),
|
|
475 new_imp, new_types);
|
|
476
|
|
477 if ([(MySubClass*)o mySelf] != objc_getClass ("DifferentClass"))
|
|
478 abort ();
|
|
479 }
|
|
480 }
|
|
481
|
|
482 printf ("Testing class_respondsToSelector ()...\n");
|
|
483 {
|
|
484 if (! class_respondsToSelector (objc_getClass ("MySubClass"), @selector(setVariable:)))
|
|
485 abort ();
|
|
486
|
|
487 if (class_respondsToSelector (objc_getClass ("MyRootClass"), @selector(setVariable:)))
|
|
488 abort ();
|
|
489 }
|
|
490
|
|
491 /* This is not really implemented with the GNU runtime. */
|
|
492 /* printf ("Testing class_setIvarLayout ()...\n"); */
|
|
493
|
|
494 printf ("Testing class_setVersion ()...\n");
|
|
495 {
|
|
496 class_setVersion (objc_getClass ("MySubClass"), 45);
|
|
497
|
|
498 if (class_getVersion (objc_getClass ("MySubClass")) != 45)
|
|
499 abort ();
|
|
500
|
|
501 class_setVersion (objc_getClass ("MySubClass"), 46);
|
|
502
|
|
503 if (class_getVersion (objc_getClass ("MySubClass")) != 46)
|
|
504 abort ();
|
|
505 }
|
|
506
|
|
507 /* This is not really implemented with the GNU runtime. */
|
|
508 /* printf ("Testing class_setWeakIvarLayout ()...\n"); */
|
|
509
|
|
510 return 0;
|
|
511 }
|