Merge pull request #56 from Microsoft/cherry-pick-aa58f6f
teach class_setSuperclass about metaclasses, subclass lists, and dtablesmain
commit
c5b96fafe9
@ -0,0 +1,293 @@
|
||||
#include "../objc/runtime.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
__attribute__((objc_root_class))
|
||||
@interface Root @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
|
||||
|
||||
@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
|
||||
|
||||
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) {
|
||||
Class firstSuperclass = objc_getClass("DefaultSuperclass");
|
||||
Class subclass1 = objc_getClass("Subclass1");
|
||||
|
||||
/* 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([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);
|
||||
|
||||
Subclass2 *subclass2instance2 = class_createInstance(subclass2, 0);
|
||||
|
||||
// CLASS
|
||||
// can call method on subclass
|
||||
expect([subclass2instance1 existsOnSubclass2]);
|
||||
// can call method on _new_ superclass
|
||||
expect([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]);
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
Loading…
Reference in New Issue