]> git.saurik.com Git - apple/objc4.git/blob - test/classpair.m
objc4-680.tar.gz
[apple/objc4.git] / test / classpair.m
1 // TEST_CFLAGS -Wno-deprecated-declarations
2
3 #include "test.h"
4
5 #include "testroot.i"
6 #include <objc/runtime.h>
7 #include <string.h>
8 #ifndef OBJC_NO_GC
9 #include <objc/objc-auto.h>
10 #include <auto_zone.h>
11 #endif
12
13 @protocol Proto
14 -(void) instanceMethod;
15 +(void) classMethod;
16 @optional
17 -(void) instanceMethod2;
18 +(void) classMethod2;
19 @end
20
21 @protocol Proto2
22 -(void) instanceMethod;
23 +(void) classMethod;
24 @optional
25 -(void) instanceMethod2;
26 +(void) classMethod_that_does_not_exist;
27 @end
28
29 @protocol Proto3
30 -(void) instanceMethod;
31 +(void) classMethod_that_does_not_exist;
32 @optional
33 -(void) instanceMethod2;
34 +(void) classMethod2;
35 @end
36
37 static int super_initialize;
38
39 @interface Super : TestRoot
40 @property int superProp;
41 @end
42 @implementation Super
43 @dynamic superProp;
44 +(void)initialize { super_initialize++; }
45
46 +(void) classMethod { fail("+[Super classMethod] called"); }
47 +(void) classMethod2 { fail("+[Super classMethod2] called"); }
48 -(void) instanceMethod { fail("-[Super instanceMethod] called"); }
49 -(void) instanceMethod2 { fail("-[Super instanceMethod2] called"); }
50 @end
51
52 @interface WeakSuper : Super { __weak id weakIvar; } @end
53 @implementation WeakSuper @end
54
55 static int state;
56
57 static void instance_fn(id self, SEL _cmd __attribute__((unused)))
58 {
59 testassert(!class_isMetaClass(object_getClass(self)));
60 state++;
61 }
62
63 static void class_fn(id self, SEL _cmd __attribute__((unused)))
64 {
65 testassert(class_isMetaClass(object_getClass(self)));
66 state++;
67 }
68
69 static void fail_fn(id self __attribute__((unused)), SEL _cmd)
70 {
71 fail("fail_fn '%s' called", sel_getName(_cmd));
72 }
73
74
75 static void cycle(void)
76 {
77 Class cls;
78 BOOL ok;
79 objc_property_t prop;
80 char namebuf[256];
81
82 testassert(!objc_getClass("Sub"));
83 testassert([Super class]);
84
85 // Test subclass with bells and whistles
86
87 cls = objc_allocateClassPair([Super class], "Sub", 0);
88 testassert(cls);
89 #ifndef OBJC_NO_GC
90 if (objc_collectingEnabled()) {
91 testassert(auto_zone_size(objc_collectableZone(), objc_unretainedPointer(cls)));
92 testassert(auto_zone_size(objc_collectableZone(), objc_unretainedPointer(object_getClass(cls))));
93 }
94 #endif
95
96 class_addMethod(cls, @selector(instanceMethod),
97 (IMP)&instance_fn, "v@:");
98 class_addMethod(object_getClass(cls), @selector(classMethod),
99 (IMP)&class_fn, "v@:");
100 class_addMethod(object_getClass(cls), @selector(initialize),
101 (IMP)&class_fn, "v@:");
102 class_addMethod(object_getClass(cls), @selector(load),
103 (IMP)&fail_fn, "v@:");
104
105 ok = class_addProtocol(cls, @protocol(Proto));
106 testassert(ok);
107 ok = class_addProtocol(cls, @protocol(Proto));
108 testassert(!ok);
109
110 char attrname[2];
111 char attrvalue[2];
112 objc_property_attribute_t attrs[1];
113 unsigned int attrcount = sizeof(attrs) / sizeof(attrs[0]);
114
115 attrs[0].name = attrname;
116 attrs[0].value = attrvalue;
117 strcpy(attrname, "T");
118 strcpy(attrvalue, "x");
119
120 strcpy(namebuf, "subProp");
121 ok = class_addProperty(cls, namebuf, attrs, attrcount);
122 testassert(ok);
123 strcpy(namebuf, "subProp");
124 ok = class_addProperty(cls, namebuf, attrs, attrcount);
125 testassert(!ok);
126 strcpy(attrvalue, "i");
127 class_replaceProperty(cls, namebuf, attrs, attrcount);
128 strcpy(namebuf, "superProp");
129 ok = class_addProperty(cls, namebuf, attrs, attrcount);
130 testassert(!ok);
131 bzero(namebuf, sizeof(namebuf));
132 bzero(attrs, sizeof(attrs));
133 bzero(attrname, sizeof(attrname));
134 bzero(attrvalue, sizeof(attrvalue));
135
136 #ifndef __LP64__
137 # define size 4
138 # define align 2
139 #else
140 #define size 8
141 # define align 3
142 #endif
143
144 /*
145 {
146 int ivar;
147 id ivarid;
148 id* ivaridstar;
149 Block_t ivarblock;
150 }
151 */
152 ok = class_addIvar(cls, "ivar", 4, 2, "i");
153 testassert(ok);
154 ok = class_addIvar(cls, "ivarid", size, align, "@");
155 testassert(ok);
156 ok = class_addIvar(cls, "ivaridstar", size, align, "^@");
157 testassert(ok);
158 ok = class_addIvar(cls, "ivarblock", size, align, "@?");
159 testassert(ok);
160
161 ok = class_addIvar(cls, "ivar", 4, 2, "i");
162 testassert(!ok);
163 ok = class_addIvar(object_getClass(cls), "classvar", 4, 2, "i");
164 testassert(!ok);
165
166 objc_registerClassPair(cls);
167
168 // should call cls's +initialize, not super's
169 // Provoke +initialize using class_getMethodImplementation(class method)
170 // in order to test getNonMetaClass's slow case
171 super_initialize = 0;
172 state = 0;
173 class_getMethodImplementation(object_getClass(cls), @selector(class));
174 testassert(super_initialize == 0);
175 testassert(state == 1);
176
177 testassert(cls == [cls class]);
178 testassert(cls == objc_getClass("Sub"));
179
180 testassert(!class_isMetaClass(cls));
181 testassert(class_isMetaClass(object_getClass(cls)));
182
183 testassert(class_getSuperclass(cls) == [Super class]);
184 testassert(class_getSuperclass(object_getClass(cls)) == object_getClass([Super class]));
185
186 testassert(class_getInstanceSize(cls) >= sizeof(Class) + 4 + 3*size);
187 testassert(class_conformsToProtocol(cls, @protocol(Proto)));
188
189 if (objc_collectingEnabled()) {
190 testassert(0 == strcmp((char *)class_getIvarLayout(cls), "\x01\x13"));
191 testassert(NULL == class_getWeakIvarLayout(cls));
192 }
193
194 class_addMethod(cls, @selector(instanceMethod2),
195 (IMP)&instance_fn, "v@:");
196 class_addMethod(object_getClass(cls), @selector(classMethod2),
197 (IMP)&class_fn, "v@:");
198
199 ok = class_addIvar(cls, "ivar2", 4, 4, "i");
200 testassert(!ok);
201 ok = class_addIvar(object_getClass(cls), "classvar2", 4, 4, "i");
202 testassert(!ok);
203
204 ok = class_addProtocol(cls, @protocol(Proto2));
205 testassert(ok);
206 ok = class_addProtocol(cls, @protocol(Proto2));
207 testassert(!ok);
208 ok = class_addProtocol(cls, @protocol(Proto));
209 testassert(!ok);
210
211 attrs[0].name = attrname;
212 attrs[0].value = attrvalue;
213 strcpy(attrname, "T");
214 strcpy(attrvalue, "i");
215
216 strcpy(namebuf, "subProp2");
217 ok = class_addProperty(cls, namebuf, attrs, attrcount);
218 testassert(ok);
219 strcpy(namebuf, "subProp");
220 ok = class_addProperty(cls, namebuf, attrs, attrcount);
221 testassert(!ok);
222 strcpy(namebuf, "superProp");
223 ok = class_addProperty(cls, namebuf, attrs, attrcount);
224 testassert(!ok);
225 bzero(namebuf, sizeof(namebuf));
226 bzero(attrs, sizeof(attrs));
227 bzero(attrname, sizeof(attrname));
228 bzero(attrvalue, sizeof(attrvalue));
229
230 prop = class_getProperty(cls, "subProp");
231 testassert(prop);
232 testassert(0 == strcmp(property_getName(prop), "subProp"));
233 testassert(0 == strcmp(property_getAttributes(prop), "Ti"));
234 prop = class_getProperty(cls, "subProp2");
235 testassert(prop);
236 testassert(0 == strcmp(property_getName(prop), "subProp2"));
237 testassert(0 == strcmp(property_getAttributes(prop), "Ti"));
238
239 // note: adding more methods here causes a false leak check failure
240 state = 0;
241 [cls classMethod];
242 [cls classMethod2];
243 testassert(state == 2);
244
245 // put instance tests on a separate thread so they
246 // are reliably GC'd before class destruction
247 testonthread(^{
248 id obj = [cls new];
249 state = 0;
250 [obj instanceMethod];
251 [obj instanceMethod2];
252 testassert(state == 2);
253 RELEASE_VAR(obj);
254 });
255
256 // Test ivar layouts of sub-subclass
257 Class cls2 = objc_allocateClassPair(cls, "SubSub", 0);
258 testassert(cls2);
259
260 /*
261 {
262 id ivarid2;
263 id idarray[16];
264 void* ptrarray[16];
265 char a;
266 char b;
267 char c;
268 }
269 */
270 ok = class_addIvar(cls2, "ivarid2", size, align, "@");
271 testassert(ok);
272 ok = class_addIvar(cls2, "idarray", 16*sizeof(id), align, "[16@]");
273 testassert(ok);
274 ok = class_addIvar(cls2, "ptrarray", 16*sizeof(void*), align, "[16^]");
275 testassert(ok);
276 ok = class_addIvar(cls2, "a", 1, 0, "c");
277 testassert(ok);
278 ok = class_addIvar(cls2, "b", 1, 0, "c");
279 testassert(ok);
280 ok = class_addIvar(cls2, "c", 1, 0, "c");
281 testassert(ok);
282
283 objc_registerClassPair(cls2);
284
285 if (objc_collectingEnabled()) {
286 testassert(0 == strcmp((char *)class_getIvarLayout(cls2), "\x01\x1f\x05\xf0\x10"));
287 testassert(NULL == class_getWeakIvarLayout(cls2));
288 }
289
290 // 1-byte ivars should be well packed
291 testassert(ivar_getOffset(class_getInstanceVariable(cls2, "b")) ==
292 ivar_getOffset(class_getInstanceVariable(cls2, "a")) + 1);
293 testassert(ivar_getOffset(class_getInstanceVariable(cls2, "c")) ==
294 ivar_getOffset(class_getInstanceVariable(cls2, "b")) + 1);
295
296 testcollect(); // GC: finalize "obj" above before disposing its class
297 objc_disposeClassPair(cls2);
298 objc_disposeClassPair(cls);
299
300 testassert(!objc_getClass("Sub"));
301
302
303 // Test unmodified ivar layouts
304
305 cls = objc_allocateClassPair([Super class], "Sub2", 0);
306 testassert(cls);
307 objc_registerClassPair(cls);
308 if (objc_collectingEnabled()) {
309 const char *l1, *l2;
310 l1 = (char *)class_getIvarLayout([Super class]);
311 l2 = (char *)class_getIvarLayout(cls);
312 testassert(l1 == l2 || 0 == strcmp(l1, l2));
313 l1 = (char *)class_getWeakIvarLayout([Super class]);
314 l2 = (char *)class_getWeakIvarLayout(cls);
315 testassert(l1 == l2 || 0 == strcmp(l1, l2));
316 }
317 objc_disposeClassPair(cls);
318
319 cls = objc_allocateClassPair([WeakSuper class], "Sub3", 0);
320 testassert(cls);
321 objc_registerClassPair(cls);
322 if (objc_collectingEnabled()) {
323 const char *l1, *l2;
324 l1 = (char *)class_getIvarLayout([WeakSuper class]);
325 l2 = (char *)class_getIvarLayout(cls);
326 testassert(l1 == l2 || 0 == strcmp(l1, l2));
327 l1 = (char *)class_getWeakIvarLayout([WeakSuper class]);
328 l2 = (char *)class_getWeakIvarLayout(cls);
329 testassert(l1 == l2 || 0 == strcmp(l1, l2));
330 }
331 objc_disposeClassPair(cls);
332
333 // Test layout setters
334 if (objc_collectingEnabled()) {
335 cls = objc_allocateClassPair([Super class], "Sub4", 0);
336 testassert(cls);
337 class_setIvarLayout(cls, (uint8_t *)"foo");
338 class_setWeakIvarLayout(cls, NULL);
339 objc_registerClassPair(cls);
340 testassert(0 == strcmp("foo", (char *)class_getIvarLayout(cls)));
341 testassert(NULL == class_getWeakIvarLayout(cls));
342 objc_disposeClassPair(cls);
343
344 cls = objc_allocateClassPair([Super class], "Sub5", 0);
345 testassert(cls);
346 class_setIvarLayout(cls, NULL);
347 class_setWeakIvarLayout(cls, (uint8_t *)"bar");
348 objc_registerClassPair(cls);
349 testassert(NULL == class_getIvarLayout(cls));
350 testassert(0 == strcmp("bar", (char *)class_getWeakIvarLayout(cls)));
351 objc_disposeClassPair(cls);
352 }
353 }
354
355 int main()
356 {
357 int count = 1000;
358
359 testonthread(^{ cycle(); });
360 testonthread(^{ cycle(); });
361 testonthread(^{ cycle(); });
362
363 leak_mark();
364 while (count--) {
365 testonthread(^{ cycle(); });
366 }
367 #if __OBJC_GC__
368 testwarn("rdar://19042235 possible leaks suppressed under GC");
369 leak_check(16000);
370 #else
371 leak_check(0);
372 #endif
373
374 succeed(__FILE__);
375 }
376