1 /* Test the Modern GNU Objective-C Runtime API.
3 This is test 'resolve-method', covering +resolveClassMethod: and
4 +resolveInstanceMethod:. */
7 /* { dg-skip-if "" { *-*-* } { "-fnext-runtime" } { "" } } */
9 /* To get the modern GNU Objective-C Runtime API, you include
11 #include <objc/runtime.h>
16 @interface MyRootClass
22 @implementation MyRootClass
23 + alloc { return class_createInstance (self, 0); }
24 - init { return self; }
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. */
33 /* Keep track of how many times a non-existing method was executed. */
34 static int nonExistingMethodCount = 0;
36 /* Inspired by nil_method in libobjc. */
37 id nonExisting_method (id receiver __attribute__ ((__unused__)),
38 SEL sel __attribute__ ((__unused__)))
40 nonExistingMethodCount++;
44 /* Keep track of how many times the forwarding lookup was invoked. */
45 static int forwardingCount = 0;
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__)))
53 return (IMP)nonExisting_method;
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;
67 /* Count how many times the methods are called for class
69 static int resolveClassMethodCount = 0;
70 static int resolveInstanceMethodCount = 0;
72 @implementation CountClass : MyRootClass
73 + (BOOL) resolveClassMethod: (SEL)selector
75 resolveClassMethodCount++;
78 + (BOOL) resolveInstanceMethod: (SEL)selector
80 resolveInstanceMethodCount++;
83 + (void) existingClassMethod
87 - (void) existingInstanceMethod
93 @protocol NonExistingStuff
94 + (void) nonExistingClassMethod;
95 - (void) nonExistingInstanceMethod;
98 /* Declare a category with some non existing methods, but don't
99 actually implement them. */
100 @interface CountClass (NonExistingStuff) <NonExistingStuff>
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
110 + (BOOL) resolveClassMethod: (SEL)selector;
111 + (BOOL) resolveInstanceMethod: (SEL)selector;
114 /* How many times the countHits method (or a clone) was called. */
115 static int hitCount = 0;
117 @implementation SelfExtendingClass : MyRootClass
123 + (BOOL) resolveClassMethod: (SEL)selector
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++;
133 + (BOOL) resolveInstanceMethod: (SEL)selector
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++;
146 @protocol NonExistingStuff2
147 + (int) nonExistingCountHitsMethod;
148 - (int) nonExistingCountHitsMethod;
150 + (int) nonExistingCountHitsMethod2;
151 - (int) nonExistingCountHitsMethod2;
153 + (int) nonExistingCountHitsMethod3;
154 - (int) nonExistingCountHitsMethod3;
157 /* Declare a category with some non existing methods, but don't
158 actually implement them. */
159 @interface SelfExtendingClass (NonExistingStuff) <NonExistingStuff2>
163 int main (int argc, void **args)
165 /* Functions are tested in alphabetical order. */
167 /* Install our test forwarding hook. */
168 __objc_msg_forward2 = forward_everything_to_non_existing_method;
170 printf ("Testing [+resolveClassMethod:]...\n");
175 /** CountClass tests. **/
177 /* Call an existing method. No +resolveClassMethod and no
178 forwarding should be triggered. */
179 [CountClass existingClassMethod];
181 if (resolveClassMethodCount != 0)
184 if (forwardingCount != 0)
187 if (nonExistingMethodCount != 0)
190 /* Call a non-existing method. Both +resolveClassMethod and the
191 forwarding should be triggered. */
192 [CountClass nonExistingClassMethod];
194 if (resolveClassMethodCount != 1)
197 if (forwardingCount != 1)
200 if (nonExistingMethodCount != 1)
203 /* Now try the same tests with class_getClassMethod(), which
204 should trigger the resolve methods too, but not the
206 m = class_getClassMethod (objc_getClass ("CountClass"),
207 @selector (existingClassMethod));
208 if (resolveClassMethodCount != 1)
211 if (forwardingCount != 1)
214 if (nonExistingMethodCount != 1)
217 m = class_getClassMethod (objc_getClass ("CountClass"),
218 @selector (nonExistingClassMethod));
219 if (resolveClassMethodCount != 2)
222 if (forwardingCount != 1)
225 if (nonExistingMethodCount != 1)
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)
236 if (forwardingCount != 1)
239 if (nonExistingMethodCount != 1)
242 i = class_getMethodImplementation (object_getClass (objc_getClass ("CountClass")),
243 @selector (nonExistingClassMethod));
244 if (resolveClassMethodCount != 3)
247 if (forwardingCount != 2)
250 if (nonExistingMethodCount != 1)
254 /* Reset the counters for the next test. */
255 resolveClassMethodCount = 0;
257 nonExistingMethodCount = 0;
260 /** SelfExtendingClass tests. **/
262 /* Try the direct countHits method first. No resolving or
263 forwarding should be triggered. */
264 if ([SelfExtendingClass countHits] != 1)
267 if (resolveClassMethodCount != 0)
270 if (forwardingCount != 0)
273 if (nonExistingMethodCount != 0)
276 /* Now, try calling a non-existing count method; it should be
277 installed and invoked. */
278 if ([SelfExtendingClass nonExistingCountHitsMethod] != 2)
281 if (resolveClassMethodCount != 1)
284 if (forwardingCount != 0)
287 if (nonExistingMethodCount != 0)
290 /* Try it again. The method has now been installed, so it should
291 be used and work, but with no additional resolving
293 if ([SelfExtendingClass nonExistingCountHitsMethod] != 3)
296 if (resolveClassMethodCount != 1)
299 if (forwardingCount != 0)
302 if (nonExistingMethodCount != 0)
306 /* Now try the same tests with class_getClassMethod(). */
307 m = class_getClassMethod (objc_getClass ("SelfExtendingClass"),
308 @selector (nonExistingCountHitsMethod2));
309 if (resolveClassMethodCount != 2)
312 if (forwardingCount != 0)
315 if (nonExistingMethodCount != 0)
318 /* Try it again. The method has now been installed, so it should
319 be used and work, but with no additional resolving
321 if ([SelfExtendingClass nonExistingCountHitsMethod2] != 4)
324 if (resolveClassMethodCount != 2)
327 if (forwardingCount != 0)
330 if (nonExistingMethodCount != 0)
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)
340 if (forwardingCount != 0)
343 if (nonExistingMethodCount != 0)
346 /* Try it again. The method has now been installed, so it should
347 be used and work, but with no additional resolving
349 if ([SelfExtendingClass nonExistingCountHitsMethod3] != 5)
352 if (resolveClassMethodCount != 3)
355 if (forwardingCount != 0)
358 if (nonExistingMethodCount != 0)
362 /* Reset the counters for the next test. */
363 nonExistingMethodCount = 0;
367 printf ("Testing [+resolveInstanceMethod:]...\n");
371 CountClass *object = [[CountClass alloc] init];
372 SelfExtendingClass *object2 = [[SelfExtendingClass alloc] init];
374 /** CountClass tests. **/
376 /* Call an existing method. No +resolveInstanceMethod and no
377 forwarding should be triggered. */
378 [object existingInstanceMethod];
380 if (resolveInstanceMethodCount != 0)
383 if (forwardingCount != 0)
386 if (nonExistingMethodCount != 0)
389 /* Call a non-existing method. Both +resolveInstanceMethod and the
390 forwarding should be triggered. */
391 [object nonExistingInstanceMethod];
393 if (resolveInstanceMethodCount != 1)
396 if (forwardingCount != 1)
399 if (nonExistingMethodCount != 1)
402 /* Now try the same tests with class_getInstanceMethod(), which
403 should trigger the resolve methods too, but not the
405 m = class_getInstanceMethod (objc_getClass ("CountClass"),
406 @selector (existingInstanceMethod));
408 if (resolveInstanceMethodCount != 1)
411 if (forwardingCount != 1)
414 if (nonExistingMethodCount != 1)
417 m = class_getInstanceMethod (objc_getClass ("CountClass"),
418 @selector (nonExistingInstanceMethod));
420 if (resolveInstanceMethodCount != 2)
423 if (forwardingCount != 1)
426 if (nonExistingMethodCount != 1)
429 /* Now try the same tests with class_getMethodImplementation(),
430 which should trigger the resolve methods and the
432 i = class_getMethodImplementation (objc_getClass ("CountClass"),
433 @selector (existingInstanceMethod));
434 if (resolveInstanceMethodCount != 2)
437 if (forwardingCount != 1)
440 if (nonExistingMethodCount != 1)
443 i = class_getMethodImplementation (objc_getClass ("CountClass"),
444 @selector (nonExistingInstanceMethod));
445 if (resolveInstanceMethodCount != 3)
448 if (forwardingCount != 2)
451 if (nonExistingMethodCount != 1)
454 /* Reset the counters for the next test. */
455 resolveInstanceMethodCount = 0;
457 nonExistingMethodCount = 0;
460 /** SelfExtendingClass tests. **/
462 /* Try the direct countHits method first. No resolving or
463 forwarding should be triggered. */
464 if ([SelfExtendingClass countHits] != 1)
467 if (resolveInstanceMethodCount != 0)
470 if (forwardingCount != 0)
473 if (nonExistingMethodCount != 0)
476 /* Now, try calling a non-existing count method; it should be
477 installed and invoked. */
478 if ([object2 nonExistingCountHitsMethod] != 2)
481 if (resolveInstanceMethodCount != 1)
484 if (forwardingCount != 0)
487 if (nonExistingMethodCount != 0)
490 /* Try it again. The method has now been installed, so it should
491 be used and work, but with no additional resolving
493 if ([object2 nonExistingCountHitsMethod] != 3)
496 if (resolveInstanceMethodCount != 1)
499 if (forwardingCount != 0)
502 if (nonExistingMethodCount != 0)
505 /* Now try the same tests with class_getInstanceMethod(). */
506 m = class_getInstanceMethod (objc_getClass ("SelfExtendingClass"),
507 @selector (nonExistingCountHitsMethod2));
508 if (resolveInstanceMethodCount != 2)
511 if (forwardingCount != 0)
514 if (nonExistingMethodCount != 0)
517 /* Try it again. The method has now been installed, so it should
518 be used and work, but with no additional resolving
520 if ([object2 nonExistingCountHitsMethod2] != 4)
523 if (resolveInstanceMethodCount != 2)
526 if (forwardingCount != 0)
529 if (nonExistingMethodCount != 0)
533 /* Now try the same tests with class_getMethodImplementation(). */
534 i = class_getMethodImplementation (objc_getClass ("SelfExtendingClass"),
535 @selector (nonExistingCountHitsMethod3));
536 if (resolveInstanceMethodCount != 3)
539 if (forwardingCount != 0)
542 if (nonExistingMethodCount != 0)
545 /* Try it again. The method has now been installed, so it should
546 be used and work, but with no additional resolving
548 if ([object2 nonExistingCountHitsMethod3] != 5)
551 if (resolveInstanceMethodCount != 3)
554 if (forwardingCount != 0)
557 if (nonExistingMethodCount != 0)