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