1/* Test the Modern GNU Objective-C Runtime API.
2
3  This is test 'class-meta', covering calling functions starting with
4  'class' but using a meta class as argument.
5
6  Functions that manipulate methods (adding, replacing methods)
7  usually take a meta class argument to manipulate the class methods
8  instead of the instance ones.  This is an important part of the API
9  that needs testing.
10
11  Functions that manipulate instances, instance variables, properties
12  and protocols at the moment must take a normal class as argument;
13  calling them with a meta class as argument is of no particular use
14  and generally produces a behavior that is undocumented and/or
15  undefined (and this is true with all runtimes).  As in the future
16  this behavior may be defined or documented (for example, if class
17  variables are implemented as instance variables of meta classes) we
18  avoid testing it for compatibility with future runtimes.  */
19
20/* { dg-do run } */
21/* { dg-skip-if "No API#2 pre-Darwin9" { *-*-darwin[5-8]* } { "-fnext-runtime" } { "" } } */
22/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
23/* { dg-additional-options "-DOBJC_OLD_DISPATCH_PROTOTYPES" { target { *-*-darwin* } } } */
24// { dg-additional-options "-Wno-objc-root-class" }
25
26/* To get the modern GNU Objective-C Runtime API, you include
27   objc/runtime.h.  */
28#include <objc/runtime.h>
29#include <stdlib.h>
30#include <iostream>
31#include <cstring>
32
33@interface MyRootClass
34{ Class isa; }
35+ alloc;
36- init;
37+ initialize;
38@end
39
40@implementation MyRootClass
41+ alloc { return class_createInstance (self, 0); }
42- init  { return self; }
43+ initialize { return self; }
44@end
45
46static id static_variable = nil;
47
48@interface MySubClass : MyRootClass
49+ (void) setVariable: (id)value;
50+ (id) variable;
51@end
52
53@implementation MySubClass
54+ (void) setVariable: (id)value { static_variable = value; }
55+ (id) variable { return static_variable; }
56@end
57
58@interface DifferentClass : MyRootClass
59+ (id) myClass;
60@end
61
62@implementation DifferentClass
63+ (id) myClass { return self; }
64@end
65
66@interface MySubClass (MySelf)
67+ (id) mySelf;
68@end
69
70int main ()
71{
72  /* Functions are tested in alphabetical order.  */
73
74  /* Calling class_addIvar() with a meta class is not documented and
75     (currently) of no use.  */
76  /* std::cout << "Testing class_addIvar ()...\n"; */
77
78  std::cout << "Testing class_addMethod () on a meta class...\n";
79  {
80    /* Here we test adding methods to meta classes, ie, adding class methods.  */
81    Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass2", 0);
82    Method method1 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), @selector (setVariable:));
83    Method method2 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"), @selector (variable));
84
85    if (new_class == Nil)
86      abort ();
87
88    if (! class_addMethod (object_getClass (new_class), @selector (setVariable:), method_getImplementation (method1),
89			   method_getTypeEncoding (method1)))
90      abort ();
91
92    if (! class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2),
93			   method_getTypeEncoding (method2)))
94      abort ();
95
96    /* Test that if the method already exists in the class,
97       class_addMethod() returns NO.  */
98    if (class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2),
99			 method_getTypeEncoding (method2)))
100      abort ();
101
102    objc_registerClassPair (new_class);
103
104    /* Now, MySubClass2 is basically the same as MySubClass!  We'll
105       use the +variable and +setVariable: methods on it.  */
106    {
107      Class c = objc_getClass ("MySubClass2");
108      id o = [[MyRootClass alloc] init];
109
110      [c setVariable: o];
111
112      if ([c variable] != o)
113	abort ();
114    }
115
116    /* Now, try that if you take an existing class and try to add an
117       already existing method, class_addMethod returns NO.  This is
118       subtly different from before, when 'new_class' was still in
119       construction.  Now it's a real class and the libobjc internals
120       differ between the two cases.  */
121    if (class_addMethod (object_getClass (new_class), @selector (variable), method_getImplementation (method2),
122			 method_getTypeEncoding (method2)))
123      abort ();
124  }
125
126  /* Calling class_addProtocol() on a meta class is not documented and
127     (currently) of no use.  */
128  /* std::cout << "Testing class_addProtocol () on a meta class...\n"; */
129
130  /* Calling class_conformsToProtocol() on a meta class is not
131     documented and (currently) of no use.  */
132  /* std::cout << "Testing class_conformsToProtocol () on a meta class...\n"; */
133
134  /* Calling class_copyIvarList() on a meta class is not documented
135     and (currently) of no use.  */
136  /* std::cout << "Testing class_copyIvarList () on a meta class...\n"; */
137
138  std::cout << "Testing class_copyMethodList () on a meta class...\n";
139  {
140    /* Test that you can copy the method list of a meta class.  They
141       are the class methods of the class.  */
142    unsigned int count;
143    Method * list = class_copyMethodList (objc_getMetaClass ("MySubClass"), &count);
144
145    if (count != 2)
146      abort ();
147
148    if (! ((std::strcmp (sel_getName (method_getName (list[0])), "variable") == 0
149	    && std::strcmp (sel_getName (method_getName (list[1])), "setVariable:") == 0)
150	   || (std::strcmp (sel_getName (method_getName (list[0])), "setVariable:") == 0
151	       && std::strcmp (sel_getName (method_getName (list[1])), "variable") == 0)))
152      abort ();
153
154    if (list[2] != NULL)
155      abort ();
156  }
157
158  /* Calling class_copyPropertyList() on a meta class is not
159     documented and (currently) of no use.  */
160  /* std::cout << "Testing class_copyPropertyList () on a meta class...\n"; */
161
162  /* Calling class_copyProtocolList() on a meta class is not
163     documented and (currently) of no use.  */
164  /* std::cout << "Testing class_copyProtocolList () on a meta class...\n"; */
165
166  /* Calling class_createInstance() on a meta class is not documented
167     and (currently) of no use.  */
168  /* std::cout << "Testing class_createInstance () on a meta class...\n"; */
169
170  /* Calling class_getClassMethod () on a meta class is not documented
171     and (currently) of no use.  */
172  /* std::cout << "Testing class_getClassMethod () on a meta class...\n"; */
173
174  /* Calling class_getClassVariable () on a meta class is not
175     documented and (currently) of no use.  */
176  /* std::cout << "Testing class_getClassVariable () on a meta class ...\n"; */
177
178  std::cout << "Testing class_getInstanceMethod () on a meta class...\n";
179  {
180    /* The instance method of a meta class is the class method with
181       the same name of the class. */
182    Method method_1 = class_getInstanceMethod (objc_getMetaClass ("MySubClass"),
183					       @selector(variable));
184    Method method_2 = class_getClassMethod (objc_getClass ("MySubClass"),
185					    @selector(variable));
186
187    if (method_1 == NULL || method_2 == NULL)
188      abort ();
189
190    if (method_1 != method_2)
191      abort ();
192
193    if (std::strcmp (sel_getName (method_getName (method_1)), "variable") != 0)
194      abort ();
195  }
196
197  /* Calling class_getInstanceSize() with a meta class is not
198     documented and (currently) of no use.  */
199  /* std::cout << "Testing class_getInstanceSize () on a meta class...\n"; */
200
201  /* Calling class_getInstanceVariable() with a meta class is not
202     documented and (currently) of no use.  */
203  /* std::cout << "Testing class_getInstanceVariable () on a meta class...\n"; */
204
205  /* Calling class_getIvarLayout() with a meta class is not documented
206     and (currently) of no use.  */
207  /* std::cout << "Testing class_getIvarLayout () on a meta class...\n"; */
208
209  std::cout << "Testing class_getMethodImplementation () on a meta class...\n";
210  {
211    /* Getting the method implementation with a meta class returns a
212       class method.  */
213    MySubClass *object = [[MySubClass alloc] init];
214    IMP imp = class_getMethodImplementation (objc_getMetaClass ("MySubClass"),
215					     @selector(variable));
216
217    if (imp == NULL)
218      abort ();
219
220    [MySubClass setVariable: object];
221
222    if ((*imp)(objc_getClass ("MySubClass"), @selector(variable)) != object)
223      abort ();
224  }
225
226  /* This function does not exist with the GNU runtime.  */
227  /* std::cout << "Testing class_getMethodImplementation_stret () on a meta class...\n"; */
228
229  std::cout << "Testing class_getName () on a meta class...\n";
230  {
231    /* Traditionally, a meta class has the same name as the class.  */
232    if (std::strcmp (class_getName (objc_getMetaClass ("MyRootClass")),
233		     "MyRootClass") != 0)
234      abort ();
235  }
236
237  /* Calling class_getProperty() with a meta class is not documented
238     and (currently) of no use.  */
239  /* std::cout << "Testing class_getProperty ()...\n"; */
240
241  std::cout << "Testing class_getSuperclass () on a meta class...\n";
242  {
243    /* The superclass of a meta class is the meta class of the superclass.  */
244    if (class_getSuperclass (objc_getMetaClass ("MySubClass")) != objc_getMetaClass ("MyRootClass"))
245      abort ();
246
247    /* Test that it works on a newly created, but not registered, class.  */
248    {
249      Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MySubClass3", 0);
250
251      if (class_getSuperclass (object_getClass (new_class)) != object_getClass (objc_getClass ("MyRootClass")))
252	abort ();
253    }
254  }
255
256  /* Calling class_getVersion() with a meta class is not documented
257     and (currently) of no use.  */
258  /* std::cout << "Testing class_getVersion ()...\n"; */
259
260  /* Calling class_getWeakIvarLayout() with a meta class is not
261     documented and (currently) of no use.  */
262  /* std::cout << "Testing class_getWeakIvarLayout () on a meta class...\n"; */
263
264  /* class_isMetaClass() is already tested in gnu-api-2-class.m  */
265  /* std::cout << "Testing class_isMetaClass ()...\n"; */
266
267  std::cout << "Testing class_replaceMethod () on a meta class...\n";
268  {
269    /* We are going to replace the [MySubclass +variable] method with
270       the [DifferentClass +myClass] one.  */
271    Method new_method = class_getClassMethod (objc_getClass ("DifferentClass"),
272					      @selector (myClass));
273    Method old_method = class_getClassMethod (objc_getClass ("MySubClass"),
274					      @selector (variable));
275    const char *new_types = method_getTypeEncoding (new_method);
276    IMP new_imp = method_getImplementation (new_method);
277    const char *old_types = method_getTypeEncoding (old_method);
278    IMP old_imp = class_replaceMethod (objc_getMetaClass ("MySubClass"), @selector (variable),
279				       method_getImplementation (new_method),
280				       method_getTypeEncoding (new_method));
281    id o = [[MyRootClass alloc] init];
282
283    [MySubClass setVariable: o];
284
285    /* Try the new method implementation.  */
286    if ([MySubClass variable] != objc_getClass ("MySubClass"))
287      abort ();
288
289    /* Put the original method back.  */
290    class_replaceMethod (objc_getMetaClass ("MySubClass"), @selector (variable),
291			 old_imp, old_types);
292
293    /* Test it's back to what it was.  */
294    if ([MySubClass variable] != o)
295      abort ();
296
297    {
298      /* Finally, try adding a new method.  */
299      class_replaceMethod (objc_getMetaClass ("DifferentClass"), @selector (mySelf),
300			   new_imp, new_types);
301
302      if ([(Class)objc_getClass ("DifferentClass") mySelf] != objc_getClass ("DifferentClass"))
303	abort ();
304    }
305  }
306
307  std::cout << "Testing class_respondsToSelector () on a meta class...\n";
308  {
309    /* A meta class responds to a selector if and only if the class
310       responds to the corresponding class method.  */
311    if (! class_respondsToSelector (objc_getMetaClass ("MySubClass"), @selector(setVariable:)))
312      abort ();
313
314    if (class_respondsToSelector (objc_getMetaClass ("MyRootClass"), @selector(setVariable:)))
315      abort ();
316  }
317
318  /* This is not really implemented with the GNU runtime.  */
319  /* std::cout << "Testing class_setIvarLayout () on a meta class...\n"; */
320
321  /* Calling class_setVersion() with a meta class is not documented
322     and (currently) of no use.  */
323  /* std::cout << "Testing class_setVersion () on a meta class...\n"; */
324
325  /* This is not really implemented with the GNU runtime.  */
326  /* std::cout << "Testing class_setWeakIvarLayout () on a meta class...\n"; */
327
328return (0);
329}
330