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