]> git.saurik.com Git - apple/objc4.git/blob - test/ivarSlide.m
objc4-818.2.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 // 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
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;
186 testassertequal(readWord(sub, 2), 10);
187
188 #ifdef __cplusplus
189 testassertequal(readWord(sub, 5), 1);
190 testassertequal(sub->cxx.magic, 1);
191 sub->cxx.magic++;
192 testassertequal(readWord(sub, 5), 2);
193 testassertequal(sub->cxx.magic, 2);
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];
264 sub2->subIvar = (void *)10;
265 testassertequal(readWord(sub2, 11), 10);
266
267 testassertequal(class_getInstanceSize([Sub2 class]), 13*sizeof(void*));
268
269 ivar = class_getInstanceVariable([Sub2 class], "subIvar");
270 testassert(ivar);
271 testassertequal(11*sizeof(void*), (size_t)ivar_getOffset(ivar));
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 }