]> git.saurik.com Git - apple/objc4.git/blob - test/exchangeImp.m
objc4-818.2.tar.gz
[apple/objc4.git] / test / exchangeImp.m
1 /*
2 TEST_BUILD_OUTPUT
3 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
4 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
5 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
6 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
7 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
8 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
9 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
10 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
11 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
12 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
13 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
14 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
15 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
16 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
17 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
18 .*exchangeImp.m:\d+:\d+: warning: null passed to a callee that requires a non-null argument \[-Wnonnull\](\n.* note: expanded from macro 'checkExchange')?
19 END
20 */
21
22 #include "test.h"
23 #include "testroot.i"
24 #include <objc/runtime.h>
25
26 static int state;
27 static int swizzleOld;
28 static int swizzleNew;
29 static int swizzleB;
30
31 #define ONE 1
32 #define TWO 2
33 #define LENGTH 3
34 #define COUNT 4
35
36 @interface Super : TestRoot @end
37 @implementation Super
38 +(void) one { state = ONE; }
39 +(void) two { state = TWO; }
40 +(void) length { state = LENGTH; }
41 +(void) count { state = COUNT; }
42
43 -(void) swizzleTarget {
44 swizzleOld++;
45 }
46 -(void) swizzleReplacement {
47 swizzleNew++;
48 }
49 @end
50
51 #define checkExchange(s1, v1, s2, v2) \
52 do { \
53 Method m1, m2; \
54 \
55 testprintf("Check unexchanged version\n"); \
56 state = 0; \
57 [Super s1]; \
58 testassert(state == v1); \
59 state = 0; \
60 [Super s2]; \
61 testassert(state == v2); \
62 \
63 testprintf("Exchange\n"); \
64 m1 = class_getClassMethod([Super class], @selector(s1)); \
65 m2 = class_getClassMethod([Super class], @selector(s2)); \
66 testassert(m1); \
67 testassert(m2); \
68 method_exchangeImplementations(m1, m2); \
69 \
70 testprintf("Check exchanged version\n"); \
71 state = 0; \
72 [Super s1]; \
73 testassert(state == v2); \
74 state = 0; \
75 [Super s2]; \
76 testassert(state == v1); \
77 \
78 testprintf("NULL should do nothing\n"); \
79 method_exchangeImplementations(m1, NULL); \
80 method_exchangeImplementations(NULL, m2); \
81 method_exchangeImplementations(NULL, NULL); \
82 \
83 testprintf("Make sure NULL did nothing\n"); \
84 state = 0; \
85 [Super s1]; \
86 testassert(state == v2); \
87 state = 0; \
88 [Super s2]; \
89 testassert(state == v1); \
90 \
91 testprintf("Put them back\n"); \
92 method_exchangeImplementations(m1, m2); \
93 \
94 testprintf("Check restored version\n"); \
95 state = 0; \
96 [Super s1]; \
97 testassert(state == v1); \
98 state = 0; \
99 [Super s2]; \
100 testassert(state == v2); \
101 } while (0)
102
103 @interface A : Super
104 @end
105 @implementation A
106 @end
107
108 @interface B : Super
109 @end
110 @implementation B
111 - (void) swizzleTarget {
112 swizzleB++;
113 }
114 @end
115
116 @interface C : Super
117 @end
118 @implementation C
119 - (void) hello { }
120 @end
121
122 static IMP findInCache(Class cls, SEL sel)
123 {
124 struct objc_imp_cache_entry *ents;
125 int count;
126 IMP ret = nil;
127
128 ents = class_copyImpCache(cls, &count);
129 for (int i = 0; i < count; i++) {
130 if (ents[i].sel == sel) {
131 ret = ents[i].imp;
132 break;
133 }
134 }
135 free(ents);
136 return ret;
137 }
138
139 int main()
140 {
141 // Check ordinary selectors
142 checkExchange(one, ONE, two, TWO);
143
144 // Check vtable selectors
145 checkExchange(length, LENGTH, count, COUNT);
146
147 // Check ordinary<->vtable and vtable<->ordinary
148 checkExchange(count, COUNT, one, ONE);
149 checkExchange(two, TWO, length, LENGTH);
150
151 Super *s = [Super new];
152 A *a = [A new];
153 B *b = [B new];
154 C *c = [C new];
155
156 // cache swizzleTarget in Super, A and B
157 [s swizzleTarget];
158 testassert(swizzleOld == 1);
159 testassert(swizzleNew == 0);
160 testassert(swizzleB == 0);
161 testassert(findInCache([Super class], @selector(swizzleTarget)) != nil);
162
163 [a swizzleTarget];
164 testassert(swizzleOld == 2);
165 testassert(swizzleNew == 0);
166 testassert(swizzleB == 0);
167 testassert(findInCache([A class], @selector(swizzleTarget)) != nil);
168
169 [b swizzleTarget];
170 testassert(swizzleOld == 2);
171 testassert(swizzleNew == 0);
172 testassert(swizzleB == 1);
173 testassert(findInCache([B class], @selector(swizzleTarget)) != nil);
174
175 // prime C's cache too
176 [c hello];
177 testassert(findInCache([C class], @selector(hello)) != nil);
178
179 Method m1 = class_getInstanceMethod([Super class], @selector(swizzleTarget));
180 Method m2 = class_getInstanceMethod([Super class], @selector(swizzleReplacement));
181 method_exchangeImplementations(m1, m2);
182
183 // this should invalidate Super, A, but:
184 // - not B because it overrides - swizzleTarget and hence doesn't care
185 // - not C because it neither called swizzleTarget nor swizzleReplacement
186 testassert(findInCache([Super class], @selector(swizzleTarget)) == nil);
187 testassert(findInCache([A class], @selector(swizzleTarget)) == nil);
188 testassert(findInCache([B class], @selector(swizzleTarget)) != nil);
189 testassert(findInCache([C class], @selector(hello)) != nil);
190
191 // now check that all lookups do the right thing
192 [s swizzleTarget];
193 testassert(swizzleOld == 2);
194 testassert(swizzleNew == 1);
195 testassert(swizzleB == 1);
196
197 [a swizzleTarget];
198 testassert(swizzleOld == 2);
199 testassert(swizzleNew == 2);
200 testassert(swizzleB == 1);
201
202 [b swizzleTarget];
203 testassert(swizzleOld == 2);
204 testassert(swizzleNew == 2);
205 testassert(swizzleB == 2);
206
207 [c swizzleTarget];
208 testassert(swizzleOld == 2);
209 testassert(swizzleNew == 3);
210 testassert(swizzleB == 2);
211
212 succeed(__FILE__);
213 }