mirror of
https://github.com/darlinghq/darling-objc4.git
synced 2024-11-23 04:09:46 +00:00
307 lines
8.5 KiB
Plaintext
307 lines
8.5 KiB
Plaintext
// TEST_CONFIG
|
|
|
|
#if USE_FOUNDATION
|
|
#include <Foundation/Foundation.h>
|
|
#define SUPERCLASS NSObject
|
|
#define FILENAME "nscdtors.mm"
|
|
#else
|
|
#define SUPERCLASS TestRoot
|
|
#define FILENAME "cdtors.mm"
|
|
#endif
|
|
|
|
#include "test.h"
|
|
|
|
#include <pthread.h>
|
|
#include "objc/objc-internal.h"
|
|
#include "testroot.i"
|
|
|
|
static unsigned ctors1 = 0;
|
|
static unsigned dtors1 = 0;
|
|
static unsigned ctors2 = 0;
|
|
static unsigned dtors2 = 0;
|
|
|
|
class cxx1 {
|
|
unsigned & ctors;
|
|
unsigned& dtors;
|
|
|
|
public:
|
|
cxx1() : ctors(ctors1), dtors(dtors1) { ctors++; }
|
|
|
|
~cxx1() { dtors++; }
|
|
};
|
|
class cxx2 {
|
|
unsigned& ctors;
|
|
unsigned& dtors;
|
|
|
|
public:
|
|
cxx2() : ctors(ctors2), dtors(dtors2) { ctors++; }
|
|
|
|
~cxx2() { dtors++; }
|
|
};
|
|
|
|
/*
|
|
Class hierarchy:
|
|
TestRoot
|
|
CXXBase
|
|
NoCXXSub
|
|
CXXSub
|
|
|
|
This has two cxx-wielding classes, and a class in between without cxx.
|
|
*/
|
|
|
|
|
|
@interface CXXBase : SUPERCLASS {
|
|
cxx1 baseIvar;
|
|
}
|
|
@end
|
|
@implementation CXXBase @end
|
|
|
|
@interface NoCXXSub : CXXBase {
|
|
int nocxxIvar;
|
|
}
|
|
@end
|
|
@implementation NoCXXSub @end
|
|
|
|
@interface CXXSub : NoCXXSub {
|
|
cxx2 subIvar;
|
|
}
|
|
@end
|
|
@implementation CXXSub @end
|
|
|
|
|
|
void test_single(void)
|
|
{
|
|
// Single allocation
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
testonthread(^{
|
|
id o = [TestRoot new];
|
|
testassert(ctors1 == 0 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
testassert([o class] == [TestRoot class]);
|
|
RELEASE_VAR(o);
|
|
});
|
|
testcollect();
|
|
testassert(ctors1 == 0 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
testonthread(^{
|
|
id o = [CXXBase new];
|
|
testassert(ctors1 == 1 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
testassert([o class] == [CXXBase class]);
|
|
RELEASE_VAR(o);
|
|
});
|
|
testcollect();
|
|
testassert(ctors1 == 1 && dtors1 == 1 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
testonthread(^{
|
|
id o = [NoCXXSub new];
|
|
testassert(ctors1 == 1 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
testassert([o class] == [NoCXXSub class]);
|
|
RELEASE_VAR(o);
|
|
});
|
|
testcollect();
|
|
testassert(ctors1 == 1 && dtors1 == 1 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
testonthread(^{
|
|
id o = [CXXSub new];
|
|
testassert(ctors1 == 1 && dtors1 == 0 &&
|
|
ctors2 == 1 && dtors2 == 0);
|
|
testassert([o class] == [CXXSub class]);
|
|
RELEASE_VAR(o);
|
|
});
|
|
testcollect();
|
|
testassert(ctors1 == 1 && dtors1 == 1 &&
|
|
ctors2 == 1 && dtors2 == 1);
|
|
}
|
|
|
|
void test_inplace(void)
|
|
{
|
|
__unsafe_unretained volatile id o;
|
|
char o2[64];
|
|
|
|
id (*objc_constructInstance_fn)(Class, void*) = (id(*)(Class, void*))dlsym(RTLD_DEFAULT, "objc_constructInstance");
|
|
void (*objc_destructInstance_fn)(id) = (void(*)(id))dlsym(RTLD_DEFAULT, "objc_destructInstance");
|
|
|
|
// In-place allocation
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
o = objc_constructInstance_fn([TestRoot class], o2);
|
|
testassert(ctors1 == 0 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
testassert([o class] == [TestRoot class]);
|
|
objc_destructInstance_fn(o), o = nil;
|
|
testcollect();
|
|
testassert(ctors1 == 0 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
o = objc_constructInstance_fn([CXXBase class], o2);
|
|
testassert(ctors1 == 1 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
testassert([o class] == [CXXBase class]);
|
|
objc_destructInstance_fn(o), o = nil;
|
|
testcollect();
|
|
testassert(ctors1 == 1 && dtors1 == 1 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
o = objc_constructInstance_fn([NoCXXSub class], o2);
|
|
testassert(ctors1 == 1 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
testassert([o class] == [NoCXXSub class]);
|
|
objc_destructInstance_fn(o), o = nil;
|
|
testcollect();
|
|
testassert(ctors1 == 1 && dtors1 == 1 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
o = objc_constructInstance_fn([CXXSub class], o2);
|
|
testassert(ctors1 == 1 && dtors1 == 0 &&
|
|
ctors2 == 1 && dtors2 == 0);
|
|
testassert([o class] == [CXXSub class]);
|
|
objc_destructInstance_fn(o), o = nil;
|
|
testcollect();
|
|
testassert(ctors1 == 1 && dtors1 == 1 &&
|
|
ctors2 == 1 && dtors2 == 1);
|
|
}
|
|
|
|
|
|
#if __has_feature(objc_arc)
|
|
|
|
void test_batch(void)
|
|
{
|
|
// not converted to ARC yet
|
|
return;
|
|
}
|
|
|
|
#else
|
|
|
|
// Like class_createInstances(), but refuses to accept zero allocations
|
|
static unsigned
|
|
reallyCreateInstances(Class cls, size_t extraBytes, id *dst, unsigned want)
|
|
{
|
|
unsigned count;
|
|
while (0 == (count = class_createInstances(cls, extraBytes, dst, want))) {
|
|
testprintf("class_createInstances created nothing; retrying\n");
|
|
RELEASE_VALUE([[TestRoot alloc] init]);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void test_batch(void)
|
|
{
|
|
id o2[100];
|
|
unsigned int count, i;
|
|
|
|
// Batch allocation
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
o2[i] = (id)malloc(class_getInstanceSize([TestRoot class]));
|
|
}
|
|
for (i = 0; i < 100; i++) {
|
|
free(o2[i]);
|
|
}
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
count = reallyCreateInstances([TestRoot class], 0, o2, 10);
|
|
testassert(count > 0);
|
|
testassert(ctors1 == 0 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
for (i = 0; i < count; i++) testassert([o2[i] class] == [TestRoot class]);
|
|
for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
|
|
testcollect();
|
|
testassert(ctors1 == 0 && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
// prime batch allocator
|
|
free(malloc(class_getInstanceSize([TestRoot class])));
|
|
}
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
count = reallyCreateInstances([CXXBase class], 0, o2, 10);
|
|
testassert(count > 0);
|
|
testassert(ctors1 == count && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
for (i = 0; i < count; i++) testassert([o2[i] class] == [CXXBase class]);
|
|
for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
|
|
testcollect();
|
|
testassert(ctors1 == count && dtors1 == count &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
// prime batch allocator
|
|
free(malloc(class_getInstanceSize([TestRoot class])));
|
|
}
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
count = reallyCreateInstances([NoCXXSub class], 0, o2, 10);
|
|
testassert(count > 0);
|
|
testassert(ctors1 == count && dtors1 == 0 &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
for (i = 0; i < count; i++) testassert([o2[i] class] == [NoCXXSub class]);
|
|
for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
|
|
testcollect();
|
|
testassert(ctors1 == count && dtors1 == count &&
|
|
ctors2 == 0 && dtors2 == 0);
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
// prime batch allocator
|
|
free(malloc(class_getInstanceSize([TestRoot class])));
|
|
}
|
|
|
|
ctors1 = dtors1 = ctors2 = dtors2 = 0;
|
|
count = reallyCreateInstances([CXXSub class], 0, o2, 10);
|
|
testassert(count > 0);
|
|
testassert(ctors1 == count && dtors1 == 0 &&
|
|
ctors2 == count && dtors2 == 0);
|
|
for (i = 0; i < count; i++) testassert([o2[i] class] == [CXXSub class]);
|
|
for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
|
|
testcollect();
|
|
testassert(ctors1 == count && dtors1 == count &&
|
|
ctors2 == count && dtors2 == count);
|
|
}
|
|
|
|
// not ARC
|
|
#endif
|
|
|
|
|
|
int main()
|
|
{
|
|
for (int i = 0; i < 1000; i++) {
|
|
testonthread(^{ test_single(); });
|
|
testonthread(^{ test_inplace(); });
|
|
testonthread(^{ test_batch(); });
|
|
}
|
|
|
|
testonthread(^{ test_single(); });
|
|
testonthread(^{ test_inplace(); });
|
|
testonthread(^{ test_batch(); });
|
|
|
|
leak_mark();
|
|
|
|
for (int i = 0; i < 1000; i++) {
|
|
testonthread(^{ test_single(); });
|
|
testonthread(^{ test_inplace(); });
|
|
testonthread(^{ test_batch(); });
|
|
}
|
|
|
|
leak_check(0);
|
|
|
|
// fixme ctor exceptions aren't caught inside .cxx_construct ?
|
|
// Single allocation, ctors fail
|
|
// In-place allocation, ctors fail
|
|
// Batch allocation, ctors fail for every object
|
|
// Batch allocation, ctors fail for every other object
|
|
|
|
succeed(FILENAME);
|
|
}
|