summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm563
-rw-r--r--gcc/testsuite/objc.dg/gnu-api-2-resolve-method.m563
-rw-r--r--libobjc/ChangeLog16
-rw-r--r--libobjc/objc/runtime.h18
-rw-r--r--libobjc/sendmsg.c183
6 files changed, 1331 insertions, 17 deletions
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index a2eac8999ed..08baf3439a1 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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.
diff --git a/gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm b/gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm
new file mode 100644
index 00000000000..09f21b9ec63
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm
@@ -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);
+}
diff --git a/gcc/testsuite/objc.dg/gnu-api-2-resolve-method.m b/gcc/testsuite/objc.dg/gnu-api-2-resolve-method.m
new file mode 100644
index 00000000000..767dc73cf33
--- /dev/null
+++ b/gcc/testsuite/objc.dg/gnu-api-2-resolve-method.m
@@ -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;
+}
diff --git a/libobjc/ChangeLog b/libobjc/ChangeLog
index bc522133feb..fa609a58f86 100644
--- a/libobjc/ChangeLog
+++ b/libobjc/ChangeLog
@@ -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.
diff --git a/libobjc/objc/runtime.h b/libobjc/objc/runtime.h
index b37eee2125b..f5df691fe52 100644
--- a/libobjc/objc/runtime.h
+++ b/libobjc/objc/runtime.h
@@ -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 class_, SEL selector);
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
diff --git a/libobjc/sendmsg.c b/libobjc/sendmsg.c
index 7f7024c275a..4748695938d 100644
--- a/libobjc/sendmsg.c
+++ b/libobjc/sendmsg.c
@@ -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
OpenPOWER on IntegriCloud