forked from RetVal/objc-runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cdtors.mm
311 lines (258 loc) · 8.67 KB
/
cdtors.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
// TEST_CONFIG
#if USE_FOUNDATION
#include <Foundation/Foundation.h>
#define SUPERCLASS NSObject
#define FILENAME "nscdtors.mm"
#else
#define SUPERCLASS TestRoot
#define FILENAME "cdtors.mm"
#endif
#include "test.h"
#include <pthread.h>
#include "objc/objc-internal.h"
#include "testroot.i"
static unsigned ctors1 = 0;
static unsigned dtors1 = 0;
static unsigned ctors2 = 0;
static unsigned dtors2 = 0;
class cxx1 {
unsigned & ctors;
unsigned& dtors;
public:
cxx1() : ctors(ctors1), dtors(dtors1) { ctors++; }
~cxx1() { dtors++; }
};
class cxx2 {
unsigned& ctors;
unsigned& dtors;
public:
cxx2() : ctors(ctors2), dtors(dtors2) { ctors++; }
~cxx2() { dtors++; }
};
/*
Class hierarchy:
TestRoot
CXXBase
NoCXXSub
CXXSub
This has two cxx-wielding classes, and a class in between without cxx.
*/
@interface CXXBase : SUPERCLASS {
cxx1 baseIvar;
}
@end
@implementation CXXBase @end
@interface NoCXXSub : CXXBase {
int nocxxIvar;
}
@end
@implementation NoCXXSub @end
@interface CXXSub : NoCXXSub {
cxx2 subIvar;
}
@end
@implementation CXXSub @end
void test_single(void)
{
// Single allocation
ctors1 = dtors1 = ctors2 = dtors2 = 0;
testonthread(^{
id o = [TestRoot new];
testassert(ctors1 == 0 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
testassert([o class] == [TestRoot class]);
RELEASE_VAR(o);
});
testcollect();
testassert(ctors1 == 0 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
ctors1 = dtors1 = ctors2 = dtors2 = 0;
testonthread(^{
id o = [CXXBase new];
testassert(ctors1 == 1 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
testassert([o class] == [CXXBase class]);
RELEASE_VAR(o);
});
testcollect();
testassert(ctors1 == 1 && dtors1 == 1 &&
ctors2 == 0 && dtors2 == 0);
ctors1 = dtors1 = ctors2 = dtors2 = 0;
testonthread(^{
id o = [NoCXXSub new];
testassert(ctors1 == 1 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
testassert([o class] == [NoCXXSub class]);
RELEASE_VAR(o);
});
testcollect();
testassert(ctors1 == 1 && dtors1 == 1 &&
ctors2 == 0 && dtors2 == 0);
ctors1 = dtors1 = ctors2 = dtors2 = 0;
testonthread(^{
id o = [CXXSub new];
testassert(ctors1 == 1 && dtors1 == 0 &&
ctors2 == 1 && dtors2 == 0);
testassert([o class] == [CXXSub class]);
RELEASE_VAR(o);
});
testcollect();
testassert(ctors1 == 1 && dtors1 == 1 &&
ctors2 == 1 && dtors2 == 1);
}
void test_inplace(void)
{
__unsafe_unretained volatile id o;
char o2[64];
id (*objc_constructInstance_fn)(Class, void*) = (id(*)(Class, void*))dlsym(RTLD_DEFAULT, "objc_constructInstance");
void (*objc_destructInstance_fn)(id) = (void(*)(id))dlsym(RTLD_DEFAULT, "objc_destructInstance");
// In-place allocation
ctors1 = dtors1 = ctors2 = dtors2 = 0;
o = objc_constructInstance_fn([TestRoot class], o2);
testassert(ctors1 == 0 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
testassert([o class] == [TestRoot class]);
objc_destructInstance_fn(o), o = nil;
testcollect();
testassert(ctors1 == 0 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
ctors1 = dtors1 = ctors2 = dtors2 = 0;
o = objc_constructInstance_fn([CXXBase class], o2);
testassert(ctors1 == 1 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
testassert([o class] == [CXXBase class]);
objc_destructInstance_fn(o), o = nil;
testcollect();
testassert(ctors1 == 1 && dtors1 == 1 &&
ctors2 == 0 && dtors2 == 0);
ctors1 = dtors1 = ctors2 = dtors2 = 0;
o = objc_constructInstance_fn([NoCXXSub class], o2);
testassert(ctors1 == 1 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
testassert([o class] == [NoCXXSub class]);
objc_destructInstance_fn(o), o = nil;
testcollect();
testassert(ctors1 == 1 && dtors1 == 1 &&
ctors2 == 0 && dtors2 == 0);
ctors1 = dtors1 = ctors2 = dtors2 = 0;
o = objc_constructInstance_fn([CXXSub class], o2);
testassert(ctors1 == 1 && dtors1 == 0 &&
ctors2 == 1 && dtors2 == 0);
testassert([o class] == [CXXSub class]);
objc_destructInstance_fn(o), o = nil;
testcollect();
testassert(ctors1 == 1 && dtors1 == 1 &&
ctors2 == 1 && dtors2 == 1);
}
#if __has_feature(objc_arc)
void test_batch(void)
{
// not converted to ARC yet
return;
}
#else
// Like class_createInstances(), but refuses to accept zero allocations
static unsigned
reallyCreateInstances(Class cls, size_t extraBytes, id *dst, unsigned want)
{
unsigned count;
while (0 == (count = class_createInstances(cls, extraBytes, dst, want))) {
testprintf("class_createInstances created nothing; retrying\n");
RELEASE_VALUE([[TestRoot alloc] init]);
}
return count;
}
void test_batch(void)
{
id o2[100];
unsigned int count, i;
// Batch allocation
for (i = 0; i < 100; i++) {
o2[i] = (id)malloc(class_getInstanceSize([TestRoot class]));
}
for (i = 0; i < 100; i++) {
free(o2[i]);
}
ctors1 = dtors1 = ctors2 = dtors2 = 0;
count = reallyCreateInstances([TestRoot class], 0, o2, 10);
testassert(count > 0);
testassert(ctors1 == 0 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
for (i = 0; i < count; i++) testassert([o2[i] class] == [TestRoot class]);
for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
testcollect();
testassert(ctors1 == 0 && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
for (i = 0; i < 100; i++) {
// prime batch allocator
free(malloc(class_getInstanceSize([TestRoot class])));
}
ctors1 = dtors1 = ctors2 = dtors2 = 0;
count = reallyCreateInstances([CXXBase class], 0, o2, 10);
testassert(count > 0);
testassert(ctors1 == count && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
for (i = 0; i < count; i++) testassert([o2[i] class] == [CXXBase class]);
for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
testcollect();
testassert(ctors1 == count && dtors1 == count &&
ctors2 == 0 && dtors2 == 0);
for (i = 0; i < 100; i++) {
// prime batch allocator
free(malloc(class_getInstanceSize([TestRoot class])));
}
ctors1 = dtors1 = ctors2 = dtors2 = 0;
count = reallyCreateInstances([NoCXXSub class], 0, o2, 10);
testassert(count > 0);
testassert(ctors1 == count && dtors1 == 0 &&
ctors2 == 0 && dtors2 == 0);
for (i = 0; i < count; i++) testassert([o2[i] class] == [NoCXXSub class]);
for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
testcollect();
testassert(ctors1 == count && dtors1 == count &&
ctors2 == 0 && dtors2 == 0);
for (i = 0; i < 100; i++) {
// prime batch allocator
free(malloc(class_getInstanceSize([TestRoot class])));
}
ctors1 = dtors1 = ctors2 = dtors2 = 0;
count = reallyCreateInstances([CXXSub class], 0, o2, 10);
testassert(count > 0);
testassert(ctors1 == count && dtors1 == 0 &&
ctors2 == count && dtors2 == 0);
for (i = 0; i < count; i++) testassert([o2[i] class] == [CXXSub class]);
for (i = 0; i < count; i++) object_dispose(o2[i]), o2[i] = nil;
testcollect();
testassert(ctors1 == count && dtors1 == count &&
ctors2 == count && dtors2 == count);
}
// not ARC
#endif
int main()
{
if (objc_collectingEnabled()) {
testwarn("rdar://19042235 test disabled in GC because it is slow");
succeed(FILENAME);
}
for (int i = 0; i < 1000; i++) {
testonthread(^{ test_single(); });
testonthread(^{ test_inplace(); });
testonthread(^{ test_batch(); });
}
testonthread(^{ test_single(); });
testonthread(^{ test_inplace(); });
testonthread(^{ test_batch(); });
leak_mark();
for (int i = 0; i < 1000; i++) {
testonthread(^{ test_single(); });
testonthread(^{ test_inplace(); });
testonthread(^{ test_batch(); });
}
leak_check(0);
// fixme ctor exceptions aren't caught inside .cxx_construct ?
// Single allocation, ctors fail
// In-place allocation, ctors fail
// Batch allocation, ctors fail for every object
// Batch allocation, ctors fail for every other object
succeed(FILENAME);
}