]>
Commit | Line | Data |
---|---|---|
13ba007e A |
1 | // TEST_CONFIG MEM=mrc |
2 | // TEST_CRASHES | |
3 | /* | |
4 | TEST_RUN_OUTPUT | |
5 | objc\[\d+\]: Cannot form weak reference to instance \(0x[0-9a-f]+\) of class Crash. It is possible that this object was over-released, or is in the process of deallocation. | |
6 | objc\[\d+\]: HALTED | |
7 | END | |
8 | */ | |
9 | ||
10 | #include "test.h" | |
11 | ||
12 | #include <Foundation/NSObject.h> | |
13 | ||
14 | static id weak; | |
15 | static id weak2; | |
16 | static id weak3; | |
17 | static id weak4; | |
18 | static bool did_dealloc; | |
19 | ||
20 | static int state; | |
21 | ||
22 | @interface NSObject (WeakInternals) | |
23 | -(BOOL)_tryRetain; | |
24 | -(BOOL)_isDeallocating; | |
25 | @end | |
26 | ||
27 | @interface Test : NSObject @end | |
28 | @implementation Test | |
29 | -(void)dealloc { | |
30 | testprintf("Weak storeOrNil does not crash while deallocating\n"); | |
31 | weak4 = (id)0x100; // old value must not be used | |
32 | id result = objc_initWeakOrNil(&weak4, self); | |
33 | testassert(result == nil); | |
34 | testassert(weak4 == nil); | |
35 | result = objc_storeWeakOrNil(&weak4, self); | |
36 | testassert(result == nil); | |
37 | testassert(weak4 == nil); | |
38 | ||
39 | // The value returned by objc_loadWeak() is now nil, | |
40 | // but the storage is not yet cleared. | |
41 | testassert(weak == self); | |
42 | testassert(weak2 == self); | |
43 | ||
44 | // objc_loadWeak() does not eagerly clear the storage. | |
45 | testassert(objc_loadWeakRetained(&weak) == nil); | |
46 | testassert(weak != nil); | |
47 | ||
48 | // dealloc clears the storage. | |
49 | testprintf("Weak references clear during super dealloc\n"); | |
50 | testassert(weak2 != nil); | |
51 | [super dealloc]; | |
52 | testassert(weak == nil); | |
53 | testassert(weak2 == nil); | |
54 | ||
55 | did_dealloc = true; | |
56 | } | |
57 | @end | |
58 | ||
59 | @interface CustomTryRetain : Test @end | |
60 | @implementation CustomTryRetain | |
61 | -(BOOL)_tryRetain { state++; return [super _tryRetain]; } | |
62 | @end | |
63 | ||
64 | @interface CustomIsDeallocating : Test @end | |
65 | @implementation CustomIsDeallocating | |
66 | -(BOOL)_isDeallocating { state++; return [super _isDeallocating]; } | |
67 | @end | |
68 | ||
69 | @interface CustomAllowsWeakReference : Test @end | |
70 | @implementation CustomAllowsWeakReference | |
71 | -(BOOL)allowsWeakReference { state++; return [super allowsWeakReference]; } | |
72 | @end | |
73 | ||
74 | @interface CustomRetainWeakReference : Test @end | |
75 | @implementation CustomRetainWeakReference | |
76 | -(BOOL)retainWeakReference { state++; return [super retainWeakReference]; } | |
77 | @end | |
78 | ||
79 | @interface Crash : NSObject @end | |
80 | @implementation Crash | |
81 | -(void)dealloc { | |
82 | testassert(weak == self); | |
83 | testassert(weak2 == self); | |
84 | testassert(objc_loadWeakRetained(&weak) == nil); | |
85 | testassert(objc_loadWeakRetained(&weak2) == nil); | |
86 | ||
87 | testprintf("Weak storeOrNil does not crash while deallocating\n"); | |
88 | id result = objc_storeWeakOrNil(&weak, self); | |
89 | testassert(result == nil); | |
90 | ||
91 | testprintf("Weak store crashes while deallocating\n"); | |
92 | objc_storeWeak(&weak, self); | |
93 | fail("objc_storeWeak of deallocating value should have crashed"); | |
94 | [super dealloc]; | |
95 | } | |
96 | @end | |
97 | ||
98 | ||
99 | void cycle(Class cls, Test *obj, Test *obj2, bool storeOrNil) | |
100 | { | |
101 | testprintf("Cycling class %s\n", class_getName(cls)); | |
102 | ||
103 | id result; | |
104 | ||
105 | id (*storeWeak)(id *location, id obj); | |
106 | id (*initWeak)(id *location, id obj); | |
107 | if (storeOrNil) { | |
108 | testprintf("Using objc_storeWeakOrNil\n"); | |
109 | storeWeak = objc_storeWeakOrNil; | |
110 | initWeak = objc_initWeakOrNil; | |
111 | } else { | |
112 | testprintf("Using objc_storeWeak\n"); | |
113 | storeWeak = objc_storeWeak; | |
114 | initWeak = objc_initWeak; | |
115 | } | |
116 | ||
117 | // state counts calls to custom weak methods | |
118 | // Difference test classes have different expected values. | |
119 | int storeTarget; | |
120 | int loadTarget; | |
121 | if (cls == [Test class]) { | |
122 | storeTarget = 0; | |
123 | loadTarget = 0; | |
124 | } | |
125 | else if (cls == [CustomTryRetain class] || | |
126 | cls == [CustomRetainWeakReference class]) | |
127 | { | |
128 | storeTarget = 0; | |
129 | loadTarget = 1; | |
130 | } | |
131 | else if (cls == [CustomIsDeallocating class] || | |
132 | cls == [CustomAllowsWeakReference class]) | |
133 | { | |
134 | storeTarget = 1; | |
135 | loadTarget = 0; | |
136 | } | |
137 | else fail("wut"); | |
138 | ||
139 | testprintf("Weak assignment\n"); | |
140 | state = 0; | |
141 | result = storeWeak(&weak, obj); | |
142 | testassert(state == storeTarget); | |
143 | testassert(result == obj); | |
144 | testassert(weak == obj); | |
145 | ||
146 | testprintf("Weak assignment to the same value\n"); | |
147 | state = 0; | |
148 | result = storeWeak(&weak, obj); | |
149 | testassert(state == storeTarget); | |
150 | testassert(result == obj); | |
151 | testassert(weak == obj); | |
152 | ||
153 | testprintf("Weak load\n"); | |
154 | state = 0; | |
155 | result = objc_loadWeakRetained(&weak); | |
156 | if (state != loadTarget) testprintf("state %d target %d\n", state, loadTarget); | |
157 | testassert(state == loadTarget); | |
158 | testassert(result == obj); | |
159 | testassert(result == weak); | |
160 | [result release]; | |
161 | ||
162 | testprintf("Weak assignment to different value\n"); | |
163 | state = 0; | |
164 | result = storeWeak(&weak, obj2); | |
165 | testassert(state == storeTarget); | |
166 | testassert(result == obj2); | |
167 | testassert(weak == obj2); | |
168 | ||
169 | testprintf("Weak assignment to NULL\n"); | |
170 | state = 0; | |
171 | result = storeWeak(&weak, NULL); | |
172 | testassert(state == 0); | |
173 | testassert(result == NULL); | |
174 | testassert(weak == NULL); | |
175 | ||
176 | testprintf("Weak re-assignment to NULL\n"); | |
177 | state = 0; | |
178 | result = storeWeak(&weak, NULL); | |
179 | testassert(state == 0); | |
180 | testassert(result == NULL); | |
181 | testassert(weak == NULL); | |
182 | ||
183 | testprintf("Weak move\n"); | |
184 | state = 0; | |
185 | result = storeWeak(&weak, obj); | |
186 | testassert(state == storeTarget); | |
187 | testassert(result == obj); | |
188 | testassert(weak == obj); | |
189 | weak2 = (id)(PAGE_MAX_SIZE-16); | |
190 | objc_moveWeak(&weak2, &weak); | |
191 | testassert(weak == nil); | |
192 | testassert(weak2 == obj); | |
193 | storeWeak(&weak2, NULL); | |
194 | ||
195 | testprintf("Weak copy\n"); | |
196 | state = 0; | |
197 | result = storeWeak(&weak, obj); | |
198 | testassert(state == storeTarget); | |
199 | testassert(result == obj); | |
200 | testassert(weak == obj); | |
201 | weak2 = (id)(PAGE_MAX_SIZE-16); | |
202 | objc_copyWeak(&weak2, &weak); | |
203 | testassert(weak == obj); | |
204 | testassert(weak2 == obj); | |
205 | storeWeak(&weak, NULL); | |
206 | storeWeak(&weak2, NULL); | |
207 | ||
208 | testprintf("Weak clear\n"); | |
209 | ||
210 | id obj3 = [cls new]; | |
211 | ||
212 | state = 0; | |
213 | result = storeWeak(&weak, obj3); | |
214 | testassert(state == storeTarget); | |
215 | testassert(result == obj3); | |
216 | testassert(weak == obj3); | |
217 | ||
218 | state = 0; | |
219 | result = storeWeak(&weak2, obj3); | |
220 | testassert(state == storeTarget); | |
221 | testassert(result == obj3); | |
222 | testassert(weak2 == obj3); | |
223 | ||
224 | did_dealloc = false; | |
225 | [obj3 release]; | |
226 | testassert(did_dealloc); | |
227 | testassert(weak == NULL); | |
228 | testassert(weak2 == NULL); | |
229 | ||
230 | ||
231 | testprintf("Weak init and destroy\n"); | |
232 | ||
233 | id obj4 = [cls new]; | |
234 | ||
235 | state = 0; | |
236 | weak = (id)0x100; // old value must not be used | |
237 | result = initWeak(&weak, obj4); | |
238 | testassert(state == storeTarget); | |
239 | testassert(result == obj4); | |
240 | testassert(weak == obj4); | |
241 | ||
242 | state = 0; | |
243 | weak2 = (id)0x100; // old value must not be used | |
244 | result = initWeak(&weak2, obj4); | |
245 | testassert(state == storeTarget); | |
246 | testassert(result == obj4); | |
247 | testassert(weak2 == obj4); | |
248 | ||
249 | state = 0; | |
250 | weak3 = (id)0x100; // old value must not be used | |
251 | result = initWeak(&weak3, obj4); | |
252 | testassert(state == storeTarget); | |
253 | testassert(result == obj4); | |
254 | testassert(weak3 == obj4); | |
255 | ||
256 | state = 0; | |
257 | objc_destroyWeak(&weak3); | |
258 | testassert(state == 0); | |
259 | testassert(weak3 == obj4); // storage is unchanged | |
260 | ||
261 | did_dealloc = false; | |
262 | [obj4 release]; | |
263 | testassert(did_dealloc); | |
264 | testassert(weak == NULL); // not destroyed earlier so cleared now | |
265 | testassert(weak2 == NULL); // not destroyed earlier so cleared now | |
266 | testassert(weak3 == obj4); // destroyed earlier so not cleared now | |
267 | ||
268 | objc_destroyWeak(&weak); | |
269 | objc_destroyWeak(&weak2); | |
270 | } | |
271 | ||
272 | ||
273 | void test_class(Class cls) | |
274 | { | |
275 | // prime strong and weak side tables before leak checking | |
276 | Test *prime[256] = {nil}; | |
277 | for (size_t i = 0; i < sizeof(prime)/sizeof(prime[0]); i++) { | |
278 | objc_storeWeak(&prime[i], [cls new]); | |
279 | } | |
280 | ||
281 | Test *obj = [cls new]; | |
282 | Test *obj2 = [cls new]; | |
283 | ||
284 | for (int i = 0; i < 100000; i++) { | |
285 | cycle(cls, obj, obj2, false); | |
286 | cycle(cls, obj, obj2, true); | |
287 | } | |
288 | leak_mark(); | |
289 | for (int i = 0; i < 100000; i++) { | |
290 | cycle(cls, obj, obj2, false); | |
291 | cycle(cls, obj, obj2, true); | |
292 | } | |
293 | // allow some slop for side table expansion | |
294 | // 5120 is common with this configuration | |
295 | leak_check(6000); | |
296 | ||
297 | // rdar://14105994 | |
298 | id weaks[8]; | |
299 | for (size_t i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) { | |
300 | objc_storeWeak(&weaks[i], obj); | |
301 | } | |
302 | for (size_t i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) { | |
303 | objc_storeWeak(&weaks[i], nil); | |
304 | } | |
305 | } | |
306 | ||
307 | int main() | |
308 | { | |
309 | test_class([Test class]); | |
310 | test_class([CustomTryRetain class]); | |
311 | test_class([CustomIsDeallocating class]); | |
312 | test_class([CustomAllowsWeakReference class]); | |
313 | test_class([CustomRetainWeakReference class]); | |
314 | ||
315 | ||
316 | id result; | |
317 | ||
318 | Crash *obj3 = [Crash new]; | |
319 | result = objc_storeWeak(&weak, obj3); | |
320 | testassert(result == obj3); | |
321 | testassert(weak == obj3); | |
322 | ||
323 | result = objc_storeWeak(&weak2, obj3); | |
324 | testassert(result == obj3); | |
325 | testassert(weak2 == obj3); | |
326 | ||
327 | [obj3 release]; | |
328 | fail("should have crashed in -[Crash dealloc]"); | |
329 | } |