From 0e0677a2f0c08632e898722280bed7e842ab8f84 Mon Sep 17 00:00:00 2001 From: Nicola Pero Date: Sat, 6 Aug 2011 14:20:09 +0000 Subject: [PATCH] In libobjc/: 2011-08-06 Nicola Pero In libobjc/: 2011-08-06 Nicola Pero PR libobjc/50002 * class.c (__objc_update_classes_with_methods): Iterate over meta classes as well as normal classes when refreshing the method implementations. This fixes replacing class methods. 2011-08-06 Nicola Pero * class.c (class_getSuperclass): Fixed to work with meta classes still in construction too. In gcc/testsuite/: 2011-08-06 Nicola Pero PR libobjc/50002 * objc.dg/gnu-api-2-class.m: Updated comments. * obj-c++.dg/gnu-api-2-class.mm: Likewise. * objc.dg/gnu-api-2-class-meta.m: New test. * obj-c++.dg/gnu-api-2-class-meta.mm: Likewise. 2011-08-06 Nicola Pero PR libobjc/49882 * obj-c++.dg/gnu-api-2-class.mm (main): Test class_getSuperclass() with classes that are in construction. From-SVN: r177510 --- gcc/testsuite/ChangeLog | 14 + .../obj-c++.dg/gnu-api-2-class-meta.mm | 327 ++++++++++++++++++ gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm | 12 +- gcc/testsuite/objc.dg/gnu-api-2-class-meta.m | 327 ++++++++++++++++++ gcc/testsuite/objc.dg/gnu-api-2-class.m | 6 +- libobjc/ChangeLog | 12 + libobjc/class.c | 69 ++-- 7 files changed, 743 insertions(+), 24 deletions(-) create mode 100644 gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm create mode 100644 gcc/testsuite/objc.dg/gnu-api-2-class-meta.m diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index f61bdab31cf..3b004cf4f60 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,17 @@ +2011-08-06 Nicola Pero + + PR libobjc/50002 + * objc.dg/gnu-api-2-class.m: Updated comments. + * obj-c++.dg/gnu-api-2-class.mm: Likewise. + * objc.dg/gnu-api-2-class-meta.m: New test. + * obj-c++.dg/gnu-api-2-class-meta.mm: Likewise. + +2011-08-06 Nicola Pero + + PR libobjc/49882 + * obj-c++.dg/gnu-api-2-class.mm (main): Test class_getSuperclass() + with classes that are in construction. + 2011-08-06 H.J. Lu PR target/48084 diff --git a/gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm b/gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm new file mode 100644 index 00000000000..e7c5fc20e43 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm @@ -0,0 +1,327 @@ +/* Test the Modern GNU Objective-C Runtime API. + + This is test 'class-meta', covering calling functions starting with + 'class' but using a meta class as argument. + + Functions that manipulate methods (adding, replacing methods) + usually take a meta class argument to manipulate the class methods + instead of the instance ones. This is an important part of the API + that needs testing. + + Functions that manipulate instances, instance variables, properties + and protocols at the moment must take a normal class as argument; + calling them with a meta class as argument is of no particular use + and generally produces a behaviour that is undocumented and/or + undefined (and this is true with all runtimes). As in the future + this behaviour may be defined or documented (for example, if class + variables are implemented as instance variables of meta classes) we + avoid testing it for compatibility with future runtimes. */ + +/* { dg-do run } */ +/* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + +/* To get the modern GNU Objective-C Runtime API, you include + objc/runtime.h. */ +#include +#include +#include +#include + +@interface MyRootClass +{ Class isa; } ++ alloc; +- init; ++ initialize; +@end + +@implementation MyRootClass ++ alloc { return class_createInstance (self, 0); } +- init { return self; } ++ initialize { return self; } +@end + +static id static_variable = nil; + +@interface MySubClass : MyRootClass ++ (void) setVariable: (id)value; ++ (id) variable; +@end + +@implementation MySubClass ++ (void) setVariable: (id)value { static_variable = value; } ++ (id) variable { return static_variable; } +@end + +@interface DifferentClass : MyRootClass ++ (id) myClass; +@end + +@implementation DifferentClass ++ (id) myClass { return self; } +@end + +@interface MySubClass (MySelf) ++ (id) mySelf; +@end + +int main () +{ + /* Functions are tested in alphabetical order. */ + + /* Calling class_addIvar() with a meta class is not documented and + (currently) of no use. */ + /* std::cout << "Testing class_addIvar ()...\n"; */ + + std::cout << "Testing class_addMethod () on a meta class...\n"; + { + /* Here we test adding methods to meta classes, ie, adding class methods. */ + Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass2", 0); + Method method1 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), @selector (setVariable:)); + Method method2 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), @selector (variable)); + + if (new_class == Nil) + abort (); + + if (! class_addMethod (object_getClass (new_class), @selector (setVariable:), method_getImplementation (method1), + method_getTypeEncoding (method1))) + abort (); + + if (! class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2), + method_getTypeEncoding (method2))) + abort (); + + /* Test that if the method already exists in the class, + class_addMethod() returns NO. */ + if (class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2), + method_getTypeEncoding (method2))) + abort (); + + objc_registerClassPair (new_class); + + /* Now, MySubClass2 is basically the same as MySubClass! We'll + use the +variable and +setVariable: methods on it. */ + { + Class c = objc_getClass ("MySubClass2"); + id o = [[MyRootClass alloc] init]; + + [c setVariable: o]; + + if ([c variable] != o) + abort (); + } + + /* Now, try that if you take an existing class and try to add an + already existing method, class_addMethod returns NO. This is + subtly different from before, when 'new_class' was still in + construction. Now it's a real class and the libobjc internals + differ between the two cases. */ + if (class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2), + method_getTypeEncoding (method2))) + abort (); + } + + /* Calling class_addProtocol() on a meta class is not documented and + (currently) of no use. */ + /* std::cout << "Testing class_addProtocol () on a meta class...\n"; */ + + /* Calling class_conformsToProtocol() on a meta class is not + documented and (currently) of no use. */ + /* std::cout << "Testing class_conformsToProtocol () on a meta class...\n"; */ + + /* Calling class_copyIvarList() on a meta class is not documented + and (currently) of no use. */ + /* std::cout << "Testing class_copyIvarList () on a meta class...\n"; */ + + std::cout << "Testing class_copyMethodList () on a meta class...\n"; + { + /* Test that you can copy the method list of a meta class. They + are the class methods of the class. */ + unsigned int count; + Method * list = class_copyMethodList (objc_getMetaClass ("MySubClass"), &count); + + if (count != 2) + abort (); + + if (! ((std::strcmp (sel_getName (method_getName (list[0])), "variable") == 0 + && std::strcmp (sel_getName (method_getName (list[1])), "setVariable:") == 0) + || (std::strcmp (sel_getName (method_getName (list[0])), "setVariable:") == 0 + && std::strcmp (sel_getName (method_getName (list[1])), "variable") == 0))) + abort (); + + if (list[2] != NULL) + abort (); + } + + /* Calling class_copyPropertyList() on a meta class is not + documented and (currently) of no use. */ + /* std::cout << "Testing class_copyPropertyList () on a meta class...\n"; */ + + /* Calling class_copyProtocolList() on a meta class is not + documented and (currently) of no use. */ + /* std::cout << "Testing class_copyProtocolList () on a meta class...\n"; */ + + /* Calling class_createInstance() on a meta class is not documented + and (currently) of no use. */ + /* std::cout << "Testing class_createInstance () on a meta class...\n"; */ + + /* Calling class_getClassMethod () on a meta class is not documented + and (currently) of no use. */ + /* std::cout << "Testing class_getClassMethod () on a meta class...\n"; */ + + /* Calling class_getClassVariable () on a meta class is not + documented and (currently) of no use. */ + /* std::cout << "Testing class_getClassVariable () on a meta class ...\n"; */ + + std::cout << "Testing class_getInstanceMethod () on a meta class...\n"; + { + /* The instance method of a meta class is the class method with + the same name of the class. */ + Method method_1 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), + @selector(variable)); + Method method_2 = class_getClassMethod (objc_getClass ("MySubClass"), + @selector(variable)); + + if (method_1 == NULL || method_2 == NULL) + abort (); + + if (method_1 != method_2) + abort (); + + if (std::strcmp (sel_getName (method_getName (method_1)), "variable") != 0) + abort (); + } + + /* Calling class_getInstanceSize() with a meta class is not + documented and (currently) of no use. */ + /* std::cout << "Testing class_getInstanceSize () on a meta class...\n"; */ + + /* Calling class_getInstanceVariable() with a meta class is not + documented and (currently) of no use. */ + /* std::cout << "Testing class_getInstanceVariable () on a meta class...\n"; */ + + /* Calling class_getIvarLayout() with a meta class is not documented + and (currently) of no use. */ + /* std::cout << "Testing class_getIvarLayout () on a meta class...\n"; */ + + std::cout << "Testing class_getMethodImplementation () on a meta class...\n"; + { + /* Getting the method implementation with a meta class returns a + class method. */ + MySubClass *object = [[MySubClass alloc] init]; + IMP imp = class_getMethodImplementation (objc_getMetaClass ("MySubClass"), + @selector(variable)); + + if (imp == NULL) + abort (); + + [MySubClass setVariable: object]; + + if ((*imp)(objc_getClass ("MySubClass"), @selector(variable)) != object) + abort (); + } + + /* This function does not exist with the GNU runtime. */ + /* std::cout << "Testing class_getMethodImplementation_stret () on a meta class...\n"; */ + + std::cout << "Testing class_getName () on a meta class...\n"; + { + /* Traditionally, a meta class has the same name as the class. */ + if (std::strcmp (class_getName (objc_getMetaClass ("MyRootClass")), + "MyRootClass") != 0) + abort (); + } + + /* Calling class_getProperty() with a meta class is not documented + and (currently) of no use. */ + /* std::cout << "Testing class_getProperty ()...\n"; */ + + std::cout << "Testing class_getSuperclass () on a meta class...\n"; + { + /* The superclass of a meta class is the meta class of the superclass. */ + if (class_getSuperclass (objc_getMetaClass ("MySubClass")) != objc_getMetaClass ("MyRootClass")) + abort (); + + /* Test that it works on a newly created, but not registered, class. */ + { + Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass3", 0); + + if (class_getSuperclass (object_getClass (new_class)) != object_getClass (objc_getClass ("MyRootClass"))) + abort (); + } + } + + /* Calling class_getVersion() with a meta class is not documented + and (currently) of no use. */ + /* std::cout << "Testing class_getVersion ()...\n"; */ + + /* Calling class_getWeakIvarLayout() with a meta class is not + documented and (currently) of no use. */ + /* std::cout << "Testing class_getWeakIvarLayout () on a meta class...\n"; */ + + /* class_isMetaClass() is already tested in gnu-api-2-class.m */ + /* std::cout << "Testing class_isMetaClass ()...\n"; */ + + std::cout << "Testing class_replaceMethod () on a meta class...\n"; + { + /* We are going to replace the [MySubclass +variable] method with + the [DifferentClass +myClass] one. */ + Method new_method = class_getClassMethod (objc_getClass ("DifferentClass"), + @selector (myClass)); + Method old_method = class_getClassMethod (objc_getClass ("MySubClass"), + @selector (variable)); + const char *new_types = method_getTypeEncoding (new_method); + IMP new_imp = method_getImplementation (new_method); + const char *old_types = method_getTypeEncoding (old_method); + IMP old_imp = class_replaceMethod (objc_getMetaClass ("MySubClass"), @selector (variable), + method_getImplementation (new_method), + method_getTypeEncoding (new_method)); + id o = [[MyRootClass alloc] init]; + + [MySubClass setVariable: o]; + + /* Try the new method implementation. */ + if ([MySubClass variable] != objc_getClass ("MySubClass")) + abort (); + + /* Put the original method back. */ + class_replaceMethod (objc_getMetaClass ("MySubClass"), @selector (variable), + old_imp, old_types); + + /* Test it's back to what it was. */ + if ([MySubClass variable] != o) + abort (); + + { + /* Finally, try adding a new method. */ + class_replaceMethod (objc_getMetaClass ("DifferentClass"), @selector (mySelf), + new_imp, new_types); + + if ([(Class)objc_getClass ("DifferentClass") mySelf] != objc_getClass ("DifferentClass")) + abort (); + } + } + + std::cout << "Testing class_respondsToSelector () on a meta class...\n"; + { + /* A meta class responds to a selector if and only if the class + responds to the corresponding class method. */ + if (! class_respondsToSelector (objc_getMetaClass ("MySubClass"), @selector(setVariable:))) + abort (); + + if (class_respondsToSelector (objc_getMetaClass ("MyRootClass"), @selector(setVariable:))) + abort (); + } + + /* This is not really implemented with the GNU runtime. */ + /* std::cout << "Testing class_setIvarLayout () on a meta class...\n"; */ + + /* Calling class_setVersion() with a meta class is not documented + and (currently) of no use. */ + /* std::cout << "Testing class_setVersion () on a meta class...\n"; */ + + /* This is not really implemented with the GNU runtime. */ + /* std::cout << "Testing class_setWeakIvarLayout () on a meta class...\n"; */ + +return (0); +} diff --git a/gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm b/gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm index 6dc9dd3733c..9a7c092f3b2 100644 --- a/gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm +++ b/gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm @@ -1,6 +1,8 @@ /* Test the Modern GNU Objective-C Runtime API. - This is test 'class', covering all functions starting with 'class'. */ + This is test 'class', covering all functions starting with 'class'. + Tests calling the functions with a meta class as argument are covered + in the separate file, gnu-api-2-class-meta.mm. */ /* { dg-do run } */ /* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ @@ -394,6 +396,14 @@ int main () MySubClass *object = [[MySubClass alloc] init]; if (class_getSuperclass (object_getClass (object)) != objc_getClass ("MyRootClass")) abort (); + + /* Test that it works on a newly created, but not registered, class. */ + { + Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass3", 0); + + if (class_getSuperclass (new_class) != objc_getClass ("MyRootClass")) + abort (); + } } std::cout << "Testing class_getVersion ()...\n"; diff --git a/gcc/testsuite/objc.dg/gnu-api-2-class-meta.m b/gcc/testsuite/objc.dg/gnu-api-2-class-meta.m new file mode 100644 index 00000000000..ea187b6a45a --- /dev/null +++ b/gcc/testsuite/objc.dg/gnu-api-2-class-meta.m @@ -0,0 +1,327 @@ +/* Test the Modern GNU Objective-C Runtime API. + + This is test 'class-meta', covering calling functions starting with + 'class' but using a meta class as argument. + + Functions that manipulate methods (adding, replacing methods) + usually take a meta class argument to manipulate the class methods + instead of the instance ones. This is an important part of the API + that needs testing. + + Functions that manipulate instances, instance variables, properties + and protocols at the moment must take a normal class as argument; + calling them with a meta class as argument is of no particular use + and generally produces a behaviour that is undocumented and/or + undefined (and this is true with all runtimes). As in the future + this behaviour may be defined or documented (for example, if class + variables are implemented as instance variables of meta classes) we + avoid testing it for compatibility with future runtimes. */ + +/* { dg-do run } */ +/* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + +/* To get the modern GNU Objective-C Runtime API, you include + objc/runtime.h. */ +#include +#include +#include +#include + +@interface MyRootClass +{ Class isa; } ++ alloc; +- init; ++ initialize; +@end + +@implementation MyRootClass ++ alloc { return class_createInstance (self, 0); } +- init { return self; } ++ initialize { return self; } +@end + +static id static_variable = nil; + +@interface MySubClass : MyRootClass ++ (void) setVariable: (id)value; ++ (id) variable; +@end + +@implementation MySubClass ++ (void) setVariable: (id)value { static_variable = value; } ++ (id) variable { return static_variable; } +@end + +@interface DifferentClass : MyRootClass ++ (id) myClass; +@end + +@implementation DifferentClass ++ (id) myClass { return self; } +@end + +@interface MySubClass (MySelf) ++ (id) mySelf; +@end + +int main(int argc, void **args) +{ + /* Functions are tested in alphabetical order. */ + + /* Calling class_addIvar() with a meta class is not documented and + (currently) of no use. */ + /* printf ("Testing class_addIvar ()...\n"); */ + + printf ("Testing class_addMethod () on a meta class...\n"); + { + /* Here we test adding methods to meta classes, ie, adding class methods. */ + Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass2", 0); + Method method1 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), @selector (setVariable:)); + Method method2 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), @selector (variable)); + + if (new_class == Nil) + abort (); + + if (! class_addMethod (object_getClass (new_class), @selector (setVariable:), method_getImplementation (method1), + method_getTypeEncoding (method1))) + abort (); + + if (! class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2), + method_getTypeEncoding (method2))) + abort (); + + /* Test that if the method already exists in the class, + class_addMethod() returns NO. */ + if (class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2), + method_getTypeEncoding (method2))) + abort (); + + objc_registerClassPair (new_class); + + /* Now, MySubClass2 is basically the same as MySubClass! We'll + use the +variable and +setVariable: methods on it. */ + { + Class c = objc_getClass ("MySubClass2"); + id o = [[MyRootClass alloc] init]; + + [c setVariable: o]; + + if ([c variable] != o) + abort (); + } + + /* Now, try that if you take an existing class and try to add an + already existing method, class_addMethod returns NO. This is + subtly different from before, when 'new_class' was still in + construction. Now it's a real class and the libobjc internals + differ between the two cases. */ + if (class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2), + method_getTypeEncoding (method2))) + abort (); + } + + /* Calling class_addProtocol() on a meta class is not documented and + (currently) of no use. */ + /* printf ("Testing class_addProtocol () on a meta class...\n"); */ + + /* Calling class_conformsToProtocol() on a meta class is not + documented and (currently) of no use. */ + /* printf ("Testing class_conformsToProtocol () on a meta class...\n"); */ + + /* Calling class_copyIvarList() on a meta class is not documented + and (currently) of no use. */ + /* printf ("Testing class_copyIvarList () on a meta class...\n"); */ + + printf ("Testing class_copyMethodList () on a meta class...\n"); + { + /* Test that you can copy the method list of a meta class. They + are the class methods of the class. */ + unsigned int count; + Method * list = class_copyMethodList (objc_getMetaClass ("MySubClass"), &count); + + if (count != 2) + abort (); + + if (! ((strcmp (sel_getName (method_getName (list[0])), "variable") == 0 + && strcmp (sel_getName (method_getName (list[1])), "setVariable:") == 0) + || (strcmp (sel_getName (method_getName (list[0])), "setVariable:") == 0 + && strcmp (sel_getName (method_getName (list[1])), "variable") == 0))) + abort (); + + if (list[2] != NULL) + abort (); + } + + /* Calling class_copyPropertyList() on a meta class is not + documented and (currently) of no use. */ + /* printf ("Testing class_copyPropertyList () on a meta class...\n"); */ + + /* Calling class_copyProtocolList() on a meta class is not + documented and (currently) of no use. */ + /* printf ("Testing class_copyProtocolList () on a meta class...\n"); */ + + /* Calling class_createInstance() on a meta class is not documented + and (currently) of no use. */ + /* printf ("Testing class_createInstance () on a meta class...\n"); */ + + /* Calling class_getClassMethod () on a meta class is not documented + and (currently) of no use. */ + /* printf ("Testing class_getClassMethod () on a meta class...\n"); */ + + /* Calling class_getClassVariable () on a meta class is not + documented and (currently) of no use. */ + /* printf ("Testing class_getClassVariable () on a meta class ...\n"); */ + + printf ("Testing class_getInstanceMethod () on a meta class...\n"); + { + /* The instance method of a meta class is the class method with + the same name of the class. */ + Method method_1 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), + @selector(variable)); + Method method_2 = class_getClassMethod (objc_getClass ("MySubClass"), + @selector(variable)); + + if (method_1 == NULL || method_2 == NULL) + abort (); + + if (method_1 != method_2) + abort (); + + if (strcmp (sel_getName (method_getName (method_1)), "variable") != 0) + abort (); + } + + /* Calling class_getInstanceSize() with a meta class is not + documented and (currently) of no use. */ + /* printf ("Testing class_getInstanceSize () on a meta class...\n"); */ + + /* Calling class_getInstanceVariable() with a meta class is not + documented and (currently) of no use. */ + /* printf ("Testing class_getInstanceVariable () on a meta class...\n"); */ + + /* Calling class_getIvarLayout() with a meta class is not documented + and (currently) of no use. */ + /* printf ("Testing class_getIvarLayout () on a meta class...\n"); */ + + printf ("Testing class_getMethodImplementation () on a meta class...\n"); + { + /* Getting the method implementation with a meta class returns a + class method. */ + MySubClass *object = [[MySubClass alloc] init]; + IMP imp = class_getMethodImplementation (objc_getMetaClass ("MySubClass"), + @selector(variable)); + + if (imp == NULL) + abort (); + + [MySubClass setVariable: object]; + + if ((*imp)(objc_getClass ("MySubClass"), @selector(variable)) != object) + abort (); + } + + /* This function does not exist with the GNU runtime. */ + /* printf ("Testing class_getMethodImplementation_stret () on a meta class...\n"); */ + + printf ("Testing class_getName () on a meta class...\n"); + { + /* Traditionally, a meta class has the same name as the class. */ + if (strcmp (class_getName (objc_getMetaClass ("MyRootClass")), + "MyRootClass") != 0) + abort (); + } + + /* Calling class_getProperty() with a meta class is not documented + and (currently) of no use. */ + /* printf ("Testing class_getProperty ()...\n"); */ + + printf ("Testing class_getSuperclass () on a meta class...\n"); + { + /* The superclass of a meta class is the meta class of the superclass. */ + if (class_getSuperclass (objc_getMetaClass ("MySubClass")) != objc_getMetaClass ("MyRootClass")) + abort (); + + /* Test that it works on a newly created, but not registered, class. */ + { + Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass3", 0); + + if (class_getSuperclass (object_getClass (new_class)) != object_getClass (objc_getClass ("MyRootClass"))) + abort (); + } + } + + /* Calling class_getVersion() with a meta class is not documented + and (currently) of no use. */ + /* printf ("Testing class_getVersion ()...\n"); */ + + /* Calling class_getWeakIvarLayout() with a meta class is not + documented and (currently) of no use. */ + /* printf ("Testing class_getWeakIvarLayout () on a meta class...\n"); */ + + /* class_isMetaClass() is already tested in gnu-api-2-class.m */ + /* printf ("Testing class_isMetaClass ()...\n"); */ + + printf ("Testing class_replaceMethod () on a meta class...\n"); + { + /* We are going to replace the [MySubclass +variable] method with + the [DifferentClass +myClass] one. */ + Method new_method = class_getClassMethod (objc_getClass ("DifferentClass"), + @selector (myClass)); + Method old_method = class_getClassMethod (objc_getClass ("MySubClass"), + @selector (variable)); + const char *new_types = method_getTypeEncoding (new_method); + IMP new_imp = method_getImplementation (new_method); + const char *old_types = method_getTypeEncoding (old_method); + IMP old_imp = class_replaceMethod (objc_getMetaClass ("MySubClass"), @selector (variable), + method_getImplementation (new_method), + method_getTypeEncoding (new_method)); + id o = [[MyRootClass alloc] init]; + + [MySubClass setVariable: o]; + + /* Try the new method implementation. */ + if ([MySubClass variable] != objc_getClass ("MySubClass")) + abort (); + + /* Put the original method back. */ + class_replaceMethod (objc_getMetaClass ("MySubClass"), @selector (variable), + old_imp, old_types); + + /* Test it's back to what it was. */ + if ([MySubClass variable] != o) + abort (); + + { + /* Finally, try adding a new method. */ + class_replaceMethod (objc_getMetaClass ("DifferentClass"), @selector (mySelf), + new_imp, new_types); + + if ([(Class)objc_getClass ("DifferentClass") mySelf] != objc_getClass ("DifferentClass")) + abort (); + } + } + + printf ("Testing class_respondsToSelector () on a meta class...\n"); + { + /* A meta class responds to a selector if and only if the class + responds to the corresponding class method. */ + if (! class_respondsToSelector (objc_getMetaClass ("MySubClass"), @selector(setVariable:))) + abort (); + + if (class_respondsToSelector (objc_getMetaClass ("MyRootClass"), @selector(setVariable:))) + abort (); + } + + /* This is not really implemented with the GNU runtime. */ + /* printf ("Testing class_setIvarLayout () on a meta class...\n"); */ + + /* Calling class_setVersion() with a meta class is not documented + and (currently) of no use. */ + /* printf ("Testing class_setVersion () on a meta class...\n"); */ + + /* This is not really implemented with the GNU runtime. */ + /* printf ("Testing class_setWeakIvarLayout () on a meta class...\n"); */ + + return 0; +} diff --git a/gcc/testsuite/objc.dg/gnu-api-2-class.m b/gcc/testsuite/objc.dg/gnu-api-2-class.m index d69f8eba20f..7f9cf861c8a 100644 --- a/gcc/testsuite/objc.dg/gnu-api-2-class.m +++ b/gcc/testsuite/objc.dg/gnu-api-2-class.m @@ -1,6 +1,8 @@ /* Test the Modern GNU Objective-C Runtime API. - This is test 'class', covering all functions starting with 'class'. */ + This is test 'class', covering all functions starting with 'class'. + Tests calling the functions with a meta class as argument are covered + in the separate file, gnu-api-2-class-meta.m. */ /* { dg-do run } */ /* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ @@ -401,7 +403,7 @@ int main(int argc, void **args) if (class_getSuperclass (new_class) != objc_getClass ("MyRootClass")) abort (); - } + } } printf ("Testing class_getVersion ()...\n"); diff --git a/libobjc/ChangeLog b/libobjc/ChangeLog index a69e59032cc..b9f87fabec9 100644 --- a/libobjc/ChangeLog +++ b/libobjc/ChangeLog @@ -1,3 +1,15 @@ +2011-08-06 Nicola Pero + + PR libobjc/50002 + * class.c (__objc_update_classes_with_methods): Iterate over meta + classes as well as normal classes when refreshing the method + implementations. This fixes replacing class methods. + +2011-08-06 Nicola Pero + + * class.c (class_getSuperclass): Fixed to work with meta classes + still in construction too. + 2011-08-06 Nicola Pero * class.c (class_getSuperclass): Fixed typo in comment. diff --git a/libobjc/class.c b/libobjc/class.c index fa21afc461d..edc56aa9ec4 100644 --- a/libobjc/class.c +++ b/libobjc/class.c @@ -781,35 +781,57 @@ __objc_update_classes_with_methods (struct objc_method *method_a, struct objc_me while (node != NULL) { - /* Iterate over all methods in the class. */ - Class class = node->pointer; - struct objc_method_list * method_list = class->methods; - - while (method_list) + /* We execute this loop twice: the first time, we iterate + over all methods in the class (instance methods), while + the second time we iterate over all methods in the meta + class (class methods). */ + Class class = Nil; + BOOL done = NO; + + while (done == NO) { - int i; + struct objc_method_list * method_list; - for (i = 0; i < method_list->method_count; ++i) + if (class == Nil) + { + /* The first time, we work on the class. */ + class = node->pointer; + } + else { - struct objc_method *method = &method_list->method_list[i]; + /* The second time, we work on the meta class. */ + class = class->class_pointer; + done = YES; + } - /* If the method is one of the ones we are looking - for, update the implementation. */ - if (method == method_a) - sarray_at_put_safe (class->dtable, - (sidx) method_a->method_name->sel_id, - method_a->method_imp); + method_list = class->methods; - if (method == method_b) + while (method_list) + { + int i; + + for (i = 0; i < method_list->method_count; ++i) { - if (method_b != NULL) + struct objc_method *method = &method_list->method_list[i]; + + /* If the method is one of the ones we are + looking for, update the implementation. */ + if (method == method_a) sarray_at_put_safe (class->dtable, - (sidx) method_b->method_name->sel_id, - method_b->method_imp); + (sidx) method_a->method_name->sel_id, + method_a->method_imp); + + if (method == method_b) + { + if (method_b != NULL) + sarray_at_put_safe (class->dtable, + (sidx) method_b->method_name->sel_id, + method_b->method_imp); + } } + + method_list = method_list->method_next; } - - method_list = method_list->method_next; } node = node->next; } @@ -929,7 +951,12 @@ class_getSuperclass (Class class_) superclass name to return the superclass. We can not resolve the class until it is registered. */ if (CLS_IS_IN_CONSTRUCTION (class_)) - return objc_lookUpClass ((const char *)(class_->super_class)); + { + if (CLS_ISMETA (class_)) + return object_getClass ((id)objc_lookUpClass ((const char *)(class_->super_class))); + else + return objc_lookUpClass ((const char *)(class_->super_class)); + } /* If the class is not resolved yet, super_class would point to a string (the name of the super class) as opposed to the actual -- 2.30.2