OSDN Git Service

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