]> git.saurik.com Git - apple/objc4.git/blob - test/ivarSlide.m
objc4-532.2.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->isa = [Sub2 class];
266 sub2->subIvar = (void *)10;
267 testassert(((uintptr_t *)objc_unretainedPointer(sub2))[11] == 10);
268
269 testassert(class_getInstanceSize([Sub2 class]) == 13*sizeof(void*));
270
271 ivar = class_getInstanceVariable([Sub2 class], "subIvar");
272 testassert(ivar);
273 testassert(11*sizeof(void*) == (size_t)ivar_getOffset(ivar));
274 testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
275
276 ivar = class_getInstanceVariable([ShrinkingSuper class], "superIvar");
277 testassert(!ivar);
278
279 if (objc_collectingEnabled()) {
280 const uint8_t *superlayout;
281 const uint8_t *sublayout;
282 superlayout = class_getIvarLayout([ShrinkingSuper class]);
283 sublayout = class_getIvarLayout([Sub2 class]);
284 // only `isa` is left; superIvar[] and superIvar2[] are gone
285 testassert(superlayout == NULL || 0 == ustrcmp(superlayout, "\x01"));
286 testassert(0 == ustrcmp(sublayout, "\x01\xb1"));
287
288 superlayout = class_getWeakIvarLayout([ShrinkingSuper class]);
289 sublayout = class_getWeakIvarLayout([Sub2 class]);
290 testassert(superlayout == NULL);
291 testassert(0 == ustrcmp(sublayout, "\xb1\x10"));
292 }
293
294 /*
295 Ivars slide but GC layouts stay the same
296 Here, the last word of the superclass is misaligned, but
297 its GC layout includes a bit for that whole word.
298 Additionally, all of the subclass ivars fit into that word too,
299 both before and after sliding.
300 The runtime will try to slide the GC layout and must not be
301 confused (rdar://6851700). Note that the second skip-word may or may
302 not actually be included, because it crosses the end of the object.
303
304
305 Compile-time layout of NoGCChangeSub:
306 [0 scan] isa
307 [1 skip] d
308 [2 skip] superc1, subc3
309
310 Runtime layout of NoGCChangeSub:
311 [0 scan] isa
312 [1 skip] d
313 [2 skip] superc1, superc2, subc3
314 */
315 if (objc_collectingEnabled()) {
316 Ivar ivar1 = class_getInstanceVariable([NoGCChangeSub class], "superc1");
317 testassert(ivar1);
318 Ivar ivar2 = class_getInstanceVariable([NoGCChangeSub class], "superc2");
319 testassert(ivar2);
320 Ivar ivar3 = class_getInstanceVariable([NoGCChangeSub class], "subc3");
321 testassert(ivar3);
322 testassert(ivar_getOffset(ivar1) != ivar_getOffset(ivar2) &&
323 ivar_getOffset(ivar1) != ivar_getOffset(ivar3) &&
324 ivar_getOffset(ivar2) != ivar_getOffset(ivar3));
325 }
326
327 /* Ivar layout includes runs of 15 words.
328 rdar://6859875 this would generate a truncated GC layout.
329 */
330 if (objc_collectingEnabled()) {
331 const uint8_t *layout =
332 class_getIvarLayout(objc_getClass("RunsOf15Sub"));
333 testassert(layout);
334 int totalSkip = 0;
335 int totalScan = 0;
336 // should find 30+ each of skip and scan
337 uint8_t c;
338 while ((c = *layout++)) {
339 totalSkip += c>>4;
340 totalScan += c&0xf;
341 }
342 testassert(totalSkip >= 30);
343 testassert(totalScan >= 30);
344 }
345
346 // __OBJC2__
347 #endif
348
349
350 /*
351 Non-strong -> strong
352 Classes do not change size, but GC layouts must be updated.
353 Both new and old ABI detect this case (rdar://5774578)
354
355 Compile-time layout of MoreStrongSub:
356 [0 scan] isa
357 [1 skip] superIvar
358 [2 scan] subIvar
359
360 Runtime layout of MoreStrongSub:
361 [0 scan] isa
362 [1 scan] superIvar
363 [2 scan] subIvar
364 */
365 testassert(class_getInstanceSize([MoreStrongSub class]) == 3*sizeof(void*));
366 if (objc_collectingEnabled()) {
367 const uint8_t *layout;
368 layout = class_getIvarLayout([MoreStrongSub class]);
369 testassert(layout == NULL);
370
371 layout = class_getWeakIvarLayout([MoreStrongSub class]);
372 testassert(layout == NULL);
373 }
374
375
376 /*
377 Strong -> weak
378 Classes do not change size, but GC layouts must be updated.
379 Old ABI intentionally does not detect this case (rdar://5774578)
380
381 Compile-time layout of MoreWeakSub:
382 [0 scan] isa
383 [1 scan] superIvar
384 [2 scan] subIvar
385
386 Runtime layout of MoreWeakSub:
387 [0 scan] isa
388 [1 weak] superIvar
389 [2 scan] subIvar
390 */
391 testassert(class_getInstanceSize([MoreWeakSub class]) == 3*sizeof(void*));
392 if (objc_collectingEnabled()) {
393 const uint8_t *layout;
394 layout = class_getIvarLayout([MoreWeakSub class]);
395 #if __OBJC2__
396 // fixed version: scan / weak / scan
397 testassert(0 == ustrcmp(layout, "\x01\x11"));
398 #else
399 // unfixed version: scan / scan / scan
400 testassert(layout == NULL || 0 == ustrcmp(layout, "\x03"));
401 #endif
402
403 layout = class_getWeakIvarLayout([MoreWeakSub class]);
404 #if __OBJC2__
405 testassert(0 == ustrcmp(layout, "\x11\x10"));
406 #else
407 testassert(layout == NULL);
408 #endif
409 }
410
411
412 /*
413 Non-strong -> weak
414 Classes do not change size, but GC layouts must be updated.
415 Old ABI intentionally does not detect this case (rdar://5774578)
416
417 Compile-time layout of MoreWeak2Sub:
418 [0 scan] isa
419 [1 skip] superIvar
420 [2 scan] subIvar
421
422 Runtime layout of MoreWeak2Sub:
423 [0 scan] isa
424 [1 weak] superIvar
425 [2 scan] subIvar
426 */
427 testassert(class_getInstanceSize([MoreWeak2Sub class]) == 3*sizeof(void*));
428 if (objc_collectingEnabled()) {
429 const uint8_t *layout;
430 layout = class_getIvarLayout([MoreWeak2Sub class]);
431 testassert(0 == ustrcmp(layout, "\x01\x11") ||
432 0 == ustrcmp(layout, "\x01\x10\x01"));
433
434 layout = class_getWeakIvarLayout([MoreWeak2Sub class]);
435 #if __OBJC2__
436 testassert(0 == ustrcmp(layout, "\x11\x10"));
437 #else
438 testassert(layout == NULL);
439 #endif
440 }
441
442
443 /*
444 Strong -> non-strong
445 Classes do not change size, but GC layouts must be updated.
446 Old ABI intentionally does not detect this case (rdar://5774578)
447
448 Compile-time layout of LessStrongSub:
449 [0 scan] isa
450 [1 scan] superIvar
451 [2 scan] subIvar
452
453 Runtime layout of LessStrongSub:
454 [0 scan] isa
455 [1 skip] superIvar
456 [2 scan] subIvar
457 */
458 testassert(class_getInstanceSize([LessStrongSub class]) == 3*sizeof(void*));
459 if (objc_collectingEnabled()) {
460 const uint8_t *layout;
461 layout = class_getIvarLayout([LessStrongSub class]);
462 #if __OBJC2__
463 // fixed version: scan / skip / scan
464 testassert(0 == ustrcmp(layout, "\x01\x11"));
465 #else
466 // unfixed version: scan / scan / scan
467 testassert(layout == NULL || 0 == ustrcmp(layout, "\x03"));
468 #endif
469
470 layout = class_getWeakIvarLayout([LessStrongSub class]);
471 testassert(layout == NULL);
472 }
473
474
475 /*
476 Weak -> strong
477 Classes do not change size, but GC layouts must be updated.
478 Both new and old ABI detect this case (rdar://5774578 rdar://6924114)
479
480 Compile-time layout of LessWeakSub:
481 [0 scan] isa
482 [1 weak] superIvar
483 [2 scan] subIvar
484
485 Runtime layout of LessWeakSub:
486 [0 scan] isa
487 [1 scan] superIvar
488 [2 scan] subIvar
489 */
490 testassert(class_getInstanceSize([LessWeakSub class]) == 3*sizeof(void*));
491 if (objc_collectingEnabled()) {
492 const uint8_t *layout;
493 layout = class_getIvarLayout([LessWeakSub class]);
494 testassert(layout == NULL);
495
496 layout = class_getWeakIvarLayout([LessWeakSub class]);
497 testassert(layout == NULL);
498 }
499
500
501 /*
502 Weak -> non-strong
503 Classes do not change size, but GC layouts must be updated.
504 Old ABI intentionally does not detect this case (rdar://5774578)
505
506 Compile-time layout of LessWeak2Sub:
507 [0 scan] isa
508 [1 weak] superIvar
509 [2 scan] subIvar
510
511 Runtime layout of LessWeak2Sub:
512 [0 scan] isa
513 [1 skip] superIvar
514 [2 scan] subIvar
515 */
516 testassert(class_getInstanceSize([LessWeak2Sub class]) == 3*sizeof(void*));
517 if (objc_collectingEnabled()) {
518 const uint8_t *layout;
519 layout = class_getIvarLayout([LessWeak2Sub class]);
520 testassert(0 == ustrcmp(layout, "\x01\x11") ||
521 0 == ustrcmp(layout, "\x01\x10\x01"));
522
523 layout = class_getWeakIvarLayout([LessWeak2Sub class]);
524 #if __OBJC2__
525 testassert(layout == NULL);
526 #else
527 testassert(0 == ustrcmp(layout, "\x11\x10"));
528 #endif
529 }
530
531
532 succeed(basename(argv[0]));
533 return 0;
534 }