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