From patchwork Sat Dec 18 12:23:03 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicola Pero X-Patchwork-Id: 76066 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 6F277B708B for ; Sat, 18 Dec 2010 23:23:41 +1100 (EST) Received: (qmail 2229 invoked by alias); 18 Dec 2010 12:23:37 -0000 Received: (qmail 2186 invoked by uid 22791); 18 Dec 2010 12:23:24 -0000 X-SWARE-Spam-Status: No, hits=-0.0 required=5.0 tests=AWL, BAYES_50, TW_BJ, 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, 18 Dec 2010 12:23:13 +0000 Received: from eggs.gnu.org ([140.186.70.92]:52765) by fencepost.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.69) (envelope-from ) id 1PTvoa-0008FL-Oq for gcc-patches@gnu.org; Sat, 18 Dec 2010 07:23:08 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PTvoX-0002D3-Ce for gcc-patches@gnu.org; Sat, 18 Dec 2010 07:23:11 -0500 Received: from smtp201.iad.emailsrvr.com ([207.97.245.201]:36441) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PTvoX-0002Co-7m for gcc-patches@gnu.org; Sat, 18 Dec 2010 07:23:05 -0500 Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp40.relay.iad1a.emailsrvr.com (SMTP Server) with ESMTP id 22675350473 for ; Sat, 18 Dec 2010 07:23:04 -0500 (EST) Received: from dynamic5.wm-web.iad.mlsrvr.com (dynamic5.wm-web.iad1a.rsapps.net [192.168.2.146]) by smtp40.relay.iad1a.emailsrvr.com (SMTP Server) with ESMTP id 0A2C13501C3 for ; Sat, 18 Dec 2010 07:23:04 -0500 (EST) Received: from meta-innovation.com (localhost [127.0.0.1]) by dynamic5.wm-web.iad.mlsrvr.com (Postfix) with ESMTP id E8B4C8D0505 for ; Sat, 18 Dec 2010 07:23:03 -0500 (EST) Received: by www2.webmail.us (Authenticated sender: nicola.pero@meta-innovation.com, from: nicola.pero@meta-innovation.com) with HTTP; Sat, 18 Dec 2010 13:23:03 +0100 (CET) Date: Sat, 18 Dec 2010 13:23:03 +0100 (CET) Subject: libobjc: Tidyup of comments and indentation From: "Nicola Pero" To: "gcc-patches@gnu.org" MIME-Version: 1.0 X-Type: plain Message-ID: <1292674983.950725054@192.168.2.228> 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 just tidies up comments and indentation of a number of files in libobjc; no actual code changes. Committed to trunk. Thanks In libobjc/: 2010-12-18 Nicola Pero * class.c: Tidied up comments and indentation. No code changes. * error.c: Same. * exception.c: Same. * init.c: Same. * ivars.c: Same. * memory.c: Same. * objc-foreach.c: Same. * objc-sync.c: Same. * objects.c: Same. * protocols.c: Same. * sarray.c: Same. * thr.c: Same. Index: gcc/testsuite/ChangeLog =================================================================== --- gcc/testsuite/ChangeLog (revision 167711) +++ gcc/testsuite/ChangeLog (working copy) @@ -1,3 +1,8 @@ +2010-12-11 Nicola Pero + + * objc.dg/gnu-api-2-resolve-method.m: New. + * obj-c++.dg/gnu-api-2-resolve-method.mm: New. + 2010-12-10 Ahmad Sharif * gcc.target/i386/max-stack-align.c: New testcase. Index: gcc/testsuite/objc.dg/gnu-api-2-resolve-method.m =================================================================== --- gcc/testsuite/objc.dg/gnu-api-2-resolve-method.m (revision 0) +++ gcc/testsuite/objc.dg/gnu-api-2-resolve-method.m (revision 0) @@ -0,0 +1,563 @@ +/* Test the Modern GNU Objective-C Runtime API. + + This is test 'resolve-method', covering +resolveClassMethod: and + +resolveInstanceMethod:. */ + +/* { 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 + + +/* A number of tests will try invoking methods that don't exist. We + want to record the fact, but not abort the program, so we supply + our own fowarding implementation which will invoke the following + function for any method that is not found. */ + +/* Keep track of how many times a non-existing method was executed. */ +static int nonExistingMethodCount = 0; + +/* Inspired by nil_method in libobjc. */ +id nonExisting_method (id receiver __attribute__ ((__unused__)), + SEL sel __attribute__ ((__unused__))) +{ + nonExistingMethodCount++; + return nil; +} + +/* Keep track of how many times the forwarding lookup was invoked. */ +static int forwardingCount = 0; + +/* We install this forwarding hook to cause all failed method lookups + to call our 'nonExisting_method' function. */ +IMP forward_everything_to_non_existing_method (id receiver __attribute__ ((__unused__)), + SEL sel __attribute__ ((__unused__))) +{ + forwardingCount++; + return (IMP)nonExisting_method; +} + + +/* 'CountClass' is used to test that +resolveClassMethod: and + +resolveInstanceMethod: are called when expected. They do nothing + other than recording that they are called. */ +@interface CountClass : MyRootClass ++ (BOOL) resolveClassMethod: (SEL)selector; ++ (BOOL) resolveInstanceMethod: (SEL)selector; ++ (void) existingClassMethod; +- (void) existingInstanceMethod; +@end + +/* Count how many times the methods are called for class + 'CountClass'. */ +static int resolveClassMethodCount = 0; +static int resolveInstanceMethodCount = 0; + +@implementation CountClass : MyRootClass ++ (BOOL) resolveClassMethod: (SEL)selector +{ + resolveClassMethodCount++; + return NO; +} ++ (BOOL) resolveInstanceMethod: (SEL)selector +{ + resolveInstanceMethodCount++; + return NO; +} ++ (void) existingClassMethod +{ + return; +} +- (void) existingInstanceMethod +{ + return; +} +@end + +@protocol NonExistingStuff ++ (void) nonExistingClassMethod; +- (void) nonExistingInstanceMethod; +@end + +/* Declare a category with some non existing methods, but don't + actually implement them. */ +@interface CountClass (NonExistingStuff) +@end + + +/* 'SelfExtendingClass' is used to test that +resolveClassMethod: and + +resolveInstanceMethod: can extend the class. Any time they are + called, they install the requested method, mapping it to the same + implementation as 'countHits'. */ +@interface SelfExtendingClass : MyRootClass ++ (int) countHits; ++ (BOOL) resolveClassMethod: (SEL)selector; ++ (BOOL) resolveInstanceMethod: (SEL)selector; +@end + +/* How many times the countHits method (or a clone) was called. */ +static int hitCount = 0; + +@implementation SelfExtendingClass : MyRootClass ++ (int) countHits +{ + hitCount++; + return hitCount; +} ++ (BOOL) resolveClassMethod: (SEL)selector +{ + /* Duplicate the 'countHits' method into the new method. */ + Method method = class_getClassMethod (self, @selector (countHits)); + class_addMethod (object_getClass (self), selector, + method_getImplementation (method), + method_getTypeEncoding (method)); + resolveClassMethodCount++; + return YES; +} ++ (BOOL) resolveInstanceMethod: (SEL)selector +{ + /* Duplicate the 'countHits' method into the new method. */ + Method method = class_getClassMethod (self, @selector (countHits)); + class_addMethod (self, selector, + method_getImplementation (method), + method_getTypeEncoding (method)); + resolveInstanceMethodCount++; + return YES; + +} +@end + +@protocol NonExistingStuff2 ++ (int) nonExistingCountHitsMethod; +- (int) nonExistingCountHitsMethod; + ++ (int) nonExistingCountHitsMethod2; +- (int) nonExistingCountHitsMethod2; + ++ (int) nonExistingCountHitsMethod3; +- (int) nonExistingCountHitsMethod3; +@end + +/* Declare a category with some non existing methods, but don't + actually implement them. */ +@interface SelfExtendingClass (NonExistingStuff) +@end + + +int main (int argc, void **args) +{ + /* Functions are tested in alphabetical order. */ + + /* Install our test forwarding hook. */ + __objc_msg_forward2 = forward_everything_to_non_existing_method; + + printf ("Testing [+resolveClassMethod:]...\n"); + { + Method m; + IMP i; + + /** CountClass tests. **/ + + /* Call an existing method. No +resolveClassMethod and no + forwarding should be triggered. */ + [CountClass existingClassMethod]; + + if (resolveClassMethodCount != 0) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Call a non-existing method. Both +resolveClassMethod and the + forwarding should be triggered. */ + [CountClass nonExistingClassMethod]; + + if (resolveClassMethodCount != 1) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Now try the same tests with class_getClassMethod(), which + should trigger the resolve methods too, but not the + forwarding. */ + m = class_getClassMethod (objc_getClass ("CountClass"), + @selector (existingClassMethod)); + if (resolveClassMethodCount != 1) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + m = class_getClassMethod (objc_getClass ("CountClass"), + @selector (nonExistingClassMethod)); + if (resolveClassMethodCount != 2) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Now try the same tests with class_getMethodImplementation(), + which should trigger the resolve methods and the forwarding + (but not execute the forwarding, obviously). */ + i = class_getMethodImplementation (object_getClass (objc_getClass ("CountClass")), + @selector (existingClassMethod)); + if (resolveClassMethodCount != 2) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + i = class_getMethodImplementation (object_getClass (objc_getClass ("CountClass")), + @selector (nonExistingClassMethod)); + if (resolveClassMethodCount != 3) + abort (); + + if (forwardingCount != 2) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + + /* Reset the counters for the next test. */ + resolveClassMethodCount = 0; + forwardingCount = 0; + nonExistingMethodCount = 0; + + + /** SelfExtendingClass tests. **/ + + /* Try the direct countHits method first. No resolving or + forwarding should be triggered. */ + if ([SelfExtendingClass countHits] != 1) + abort (); + + if (resolveClassMethodCount != 0) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Now, try calling a non-existing count method; it should be + installed and invoked. */ + if ([SelfExtendingClass nonExistingCountHitsMethod] != 2) + abort (); + + if (resolveClassMethodCount != 1) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([SelfExtendingClass nonExistingCountHitsMethod] != 3) + abort (); + + if (resolveClassMethodCount != 1) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + + /* Now try the same tests with class_getClassMethod(). */ + m = class_getClassMethod (objc_getClass ("SelfExtendingClass"), + @selector (nonExistingCountHitsMethod2)); + if (resolveClassMethodCount != 2) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([SelfExtendingClass nonExistingCountHitsMethod2] != 4) + abort (); + + if (resolveClassMethodCount != 2) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + + /* Now try the same tests with class_getMethodImplementation(). */ + i = class_getMethodImplementation (object_getClass (objc_getClass ("SelfExtendingClass")), + @selector (nonExistingCountHitsMethod3)); + if (resolveClassMethodCount != 3) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([SelfExtendingClass nonExistingCountHitsMethod3] != 5) + abort (); + + if (resolveClassMethodCount != 3) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + } + + /* Reset the counters for the next test. */ + nonExistingMethodCount = 0; + forwardingCount = 0; + hitCount = 0; + + printf ("Testing [+resolveInstanceMethod:]...\n"); + { + Method m; + IMP i; + CountClass *object = [[CountClass alloc] init]; + SelfExtendingClass *object2 = [[SelfExtendingClass alloc] init]; + + /** CountClass tests. **/ + + /* Call an existing method. No +resolveInstanceMethod and no + forwarding should be triggered. */ + [object existingInstanceMethod]; + + if (resolveInstanceMethodCount != 0) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Call a non-existing method. Both +resolveInstanceMethod and the + forwarding should be triggered. */ + [object nonExistingInstanceMethod]; + + if (resolveInstanceMethodCount != 1) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Now try the same tests with class_getInstanceMethod(), which + should trigger the resolve methods too, but not the + forwarding. */ + m = class_getInstanceMethod (objc_getClass ("CountClass"), + @selector (existingInstanceMethod)); + + if (resolveInstanceMethodCount != 1) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + m = class_getInstanceMethod (objc_getClass ("CountClass"), + @selector (nonExistingInstanceMethod)); + + if (resolveInstanceMethodCount != 2) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Now try the same tests with class_getMethodImplementation(), + which should trigger the resolve methods and the + forwarding. */ + i = class_getMethodImplementation (objc_getClass ("CountClass"), + @selector (existingInstanceMethod)); + if (resolveInstanceMethodCount != 2) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + i = class_getMethodImplementation (objc_getClass ("CountClass"), + @selector (nonExistingInstanceMethod)); + if (resolveInstanceMethodCount != 3) + abort (); + + if (forwardingCount != 2) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Reset the counters for the next test. */ + resolveInstanceMethodCount = 0; + forwardingCount = 0; + nonExistingMethodCount = 0; + + + /** SelfExtendingClass tests. **/ + + /* Try the direct countHits method first. No resolving or + forwarding should be triggered. */ + if ([SelfExtendingClass countHits] != 1) + abort (); + + if (resolveInstanceMethodCount != 0) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Now, try calling a non-existing count method; it should be + installed and invoked. */ + if ([object2 nonExistingCountHitsMethod] != 2) + abort (); + + if (resolveInstanceMethodCount != 1) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([object2 nonExistingCountHitsMethod] != 3) + abort (); + + if (resolveInstanceMethodCount != 1) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Now try the same tests with class_getInstanceMethod(). */ + m = class_getInstanceMethod (objc_getClass ("SelfExtendingClass"), + @selector (nonExistingCountHitsMethod2)); + if (resolveInstanceMethodCount != 2) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([object2 nonExistingCountHitsMethod2] != 4) + abort (); + + if (resolveInstanceMethodCount != 2) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + + /* Now try the same tests with class_getMethodImplementation(). */ + i = class_getMethodImplementation (objc_getClass ("SelfExtendingClass"), + @selector (nonExistingCountHitsMethod3)); + if (resolveInstanceMethodCount != 3) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([object2 nonExistingCountHitsMethod3] != 5) + abort (); + + if (resolveInstanceMethodCount != 3) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + } + + + return 0; +} Index: gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm =================================================================== --- gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm (revision 0) +++ gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm (revision 0) @@ -0,0 +1,563 @@ +/* Test the Modern GNU Objective-C Runtime API. + + This is test 'resolve-method', covering +resolveClassMethod: and + +resolveInstanceMethod:. */ + +/* { 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 + + +/* A number of tests will try invoking methods that don't exist. We + want to record the fact, but not abort the program, so we supply + our own fowarding implementation which will invoke the following + function for any method that is not found. */ + +/* Keep track of how many times a non-existing method was executed. */ +static int nonExistingMethodCount = 0; + +/* Inspired by nil_method in libobjc. */ +id nonExisting_method (id receiver __attribute__ ((__unused__)), + SEL sel __attribute__ ((__unused__))) +{ + nonExistingMethodCount++; + return nil; +} + +/* Keep track of how many times the forwarding lookup was invoked. */ +static int forwardingCount = 0; + +/* We install this forwarding hook to cause all failed method lookups + to call our 'nonExisting_method' function. */ +IMP forward_everything_to_non_existing_method (id receiver __attribute__ ((__unused__)), + SEL sel __attribute__ ((__unused__))) +{ + forwardingCount++; + return (IMP)nonExisting_method; +} + + +/* 'CountClass' is used to test that +resolveClassMethod: and + +resolveInstanceMethod: are called when expected. They do nothing + other than recording that they are called. */ +@interface CountClass : MyRootClass ++ (BOOL) resolveClassMethod: (SEL)selector; ++ (BOOL) resolveInstanceMethod: (SEL)selector; ++ (void) existingClassMethod; +- (void) existingInstanceMethod; +@end + +/* Count how many times the methods are called for class + 'CountClass'. */ +static int resolveClassMethodCount = 0; +static int resolveInstanceMethodCount = 0; + +@implementation CountClass : MyRootClass ++ (BOOL) resolveClassMethod: (SEL)selector +{ + resolveClassMethodCount++; + return NO; +} ++ (BOOL) resolveInstanceMethod: (SEL)selector +{ + resolveInstanceMethodCount++; + return NO; +} ++ (void) existingClassMethod +{ + return; +} +- (void) existingInstanceMethod +{ + return; +} +@end + +@protocol NonExistingStuff ++ (void) nonExistingClassMethod; +- (void) nonExistingInstanceMethod; +@end + +/* Declare a category with some non existing methods, but don't + actually implement them. */ +@interface CountClass (NonExistingStuff) +@end + + +/* 'SelfExtendingClass' is used to test that +resolveClassMethod: and + +resolveInstanceMethod: can extend the class. Any time they are + called, they install the requested method, mapping it to the same + implementation as 'countHits'. */ +@interface SelfExtendingClass : MyRootClass ++ (int) countHits; ++ (BOOL) resolveClassMethod: (SEL)selector; ++ (BOOL) resolveInstanceMethod: (SEL)selector; +@end + +/* How many times the countHits method (or a clone) was called. */ +static int hitCount = 0; + +@implementation SelfExtendingClass : MyRootClass ++ (int) countHits +{ + hitCount++; + return hitCount; +} ++ (BOOL) resolveClassMethod: (SEL)selector +{ + /* Duplicate the 'countHits' method into the new method. */ + Method method = class_getClassMethod (self, @selector (countHits)); + class_addMethod (object_getClass (self), selector, + method_getImplementation (method), + method_getTypeEncoding (method)); + resolveClassMethodCount++; + return YES; +} ++ (BOOL) resolveInstanceMethod: (SEL)selector +{ + /* Duplicate the 'countHits' method into the new method. */ + Method method = class_getClassMethod (self, @selector (countHits)); + class_addMethod (self, selector, + method_getImplementation (method), + method_getTypeEncoding (method)); + resolveInstanceMethodCount++; + return YES; + +} +@end + +@protocol NonExistingStuff2 ++ (int) nonExistingCountHitsMethod; +- (int) nonExistingCountHitsMethod; + ++ (int) nonExistingCountHitsMethod2; +- (int) nonExistingCountHitsMethod2; + ++ (int) nonExistingCountHitsMethod3; +- (int) nonExistingCountHitsMethod3; +@end + +/* Declare a category with some non existing methods, but don't + actually implement them. */ +@interface SelfExtendingClass (NonExistingStuff) +@end + + +int main () +{ + /* Functions are tested in alphabetical order. */ + + /* Install our test forwarding hook. */ + __objc_msg_forward2 = forward_everything_to_non_existing_method; + + std::cout << "Testing [+resolveClassMethod:] ...\n"; + { + Method m; + IMP i; + + /** CountClass tests. **/ + + /* Call an existing method. No +resolveClassMethod and no + forwarding should be triggered. */ + [CountClass existingClassMethod]; + + if (resolveClassMethodCount != 0) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Call a non-existing method. Both +resolveClassMethod and the + forwarding should be triggered. */ + [CountClass nonExistingClassMethod]; + + if (resolveClassMethodCount != 1) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Now try the same tests with class_getClassMethod(), which + should trigger the resolve methods too, but not the + forwarding. */ + m = class_getClassMethod (objc_getClass ("CountClass"), + @selector (existingClassMethod)); + if (resolveClassMethodCount != 1) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + m = class_getClassMethod (objc_getClass ("CountClass"), + @selector (nonExistingClassMethod)); + if (resolveClassMethodCount != 2) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Now try the same tests with class_getMethodImplementation(), + which should trigger the resolve methods and the forwarding + (but not execute the forwarding, obviously). */ + i = class_getMethodImplementation (object_getClass (objc_getClass ("CountClass")), + @selector (existingClassMethod)); + if (resolveClassMethodCount != 2) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + i = class_getMethodImplementation (object_getClass (objc_getClass ("CountClass")), + @selector (nonExistingClassMethod)); + if (resolveClassMethodCount != 3) + abort (); + + if (forwardingCount != 2) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + + /* Reset the counters for the next test. */ + resolveClassMethodCount = 0; + forwardingCount = 0; + nonExistingMethodCount = 0; + + + /** SelfExtendingClass tests. **/ + + /* Try the direct countHits method first. No resolving or + forwarding should be triggered. */ + if ([SelfExtendingClass countHits] != 1) + abort (); + + if (resolveClassMethodCount != 0) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Now, try calling a non-existing count method; it should be + installed and invoked. */ + if ([SelfExtendingClass nonExistingCountHitsMethod] != 2) + abort (); + + if (resolveClassMethodCount != 1) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([SelfExtendingClass nonExistingCountHitsMethod] != 3) + abort (); + + if (resolveClassMethodCount != 1) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + + /* Now try the same tests with class_getClassMethod(). */ + m = class_getClassMethod (objc_getClass ("SelfExtendingClass"), + @selector (nonExistingCountHitsMethod2)); + if (resolveClassMethodCount != 2) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([SelfExtendingClass nonExistingCountHitsMethod2] != 4) + abort (); + + if (resolveClassMethodCount != 2) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + + /* Now try the same tests with class_getMethodImplementation(). */ + i = class_getMethodImplementation (object_getClass (objc_getClass ("SelfExtendingClass")), + @selector (nonExistingCountHitsMethod3)); + if (resolveClassMethodCount != 3) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([SelfExtendingClass nonExistingCountHitsMethod3] != 5) + abort (); + + if (resolveClassMethodCount != 3) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + } + + /* Reset the counters for the next test. */ + nonExistingMethodCount = 0; + forwardingCount = 0; + hitCount = 0; + + std::cout << "Testing [+resolveInstanceMethod:] ...\n"; + { + Method m; + IMP i; + CountClass *object = [[CountClass alloc] init]; + SelfExtendingClass *object2 = [[SelfExtendingClass alloc] init]; + + /** CountClass tests. **/ + + /* Call an existing method. No +resolveInstanceMethod and no + forwarding should be triggered. */ + [object existingInstanceMethod]; + + if (resolveInstanceMethodCount != 0) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Call a non-existing method. Both +resolveInstanceMethod and the + forwarding should be triggered. */ + [object nonExistingInstanceMethod]; + + if (resolveInstanceMethodCount != 1) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Now try the same tests with class_getInstanceMethod(), which + should trigger the resolve methods too, but not the + forwarding. */ + m = class_getInstanceMethod (objc_getClass ("CountClass"), + @selector (existingInstanceMethod)); + + if (resolveInstanceMethodCount != 1) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + m = class_getInstanceMethod (objc_getClass ("CountClass"), + @selector (nonExistingInstanceMethod)); + + if (resolveInstanceMethodCount != 2) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Now try the same tests with class_getMethodImplementation(), + which should trigger the resolve methods and the + forwarding. */ + i = class_getMethodImplementation (objc_getClass ("CountClass"), + @selector (existingInstanceMethod)); + if (resolveInstanceMethodCount != 2) + abort (); + + if (forwardingCount != 1) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + i = class_getMethodImplementation (objc_getClass ("CountClass"), + @selector (nonExistingInstanceMethod)); + if (resolveInstanceMethodCount != 3) + abort (); + + if (forwardingCount != 2) + abort (); + + if (nonExistingMethodCount != 1) + abort (); + + /* Reset the counters for the next test. */ + resolveInstanceMethodCount = 0; + forwardingCount = 0; + nonExistingMethodCount = 0; + + + /** SelfExtendingClass tests. **/ + + /* Try the direct countHits method first. No resolving or + forwarding should be triggered. */ + if ([SelfExtendingClass countHits] != 1) + abort (); + + if (resolveInstanceMethodCount != 0) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Now, try calling a non-existing count method; it should be + installed and invoked. */ + if ([object2 nonExistingCountHitsMethod] != 2) + abort (); + + if (resolveInstanceMethodCount != 1) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([object2 nonExistingCountHitsMethod] != 3) + abort (); + + if (resolveInstanceMethodCount != 1) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Now try the same tests with class_getInstanceMethod(). */ + m = class_getInstanceMethod (objc_getClass ("SelfExtendingClass"), + @selector (nonExistingCountHitsMethod2)); + if (resolveInstanceMethodCount != 2) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([object2 nonExistingCountHitsMethod2] != 4) + abort (); + + if (resolveInstanceMethodCount != 2) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + + /* Now try the same tests with class_getMethodImplementation(). */ + i = class_getMethodImplementation (objc_getClass ("SelfExtendingClass"), + @selector (nonExistingCountHitsMethod3)); + if (resolveInstanceMethodCount != 3) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + + /* Try it again. The method has now been installed, so it should + be used and work, but with no additional resolving + involved. */ + if ([object2 nonExistingCountHitsMethod3] != 5) + abort (); + + if (resolveInstanceMethodCount != 3) + abort (); + + if (forwardingCount != 0) + abort (); + + if (nonExistingMethodCount != 0) + abort (); + } + + + return (0); +} Index: libobjc/sendmsg.c =================================================================== --- libobjc/sendmsg.c (revision 167709) +++ libobjc/sendmsg.c (working copy) @@ -138,6 +138,102 @@ __objc_get_forward_imp (id rcv, SEL sel) } } +/* Selectors for +resolveClassMethod: and +resolveInstanceMethod:. + These are set up at startup. */ +static SEL selector_resolveClassMethod = NULL; +static SEL selector_resolveInstanceMethod = NULL; + +/* Internal routines use to resolve a class method using + +resolveClassMethod:. 'class' is always a non-Nil class (*not* a + meta-class), and 'sel' is the selector that we are trying to + resolve. This must be called when class is not Nil, and the + dispatch table for class methods has already been installed. + + This routine tries to call +resolveClassMethod: to give an + opportunity to resolve the method. If +resolveClassMethod: returns + YES, it tries looking up the method again, and if found, it returns + it. Else, it returns NULL. */ +static inline +IMP +__objc_resolve_class_method (Class class, SEL sel) +{ + /* We need to lookup +resolveClassMethod:. */ + BOOL (*resolveMethodIMP) (id, SEL, SEL); + + /* The dispatch table for class methods is already installed and we + don't want any forwarding to happen when looking up this method, + so we just look it up directly. Note that if 'sel' is precisely + +resolveClassMethod:, this would look it up yet again and find + nothing. That's no problem and there's no recursion. */ + resolveMethodIMP = (BOOL (*) (id, SEL, SEL))sarray_get_safe + (class->class_pointer->dtable, (size_t) selector_resolveClassMethod->sel_id); + + if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveClassMethod, sel)) + { + /* +resolveClassMethod: returned YES. Look the method up again. + We already know the dtable is installed. */ + + /* TODO: There is the case where +resolveClassMethod: is buggy + and returned YES without actually adding the method. We + could maybe print an error message. */ + return sarray_get_safe (class->class_pointer->dtable, (size_t) sel->sel_id); + } + + return NULL; +} + +/* Internal routines use to resolve a instance method using + +resolveInstanceMethod:. 'class' is always a non-Nil class, and + 'sel' is the selector that we are trying to resolve. This must be + called when class is not Nil, and the dispatch table for instance + methods has already been installed. + + This routine tries to call +resolveInstanceMethod: to give an + opportunity to resolve the method. If +resolveInstanceMethod: + returns YES, it tries looking up the method again, and if found, it + returns it. Else, it returns NULL. */ +static inline +IMP +__objc_resolve_instance_method (Class class, SEL sel) +{ + /* We need to lookup +resolveInstanceMethod:. */ + BOOL (*resolveMethodIMP) (id, SEL, SEL); + + /* The dispatch table for class methods may not be already installed + so we have to install it if needed. */ + resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable, + (size_t) selector_resolveInstanceMethod->sel_id); + if (resolveMethodIMP == 0) + { + /* Try again after installing the dtable. */ + if (class->class_pointer->dtable == __objc_uninstalled_dtable) + { + objc_mutex_lock (__objc_runtime_mutex); + if (class->class_pointer->dtable == __objc_uninstalled_dtable) + __objc_install_dispatch_table_for_class (class->class_pointer); + objc_mutex_unlock (__objc_runtime_mutex); + } + resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable, + (size_t) selector_resolveInstanceMethod->sel_id); + } + + if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveInstanceMethod, sel)) + { + /* +resolveInstanceMethod: returned YES. Look the method up + again. We already know the dtable is installed. */ + + /* TODO: There is the case where +resolveInstanceMethod: is + buggy and returned YES without actually adding the method. + We could maybe print an error message. */ + return sarray_get_safe (class->dtable, (size_t) sel->sel_id); + } + + return NULL; +} + +/* Temporary definition until we include objc/runtime.h. */ +objc_EXPORT Class objc_lookupClass (const char *name); + /* Given a class and selector, return the selector's implementation. */ inline IMP @@ -188,14 +284,35 @@ get_imp (Class class, SEL sel) { /* The dispatch table has been installed, and the method is not in the dispatch table. So the method just - doesn't exist for the class. Return the forwarding - implementation. We don't know the receiver (only its - class), so we have to pass 'nil' as the first - argument. Passing the class as first argument is - wrong because the class is not the receiver; it can - result in us calling a class method when we want an - instance method of the same name. */ - res = __objc_get_forward_imp (nil, sel); + doesn't exist for the class. */ + + /* Try going through the +resolveClassMethod: or + +resolveInstanceMethod: process. */ + if (CLS_ISMETA (class)) + { + /* We have the meta class, but we need to invoke the + +resolveClassMethod: method on the class. So, we + need to obtain the class from the meta class, + which we do using the fact that both the class + and the meta-class have the same name. */ + Class realClass = objc_lookupClass (class->name); + if (realClass) + res = __objc_resolve_class_method (realClass, sel); + } + else + res = __objc_resolve_instance_method (class, sel); + + if (res == 0) + { + /* If that fails, then return the forwarding + implementation. We don't know the receiver (only its + class), so we have to pass 'nil' as the first + argument. Passing the class as first argument is + wrong because the class is not the receiver; it can + result in us calling a class method when we want an + instance method of the same name. */ + res = __objc_get_forward_imp (nil, sel); + } } } } @@ -304,9 +421,20 @@ objc_msg_lookup (id receiver, SEL op) (sidx)op->sel_id); if (result == 0) { - /* If the method still just doesn't exist for the - class, attempt to forward the method. */ - result = __objc_get_forward_imp (receiver, op); + /* Try going through the +resolveClassMethod: or + +resolveInstanceMethod: process. */ + if (CLS_ISMETA (receiver->class_pointer)) + result = __objc_resolve_class_method ((Class)receiver, op); + else + result = __objc_resolve_instance_method (receiver->class_pointer, + op); + + if (result == 0) + { + /* If the method still just doesn't exist for + the class, attempt to forward the method. */ + result = __objc_get_forward_imp (receiver, op); + } } } } @@ -343,6 +471,10 @@ void __objc_init_dispatch_tables () { __objc_uninstalled_dtable = sarray_new (200, 0); + + /* TODO: It would be cool to register typed selectors here. */ + selector_resolveClassMethod = sel_register_name ("resolveClassMethod:"); + selector_resolveInstanceMethod =sel_register_name ("resolveInstanceMethod:"); } /* This function is called by objc_msg_lookup when the @@ -566,20 +698,43 @@ class_get_class_method (MetaClass class, SEL op) struct objc_method * class_getInstanceMethod (Class class_, SEL selector) { + struct objc_method *m; + if (class_ == Nil || selector == NULL) return NULL; - return search_for_method_in_hierarchy (class_, selector); + m = search_for_method_in_hierarchy (class_, selector); + if (m) + return m; + + /* Try going through +resolveInstanceMethod:, and do + the search again if successful. */ + if (__objc_resolve_instance_method (class_, selector)) + return search_for_method_in_hierarchy (class_, selector); + + return NULL; } struct objc_method * class_getClassMethod (Class class_, SEL selector) { + struct objc_method *m; + if (class_ == Nil || selector == NULL) return NULL; - return search_for_method_in_hierarchy (class_->class_pointer, - selector); + m = search_for_method_in_hierarchy (class_->class_pointer, + selector); + if (m) + return m; + + /* Try going through +resolveClassMethod:, and do the search again + if successful. */ + if (__objc_resolve_class_method (class_, selector)) + return search_for_method_in_hierarchy (class_->class_pointer, + selector); + + return NULL; } BOOL Index: libobjc/ChangeLog =================================================================== --- libobjc/ChangeLog (revision 167710) +++ libobjc/ChangeLog (working copy) @@ -1,5 +1,21 @@ 2010-12-11 Nicola Pero + * sendmsg.c (selector_resolveClassMethod): New. + (selector_resolveInstanceMethod): New. + (__objc_resolve_class_method): New. + (__objc_resolve_instance_method): New. + (get_imp): Call __objc_resolve_class_method or + __objc_resolve_instance_method at the appropriate time. + (objc_msg_lookup): Same. + (class_getClassMethod): Same. + (class_getInstanceMethod): Same. + (__objc_init_dispatch_tables): Initialize + selector_resolveClassMethod and selector_resolveInstanceMethod. + * objc/runtime.h: Updated documentation of class_getClassMethod, + class_getInstanceMethod and class_getMethodImplementation. + +2010-12-11 Nicola Pero + * objc-private/module-abi-8.h (struct objc_symtab): Updated description of sel_ref_cnt and refs. * objc/deprecated/struct_objc_symtab.h (objc_symtab): Same change. Index: libobjc/objc/runtime.h =================================================================== --- libobjc/objc/runtime.h (revision 167709) +++ libobjc/objc/runtime.h (working copy) @@ -590,13 +590,17 @@ objc_disposeClassPair (Class class_); /* Return the instance method with selector 'selector' of class 'class_', or NULL if the class (or one of its superclasses) does not implement the method. Return NULL if class_ is Nil or selector - is NULL. */ + is NULL. Calling this function may trigger a call to + +resolveInstanceMethod:, but does not return a forwarding + function. */ objc_EXPORT Method class_getInstanceMethod (Class class_, SEL selector); /* Return the class method with selector 'selector' of class 'class_', or NULL if the class (or one of its superclasses) does not implement the method. Return NULL if class_ is Nil or selector is - NULL. */ + NULL. Calling this function may trigger a call to + +resolveClassMethod:, but does not return a forwarding + function. */ objc_EXPORT Method class_getClassMethod (Class class_, SEL selector); /* Return the IMP (pointer to the function implementing a method) for @@ -607,7 +611,15 @@ objc_EXPORT Method class_getClassMethod (Class cla arguments and return type before calling it. To get a class method, you can pass the meta-class as the class_ argument (ie, use class_getMethodImplementation (object_getClass (class_), - selector)). Return NULL if class_ is Nil or selector is NULL. */ + selector)). Return NULL if class_ is Nil or selector is NULL. + This function first looks for an existing method; if it is not + found, it calls +resolveClassMethod: or +resolveInstanceMethod: + (depending on whether a class or instance method is being looked + up) if it is implemented. If the method returns YES, then it tries + the look up again (the assumption being that +resolveClassMethod: + or resolveInstanceMethod: will add the method using + class_addMethod()). If it is still not found, it returns a + forwarding function. */ objc_EXPORT IMP class_getMethodImplementation (Class class_, SEL selector); /* Compatibility Note: the Apple/NeXT runtime has the function