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