1/* Test the Modern GNU Objective-C Runtime API.
2
3  This is test 'method', covering all functions starting with 'method'.  */
4
5/* { dg-do run } */
6/* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */
7/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
8
9/* To get the modern GNU Objective-C Runtime API, you include
10   objc/runtime.h.  */
11#include <objc/runtime.h>
12#include <stdlib.h>
13#include <iostream>
14#include <cstring>
15
16@interface MyRootClass
17{ Class isa; }
18+ alloc;
19- init;
20+ initialize;
21@end
22
23@implementation MyRootClass
24+ alloc { return class_createInstance (self, 0); }
25- init  { return self; }
26+ initialize { return self; }
27@end
28
29@protocol MyProtocol
30- (id) variable;
31@end
32
33@protocol MySecondProtocol
34- (id) setVariable: (id)value;
35@end
36
37@interface MySubClass : MyRootClass <MyProtocol>
38{ id variable_ivar; }
39- (void) setVariable: (id)value;
40- (id) variable;
41- (id) constant;
42@end
43
44@implementation MySubClass
45- (void) setVariable: (id)value { variable_ivar = value; }
46- (id) variable { return variable_ivar; }
47- (id) constant { return nil; }
48@end
49
50
51int main ()
52{
53  /* Functions are tested in alphabetical order.  */
54
55  std::cout <<"Testing method_copyArgumentType () ...\n";
56  {
57    Method method = class_getInstanceMethod (objc_getClass ("MySubClass"),
58					     @selector (setVariable:));
59    char *type = method_copyArgumentType (method, 2);
60
61    if (type == NULL  ||  type[0] != '@')
62      abort ();
63  }
64
65  std::cout << "Testing method_copyReturnType () ...\n";
66  {
67    Method method = class_getClassMethod (objc_getClass ("MyRootClass"),
68					  @selector (alloc));
69    char *type = method_copyReturnType (method);
70
71    /* Check that it returns an object.  */
72    if (type == NULL  ||  type[0] != '@')
73      abort ();
74  }
75
76  std::cout << "Testing method_exchangeImplementations () ...\n";
77  {
78    Method method_a = class_getInstanceMethod (objc_getClass ("MySubClass"),
79					       @selector (variable));
80    Method method_b = class_getInstanceMethod (objc_getClass ("MySubClass"),
81					       @selector (constant));
82    MySubClass *object = [[MySubClass alloc] init];
83
84    /* Check that things work as expected before the swap.  */
85    [object setVariable: object];
86
87    if ([object variable] != object  ||  [object constant] != nil)
88      abort ();
89
90    /* Swap the methods.  */
91    method_exchangeImplementations (method_a, method_b);
92
93    /* Check that behavior has changed.  */
94    if ([object variable] != nil  ||  [object constant] != object)
95      abort ();
96
97    /* Swap the methods again.  */
98    method_exchangeImplementations (method_a, method_b);
99
100    /* Check that behavior is back to normal.  */
101    if ([object variable] != object  ||  [object constant] != nil)
102      abort ();
103  }
104
105  std::cout << "Testing method_getArgumentType () ...\n";
106  {
107    Method method = class_getInstanceMethod (objc_getClass ("MyRootClass"),
108					     @selector (init));
109    char type[16];
110
111    method_getArgumentType (method, 1, type, 16);
112
113    /* Check the second argument (_cmd), which should be a SEL.  */
114    if (type[0] != ':')
115      abort ();
116  }
117
118  std::cout << "Testing method_getDescription () ...\n";
119  {
120    Method method = class_getInstanceMethod (objc_getClass ("MySubClass"),
121					     @selector (variable));
122    struct objc_method_description *description = method_getDescription (method);
123
124    if (std::strcmp (sel_getName (description->name), "variable") != 0)
125      abort ();
126
127    if (method_getDescription (NULL) != NULL)
128      abort ();
129  }
130
131  std::cout << "Testing method_getImplementation () ...\n";
132  {
133    typedef void (*set_variable_function) (id receiver, SEL _cmd, id variable);
134    Method method = class_getInstanceMethod (objc_getClass ("MySubClass"),
135					     @selector (setVariable:));
136    set_variable_function imp;
137    MySubClass *object = [[MySubClass alloc] init];
138
139    imp = (set_variable_function)(method_getImplementation (method));
140
141    (*imp)(object, @selector (setVariable:), object);
142
143    if ([object variable] != object)
144      abort ();
145  }
146
147  std::cout << "Testing method_getName () ...\n";
148  {
149    Method method = class_getInstanceMethod (objc_getClass ("MySubClass"),
150					     @selector (setVariable:));
151    if (std::strcmp (sel_getName (method_getName (method)), "setVariable:") != 0)
152      abort ();
153  }
154
155  std::cout << "Testing method_getNumberOfArguments () ...\n";
156  {
157    Method method = class_getInstanceMethod (objc_getClass ("MySubClass"),
158					     @selector (setVariable:));
159    if (method_getNumberOfArguments (method) != 3)
160      abort ();
161
162    method = class_getInstanceMethod (objc_getClass ("MySubClass"),
163				      @selector (variable));
164    if (method_getNumberOfArguments (method) != 2)
165      abort ();
166  }
167
168  std::cout << "Testing method_getTypeEncoding () ...\n";
169  {
170    Method method = class_getInstanceMethod (objc_getClass ("MySubClass"),
171					     @selector (setVariable:));
172    const char *types = method_getTypeEncoding (method);
173
174    /* Check that method type string starts with 'v' (void)  */
175    if (types == NULL || types[0] != 'v')
176      abort ();
177  }
178
179  std::cout << "Testing method_getReturnType () ...\n";
180  {
181    Method method = class_getInstanceMethod (objc_getClass ("MySubClass"),
182					     @selector (setVariable:));
183    char type[16];
184
185    method_getReturnType (method, type, 16);
186
187    if (type[0] != 'v')
188      abort ();
189
190    method_getReturnType (NULL, type, 16);
191
192    if (type[0] != 0)
193      abort ();
194  }
195
196  std::cout << "Testing method_setImplementation () ...\n";
197  {
198    Method method_a = class_getInstanceMethod (objc_getClass ("MySubClass"),
199					       @selector (variable));
200    Method method_b = class_getInstanceMethod (objc_getClass ("MySubClass"),
201					       @selector (constant));
202    IMP original_imp_a = method_getImplementation (method_a);
203    IMP original_imp_b = method_getImplementation (method_b);
204    MySubClass *object = [[MySubClass alloc] init];
205
206    /* Check that things work as expected before the swap.  */
207    [object setVariable: object];
208
209    if ([object variable] != object  ||  [object constant] != nil)
210      abort ();
211
212    /* Have 'variable' use the same implementation as 'constant'.  */
213    if (method_setImplementation (method_a, original_imp_b) != original_imp_a)
214      abort ();
215
216    /* Check that behavior has changed.  */
217    if ([object variable] != nil  ||  [object constant] != nil)
218      abort ();
219
220    /* Put the original method back.  */
221    if (method_setImplementation (method_a, original_imp_a) != original_imp_b)
222      abort ();
223
224    /* Check that behavior is back to normal.  */
225    if ([object variable] != object  ||  [object constant] != nil)
226      abort ();
227  }
228
229  return (0);
230}
231