diff --git a/Test/setSuperclass.m b/Test/setSuperclass.m index 3ff468b..27994e0 100644 --- a/Test/setSuperclass.m +++ b/Test/setSuperclass.m @@ -32,6 +32,12 @@ __attribute__((objc_root_class)) @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 { @@ -141,6 +147,13 @@ static BOOL _otherInitializedClassInitialized = NO; @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 \ @@ -293,5 +306,28 @@ int main(int argc, char **argv) { 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; } diff --git a/dtable.c b/dtable.c index 36d0b32..8190089 100644 --- a/dtable.c +++ b/dtable.c @@ -419,21 +419,24 @@ static void rebaseDtableRecursive(Class cls, Class newSuper) dtable_t dtable = dtable_for_class(cls); uint32_t idx = 0; struct objc_method *method; - // Install all methods in the dtable with the correct ones. - while ((method = SparseArrayNext(temporaryDtable, &idx))) + // Install all methods from the parent that aren't overridden here. + while ((method = SparseArrayNext(parentDtable, &idx))) { - SparseArrayInsert(dtable, idx, method); + if (SparseArrayLookup(temporaryDtable, idx) == NULL) + { + SparseArrayInsert(dtable, idx, method); + SparseArrayInsert(temporaryDtable, idx, method); + } } idx = 0; // Now look at all of the methods in the dtable. If they're not ones from - // the dtable that we've just created, then they must come from the - // superclass, so replace them with whatever the superclass has (which may - // be NULL). + // the dtable that we've just created, then they must have come from the + // original superclass, so remove them by replacing them with NULL. while ((method = SparseArrayNext(dtable, &idx))) { if (SparseArrayLookup(temporaryDtable, idx) == NULL) { - SparseArrayInsert(dtable, idx, SparseArrayLookup(parentDtable, idx)); + SparseArrayInsert(dtable, idx, NULL); } } SparseArrayDestroy(temporaryDtable);