Patchwork libobjc: add missing support for +resolveClassMethod: and +resolveInstanceMethod: to the new API

login
register
mail settings
Submitter Nicola Pero
Date Dec. 11, 2010, 7:43 p.m.
Message ID <1292096639.866324878@192.168.2.227>
Download mbox | patch
Permalink /patch/75204/
State New
Headers show

Comments

Nicola Pero - Dec. 11, 2010, 7:43 p.m.
While looking at testcases for the Apple/NeXT Objective-C runtime I noticed
that while implementing the new "Objective-C 2.0" API in libobjc I had missed
the new +resolveClassMethod: and +resolveInstanceMethod:.

This patch adds support for them.  Full testcases are included.

Committed to trunk.

Thanks

In libobjc/:
2010-12-11  Nicola Pero  <nicola.pero@meta-innovation.com>

        * 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.

In gcc/testsuite/:
2010-12-11  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc.dg/gnu-api-2-resolve-method.m: New.
        * obj-c++.dg/gnu-api-2-resolve-method.mm: New.

Patch

Index: gcc/testsuite/ChangeLog
===================================================================
--- gcc/testsuite/ChangeLog	(revision 167711)
+++ gcc/testsuite/ChangeLog	(working copy)
@@ -1,3 +1,8 @@ 
+2010-12-11  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc.dg/gnu-api-2-resolve-method.m: New.
+	* obj-c++.dg/gnu-api-2-resolve-method.mm: New.	
+
 2010-12-10 Ahmad Sharif <asharif@google.com>
 
 	* 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 <objc/runtime.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+@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) <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) <NonExistingStuff2>
+@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 <objc/runtime.h>
+#include <stdlib.h>
+#include <iostream>
+#include <cstring>
+
+@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) <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) <NonExistingStuff2>
+@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  <nicola.pero@meta-innovation.com>
 
+	* 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  <nicola.pero@meta-innovation.com>
+
 	* 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