]> git.saurik.com Git - apple/objc4.git/blob - test/arr-weak.m
objc4-680.tar.gz
[apple/objc4.git] / test / arr-weak.m
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 CRASHED: SIG(ILL|TRAP)
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 }