From patchwork Sat Oct 16 21:27:35 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicola Pero X-Patchwork-Id: 68053 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 97F3DB70AF for ; Sun, 17 Oct 2010 08:28:00 +1100 (EST) Received: (qmail 30177 invoked by alias); 16 Oct 2010 21:27:57 -0000 Received: (qmail 30163 invoked by uid 22791); 16 Oct 2010 21:27:52 -0000 X-SWARE-Spam-Status: No, hits=-0.4 required=5.0 tests=AWL, BAYES_05, SARE_SUB_ENC_UTF8, TW_BJ, TW_CP, T_RP_MATCHES_RCVD 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, 16 Oct 2010 21:27:43 +0000 Received: from eggs.gnu.org ([140.186.70.92]:41887) by fencepost.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.69) (envelope-from ) id 1P7EI1-0005pL-8S for gcc-patches@gnu.org; Sat, 16 Oct 2010 17:27:41 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1P7EHw-0002Ze-To for gcc-patches@gnu.org; Sat, 16 Oct 2010 17:27:41 -0400 Received: from smtp201.iad.emailsrvr.com ([207.97.245.201]:50329) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1P7EHw-0002ZY-O3 for gcc-patches@gnu.org; Sat, 16 Oct 2010 17:27:36 -0400 Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp40.relay.iad1a.emailsrvr.com (SMTP Server) with ESMTP id B2767350157 for ; Sat, 16 Oct 2010 17:27:35 -0400 (EDT) X-Orig-To: gcc-patches@gnu.org Received: from dynamic2.wm-web.iad.mlsrvr.com (dynamic2.wm-web.iad1a.rsapps.net [192.168.2.151]) by smtp40.relay.iad1a.emailsrvr.com (SMTP Server) with ESMTP id 9B196350144 for ; Sat, 16 Oct 2010 17:27:35 -0400 (EDT) Received: from meta-innovation.com (localhost [127.0.0.1]) by dynamic2.wm-web.iad.mlsrvr.com (Postfix) with ESMTP id 8999828E8066 for ; Sat, 16 Oct 2010 17:27:35 -0400 (EDT) Received: by www2.webmail.us (Authenticated sender: nicola.pero@meta-innovation.com, from: nicola.pero@meta-innovation.com) with HTTP; Sat, 16 Oct 2010 23:27:35 +0200 (CEST) Date: Sat, 16 Oct 2010 23:27:35 +0200 (CEST) Subject: =?UTF-8?Q?libobjc=20-=20more=20modern=20Objective-C=20runtime=20API=20(1?= =?UTF-8?Q?2)?= From: "Nicola Pero" To: "gcc-patches@gnu.org" MIME-Version: 1.0 X-Type: plain Message-ID: <1287264455.5625462@192.168.2.227> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) 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 adds the last missing bits in the new API - to create new classes and adding ivars and methods. It also includes two testcases that test all the new API objc_xxx and class_xxx functions. :-) Committed to trunk. Thanks In libobjc/: 2010-10-16 Nicola Pero * objc/runtime.h: Updated comments. (class_addMethod): New. (class_addIvar): New. (class_replaceMethod): New. (objc_allocateClassPair): New. (objc_registerClassPair): New. (objc_disposeClassPair): New. * class.c (objc_allocateClassPair): New. (objc_registerClassPair): New. (objc_disposeClassPair): New. (class_getSuperclass): Return Nil if a class is in construction. * init.c (__objc_exec_class): Call __objc_init_class. (__objc_init_class): New. * ivars.c (class_copyIvarList): Return NULL if class is in construction. Do not lock the runtime mutex. (class_getInstanceVariable): Return NULL if class is in construction. Do not lock the runtime mutex. (class_addIvar): New. * sendmsg.c (class_addMethod): New. (class_replaceMethod): New. * objc-private/module-abi-8.h (__CLS_SETNOTINFO): New. (_CLS_IN_CONSTRUCTION): New. (CLS_IS_IN_CONSTRUCTION): New. (CLS_SET_IN_CONSTRUCTION): New. (CLS_SET_NOT_IN_CONSTRUCTION): New. * objc-private/runtime.h (__objc_init_class): New. In gcc/testsuite/: 2010-10-16 Nicola Pero * objc.dg/gnu-api-2-class.m: New. * objc.dg/gnu-api-2-objc.m: New. Index: ChangeLog =================================================================== --- ChangeLog (revision 165562) +++ ChangeLog (working copy) @@ -1,5 +1,10 @@ 2010-10-16 Nicola Pero + * objc.dg/gnu-api-2-class.m: New. + * objc.dg/gnu-api-2-objc.m: New. + +2010-10-16 Nicola Pero + * objc.dg/gnu-api-2-ivar.m: New. 2010-10-15 Nicola Pero Index: objc.dg/gnu-api-2-objc.m =================================================================== --- objc.dg/gnu-api-2-objc.m (revision 0) +++ objc.dg/gnu-api-2-objc.m (revision 0) @@ -0,0 +1,242 @@ +/* Test the Modern GNU Objective-C Runtime API. + + This is test 'objc', covering all functions starting with 'objc'. */ + +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "-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; +@end + +@implementation MyRootClass ++ alloc { return class_createInstance (self, 0); } +- init { return self; } +@end + +@protocol MyProtocol +- (id) variable; +@end + +@protocol MySecondProtocol +- (id) setVariable: (id)value; +@end + +@interface MySubClass : MyRootClass +{ id variable_ivar; } +- (void) setVariable: (id)value; +- (id) variable; +@end + +@implementation MySubClass +- (void) setVariable: (id)value { variable_ivar = value; } +- (id) variable { return variable_ivar; } +@end + + +int main(int argc, void **args) +{ + /* Functions are tested in alphabetical order. */ + + printf ("Testing objc_allocateClassPair ()...\n"); + { + Class new_root_class = objc_allocateClassPair (Nil, "MyNewRootClass", 0); + Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MyNewSubClass", 0); + + /* A new root class would obviously need at least an 'isa' + instance variable. We don't add it so we never actually + instantiate an instance of the class, which wouldn't work. */ + + objc_registerClassPair (new_root_class); + objc_registerClassPair (new_class); + + if (strcmp (class_getName (new_class), "MyNewSubClass") != 0) + abort (); + + if (class_getSuperclass (new_class) != objc_getClass ("MyRootClass")) + abort (); + + if (strcmp (class_getName (new_root_class), "MyNewRootClass") != 0) + abort (); + + if (class_getSuperclass (new_root_class) != Nil) + abort (); + + { + MySubClass *o = [[objc_getClass ("MyNewSubClass") alloc] init]; + + if (object_getClass (o) != objc_getClass ("MyNewSubClass")) + abort (); + } + } + + printf ("Testing objc_copyProtocolList ()...\n"); + { + /* Make sure both our two protocols are known to the runtime. */ + id my_protocol = @protocol (MyProtocol); + id my_second_protocol = @protocol (MySecondProtocol); + unsigned int count; + Protocol ** list = objc_copyProtocolList (&count); + + if (count != 2) + abort (); + + if (! ((strcmp (protocol_getName (list[0]), "MyProtocol") == 0 + && strcmp (protocol_getName (list[1]), "MySecondProtocol") == 0) + || (strcmp (protocol_getName (list[0]), "MySecondProtocol") == 0 + && strcmp (protocol_getName (list[1]), "MyProtocol") == 0))) + abort (); + + if (list[2] != NULL) + abort (); + } + + printf ("Testing objc_disposeClassPair ()...\n"); + { + Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), @selector (setVariable:)); + Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MyNewSubClass", 0); + + /* Add a bit of everything to the class to exercise undoing all these changes. */ + + /* Instance variable. */ + class_addIvar (new_class, "my_variable", sizeof (float), __alignof__ (float), @encode (float)); + + /* Instance method. */ + class_addMethod (new_class, @selector (setVariable:), method_getImplementation (method), + method_getTypeEncoding (method)); + + /* Class method. */ + class_addMethod (object_getClass (new_class), @selector (setVariable:), method_getImplementation (method), + method_getTypeEncoding (method)); + + /* Protocol. */ + class_addProtocol (new_class, @protocol (MyProtocol)); + + objc_disposeClassPair (new_class); + } + + /* This function currently does not exist with the GNU runtime. */ + /* printf ("Testing objc_duplicateClass ()...\n"); */ + + /* TODO - Test it when implemented in the GNU Runtime */ + /* printf ("Testing objc_getAssociatedObject ()...\n"); */ + + printf ("Testing objc_getClass ()...\n"); + { + if (strcmp (class_getName (objc_getClass ("MySubClass")), + "MySubClass") != 0) + abort (); + } + + printf ("Testing objc_getClassList ()...\n"); + { + Class *list; + int i, count, other_count; + count = objc_getClassList (NULL, 0); + + /* count most likely will be 5, (MyRootClass, MySubClass, + Protocol, Object, NXConstantString). */ + if (count < 3) + abort (); + + list = malloc (sizeof (Class) * count); + other_count = objc_getClassList (list, count); + + if (other_count != count) + abort (); + + /* Spot-check: search for class 'MyRootClass' in the list. */ + for (i = 0; i < count; i++) + { + if (strcmp (class_getName (list[i]), "MyRootClass") == 0) + break; + } + if (i == count) + abort (); + + /* Spot-check: search for class 'MySubClass' in the list. */ + for (i = 0; i < count; i++) + { + if (strcmp (class_getName (list[i]), "MySubClass") == 0) + break; + } + if (i == count) + abort (); + + /* Spot-check: search for class 'Protocol' in the list. */ + for (i = 0; i < count; i++) + { + if (strcmp (class_getName (list[i]), "Protocol") == 0) + break; + } + if (i == count) + abort (); + } + + /* This function does not exist with the GNU runtime. */ + /* printf ("Testing objc_getFutureClass ()...\n"); */ + + printf ("Testing objc_getMetaClass ()...\n"); + { + if (! class_isMetaClass (objc_getMetaClass ("MyRootClass"))) + abort (); + } + + printf ("Testing objc_getProtocol ()...\n"); + { + if (! protocol_isEqual (objc_getProtocol ("MyProtocol"), @protocol (MyProtocol))) + abort (); + } + + printf ("Testing objc_getRequiredClass ()...\n"); + { + if (strcmp (class_getName (objc_getRequiredClass ("MyRootClass")), + "MyRootClass") != 0) + abort (); + } + + printf ("Testing objc_lookupClass ()...\n"); + { + if (strcmp (class_getName (objc_lookupClass ("MyRootClass")), + "MyRootClass") != 0) + abort (); + } + + /* This function does not exist with the GNU runtime. */ + /* printf ("Testing objc_setFutureClass ()...\n"); */ + + printf ("Testing objc_registerClassPair ()...\n"); + { + Class new_class = objc_allocateClassPair (objc_getClass ("MySubClass"), "MySubSubClass", 0); + + class_addProtocol (new_class, @protocol (MySecondProtocol)); + + objc_registerClassPair (new_class); + + if (strcmp (class_getName (new_class), "MySubSubClass") != 0) + abort (); + + if (class_getSuperclass (new_class) != objc_getClass ("MySubClass")) + abort (); + + if (! class_conformsToProtocol (new_class, @protocol (MySecondProtocol))) + abort (); + } + + /* TODO - Test it when implemented in the GNU Runtime */ + /* printf ("Testing objc_removeAssociatedObjects ()...\n"); */ + + /* TODO - Test it when implemented in the GNU Runtime */ + /* printf ("Testing objc_setAssociatedObject ()...\n"); */ + + return 0; +} Index: objc.dg/gnu-api-2-class.m =================================================================== --- objc.dg/gnu-api-2-class.m (revision 0) +++ objc.dg/gnu-api-2-class.m (revision 0) @@ -0,0 +1,441 @@ +/* Test the Modern GNU Objective-C Runtime API. + + This is test 'class', covering all functions starting with 'class'. */ + +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "-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; +@end + +@implementation MyRootClass ++ alloc { return class_createInstance (self, 0); } +- init { return self; } +@end + +@protocol MyProtocol +- (id) variable; +@end + +@protocol MySecondProtocol +- (id) setVariable: (id)value; +@end + +@interface MySubClass : MyRootClass +{ id variable_ivar; } +- (void) setVariable: (id)value; +- (id) variable; +@end + +@implementation MySubClass +- (void) setVariable: (id)value { variable_ivar = value; } +- (id) variable { return variable_ivar; } +@end + +@interface DifferentClass : MyRootClass +- (id) myClass; +- (id) self; +@end + +@implementation DifferentClass +- (id) myClass { return object_getClass (self); } +- (id) self { return self; } +@end + +@interface MySubClass (MySelf) +- (id) mySelf; +@end + +int main(int argc, void **args) +{ + /* Functions are tested in alphabetical order. */ + + printf ("Testing class_addIvar ()...\n"); + { + Class new_class = objc_allocateClassPair (objc_getClass ("MySubClass"), "MySubSubClass", 0); + + if (new_class == Nil) + abort (); + + if (! class_addIvar (new_class, "variable2_ivar", sizeof (id), + __alignof__ (id), @encode (id))) + abort (); + + if (! class_addIvar (new_class, "variable3_ivar", sizeof (unsigned char), + __alignof__ (unsigned char), @encode (unsigned char))) + abort (); + + if (! class_addIvar (new_class, "variable4_ivar", sizeof (unsigned long), + __alignof__ (unsigned long), @encode (unsigned long))) + abort (); + + objc_registerClassPair (new_class); + + { + MySubClass *o = [[objc_getClass ("MySubSubClass") alloc] init]; + Ivar variable2 = class_getInstanceVariable (objc_getClass ("MySubSubClass"), "variable2_ivar"); + Ivar variable3 = class_getInstanceVariable (objc_getClass ("MySubSubClass"), "variable3_ivar"); + Ivar variable4 = class_getInstanceVariable (objc_getClass ("MySubSubClass"), "variable4_ivar"); + + if (variable2 == NULL || variable3 == NULL || variable4 == NULL) + abort (); + + if (strcmp (ivar_getName (variable2), "variable2_ivar") != 0) + abort (); + + if (strcmp (ivar_getName (variable3), "variable3_ivar") != 0) + abort (); + + if (strcmp (ivar_getName (variable4), "variable4_ivar") != 0) + abort (); + + { + unsigned char *var3 = (unsigned char *)((char *)o + ivar_getOffset (variable3)); + unsigned long *var4 = (unsigned long *)((char *)o + ivar_getOffset (variable4)); + + object_setIvar (o, variable2, new_class); + *var3 = 230; + *var4 = 89000L; + + if (object_getIvar (o, variable2) != new_class) + abort (); + + if (*var3 != 230) + abort (); + + if (*var4 != 89000L) + abort (); + } + } + } + + printf ("Testing class_addMethod ()...\n"); + { + Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass2", 0); + Method method1 = class_getInstanceMethod (objc_getClass ("MySubClass"), @selector (setVariable:)); + Method method2 = class_getInstanceMethod (objc_getClass ("MySubClass"), @selector (variable)); + + if (new_class == Nil) + abort (); + + if (! class_addIvar (new_class, "variable_ivar", sizeof (id), + __alignof__ (id), @encode (id))) + abort (); + + if (! class_addMethod (new_class, @selector (setVariable:), method_getImplementation (method1), + method_getTypeEncoding (method1))) + abort (); + + if (! class_addMethod (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. */ + { + MySubClass *o = (MySubClass *)[[objc_getClass ("MySubClass2") alloc] init]; + + [o setVariable: o]; + + if ([o variable] != o) + abort (); + } + } + + printf ("Testing class_addProtocol ()...\n"); + { + if (!class_addProtocol (objc_getClass ("MySubClass"), @protocol (MySecondProtocol))) + abort (); + + if (!class_conformsToProtocol (objc_getClass ("MySubClass"), @protocol (MyProtocol))) + abort (); + + if (!class_conformsToProtocol (objc_getClass ("MySubClass"), @protocol (MySecondProtocol))) + abort (); + } + + printf ("Testing class_conformsToProtocol ()...\n"); + { + if (class_conformsToProtocol (objc_getClass ("MyRootClass"), @protocol (MyProtocol))) + abort (); + + if (!class_conformsToProtocol (objc_getClass ("MySubClass"), @protocol (MyProtocol))) + abort (); + } + + printf ("Testing class_copyIvarList ()...\n"); + { + unsigned int count; + Ivar * list = class_copyIvarList (objc_getClass ("MySubClass"), &count); + + if (count != 1) + abort (); + + if (strcmp (ivar_getName (list[0]), "variable_ivar") != 0) + abort (); + + if (list[1] != NULL) + abort (); + } + + printf ("Testing class_copyMethodList ()...\n"); + { + unsigned int count; + Method * list = class_copyMethodList (objc_getClass ("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 (); + } + + /* TODO: Test new ABI (when available). */ + printf ("Testing class_copyPropertyList ()...\n"); + { + unsigned int count; + Property * list = class_copyPropertyList (objc_getClass ("MySubClass"), &count); + + if (count != 0 || list != NULL) + abort (); + } + + printf ("Testing class_copyProtocolList ()...\n"); + { + unsigned int count; + Protocol ** list = class_copyProtocolList (objc_getClass ("MySubClass"), &count); + + /* Remember that we added MySecondProtocol in the test above. */ + if (count != 2) + abort (); + + if (! ((strcmp (protocol_getName (list[0]), "MyProtocol") == 0 + && strcmp (protocol_getName (list[1]), "MySecondProtocol") == 0) + || (strcmp (protocol_getName (list[0]), "MySecondProtocol") == 0 + && strcmp (protocol_getName (list[1]), "MyProtocol") == 0))) + abort (); + + if (list[2] != NULL) + abort (); + } + + printf ("Testing class_createInstance ()...\n"); + { + MySubClass *object = [[MySubClass alloc] init]; + + [object setVariable: object]; + if ([object variable] != object) + abort (); + } + + printf ("Testing class_getClassMethod ()...\n"); + { + Method method = class_getClassMethod (objc_getClass ("MySubClass"), + @selector(alloc)); + + if (method == NULL) + abort (); + + if (strcmp (sel_getName (method_getName (method)), "alloc") != 0) + abort (); + + if (class_getClassMethod (objc_getClass ("MySubClass"), + @selector(variable))) + abort (); + } + + printf ("Testing class_getClassVariable ()...\n"); + { + if (class_getClassVariable (objc_getClass ("MySubClass"), "variable_ivar")) + abort (); + } + + printf ("Testing class_getInstanceMethod ()...\n"); + { + Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), + @selector(variable)); + + if (method == NULL) + abort (); + + if (strcmp (sel_getName (method_getName (method)), "variable") != 0) + abort (); + + if (class_getInstanceMethod (objc_getClass ("MySubClass"), + @selector(alloc))) + abort (); + } + + printf ("Testing class_getInstanceSize ()...\n"); + { + if (class_getInstanceSize (objc_getClass ("MyRootClass")) != sizeof (struct objc_object)) + abort (); + } + + printf ("Testing class_getInstanceVariable ()...\n"); + { + Ivar variable = class_getInstanceVariable (objc_getClass ("MySubClass"), "variable_ivar"); + + if (variable == NULL) + abort (); + + if (strcmp (ivar_getName (variable), "variable_ivar") != 0) + abort (); + + if (class_getInstanceVariable (objc_getClass ("MySubClass"), "variable_ivar_no")) + abort (); + } + + printf ("Testing class_getIvarLayout ()...\n"); + { + if (class_getIvarLayout (objc_getClass ("MyRootClass")) != NULL) + abort (); + } + + printf ("Testing class_getMethodImplementation ()...\n"); + { + MySubClass *object = [[MySubClass alloc] init]; + IMP imp = class_getMethodImplementation (objc_getClass ("MySubClass"), + @selector(variable)); + + if (imp == NULL) + abort (); + + [object setVariable: object]; + + if ((*imp)(object, @selector(variable)) != object) + abort (); + } + + /* This function does not exist with the GNU runtime. */ + /* printf ("Testing class_getMethodImplementation_stret ()...\n"); */ + + printf ("Testing class_getName ()...\n"); + { + if (strcmp (class_getName (objc_getClass ("MyRootClass")), + "MyRootClass") != 0) + abort (); + } + + /* TODO: Test new ABI (when available). */ + printf ("Testing class_getProperty ()...\n"); + { + if (class_getProperty (objc_getClass ("MyRootClass"), "property") != NULL) + abort (); + } + + printf ("Testing class_getSuperclass ()...\n"); + { + MySubClass *object = [[MySubClass alloc] init]; + if (class_getSuperclass (object_getClass (object)) != objc_getClass ("MyRootClass")) + abort (); + } + + printf ("Testing class_getVersion ()...\n"); + { + if (class_getVersion (objc_getClass ("MySubClass")) != 0) + abort (); + } + + printf ("Testing class_getWeakIvarLayout ()...\n"); + { + if (class_getWeakIvarLayout (objc_getClass ("MyRootClass")) != NULL) + abort (); + } + + printf ("Testing class_isMetaClass ()...\n"); + { + MySubClass *object = [[MySubClass alloc] init]; + if (class_isMetaClass (object_getClass (object)) + || ! class_isMetaClass (object_getClass (object_getClass (object)))) + abort (); + } + + printf ("Testing class_replaceMethod ()...\n"); + { + Method new_method = class_getInstanceMethod (objc_getClass ("DifferentClass"), + @selector (myClass)); + Method old_method = class_getInstanceMethod (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_getClass ("MySubClass"), @selector (variable), + method_getImplementation (new_method), + method_getTypeEncoding (new_method)); + MySubClass *o = [[MySubClass alloc] init]; + + [o setVariable: o]; + + /* Try the new method implementation. */ + if ([o variable] != objc_getClass ("MySubClass")) + abort (); + + /* Put the original method back. */ + class_replaceMethod (objc_getClass ("MySubClass"), @selector (variable), + old_imp, old_types); + + /* Test it's back to what it was. */ + if ([o variable] != o) + abort (); + + { + DifferentClass *o = [[DifferentClass alloc] init]; + + /* Finally, try adding a new method. */ + class_replaceMethod (objc_getClass ("DifferentClass"), @selector (mySelf), + new_imp, new_types); + + if ([(MySubClass*)o mySelf] != objc_getClass ("DifferentClass")) + abort (); + } + } + + printf ("Testing class_respondsToSelector ()...\n"); + { + if (! class_respondsToSelector (objc_getClass ("MySubClass"), @selector(setVariable:))) + abort (); + + if (class_respondsToSelector (objc_getClass ("MyRootClass"), @selector(setVariable:))) + abort (); + } + + /* This is not really implemented with the GNU runtime. */ + /* printf ("Testing class_setIvarLayout ()...\n"); */ + + printf ("Testing class_setVersion ()...\n"); + { + class_setVersion (objc_getClass ("MySubClass"), 45); + + if (class_getVersion (objc_getClass ("MySubClass")) != 45) + abort (); + + class_setVersion (objc_getClass ("MySubClass"), 46); + + if (class_getVersion (objc_getClass ("MySubClass")) != 46) + abort (); + } + + /* This is not really implemented with the GNU runtime. */ + /* printf ("Testing class_setWeakIvarLayout ()...\n"); */ + + return 0; +} Index: sendmsg.c =================================================================== --- sendmsg.c (revision 165524) +++ sendmsg.c (working copy) @@ -42,6 +42,10 @@ see the files COPYING3 and COPYING.RUNTIME respect #include /* For assert */ #include /* For strlen */ +/* Temporarily while we include objc/objc-api.h instead of objc-private/module-abi-8.h. */ +#define _CLS_IN_CONSTRUCTION 0x10L +#define CLS_IS_IN_CONSTRUCTION(cls) __CLS_ISINFO(cls, _CLS_IN_CONSTRUCTION) + /* This is how we hack STRUCT_VALUE to be 1 or 0. */ #define gen_rtx(args...) 1 #define gen_rtx_MEM(args...) 1 @@ -573,6 +577,80 @@ class_getClassMethod (Class class_, SEL selector) selector); } +BOOL +class_addMethod (Class class_, SEL selector, IMP implementation, + const char *method_types) +{ + struct objc_method_list *method_list; + struct objc_method *method; + const char *method_name; + + if (class_ == Nil || selector == NULL || implementation == NULL + || method_types == NULL || (strcmp (method_types, "") == 0)) + return NO; + + method_name = sel_get_name (selector); + if (method_name == NULL) + return NO; + + method_list = (struct objc_method_list *)objc_calloc (1, sizeof (struct objc_method_list)); + method_list->method_count = 1; + + method = &(method_list->method_list[0]); + method->method_name = objc_malloc (strlen (method_name) + 1); + strcpy ((char *)method->method_name, method_name); + + method->method_types = objc_malloc (strlen (method_types) + 1); + strcpy ((char *)method->method_types, method_types); + + method->method_imp = implementation; + + if (CLS_IS_IN_CONSTRUCTION (class_)) + { + /* We only need to add the method to the list. It will be + registered with the runtime when the class pair is registered + (if ever). */ + method_list->method_next = class_->methods; + class_->methods = method_list; + } + else + { + /* Add the method to a live class. */ + objc_mutex_lock (__objc_runtime_mutex); + class_add_method_list (class_, method_list); + objc_mutex_unlock (__objc_runtime_mutex); + } + + return YES; +} + +/* Temporarily, until we include objc/runtime.h. */ +extern IMP +method_setImplementation (struct objc_method * method, IMP implementation); + +IMP +class_replaceMethod (Class class_, SEL selector, IMP implementation, + const char *method_types) +{ + struct objc_method * method; + + if (class_ == Nil || selector == NULL || implementation == NULL + || method_types == NULL) + return NULL; + + method = search_for_method_in_hierarchy (class_, selector); + + if (method) + { + return method_setImplementation (method, implementation); + } + else + { + class_addMethod (class_, selector, implementation, method_types); + return NULL; + } +} + /* Search for a method starting from the current class up its hierarchy. Return a pointer to the method's method structure if found. NULL otherwise. */ Index: init.c =================================================================== --- init.c (revision 165524) +++ init.c (working copy) @@ -623,24 +623,8 @@ __objc_exec_class (Module_t module) In some cases it isn't and this crashes the program. */ class->subclass_list = NULL; - /* Store the class in the class table and assign class numbers. */ - __objc_add_class_to_hash (class); + __objc_init_class (class); - /* Register all of the selectors in the class and meta class. */ - __objc_register_selectors_from_class (class); - __objc_register_selectors_from_class ((Class) class->class_pointer); - - /* Install the fake dispatch tables */ - __objc_install_premature_dtable (class); - __objc_install_premature_dtable (class->class_pointer); - - /* Register the instance methods as class methods, this is - only done for root classes. */ - __objc_register_instance_methods_to_class (class); - - if (class->protocols) - __objc_init_protocols (class->protocols); - /* Check to see if the superclass is known in this point. If it's not add the class to the unresolved_classes list. */ if (superclass && ! objc_lookup_class (superclass)) @@ -864,6 +848,29 @@ init_check_module_version (Module_t module) } } +/* __objc_init_class must be called with __objc_runtime_mutex already locked. */ +void +__objc_init_class (Class class) +{ + /* Store the class in the class table and assign class numbers. */ + __objc_add_class_to_hash (class); + + /* Register all of the selectors in the class and meta class. */ + __objc_register_selectors_from_class (class); + __objc_register_selectors_from_class ((Class) class->class_pointer); + + /* Install the fake dispatch tables */ + __objc_install_premature_dtable (class); + __objc_install_premature_dtable (class->class_pointer); + + /* Register the instance methods as class methods, this is only done + for root classes. */ + __objc_register_instance_methods_to_class (class); + + if (class->protocols) + __objc_init_protocols (class->protocols); +} + /* __objc_init_protocol must be called with __objc_runtime_mutex already locked, and the "Protocol" class already registered. */ static void Index: objc-private/module-abi-8.h =================================================================== --- objc-private/module-abi-8.h (revision 165524) +++ objc-private/module-abi-8.h (working copy) @@ -181,7 +181,7 @@ struct objc_protocol_list places a string in the following member variables: super_class. */ #ifndef __objc_STRUCT_OBJC_CLASS_defined -struct objc_class { +struct objc_class { struct objc_class* class_pointer; /* Pointer to the class's meta class. */ struct objc_class* super_class; /* Pointer to the super @@ -234,6 +234,7 @@ struct objc_protocol_list #define __CLS_INFO(cls) ((cls)->info) #define __CLS_ISINFO(cls, mask) ((__CLS_INFO(cls)&mask)==mask) #define __CLS_SETINFO(cls, mask) (__CLS_INFO(cls) |= mask) +#define __CLS_SETNOTINFO(cls, mask) (__CLS_INFO(cls) &= ~mask) /* The structure is of type MetaClass */ #define _CLS_META 0x2L @@ -255,6 +256,16 @@ struct objc_protocol_list #define CLS_ISINITIALIZED(cls) __CLS_ISINFO(cls, _CLS_INITIALIZED) #define CLS_SETINITIALIZED(cls) __CLS_SETINFO(cls, _CLS_INITIALIZED) +/* The class is being constructed; it has been allocated using + objc_allocateClassPair(), but has not been registered yet by using + objc_registerClassPair(). This means it is possible to freely add + instance variables to the class, but it can't be used for anything + yet. */ +#define _CLS_IN_CONSTRUCTION 0x10L +#define CLS_IS_IN_CONSTRUCTION(cls) __CLS_ISINFO(cls, _CLS_IN_CONSTRUCTION) +#define CLS_SET_IN_CONSTRUCTION(cls) __CLS_SETINFO(cls, _CLS_IN_CONSTRUCTION) +#define CLS_SET_NOT_IN_CONSTRUCTION(cls) __CLS_SETNOTINFO(cls, _CLS_IN_CONSTRUCTION) + /* The class number of this class. This must be the same for both the class and its meta class object. */ #define CLS_GETNUMBER(cls) (__CLS_INFO(cls) >> (HOST_BITS_PER_LONG/2)) Index: objc-private/runtime.h =================================================================== --- objc-private/runtime.h (revision 165525) +++ objc-private/runtime.h (working copy) @@ -67,7 +67,7 @@ extern void __objc_update_dispatch_table_for_class extern int __objc_init_thread_system(void); /* thread.c */ extern int __objc_fini_thread_system(void); /* thread.c */ extern void __objc_print_dtable_stats(void); /* sendmsg.c */ - +extern void __objc_init_class (Class class); /* init.c */ extern void class_add_method_list(Class, struct objc_method_list *); /* Registering instance methods as class methods for root classes */ Index: class.c =================================================================== --- class.c (revision 165542) +++ class.c (working copy) @@ -570,6 +570,199 @@ objc_getClassList (Class *returnValue, int maxNumb return count; } +Class +objc_allocateClassPair (Class super_class, const char *class_name, size_t extraBytes) +{ + Class new_class; + Class new_meta_class; + + if (class_name == NULL) + return Nil; + + if (objc_getClass (class_name)) + return Nil; + + if (super_class) + { + /* If you want to build a hierarchy of classes, you need to + build and register them one at a time. The risk is that you + are able to cause confusion by registering a subclass before + the superclass or similar. */ + if (CLS_IS_IN_CONSTRUCTION (super_class)) + return Nil; + } + + /* Technically, we should create the metaclass first, then use + class_createInstance() to create the class. That complication + would be relevant if we had class variables, but we don't, so we + just ignore it and create everything directly and assume all + classes have the same size. */ + new_class = objc_calloc (1, sizeof (struct objc_class) + extraBytes); + new_meta_class = objc_calloc (1, sizeof (struct objc_class) + extraBytes); + + /* We create an unresolved class, similar to one generated by the + compiler. It will be resolved later when we register it. + + Note how the metaclass details are not that important; when the + class is resolved, the ones that matter will be fixed up. */ + new_class->class_pointer = new_meta_class; + new_meta_class->class_pointer = 0; + + if (super_class) + { + /* Force the name of the superclass in place of the link to the + actual superclass, which will be put there when the class is + resolved. */ + const char *super_class_name = class_getName (super_class); + new_class->super_class = (void *)super_class_name; + new_meta_class->super_class = (void *)super_class_name; + } + else + { + new_class->super_class = (void *)0; + new_meta_class->super_class = (void *)0; + } + + new_class->name = objc_malloc (strlen (class_name) + 1); + strcpy ((char*)new_class->name, class_name); + new_meta_class->name = new_class->name; + + new_class->version = 0; + new_meta_class->version = 0; + + new_class->info = _CLS_CLASS | _CLS_IN_CONSTRUCTION; + new_meta_class->info = _CLS_META | _CLS_IN_CONSTRUCTION; + + if (super_class) + new_class->instance_size = super_class->instance_size; + else + new_class->instance_size = 0; + new_meta_class->instance_size = sizeof (struct objc_class); + + return new_class; +} + +void +objc_registerClassPair (Class class_) +{ + if (class_ == Nil) + return; + + if ((! CLS_ISCLASS (class_)) || (! CLS_IS_IN_CONSTRUCTION (class_))) + return; + + if ((! CLS_ISMETA (class_->class_pointer)) || (! CLS_IS_IN_CONSTRUCTION (class_->class_pointer))) + return; + + objc_mutex_lock (__objc_runtime_mutex); + + if (objc_getClass (class_->name)) + { + objc_mutex_unlock (__objc_runtime_mutex); + return; + } + + CLS_SET_NOT_IN_CONSTRUCTION (class_); + CLS_SET_NOT_IN_CONSTRUCTION (class_->class_pointer); + + __objc_init_class (class_); + + /* Resolve class links immediately. No point in waiting. */ + __objc_resolve_class_links (); + + objc_mutex_unlock (__objc_runtime_mutex); +} + +void +objc_disposeClassPair (Class class_) +{ + if (class_ == Nil) + return; + + if ((! CLS_ISCLASS (class_)) || (! CLS_IS_IN_CONSTRUCTION (class_))) + return; + + if ((! CLS_ISMETA (class_->class_pointer)) || (! CLS_IS_IN_CONSTRUCTION (class_->class_pointer))) + return; + + /* Undo any class_addIvar(). */ + if (class_->ivars) + { + int i; + for (i = 0; i < class_->ivars->ivar_count; i++) + { + struct objc_ivar *ivar = &(class_->ivars->ivar_list[i]); + + objc_free ((char *)ivar->ivar_name); + objc_free ((char *)ivar->ivar_type); + } + + objc_free (class_->ivars); + } + + /* Undo any class_addMethod(). */ + if (class_->methods) + { + struct objc_method_list *list = class_->methods; + while (list) + { + int i; + struct objc_method_list *next = list->method_next; + + for (i = 0; i < list->method_count; i++) + { + struct objc_method *method = &(list->method_list[i]); + + objc_free ((char *)method->method_name); + objc_free ((char *)method->method_types); + } + + objc_free (list); + list = next; + } + } + + /* Undo any class_addProtocol(). */ + if (class_->protocols) + { + struct objc_protocol_list *list = class_->protocols; + while (list) + { + struct objc_protocol_list *next = list->next; + + objc_free (list); + list = next; + } + } + + /* Undo any class_addMethod() on the meta-class. */ + if (class_->class_pointer->methods) + { + struct objc_method_list *list = class_->class_pointer->methods; + while (list) + { + int i; + struct objc_method_list *next = list->method_next; + + for (i = 0; i < list->method_count; i++) + { + struct objc_method *method = &(list->method_list[i]); + + objc_free ((char *)method->method_name); + objc_free ((char *)method->method_types); + } + + objc_free (list); + list = next; + } + } + + /* Undo objc_allocateClassPair(). */ + objc_free ((char *)(class_->name)); + objc_free (class_->class_pointer); + objc_free (class_); +} + /* Traditional GNU Objective-C Runtime API. */ /* Get the class object for the class named NAME. If NAME does not identify a known class, the hook _objc_lookup_class is called. If @@ -807,6 +1000,11 @@ class_getSuperclass (Class class_) if (class_ == Nil) return Nil; + /* Classes that are in construction are not resolved and can not be + resolved! */ + if (CLS_IS_IN_CONSTRUCTION (class_)) + return Nil; + /* 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 super class. In that case, we need to resolve the class links Index: ChangeLog =================================================================== --- ChangeLog (revision 165542) +++ ChangeLog (working copy) @@ -1,5 +1,34 @@ 2010-10-16 Nicola Pero + * objc/runtime.h: Updated comments. + (class_addMethod): New. + (class_addIvar): New. + (class_replaceMethod): New. + (objc_allocateClassPair): New. + (objc_registerClassPair): New. + (objc_disposeClassPair): New. + * class.c (objc_allocateClassPair): New. + (objc_registerClassPair): New. + (objc_disposeClassPair): New. + (class_getSuperclass): Return Nil if a class is in construction. + * init.c (__objc_exec_class): Call __objc_init_class. + (__objc_init_class): New. + * ivars.c (class_copyIvarList): Return NULL if class is in + construction. Do not lock the runtime mutex. + (class_getInstanceVariable): Return NULL if class is in + construction. Do not lock the runtime mutex. + (class_addIvar): New. + * sendmsg.c (class_addMethod): New. + (class_replaceMethod): New. + * objc-private/module-abi-8.h (__CLS_SETNOTINFO): New. + (_CLS_IN_CONSTRUCTION): New. + (CLS_IS_IN_CONSTRUCTION): New. + (CLS_SET_IN_CONSTRUCTION): New. + (CLS_SET_NOT_IN_CONSTRUCTION): New. + * objc-private/runtime.h (__objc_init_class): New. + +2010-10-16 Nicola Pero + * class.c (class_getSuperclass): Call __objc_resolve_class_links if the class is not resolved yet. * ivars.c (class_getInstanceVariable): Use class_getSuperclass. Index: ivars.c =================================================================== --- ivars.c (revision 165542) +++ ivars.c (working copy) @@ -32,9 +32,8 @@ see the files COPYING3 and COPYING.RUNTIME respect struct objc_ivar * class_getInstanceVariable (Class class_, const char *name) { - if (class_ != Nil && name != NULL) + if (class_ != Nil && name != NULL && ! CLS_IS_IN_CONSTRUCTION (class_)) { - objc_mutex_lock (__objc_runtime_mutex); while (class_ != Nil) { struct objc_ivar_list *ivars = class_->ivars; @@ -48,14 +47,12 @@ class_getInstanceVariable (Class class_, const cha if (!strcmp (ivar->ivar_name, name)) { - objc_mutex_unlock (__objc_runtime_mutex); return ivar; } } } class_ = class_getSuperclass (class_); } - objc_mutex_unlock (__objc_runtime_mutex); } return NULL; } @@ -185,22 +182,13 @@ struct objc_ivar ** class_copyIvarList (Class clas struct objc_ivar **returnValue = NULL; struct objc_ivar_list* ivar_list; - if (class_ == Nil) + if (class_ == Nil || CLS_IS_IN_CONSTRUCTION (class_)) { if (numberOfReturnedIvars) *numberOfReturnedIvars = 0; return NULL; } - - /* TODO: We do not need to lock the runtime mutex if the class has - been registered with the runtime, since the instance variable - list can not change after the class is registered. The only case - where the lock may be useful if the class is still being created - using objc_allocateClassPair(), but has not been registered using - objc_registerClassPair() yet. I'm not even sure that is - allowed. */ - objc_mutex_lock (__objc_runtime_mutex); - + /* Count how many ivars we have. */ ivar_list = class_->ivars; count = ivar_list->ivar_count; @@ -221,14 +209,99 @@ struct objc_ivar ** class_copyIvarList (Class clas returnValue[i] = NULL; } - objc_mutex_unlock (__objc_runtime_mutex); - if (numberOfReturnedIvars) *numberOfReturnedIvars = count; return returnValue; } +BOOL +class_addIvar (Class class_, const char * ivar_name, size_t size, + unsigned char alignment, const char *type) +{ + struct objc_ivar_list *ivars; + + if (class_ == Nil + || (! CLS_IS_IN_CONSTRUCTION (class_)) + || ivar_name == NULL + || (strcmp (ivar_name, "") == 0) + || size == 0 + || type == NULL) + return NO; + + /* Check if the class has an instance variable with that name + already. */ + ivars = class_->ivars; + + if (ivars != NULL) + { + int i; + + for (i = 0; i < ivars->ivar_count; i++) + { + struct objc_ivar *ivar = &(ivars->ivar_list[i]); + + if (strcmp (ivar->ivar_name, ivar_name) == 0) + { + return NO; + } + } + } + + /* Ok, no direct ivars. Check superclasses. */ + if (class_getInstanceVariable (objc_getClass ((char *)(class_->super_class)), + ivar_name)) + return NO; + + /* Good. Create space for the new instance variable. */ + if (ivars) + { + int ivar_count = ivars->ivar_count + 1; + int new_size = sizeof (struct objc_ivar_list) + + (ivar_count - 1) * sizeof (struct objc_ivar); + + ivars = (struct objc_ivar_list*) objc_realloc (ivars, new_size); + ivars->ivar_count = ivar_count; + class_->ivars = ivars; + } + else + { + int new_size = sizeof (struct objc_ivar_list); + + ivars = (struct objc_ivar_list*) objc_malloc (new_size); + ivars->ivar_count = 1; + class_->ivars = ivars; + } + + /* Now ivars is set to a list of instance variables of the right + size. */ + { + struct objc_ivar *ivar = &(ivars->ivar_list[ivars->ivar_count - 1]); + int misalignment; + + ivar->ivar_name = objc_malloc (strlen (ivar_name) + 1); + strcpy ((char *)ivar->ivar_name, ivar_name); + + ivar->ivar_type = objc_malloc (strlen (type) + 1); + strcpy ((char *)ivar->ivar_type, type); + + /* The new instance variable is placed at the end of the existing + instance_size, at the first byte that is aligned with + alignment. */ + misalignment = class_->instance_size % alignment; + + if (misalignment == 0) + ivar->ivar_offset = class_->instance_size; + else + ivar->ivar_offset = class_->instance_size - misalignment + alignment; + + class_->instance_size = ivar->ivar_offset + objc_sizeof_type (ivar->ivar_type); + } + + return YES; +} + + const char * property_getName (struct objc_property * property __attribute__ ((__unused__))) { Index: objc/runtime.h =================================================================== --- objc/runtime.h (revision 165533) +++ objc/runtime.h (working copy) @@ -302,9 +302,36 @@ objc_EXPORT const char * ivar_getTypeEncoding (Iva include instance variables of superclasses. The list is terminated by NULL. Optionally, if you pass a non-NULL 'numberOfReturnedIvars' pointer, the unsigned int that it points to - will be filled with the number of instance variables returned. */ + will be filled with the number of instance variables returned. + Return NULL for classes still in construction (ie, allocated using + objc_allocatedClassPair() but not yet registered with the runtime + using objc_registerClassPair()). */ objc_EXPORT Ivar * class_copyIvarList (Class class_, unsigned int *numberOfReturnedIvars); +/* Add an instance variable with name 'ivar_name' to class 'class_', + where 'class_' is a class in construction that has been created + using objc_allocateClassPair() and has not been registered with the + runtime using objc_registerClassPair() yet. You can not add + instance variables to classes already registered with the runtime. + 'size' is the size of the instance variable, 'alignment' the + alignment, and 'type' the type encoding of the variable type. You + can use objc_sizeof_type() (or sizeof()), objc_alignof_type() (or + __alignof__()) and @encode() to determine the right 'size', + 'alignment' and 'type' for your instance variable. For example, to + add an instance variable name "my_variable" and of type 'id', you + can use: + + class_addIvar (class, "my_variable", sizeof (id), __alignof__ (id), + @encode (id)); + + Return YES if the variable was added, and NO if not. In + particular, return NO if 'class_' is Nil, or a meta-class or a + class not in construction. Return Nil also if 'ivar_name' or + 'type' is NULL, or 'size' is 0. + */ +objc_EXPORT BOOL class_addIvar (Class class_, const char * ivar_name, size_t size, + unsigned char alignment, const char *type); + /* Return the name of the property. Return NULL if 'property' is NULL. */ objc_EXPORT const char * property_getName (Property property); @@ -383,7 +410,6 @@ typedef Class (*objc_get_unknown_class_handler)(co objc_get_unknown_class_handler objc_setGetUnknownClassHandler (objc_get_unknown_class_handler new_handler); - /* Return the class with name 'name', if it is already registered with the runtime. If it is not registered, and objc_setGetUnknownClassHandler() has been called to set a handler @@ -437,11 +463,11 @@ objc_EXPORT const char * class_getName (Class clas is Nil, return NO. */ objc_EXPORT BOOL class_isMetaClass (Class class_); -/* Return the superclass of 'class_'. If 'class_' is Nil, or it is a root - class, return Nil. - - TODO: It may be worth to define this inline, since it is usually - used in loops when traversing the class hierarchy. */ +/* Return the superclass of 'class_'. If 'class_' is Nil, or it is a + root class, return Nil. If 'class_' is a class being constructed, + that is, a class returned by objc_allocateClassPair() but before it + has been registered with the runtime using + objc_registerClassPair(), return Nil. */ objc_EXPORT Class class_getSuperclass (Class class_); /* Return the 'version' number of the class, which is an integer that @@ -496,7 +522,65 @@ method_setImplementation (Method method, IMP imple objc_EXPORT void method_exchangeImplementations (Method method_a, Method method_b); +/* Create a new class/meta-class pair. This function is called to + create a new class at runtime. The class is created with + superclass 'superclass' (use 'Nil' to create a new root class) and + name 'class_name'. 'extraBytes' can be used to specify some extra + space for indexed variables to be added at the end of the class and + meta-class objects (it is recommended that you set extraBytes to + 0). Once you have created the class, it is not usable yet. You + need to add any instance variables (by using class_addIvar()), any + instance methods (by using class_addMethod()) and any class methods + (by using class_addMethod() on the meta-class, as in + class_addMethod (object_getClass (class), method)) that are + required, and then you need to call objc_registerClassPair() to + activate the class. If you need to create a hierarchy of classes, + you need to create and register them one at a time. You can not + create a new class using another class in construction as + superclass. Return Nil if 'class-name' is NULL or if a class with + that name already exists or 'superclass' is a class still in + construction. + Implementation Note: in the GNU runtime, allocating a class pair + only creates the structures for the class pair, but does not + register anything with the runtime. The class is registered with + the runtime only when objc_registerClassPair() is called. In + particular, if a class is in construction, objc_getClass() will not + find it, the superclass will not know about it, + class_getSuperclass() will return Nil and another thread may + allocate a class pair with the same name; the conflict will only be + detected when the classes are registered with the runtime. + */ +objc_EXPORT Class +objc_allocateClassPair (Class super_class, const char *class_name, + size_t extraBytes); + +/* Register a class pair that was created with + objc_allocateClassPair(). After you register a class, you can no + longer make changes to its instance variables, but you can start + creating instances of it. Do nothing if 'class_' is NULL or if it + is not a class allocated by objc_allocateClassPair() and still in + construction. */ +objc_EXPORT void +objc_registerClassPair (Class class_); + +/* Dispose of a class pair created using objc_allocateClassPair(). + Call this function if you started creating a new class with + objc_allocateClassPair() but then want to abort the process. You + should not access 'class_' after calling this method. Note that if + 'class_' has already been registered with the runtime via + objc_registerClassPair(), this function does nothing; you can only + dispose of class pairs that are still being constructed. Do + nothing if class is 'Nil' or if 'class_' is not a class being + constructed. */ +objc_EXPORT void +objc_disposeClassPair (Class class_); + +/* Compatibility Note: The Apple/NeXT runtime has the function + objc_duplicateClass () but it's undocumented. The GNU runtime does + not have it. */ + + /** Implementation: the following functions are in sendmsg.c. */ /* Return the instance method with selector 'selector' of class @@ -534,7 +618,34 @@ objc_EXPORT IMP class_getMethodImplementation (Cla (object_getClass (class_), selector)). */ objc_EXPORT BOOL class_respondsToSelector (Class class_, SEL selector); +/* Add a method to a class. Use this function to add a new method to + a class (potentially overriding a method with the same selector in + the superclass); if you want to modify an existing method, use + method_setImplementation() instead (or class_replaceMethod ()). + This method adds an instance method to 'class_'; to add a class + method, get the meta class first, then add the method to the meta + class, that is, use + class_addMethod (object_getClass (class_), selector, + implementation, type); + + Return YES if the method was added, and NO if not. Do nothing if + one of the arguments is NULL. */ +objc_EXPORT BOOL class_addMethod (Class class_, SEL selector, IMP implementation, + const char *method_types); + +/* Replace a method in a class. If the class already have a method + with this 'selector', find it and use method_setImplementation() to + replace the implementation with 'implementation' (method_types is + ignored in that case). If the class does not already have a method + with this 'selector', call 'class_addMethod() to add it. + + Return the previous implementation of the method, or NULL if none + was found. Return NULL if any of the arguments is NULL. */ +objc_EXPORT IMP class_replaceMethod (Class class_, SEL selector, IMP implementation, + const char *method_types); + + /** Implementation: the following functions are in methods.c. */ /* Return the selector for method 'method'. Return NULL if 'method'