From patchwork Sat Aug 6 14:20:39 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicola Pero X-Patchwork-Id: 108775 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 0CFF4B6F80 for ; Sun, 7 Aug 2011 00:21:14 +1000 (EST) Received: (qmail 3800 invoked by alias); 6 Aug 2011 14:21:13 -0000 Received: (qmail 3781 invoked by uid 22791); 6 Aug 2011 14:21:08 -0000 X-SWARE-Spam-Status: No, hits=-1.8 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, TW_BJ X-Spam-Check-By: sourceware.org Received: from fencepost.gnu.org (HELO fencepost.gnu.org) (140.186.70.10) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sat, 06 Aug 2011 14:20:49 +0000 Received: from eggs.gnu.org ([140.186.70.92]:42273) by fencepost.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1Qphk7-0007wa-Of for gcc-patches@gnu.org; Sat, 06 Aug 2011 10:20:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Qphk4-0004OK-Ti for gcc-patches@gnu.org; Sat, 06 Aug 2011 10:20:47 -0400 Received: from smtp201.iad.emailsrvr.com ([207.97.245.201]:35050) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qphk4-0004OG-PC for gcc-patches@gnu.org; Sat, 06 Aug 2011 10:20:44 -0400 Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp30.relay.iad1a.emailsrvr.com (SMTP Server) with ESMTP id 0F5CB20287 for ; Sat, 6 Aug 2011 10:20:44 -0400 (EDT) Received: by smtp30.relay.iad1a.emailsrvr.com (Authenticated sender: nicola.pero-AT-meta-innovation.com) with ESMTPSA id 2730C2025A for ; Sat, 6 Aug 2011 10:20:43 -0400 (EDT) From: Nicola Pero Subject: libobjc: Fix PR libobjc/50002 ("class_replaceMethod does not work on class methods") Date: Sat, 6 Aug 2011 15:20:39 +0100 Message-Id: <3DE555DC-1AD5-4AE4-8575-04DC660F9651@meta-innovation.com> To: gcc-patches@gnu.org Mime-Version: 1.0 (Apple Message framework v1084) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 207.97.245.201 X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This patch fixes PR libobjc/50002. The problem was that replacing an existing class method wouldn't work because the messaging tables weren't being refreshed for class methods when a method was replaced. This patch also includes three other related changes: * a new couple of comprehensive testcases, gnu-api-2-class-meta.m and gnu-api-2-class-meta.mm, covering calling various runtime functions with a meta class (as opposed to a normal class) as argument. This tests the above-mentioned fix for libobjc/50002, but also a variety of other similar functions and situations (this is mostly for peace of mind - I wanted to be sure that there aren't other problems similar to libobjc/50002). * a fix for a small issue with class_getSuperclass() (when used on a meta class in construction), which was identified by the testcases above. The actual problem is actually related to libobjc/49882 - which I fixed this morning. * synchronizes the ObjC++ testsuite with the ObjC one. In particular, when fixing libobjc/49882 this morning I added an ObjC testcase but forgot to add an ObjC++ one. This patch includes it. Tested with GNU and Apple runtime. Committed to trunk. Thanks Index: libobjc/ChangeLog =================================================================== --- libobjc/ChangeLog (revision 177506) +++ libobjc/ChangeLog (working copy) @@ -1,5 +1,17 @@ 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. 2011-08-06 Nicola Pero Index: libobjc/class.c =================================================================== --- libobjc/class.c (revision 177506) +++ libobjc/class.c (working copy) @@ -781,35 +781,57 @@ __objc_update_classes_with_methods (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; + /* 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 (method_list) + while (done == NO) { - int i; + struct objc_method_list * method_list; - for (i = 0; i < method_list->method_count; ++i) + if (class == Nil) { - struct objc_method *method = &method_list->method_list[i]; + /* The first time, we work on the class. */ + class = node->pointer; + } + else + { + /* 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 Index: gcc/testsuite/ChangeLog =================================================================== --- gcc/testsuite/ChangeLog (revision 177508) +++ gcc/testsuite/ChangeLog (working copy) @@ -1,6 +1,20 @@ 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 Nicola Pero + + PR libobjc/49882 * objc.dg/gnu-api-2-class.m (main): Test class_getSuperclass() with classes that are in construction. Index: gcc/testsuite/objc.dg/gnu-api-2-class.m =================================================================== --- gcc/testsuite/objc.dg/gnu-api-2-class.m (revision 177508) +++ gcc/testsuite/objc.dg/gnu-api-2-class.m (working copy) @@ -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"); Index: gcc/testsuite/objc.dg/gnu-api-2-class-meta.m =================================================================== --- gcc/testsuite/objc.dg/gnu-api-2-class-meta.m (revision 0) +++ gcc/testsuite/objc.dg/gnu-api-2-class-meta.m (revision 0) @@ -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; +} Index: gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm =================================================================== --- gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm (revision 177508) +++ gcc/testsuite/obj-c++.dg/gnu-api-2-class.mm (working copy) @@ -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"; Index: gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm =================================================================== --- gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm (revision 0) +++ gcc/testsuite/obj-c++.dg/gnu-api-2-class-meta.mm (revision 0) @@ -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); +}