| /* Test the Modern GNU Objective-C Runtime API. |
| |
| This is test 'method', covering all functions starting with 'method'. */ |
| |
| /* { dg-do run } */ |
| /* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */ |
| /* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ |
| // { dg-additional-options "-Wno-objc-root-class" } |
| |
| /* 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; |
| + initialize; |
| @end |
| |
| @implementation MyRootClass |
| + alloc { return class_createInstance (self, 0); } |
| - init { return self; } |
| + initialize { return self; } |
| @end |
| |
| @protocol MyProtocol |
| - (id) variable; |
| @end |
| |
| @protocol MySecondProtocol |
| - (id) setVariable: (id)value; |
| @end |
| |
| @interface MySubClass : MyRootClass <MyProtocol> |
| { id variable_ivar; } |
| - (void) setVariable: (id)value; |
| - (id) variable; |
| - (id) constant; |
| @end |
| |
| @implementation MySubClass |
| - (void) setVariable: (id)value { variable_ivar = value; } |
| - (id) variable { return variable_ivar; } |
| - (id) constant { return nil; } |
| @end |
| |
| |
| int main () |
| { |
| /* Functions are tested in alphabetical order. */ |
| |
| std::cout <<"Testing method_copyArgumentType () ...\n"; |
| { |
| Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (setVariable:)); |
| char *type = method_copyArgumentType (method, 2); |
| |
| if (type == NULL || type[0] != '@') |
| abort (); |
| } |
| |
| std::cout << "Testing method_copyReturnType () ...\n"; |
| { |
| Method method = class_getClassMethod (objc_getClass ("MyRootClass"), |
| @selector (alloc)); |
| char *type = method_copyReturnType (method); |
| |
| /* Check that it returns an object. */ |
| if (type == NULL || type[0] != '@') |
| abort (); |
| } |
| |
| std::cout << "Testing method_exchangeImplementations () ...\n"; |
| { |
| Method method_a = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (variable)); |
| Method method_b = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (constant)); |
| MySubClass *object = [[MySubClass alloc] init]; |
| |
| /* Check that things work as expected before the swap. */ |
| [object setVariable: object]; |
| |
| if ([object variable] != object || [object constant] != nil) |
| abort (); |
| |
| /* Swap the methods. */ |
| method_exchangeImplementations (method_a, method_b); |
| |
| /* Check that behavior has changed. */ |
| if ([object variable] != nil || [object constant] != object) |
| abort (); |
| |
| /* Swap the methods again. */ |
| method_exchangeImplementations (method_a, method_b); |
| |
| /* Check that behavior is back to normal. */ |
| if ([object variable] != object || [object constant] != nil) |
| abort (); |
| } |
| |
| std::cout << "Testing method_getArgumentType () ...\n"; |
| { |
| Method method = class_getInstanceMethod (objc_getClass ("MyRootClass"), |
| @selector (init)); |
| char type[16]; |
| |
| method_getArgumentType (method, 1, type, 16); |
| |
| /* Check the second argument (_cmd), which should be a SEL. */ |
| if (type[0] != ':') |
| abort (); |
| } |
| |
| std::cout << "Testing method_getDescription () ...\n"; |
| { |
| Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (variable)); |
| struct objc_method_description *description = method_getDescription (method); |
| |
| if (std::strcmp (sel_getName (description->name), "variable") != 0) |
| abort (); |
| |
| if (method_getDescription (NULL) != NULL) |
| abort (); |
| } |
| |
| std::cout << "Testing method_getImplementation () ...\n"; |
| { |
| typedef void (*set_variable_function) (id receiver, SEL _cmd, id variable); |
| Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (setVariable:)); |
| set_variable_function imp; |
| MySubClass *object = [[MySubClass alloc] init]; |
| |
| imp = (set_variable_function)(method_getImplementation (method)); |
| |
| (*imp)(object, @selector (setVariable:), object); |
| |
| if ([object variable] != object) |
| abort (); |
| } |
| |
| std::cout << "Testing method_getName () ...\n"; |
| { |
| Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (setVariable:)); |
| if (std::strcmp (sel_getName (method_getName (method)), "setVariable:") != 0) |
| abort (); |
| } |
| |
| std::cout << "Testing method_getNumberOfArguments () ...\n"; |
| { |
| Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (setVariable:)); |
| if (method_getNumberOfArguments (method) != 3) |
| abort (); |
| |
| method = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (variable)); |
| if (method_getNumberOfArguments (method) != 2) |
| abort (); |
| } |
| |
| std::cout << "Testing method_getTypeEncoding () ...\n"; |
| { |
| Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (setVariable:)); |
| const char *types = method_getTypeEncoding (method); |
| |
| /* Check that method type string starts with 'v' (void) */ |
| if (types == NULL || types[0] != 'v') |
| abort (); |
| } |
| |
| std::cout << "Testing method_getReturnType () ...\n"; |
| { |
| Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (setVariable:)); |
| char type[16]; |
| |
| method_getReturnType (method, type, 16); |
| |
| if (type[0] != 'v') |
| abort (); |
| |
| method_getReturnType (NULL, type, 16); |
| |
| if (type[0] != 0) |
| abort (); |
| } |
| |
| std::cout << "Testing method_setImplementation () ...\n"; |
| { |
| Method method_a = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (variable)); |
| Method method_b = class_getInstanceMethod (objc_getClass ("MySubClass"), |
| @selector (constant)); |
| IMP original_imp_a = method_getImplementation (method_a); |
| IMP original_imp_b = method_getImplementation (method_b); |
| MySubClass *object = [[MySubClass alloc] init]; |
| |
| /* Check that things work as expected before the swap. */ |
| [object setVariable: object]; |
| |
| if ([object variable] != object || [object constant] != nil) |
| abort (); |
| |
| /* Have 'variable' use the same implementation as 'constant'. */ |
| if (method_setImplementation (method_a, original_imp_b) != original_imp_a) |
| abort (); |
| |
| /* Check that behavior has changed. */ |
| if ([object variable] != nil || [object constant] != nil) |
| abort (); |
| |
| /* Put the original method back. */ |
| if (method_setImplementation (method_a, original_imp_a) != original_imp_b) |
| abort (); |
| |
| /* Check that behavior is back to normal. */ |
| if ([object variable] != object || [object constant] != nil) |
| abort (); |
| } |
| |
| return (0); |
| } |