OSDN Git Service

gcc:
[pf3gnuchains/gcc-fork.git] / gcc / testsuite / objc.dg / gnu-api-2-resolve-method.m
1 /* Test the Modern GNU Objective-C Runtime API.
2
3    This is test 'resolve-method', covering +resolveClassMethod: and
4    +resolveInstanceMethod:.  */
5
6 /* { dg-do run } */
7 /* { dg-skip-if "" { *-*-* } { "-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 <stdio.h>
14 #include <string.h>
15
16 @interface MyRootClass
17 { Class isa; }
18 + alloc;
19 - init;
20 @end
21
22 @implementation MyRootClass
23 + alloc { return class_createInstance (self, 0); }
24 - init  { return self; }
25 @end
26
27
28 /* A number of tests will try invoking methods that don't exist.  We
29    want to record the fact, but not abort the program, so we supply
30    our own fowarding implementation which will invoke the following
31    function for any method that is not found.  */
32
33 /* Keep track of how many times a non-existing method was executed.  */
34 static int nonExistingMethodCount = 0;
35
36 /* Inspired by nil_method in libobjc.  */
37 id nonExisting_method (id receiver __attribute__ ((__unused__)),
38                        SEL sel __attribute__ ((__unused__)))
39 {
40   nonExistingMethodCount++;
41   return nil;
42 }
43
44 /* Keep track of how many times the forwarding lookup was invoked.  */
45 static int forwardingCount = 0;
46
47 /* We install this forwarding hook to cause all failed method lookups
48    to call our 'nonExisting_method' function.  */
49 IMP forward_everything_to_non_existing_method (id receiver __attribute__ ((__unused__)),
50                                                SEL sel __attribute__ ((__unused__)))
51 {
52   forwardingCount++;
53   return (IMP)nonExisting_method;
54 }
55
56
57 /* 'CountClass' is used to test that +resolveClassMethod: and
58    +resolveInstanceMethod: are called when expected.  They do nothing
59    other than recording that they are called.  */
60 @interface CountClass : MyRootClass
61 + (BOOL) resolveClassMethod: (SEL)selector;
62 + (BOOL) resolveInstanceMethod: (SEL)selector;
63 + (void) existingClassMethod;
64 - (void) existingInstanceMethod;
65 @end
66
67 /* Count how many times the methods are called for class
68    'CountClass'.  */
69 static int resolveClassMethodCount = 0;
70 static int resolveInstanceMethodCount = 0;
71
72 @implementation CountClass : MyRootClass
73 + (BOOL) resolveClassMethod: (SEL)selector
74 {
75   resolveClassMethodCount++;
76   return NO;
77 }
78 + (BOOL) resolveInstanceMethod: (SEL)selector
79 {
80   resolveInstanceMethodCount++;
81   return NO;
82 }
83 + (void) existingClassMethod
84 {
85   return;
86 }
87 - (void) existingInstanceMethod
88 {
89   return;
90 }
91 @end
92
93 @protocol NonExistingStuff
94 + (void) nonExistingClassMethod;
95 - (void) nonExistingInstanceMethod;
96 @end
97
98 /* Declare a category with some non existing methods, but don't
99    actually implement them.  */
100 @interface CountClass (NonExistingStuff) <NonExistingStuff>
101 @end
102
103
104 /* 'SelfExtendingClass' is used to test that +resolveClassMethod: and
105    +resolveInstanceMethod: can extend the class.  Any time they are
106    called, they install the requested method, mapping it to the same
107    implementation as 'countHits'.  */
108 @interface SelfExtendingClass : MyRootClass
109 + (int) countHits;
110 + (BOOL) resolveClassMethod: (SEL)selector;
111 + (BOOL) resolveInstanceMethod: (SEL)selector;
112 @end
113
114 /* How many times the countHits method (or a clone) was called.  */
115 static int hitCount = 0;
116
117 @implementation SelfExtendingClass : MyRootClass
118 + (int) countHits
119 {
120   hitCount++;
121   return hitCount;
122 }
123 + (BOOL) resolveClassMethod: (SEL)selector
124 {
125   /* Duplicate the 'countHits' method into the new method.  */
126   Method method = class_getClassMethod (self, @selector (countHits));
127   class_addMethod (object_getClass (self), selector,
128                    method_getImplementation (method),
129                    method_getTypeEncoding (method));
130   resolveClassMethodCount++;
131   return YES;
132 }
133 + (BOOL) resolveInstanceMethod: (SEL)selector
134 {
135   /* Duplicate the 'countHits' method into the new method.  */
136   Method method = class_getClassMethod (self, @selector (countHits));
137   class_addMethod (self, selector,
138                    method_getImplementation (method),
139                    method_getTypeEncoding (method));
140   resolveInstanceMethodCount++;
141   return YES;
142
143 }
144 @end
145
146 @protocol NonExistingStuff2
147 + (int) nonExistingCountHitsMethod;
148 - (int) nonExistingCountHitsMethod;
149
150 + (int) nonExistingCountHitsMethod2;
151 - (int) nonExistingCountHitsMethod2;
152
153 + (int) nonExistingCountHitsMethod3;
154 - (int) nonExistingCountHitsMethod3;
155 @end
156
157 /* Declare a category with some non existing methods, but don't
158    actually implement them.  */
159 @interface SelfExtendingClass (NonExistingStuff) <NonExistingStuff2>
160 @end
161
162
163 int main (int argc, void **args)
164 {
165   /* Functions are tested in alphabetical order.  */
166
167   /* Install our test forwarding hook.  */
168   __objc_msg_forward2 = forward_everything_to_non_existing_method;
169
170   printf ("Testing [+resolveClassMethod:]...\n");
171   {
172     Method m;
173     IMP i;
174
175     /** CountClass tests.  **/
176
177     /* Call an existing method.  No +resolveClassMethod and no
178        forwarding should be triggered.  */
179     [CountClass existingClassMethod];
180
181     if (resolveClassMethodCount != 0)
182       abort ();
183
184     if (forwardingCount != 0)
185       abort ();
186
187     if (nonExistingMethodCount != 0)
188       abort ();
189
190     /* Call a non-existing method.  Both +resolveClassMethod and the
191        forwarding should be triggered.  */
192     [CountClass nonExistingClassMethod];
193
194     if (resolveClassMethodCount != 1)
195       abort ();
196
197     if (forwardingCount != 1)
198       abort ();
199
200     if (nonExistingMethodCount != 1)
201       abort ();
202
203     /* Now try the same tests with class_getClassMethod(), which
204        should trigger the resolve methods too, but not the
205        forwarding.  */
206     m = class_getClassMethod (objc_getClass ("CountClass"),
207                               @selector (existingClassMethod));
208     if (resolveClassMethodCount != 1)
209       abort ();
210
211     if (forwardingCount != 1)
212       abort ();
213     
214     if (nonExistingMethodCount != 1)
215       abort ();
216
217     m = class_getClassMethod (objc_getClass ("CountClass"),
218                               @selector (nonExistingClassMethod));
219     if (resolveClassMethodCount != 2)
220       abort ();
221
222     if (forwardingCount != 1)
223       abort ();
224     
225     if (nonExistingMethodCount != 1)
226       abort ();
227
228     /* Now try the same tests with class_getMethodImplementation(),
229        which should trigger the resolve methods and the forwarding
230        (but not execute the forwarding, obviously).  */
231     i = class_getMethodImplementation (object_getClass (objc_getClass ("CountClass")),
232                                        @selector (existingClassMethod));
233     if (resolveClassMethodCount != 2)
234       abort ();
235
236     if (forwardingCount != 1)
237       abort ();
238     
239     if (nonExistingMethodCount != 1)
240       abort ();
241
242     i = class_getMethodImplementation (object_getClass (objc_getClass ("CountClass")),
243                                        @selector (nonExistingClassMethod));
244     if (resolveClassMethodCount != 3)
245       abort ();
246     
247     if (forwardingCount != 2)
248       abort ();
249
250     if (nonExistingMethodCount != 1)
251       abort ();
252
253
254     /* Reset the counters for the next test.  */
255     resolveClassMethodCount = 0;
256     forwardingCount = 0;
257     nonExistingMethodCount = 0;
258
259
260     /** SelfExtendingClass tests.  **/
261
262     /* Try the direct countHits method first.  No resolving or
263        forwarding should be triggered.  */
264     if ([SelfExtendingClass countHits] != 1)
265       abort ();
266
267     if (resolveClassMethodCount != 0)
268       abort ();
269
270     if (forwardingCount != 0)
271       abort ();
272
273     if (nonExistingMethodCount != 0)
274       abort ();
275
276     /* Now, try calling a non-existing count method; it should be
277        installed and invoked.  */
278     if ([SelfExtendingClass nonExistingCountHitsMethod] != 2)
279       abort ();
280
281     if (resolveClassMethodCount != 1)
282       abort ();
283
284     if (forwardingCount != 0)
285       abort ();
286
287     if (nonExistingMethodCount != 0)
288       abort ();
289
290     /* Try it again.  The method has now been installed, so it should
291        be used and work, but with no additional resolving
292        involved.  */
293     if ([SelfExtendingClass nonExistingCountHitsMethod] != 3)
294       abort ();
295
296     if (resolveClassMethodCount != 1)
297       abort ();
298
299     if (forwardingCount != 0)
300       abort ();
301
302     if (nonExistingMethodCount != 0)
303       abort ();
304
305
306     /* Now try the same tests with class_getClassMethod().  */
307     m = class_getClassMethod (objc_getClass ("SelfExtendingClass"),
308                               @selector (nonExistingCountHitsMethod2));
309     if (resolveClassMethodCount != 2)
310       abort ();
311
312     if (forwardingCount != 0)
313       abort ();
314     
315     if (nonExistingMethodCount != 0)
316       abort ();
317
318     /* Try it again.  The method has now been installed, so it should
319        be used and work, but with no additional resolving
320        involved.  */
321     if ([SelfExtendingClass nonExistingCountHitsMethod2] != 4)
322       abort ();
323
324     if (resolveClassMethodCount != 2)
325       abort ();
326
327     if (forwardingCount != 0)
328       abort ();
329
330     if (nonExistingMethodCount != 0)
331       abort ();
332
333
334     /* Now try the same tests with class_getMethodImplementation().  */
335     i = class_getMethodImplementation (object_getClass (objc_getClass ("SelfExtendingClass")),
336                                        @selector (nonExistingCountHitsMethod3));
337     if (resolveClassMethodCount != 3)
338       abort ();
339
340     if (forwardingCount != 0)
341       abort ();
342     
343     if (nonExistingMethodCount != 0)
344       abort ();
345
346     /* Try it again.  The method has now been installed, so it should
347        be used and work, but with no additional resolving
348        involved.  */
349     if ([SelfExtendingClass nonExistingCountHitsMethod3] != 5)
350       abort ();
351
352     if (resolveClassMethodCount != 3)
353       abort ();
354
355     if (forwardingCount != 0)
356       abort ();
357
358     if (nonExistingMethodCount != 0)
359       abort ();
360   }
361
362   /* Reset the counters for the next test.  */
363   nonExistingMethodCount = 0;
364   forwardingCount = 0;
365   hitCount = 0;
366
367   printf ("Testing [+resolveInstanceMethod:]...\n");
368   {
369     Method m;
370     IMP i;
371     CountClass *object = [[CountClass alloc] init];
372     SelfExtendingClass *object2 = [[SelfExtendingClass alloc] init];
373
374     /** CountClass tests.  **/
375
376     /* Call an existing method.  No +resolveInstanceMethod and no
377        forwarding should be triggered.  */
378     [object existingInstanceMethod];
379
380     if (resolveInstanceMethodCount != 0)
381       abort ();
382
383     if (forwardingCount != 0)
384       abort ();
385
386     if (nonExistingMethodCount != 0)
387       abort ();
388
389     /* Call a non-existing method.  Both +resolveInstanceMethod and the
390        forwarding should be triggered.  */
391     [object nonExistingInstanceMethod];
392
393     if (resolveInstanceMethodCount != 1)
394       abort ();
395
396     if (forwardingCount != 1)
397       abort ();
398
399     if (nonExistingMethodCount != 1)
400       abort ();
401
402     /* Now try the same tests with class_getInstanceMethod(), which
403        should trigger the resolve methods too, but not the
404        forwarding.  */
405     m = class_getInstanceMethod (objc_getClass ("CountClass"),
406                                  @selector (existingInstanceMethod));
407     
408     if (resolveInstanceMethodCount != 1)
409       abort ();
410
411     if (forwardingCount != 1)
412       abort ();
413     
414     if (nonExistingMethodCount != 1)
415       abort ();
416
417     m = class_getInstanceMethod (objc_getClass ("CountClass"),
418                                  @selector (nonExistingInstanceMethod));
419
420     if (resolveInstanceMethodCount != 2)
421       abort ();
422
423     if (forwardingCount != 1)
424       abort ();
425     
426     if (nonExistingMethodCount != 1)
427       abort ();
428
429     /* Now try the same tests with class_getMethodImplementation(),
430        which should trigger the resolve methods and the
431        forwarding.  */
432     i = class_getMethodImplementation (objc_getClass ("CountClass"),
433                                        @selector (existingInstanceMethod));
434     if (resolveInstanceMethodCount != 2)
435       abort ();
436
437     if (forwardingCount != 1)
438       abort ();
439     
440     if (nonExistingMethodCount != 1)
441       abort ();
442
443     i = class_getMethodImplementation (objc_getClass ("CountClass"),
444                                        @selector (nonExistingInstanceMethod));
445     if (resolveInstanceMethodCount != 3)
446       abort ();
447     
448     if (forwardingCount != 2)
449       abort ();
450
451     if (nonExistingMethodCount != 1)
452       abort ();
453
454     /* Reset the counters for the next test.  */
455     resolveInstanceMethodCount = 0;
456     forwardingCount = 0;
457     nonExistingMethodCount = 0;
458
459
460     /** SelfExtendingClass tests.  **/
461
462     /* Try the direct countHits method first.  No resolving or
463        forwarding should be triggered.  */
464     if ([SelfExtendingClass countHits] != 1)
465       abort ();
466
467     if (resolveInstanceMethodCount != 0)
468       abort ();
469
470     if (forwardingCount != 0)
471       abort ();
472
473     if (nonExistingMethodCount != 0)
474       abort ();
475
476     /* Now, try calling a non-existing count method; it should be
477        installed and invoked.  */
478     if ([object2 nonExistingCountHitsMethod] != 2)
479       abort ();
480
481     if (resolveInstanceMethodCount != 1)
482       abort ();
483
484     if (forwardingCount != 0)
485       abort ();
486
487     if (nonExistingMethodCount != 0)
488       abort ();
489
490     /* Try it again.  The method has now been installed, so it should
491        be used and work, but with no additional resolving
492        involved.  */
493     if ([object2 nonExistingCountHitsMethod] != 3)
494       abort ();
495
496     if (resolveInstanceMethodCount != 1)
497       abort ();
498
499     if (forwardingCount != 0)
500       abort ();
501
502     if (nonExistingMethodCount != 0)
503       abort ();
504
505     /* Now try the same tests with class_getInstanceMethod().  */
506     m = class_getInstanceMethod (objc_getClass ("SelfExtendingClass"),
507                                  @selector (nonExistingCountHitsMethod2));
508     if (resolveInstanceMethodCount != 2)
509       abort ();
510
511     if (forwardingCount != 0)
512       abort ();
513     
514     if (nonExistingMethodCount != 0)
515       abort ();
516
517     /* Try it again.  The method has now been installed, so it should
518        be used and work, but with no additional resolving
519        involved.  */
520     if ([object2 nonExistingCountHitsMethod2] != 4)
521       abort ();
522
523     if (resolveInstanceMethodCount != 2)
524       abort ();
525
526     if (forwardingCount != 0)
527       abort ();
528
529     if (nonExistingMethodCount != 0)
530       abort ();
531
532
533     /* Now try the same tests with class_getMethodImplementation().  */
534     i = class_getMethodImplementation (objc_getClass ("SelfExtendingClass"),
535                                        @selector (nonExistingCountHitsMethod3));
536     if (resolveInstanceMethodCount != 3)
537       abort ();
538     
539     if (forwardingCount != 0)
540       abort ();
541     
542     if (nonExistingMethodCount != 0)
543       abort ();
544
545     /* Try it again.  The method has now been installed, so it should
546        be used and work, but with no additional resolving
547        involved.  */
548     if ([object2 nonExistingCountHitsMethod3] != 5)
549       abort ();
550
551     if (resolveInstanceMethodCount != 3)
552       abort ();
553
554     if (forwardingCount != 0)
555       abort ();
556
557     if (nonExistingMethodCount != 0)
558       abort ();
559   }
560
561
562   return 0;
563 }