]> git.saurik.com Git - apple/objc4.git/blob - test/ivarSlide2.m
objc4-437.tar.gz
[apple/objc4.git] / test / ivarSlide2.m
1 #include "test.h"
2 #include <string.h>
3 #include <stdint.h>
4 #include <objc/objc-runtime.h>
5 #include <objc/objc-auto.h>
6 #include <auto_zone.h>
7
8 #define OLD 1
9 #include "ivarSlide.h"
10
11 #ifdef __cplusplus
12 class CXX {
13 public:
14 static uintptr_t count;
15 uintptr_t magic;
16 CXX() : magic(1) { }
17 ~CXX() { count += magic; }
18 };
19 uintptr_t CXX::count;
20 #endif
21
22 @interface Bitfields : Super {
23 uint8_t uint8_ivar;
24 uint8_t uint8_bitfield1 :7;
25 uint8_t uint8_bitfield2 :1;
26
27 id id_ivar;
28
29 uintptr_t uintptr_ivar;
30 uintptr_t /*uintptr_bitfield1*/ :31; // anonymous (rdar://5723893)
31 uintptr_t uintptr_bitfield2 :1;
32
33 id id_ivar2;
34 }
35 @end
36
37 @implementation Bitfields @end
38
39
40 @interface Sub : Super {
41 @public
42 uintptr_t subIvar;
43 __strong uintptr_t subIvar2;
44 __weak uintptr_t subIvar3;
45 #ifdef __cplusplus
46 CXX cxx;
47 #else
48 // same layout as cxx
49 uintptr_t cxx_magic;
50 #endif
51 }
52 @end
53
54 @implementation Sub @end
55
56
57 @interface Sub2 : ShrinkingSuper {
58 @public
59 __weak uintptr_t subIvar;
60 __strong uintptr_t subIvar2;
61 }
62 @end
63
64 @implementation Sub2 @end
65
66 @interface MoreStrongSub : MoreStrongSuper { id subIvar; } @end
67 @interface LessStrongSub : LessStrongSuper { id subIvar; } @end
68 @interface MoreWeakSub : MoreWeakSuper { id subIvar; } @end
69 @interface MoreWeak2Sub : MoreWeak2Super { id subIvar; } @end
70 @interface LessWeakSub : LessWeakSuper { id subIvar; } @end
71 @interface LessWeak2Sub : LessWeak2Super { id subIvar; } @end
72
73 @implementation MoreStrongSub @end
74 @implementation LessStrongSub @end
75 @implementation MoreWeakSub @end
76 @implementation MoreWeak2Sub @end
77 @implementation LessWeakSub @end
78 @implementation LessWeak2Sub @end
79
80 @interface NoGCChangeSub : NoGCChangeSuper {
81 @public
82 char subc3;
83 }
84 @end
85 @implementation NoGCChangeSub @end
86
87 @interface RunsOf15Sub : RunsOf15 {
88 @public
89 char sub;
90 }
91 @end
92 @implementation RunsOf15Sub @end
93
94
95 int main(int argc __attribute__((unused)), char **argv)
96 {
97 if (objc_collecting_enabled()) {
98 objc_startCollectorThread();
99 }
100
101 #if __OBJC2__
102
103 /*
104 Bitfield ivars.
105 rdar://5723893 anonymous bitfield ivars crash when slid
106 rdar://5724385 bitfield ivar alignment incorrect
107
108 Compile-time layout of Bitfields:
109 [0 scan] isa
110 [1 skip] uint8_ivar, uint8_bitfield
111 [2 scan] id_ivar
112 [3 skip] uintptr_ivar
113 [4 skip] uintptr_bitfield
114 [5 scan] id_ivar2
115
116 Runtime layout of Bitfields:
117 [0 scan] isa
118 [1 skip] superIvar
119 [2 skip] uint8_ivar, uint8_bitfield
120 [3 scan] id_ivar
121 [4 skip] uintptr_ivar
122 [5 skip] uintptr_bitfield
123 [6 scan] id_ivar2
124 */
125
126 [Bitfields class];
127
128 testassert(class_getInstanceSize([Bitfields class]) == 7*sizeof(void*));
129
130 if (objc_collecting_enabled()) {
131 const char *bitfieldlayout;
132 bitfieldlayout = class_getIvarLayout([Bitfields class]);
133 testassert(0 == strcmp(bitfieldlayout, "\x01\x21\x21"));
134
135 bitfieldlayout = class_getWeakIvarLayout([Bitfields class]);
136 testassert(bitfieldlayout == NULL);
137 }
138
139 /*
140 Compile-time layout of Sub:
141 [0 scan] isa
142 [1 skip] subIvar
143 [2 scan] subIvar2
144 [3 weak] subIvar3
145 [6 skip] cxx
146
147 Runtime layout of Sub:
148 [0 scan] isa
149 [1 skip] superIvar
150 [2 skip] subIvar
151 [3 scan] subIvar2
152 [4 weak] subIvar3
153 [6 skip] cxx
154
155 Also, superIvar is only one byte, so subIvar's alignment must
156 be handled correctly.
157
158 fixme test more layouts
159 */
160
161 Ivar ivar;
162 static Sub * volatile sub;
163 sub = [Sub new];
164 sub->subIvar = 10;
165 testassert(((uintptr_t *)sub)[2] == 10);
166
167 #ifdef __cplusplus
168 uintptr_t i;
169 for (i = 0; i < 10; i++) {
170 if (i != 0) sub = [Sub new];
171 testassert(((uintptr_t *)sub)[5] == 1);
172 testassert(sub->cxx.magic == 1);
173 sub->cxx.magic++;
174 testassert(((uintptr_t *)sub)[5] == 2);
175 testassert(sub->cxx.magic == 2);
176 if (! objc_collecting_enabled()) {
177 [sub dealloc];
178 }
179 }
180
181 if (objc_collecting_enabled()) {
182 // only require one of the objects to be reclaimed
183 objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
184 objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
185 testassert(CXX::count > 0 && CXX::count <= i*2 && CXX::count % 2 == 0);
186 }
187 else {
188 testassert(CXX::count == i*2);
189 }
190 #endif
191
192
193 testassert(class_getInstanceSize([Sub class]) == 6*sizeof(void*));
194
195 ivar = class_getInstanceVariable([Sub class], "subIvar");
196 testassert(ivar);
197 testassert(2*sizeof(void*) == ivar_getOffset(ivar));
198 testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
199 testassert(0 == strcmp(ivar_getTypeEncoding(ivar),
200 #if __LP64__
201 "Q"
202 #else
203 "I"
204 #endif
205 ));
206
207 #ifdef __cplusplus
208 ivar = class_getInstanceVariable([Sub class], "cxx");
209 testassert(ivar);
210 #endif
211
212 ivar = class_getInstanceVariable([Super class], "superIvar");
213 testassert(ivar);
214 testassert(sizeof(void*) == ivar_getOffset(ivar));
215 testassert(0 == strcmp(ivar_getName(ivar), "superIvar"));
216 testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "c"));
217
218 ivar = class_getInstanceVariable([Super class], "subIvar");
219 testassert(!ivar);
220
221 if (objc_collecting_enabled()) {
222 const char *superlayout;
223 const char *sublayout;
224 superlayout = class_getIvarLayout([Super class]);
225 sublayout = class_getIvarLayout([Sub class]);
226 testassert(0 == strcmp(superlayout, "\x01\x10"));
227 testassert(0 == strcmp(sublayout, "\x01\x21\x20"));
228
229 superlayout = class_getWeakIvarLayout([Super class]);
230 sublayout = class_getWeakIvarLayout([Sub class]);
231 testassert(superlayout == NULL);
232 testassert(0 == strcmp(sublayout, "\x41\x10"));
233 }
234
235 /*
236 Shrinking superclass.
237 Subclass ivars do not compact, but the GC layout needs to
238 update, including the gap that the superclass no longer spans.
239
240 Compile-time layout of Sub2:
241 [0 scan] isa
242 [1-5 scan] superIvar
243 [6-10 weak] superIvar2
244 [11 weak] subIvar
245 [12 scan] subIvar2
246
247 Runtime layout of Sub2:
248 [0 scan] isa
249 [1-10 skip] was superIvar
250 [11 weak] subIvar
251 [12 scan] subIvar2
252 */
253
254 Sub2 *sub2 = [Sub2 new];
255 sub2->isa = [Sub2 class];
256 sub2->subIvar = 10;
257 testassert(((uintptr_t *)sub2)[11] == 10);
258
259 testassert(class_getInstanceSize([Sub2 class]) == 13*sizeof(void*));
260
261 ivar = class_getInstanceVariable([Sub2 class], "subIvar");
262 testassert(ivar);
263 testassert(11*sizeof(void*) == ivar_getOffset(ivar));
264 testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
265
266 ivar = class_getInstanceVariable([ShrinkingSuper class], "superIvar");
267 testassert(!ivar);
268
269 if (objc_collecting_enabled()) {
270 const char *superlayout;
271 const char *sublayout;
272 superlayout = class_getIvarLayout([ShrinkingSuper class]);
273 sublayout = class_getIvarLayout([Sub2 class]);
274 testassert(superlayout == NULL);
275 testassert(0 == strcmp(sublayout, "\x01\xb1"));
276
277 superlayout = class_getWeakIvarLayout([ShrinkingSuper class]);
278 sublayout = class_getWeakIvarLayout([Sub2 class]);
279 testassert(superlayout == NULL);
280 testassert(0 == strcmp(sublayout, "\xb1\x10"));
281 }
282
283 /*
284 Ivars slide but GC layouts stay the same
285 Here, the last word of the superclass is misaligned, but
286 its GC layout includes a bit for that whole word.
287 Additionally, all of the subclass ivars fit into that word too,
288 both before and after sliding.
289 The runtime will try to slide the GC layout and must not be
290 confused (rdar://6851700). Note that the second skip-word may or may
291 not actually be included, because it crosses the end of the object.
292
293
294 Compile-time layout of NoGCChangeSub:
295 [0 scan] isa
296 [1 skip] d
297 [2 skip] superc1, subc3
298
299 Runtime layout of NoGCChangeSub:
300 [0 scan] isa
301 [1 skip] d
302 [2 skip] superc1, superc2, subc3
303 */
304 if (objc_collecting_enabled()) {
305 Ivar ivar1 = class_getInstanceVariable([NoGCChangeSub class], "superc1");
306 testassert(ivar1);
307 Ivar ivar2 = class_getInstanceVariable([NoGCChangeSub class], "superc2");
308 testassert(ivar2);
309 Ivar ivar3 = class_getInstanceVariable([NoGCChangeSub class], "subc3");
310 testassert(ivar3);
311 testassert(ivar_getOffset(ivar1) != ivar_getOffset(ivar2) &&
312 ivar_getOffset(ivar1) != ivar_getOffset(ivar3) &&
313 ivar_getOffset(ivar2) != ivar_getOffset(ivar3));
314 }
315
316 /* Ivar layout includes runs of 15 words.
317 rdar://6859875 this would generate a truncated GC layout.
318 */
319 if (objc_collecting_enabled()) {
320 const uint8_t *layout = (const uint8_t *)
321 class_getIvarLayout(objc_getClass("RunsOf15Sub"));
322 testassert(layout);
323 int totalSkip = 0;
324 int totalScan = 0;
325 // should find 30+ each of skip and scan
326 uint8_t c;
327 while ((c = *layout++)) {
328 totalSkip += c>>4;
329 totalScan += c&0xf;
330 }
331 testassert(totalSkip >= 30);
332 testassert(totalScan >= 30);
333 }
334
335 // __OBJC2__
336 #endif
337
338
339 /*
340 Non-strong -> strong
341 Classes do not change size, but GC layouts must be updated.
342 Both new and old ABI detect this case (rdar://5774578)
343
344 Compile-time layout of MoreStrongSub:
345 [0 scan] isa
346 [1 skip] superIvar
347 [2 scan] subIvar
348
349 Runtime layout of MoreStrongSub:
350 [0 scan] isa
351 [1 scan] superIvar
352 [2 scan] subIvar
353 */
354 testassert(class_getInstanceSize([MoreStrongSub class]) == 3*sizeof(void*));
355 if (objc_collecting_enabled()) {
356 const char *layout;
357 layout = class_getIvarLayout([MoreStrongSub class]);
358 testassert(layout == NULL);
359
360 layout = class_getWeakIvarLayout([MoreStrongSub class]);
361 testassert(layout == NULL);
362 }
363
364
365 /*
366 Strong -> weak
367 Classes do not change size, but GC layouts must be updated.
368 Old ABI intentionally does not detect this case (rdar://5774578)
369
370 Compile-time layout of MoreWeakSub:
371 [0 scan] isa
372 [1 scan] superIvar
373 [2 scan] subIvar
374
375 Runtime layout of MoreWeakSub:
376 [0 scan] isa
377 [1 weak] superIvar
378 [2 scan] subIvar
379 */
380 testassert(class_getInstanceSize([MoreWeakSub class]) == 3*sizeof(void*));
381 if (objc_collecting_enabled()) {
382 const char *layout;
383 layout = class_getIvarLayout([MoreWeakSub class]);
384 #if __OBJC2__
385 testassert(0 == strcmp(layout, "\x01\x11"));
386 #else
387 testassert(layout == NULL);
388 #endif
389
390 layout = class_getWeakIvarLayout([MoreWeakSub class]);
391 #if __OBJC2__
392 testassert(0 == strcmp(layout, "\x11\x10"));
393 #else
394 testassert(layout == NULL);
395 #endif
396 }
397
398
399 /*
400 Non-strong -> weak
401 Classes do not change size, but GC layouts must be updated.
402 Old ABI intentionally does not detect this case (rdar://5774578)
403
404 Compile-time layout of MoreWeak2Sub:
405 [0 scan] isa
406 [1 skip] superIvar
407 [2 scan] subIvar
408
409 Runtime layout of MoreWeak2Sub:
410 [0 scan] isa
411 [1 weak] superIvar
412 [2 scan] subIvar
413 */
414 testassert(class_getInstanceSize([MoreWeak2Sub class]) == 3*sizeof(void*));
415 if (objc_collecting_enabled()) {
416 const char *layout;
417 layout = class_getIvarLayout([MoreWeak2Sub class]);
418 testassert(0 == strcmp(layout, "\x01\x11") ||
419 0 == strcmp(layout, "\x01\x10\x01"));
420
421 layout = class_getWeakIvarLayout([MoreWeak2Sub class]);
422 #if __OBJC2__
423 testassert(0 == strcmp(layout, "\x11\x10"));
424 #else
425 testassert(layout == NULL);
426 #endif
427 }
428
429
430 /*
431 Strong -> non-strong
432 Classes do not change size, but GC layouts must be updated.
433 Old ABI intentionally does not detect this case (rdar://5774578)
434
435 Compile-time layout of LessStrongSub:
436 [0 scan] isa
437 [1 scan] superIvar
438 [2 scan] subIvar
439
440 Runtime layout of LessStrongSub:
441 [0 scan] isa
442 [1 skip] superIvar
443 [2 scan] subIvar
444 */
445 testassert(class_getInstanceSize([LessStrongSub class]) == 3*sizeof(void*));
446 if (objc_collecting_enabled()) {
447 const char *layout;
448 layout = class_getIvarLayout([LessStrongSub class]);
449 #if __OBJC2__
450 testassert(0 == strcmp(layout, "\x01\x11"));
451 #else
452 testassert(layout == NULL);
453 #endif
454
455 layout = class_getWeakIvarLayout([LessStrongSub class]);
456 testassert(layout == NULL);
457 }
458
459
460 /*
461 Weak -> strong
462 Classes do not change size, but GC layouts must be updated.
463 Both new and old ABI detect this case (rdar://5774578 rdar://6924114)
464
465 Compile-time layout of LessWeakSub:
466 [0 scan] isa
467 [1 weak] superIvar
468 [2 scan] subIvar
469
470 Runtime layout of LessWeakSub:
471 [0 scan] isa
472 [1 scan] superIvar
473 [2 scan] subIvar
474 */
475 testassert(class_getInstanceSize([LessWeakSub class]) == 3*sizeof(void*));
476 if (objc_collecting_enabled()) {
477 const char *layout;
478 layout = class_getIvarLayout([LessWeakSub class]);
479 testassert(layout == NULL);
480
481 layout = class_getWeakIvarLayout([LessWeakSub class]);
482 testassert(layout == NULL);
483 }
484
485
486 /*
487 Weak -> non-strong
488 Classes do not change size, but GC layouts must be updated.
489 Old ABI intentionally does not detect this case (rdar://5774578)
490
491 Compile-time layout of LessWeak2Sub:
492 [0 scan] isa
493 [1 weak] superIvar
494 [2 scan] subIvar
495
496 Runtime layout of LessWeak2Sub:
497 [0 scan] isa
498 [1 skip] superIvar
499 [2 scan] subIvar
500 */
501 testassert(class_getInstanceSize([LessWeak2Sub class]) == 3*sizeof(void*));
502 if (objc_collecting_enabled()) {
503 const char *layout;
504 layout = class_getIvarLayout([LessWeak2Sub class]);
505 testassert(0 == strcmp(layout, "\x01\x11") ||
506 0 == strcmp(layout, "\x01\x10\x01"));
507
508 layout = class_getWeakIvarLayout([LessWeak2Sub class]);
509 #if __OBJC2__
510 testassert(layout == NULL);
511 #else
512 testassert(0 == strcmp(layout, "\x11\x10"));
513 #endif
514 }
515
516
517 succeed(basename(argv[0]));
518 return 0;
519 }