1/* Test the Modern GNU Objective-C Runtime API.
2
3  This is test 'objc', covering all functions starting with 'objc'.  */
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@end
42
43@implementation MySubClass
44- (void) setVariable: (id)value { variable_ivar = value; }
45- (id) variable { return variable_ivar; }
46@end
47
48/* Hack to calculate the log2 of a byte alignment.  */
49unsigned char
50log_2_of (unsigned int x)
51{
52  unsigned char result = 0;
53
54  /* We count how many times we need to divide by 2 before we reach 1.
55     This algorithm is good enough for the small numbers (such as 8,
56     16 or 64) that we have to deal with.  */
57  while (x > 1)
58    {
59      x = x / 2;
60      result++;
61    }
62
63  return result;
64}
65
66int main ()
67{
68  /* Functions are tested in alphabetical order.  */
69
70  std::cout << "Testing objc_allocateClassPair ()...\n";
71  {
72    Class new_root_class = objc_allocateClassPair (Nil, "MyNewRootClass", 0);
73    Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MyNewSubClass", 0);
74
75    /* A new root class would obviously need at least an 'isa'
76       instance variable.  */
77    class_addIvar (new_root_class, "isa", sizeof (Class), log_2_of (__alignof__ (Class)),
78		   @encode (Class));
79
80    objc_registerClassPair (new_root_class);
81    objc_registerClassPair (new_class);
82
83    if (std::strcmp (class_getName (new_class), "MyNewSubClass") != 0)
84      abort ();
85
86    if (class_getSuperclass (new_class) != objc_getClass ("MyRootClass"))
87      abort ();
88
89    if (std::strcmp (class_getName (new_root_class), "MyNewRootClass") != 0)
90      abort ();
91
92    if (class_getSuperclass (new_root_class) != Nil)
93      abort ();
94
95    {
96      MySubClass *o = [[(Class)objc_getClass ("MyNewSubClass") alloc] init];
97
98      if (object_getClass (o) != objc_getClass ("MyNewSubClass"))
99	abort ();
100    }
101  }
102
103  std::cout << "Testing objc_copyProtocolList ()...\n";
104  {
105    /* Make sure both our two protocols are known to the runtime.  */
106    id my_protocol = @protocol (MyProtocol);
107    id my_second_protocol = @protocol (MySecondProtocol);
108    unsigned int count;
109    Protocol ** list = objc_copyProtocolList (&count);
110
111    if (count != 2)
112      abort ();
113
114    if (! ((std::strcmp (protocol_getName (list[0]), "MyProtocol") == 0
115	    && std::strcmp (protocol_getName (list[1]), "MySecondProtocol") == 0)
116	   || (std::strcmp (protocol_getName (list[0]), "MySecondProtocol") == 0
117	       && std::strcmp (protocol_getName (list[1]), "MyProtocol") == 0)))
118      abort ();
119
120    if (list[2] != NULL)
121      abort ();
122  }
123
124  std::cout << "Testing objc_disposeClassPair ()...\n";
125  {
126    Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), @selector (setVariable:));
127    Class new_class = objc_allocateClassPair (objc_getClass ("MyRootClass"), "MyNewSubClass2", 0);
128
129    if (new_class == Nil)
130      abort ();
131
132    /* Add a bit of everything to the class to exercise undoing all these changes.  */
133
134    /* Instance variable.  */
135    class_addIvar (new_class, "my_variable", sizeof (float), log_2_of (__alignof__ (float)), @encode (float));
136
137    /* Instance method.  */
138    class_addMethod (new_class, @selector (setVariable:), method_getImplementation (method),
139		     method_getTypeEncoding (method));
140
141    /* Class method.  */
142    class_addMethod (object_getClass (new_class), @selector (setVariable:), method_getImplementation (method),
143		     method_getTypeEncoding (method));
144
145    /* Protocol.  */
146    class_addProtocol (new_class, @protocol (MyProtocol));
147
148    objc_disposeClassPair (new_class);
149  }
150
151  /* This function currently does not exist with the GNU runtime.  */
152  /* std::cout << "Testing objc_duplicateClass ()...\n"; */
153
154  /* TODO - Test it when implemented in the GNU Runtime */
155  /*  std::cout << "Testing objc_getAssociatedObject ()...\n";  */
156
157  std::cout << "Testing objc_getClass ()...\n";
158  {
159    if (std::strcmp (class_getName (objc_getClass ("MySubClass")),
160			"MySubClass") != 0)
161      abort ();
162  }
163
164  std::cout << "Testing objc_getClassList ()...\n";
165  {
166    Class *list;
167    int i, count, other_count;
168    count = objc_getClassList (NULL, 0);
169
170    /* count most likely will be 5, (MyRootClass, MySubClass,
171       Protocol, Object, NXConstantString).  */
172    if (count < 3)
173      abort ();
174
175    list = (Class *)(malloc (sizeof (Class) * count));
176    other_count = objc_getClassList (list, count);
177
178    if (other_count != count)
179      abort ();
180
181    /* Spot-check: search for class 'MyRootClass' in the list.  */
182    for (i = 0; i < count; i++)
183      {
184	if (std::strcmp (class_getName (list[i]), "MyRootClass") == 0)
185	  break;
186      }
187    if (i == count)
188      abort ();
189
190    /* Spot-check: search for class 'MySubClass' in the list.  */
191    for (i = 0; i < count; i++)
192      {
193	if (std::strcmp (class_getName (list[i]), "MySubClass") == 0)
194	  break;
195      }
196    if (i == count)
197      abort ();
198
199    /* Spot-check: search for class 'Protocol' in the list.  */
200    for (i = 0; i < count; i++)
201      {
202	if (std::strcmp (class_getName (list[i]), "Protocol") == 0)
203	  break;
204      }
205    if (i == count)
206      abort ();
207  }
208
209  /* This function does not exist with the GNU runtime.  */
210  /* std::cout << "Testing objc_getFutureClass ()...\n"; */
211
212  std::cout << "Testing objc_getMetaClass ()...\n";
213  {
214    if (! class_isMetaClass (objc_getMetaClass ("MyRootClass")))
215      abort ();
216  }
217
218  std::cout << "Testing objc_getProtocol ()...\n";
219  {
220    if (! protocol_isEqual (objc_getProtocol ("MyProtocol"), @protocol (MyProtocol)))
221      abort ();
222  }
223
224  std::cout << "Testing objc_getRequiredClass ()...\n";
225  {
226    if (std::strcmp (class_getName (objc_getRequiredClass ("MyRootClass")),
227			"MyRootClass") != 0)
228      abort ();
229  }
230
231  std::cout << "Testing objc_lookUpClass ()...\n";
232  {
233    if (std::strcmp (class_getName (objc_lookUpClass ("MyRootClass")),
234			"MyRootClass") != 0)
235      abort ();
236  }
237
238  /* This function does not exist with the GNU runtime.  */
239  /* std::cout << "Testing objc_setFutureClass ()...\n"; */
240
241  std::cout << "Testing objc_registerClassPair ()...\n";
242  {
243    Class new_class = objc_allocateClassPair (objc_getClass ("MySubClass"), "MySubSubClass", 0);
244
245    class_addProtocol (new_class, @protocol (MySecondProtocol));
246
247    objc_registerClassPair (new_class);
248
249    if (std::strcmp (class_getName (new_class), "MySubSubClass") != 0)
250      abort ();
251
252    if (class_getSuperclass (new_class) != objc_getClass ("MySubClass"))
253      abort ();
254
255    if (! class_conformsToProtocol (new_class, @protocol (MySecondProtocol)))
256      abort ();
257  }
258
259  /* TODO - Test it when implemented in the GNU Runtime */
260  /*  std::cout << "Testing objc_removeAssociatedObjects ()...\n";  */
261
262  /* TODO - Test it when implemented in the GNU Runtime */
263  /*  std::cout << "Testing objc_setAssociatedObject ()...\n";  */
264
265  return (0);
266}
267