]> git.saurik.com Git - apple/objc4.git/blobdiff - test/ivarSlide2.m
objc4-437.tar.gz
[apple/objc4.git] / test / ivarSlide2.m
diff --git a/test/ivarSlide2.m b/test/ivarSlide2.m
new file mode 100644 (file)
index 0000000..22d8493
--- /dev/null
@@ -0,0 +1,519 @@
+#include "test.h"
+#include <string.h>
+#include <stdint.h>
+#include <objc/objc-runtime.h>
+#include <objc/objc-auto.h>
+#include <auto_zone.h>
+
+#define OLD 1
+#include "ivarSlide.h"
+
+#ifdef __cplusplus
+class CXX {
+ public:
+    static uintptr_t count;
+    uintptr_t magic;
+    CXX() : magic(1) { } 
+    ~CXX() { count += magic; }
+};
+uintptr_t CXX::count;
+#endif
+
+@interface Bitfields : Super {
+    uint8_t uint8_ivar;
+    uint8_t uint8_bitfield1 :7;
+    uint8_t uint8_bitfield2 :1;
+
+    id id_ivar;
+    uintptr_t uintptr_ivar;
+    uintptr_t /*uintptr_bitfield1*/ :31;  // anonymous (rdar://5723893)
+    uintptr_t uintptr_bitfield2 :1;
+
+    id id_ivar2;
+}
+@end
+
+@implementation Bitfields @end
+
+
+@interface Sub : Super {
+  @public 
+    uintptr_t subIvar;
+    __strong uintptr_t subIvar2;
+    __weak uintptr_t subIvar3;
+#ifdef __cplusplus
+    CXX cxx;
+#else
+    // same layout as cxx
+    uintptr_t cxx_magic;
+#endif
+}
+@end
+
+@implementation Sub @end
+
+
+@interface Sub2 : ShrinkingSuper {
+  @public 
+    __weak uintptr_t subIvar;
+    __strong uintptr_t subIvar2;
+}
+@end
+
+@implementation Sub2 @end
+
+@interface MoreStrongSub : MoreStrongSuper { id subIvar; } @end
+@interface LessStrongSub : LessStrongSuper { id subIvar; } @end
+@interface MoreWeakSub : MoreWeakSuper { id subIvar; }  @end
+@interface MoreWeak2Sub : MoreWeak2Super { id subIvar; }  @end
+@interface LessWeakSub : LessWeakSuper { id subIvar; }  @end
+@interface LessWeak2Sub : LessWeak2Super { id subIvar; }  @end
+
+@implementation MoreStrongSub @end
+@implementation LessStrongSub @end
+@implementation MoreWeakSub @end
+@implementation MoreWeak2Sub @end
+@implementation LessWeakSub @end
+@implementation LessWeak2Sub @end
+
+@interface NoGCChangeSub : NoGCChangeSuper { 
+  @public
+    char subc3; 
+} 
+@end
+@implementation NoGCChangeSub @end
+
+@interface RunsOf15Sub : RunsOf15 { 
+  @public
+    char sub; 
+} 
+@end
+@implementation RunsOf15Sub @end
+
+
+int main(int argc __attribute__((unused)), char **argv)
+{
+    if (objc_collecting_enabled()) {
+        objc_startCollectorThread();
+    }
+
+#if __OBJC2__
+
+    /* 
+       Bitfield ivars.
+       rdar://5723893 anonymous bitfield ivars crash when slid
+       rdar://5724385 bitfield ivar alignment incorrect
+
+       Compile-time layout of Bitfields: 
+         [0 scan] isa
+         [1 skip] uint8_ivar, uint8_bitfield
+         [2 scan] id_ivar
+         [3 skip] uintptr_ivar
+         [4 skip] uintptr_bitfield
+         [5 scan] id_ivar2
+
+       Runtime layout of Bitfields:
+         [0 scan] isa
+         [1 skip] superIvar
+         [2 skip] uint8_ivar, uint8_bitfield
+         [3 scan] id_ivar
+         [4 skip] uintptr_ivar
+         [5 skip] uintptr_bitfield
+         [6 scan] id_ivar2
+    */
+
+    [Bitfields class];
+
+    testassert(class_getInstanceSize([Bitfields class]) == 7*sizeof(void*));
+
+    if (objc_collecting_enabled()) {
+        const char *bitfieldlayout;
+        bitfieldlayout = class_getIvarLayout([Bitfields class]);
+        testassert(0 == strcmp(bitfieldlayout, "\x01\x21\x21"));
+
+        bitfieldlayout = class_getWeakIvarLayout([Bitfields class]);
+        testassert(bitfieldlayout == NULL);
+    }
+
+    /* 
+       Compile-time layout of Sub: 
+         [0 scan] isa
+         [1 skip] subIvar
+         [2 scan] subIvar2
+         [3 weak] subIvar3
+         [6 skip] cxx
+
+       Runtime layout of Sub:
+         [0 scan] isa
+         [1 skip] superIvar
+         [2 skip] subIvar
+         [3 scan] subIvar2
+         [4 weak] subIvar3
+         [6 skip] cxx
+
+       Also, superIvar is only one byte, so subIvar's alignment must 
+       be handled correctly.
+
+       fixme test more layouts
+    */
+
+    Ivar ivar;
+    static Sub * volatile sub;
+    sub = [Sub new];
+    sub->subIvar = 10;
+    testassert(((uintptr_t *)sub)[2] == 10);
+
+#ifdef __cplusplus
+    uintptr_t i;
+    for (i = 0; i < 10; i++) {
+        if (i != 0) sub = [Sub new];
+        testassert(((uintptr_t *)sub)[5] == 1);
+        testassert(sub->cxx.magic == 1);
+        sub->cxx.magic++;
+        testassert(((uintptr_t *)sub)[5] == 2);
+        testassert(sub->cxx.magic == 2);
+        if (! objc_collecting_enabled()) {
+            [sub dealloc];
+        }
+    }
+
+    if (objc_collecting_enabled()) {
+        // only require one of the objects to be reclaimed
+        objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
+        objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
+        testassert(CXX::count > 0  &&  CXX::count <= i*2  &&  CXX::count % 2 == 0);
+    }
+    else {
+        testassert(CXX::count == i*2);
+    }
+#endif
+
+
+    testassert(class_getInstanceSize([Sub class]) == 6*sizeof(void*));
+
+    ivar = class_getInstanceVariable([Sub class], "subIvar");
+    testassert(ivar);
+    testassert(2*sizeof(void*) == ivar_getOffset(ivar));
+    testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
+    testassert(0 == strcmp(ivar_getTypeEncoding(ivar), 
+#if __LP64__
+                           "Q"
+#else
+                           "I"
+#endif
+                           ));
+
+#ifdef __cplusplus
+    ivar = class_getInstanceVariable([Sub class], "cxx");
+    testassert(ivar);
+#endif
+
+    ivar = class_getInstanceVariable([Super class], "superIvar");
+    testassert(ivar);
+    testassert(sizeof(void*) == ivar_getOffset(ivar));
+    testassert(0 == strcmp(ivar_getName(ivar), "superIvar"));
+    testassert(0 == strcmp(ivar_getTypeEncoding(ivar), "c"));
+
+    ivar = class_getInstanceVariable([Super class], "subIvar");
+    testassert(!ivar);
+
+    if (objc_collecting_enabled()) {
+        const char *superlayout;
+        const char *sublayout;
+        superlayout = class_getIvarLayout([Super class]);
+        sublayout = class_getIvarLayout([Sub class]);
+        testassert(0 == strcmp(superlayout, "\x01\x10"));
+        testassert(0 == strcmp(sublayout, "\x01\x21\x20"));
+
+        superlayout = class_getWeakIvarLayout([Super class]);
+        sublayout = class_getWeakIvarLayout([Sub class]);
+        testassert(superlayout == NULL);
+        testassert(0 == strcmp(sublayout, "\x41\x10"));
+    }
+
+    /* 
+       Shrinking superclass.
+       Subclass ivars do not compact, but the GC layout needs to 
+       update, including the gap that the superclass no longer spans.
+
+       Compile-time layout of Sub2: 
+         [0 scan] isa
+         [1-5 scan] superIvar
+         [6-10 weak] superIvar2
+         [11 weak] subIvar
+         [12 scan] subIvar2
+
+       Runtime layout of Sub2:
+         [0 scan] isa
+         [1-10 skip] was superIvar
+         [11 weak] subIvar
+         [12 scan] subIvar2
+    */
+
+    Sub2 *sub2 = [Sub2 new];
+    sub2->isa = [Sub2 class];
+    sub2->subIvar = 10;
+    testassert(((uintptr_t *)sub2)[11] == 10);
+
+    testassert(class_getInstanceSize([Sub2 class]) == 13*sizeof(void*));
+
+    ivar = class_getInstanceVariable([Sub2 class], "subIvar");
+    testassert(ivar);
+    testassert(11*sizeof(void*) == ivar_getOffset(ivar));
+    testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
+
+    ivar = class_getInstanceVariable([ShrinkingSuper class], "superIvar");
+    testassert(!ivar);
+
+    if (objc_collecting_enabled()) {
+        const char *superlayout;
+        const char *sublayout;
+        superlayout = class_getIvarLayout([ShrinkingSuper class]);
+        sublayout = class_getIvarLayout([Sub2 class]);
+        testassert(superlayout == NULL);
+        testassert(0 == strcmp(sublayout, "\x01\xb1"));
+
+        superlayout = class_getWeakIvarLayout([ShrinkingSuper class]);
+        sublayout = class_getWeakIvarLayout([Sub2 class]);
+        testassert(superlayout == NULL);
+        testassert(0 == strcmp(sublayout, "\xb1\x10"));
+    }
+
+    /*
+       Ivars slide but GC layouts stay the same
+       Here, the last word of the superclass is misaligned, but 
+       its GC layout includes a bit for that whole word. 
+       Additionally, all of the subclass ivars fit into that word too, 
+       both before and after sliding. 
+       The runtime will try to slide the GC layout and must not be 
+       confused (rdar://6851700). Note that the second skip-word may or may 
+       not actually be included, because it crosses the end of the object.
+       
+
+       Compile-time layout of NoGCChangeSub: 
+         [0 scan] isa
+         [1 skip] d
+         [2 skip] superc1, subc3
+
+       Runtime layout of NoGCChangeSub:
+         [0 scan] isa
+         [1 skip] d
+         [2 skip] superc1, superc2, subc3
+    */
+    if (objc_collecting_enabled()) {
+        Ivar ivar1 = class_getInstanceVariable([NoGCChangeSub class], "superc1");
+        testassert(ivar1);
+        Ivar ivar2 = class_getInstanceVariable([NoGCChangeSub class], "superc2");
+        testassert(ivar2);
+        Ivar ivar3 = class_getInstanceVariable([NoGCChangeSub class], "subc3");
+        testassert(ivar3);
+        testassert(ivar_getOffset(ivar1) != ivar_getOffset(ivar2)  &&  
+                   ivar_getOffset(ivar1) != ivar_getOffset(ivar3)  &&  
+                   ivar_getOffset(ivar2) != ivar_getOffset(ivar3));
+    }
+
+    /* Ivar layout includes runs of 15 words.
+       rdar://6859875 this would generate a truncated GC layout.
+    */
+    if (objc_collecting_enabled()) {
+        const uint8_t *layout = (const uint8_t *)
+            class_getIvarLayout(objc_getClass("RunsOf15Sub"));
+        testassert(layout);
+        int totalSkip = 0;
+        int totalScan = 0;
+        // should find 30+ each of skip and scan
+        uint8_t c;
+        while ((c = *layout++)) {
+            totalSkip += c>>4;
+            totalScan += c&0xf;
+        }
+        testassert(totalSkip >= 30);
+        testassert(totalScan >= 30);
+    }
+
+// __OBJC2__
+#endif
+
+
+    /* 
+       Non-strong -> strong
+       Classes do not change size, but GC layouts must be updated.
+       Both new and old ABI detect this case (rdar://5774578)
+
+       Compile-time layout of MoreStrongSub: 
+         [0 scan] isa
+         [1 skip] superIvar
+         [2 scan] subIvar
+
+       Runtime layout of MoreStrongSub:
+         [0 scan] isa
+         [1 scan] superIvar
+         [2 scan] subIvar
+    */
+    testassert(class_getInstanceSize([MoreStrongSub class]) == 3*sizeof(void*));
+    if (objc_collecting_enabled()) {
+        const char *layout;
+        layout = class_getIvarLayout([MoreStrongSub class]);
+        testassert(layout == NULL);
+
+        layout = class_getWeakIvarLayout([MoreStrongSub class]);
+        testassert(layout == NULL);
+    }
+
+
+    /*
+       Strong -> weak
+       Classes do not change size, but GC layouts must be updated.
+       Old ABI intentionally does not detect this case (rdar://5774578)
+       
+       Compile-time layout of MoreWeakSub: 
+         [0 scan] isa
+         [1 scan] superIvar
+         [2 scan] subIvar
+
+       Runtime layout of MoreWeakSub:
+         [0 scan] isa
+         [1 weak] superIvar
+         [2 scan] subIvar
+    */
+    testassert(class_getInstanceSize([MoreWeakSub class]) == 3*sizeof(void*));
+    if (objc_collecting_enabled()) {
+        const char *layout;
+        layout = class_getIvarLayout([MoreWeakSub class]);
+#if __OBJC2__
+        testassert(0 == strcmp(layout, "\x01\x11"));
+#else
+        testassert(layout == NULL);
+#endif
+
+        layout = class_getWeakIvarLayout([MoreWeakSub class]);
+#if __OBJC2__
+        testassert(0 == strcmp(layout, "\x11\x10"));
+#else
+        testassert(layout == NULL);
+#endif
+    }
+
+
+    /*
+       Non-strong -> weak
+       Classes do not change size, but GC layouts must be updated.
+       Old ABI intentionally does not detect this case (rdar://5774578)
+       
+       Compile-time layout of MoreWeak2Sub: 
+         [0 scan] isa
+         [1 skip] superIvar
+         [2 scan] subIvar
+
+       Runtime layout of MoreWeak2Sub:
+         [0 scan] isa
+         [1 weak] superIvar
+         [2 scan] subIvar
+    */
+    testassert(class_getInstanceSize([MoreWeak2Sub class]) == 3*sizeof(void*));
+    if (objc_collecting_enabled()) {
+        const char *layout;
+        layout = class_getIvarLayout([MoreWeak2Sub class]);
+        testassert(0 == strcmp(layout, "\x01\x11")  ||
+                   0 == strcmp(layout, "\x01\x10\x01"));
+
+        layout = class_getWeakIvarLayout([MoreWeak2Sub class]);
+#if __OBJC2__
+        testassert(0 == strcmp(layout, "\x11\x10"));
+#else
+        testassert(layout == NULL);
+#endif
+    }
+
+
+    /*
+       Strong -> non-strong
+       Classes do not change size, but GC layouts must be updated.
+       Old ABI intentionally does not detect this case (rdar://5774578)
+
+       Compile-time layout of LessStrongSub: 
+         [0 scan] isa
+         [1 scan] superIvar
+         [2 scan] subIvar
+
+       Runtime layout of LessStrongSub:
+         [0 scan] isa
+         [1 skip] superIvar
+         [2 scan] subIvar
+    */
+    testassert(class_getInstanceSize([LessStrongSub class]) == 3*sizeof(void*));
+    if (objc_collecting_enabled()) {
+        const char *layout;
+        layout = class_getIvarLayout([LessStrongSub class]);
+#if __OBJC2__
+        testassert(0 == strcmp(layout, "\x01\x11"));
+#else
+        testassert(layout == NULL);
+#endif
+
+        layout = class_getWeakIvarLayout([LessStrongSub class]);
+        testassert(layout == NULL);
+    }
+
+
+    /*
+       Weak -> strong
+       Classes do not change size, but GC layouts must be updated.
+       Both new and old ABI detect this case (rdar://5774578 rdar://6924114)
+       
+       Compile-time layout of LessWeakSub: 
+         [0 scan] isa
+         [1 weak] superIvar
+         [2 scan] subIvar
+
+       Runtime layout of LessWeakSub:
+         [0 scan] isa
+         [1 scan] superIvar
+         [2 scan] subIvar
+    */
+    testassert(class_getInstanceSize([LessWeakSub class]) == 3*sizeof(void*));
+    if (objc_collecting_enabled()) {
+        const char *layout;
+        layout = class_getIvarLayout([LessWeakSub class]);
+        testassert(layout == NULL);
+
+        layout = class_getWeakIvarLayout([LessWeakSub class]);
+        testassert(layout == NULL);
+    }
+
+
+    /*
+       Weak -> non-strong
+       Classes do not change size, but GC layouts must be updated.
+       Old ABI intentionally does not detect this case (rdar://5774578)
+       
+       Compile-time layout of LessWeak2Sub: 
+         [0 scan] isa
+         [1 weak] superIvar
+         [2 scan] subIvar
+
+       Runtime layout of LessWeak2Sub:
+         [0 scan] isa
+         [1 skip] superIvar
+         [2 scan] subIvar
+    */
+    testassert(class_getInstanceSize([LessWeak2Sub class]) == 3*sizeof(void*));
+    if (objc_collecting_enabled()) {
+        const char *layout;
+        layout = class_getIvarLayout([LessWeak2Sub class]);
+        testassert(0 == strcmp(layout, "\x01\x11")  ||
+                   0 == strcmp(layout, "\x01\x10\x01"));
+
+        layout = class_getWeakIvarLayout([LessWeak2Sub class]);
+#if __OBJC2__
+        testassert(layout == NULL);
+#else
+        testassert(0 == strcmp(layout, "\x11\x10"));
+#endif
+    }
+
+
+    succeed(basename(argv[0]));
+    return 0;
+}