You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
12 KiB
Objective-C

#include "../objc/runtime.h"
#include <assert.h>
#include <stdio.h>
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
__attribute__((objc_root_class))
@interface Root
{
id isa;
}
@end
@interface DefaultSuperclass: Root @end
// test: new superclass when not initialized at the time of class_setSuperclass
@interface NotInitializedSuperclass1: Root @end
@interface Subclass1: DefaultSuperclass @end
// test: new superclass when already initialized at the time of class_setSuperclass
@interface NotInitializedSuperclass2: Root @end
@interface Subclass2: DefaultSuperclass @end
@interface Subclass2Subclass: Subclass2 @end
@interface ChangesDuringInitialize: DefaultSuperclass @end
// test: class gets reparented under its parent's parent.
@interface RemovedFromHierarchy: DefaultSuperclass @end
@interface MovesUpwardsInHierarchy: RemovedFromHierarchy @end
// test: one class initializes anotherwhile initializing during class_setSuperclass
@interface OtherInitializedClass: Root @end
@interface InitializesOneClassWhileBeingInitialized: NotInitializedSuperclass2 @end
@interface Subclass3: DefaultSuperclass @end
// test: transitioning an initialized class to an initialized superclass
// Test4Subclass only inherits access to "onlyExistsOnFinalSuperclass" from
// its new superclass's superclass (Test4FinalSuperclass)
@interface Test4Subclass: Root @end
@interface Test4FinalSuperclass: DefaultSuperclass @end
@implementation Root
+ (Class)class { return self; }
+ (BOOL)respondsToSelector:(SEL)selector {
return class_respondsToSelector(object_getClass(self), selector);
}
+ (BOOL)instancesRespondToSelector:(SEL)selector {
return class_respondsToSelector(self, selector);
}
@end
@implementation NotInitializedSuperclass1
static BOOL _notInitializedSuperclass1Initialized = NO;
+ (void)initialize {
_notInitializedSuperclass1Initialized = YES;
}
+ (void)existsOnNotInitializedSuperclassMeta { };
+ (int)sameNameMeta { return 12; }
+ (int)overriddenMeta { return 12; }
- (BOOL)existsOnNotInitializedSuperclass { return YES; }
- (int)sameName { return 2; }
- (int)overridden { return 2; }
@end
@implementation NotInitializedSuperclass2
static BOOL _notInitializedSuperclass2Initialized = NO;
+ (void)initialize {
_notInitializedSuperclass2Initialized = YES;
}
+ (void)existsOnNotInitializedSuperclassMeta { };
+ (int)sameNameMeta { return 13; }
+ (int)overriddenMeta { return 13; }
- (BOOL)existsOnNotInitializedSuperclass { return YES; }
- (int)sameName { return 3; }
- (int)overridden { return 3; }
@end
@implementation DefaultSuperclass
static BOOL _alreadyInitializedSuperclassInitialized = NO;
+ (void)initialize {
_alreadyInitializedSuperclassInitialized = YES;
}
+ (void)existsOnDefaultSuperclassMeta { };
+ (int)sameNameMeta { return 14; }
+ (int)overriddenMeta { return 14; }
- (BOOL)existsOnDefaultSuperclass { return YES; }
- (int)sameName { return 4; }
- (int)overridden { return 4; }
@end
@implementation Subclass1
static BOOL _subclass1Initialized = NO;
+ (void)initialize {
_subclass1Initialized = YES;
}
+ (int)overriddenMeta { return 15; } // shadows 14
- (BOOL)existsOnSubclass1 { return YES; }
- (int)overridden { return 5; } // shadows 4
@end
@implementation Subclass2
static BOOL _subclass2Initialized = NO;
+ (void)initialize {
_subclass2Initialized = YES;
}
+ (int)overriddenMeta { return 16; } // shadows 14
- (BOOL)existsOnSubclass2 { return YES; }
- (int)overridden { return 6; } // shadows 4
- (int)intermediateOverride { return 100; }
@end
@implementation Subclass2Subclass
- (int)intermediateOverride { return 200; }
@end
@implementation ChangesDuringInitialize
+ (void)initialize {
class_setSuperclass(self, objc_getClass("NotInitializedSuperclass1"));
}
+ (int)overriddenMeta { return 18; }
@end
@implementation RemovedFromHierarchy
+ (int)overriddenMeta { return 19; } // shadows 14 on DefaultSuperClass
+ (int)sameNameMeta { return 19; } // shadows 14 on DefaultSuperClass
+ (void)onlyExistsOnRemovedClassMeta { }
- (void)onlyExistsOnRemovedClass { }
@end
@implementation MovesUpwardsInHierarchy
+ (int)overriddenMeta { return 20; } // shadows 19 on RemovedFromHierarchy or 14 on DefaultSuperClass
@end
@implementation OtherInitializedClass
static BOOL _otherInitializedClassInitialized = NO;
+ (void)initialize {
_otherInitializedClassInitialized = YES;
}
@end
@implementation InitializesOneClassWhileBeingInitialized
+ (void)initialize {
[OtherInitializedClass class];
}
@end
@implementation Subclass3
@end
@implementation Test4Subclass
@end
@implementation Test4FinalSuperclass
+ (int)onlyExistsOnFinalSuperclassMeta { return 501; }
- (int)onlyExistsOnFinalSuperclass { return 500; }
@end
static int failures = 0;
#define expect(x) do \
{ \
if (!(x)) \
{ \
fprintf(stderr, "expectation FAILED: %s\n", #x); \
++failures; \
} \
} while(0)
int main(int argc, char **argv) {
/* Transitioning to a new superclass before +initialize has been called */
{
Class subclass1 = objc_getClass("Subclass1");
Class secondSuperclass = objc_getClass("NotInitializedSuperclass1");
assert(!_notInitializedSuperclass1Initialized);
assert(!_subclass1Initialized);
class_setSuperclass(subclass1, secondSuperclass);
// assert: dtable has not been installed; new superclass is still not initialized
assert(!_notInitializedSuperclass1Initialized);
[Subclass1 class];
// initialization and dtable installation has taken place
assert(_notInitializedSuperclass1Initialized);
Subclass1 *subclass1instance1 = class_createInstance(subclass1, 0);
// CLASS
// can call method on subclass
expect([subclass1instance1 existsOnSubclass1]);
// can call method on _new_ superclass
expect([(id)subclass1instance1 existsOnNotInitializedSuperclass]);
// does not respond to selector from original superclass
expect(![subclass1 instancesRespondToSelector:@selector(existsOnDefaultSuperclass)]);
// *does* respond to selector from new superclass
expect([subclass1 instancesRespondToSelector:@selector(existsOnNotInitializedSuperclass)]);
// method existing on both old and new superclass kept, IMP updated
expect(2 == [subclass1instance1 sameName]);
// method existing on subclass, old and new superclass kept, IMP kept
expect(5 == [subclass1instance1 overridden]);
// METACLASS
// metaclass does not respond to selector from original meta superclass
expect(![subclass1 respondsToSelector:@selector(existsOnDefaultSuperclassMeta)]);
// metaclass *does* respond to selector from new meta superclass
expect([subclass1 respondsToSelector:@selector(existsOnNotInitializedSuperclassMeta)]);
// method existing on both old and new superclass kept, IMP updated
expect(12 == [subclass1 sameNameMeta]);
// method existing on subclass, old and new superclass kept, IMP kept
expect(15 == [subclass1 overriddenMeta]);
}
/* Transitioning to a new superclass when +initialize has already been called */
{
Class subclass2 = objc_getClass("Subclass2");
Class secondSuperclass = objc_getClass("NotInitializedSuperclass2");
assert(!_notInitializedSuperclass2Initialized);
assert(!_subclass2Initialized);
[Subclass2 class];
[Subclass2Subclass class]; // Make sure the subclass is initialized too.
assert(_alreadyInitializedSuperclassInitialized);
assert(_subclass2Initialized);
Subclass2 *subclass2instance1 = class_createInstance(subclass2, 0);
assert([subclass2instance1 existsOnSubclass2]);
class_setSuperclass(subclass2, secondSuperclass);
assert(_notInitializedSuperclass2Initialized);
// CLASS
// can call method on subclass
expect([subclass2instance1 existsOnSubclass2]);
// can call method on _new_ superclass
expect([(id)subclass2instance1 existsOnNotInitializedSuperclass]);
// does not respond to selector from original superclass
expect(![subclass2 instancesRespondToSelector:@selector(existsOnDefaultSuperclass)]);
// *does* respond to selector from new superclass
expect([subclass2 instancesRespondToSelector:@selector(existsOnNotInitializedSuperclass)]);
// method existing on both old and new superclass kept, IMP updated
expect(3 == [subclass2instance1 sameName]);
// method existing on subclass, old and new superclass kept, IMP kept
expect(6 == [subclass2instance1 overridden]);
// method existing only on subclass preserved
expect(100 == [subclass2instance1 intermediateOverride]);
// METACLASS
// metaclass does not respond to selector from original meta superclass
expect(![subclass2 respondsToSelector:@selector(existsOnDefaultSuperclassMeta)]);
// metaclass *does* respond to selector from new meta superclass
expect([subclass2 respondsToSelector:@selector(existsOnNotInitializedSuperclassMeta)]);
// method existing on both old and new superclass kept, IMP updated
expect(13 == [subclass2 sameNameMeta]);
// method existing on subclass, old and new superclass kept, IMP kept
expect(16 == [subclass2 overriddenMeta]);
// SUBCLASS
Subclass2 *subclass2subclassInstance = class_createInstance([Subclass2Subclass class], 0);
expect(![Subclass2Subclass instancesRespondToSelector:@selector(existsOnDefaultSuperclass)]);
expect(![Subclass2Subclass respondsToSelector:@selector(existsOnDefaultSuperclassMeta)]);
expect(3 == [subclass2subclassInstance sameName]);
expect(6 == [subclass2subclassInstance overridden]);
expect(200 == [subclass2subclassInstance intermediateOverride]);
expect(13 == [Subclass2Subclass sameNameMeta]);
expect(16 == [Subclass2Subclass overriddenMeta]);
}
/* Transitioning ourselves to a new superclass while +initialize is running */
{
expect(12 == [ChangesDuringInitialize sameNameMeta]);
expect(18 == [ChangesDuringInitialize overriddenMeta]);
}
/* Transitioning to a superclass that's in our inheritance hierarchy already */
{
assert(20 == [MovesUpwardsInHierarchy overriddenMeta]);
assert(19 == [MovesUpwardsInHierarchy sameNameMeta]);
assert([MovesUpwardsInHierarchy respondsToSelector:@selector(onlyExistsOnRemovedClassMeta)]);
assert([MovesUpwardsInHierarchy instancesRespondToSelector:@selector(onlyExistsOnRemovedClass)]);
class_setSuperclass([MovesUpwardsInHierarchy class], [DefaultSuperclass class]);
expect(20 == [MovesUpwardsInHierarchy overriddenMeta]); // still overridden
expect(14 == [MovesUpwardsInHierarchy sameNameMeta]); // falls back to DefaultSuperclass
expect(![MovesUpwardsInHierarchy respondsToSelector:@selector(onlyExistsOnRemovedClassMeta)]);
expect(![MovesUpwardsInHierarchy instancesRespondToSelector:@selector(onlyExistsOnRemovedClass)]);
}
/* Transitioning to a superclass that may cause initialize lock contention */
{
assert(!_otherInitializedClassInitialized);
expect(14 == [Subclass3 sameNameMeta]);
expect(14 == [Subclass3 overriddenMeta]);
class_setSuperclass([Subclass3 class], objc_getClass("InitializesOneClassWhileBeingInitialized"));
expect(_otherInitializedClassInitialized);
expect(13 == [Subclass3 sameNameMeta]);
expect(13 == [Subclass3 overriddenMeta]);
}
/* Transitioning an initialized class to an initialized superclass. */
{
Class test4subclass = objc_getClass("Test4Subclass");
Class newSuperclass = objc_getClass("Test4FinalSuperclass");
// Make sure every class in the hierarchy is initialized.
[Test4Subclass class];
[Test4FinalSuperclass class];
expect(![test4subclass respondsToSelector:@selector(onlyExistsOnFinalSuperclassMeta)]);
expect(![test4subclass instancesRespondToSelector:@selector(onlyExistsOnFinalSuperclass)]);
class_setSuperclass(test4subclass, newSuperclass);
Test4Subclass *test4instance = class_createInstance(test4subclass, 0);
expect([test4subclass respondsToSelector:@selector(onlyExistsOnFinalSuperclassMeta)]);
expect([test4subclass instancesRespondToSelector:@selector(onlyExistsOnFinalSuperclass)]);
expect(501 == [(id)test4subclass onlyExistsOnFinalSuperclassMeta]);
expect(500 == [(id)test4instance onlyExistsOnFinalSuperclass]);
}
return failures;
}