In libobjc/: 2010-12-11 Nicola Pero <nicola.pero@meta-innovation.com>
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. From-SVN: r167712
This commit is contained in:
parent
375d1239a4
commit
e97cfd9707
6 changed files with 1331 additions and 17 deletions
|
@ -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.
|
||||
|
|
563
gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm
Normal file
563
gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm
Normal file
|
@ -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);
|
||||
}
|
563
gcc/testsuite/objc.dg/gnu-api-2-resolve-method.m
Normal file
563
gcc/testsuite/objc.dg/gnu-api-2-resolve-method.m
Normal file
|
@ -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;
|
||||
}
|
|
@ -1,3 +1,19 @@
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue