1/* resolve.m
2 * Test +resolveClassMethod: and +resolveInstanceMethod:
3 */
4
5// TEST_CFLAGS -Wno-deprecated-declarations
6
7#include "test.h"
8#include "testroot.i"
9#include <objc/objc.h>
10#include <objc/objc-runtime.h>
11#include <unistd.h>
12
13#if __has_feature(objc_arc)
14
15int main()
16{
17    testwarn("rdar://11368528 confused by Foundation");
18    succeed(__FILE__);
19}
20
21#else
22
23static int state = 0;
24
25@interface Super : TestRoot @end
26@interface Sub : Super @end
27
28
29@implementation Super
30+(void)initialize {
31    if (self == [Super class]) {
32        testassert(state == 1);
33        state = 2;
34    }
35}
36@end
37
38static id forward_handler(id self, SEL sel)
39{
40    if (class_isMetaClass(object_getClass(self))) {
41        // self is a class object
42        if (sel == @selector(missingClassMethod)) {
43            testassert(state == 21  ||  state == 25  ||  state == 80);
44            if (state == 21) state = 22;
45            if (state == 25) state = 26;
46            if (state == 80) state = 81;;
47            return nil;
48        } else if (sel == @selector(lyingClassMethod)) {
49            testassert(state == 31  ||  state == 35);
50            if (state == 31) state = 32;
51            if (state == 35) state = 36;
52            return nil;
53        }
54        fail("+forward:: shouldn't be called with sel %s", sel_getName(sel));
55        return nil;
56    }
57    else {
58        // self is not a class object
59        if (sel == @selector(missingInstanceMethod)) {
60            testassert(state == 61  ||  state == 65);
61            if (state == 61) state = 62;
62            if (state == 65) state = 66;
63            return nil;
64        } else if (sel == @selector(lyingInstanceMethod)) {
65            testassert(state == 71  ||  state == 75);
66            if (state == 71) state = 72;
67            if (state == 75) state = 76;
68            return nil;
69        }
70        fail("-forward:: shouldn't be called with sel %s", sel_getName(sel));
71        return nil;
72    }
73}
74
75
76static id classMethod_c(id __unused self, SEL __unused sel)
77{
78    testassert(state == 4  ||  state == 10);
79    if (state == 4) state = 5;
80    if (state == 10) state = 11;
81    return [Super class];
82}
83
84static id instanceMethod_c(id __unused self, SEL __unused sel)
85{
86    testassert(state == 41  ||  state == 50);
87    if (state == 41) state = 42;
88    if (state == 50) state = 51;
89    return [Sub class];
90}
91
92
93@implementation Sub
94
95+(void)method2 { }
96+(void)method3 { }
97+(void)method4 { }
98+(void)method5 { }
99
100+(void)initialize {
101    if (self == [Sub class]) {
102        testassert(state == 2);
103        state = 3;
104    }
105}
106
107+(BOOL)resolveClassMethod:(SEL)sel
108{
109    if (sel == @selector(classMethod)) {
110        testassert(state == 3);
111        state = 4;
112        class_addMethod(object_getClass(self), sel, (IMP)&classMethod_c, "");
113        return YES;
114    } else if (sel == @selector(missingClassMethod)) {
115        testassert(state == 20);
116        state = 21;
117        return NO;
118    } else if (sel == @selector(lyingClassMethod)) {
119        testassert(state == 30);
120        state = 31;
121        return YES;  // lie
122    } else {
123        fail("+resolveClassMethod: called incorrectly (sel %s)",
124             sel_getName(sel));
125        return NO;
126    }
127}
128
129+(BOOL)resolveInstanceMethod:(SEL)sel
130{
131    if (sel == @selector(instanceMethod)) {
132        testassert(state == 40);
133        state = 41;
134        class_addMethod(self, sel, (IMP)instanceMethod_c, "");
135        return YES;
136    } else if (sel == @selector(missingInstanceMethod)) {
137        testassert(state == 60);
138        state = 61;
139        return NO;
140    } else if (sel == @selector(lyingInstanceMethod)) {
141        testassert(state == 70);
142        state = 71;
143        return YES;  // lie
144    } else {
145        fail("+resolveInstanceMethod: called incorrectly (sel %s)",
146             sel_getName(sel));
147        return NO;
148    }
149}
150
151@end
152
153@interface Super (MissingMethods)
154+(id)missingClassMethod;
155@end
156
157@interface Sub (ResolvedMethods)
158+(id)classMethod;
159-(id)instanceMethod;
160+(id)missingClassMethod;
161-(id)missingInstanceMethod;
162+(id)lyingClassMethod;
163-(id)lyingInstanceMethod;
164@end
165
166
167int main()
168{
169    Sub *s;
170    id ret;
171
172    objc_setForwardHandler((void*)&forward_handler, NULL);
173
174    // Be ready for ARC to retain the class object and call +initialize early
175    state = 1;
176
177    Class dup = objc_duplicateClass(objc_getClass("Sub"), "Sub_copy", 0);
178
179    // Resolve a class method
180    // +initialize should fire first (if it hasn't already)
181    ret = [Sub classMethod];
182    testassert(state == 5);
183    testassert(ret == [Super class]);
184
185    // Call it again, cached
186    // Resolver shouldn't be called again.
187    state = 10;
188    ret = [Sub classMethod];
189    testassert(state == 11);
190    testassert(ret == [Super class]);
191
192    _objc_flush_caches(object_getClass([Sub class]));
193
194    // Call a method that won't get resolved
195    state = 20;
196    ret = [Sub missingClassMethod];
197    testassert(state == 22);
198    testassert(ret == nil);
199
200    // Call it again, cached
201    // Resolver shouldn't be called again.
202    state = 25;
203    ret = [Sub missingClassMethod];
204    testassert(state == 26);
205    testassert(ret == nil);
206
207    _objc_flush_caches(object_getClass([Sub class]));
208
209    // Call a method that won't get resolved but the resolver lies about it
210    state = 30;
211    ret = [Sub lyingClassMethod];
212    testassert(state == 32);
213    testassert(ret == nil);
214
215    // Call it again, cached
216    // Resolver shouldn't be called again.
217    state = 35;
218    ret = [Sub lyingClassMethod];
219    testassert(state == 36);
220    testassert(ret == nil);
221
222    _objc_flush_caches(object_getClass([Sub class]));
223
224
225    // Resolve an instance method
226    s = [Sub new];
227    state = 40;
228    ret = [s instanceMethod];
229    testassert(state == 42);
230    testassert(ret == [Sub class]);
231
232    // Call it again, cached
233    // Resolver shouldn't be called again.
234    state = 50;
235    ret = [s instanceMethod];
236    testassert(state == 51);
237    testassert(ret == [Sub class]);
238
239    _objc_flush_caches([Sub class]);
240
241    // Call a method that won't get resolved
242    state = 60;
243    ret = [s missingInstanceMethod];
244    testassert(state == 62);
245    testassert(ret == nil);
246
247    // Call it again, cached
248    // Resolver shouldn't be called again.
249    state = 65;
250    ret = [s missingInstanceMethod];
251    testassert(state == 66);
252    testassert(ret == nil);
253
254    _objc_flush_caches([Sub class]);
255
256    // Call a method that won't get resolved but the resolver lies about it
257    state = 70;
258    ret = [s lyingInstanceMethod];
259    testassert(state == 72);
260    testassert(ret == nil);
261
262    // Call it again, cached
263    // Resolver shouldn't be called again.
264    state = 75;
265    ret = [s lyingInstanceMethod];
266    testassert(state == 76);
267    testassert(ret == nil);
268
269    _objc_flush_caches([Sub class]);
270
271    // Call a missing method on a class that doesn't support resolving
272    state = 80;
273    ret = [Super missingClassMethod];
274    testassert(state == 81);
275    testassert(ret == nil);
276    RELEASE_VAR(s);
277
278    // Resolve an instance method on a class duplicated before resolving
279    s = [dup new];
280    state = 40;
281    ret = [s instanceMethod];
282    testassert(state == 42);
283    testassert(ret == [Sub class]);
284
285    // Call it again, cached
286    // Resolver shouldn't be called again.
287    state = 50;
288    ret = [s instanceMethod];
289    testassert(state == 51);
290    testassert(ret == [Sub class]);
291    RELEASE_VAR(s);
292
293    succeed(__FILE__);
294    return 0;
295}
296
297#endif
298
299