--- /dev/null
+// TEST_CONFIG
+
+// initialize.m
+// Test basic +initialize behavior
+// * +initialize before class method
+// * superclass +initialize before subclass +initialize
+// * subclass inheritance of superclass implementation
+// * messaging during +initialize
+// * +initialize provoked by class_getMethodImplementation
+// * +initialize not provoked by objc_getClass
+#include "test.h"
+#include "testroot.i"
+
+int state = 0;
+
+@interface Super0 : TestRoot @end
+@implementation Super0
++(void)initialize {
+ fail("objc_getClass() must not trigger +initialize");
+}
+@end
+
+@interface Super : TestRoot @end
+@implementation Super
++(void)initialize {
+ testprintf("in [Super initialize]\n");
+ testassert(state == 0);
+ state = 1;
+}
++(void)method {
+ fail("[Super method] shouldn't be called");
+}
+@end
+
+@interface Sub : Super @end
+@implementation Sub
++(void)initialize {
+ testprintf("in [Sub initialize]\n");
+ testassert(state == 1);
+ state = 2;
+}
++(void)method {
+ testprintf("in [Sub method]\n");
+ testassert(state == 2);
+ state = 3;
+}
+@end
+
+
+@interface Super2 : TestRoot @end
+@interface Sub2 : Super2 @end
+
+@implementation Super2
++(void)initialize {
+ if (self == objc_getClass("Sub2")) {
+ testprintf("in [Super2 initialize] of Sub2\n");
+ testassert(state == 1);
+ state = 2;
+ } else if (self == objc_getClass("Super2")) {
+ testprintf("in [Super2 initialize] of Super2\n");
+ testassert(state == 0);
+ state = 1;
+ } else {
+ fail("in [Super2 initialize] of unknown class");
+ }
+}
++(void)method {
+ testprintf("in [Super2 method]\n");
+ testassert(state == 2);
+ state = 3;
+}
+@end
+
+@implementation Sub2
+// nothing here
+@end
+
+
+@interface Super3 : TestRoot @end
+@interface Sub3 : Super3 @end
+
+@implementation Super3
++(void)initialize {
+ if (self == [Sub3 class]) { // this message triggers [Sub3 initialize]
+ testprintf("in [Super3 initialize] of Sub3\n");
+ testassert(state == 0);
+ state = 1;
+ } else if (self == [Super3 class]) {
+ testprintf("in [Super3 initialize] of Super3\n");
+ testassert(state == 1);
+ state = 2;
+ } else {
+ fail("in [Super3 initialize] of unknown class");
+ }
+}
++(void)method {
+ testprintf("in [Super3 method]\n");
+ testassert(state == 2);
+ state = 3;
+}
+@end
+
+@implementation Sub3
+// nothing here
+@end
+
+
+@interface Super4 : TestRoot @end
+@implementation Super4
+-(void)instanceMethod {
+ testassert(state == 1);
+ state = 2;
+}
++(void)initialize {
+ testprintf("in [Super4 initialize]\n");
+ testassert(state == 0);
+ state = 1;
+ id x = [[self alloc] init];
+ [x instanceMethod];
+ RELEASE_VALUE(x);
+}
+@end
+
+
+@interface Super5 : TestRoot @end
+@implementation Super5
+-(void)instanceMethod {
+}
++(void)classMethod {
+ testassert(state == 1);
+ state = 2;
+}
++(void)initialize {
+ testprintf("in [Super5 initialize]\n");
+ testassert(state == 0);
+ state = 1;
+ class_getMethodImplementation(self, @selector(instanceMethod));
+ // this is the "memoized" case for getNonMetaClass
+ class_getMethodImplementation(object_getClass(self), @selector(classMethod));
+ [self classMethod];
+}
+@end
+
+
+@interface Super6 : TestRoot @end
+@interface Sub6 : Super6 @end
+@implementation Super6
++(void)initialize {
+ static bool once;
+ bool wasOnce;
+ testprintf("in [Super6 initialize] (#%d)\n", 1+(int)once);
+ if (!once) {
+ once = true;
+ wasOnce = true;
+ testassert(state == 0);
+ state = 1;
+ } else {
+ wasOnce = false;
+ testassert(state == 2);
+ state = 3;
+ }
+ [Sub6 class];
+ if (wasOnce) {
+ testassert(state == 5);
+ state = 6;
+ } else {
+ testassert(state == 3);
+ state = 4;
+ }
+}
+@end
+@implementation Sub6
++(void)initialize {
+ testprintf("in [Sub6 initialize]\n");
+ testassert(state == 1);
+ state = 2;
+ [super initialize];
+ testassert(state == 4);
+ state = 5;
+}
+@end
+
+
+@interface Super7 : TestRoot @end
+@interface Sub7 : Super7 @end
+@implementation Super7
++(void)initialize {
+ static bool once;
+ bool wasOnce;
+ testprintf("in [Super7 initialize] (#%d)\n", 1+(int)once);
+ if (!once) {
+ once = true;
+ wasOnce = true;
+ testassert(state == 0);
+ state = 1;
+ } else {
+ wasOnce = false;
+ testassert(state == 2);
+ state = 3;
+ }
+ [Sub7 class];
+ if (wasOnce) {
+ testassert(state == 5);
+ state = 6;
+ } else {
+ testassert(state == 3);
+ state = 4;
+ }
+}
+@end
+@implementation Sub7
++(void)initialize {
+ testprintf("in [Sub7 initialize]\n");
+ testassert(state == 1);
+ state = 2;
+ [super initialize];
+ testassert(state == 4);
+ state = 5;
+}
+@end
+
+
+
+@interface SuperThrower : TestRoot @end
+@implementation SuperThrower
++(void)initialize {
+ testprintf("in [SuperThrower initialize]\n");
+ testassert(state == 0);
+ state = 10;
+ @throw AUTORELEASE([TestRoot new]);
+ fail("@throw didn't throw");
+}
+@end
+
+@interface SubThrower : SuperThrower @end
+@implementation SubThrower
++(void)initialize {
+ testprintf("in [SubThrower initialize]\n");
+ testassert(state == 0);
+ state = 20;
+}
+@end
+
+int main()
+{
+ Class cls;
+
+ // objc_getClass() must not +initialize anything
+ state = 0;
+ objc_getClass("Super0");
+ testassert(state == 0);
+
+ // initialize superclass, then subclass
+ state = 0;
+ [Sub method];
+ testassert(state == 3);
+
+ // check subclass's inheritance of superclass initialize
+ state = 0;
+ [Sub2 method];
+ testassert(state == 3);
+
+ // check subclass method called from superclass initialize
+ state = 0;
+ [Sub3 method];
+ testassert(state == 3);
+
+ // check class_getMethodImplementation (instance method)
+ state = 0;
+ cls = objc_getClass("Super4");
+ testassert(state == 0);
+ class_getMethodImplementation(cls, @selector(classMethod));
+ testassert(state == 2);
+
+ // check class_getMethodImplementation (class method)
+ // this is the "slow" case for getNonMetaClass
+ state = 0;
+ cls = objc_getClass("Super5");
+ testassert(state == 0);
+ class_getMethodImplementation(object_getClass(cls), @selector(instanceMethod));
+ testassert(state == 2);
+
+ // check +initialize cycles
+ // this is the "cls is a subclass" case for getNonMetaClass
+ state = 0;
+ [Super6 class];
+ testassert(state == 6);
+
+ // check +initialize cycles
+ // this is the "cls is a subclass" case for getNonMetaClass
+ state = 0;
+ [Sub7 class];
+ testassert(state == 6);
+
+ // exception from +initialize must be handled cleanly
+ PUSH_POOL {
+ alarm(3);
+ testonthread( ^{
+ @try {
+ state = 0;
+ [SuperThrower class];
+ fail("where's the beef^Wexception?");
+ } @catch (...) {
+ testassert(state == 10);
+ state = 11;
+ }
+ testassert(state == 11);
+ });
+ @try {
+ state = 0;
+ [SuperThrower class];
+ testassert(state == 0);
+ [SubThrower class];
+ testassert(state == 20);
+ } @catch (...) {
+ fail("+initialize called again after exception");
+ }
+ } POP_POOL;
+
+ succeed(__FILE__);
+
+ return 0;
+}