]>
Commit | Line | Data |
---|---|---|
bd5b749c | 1 | /* |
e588f561 | 2 | * Copyright (c) 2010 Apple Inc. All rights reserved. |
bd5b749c A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
f64f9b69 | 23 | |
bd5b749c | 24 | /* CFRuntime.c |
cf7d2af9 | 25 | Copyright (c) 1999-2009, Apple Inc. All rights reserved. |
bd5b749c A |
26 | Responsibility: Christopher Kane |
27 | */ | |
28 | ||
29 | #define ENABLE_ZOMBIES 1 | |
30 | ||
cf7d2af9 | 31 | #include <CoreFoundation/CFRuntime.h> |
bd5b749c | 32 | #include "CFInternal.h" |
cf7d2af9 | 33 | #include "CFBasicHash.h" |
bd5b749c A |
34 | #include <string.h> |
35 | #include <stdlib.h> | |
36 | #include <stdio.h> | |
cf7d2af9 | 37 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
bd5b749c A |
38 | #include <dlfcn.h> |
39 | #include <mach-o/dyld.h> | |
cf7d2af9 | 40 | #include <mach/mach.h> |
bd5b749c A |
41 | #include <crt_externs.h> |
42 | #include <unistd.h> | |
cf7d2af9 A |
43 | #include <objc/runtime.h> |
44 | #include <sys/stat.h> | |
45 | #include <CoreFoundation/CFStringDefaultEncoding.h> | |
46 | #define objc_isAuto (0) | |
47 | #else | |
48 | #include <objc/runtime.h> | |
49 | #endif | |
50 | ||
51 | #if DEPLOYMENT_TARGET_WINDOWS | |
52 | #define _objc_getFreedObjectClass() 0 | |
53 | #else | |
54 | extern Class _objc_getFreedObjectClass(void); | |
55 | #endif | |
bd5b749c | 56 | |
cf7d2af9 A |
57 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
58 | extern void __CFRecordAllocationEvent(int eventnum, void *ptr, int64_t size, uint64_t data, const char *classname); | |
59 | #else | |
bd5b749c | 60 | #define __CFRecordAllocationEvent(a, b, c, d, e) ((void)0) |
cf7d2af9 A |
61 | #endif |
62 | ||
63 | ||
64 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
65 | extern void instrumentObjcMessageSends(BOOL flag); | |
66 | #endif | |
67 | ||
68 | #if DEPLOYMENT_TARGET_WINDOWS | |
69 | #include <Shellapi.h> | |
70 | #endif | |
bd5b749c A |
71 | |
72 | enum { | |
73 | // retain/release recording constants -- must match values | |
74 | // used by OA for now; probably will change in the future | |
75 | __kCFRetainEvent = 28, | |
76 | __kCFReleaseEvent = 29 | |
77 | }; | |
78 | ||
cf7d2af9 A |
79 | #if DEPLOYMENT_TARGET_WINDOWS |
80 | #include <malloc.h> | |
81 | #else | |
bd5b749c | 82 | #include <malloc/malloc.h> |
cf7d2af9 A |
83 | #endif |
84 | ||
85 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
86 | ||
87 | bool __CFOASafe = false; | |
88 | ||
89 | void (*__CFObjectAllocRecordAllocationFunction)(int, void *, int64_t , uint64_t, const char *) = NULL; | |
90 | void (*__CFObjectAllocSetLastAllocEventNameFunction)(void *, const char *) = NULL; | |
91 | ||
92 | void __CFOAInitialize(void) { | |
93 | static void (*dyfunc)(void) = (void *)~0; | |
94 | if (NULL == __CFgetenv("OAKeepAllocationStatistics")) return; | |
95 | if ((void *)~0 == dyfunc) { | |
96 | dyfunc = dlsym(RTLD_DEFAULT, "_OAInitialize"); | |
97 | } | |
98 | if (NULL != dyfunc) { | |
99 | dyfunc(); | |
100 | __CFObjectAllocRecordAllocationFunction = dlsym(RTLD_DEFAULT, "_OARecordAllocationEvent"); | |
101 | __CFObjectAllocSetLastAllocEventNameFunction = dlsym(RTLD_DEFAULT, "_OASetLastAllocationEventName"); | |
102 | __CFOASafe = true; | |
103 | } | |
104 | } | |
105 | ||
106 | void __CFRecordAllocationEvent(int eventnum, void *ptr, int64_t size, uint64_t data, const char *classname) { | |
107 | if (!__CFOASafe || !__CFObjectAllocRecordAllocationFunction) return; | |
108 | __CFObjectAllocRecordAllocationFunction(eventnum, ptr, size, data, classname); | |
109 | } | |
110 | ||
111 | void __CFSetLastAllocationEventName(void *ptr, const char *classname) { | |
112 | if (!__CFOASafe || !__CFObjectAllocSetLastAllocEventNameFunction) return; | |
113 | __CFObjectAllocSetLastAllocEventNameFunction(ptr, classname); | |
114 | } | |
115 | ||
116 | #endif | |
bd5b749c A |
117 | |
118 | extern void __HALT(void); | |
119 | ||
120 | static CFTypeID __kCFNotATypeTypeID = _kCFRuntimeNotATypeID; | |
121 | ||
122 | #if !defined (__cplusplus) | |
123 | static const CFRuntimeClass __CFNotATypeClass = { | |
124 | 0, | |
125 | "Not A Type", | |
126 | (void *)__HALT, | |
127 | (void *)__HALT, | |
128 | (void *)__HALT, | |
129 | (void *)__HALT, | |
130 | (void *)__HALT, | |
131 | (void *)__HALT, | |
132 | (void *)__HALT | |
133 | }; | |
134 | ||
135 | static CFTypeID __kCFTypeTypeID = _kCFRuntimeNotATypeID; | |
136 | ||
137 | static const CFRuntimeClass __CFTypeClass = { | |
138 | 0, | |
139 | "CFType", | |
140 | (void *)__HALT, | |
141 | (void *)__HALT, | |
142 | (void *)__HALT, | |
143 | (void *)__HALT, | |
144 | (void *)__HALT, | |
145 | (void *)__HALT, | |
146 | (void *)__HALT | |
147 | }; | |
148 | #else | |
149 | void SIG1(CFTypeRef){__HALT();};; | |
150 | CFTypeRef SIG2(CFAllocatorRef,CFTypeRef){__HALT();return NULL;}; | |
151 | Boolean SIG3(CFTypeRef,CFTypeRef){__HALT();return FALSE;}; | |
152 | CFHashCode SIG4(CFTypeRef){__HALT(); return 0;}; | |
153 | CFStringRef SIG5(CFTypeRef,CFDictionaryRef){__HALT();return NULL;}; | |
154 | CFStringRef SIG6(CFTypeRef){__HALT();return NULL;}; | |
155 | ||
156 | static const CFRuntimeClass __CFNotATypeClass = { | |
157 | 0, | |
158 | "Not A Type", | |
159 | SIG1, | |
160 | SIG2, | |
161 | SIG1, | |
162 | SIG3, | |
163 | SIG4, | |
164 | SIG5, | |
165 | SIG6 | |
166 | }; | |
167 | ||
168 | static CFTypeID __kCFTypeTypeID = _kCFRuntimeNotATypeID; | |
169 | ||
170 | static const CFRuntimeClass __CFTypeClass = { | |
171 | 0, | |
172 | "CFType", | |
173 | SIG1, | |
174 | SIG2, | |
175 | SIG1, | |
176 | SIG3, | |
177 | SIG4, | |
178 | SIG5, | |
179 | SIG6 | |
180 | }; | |
181 | #endif //__cplusplus | |
182 | ||
cf7d2af9 A |
183 | // the lock does not protect most reading of these; we just leak the old table to allow read-only accesses to continue to work |
184 | static CFSpinLock_t __CFBigRuntimeFunnel = CFSpinLockInit; | |
bd5b749c A |
185 | static CFRuntimeClass ** __CFRuntimeClassTable = NULL; |
186 | int32_t __CFRuntimeClassTableSize = 0; | |
187 | static int32_t __CFRuntimeClassTableCount = 0; | |
188 | ||
cf7d2af9 | 189 | uintptr_t *__CFRuntimeObjCClassTable = NULL; |
bd5b749c | 190 | |
cf7d2af9 | 191 | __private_extern__ void * (*__CFSendObjCMsg)(const void *, SEL, ...) = NULL; |
bd5b749c A |
192 | |
193 | bool (*__CFObjCIsCollectable)(void *) = NULL; | |
194 | ||
cf7d2af9 | 195 | // Compiler uses this symbol name; must match compiler built-in decl, so we use 'int' |
bd5b749c A |
196 | #if __LP64__ |
197 | int __CFConstantStringClassReference[24] = {0}; | |
198 | #else | |
199 | int __CFConstantStringClassReference[12] = {0}; | |
200 | #endif | |
201 | ||
cf7d2af9 A |
202 | |
203 | Boolean _CFIsObjC(CFTypeID typeID, void *obj) { | |
204 | __CFSpinLock(&__CFBigRuntimeFunnel); | |
205 | Boolean b = ((typeID >= (CFTypeID)__CFRuntimeClassTableSize) || (((CFRuntimeBase *)obj)->_cfisa != (uintptr_t)__CFRuntimeObjCClassTable[typeID] && ((CFRuntimeBase *)obj)->_cfisa > (uintptr_t)0xFFF)); | |
206 | __CFSpinUnlock(&__CFBigRuntimeFunnel); | |
207 | return b; | |
208 | } | |
bd5b749c A |
209 | |
210 | CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls) { | |
211 | // version field must be 0 | |
212 | // className must be pure ASCII string, non-null | |
cf7d2af9 | 213 | __CFSpinLock(&__CFBigRuntimeFunnel); |
bd5b749c A |
214 | if (__CFMaxRuntimeTypes <= __CFRuntimeClassTableCount) { |
215 | CFLog(kCFLogLevelWarning, CFSTR("*** CoreFoundation class table full; registration failing for class '%s'. Program will crash soon."), cls->className); | |
cf7d2af9 | 216 | __CFSpinUnlock(&__CFBigRuntimeFunnel); |
bd5b749c A |
217 | return _kCFRuntimeNotATypeID; |
218 | } | |
219 | if (__CFRuntimeClassTableSize <= __CFRuntimeClassTableCount) { | |
220 | int32_t old_size = __CFRuntimeClassTableSize; | |
221 | int32_t new_size = __CFRuntimeClassTableSize * 4; | |
222 | ||
223 | void *new_table1 = calloc(new_size, sizeof(CFRuntimeClass *)); | |
224 | memmove(new_table1, __CFRuntimeClassTable, old_size * sizeof(CFRuntimeClass *)); | |
225 | __CFRuntimeClassTable = (CFRuntimeClass**)new_table1; | |
cf7d2af9 A |
226 | |
227 | void *new_table2 = calloc(new_size, sizeof(uintptr_t)); | |
228 | memmove(new_table2, __CFRuntimeObjCClassTable, old_size * sizeof(uintptr_t)); | |
229 | for (CFIndex idx = old_size; idx < new_size; idx++) { | |
230 | ((uintptr_t *)new_table2)[idx] = __CFRuntimeObjCClassTable[0]; | |
231 | } | |
232 | __CFRuntimeObjCClassTable = (uintptr_t *)new_table2; | |
233 | ||
bd5b749c A |
234 | __CFRuntimeClassTableSize = new_size; |
235 | // The old value of __CFRuntimeClassTable is intentionally leaked | |
236 | // for thread-safety reasons: | |
237 | // other threads might have loaded the value of that, in functions here | |
238 | // in this file executing in other threads, and may attempt to use it after | |
239 | // this thread gets done reallocating here, so freeing is unsafe. We | |
240 | // don't want to pay the expense of locking around all uses of these variables. | |
241 | // The old value of __CFRuntimeObjCClassTable is intentionally leaked | |
242 | // for thread-safety reasons: | |
243 | // other threads might have loaded the value of that, since it is | |
244 | // accessible via CFBridgingPriv.h, and may attempt to use it after | |
245 | // this thread gets done reallocating here, so freeing is unsafe. | |
246 | } | |
247 | __CFRuntimeClassTable[__CFRuntimeClassTableCount++] = (CFRuntimeClass *)cls; | |
cf7d2af9 A |
248 | CFTypeID typeID = __CFRuntimeClassTableCount - 1; |
249 | __CFSpinUnlock(&__CFBigRuntimeFunnel); | |
250 | return typeID; | |
bd5b749c A |
251 | } |
252 | ||
253 | void _CFRuntimeBridgeClasses(CFTypeID cf_typeID, const char *objc_classname) { | |
cf7d2af9 A |
254 | __CFSpinLock(&__CFBigRuntimeFunnel); |
255 | __CFRuntimeObjCClassTable[cf_typeID] = (uintptr_t)objc_getFutureClass(objc_classname); | |
256 | __CFSpinUnlock(&__CFBigRuntimeFunnel); | |
bd5b749c A |
257 | } |
258 | ||
259 | const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID) { | |
cf7d2af9 | 260 | return __CFRuntimeClassTable[typeID]; // hopelessly unthreadsafe |
bd5b749c A |
261 | } |
262 | ||
263 | void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID) { | |
cf7d2af9 | 264 | __CFSpinLock(&__CFBigRuntimeFunnel); |
bd5b749c | 265 | __CFRuntimeClassTable[typeID] = NULL; |
cf7d2af9 | 266 | __CFSpinUnlock(&__CFBigRuntimeFunnel); |
bd5b749c A |
267 | } |
268 | ||
269 | ||
270 | #if defined(DEBUG) || defined(ENABLE_ZOMBIES) | |
271 | ||
272 | /* CFZombieLevel levels: | |
273 | * bit 0: scribble deallocated CF object memory | |
274 | * bit 1: do not scribble on CFRuntimeBase header (when bit 0) | |
275 | * bit 4: do not free CF objects | |
276 | * bit 7: use 3rd-order byte as scribble byte for dealloc (otherwise 0xFC) | |
277 | */ | |
278 | ||
279 | static uint32_t __CFZombieLevel = 0x0; | |
cf7d2af9 A |
280 | __private_extern__ uint8_t __CFZombieEnabled = 0; |
281 | __private_extern__ uint8_t __CFDeallocateZombies = 0; | |
282 | #if !__OBJC2__ | |
bd5b749c | 283 | static void *_original_objc_dealloc = 0; |
cf7d2af9 | 284 | #endif |
bd5b749c | 285 | |
f64f9b69 A |
286 | void _CFEnableZombies(void) { |
287 | __CFZombieEnabled = 0xFF; | |
288 | } | |
289 | ||
bd5b749c A |
290 | #endif /* DEBUG */ |
291 | ||
292 | // XXX_PCB: use the class version field as a bitmask, to allow classes to opt-in for GC scanning. | |
293 | ||
cf7d2af9 A |
294 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
295 | CF_INLINE CFOptionFlags CF_GET_COLLECTABLE_MEMORY_TYPE(const CFRuntimeClass *cls) | |
296 | { | |
297 | return ((cls->version & _kCFRuntimeScannedObject) ? __kCFAllocatorGCScannedMemory : 0) | __kCFAllocatorGCObjectMemory; | |
298 | } | |
299 | #else | |
bd5b749c | 300 | #define CF_GET_COLLECTABLE_MEMORY_TYPE(x) (0) |
cf7d2af9 | 301 | #endif |
bd5b749c A |
302 | |
303 | CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, CFIndex extraBytes, unsigned char *category) { | |
bd5b749c | 304 | CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__); |
cf7d2af9 A |
305 | CFRuntimeClass *cls = __CFRuntimeClassTable[typeID]; |
306 | if (NULL == cls) { | |
bd5b749c A |
307 | return NULL; |
308 | } | |
309 | allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; | |
cf7d2af9 A |
310 | if (kCFAllocatorNull == allocator) { |
311 | return NULL; | |
312 | } | |
313 | Boolean usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault); | |
314 | CFIndex size = sizeof(CFRuntimeBase) + extraBytes + (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef)); | |
bd5b749c A |
315 | size = (size + 0xF) & ~0xF; // CF objects are multiples of 16 in size |
316 | // CFType version 0 objects are unscanned by default since they don't have write-barriers and hard retain their innards | |
317 | // CFType version 1 objects are scanned and use hand coded write-barriers to store collectable storage within | |
cf7d2af9 | 318 | CFRuntimeBase *memory = (CFRuntimeBase *)CFAllocatorAllocate(allocator, size, CF_GET_COLLECTABLE_MEMORY_TYPE(cls)); |
bd5b749c | 319 | if (NULL == memory) { |
cf7d2af9 A |
320 | CFStringRef msg = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Attempt to allocate %ld bytes for %s failed"), size, category ? (char *)category : (char *)cls->className); |
321 | { | |
322 | CFLog(kCFLogLevelCritical, CFSTR("%@"), msg); | |
323 | HALT; | |
324 | } | |
325 | CFRelease(msg); | |
bd5b749c A |
326 | return NULL; |
327 | } | |
cf7d2af9 A |
328 | if (!kCFUseCollectableAllocator || !CF_IS_COLLECTABLE_ALLOCATOR(allocator) || !(CF_GET_COLLECTABLE_MEMORY_TYPE(cls) & __kCFAllocatorGCScannedMemory)) { |
329 | memset(memory, 0, size); | |
330 | } | |
bd5b749c A |
331 | if (__CFOASafe && category) { |
332 | __CFSetLastAllocationEventName(memory, (char *)category); | |
333 | } else if (__CFOASafe) { | |
cf7d2af9 | 334 | __CFSetLastAllocationEventName(memory, (char *)cls->className); |
bd5b749c A |
335 | } |
336 | if (!usesSystemDefaultAllocator) { | |
337 | // add space to hold allocator ref for non-standard allocators. | |
338 | // (this screws up 8 byte alignment but seems to work) | |
339 | *(CFAllocatorRef *)((char *)memory) = (CFAllocatorRef)CFRetain(allocator); | |
340 | memory = (CFRuntimeBase *)((char *)memory + sizeof(CFAllocatorRef)); | |
341 | } | |
342 | memory->_cfisa = __CFISAForTypeID(typeID); | |
343 | #if __LP64__ | |
344 | *(uint32_t *)(memory->_cfinfo) = (uint32_t)((0 << 24) + ((typeID & 0xFFFF) << 8) + (usesSystemDefaultAllocator ? 0x80 : 0x00)); | |
345 | memory->_rc = 1; | |
346 | #else | |
347 | *(uint32_t *)(memory->_cfinfo) = (uint32_t)((1 << 24) + ((typeID & 0xFFFF) << 8) + (usesSystemDefaultAllocator ? 0x80 : 0x00)); | |
348 | #endif | |
cf7d2af9 A |
349 | if (NULL != cls->init) { |
350 | (cls->init)(memory); | |
bd5b749c A |
351 | } |
352 | return memory; | |
353 | } | |
354 | ||
355 | void _CFRuntimeInitStaticInstance(void *ptr, CFTypeID typeID) { | |
bd5b749c A |
356 | CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__); |
357 | if (NULL == __CFRuntimeClassTable[typeID]) { | |
358 | return; | |
359 | } | |
cf7d2af9 | 360 | CFRuntimeBase *memory = (CFRuntimeBase *)ptr; |
bd5b749c A |
361 | memory->_cfisa = __CFISAForTypeID(typeID); |
362 | *(uint32_t *)(memory->_cfinfo) = (uint32_t)((0 << 24) + ((typeID & 0xFFFF) << 8) + 0x80); | |
363 | #if __LP64__ | |
364 | memory->_rc = 0; | |
365 | #endif | |
366 | if (NULL != __CFRuntimeClassTable[typeID]->init) { | |
367 | (__CFRuntimeClassTable[typeID]->init)(memory); | |
368 | } | |
369 | } | |
370 | ||
371 | void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID typeID) { | |
372 | *(uint16_t *)(((CFRuntimeBase *)cf)->_cfinfo + 1) = (uint16_t)(typeID & 0xFFFF); | |
373 | } | |
374 | ||
375 | __private_extern__ Boolean __CFRuntimeIsFreedObject(id anObject) { | |
376 | if (!anObject) return false; | |
377 | static Class freedClass = Nil; | |
378 | if (!freedClass) freedClass = _objc_getFreedObjectClass(); | |
379 | Class cls = object_getClass(anObject); | |
380 | if (cls == freedClass) return true; | |
381 | // in 64-bit, a future class has nil isa, and calling class_getName() on | |
382 | // such will crash so we do this test; zombie classes are not future classes | |
cf7d2af9 | 383 | if (object_getClass((id)cls) == nil) return false; |
bd5b749c A |
384 | const char *cname = class_getName(cls); |
385 | if (cname && 0 == strncmp(cname, "_NSZombie_", 10)) return true; | |
386 | return false; | |
387 | } | |
388 | ||
cf7d2af9 A |
389 | |
390 | enum { | |
391 | __kCFObjectRetainedEvent = 12, | |
392 | __kCFObjectReleasedEvent = 13 | |
393 | }; | |
394 | ||
395 | #if DEPLOYMENT_TARGET_MACOSX | |
396 | #define NUM_EXTERN_TABLES 8 | |
397 | #define EXTERN_TABLE_IDX(O) (((uintptr_t)(O) >> 8) & 0x7) | |
398 | #elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS | |
399 | #define NUM_EXTERN_TABLES 1 | |
400 | #define EXTERN_TABLE_IDX(O) 0 | |
401 | #else | |
402 | #error | |
403 | #endif | |
404 | ||
405 | // we disguise pointers so that programs like 'leaks' forget about these references | |
406 | #define DISGUISE(O) (~(uintptr_t)(O)) | |
407 | ||
408 | static struct { | |
409 | CFSpinLock_t lock; | |
410 | CFBasicHashRef table; | |
411 | uint8_t padding[64 - sizeof(CFBasicHashRef) - sizeof(CFSpinLock_t)]; | |
412 | } __NSRetainCounters[NUM_EXTERN_TABLES]; | |
413 | ||
414 | CF_EXPORT uintptr_t __CFDoExternRefOperation(uintptr_t op, id obj) { | |
415 | if (nil == obj) HALT; | |
416 | uintptr_t idx = EXTERN_TABLE_IDX(obj); | |
417 | uintptr_t disguised = DISGUISE(obj); | |
418 | #if DEPLOYMENT_TARGET_WINDOWS | |
419 | // assume threaded on windows for now | |
420 | int thr = 1; | |
421 | #else | |
422 | int thr = pthread_is_threaded_np(); | |
423 | #endif | |
424 | CFSpinLock_t *lock = &__NSRetainCounters[idx].lock; | |
425 | CFBasicHashRef table = __NSRetainCounters[idx].table; | |
426 | uintptr_t count; | |
427 | switch (op) { | |
428 | case 300: // increment | |
429 | case 350: // increment, no event | |
430 | if (thr) __CFSpinLock(lock); | |
431 | CFBasicHashAddValue(table, disguised, disguised); | |
432 | if (thr) __CFSpinUnlock(lock); | |
433 | if (__CFOASafe && op != 350) __CFRecordAllocationEvent(__kCFObjectRetainedEvent, obj, 0, 0, NULL); | |
434 | return (uintptr_t)obj; | |
435 | case 400: // decrement | |
436 | if (__CFOASafe) __CFRecordAllocationEvent(__kCFObjectReleasedEvent, obj, 0, 0, NULL); | |
437 | case 450: // decrement, no event | |
438 | if (thr) __CFSpinLock(lock); | |
439 | count = (uintptr_t)CFBasicHashRemoveValue(table, disguised); | |
440 | if (thr) __CFSpinUnlock(lock); | |
441 | return 0 == count; | |
442 | case 500: | |
443 | if (thr) __CFSpinLock(lock); | |
444 | count = (uintptr_t)CFBasicHashGetCountOfKey(table, disguised); | |
445 | if (thr) __CFSpinUnlock(lock); | |
446 | return count; | |
447 | } | |
448 | return 0; | |
449 | } | |
450 | ||
451 | ||
bd5b749c A |
452 | CFTypeID __CFGenericTypeID(const void *cf) { |
453 | return (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF; | |
454 | } | |
455 | ||
456 | CF_INLINE CFTypeID __CFGenericTypeID_inline(const void *cf) { | |
457 | return (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF; | |
458 | } | |
459 | ||
460 | CFTypeID CFTypeGetTypeID(void) { | |
461 | return __kCFTypeTypeID; | |
462 | } | |
463 | ||
464 | __private_extern__ void __CFGenericValidateType_(CFTypeRef cf, CFTypeID type, const char *func) { | |
465 | if (cf && CF_IS_OBJC(type, cf)) return; | |
466 | CFAssert2((cf != NULL) && (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]) && (__kCFNotATypeTypeID != __CFGenericTypeID_inline(cf)) && (__kCFTypeTypeID != __CFGenericTypeID_inline(cf)), __kCFLogAssertion, "%s(): pointer %p is not a CF object", func, cf); \ | |
467 | CFAssert3(__CFGenericTypeID_inline(cf) == type, __kCFLogAssertion, "%s(): pointer %p is not a %s", func, cf, __CFRuntimeClassTable[type]->className); \ | |
468 | } | |
469 | ||
470 | #define __CFGenericAssertIsCF(cf) \ | |
471 | CFAssert2(cf != NULL && (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]) && (__kCFNotATypeTypeID != __CFGenericTypeID_inline(cf)) && (__kCFTypeTypeID != __CFGenericTypeID_inline(cf)), __kCFLogAssertion, "%s(): pointer %p is not a CF object", __PRETTY_FUNCTION__, cf); | |
472 | ||
7c97c3e0 | 473 | #if 0 |
cf7d2af9 | 474 | |
bd5b749c A |
475 | #define CFTYPE_IS_OBJC(obj) (false) |
476 | #define CFTYPE_OBJC_FUNCDISPATCH0(rettype, obj, sel) do {} while (0) | |
477 | #define CFTYPE_OBJC_FUNCDISPATCH1(rettype, obj, sel, a1) do {} while (0) | |
478 | ||
7c97c3e0 A |
479 | #else |
480 | ||
481 | CF_INLINE int CFTYPE_IS_OBJC(const void *obj) { | |
482 | CFTypeID typeID = __CFGenericTypeID_inline(obj); | |
483 | return CF_IS_OBJC(typeID, obj); | |
484 | } | |
485 | ||
486 | #define CFTYPE_OBJC_FUNCDISPATCH0(rettype, obj, sel) \ | |
487 | if (CFTYPE_IS_OBJC(obj)) \ | |
488 | {rettype (*func)(void *, SEL) = (rettype (*)(void *, SEL))objc_msgSend; \ | |
489 | static SEL s = NULL; if (!s) s = sel_registerName(sel); \ | |
490 | return func((void *)obj, s);} | |
491 | #define CFTYPE_OBJC_FUNCDISPATCH1(rettype, obj, sel, a1) \ | |
492 | if (CFTYPE_IS_OBJC(obj)) \ | |
493 | {rettype (*func)(void *, SEL, ...) = (rettype (*)(void *, SEL, ...))objc_msgSend; \ | |
494 | static SEL s = NULL; if (!s) s = sel_registerName(sel); \ | |
495 | return func((void *)obj, s, (a1));} | |
496 | ||
497 | #endif | |
cf7d2af9 | 498 | |
bd5b749c A |
499 | CFTypeID CFGetTypeID(CFTypeRef cf) { |
500 | #if defined(DEBUG) | |
501 | if (NULL == cf) HALT; | |
502 | #endif | |
503 | CFTYPE_OBJC_FUNCDISPATCH0(CFTypeID, cf, "_cfTypeID"); | |
504 | __CFGenericAssertIsCF(cf); | |
505 | return __CFGenericTypeID_inline(cf); | |
506 | } | |
507 | ||
508 | CFStringRef CFCopyTypeIDDescription(CFTypeID type) { | |
509 | CFAssert2((NULL != __CFRuntimeClassTable[type]) && __kCFNotATypeTypeID != type && __kCFTypeTypeID != type, __kCFLogAssertion, "%s(): type %d is not a CF type ID", __PRETTY_FUNCTION__, type); | |
510 | return CFStringCreateWithCString(kCFAllocatorSystemDefault, __CFRuntimeClassTable[type]->className, kCFStringEncodingASCII); | |
511 | } | |
512 | ||
bd5b749c A |
513 | // Bit 31 (highest bit) in second word of cf instance indicates external ref count |
514 | ||
515 | CF_EXPORT void _CFRelease(CFTypeRef cf); | |
516 | CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf); | |
bd5b749c A |
517 | |
518 | CFTypeRef CFRetain(CFTypeRef cf) { | |
cf7d2af9 | 519 | if (NULL == cf) HALT; |
bd5b749c | 520 | if (CF_IS_COLLECTABLE(cf)) { |
cf7d2af9 A |
521 | if (CFTYPE_IS_OBJC(cf)) { |
522 | // always honor CFRetain's with a GC-visible retain. | |
523 | auto_zone_retain(auto_zone(), (void*)cf); | |
524 | return cf; | |
525 | } else { | |
526 | // special case CF objects for performance. | |
527 | return _CFRetain(cf); | |
528 | } | |
bd5b749c A |
529 | } |
530 | CFTYPE_OBJC_FUNCDISPATCH0(CFTypeRef, cf, "retain"); | |
531 | if (cf) __CFGenericAssertIsCF(cf); | |
532 | return _CFRetain(cf); | |
533 | } | |
534 | ||
535 | __private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf); | |
536 | ||
537 | void CFRelease(CFTypeRef cf) { | |
cf7d2af9 A |
538 | if (NULL == cf) HALT; |
539 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
bd5b749c | 540 | if (CF_IS_COLLECTABLE(cf)) { |
cf7d2af9 A |
541 | if (CFTYPE_IS_OBJC(cf)) { |
542 | // release the GC-visible reference. | |
543 | auto_zone_release(auto_zone(), (void*)cf); | |
544 | } else { | |
545 | // special-case CF objects for better performance. | |
546 | _CFRelease(cf); | |
bd5b749c A |
547 | } |
548 | return; | |
549 | } | |
cf7d2af9 A |
550 | #endif |
551 | #if 0 | |
552 | void **addrs[2] = {&&start, &&end}; | |
553 | start:; | |
554 | if (addrs[0] <= __builtin_return_address(0) && __builtin_return_address(0) <= addrs[1]) { | |
555 | CFLog(3, CFSTR("*** WARNING: Recursion in CFRelease(%p) : %p '%s' : 0x%08lx 0x%08lx 0x%08lx 0x%08lx 0x%08lx 0x%08lx"), cf, object_getClass(cf), object_getClassName(cf), ((uintptr_t *)cf)[0], ((uintptr_t *)cf)[1], ((uintptr_t *)cf)[2], ((uintptr_t *)cf)[3], ((uintptr_t *)cf)[4], ((uintptr_t *)cf)[5]); | |
556 | HALT; | |
557 | } | |
bd5b749c A |
558 | #endif |
559 | CFTYPE_OBJC_FUNCDISPATCH0(void, cf, "release"); | |
560 | if (cf) __CFGenericAssertIsCF(cf); | |
561 | _CFRelease(cf); | |
cf7d2af9 A |
562 | #if 0 |
563 | end:; | |
564 | #endif | |
bd5b749c A |
565 | } |
566 | ||
567 | ||
568 | __private_extern__ const void *__CFStringCollectionCopy(CFAllocatorRef allocator, const void *ptr) { | |
cf7d2af9 | 569 | if (NULL == ptr) HALT; |
bd5b749c A |
570 | CFStringRef theString = (CFStringRef)ptr; |
571 | CFStringRef result = CFStringCreateCopy(allocator, theString); | |
572 | if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
573 | result = (CFStringRef)CFMakeCollectable(result); | |
574 | } | |
575 | return (const void *)result; | |
576 | } | |
577 | ||
578 | extern void CFCollection_non_gc_storage_error(void); | |
579 | ||
580 | __private_extern__ const void *__CFTypeCollectionRetain(CFAllocatorRef allocator, const void *ptr) { | |
cf7d2af9 | 581 | if (NULL == ptr) HALT; |
bd5b749c A |
582 | CFTypeRef cf = (CFTypeRef)ptr; |
583 | // only collections allocated in the GC zone can opt-out of reference counting. | |
584 | if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
585 | if (CFTYPE_IS_OBJC(cf)) return cf; // do nothing for OBJC objects. | |
cf7d2af9 | 586 | if (auto_zone_is_valid_pointer(auto_zone(), ptr)) { |
bd5b749c A |
587 | CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]; |
588 | if (cfClass->version & _kCFRuntimeResourcefulObject) { | |
589 | // GC: If this a CF object in the GC heap that is marked resourceful, then | |
590 | // it must be retained keep it alive in a CF collection. | |
cf7d2af9 A |
591 | // We're basically inlining CFRetain() here, to avoid extra heap membership tests. |
592 | _CFRetain(cf); | |
bd5b749c A |
593 | } |
594 | else | |
595 | ; // don't retain normal CF objects | |
596 | return cf; | |
597 | } else { | |
598 | // support constant CFTypeRef objects. | |
599 | #if __LP64__ | |
600 | uint32_t lowBits = ((CFRuntimeBase *)cf)->_rc; | |
601 | #else | |
602 | uint32_t lowBits = ((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS]; | |
603 | #endif | |
604 | if (lowBits == 0) return cf; | |
605 | // complain about non-GC objects in GC containers. | |
606 | CFLog(kCFLogLevelWarning, CFSTR("storing a non-GC object %p in a GC collection, break on CFCollection_non_gc_storage_error to debug."), cf); | |
607 | CFCollection_non_gc_storage_error(); | |
608 | // XXX should halt, except Patrick is using this somewhere. | |
609 | // HALT; | |
610 | } | |
611 | } | |
612 | return CFRetain(cf); | |
613 | } | |
614 | ||
615 | ||
616 | __private_extern__ void __CFTypeCollectionRelease(CFAllocatorRef allocator, const void *ptr) { | |
cf7d2af9 | 617 | if (NULL == ptr) HALT; |
bd5b749c A |
618 | CFTypeRef cf = (CFTypeRef)ptr; |
619 | // only collections allocated in the GC zone can opt-out of reference counting. | |
620 | if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
621 | if (CFTYPE_IS_OBJC(cf)) return; // do nothing for OBJC objects. | |
cf7d2af9 A |
622 | if (auto_zone_is_valid_pointer(auto_zone(), cf)) { |
623 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
bd5b749c A |
624 | // GC: If this a CF object in the GC heap that is marked uncollectable, then |
625 | // must balance the retain done in __CFTypeCollectionRetain(). | |
cf7d2af9 | 626 | // We're basically inlining CFRelease() here, to avoid extra heap membership tests. |
bd5b749c | 627 | CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]; |
cf7d2af9 A |
628 | if (cfClass->version & _kCFRuntimeResourcefulObject) { |
629 | // reclaim is called by _CFRelease(), which must be called to keep the | |
630 | // CF and GC retain counts in sync. | |
631 | _CFRelease(cf); | |
632 | } else { | |
633 | // avoid releasing normal CF objects. Like other collections, for example | |
bd5b749c | 634 | } |
bd5b749c A |
635 | return; |
636 | #endif | |
637 | } else { | |
638 | // support constant CFTypeRef objects. | |
639 | #if __LP64__ | |
640 | uint32_t lowBits = ((CFRuntimeBase *)cf)->_rc; | |
641 | #else | |
642 | uint32_t lowBits = ((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS]; | |
643 | #endif | |
644 | if (lowBits == 0) return; | |
645 | } | |
646 | } | |
647 | CFRelease(cf); | |
648 | } | |
649 | ||
650 | #if !__LP64__ | |
651 | static CFSpinLock_t __CFRuntimeExternRefCountTableLock = CFSpinLockInit; | |
652 | static CFMutableBagRef __CFRuntimeExternRefCountTable = NULL; | |
653 | #endif | |
654 | ||
655 | static uint64_t __CFGetFullRetainCount(CFTypeRef cf) { | |
cf7d2af9 | 656 | if (NULL == cf) HALT; |
bd5b749c A |
657 | #if __LP64__ |
658 | uint32_t lowBits = ((CFRuntimeBase *)cf)->_rc; | |
659 | if (0 == lowBits) { | |
660 | return (uint64_t)0x0fffffffffffffffULL; | |
661 | } | |
662 | return lowBits; | |
663 | #else | |
664 | uint32_t lowBits = ((CFRuntimeBase *)cf)->_cfinfo[CF_RC_BITS]; | |
665 | if (0 == lowBits) { | |
666 | return (uint64_t)0x0fffffffffffffffULL; | |
667 | } | |
668 | uint64_t highBits = 0; | |
669 | if ((lowBits & 0x80) != 0) { | |
cf7d2af9 | 670 | highBits = __CFDoExternRefOperation(500, (id)cf); |
bd5b749c A |
671 | } |
672 | uint64_t compositeRC = (lowBits & 0x7f) + (highBits << 6); | |
673 | return compositeRC; | |
674 | #endif | |
675 | } | |
676 | ||
677 | CFTypeRef _CFRetainGC(CFTypeRef cf) { | |
678 | #if defined(DEBUG) | |
cf7d2af9 | 679 | if (kCFUseCollectableAllocator && !CF_IS_COLLECTABLE(cf)) { |
bd5b749c A |
680 | fprintf(stderr, "non-auto object %p passed to _CFRetainGC.\n", cf); |
681 | HALT; | |
682 | } | |
683 | #endif | |
cf7d2af9 | 684 | return kCFUseCollectableAllocator ? cf : CFRetain(cf); |
bd5b749c A |
685 | } |
686 | ||
687 | void _CFReleaseGC(CFTypeRef cf) { | |
688 | #if defined(DEBUG) | |
cf7d2af9 | 689 | if (kCFUseCollectableAllocator && !CF_IS_COLLECTABLE(cf)) { |
bd5b749c A |
690 | fprintf(stderr, "non-auto object %p passed to _CFReleaseGC.\n", cf); |
691 | HALT; | |
692 | } | |
693 | #endif | |
cf7d2af9 | 694 | if (!kCFUseCollectableAllocator) CFRelease(cf); |
bd5b749c A |
695 | } |
696 | ||
697 | CFIndex CFGetRetainCount(CFTypeRef cf) { | |
cf7d2af9 | 698 | if (NULL == cf) HALT; |
bd5b749c | 699 | if (CF_IS_COLLECTABLE(cf)) { |
cf7d2af9 A |
700 | if (CFTYPE_IS_OBJC(cf)) return auto_zone_retain_count(auto_zone(), cf); |
701 | } else { | |
702 | CFTYPE_OBJC_FUNCDISPATCH0(CFIndex, cf, "retainCount"); | |
703 | __CFGenericAssertIsCF(cf); | |
bd5b749c | 704 | } |
bd5b749c A |
705 | uint64_t rc = __CFGetFullRetainCount(cf); |
706 | return (rc < (uint64_t)LONG_MAX) ? (CFIndex)rc : (CFIndex)LONG_MAX; | |
707 | } | |
708 | ||
709 | CFTypeRef CFMakeCollectable(CFTypeRef cf) { | |
710 | if (NULL == cf) return NULL; | |
711 | if (CF_IS_COLLECTABLE(cf)) { | |
cf7d2af9 | 712 | objc_assertRegisteredThreadWithCollector(); |
bd5b749c A |
713 | #if defined(DEBUG) |
714 | CFAllocatorRef allocator = CFGetAllocator(cf); | |
715 | if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { | |
716 | CFLog(kCFLogLevelWarning, CFSTR("object %p with non-GC allocator %p passed to CFMakeCollectable."), cf, allocator); | |
717 | HALT; | |
718 | } | |
719 | #endif | |
720 | if (!CFTYPE_IS_OBJC(cf)) { | |
721 | CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]; | |
722 | if (cfClass->version & (_kCFRuntimeResourcefulObject)) { | |
723 | // don't allow the collector to manage uncollectable objects. | |
724 | CFLog(kCFLogLevelWarning, CFSTR("uncollectable object %p passed to CFMakeCollectable."), cf); | |
725 | HALT; | |
726 | } | |
727 | } | |
cf7d2af9 | 728 | if (CFGetRetainCount(cf) == 0) { |
bd5b749c A |
729 | CFLog(kCFLogLevelWarning, CFSTR("object %p with 0 retain-count passed to CFMakeCollectable."), cf); |
730 | return cf; | |
731 | } | |
cf7d2af9 A |
732 | CFRelease(cf); |
733 | } | |
734 | return cf; | |
735 | } | |
736 | ||
737 | CFTypeRef CFMakeUncollectable(CFTypeRef cf) { | |
738 | if (NULL == cf) return NULL; | |
739 | if (CF_IS_COLLECTABLE(cf)) { | |
740 | CFRetain(cf); | |
bd5b749c A |
741 | } |
742 | return cf; | |
743 | } | |
744 | ||
745 | Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2) { | |
bd5b749c A |
746 | if (NULL == cf1) HALT; |
747 | if (NULL == cf2) HALT; | |
bd5b749c A |
748 | if (cf1 == cf2) return true; |
749 | CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf1, "isEqual:", cf2); | |
750 | CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf2, "isEqual:", cf1); | |
751 | __CFGenericAssertIsCF(cf1); | |
752 | __CFGenericAssertIsCF(cf2); | |
753 | if (__CFGenericTypeID_inline(cf1) != __CFGenericTypeID_inline(cf2)) return false; | |
754 | if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal) { | |
755 | return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal(cf1, cf2); | |
756 | } | |
757 | return false; | |
758 | } | |
759 | ||
760 | CFHashCode CFHash(CFTypeRef cf) { | |
cf7d2af9 | 761 | if (NULL == cf) HALT; |
bd5b749c A |
762 | CFTYPE_OBJC_FUNCDISPATCH0(CFHashCode, cf, "hash"); |
763 | __CFGenericAssertIsCF(cf); | |
cf7d2af9 A |
764 | if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash) { |
765 | return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash(cf); | |
766 | } | |
767 | return (CFHashCode)cf; | |
bd5b749c A |
768 | } |
769 | ||
770 | // definition: produces a normally non-NULL debugging description of the object | |
771 | CFStringRef CFCopyDescription(CFTypeRef cf) { | |
bd5b749c | 772 | if (NULL == cf) HALT; |
cf7d2af9 A |
773 | if (CFTYPE_IS_OBJC(cf)) { |
774 | static SEL s = NULL; | |
775 | CFStringRef (*func)(void *, SEL) = (CFStringRef (*)(void *, SEL))objc_msgSend; | |
776 | if (!s) s = sel_registerName("_copyDescription"); | |
777 | CFStringRef result = func((void *)cf, s); | |
778 | return result; | |
779 | } | |
780 | // CFTYPE_OBJC_FUNCDISPATCH0(CFStringRef, cf, "_copyDescription"); // XXX returns 0 refcounted item under GC | |
bd5b749c A |
781 | __CFGenericAssertIsCF(cf); |
782 | if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyDebugDesc) { | |
783 | CFStringRef result; | |
784 | result = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyDebugDesc(cf); | |
785 | if (NULL != result) return result; | |
786 | } | |
787 | return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<%s %p [%p]>"), __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->className, cf, CFGetAllocator(cf)); | |
788 | } | |
789 | ||
790 | // Definition: if type produces a formatting description, return that string, otherwise NULL | |
791 | __private_extern__ CFStringRef __CFCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { | |
bd5b749c | 792 | if (NULL == cf) HALT; |
cf7d2af9 A |
793 | if (CFTYPE_IS_OBJC(cf)) { |
794 | static SEL s = NULL, r = NULL; | |
795 | CFStringRef (*func)(void *, SEL, CFDictionaryRef) = (CFStringRef (*)(void *, SEL, CFDictionaryRef))objc_msgSend; | |
796 | BOOL (*rfunc)(void *, SEL, SEL) = (BOOL (*)(void *, SEL, SEL))objc_msgSend; | |
797 | if (!s) s = sel_registerName("_copyFormattingDescription:"); | |
798 | if (!r) r = sel_registerName("respondsToSelector:"); | |
799 | if (s && rfunc((void *)cf, r, s)) return func((void *)cf, s, formatOptions); | |
800 | return NULL; | |
801 | } | |
bd5b749c A |
802 | __CFGenericAssertIsCF(cf); |
803 | if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyFormattingDesc) { | |
804 | return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyFormattingDesc(cf, formatOptions); | |
805 | } | |
806 | return NULL; | |
807 | } | |
808 | ||
809 | extern CFAllocatorRef __CFAllocatorGetAllocator(CFTypeRef); | |
810 | ||
811 | CFAllocatorRef CFGetAllocator(CFTypeRef cf) { | |
812 | if (NULL == cf) return kCFAllocatorSystemDefault; | |
cf7d2af9 A |
813 | // CF: need to get allocator from objc objects in better way...how? |
814 | // -> bridging of CFAllocators and malloc_zone_t will help this | |
815 | if (CFTYPE_IS_OBJC(cf)) return __CFGetDefaultAllocator(); | |
bd5b749c A |
816 | if (__kCFAllocatorTypeID_CONST == __CFGenericTypeID_inline(cf)) { |
817 | return __CFAllocatorGetAllocator(cf); | |
818 | } | |
819 | return __CFGetAllocator(cf); | |
820 | } | |
821 | ||
822 | extern void __CFBaseInitialize(void); | |
823 | extern void __CFNullInitialize(void); | |
824 | extern void __CFAllocatorInitialize(void); | |
825 | extern void __CFStringInitialize(void); | |
826 | extern void __CFArrayInitialize(void); | |
bd5b749c A |
827 | extern void __CFBooleanInitialize(void); |
828 | extern void __CFCharacterSetInitialize(void); | |
829 | extern void __CFDataInitialize(void); | |
bd5b749c | 830 | extern void __CFNumberInitialize(void); |
bd5b749c A |
831 | extern void __CFStorageInitialize(void); |
832 | extern void __CFErrorInitialize(void); | |
bd5b749c A |
833 | extern void __CFTreeInitialize(void); |
834 | extern void __CFURLInitialize(void); | |
cf7d2af9 | 835 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
bd5b749c A |
836 | extern void __CFMachPortInitialize(void); |
837 | #endif | |
bd5b749c | 838 | extern void __CFMessagePortInitialize(void); |
bd5b749c A |
839 | extern void __CFRunLoopInitialize(void); |
840 | extern void __CFRunLoopObserverInitialize(void); | |
841 | extern void __CFRunLoopSourceInitialize(void); | |
842 | extern void __CFRunLoopTimerInitialize(void); | |
bd5b749c A |
843 | extern void __CFBundleInitialize(void); |
844 | extern void __CFPlugInInitialize(void); | |
845 | extern void __CFPlugInInstanceInitialize(void); | |
846 | extern void __CFUUIDInitialize(void); | |
847 | extern void __CFBinaryHeapInitialize(void); | |
848 | extern void __CFBitVectorInitialize(void); | |
cf7d2af9 | 849 | #if DEPLOYMENT_TARGET_WINDOWS |
f64f9b69 A |
850 | extern void __CFWindowsMessageQueueInitialize(void); |
851 | extern void __CFWindowsNamedPipeInitialize(void); | |
cf7d2af9 A |
852 | extern void __CFBaseCleanup(void); |
853 | #endif | |
bd5b749c A |
854 | extern void __CFStreamInitialize(void); |
855 | ||
cf7d2af9 A |
856 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
857 | __private_extern__ uint8_t __CF120290 = false; | |
858 | __private_extern__ uint8_t __CF120291 = false; | |
859 | __private_extern__ uint8_t __CF120293 = false; | |
860 | __private_extern__ char * __crashreporter_info__ = NULL; | |
861 | asm(".desc ___crashreporter_info__, 0x10"); | |
862 | ||
863 | static void __01121__(void) { | |
864 | __CF120291 = pthread_is_threaded_np() ? true : false; | |
865 | } | |
866 | ||
867 | static void __01123__(void) { | |
868 | // Ideally, child-side atfork handlers should be async-cancel-safe, as fork() | |
869 | // is async-cancel-safe and can be called from signal handlers. See also | |
870 | // http://standards.ieee.org/reading/ieee/interp/1003-1c-95_int/pasc-1003.1c-37.html | |
871 | // This is not a problem for CF. | |
872 | if (__CF120290) { | |
873 | __CF120293 = true; | |
874 | if (__CF120291) { | |
875 | __crashreporter_info__ = "*** multi-threaded process forked ***"; | |
876 | } else { | |
877 | __crashreporter_info__ = "*** single-threaded process forked ***"; | |
878 | } | |
879 | } | |
880 | } | |
881 | ||
882 | #define EXEC_WARNING_STRING_1 "The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec().\n" | |
883 | #define EXEC_WARNING_STRING_2 "Break on __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__() to debug.\n" | |
884 | ||
885 | __private_extern__ void __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__(void) { | |
886 | write(2, EXEC_WARNING_STRING_1, sizeof(EXEC_WARNING_STRING_1) - 1); | |
887 | write(2, EXEC_WARNING_STRING_2, sizeof(EXEC_WARNING_STRING_2) - 1); | |
888 | // HALT; | |
889 | } | |
890 | #endif | |
891 | ||
892 | ||
893 | CF_EXPORT const void *__CFArgStuff; | |
bd5b749c A |
894 | const void *__CFArgStuff = NULL; |
895 | __private_extern__ void *__CFAppleLanguages = NULL; | |
896 | ||
cf7d2af9 A |
897 | static struct { |
898 | const char *name; | |
899 | const char *value; | |
900 | } __CFEnv[] = { | |
901 | {"PATH", NULL}, | |
902 | {"HOME", NULL}, | |
903 | {"USER", NULL}, | |
904 | {"HOMEPATH", NULL}, | |
905 | {"HOMEDRIVE", NULL}, | |
906 | {"USERNAME", NULL}, | |
907 | {"TZFILE", NULL}, | |
908 | {"TZ", NULL}, | |
909 | {"NEXT_ROOT", NULL}, | |
910 | {"DYLD_IMAGE_SUFFIX", NULL}, | |
911 | {"CFProcessPath", NULL}, | |
912 | {"CFFIXED_USER_HOME", NULL}, | |
913 | {"CFNETWORK_LIBRARY_PATH", NULL}, | |
914 | {"CFUUIDVersionNumber", NULL}, | |
915 | {"CFDebugNamedDataSharing", NULL}, | |
916 | {"CFPropertyListAllowImmutableCollections", NULL}, | |
917 | {"CFBundleUseDYLD", NULL}, | |
918 | {"CFBundleDisableStringsSharing", NULL}, | |
919 | {"CFCharacterSetCheckForExpandedSet", NULL}, | |
920 | {"__CF_DEBUG_EXPANDED_SET", NULL}, | |
921 | {"CFStringDisableROM", NULL}, | |
922 | {"CF_CHARSET_PATH", NULL}, | |
923 | {"__CF_USER_TEXT_ENCODING", NULL}, | |
924 | {NULL, NULL}, // the last one is for optional "COMMAND_MODE" "legacy", do not use this slot, insert before | |
925 | }; | |
926 | ||
927 | __private_extern__ const char *__CFgetenv(const char *n) { | |
928 | for (CFIndex idx = 0; idx < sizeof(__CFEnv) / sizeof(__CFEnv[0]); idx++) { | |
929 | if (__CFEnv[idx].name && 0 == strcmp(n, __CFEnv[idx].name)) return __CFEnv[idx].value; | |
930 | } | |
931 | return getenv(n); | |
932 | } | |
933 | ||
934 | #if DEPLOYMENT_TARGET_WINDOWS | |
935 | #define kNilPthreadT { nil, nil } | |
936 | #else | |
937 | #define kNilPthreadT (pthread_t)0 | |
938 | #endif | |
939 | ||
940 | ||
941 | CF_EXPORT pthread_t _CFMainPThread; | |
942 | pthread_t _CFMainPThread = kNilPthreadT; | |
bd5b749c | 943 | |
cf7d2af9 A |
944 | CF_EXPORT bool kCFUseCollectableAllocator = false; |
945 | ||
946 | __private_extern__ Boolean __CFProphylacticAutofsAccess = false; | |
bd5b749c A |
947 | |
948 | #if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD | |
949 | static void __CFInitialize(void) __attribute__ ((constructor)); | |
950 | static | |
951 | #endif | |
cf7d2af9 A |
952 | #if DEPLOYMENT_TARGET_WINDOWS |
953 | CF_EXPORT | |
954 | #endif | |
bd5b749c A |
955 | void __CFInitialize(void) { |
956 | static int __done = 0; | |
957 | ||
958 | if (!__done) { | |
959 | __done = 1; | |
960 | ||
cf7d2af9 A |
961 | if (!pthread_main_np()) HALT; // CoreFoundation must be initialized on the main thread |
962 | ||
963 | _CFMainPThread = pthread_self(); | |
964 | ||
965 | __CFProphylacticAutofsAccess = true; | |
966 | ||
967 | for (CFIndex idx = 0; idx < sizeof(__CFEnv) / sizeof(__CFEnv[0]); idx++) { | |
968 | __CFEnv[idx].value = __CFEnv[idx].name ? getenv(__CFEnv[idx].name) : NULL; | |
969 | } | |
970 | ||
971 | kCFUseCollectableAllocator = objc_collectingEnabled(); | |
972 | if (kCFUseCollectableAllocator) { | |
973 | __CFObjCIsCollectable = (bool (*)(void *))objc_isAuto; | |
974 | } | |
975 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
976 | UInt32 s, r; | |
977 | __CFStringGetUserDefaultEncoding(&s, &r); // force the potential setenv to occur early | |
978 | pthread_atfork(__01121__, NULL, __01123__); | |
979 | const char *value2 = __CFgetenv("NSObjCMessageLoggingEnabled"); | |
980 | if (value2 && (*value2 == 'Y' || *value2 == 'y')) instrumentObjcMessageSends(1); | |
981 | #elif DEPLOYMENT_TARGET_WINDOWS | |
982 | #else | |
983 | #endif | |
984 | ||
bd5b749c | 985 | #if defined(DEBUG) || defined(ENABLE_ZOMBIES) |
cf7d2af9 | 986 | const char *value = __CFgetenv("NSZombieEnabled"); |
bd5b749c | 987 | if (value && (*value == 'Y' || *value == 'y')) __CFZombieEnabled = 0xff; |
cf7d2af9 | 988 | value = __CFgetenv("NSDeallocateZombies"); |
bd5b749c | 989 | if (value && (*value == 'Y' || *value == 'y')) __CFDeallocateZombies = 0xff; |
cf7d2af9 A |
990 | #if !__OBJC2__ |
991 | _original_objc_dealloc = (void *)_dealloc; | |
992 | #endif | |
bd5b749c | 993 | |
cf7d2af9 | 994 | value = __CFgetenv("CFZombieLevel"); |
bd5b749c A |
995 | if (NULL != value) { |
996 | __CFZombieLevel = (uint32_t)strtoul_l(value, NULL, 0, NULL); | |
997 | } | |
998 | if (0x0 == __CFZombieLevel) __CFZombieLevel = 0x0000FC00; // default | |
cf7d2af9 | 999 | |
bd5b749c A |
1000 | #endif |
1001 | ||
1002 | __CFRuntimeClassTableSize = 1024; | |
1003 | __CFRuntimeClassTable = (CFRuntimeClass **)calloc(__CFRuntimeClassTableSize, sizeof(CFRuntimeClass *)); | |
cf7d2af9 | 1004 | __CFRuntimeObjCClassTable = (uintptr_t *)calloc(__CFRuntimeClassTableSize, sizeof(uintptr_t)); |
bd5b749c A |
1005 | __CFBaseInitialize(); |
1006 | ||
cf7d2af9 A |
1007 | _CFRuntimeBridgeClasses(0, "__NSCFType"); |
1008 | for (CFIndex idx = 1; idx < __CFRuntimeClassTableSize; idx++) { | |
1009 | __CFRuntimeObjCClassTable[idx] = __CFRuntimeObjCClassTable[0]; | |
1010 | } | |
1011 | ||
bd5b749c A |
1012 | /* Here so that two runtime classes get indices 0, 1. */ |
1013 | __kCFNotATypeTypeID = _CFRuntimeRegisterClass(&__CFNotATypeClass); | |
1014 | __kCFTypeTypeID = _CFRuntimeRegisterClass(&__CFTypeClass); | |
1015 | ||
1016 | /* Here so that __kCFAllocatorTypeID gets index 2. */ | |
1017 | __CFAllocatorInitialize(); | |
1018 | ||
cf7d2af9 | 1019 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
bd5b749c A |
1020 | { |
1021 | CFIndex idx, cnt; | |
1022 | char **args = *_NSGetArgv(); | |
1023 | cnt = *_NSGetArgc(); | |
1024 | for (idx = 1; idx < cnt - 1; idx++) { | |
1025 | if (NULL == args[idx]) continue; | |
1026 | if (0 == strcmp(args[idx], "-AppleLanguages") && args[idx + 1]) { | |
1027 | CFIndex length = strlen(args[idx + 1]); | |
1028 | __CFAppleLanguages = malloc(length + 1); | |
1029 | memmove(__CFAppleLanguages, args[idx + 1], length + 1); | |
1030 | break; | |
1031 | } | |
1032 | } | |
1033 | } | |
1034 | #endif | |
1035 | ||
1036 | ||
cf7d2af9 A |
1037 | CFBasicHashGetTypeID(); |
1038 | CFBagGetTypeID(); | |
bd5b749c A |
1039 | #if !__LP64__ |
1040 | // Creating this lazily in CFRetain causes recursive call to CFRetain | |
1041 | __CFRuntimeExternRefCountTable = CFBagCreateMutable(kCFAllocatorSystemDefault, 0, NULL); | |
1042 | #endif | |
1043 | ||
cf7d2af9 A |
1044 | for (CFIndex idx = 0; idx < NUM_EXTERN_TABLES; idx++) { |
1045 | __NSRetainCounters[idx].table = CFBasicHashCreate(kCFAllocatorSystemDefault, kCFBasicHashHasCounts | kCFBasicHashLinearHashing | kCFBasicHashAggressiveGrowth, &CFBasicHashNullCallbacks); | |
1046 | CFBasicHashSetCapacity(__NSRetainCounters[idx].table, 40); | |
1047 | __NSRetainCounters[idx].lock = CFSpinLockInit; | |
1048 | } | |
1049 | ||
bd5b749c A |
1050 | /*** _CFRuntimeCreateInstance() can finally be called generally after this line. ***/ |
1051 | ||
1052 | __CFRuntimeClassTableCount = 7; | |
1053 | __CFStringInitialize(); // CFString's TypeID must be 0x7, now and forever | |
1054 | __CFRuntimeClassTableCount = 16; | |
cf7d2af9 A |
1055 | CFSetGetTypeID(); // See above for hard-coding of this position |
1056 | CFDictionaryGetTypeID(); // See above for hard-coding of this position | |
1057 | __CFArrayInitialize(); // See above for hard-coding of this position | |
1058 | __CFDataInitialize(); // See above for hard-coding of this position | |
bd5b749c A |
1059 | __CFNullInitialize(); // See above for hard-coding of this position |
1060 | __CFBooleanInitialize(); // See above for hard-coding of this position | |
1061 | __CFNumberInitialize(); // See above for hard-coding of this position | |
1062 | ||
bd5b749c A |
1063 | __CFBinaryHeapInitialize(); |
1064 | __CFBitVectorInitialize(); | |
1065 | __CFCharacterSetInitialize(); | |
1066 | __CFStorageInitialize(); | |
1067 | __CFErrorInitialize(); | |
1068 | __CFTreeInitialize(); | |
1069 | __CFURLInitialize(); | |
1070 | __CFBundleInitialize(); | |
cf7d2af9 | 1071 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED |
bd5b749c A |
1072 | __CFPlugInInitialize(); |
1073 | __CFPlugInInstanceInitialize(); | |
bd5b749c | 1074 | #endif |
cf7d2af9 A |
1075 | __CFUUIDInitialize(); |
1076 | __CFMessagePortInitialize(); | |
1077 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
bd5b749c A |
1078 | __CFMachPortInitialize(); |
1079 | #endif | |
1080 | __CFStreamInitialize(); | |
bd5b749c A |
1081 | __CFRunLoopInitialize(); |
1082 | __CFRunLoopObserverInitialize(); | |
1083 | __CFRunLoopSourceInitialize(); | |
1084 | __CFRunLoopTimerInitialize(); | |
bd5b749c A |
1085 | |
1086 | ||
bd5b749c A |
1087 | { |
1088 | CFIndex idx, cnt; | |
cf7d2af9 A |
1089 | char **args; |
1090 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
bd5b749c A |
1091 | args = *_NSGetArgv(); |
1092 | cnt = *_NSGetArgc(); | |
cf7d2af9 A |
1093 | #elif DEPLOYMENT_TARGET_WINDOWS |
1094 | wchar_t *commandLine = GetCommandLineW(); | |
1095 | wchar_t **wideArgs = CommandLineToArgvW(commandLine, (int *)&cnt); | |
1096 | args = (char **)malloc(sizeof(char *) * cnt); | |
1097 | for (int y=0; y < cnt; y++) { | |
1098 | int bufSize = lstrlenW(wideArgs[y]) + 20; | |
1099 | char *arg = (char *)malloc(sizeof(char) * bufSize); | |
1100 | int res = WideCharToMultiByte(CP_ACP, 1024 /*WC_NO_BEST_FIT_CHARS*/, wideArgs[y], -1, arg, bufSize, NULL, NULL); | |
1101 | if (!res) | |
1102 | printf("CF - Error converting command line arg string to ascii: %x\n", (unsigned int)wideArgs[y]); | |
1103 | args[y] = arg; | |
1104 | } | |
1105 | #endif | |
bd5b749c A |
1106 | CFIndex count; |
1107 | CFStringRef *list, buffer[256]; | |
cf7d2af9 | 1108 | list = (cnt <= 256) ? buffer : (CFStringRef *)malloc(cnt * sizeof(CFStringRef)); |
bd5b749c A |
1109 | for (idx = 0, count = 0; idx < cnt; idx++) { |
1110 | if (NULL == args[idx]) continue; | |
1111 | list[count] = CFStringCreateWithCString(kCFAllocatorSystemDefault, args[idx], kCFStringEncodingUTF8); | |
1112 | if (NULL == list[count]) { | |
1113 | list[count] = CFStringCreateWithCString(kCFAllocatorSystemDefault, args[idx], kCFStringEncodingISOLatin1); | |
1114 | // We CANNOT use the string SystemEncoding here; | |
1115 | // Do not argue: it is not initialized yet, but these | |
1116 | // arguments MUST be initialized before it is. | |
1117 | // We should just ignore the argument if the UTF-8 | |
1118 | // conversion fails, but out of charity we try once | |
1119 | // more with ISO Latin1, a standard unix encoding. | |
1120 | } | |
1121 | if (NULL != list[count]) count++; | |
1122 | } | |
1123 | __CFArgStuff = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)list, count, &kCFTypeArrayCallBacks); | |
1124 | } | |
cf7d2af9 | 1125 | |
bd5b749c A |
1126 | _CFProcessPath(); // cache this early |
1127 | ||
cf7d2af9 A |
1128 | |
1129 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
1130 | __CFOAInitialize(); | |
1131 | #endif | |
1132 | ||
bd5b749c | 1133 | if (__CFRuntimeClassTableCount < 256) __CFRuntimeClassTableCount = 256; |
cf7d2af9 A |
1134 | __CFSendObjCMsg = (void *(*)(const void *, SEL, ...))objc_msgSend; |
1135 | ||
1136 | #if DEPLOYMENT_TARGET_MACOSX | |
1137 | #elif DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_EMBEDDED | |
1138 | #else | |
1139 | #error | |
1140 | #endif | |
1141 | ||
1142 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
1143 | if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) { | |
1144 | setenv("COMMAND_MODE", "legacy", 1); | |
1145 | __CFEnv[sizeof(__CFEnv) / sizeof(__CFEnv[0]) - 1].name = "COMMAND_MODE"; | |
1146 | __CFEnv[sizeof(__CFEnv) / sizeof(__CFEnv[0]) - 1].value = "legacy"; | |
1147 | } | |
1148 | #elif DEPLOYMENT_TARGET_WINDOWS | |
1149 | #else | |
1150 | #error | |
1151 | #endif | |
bd5b749c | 1152 | |
cf7d2af9 | 1153 | #if defined(DEBUG) && (DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED) |
bd5b749c A |
1154 | CFLog(kCFLogLevelWarning, CFSTR("Assertions enabled")); |
1155 | #endif | |
cf7d2af9 A |
1156 | |
1157 | __CFProphylacticAutofsAccess = false; | |
bd5b749c A |
1158 | } |
1159 | } | |
1160 | ||
bd5b749c | 1161 | |
cf7d2af9 A |
1162 | #if DEPLOYMENT_TARGET_WINDOWS |
1163 | ||
1164 | static CFBundleRef RegisterCoreFoundationBundle(void) { | |
1165 | #ifdef _DEBUG | |
1166 | // might be nice to get this from the project file at some point | |
1167 | wchar_t *DLLFileName = (wchar_t *)L"CoreFoundation_debug.dll"; | |
1168 | #else | |
1169 | wchar_t *DLLFileName = (wchar_t *)L"CoreFoundation.dll"; | |
1170 | #endif | |
1171 | wchar_t path[MAX_PATH+1]; | |
1172 | path[0] = path[1] = 0; | |
1173 | DWORD wResult; | |
1174 | CFIndex idx; | |
1175 | HMODULE ourModule = GetModuleHandleW(DLLFileName); | |
1176 | ||
1177 | CFAssert(ourModule, __kCFLogAssertion, "GetModuleHandle failed"); | |
1178 | ||
1179 | wResult = GetModuleFileNameW(ourModule, path, MAX_PATH+1); | |
1180 | CFAssert1(wResult > 0, __kCFLogAssertion, "GetModuleFileName failed: %d", GetLastError()); | |
1181 | CFAssert1(wResult < MAX_PATH+1, __kCFLogAssertion, "GetModuleFileName result truncated: %s", path); | |
1182 | ||
1183 | // strip off last component, the DLL name | |
1184 | for (idx = wResult - 1; idx; idx--) { | |
1185 | if ('\\' == path[idx]) { | |
1186 | path[idx] = '\0'; | |
1187 | break; | |
1188 | } | |
1189 | } | |
bd5b749c | 1190 | |
cf7d2af9 A |
1191 | CFStringRef fsPath = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar*)path, idx); |
1192 | CFURLRef dllURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, fsPath, kCFURLWindowsPathStyle, TRUE); | |
1193 | CFURLRef bundleURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, dllURL, CFSTR("CoreFoundation.resources"), TRUE); | |
1194 | CFRelease(fsPath); | |
1195 | CFRelease(dllURL); | |
bd5b749c | 1196 | |
cf7d2af9 A |
1197 | // this registers us so subsequent calls to CFBundleGetBundleWithIdentifier will succeed |
1198 | CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL); | |
1199 | CFRelease(bundleURL); | |
bd5b749c | 1200 | |
cf7d2af9 | 1201 | return bundle; |
bd5b749c A |
1202 | } |
1203 | ||
cf7d2af9 A |
1204 | |
1205 | #define DLL_PROCESS_ATTACH 1 | |
1206 | #define DLL_THREAD_ATTACH 2 | |
1207 | #define DLL_THREAD_DETACH 3 | |
1208 | #define DLL_PROCESS_DETACH 0 | |
1209 | ||
1210 | int DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) { | |
bd5b749c A |
1211 | static CFBundleRef cfBundle = NULL; |
1212 | if (dwReason == DLL_PROCESS_ATTACH) { | |
1213 | __CFInitialize(); | |
1214 | cfBundle = RegisterCoreFoundationBundle(); | |
1215 | } else if (dwReason == DLL_PROCESS_DETACH) { | |
cf7d2af9 | 1216 | __CFStreamCleanup(); |
bd5b749c | 1217 | __CFSocketCleanup(); |
bd5b749c | 1218 | __CFUniCharCleanup(); |
bd5b749c | 1219 | __CFBaseCleanup(); |
cf7d2af9 A |
1220 | // do these last |
1221 | if (cfBundle) CFRelease(cfBundle); | |
1222 | __CFStringCleanup(); | |
bd5b749c A |
1223 | } else if (dwReason == DLL_THREAD_DETACH) { |
1224 | __CFFinalizeThreadData(NULL); | |
1225 | } | |
1226 | return TRUE; | |
1227 | } | |
1228 | ||
1229 | #endif | |
1230 | ||
bd5b749c A |
1231 | CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf) { |
1232 | if (NULL == cf) return NULL; | |
cf7d2af9 | 1233 | Boolean didAuto = false; |
bd5b749c | 1234 | #if __LP64__ |
cf7d2af9 | 1235 | if (0 == ((CFRuntimeBase *)cf)->_rc && !CF_IS_COLLECTABLE(cf)) return cf; // Constant CFTypeRef |
bd5b749c A |
1236 | uint32_t lowBits; |
1237 | do { | |
1238 | lowBits = ((CFRuntimeBase *)cf)->_rc; | |
cf7d2af9 A |
1239 | } while (!OSAtomicCompareAndSwap32Barrier(lowBits, lowBits + 1, (int32_t *)&((CFRuntimeBase *)cf)->_rc)); |
1240 | // GC: 0 --> 1 transition? then add a GC retain count, to root the object. we'll remove it on the 1 --> 0 transition. | |
1241 | if (lowBits == 0 && CF_IS_COLLECTABLE(cf)) { | |
1242 | auto_zone_retain(auto_zone(), (void*)cf); | |
1243 | didAuto = true; | |
1244 | } | |
bd5b749c A |
1245 | #else |
1246 | #define RC_START 24 | |
1247 | #define RC_END 31 | |
1248 | volatile UInt32 *infoLocation = (UInt32 *)&(((CFRuntimeBase *)cf)->_cfinfo); | |
1249 | CFIndex rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START); | |
cf7d2af9 | 1250 | if (__builtin_expect(0 == rcLowBits, 0) && !CF_IS_COLLECTABLE(cf)) return cf; // Constant CFTypeRef |
bd5b749c A |
1251 | bool success = 0; |
1252 | do { | |
1253 | UInt32 initialCheckInfo = *infoLocation; | |
1254 | UInt32 prospectiveNewInfo = initialCheckInfo; // don't want compiler to generate prospectiveNewInfo = *infoLocation. This is why infoLocation is declared as a pointer to volatile memory. | |
1255 | prospectiveNewInfo += (1 << RC_START); | |
1256 | rcLowBits = __CFBitfieldGetValue(prospectiveNewInfo, RC_END, RC_START); | |
1257 | if (__builtin_expect((rcLowBits & 0x7f) == 0, 0)) { | |
1258 | /* Roll over another bit to the external ref count | |
1259 | Real ref count = low 7 bits of info[CF_RC_BITS] + external ref count << 6 | |
1260 | Bit 8 of low bits indicates that external ref count is in use. | |
1261 | External ref count is shifted by 6 rather than 7 so that we can set the low | |
1262 | bits to to 1100 0000 rather than 1000 0000. | |
1263 | This prevents needing to access the external ref count for successive retains and releases | |
1264 | when the composite retain count is right around a multiple of 1 << 7. | |
1265 | */ | |
1266 | prospectiveNewInfo = initialCheckInfo; | |
1267 | __CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, ((1 << 7) | (1 << 6))); | |
1268 | __CFSpinLock(&__CFRuntimeExternRefCountTableLock); | |
cf7d2af9 | 1269 | success = OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation); |
bd5b749c | 1270 | if (__builtin_expect(success, 1)) { |
cf7d2af9 | 1271 | __CFDoExternRefOperation(350, (id)cf); |
bd5b749c A |
1272 | } |
1273 | __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock); | |
1274 | } else { | |
cf7d2af9 A |
1275 | success = OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation); |
1276 | // XXX_PCB: 0 --> 1 transition? then add a GC retain count, to root the object. we'll remove it on the 1 --> 0 transition. | |
1277 | if (success && __CFBitfieldGetValue(initialCheckInfo, RC_END, RC_START) == 0 && CF_IS_COLLECTABLE(cf)) { | |
1278 | auto_zone_retain(auto_zone(), (void*)cf); | |
1279 | didAuto = true; | |
1280 | } | |
bd5b749c A |
1281 | } |
1282 | } while (__builtin_expect(!success, 0)); | |
1283 | #endif | |
cf7d2af9 | 1284 | if (!didAuto && __builtin_expect(__CFOASafe, 0)) { |
7c97c3e0 | 1285 | __CFRecordAllocationEvent(__kCFRetainEvent, (void *)cf, 0, CFGetRetainCount(cf), NULL); |
bd5b749c A |
1286 | } |
1287 | return cf; | |
1288 | } | |
1289 | ||
1290 | CF_EXPORT void _CFRelease(CFTypeRef cf) { | |
f64f9b69 A |
1291 | CFTypeID typeID = __CFGenericTypeID_inline(cf); |
1292 | Boolean isAllocator = (__kCFAllocatorTypeID_CONST == typeID); | |
cf7d2af9 | 1293 | Boolean didAuto = false; |
bd5b749c A |
1294 | #if __LP64__ |
1295 | uint32_t lowBits; | |
1296 | do { | |
1297 | lowBits = ((CFRuntimeBase *)cf)->_rc; | |
f64f9b69 A |
1298 | if (0 == lowBits) { |
1299 | if (CF_IS_COLLECTABLE(cf)) auto_zone_release(auto_zone(), (void*)cf); | |
1300 | return; // Constant CFTypeRef | |
1301 | } | |
bd5b749c A |
1302 | if (1 == lowBits) { |
1303 | // CANNOT WRITE ANY NEW VALUE INTO [CF_RC_BITS] UNTIL AFTER FINALIZATION | |
bd5b749c A |
1304 | CFRuntimeClass *cfClass = __CFRuntimeClassTable[typeID]; |
1305 | if (cfClass->version & _kCFRuntimeResourcefulObject && cfClass->reclaim != NULL) { | |
1306 | cfClass->reclaim(cf); | |
1307 | } | |
cf7d2af9 A |
1308 | if (!CF_IS_COLLECTABLE(cf)) { |
1309 | void (*func)(CFTypeRef) = __CFRuntimeClassTable[typeID]->finalize; | |
1310 | if (NULL != func) { | |
1311 | func(cf); | |
1312 | } | |
1313 | // We recheck lowBits to see if the object has been retained again during | |
1314 | // the finalization process. This allows for the finalizer to resurrect, | |
1315 | // but the main point is to allow finalizers to be able to manage the | |
1316 | // removal of objects from uniquing caches, which may race with other threads | |
1317 | // which are allocating (looking up and finding) objects from those caches, | |
1318 | // which (that thread) would be the thing doing the extra retain in that case. | |
1319 | if (isAllocator || OSAtomicCompareAndSwap32Barrier(1, 0, (int32_t *)&((CFRuntimeBase *)cf)->_rc)) { | |
1320 | goto really_free; | |
1321 | } | |
bd5b749c A |
1322 | } |
1323 | } | |
cf7d2af9 A |
1324 | } while (!OSAtomicCompareAndSwap32Barrier(lowBits, lowBits - 1, (int32_t *)&((CFRuntimeBase *)cf)->_rc)); |
1325 | if (lowBits == 1 && CF_IS_COLLECTABLE(cf)) { | |
1326 | // GC: release the collector's hold over the object, which will call the finalize function later on. | |
1327 | auto_zone_release(auto_zone(), (void*)cf); | |
1328 | didAuto = true; | |
1329 | } | |
bd5b749c A |
1330 | #else |
1331 | volatile UInt32 *infoLocation = (UInt32 *)&(((CFRuntimeBase *)cf)->_cfinfo); | |
1332 | CFIndex rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START); | |
f64f9b69 A |
1333 | if (__builtin_expect(0 == rcLowBits, 0)) { |
1334 | if (CF_IS_COLLECTABLE(cf)) auto_zone_release(auto_zone(), (void*)cf); | |
1335 | return; // Constant CFTypeRef | |
1336 | } | |
bd5b749c A |
1337 | bool success = 0; |
1338 | do { | |
1339 | UInt32 initialCheckInfo = *infoLocation; | |
1340 | rcLowBits = __CFBitfieldGetValue(initialCheckInfo, RC_END, RC_START); | |
1341 | if (__builtin_expect(1 == rcLowBits, 0)) { | |
1342 | // we think cf should be deallocated | |
cf7d2af9 | 1343 | // CANNOT WRITE ANY NEW VALUE INTO [CF_RC_BITS] UNTIL AFTER FINALIZATION |
cf7d2af9 A |
1344 | CFRuntimeClass *cfClass = __CFRuntimeClassTable[typeID]; |
1345 | if (cfClass->version & _kCFRuntimeResourcefulObject && cfClass->reclaim != NULL) { | |
1346 | cfClass->reclaim(cf); | |
1347 | } | |
1348 | if (CF_IS_COLLECTABLE(cf)) { | |
1349 | UInt32 prospectiveNewInfo = initialCheckInfo - (1 << RC_START); | |
1350 | success = OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation); | |
1351 | // GC: release the collector's hold over the object, which will call the finalize function later on. | |
1352 | if (success) { | |
1353 | auto_zone_release(auto_zone(), (void*)cf); | |
1354 | didAuto = true; | |
1355 | } | |
1356 | } else { | |
f64f9b69 A |
1357 | if (isAllocator) { |
1358 | goto really_free; | |
cf7d2af9 | 1359 | } else { |
f64f9b69 A |
1360 | void (*func)(CFTypeRef) = __CFRuntimeClassTable[typeID]->finalize; |
1361 | if (NULL != func) { | |
1362 | func(cf); | |
cf7d2af9 A |
1363 | } |
1364 | // We recheck rcLowBits to see if the object has been retained again during | |
1365 | // the finalization process. This allows for the finalizer to resurrect, | |
1366 | // but the main point is to allow finalizers to be able to manage the | |
1367 | // removal of objects from uniquing caches, which may race with other threads | |
1368 | // which are allocating (looking up and finding) objects from those caches, | |
1369 | // which (that thread) would be the thing doing the extra retain in that case. | |
1370 | rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START); | |
1371 | success = (1 == rcLowBits); | |
1372 | if (__builtin_expect(success, 1)) { | |
1373 | goto really_free; | |
1374 | } | |
1375 | } | |
bd5b749c A |
1376 | } |
1377 | } else { | |
1378 | // not yet junk | |
1379 | UInt32 prospectiveNewInfo = initialCheckInfo; // don't want compiler to generate prospectiveNewInfo = *infoLocation. This is why infoLocation is declared as a pointer to volatile memory. | |
1380 | if (__builtin_expect((1 << 7) == rcLowBits, 0)) { | |
1381 | // Time to remove a bit from the external ref count | |
1382 | __CFSpinLock(&__CFRuntimeExternRefCountTableLock); | |
cf7d2af9 | 1383 | CFIndex rcHighBitsCnt = __CFDoExternRefOperation(500, (id)cf); |
bd5b749c A |
1384 | if (1 == rcHighBitsCnt) { |
1385 | __CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, (1 << 6) - 1); | |
1386 | } else { | |
1387 | __CFBitfieldSetValue(prospectiveNewInfo, RC_END, RC_START, ((1 << 6) | (1 << 7)) - 1); | |
1388 | } | |
cf7d2af9 | 1389 | success = OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation); |
bd5b749c | 1390 | if (__builtin_expect(success, 1)) { |
cf7d2af9 | 1391 | __CFDoExternRefOperation(450, (id)cf); |
bd5b749c A |
1392 | } |
1393 | __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock); | |
1394 | } else { | |
1395 | prospectiveNewInfo -= (1 << RC_START); | |
cf7d2af9 | 1396 | success = OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation); |
bd5b749c A |
1397 | } |
1398 | } | |
1399 | } while (__builtin_expect(!success, 0)); | |
1400 | ||
1401 | #endif | |
cf7d2af9 | 1402 | if (!didAuto && __builtin_expect(__CFOASafe, 0)) { |
7c97c3e0 | 1403 | __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, CFGetRetainCount(cf), NULL); |
bd5b749c A |
1404 | } |
1405 | return; | |
1406 | ||
1407 | really_free:; | |
cf7d2af9 A |
1408 | if (!didAuto && __builtin_expect(__CFOASafe, 0)) { |
1409 | // do not use CFGetRetainCount() because cf has been freed if it was an allocator | |
bd5b749c A |
1410 | __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL); |
1411 | } | |
1412 | // cannot zombify allocators, which get deallocated by __CFAllocatorDeallocate (finalize) | |
f64f9b69 A |
1413 | if (isAllocator) { |
1414 | __CFAllocatorDeallocate((void *)cf); | |
bd5b749c | 1415 | } else { |
f64f9b69 A |
1416 | CFAllocatorRef allocator = kCFAllocatorSystemDefault; |
1417 | Boolean usesSystemDefaultAllocator = true; | |
1418 | ||
1419 | if (!__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 7, 7)) { | |
bd5b749c | 1420 | allocator = CFGetAllocator(cf); |
bd5b749c | 1421 | usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault); |
f64f9b69 | 1422 | } |
bd5b749c | 1423 | |
cf7d2af9 A |
1424 | if (__CFZombieEnabled && !kCFUseCollectableAllocator) { |
1425 | Class cls = object_getClass((id)cf); | |
1426 | const char *name = NULL; | |
1427 | __CFSpinLock(&__CFBigRuntimeFunnel); | |
1428 | for (CFIndex idx = 0; !name && idx < __CFRuntimeClassTableCount; idx++) { | |
1429 | if ((uintptr_t)cls == __CFRuntimeObjCClassTable[idx]) { | |
1430 | CFRuntimeClass *c = __CFRuntimeClassTable[idx]; | |
1431 | if (c) name = c->className; | |
1432 | } | |
bd5b749c | 1433 | } |
cf7d2af9 A |
1434 | __CFSpinUnlock(&__CFBigRuntimeFunnel); |
1435 | // in 64-bit, a future class has nil isa, and calling class_getName() | |
1436 | // on such will crash so we do this test | |
1437 | if (!name && object_getClass((id)cls)) { | |
1438 | name = class_getName(cls); | |
1439 | } | |
1440 | if (!name) name = "$class-unknown$"; | |
1441 | char *cname = NULL; | |
1442 | asprintf(&cname, "_NSZombie_%s", name); | |
1443 | Class zclass = (Class)objc_lookUpClass(cname); | |
1444 | if (!zclass) { | |
1445 | zclass = objc_duplicateClass((Class)objc_lookUpClass("_NSZombie_"), cname, 0); | |
1446 | } | |
1447 | free(cname); | |
1448 | ||
1449 | #if DEPLOYMENT_TARGET_MACOSX | |
1450 | if (object_getClass((id)cls)) { | |
1451 | objc_destructInstance((id)cf); | |
1452 | } | |
1453 | #endif | |
1454 | if (__CFDeallocateZombies) { | |
1455 | #if __OBJC2__ | |
1456 | object_setClass((id)cf, zclass); | |
1457 | #else | |
1458 | // Set 'isa' pointer only if using standard deallocator | |
1459 | // However, _internal_object_dispose is not exported from libobjc | |
1460 | if (_dealloc == _original_objc_dealloc) { | |
1461 | object_setClass((id)cf, zclass); | |
1462 | } | |
1463 | #endif | |
1464 | CFAllocatorDeallocate(allocator, (uint8_t *)cf - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef))); | |
1465 | } else { | |
1466 | object_setClass((id)cf, zclass); | |
1467 | } | |
1468 | ||
1469 | #if 0 | |
1470 | extern uintptr_t __CFFindPointer(uintptr_t ptr, uintptr_t start); | |
1471 | uintptr_t res = __CFFindPointer((uintptr_t)cf, 0); | |
1472 | while (0 != res) { | |
1473 | if (res < (uintptr_t)&cf - 4 * 4096 || (uintptr_t)&cf + 4096 < res) { | |
1474 | printf("*** NSZombie warning: object %p deallocated, but reference still found at %p (%p %p)\n", cf, res); | |
1475 | } | |
1476 | res = __CFFindPointer((uintptr_t)cf, res + 1); | |
1477 | } | |
1478 | #endif | |
1479 | ||
1480 | } else { | |
1481 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
1482 | if (kCFUseCollectableAllocator || !(__CFZombieLevel & (1 << 4))) { | |
1483 | Class cls = object_getClass((id)cf); | |
1484 | if (object_getClass((id)cls)) { | |
1485 | objc_removeAssociatedObjects((id)cf); | |
1486 | } | |
1487 | } | |
1488 | #endif | |
1489 | if ((__CFZombieLevel & (1 << 0)) && !kCFUseCollectableAllocator) { | |
1490 | uint8_t *ptr = (uint8_t *)cf - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef)); | |
1491 | size_t size = malloc_size(ptr); | |
1492 | uint8_t byte = 0xFC; | |
1493 | if (__CFZombieLevel & (1 << 1)) { | |
1494 | ptr = (uint8_t *)cf + sizeof(CFRuntimeBase); | |
1495 | size = size - sizeof(CFRuntimeBase) - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef)); | |
1496 | } | |
1497 | if (__CFZombieLevel & (1 << 7)) { | |
1498 | byte = (__CFZombieLevel >> 8) & 0xFF; | |
1499 | } | |
1500 | memset(ptr, byte, size); | |
1501 | } | |
1502 | if (kCFUseCollectableAllocator || !(__CFZombieLevel & (1 << 4))) { | |
1503 | CFAllocatorDeallocate(allocator, (uint8_t *)cf - (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef))); | |
bd5b749c | 1504 | } |
bd5b749c | 1505 | } |
cf7d2af9 | 1506 | |
bd5b749c A |
1507 | if (kCFAllocatorSystemDefault != allocator) { |
1508 | CFRelease(allocator); | |
1509 | } | |
1510 | } | |
1511 | } | |
1512 | ||
1513 | #undef __kCFAllocatorTypeID_CONST | |
1514 | #undef __CFGenericAssertIsCF | |
1515 |