diff options
author | nicola <nicola@138bc75d-0d04-0410-961f-82ee72b054a4> | 2010-12-11 19:43:21 +0000 |
---|---|---|
committer | nicola <nicola@138bc75d-0d04-0410-961f-82ee72b054a4> | 2010-12-11 19:43:21 +0000 |
commit | 101978acaa779540c14c366687226ade41c471f7 (patch) | |
tree | c50ecdee614fbdc518483d8012362f18adfbe922 /gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm | |
parent | 609b9937816df81c4bf275b5d470fa1bc7fb75e5 (diff) | |
download | gcc-101978acaa779540c14c366687226ade41c471f7.tar.gz |
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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@167712 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm')
-rw-r--r-- | gcc/testsuite/obj-c++.dg/gnu-api-2-resolve-method.mm | 563 |
1 files changed, 563 insertions, 0 deletions
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); +} |