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>
13 /* For __objc_msg_forward2. */
14 #include <objc/message.h>
20 @interface MyRootClass
26 @implementation MyRootClass
27 + alloc { return class_createInstance (self, 0); }
28 - init { return self; }
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++;
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__)))
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;
71 /* Count how many times the methods are called for class
73 static int resolveClassMethodCount = 0;
74 static int resolveInstanceMethodCount = 0;
76 @implementation CountClass : MyRootClass
77 + (BOOL) resolveClassMethod: (SEL)selector
79 resolveClassMethodCount++;
82 + (BOOL) resolveInstanceMethod: (SEL)selector
84 resolveInstanceMethodCount++;
87 + (void) existingClassMethod
91 - (void) existingInstanceMethod
97 @protocol NonExistingStuff
98 + (void) nonExistingClassMethod;
99 - (void) nonExistingInstanceMethod;
102 /* Declare a category with some non existing methods, but don't
103 actually implement them. */
104 @interface CountClass (NonExistingStuff) <NonExistingStuff>
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
114 + (BOOL) resolveClassMethod: (SEL)selector;
115 + (BOOL) resolveInstanceMethod: (SEL)selector;
118 /* How many times the countHits method (or a clone) was called. */
119 static int hitCount = 0;
121 @implementation SelfExtendingClass : MyRootClass
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++;
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++;
150 @protocol NonExistingStuff2
151 + (int) nonExistingCountHitsMethod;
152 - (int) nonExistingCountHitsMethod;
154 + (int) nonExistingCountHitsMethod2;
155 - (int) nonExistingCountHitsMethod2;
157 + (int) nonExistingCountHitsMethod3;
158 - (int) nonExistingCountHitsMethod3;
161 /* Declare a category with some non existing methods, but don't
162 actually implement them. */
163 @interface SelfExtendingClass (NonExistingStuff) <NonExistingStuff2>
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");
179 /** CountClass tests. **/
181 /* Call an existing method. No +resolveClassMethod and no
182 forwarding should be triggered. */
183 [CountClass existingClassMethod];
185 if (resolveClassMethodCount != 0)
188 if (forwardingCount != 0)
191 if (nonExistingMethodCount != 0)
194 /* Call a non-existing method. Both +resolveClassMethod and the
195 forwarding should be triggered. */
196 [CountClass nonExistingClassMethod];
198 if (resolveClassMethodCount != 1)
201 if (forwardingCount != 1)
204 if (nonExistingMethodCount != 1)
207 /* Now try the same tests with class_getClassMethod(), which
208 should trigger the resolve methods too, but not the
210 m = class_getClassMethod (objc_getClass ("CountClass"),
211 @selector (existingClassMethod));
212 if (resolveClassMethodCount != 1)
215 if (forwardingCount != 1)
218 if (nonExistingMethodCount != 1)
221 m = class_getClassMethod (objc_getClass ("CountClass"),
222 @selector (nonExistingClassMethod));
223 if (resolveClassMethodCount != 2)
226 if (forwardingCount != 1)
229 if (nonExistingMethodCount != 1)
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)
240 if (forwardingCount != 1)
243 if (nonExistingMethodCount != 1)
246 i = class_getMethodImplementation (object_getClass (objc_getClass ("CountClass")),
247 @selector (nonExistingClassMethod));
248 if (resolveClassMethodCount != 3)
251 if (forwardingCount != 2)
254 if (nonExistingMethodCount != 1)
258 /* Reset the counters for the next test. */
259 resolveClassMethodCount = 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)
271 if (resolveClassMethodCount != 0)
274 if (forwardingCount != 0)
277 if (nonExistingMethodCount != 0)
280 /* Now, try calling a non-existing count method; it should be
281 installed and invoked. */
282 if ([SelfExtendingClass nonExistingCountHitsMethod] != 2)
285 if (resolveClassMethodCount != 1)
288 if (forwardingCount != 0)
291 if (nonExistingMethodCount != 0)
294 /* Try it again. The method has now been installed, so it should
295 be used and work, but with no additional resolving
297 if ([SelfExtendingClass nonExistingCountHitsMethod] != 3)
300 if (resolveClassMethodCount != 1)
303 if (forwardingCount != 0)
306 if (nonExistingMethodCount != 0)
310 /* Now try the same tests with class_getClassMethod(). */
311 m = class_getClassMethod (objc_getClass ("SelfExtendingClass"),
312 @selector (nonExistingCountHitsMethod2));
313 if (resolveClassMethodCount != 2)
316 if (forwardingCount != 0)
319 if (nonExistingMethodCount != 0)
322 /* Try it again. The method has now been installed, so it should
323 be used and work, but with no additional resolving
325 if ([SelfExtendingClass nonExistingCountHitsMethod2] != 4)
328 if (resolveClassMethodCount != 2)
331 if (forwardingCount != 0)
334 if (nonExistingMethodCount != 0)
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)
344 if (forwardingCount != 0)
347 if (nonExistingMethodCount != 0)
350 /* Try it again. The method has now been installed, so it should
351 be used and work, but with no additional resolving
353 if ([SelfExtendingClass nonExistingCountHitsMethod3] != 5)
356 if (resolveClassMethodCount != 3)
359 if (forwardingCount != 0)
362 if (nonExistingMethodCount != 0)
366 /* Reset the counters for the next test. */
367 nonExistingMethodCount = 0;
371 printf ("Testing [+resolveInstanceMethod:]...\n");
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)
387 if (forwardingCount != 0)
390 if (nonExistingMethodCount != 0)
393 /* Call a non-existing method. Both +resolveInstanceMethod and the
394 forwarding should be triggered. */
395 [object nonExistingInstanceMethod];
397 if (resolveInstanceMethodCount != 1)
400 if (forwardingCount != 1)
403 if (nonExistingMethodCount != 1)
406 /* Now try the same tests with class_getInstanceMethod(), which
407 should trigger the resolve methods too, but not the
409 m = class_getInstanceMethod (objc_getClass ("CountClass"),
410 @selector (existingInstanceMethod));
412 if (resolveInstanceMethodCount != 1)
415 if (forwardingCount != 1)
418 if (nonExistingMethodCount != 1)
421 m = class_getInstanceMethod (objc_getClass ("CountClass"),
422 @selector (nonExistingInstanceMethod));
424 if (resolveInstanceMethodCount != 2)
427 if (forwardingCount != 1)
430 if (nonExistingMethodCount != 1)
433 /* Now try the same tests with class_getMethodImplementation(),
434 which should trigger the resolve methods and the
436 i = class_getMethodImplementation (objc_getClass ("CountClass"),
437 @selector (existingInstanceMethod));
438 if (resolveInstanceMethodCount != 2)
441 if (forwardingCount != 1)
444 if (nonExistingMethodCount != 1)
447 i = class_getMethodImplementation (objc_getClass ("CountClass"),
448 @selector (nonExistingInstanceMethod));
449 if (resolveInstanceMethodCount != 3)
452 if (forwardingCount != 2)
455 if (nonExistingMethodCount != 1)
458 /* Reset the counters for the next test. */
459 resolveInstanceMethodCount = 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)
471 if (resolveInstanceMethodCount != 0)
474 if (forwardingCount != 0)
477 if (nonExistingMethodCount != 0)
480 /* Now, try calling a non-existing count method; it should be
481 installed and invoked. */
482 if ([object2 nonExistingCountHitsMethod] != 2)
485 if (resolveInstanceMethodCount != 1)
488 if (forwardingCount != 0)
491 if (nonExistingMethodCount != 0)
494 /* Try it again. The method has now been installed, so it should
495 be used and work, but with no additional resolving
497 if ([object2 nonExistingCountHitsMethod] != 3)
500 if (resolveInstanceMethodCount != 1)
503 if (forwardingCount != 0)
506 if (nonExistingMethodCount != 0)
509 /* Now try the same tests with class_getInstanceMethod(). */
510 m = class_getInstanceMethod (objc_getClass ("SelfExtendingClass"),
511 @selector (nonExistingCountHitsMethod2));
512 if (resolveInstanceMethodCount != 2)
515 if (forwardingCount != 0)
518 if (nonExistingMethodCount != 0)
521 /* Try it again. The method has now been installed, so it should
522 be used and work, but with no additional resolving
524 if ([object2 nonExistingCountHitsMethod2] != 4)
527 if (resolveInstanceMethodCount != 2)
530 if (forwardingCount != 0)
533 if (nonExistingMethodCount != 0)
537 /* Now try the same tests with class_getMethodImplementation(). */
538 i = class_getMethodImplementation (objc_getClass ("SelfExtendingClass"),
539 @selector (nonExistingCountHitsMethod3));
540 if (resolveInstanceMethodCount != 3)
543 if (forwardingCount != 0)
546 if (nonExistingMethodCount != 0)
549 /* Try it again. The method has now been installed, so it should
550 be used and work, but with no additional resolving
552 if ([object2 nonExistingCountHitsMethod3] != 5)
555 if (resolveInstanceMethodCount != 3)
558 if (forwardingCount != 0)
561 if (nonExistingMethodCount != 0)