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