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:
Nicola Pero 2010-12-11 19:43:21 +00:00 committed by Nicola Pero
parent 375d1239a4
commit e97cfd9707
6 changed files with 1331 additions and 17 deletions

View file

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

View 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);
}

View 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;
}

View file

@ -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

View file

@ -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

View file

@ -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