]> git.saurik.com Git - apple/objc4.git/blob - test/resolve.m
objc4-680.tar.gz
[apple/objc4.git] / test / resolve.m
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
15 int main()
16 {
17 testwarn("rdar://11368528 confused by Foundation");
18 succeed(__FILE__);
19 }
20
21 #else
22
23 static 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
38 static 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
76 static 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
84 static 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
167 int 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