Fix broken MinGW build of gcc.c
[official-gcc.git] / gcc / testsuite / objc.dg / gnu-api-2-resolve-method.m
bloba387709908cf7c08ffc66cea0caa9820b9a291e3
1 /* Test the Modern GNU Objective-C Runtime API.
3    This is test 'resolve-method', covering +resolveClassMethod: and
4    +resolveInstanceMethod:.  */
6 /* { dg-do run } */
7 /* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */
9 /* To get the modern GNU Objective-C Runtime API, you include
10    objc/runtime.h.  */
11 #include <objc/runtime.h>
13 /* For __objc_msg_forward2.  */
14 #include <objc/message.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
20 @interface MyRootClass
21 { Class isa; }
22 + alloc;
23 - init;
24 @end
26 @implementation MyRootClass
27 + alloc { return class_createInstance (self, 0); }
28 - init  { return self; }
29 @end
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.  */
37 /* Keep track of how many times a non-existing method was executed.  */
38 static int nonExistingMethodCount = 0;
40 /* Inspired by nil_method in libobjc.  */
41 id nonExisting_method (id receiver __attribute__ ((__unused__)),
42                        SEL sel __attribute__ ((__unused__)))
44   nonExistingMethodCount++;
45   return nil;
48 /* Keep track of how many times the forwarding lookup was invoked.  */
49 static int forwardingCount = 0;
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__)))
56   forwardingCount++;
57   return (IMP)nonExisting_method;
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
71 /* Count how many times the methods are called for class
72    'CountClass'.  */
73 static int resolveClassMethodCount = 0;
74 static int resolveInstanceMethodCount = 0;
76 @implementation CountClass : MyRootClass
77 + (BOOL) resolveClassMethod: (SEL)selector
79   resolveClassMethodCount++;
80   return NO;
82 + (BOOL) resolveInstanceMethod: (SEL)selector
84   resolveInstanceMethodCount++;
85   return NO;
87 + (void) existingClassMethod
89   return;
91 - (void) existingInstanceMethod
93   return;
95 @end
97 @protocol NonExistingStuff
98 + (void) nonExistingClassMethod;
99 - (void) nonExistingInstanceMethod;
100 @end
102 /* Declare a category with some non existing methods, but don't
103    actually implement them.  */
104 @interface CountClass (NonExistingStuff) <NonExistingStuff>
105 @end
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
118 /* How many times the countHits method (or a clone) was called.  */
119 static int hitCount = 0;
121 @implementation SelfExtendingClass : MyRootClass
122 + (int) countHits
124   hitCount++;
125   return hitCount;
127 + (BOOL) resolveClassMethod: (SEL)selector
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;
137 + (BOOL) resolveInstanceMethod: (SEL)selector
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;
148 @end
150 @protocol NonExistingStuff2
151 + (int) nonExistingCountHitsMethod;
152 - (int) nonExistingCountHitsMethod;
154 + (int) nonExistingCountHitsMethod2;
155 - (int) nonExistingCountHitsMethod2;
157 + (int) nonExistingCountHitsMethod3;
158 - (int) nonExistingCountHitsMethod3;
159 @end
161 /* Declare a category with some non existing methods, but don't
162    actually implement them.  */
163 @interface SelfExtendingClass (NonExistingStuff) <NonExistingStuff2>
164 @end
167 int main (int argc, void **args)
169   /* Functions are tested in alphabetical order.  */
171   /* Install our test forwarding hook.  */
172   __objc_msg_forward2 = forward_everything_to_non_existing_method;
174   printf ("Testing [+resolveClassMethod:]...\n");
175   {
176     Method m;
177     IMP i;
179     /** CountClass tests.  **/
181     /* Call an existing method.  No +resolveClassMethod and no
182        forwarding should be triggered.  */
183     [CountClass existingClassMethod];
185     if (resolveClassMethodCount != 0)
186       abort ();
188     if (forwardingCount != 0)
189       abort ();
191     if (nonExistingMethodCount != 0)
192       abort ();
194     /* Call a non-existing method.  Both +resolveClassMethod and the
195        forwarding should be triggered.  */
196     [CountClass nonExistingClassMethod];
198     if (resolveClassMethodCount != 1)
199       abort ();
201     if (forwardingCount != 1)
202       abort ();
204     if (nonExistingMethodCount != 1)
205       abort ();
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 ();
215     if (forwardingCount != 1)
216       abort ();
217     
218     if (nonExistingMethodCount != 1)
219       abort ();
221     m = class_getClassMethod (objc_getClass ("CountClass"),
222                               @selector (nonExistingClassMethod));
223     if (resolveClassMethodCount != 2)
224       abort ();
226     if (forwardingCount != 1)
227       abort ();
228     
229     if (nonExistingMethodCount != 1)
230       abort ();
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 ();
240     if (forwardingCount != 1)
241       abort ();
242     
243     if (nonExistingMethodCount != 1)
244       abort ();
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 ();
254     if (nonExistingMethodCount != 1)
255       abort ();
258     /* Reset the counters for the next test.  */
259     resolveClassMethodCount = 0;
260     forwardingCount = 0;
261     nonExistingMethodCount = 0;
264     /** SelfExtendingClass tests.  **/
266     /* Try the direct countHits method first.  No resolving or
267        forwarding should be triggered.  */
268     if ([SelfExtendingClass countHits] != 1)
269       abort ();
271     if (resolveClassMethodCount != 0)
272       abort ();
274     if (forwardingCount != 0)
275       abort ();
277     if (nonExistingMethodCount != 0)
278       abort ();
280     /* Now, try calling a non-existing count method; it should be
281        installed and invoked.  */
282     if ([SelfExtendingClass nonExistingCountHitsMethod] != 2)
283       abort ();
285     if (resolveClassMethodCount != 1)
286       abort ();
288     if (forwardingCount != 0)
289       abort ();
291     if (nonExistingMethodCount != 0)
292       abort ();
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 ();
300     if (resolveClassMethodCount != 1)
301       abort ();
303     if (forwardingCount != 0)
304       abort ();
306     if (nonExistingMethodCount != 0)
307       abort ();
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 ();
316     if (forwardingCount != 0)
317       abort ();
318     
319     if (nonExistingMethodCount != 0)
320       abort ();
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 ();
328     if (resolveClassMethodCount != 2)
329       abort ();
331     if (forwardingCount != 0)
332       abort ();
334     if (nonExistingMethodCount != 0)
335       abort ();
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 ();
344     if (forwardingCount != 0)
345       abort ();
346     
347     if (nonExistingMethodCount != 0)
348       abort ();
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 ();
356     if (resolveClassMethodCount != 3)
357       abort ();
359     if (forwardingCount != 0)
360       abort ();
362     if (nonExistingMethodCount != 0)
363       abort ();
364   }
366   /* Reset the counters for the next test.  */
367   nonExistingMethodCount = 0;
368   forwardingCount = 0;
369   hitCount = 0;
371   printf ("Testing [+resolveInstanceMethod:]...\n");
372   {
373     Method m;
374     IMP i;
375     CountClass *object = [[CountClass alloc] init];
376     SelfExtendingClass *object2 = [[SelfExtendingClass alloc] init];
378     /** CountClass tests.  **/
380     /* Call an existing method.  No +resolveInstanceMethod and no
381        forwarding should be triggered.  */
382     [object existingInstanceMethod];
384     if (resolveInstanceMethodCount != 0)
385       abort ();
387     if (forwardingCount != 0)
388       abort ();
390     if (nonExistingMethodCount != 0)
391       abort ();
393     /* Call a non-existing method.  Both +resolveInstanceMethod and the
394        forwarding should be triggered.  */
395     [object nonExistingInstanceMethod];
397     if (resolveInstanceMethodCount != 1)
398       abort ();
400     if (forwardingCount != 1)
401       abort ();
403     if (nonExistingMethodCount != 1)
404       abort ();
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 ();
415     if (forwardingCount != 1)
416       abort ();
417     
418     if (nonExistingMethodCount != 1)
419       abort ();
421     m = class_getInstanceMethod (objc_getClass ("CountClass"),
422                                  @selector (nonExistingInstanceMethod));
424     if (resolveInstanceMethodCount != 2)
425       abort ();
427     if (forwardingCount != 1)
428       abort ();
429     
430     if (nonExistingMethodCount != 1)
431       abort ();
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 ();
441     if (forwardingCount != 1)
442       abort ();
443     
444     if (nonExistingMethodCount != 1)
445       abort ();
447     i = class_getMethodImplementation (objc_getClass ("CountClass"),
448                                        @selector (nonExistingInstanceMethod));
449     if (resolveInstanceMethodCount != 3)
450       abort ();
451     
452     if (forwardingCount != 2)
453       abort ();
455     if (nonExistingMethodCount != 1)
456       abort ();
458     /* Reset the counters for the next test.  */
459     resolveInstanceMethodCount = 0;
460     forwardingCount = 0;
461     nonExistingMethodCount = 0;
464     /** SelfExtendingClass tests.  **/
466     /* Try the direct countHits method first.  No resolving or
467        forwarding should be triggered.  */
468     if ([SelfExtendingClass countHits] != 1)
469       abort ();
471     if (resolveInstanceMethodCount != 0)
472       abort ();
474     if (forwardingCount != 0)
475       abort ();
477     if (nonExistingMethodCount != 0)
478       abort ();
480     /* Now, try calling a non-existing count method; it should be
481        installed and invoked.  */
482     if ([object2 nonExistingCountHitsMethod] != 2)
483       abort ();
485     if (resolveInstanceMethodCount != 1)
486       abort ();
488     if (forwardingCount != 0)
489       abort ();
491     if (nonExistingMethodCount != 0)
492       abort ();
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 ();
500     if (resolveInstanceMethodCount != 1)
501       abort ();
503     if (forwardingCount != 0)
504       abort ();
506     if (nonExistingMethodCount != 0)
507       abort ();
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 ();
515     if (forwardingCount != 0)
516       abort ();
517     
518     if (nonExistingMethodCount != 0)
519       abort ();
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 ();
527     if (resolveInstanceMethodCount != 2)
528       abort ();
530     if (forwardingCount != 0)
531       abort ();
533     if (nonExistingMethodCount != 0)
534       abort ();
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 ();
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 ();
555     if (resolveInstanceMethodCount != 3)
556       abort ();
558     if (forwardingCount != 0)
559       abort ();
561     if (nonExistingMethodCount != 0)
562       abort ();
563   }
566   return 0;