mirror of
https://github.com/darlinghq/darling-objc4.git
synced 2024-11-23 04:09:46 +00:00
506 lines
14 KiB
Objective-C
506 lines
14 KiB
Objective-C
/*
|
|
TEST_BUILD
|
|
$C{COMPILE} -fobjc-weak $DIR/ivarSlide1.m $DIR/ivarSlide.m -o ivarSlide.exe
|
|
END
|
|
*/
|
|
|
|
#include "test.h"
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <objc/objc-runtime.h>
|
|
#include <objc/objc-auto.h>
|
|
|
|
// fixme should check ARC layout handling
|
|
// current test checks GC layout handling which is dead
|
|
#define FIXME_CHECK_ARC_LAYOUTS 0
|
|
|
|
// ARC doesn't like __strong void* or __weak void*
|
|
#define gc_weak
|
|
#define gc_strong
|
|
|
|
#define OLD 1
|
|
#include "ivarSlide.h"
|
|
|
|
#define ustrcmp(a, b) strcmp((char *)a, (char *)b)
|
|
|
|
// Aliasing-friendly way to read from a fixed offset in an object.
|
|
uintptr_t readWord(id obj, int offset) {
|
|
uintptr_t value;
|
|
char *ptr = (char *)(__bridge void*)obj;
|
|
memcpy(&value, ptr + offset * sizeof(uintptr_t), sizeof(uintptr_t));
|
|
return value;
|
|
}
|
|
|
|
#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;
|
|
gc_strong void* subIvar2;
|
|
gc_weak void* subIvar3;
|
|
#ifdef __cplusplus
|
|
CXX cxx;
|
|
#else
|
|
// same layout as cxx
|
|
uintptr_t cxx_magic;
|
|
#endif
|
|
}
|
|
@end
|
|
|
|
@implementation Sub @end
|
|
|
|
|
|
@interface Sub2 : ShrinkingSuper {
|
|
@public
|
|
gc_weak void* subIvar;
|
|
gc_strong void* 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 __has_feature(objc_arc)
|
|
testwarn("fixme check ARC layouts too");
|
|
#endif
|
|
|
|
/*
|
|
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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *bitfieldlayout;
|
|
bitfieldlayout = class_getIvarLayout([Bitfields class]);
|
|
testassert(0 == ustrcmp(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;
|
|
testassertequal(readWord(sub, 2), 10);
|
|
|
|
#ifdef __cplusplus
|
|
testassertequal(readWord(sub, 5), 1);
|
|
testassertequal(sub->cxx.magic, 1);
|
|
sub->cxx.magic++;
|
|
testassertequal(readWord(sub, 5), 2);
|
|
testassertequal(sub->cxx.magic, 2);
|
|
# if __has_feature(objc_arc)
|
|
sub = nil;
|
|
# else
|
|
[sub dealloc];
|
|
# endif
|
|
testassert(CXX::count == 2);
|
|
#endif
|
|
|
|
testassert(class_getInstanceSize([Sub class]) == 6*sizeof(void*));
|
|
|
|
ivar = class_getInstanceVariable([Sub class], "subIvar");
|
|
testassert(ivar);
|
|
testassert(2*sizeof(void*) == (size_t)ivar_getOffset(ivar));
|
|
testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
|
|
testassert(0 == strcmp(ivar_getTypeEncoding(ivar),
|
|
#if __LP64__
|
|
"Q"
|
|
#else
|
|
"L"
|
|
#endif
|
|
));
|
|
|
|
#ifdef __cplusplus
|
|
ivar = class_getInstanceVariable([Sub class], "cxx");
|
|
testassert(ivar);
|
|
#endif
|
|
|
|
ivar = class_getInstanceVariable([Super class], "superIvar");
|
|
testassert(ivar);
|
|
testassert(sizeof(void*) == (size_t)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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *superlayout;
|
|
const uint8_t *sublayout;
|
|
superlayout = class_getIvarLayout([Super class]);
|
|
sublayout = class_getIvarLayout([Sub class]);
|
|
testassert(0 == ustrcmp(superlayout, "\x01\x10"));
|
|
testassert(0 == ustrcmp(sublayout, "\x01\x21\x20"));
|
|
|
|
superlayout = class_getWeakIvarLayout([Super class]);
|
|
sublayout = class_getWeakIvarLayout([Sub class]);
|
|
testassert(superlayout == NULL);
|
|
testassert(0 == ustrcmp(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->subIvar = (void *)10;
|
|
testassertequal(readWord(sub2, 11), 10);
|
|
|
|
testassertequal(class_getInstanceSize([Sub2 class]), 13*sizeof(void*));
|
|
|
|
ivar = class_getInstanceVariable([Sub2 class], "subIvar");
|
|
testassert(ivar);
|
|
testassertequal(11*sizeof(void*), (size_t)ivar_getOffset(ivar));
|
|
testassert(0 == strcmp(ivar_getName(ivar), "subIvar"));
|
|
|
|
ivar = class_getInstanceVariable([ShrinkingSuper class], "superIvar");
|
|
testassert(!ivar);
|
|
|
|
if (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *superlayout;
|
|
const uint8_t *sublayout;
|
|
superlayout = class_getIvarLayout([ShrinkingSuper class]);
|
|
sublayout = class_getIvarLayout([Sub2 class]);
|
|
// only `isa` is left; superIvar[] and superIvar2[] are gone
|
|
testassert(superlayout == NULL || 0 == ustrcmp(superlayout, "\x01"));
|
|
testassert(0 == ustrcmp(sublayout, "\x01\xb1"));
|
|
|
|
superlayout = class_getWeakIvarLayout([ShrinkingSuper class]);
|
|
sublayout = class_getWeakIvarLayout([Sub2 class]);
|
|
testassert(superlayout == NULL);
|
|
testassert(0 == ustrcmp(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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *layout =
|
|
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);
|
|
}
|
|
|
|
|
|
/*
|
|
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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *layout;
|
|
layout = class_getIvarLayout([MoreWeakSub class]);
|
|
testassert(0 == ustrcmp(layout, "\x01\x11"));
|
|
|
|
layout = class_getWeakIvarLayout([MoreWeakSub class]);
|
|
testassert(0 == ustrcmp(layout, "\x11\x10"));
|
|
}
|
|
|
|
|
|
/*
|
|
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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *layout;
|
|
layout = class_getIvarLayout([MoreWeak2Sub class]);
|
|
testassert(0 == ustrcmp(layout, "\x01\x11") ||
|
|
0 == ustrcmp(layout, "\x01\x10\x01"));
|
|
|
|
layout = class_getWeakIvarLayout([MoreWeak2Sub class]);
|
|
testassert(0 == ustrcmp(layout, "\x11\x10"));
|
|
}
|
|
|
|
|
|
/*
|
|
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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *layout;
|
|
layout = class_getIvarLayout([LessStrongSub class]);
|
|
testassert(0 == ustrcmp(layout, "\x01\x11"));
|
|
|
|
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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *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 (FIXME_CHECK_ARC_LAYOUTS) {
|
|
const uint8_t *layout;
|
|
layout = class_getIvarLayout([LessWeak2Sub class]);
|
|
testassert(0 == ustrcmp(layout, "\x01\x11") ||
|
|
0 == ustrcmp(layout, "\x01\x10\x01"));
|
|
|
|
layout = class_getWeakIvarLayout([LessWeak2Sub class]);
|
|
testassert(layout == NULL);
|
|
}
|
|
|
|
|
|
succeed(basename(argv[0]));
|
|
return 0;
|
|
}
|