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