]> git.saurik.com Git - apple/cf.git/blob - CFRuntime.c
CF-550.42.tar.gz
[apple/cf.git] / CFRuntime.c
1 /*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
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 */
23
24 /* CFRuntime.c
25 Copyright (c) 1999-2009, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
27 */
28
29 #define ENABLE_ZOMBIES 1
30
31 #include <CoreFoundation/CFRuntime.h>
32 #include "CFInternal.h"
33 #include "CFBasicHash.h"
34 #include <string.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
38 #include <dlfcn.h>
39 #include <mach-o/dyld.h>
40 #include <mach/mach.h>
41 #include <crt_externs.h>
42 #include <unistd.h>
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
56
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
60 #define __CFRecordAllocationEvent(a, b, c, d, e) ((void)0)
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
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
79 #if DEPLOYMENT_TARGET_WINDOWS
80 #include <malloc.h>
81 #else
82 #include <malloc/malloc.h>
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
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
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;
185 static CFRuntimeClass ** __CFRuntimeClassTable = NULL;
186 int32_t __CFRuntimeClassTableSize = 0;
187 static int32_t __CFRuntimeClassTableCount = 0;
188
189 uintptr_t *__CFRuntimeObjCClassTable = NULL;
190
191 __private_extern__ void * (*__CFSendObjCMsg)(const void *, SEL, ...) = NULL;
192
193 bool (*__CFObjCIsCollectable)(void *) = NULL;
194
195 // Compiler uses this symbol name; must match compiler built-in decl, so we use 'int'
196 #if __LP64__
197 int __CFConstantStringClassReference[24] = {0};
198 #else
199 int __CFConstantStringClassReference[12] = {0};
200 #endif
201
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 }
209
210 CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls) {
211 // version field must be 0
212 // className must be pure ASCII string, non-null
213 __CFSpinLock(&__CFBigRuntimeFunnel);
214 if (__CFMaxRuntimeTypes <= __CFRuntimeClassTableCount) {
215 CFLog(kCFLogLevelWarning, CFSTR("*** CoreFoundation class table full; registration failing for class '%s'. Program will crash soon."), cls->className);
216 __CFSpinUnlock(&__CFBigRuntimeFunnel);
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;
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
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;
248 CFTypeID typeID = __CFRuntimeClassTableCount - 1;
249 __CFSpinUnlock(&__CFBigRuntimeFunnel);
250 return typeID;
251 }
252
253 void _CFRuntimeBridgeClasses(CFTypeID cf_typeID, const char *objc_classname) {
254 __CFSpinLock(&__CFBigRuntimeFunnel);
255 __CFRuntimeObjCClassTable[cf_typeID] = (uintptr_t)objc_getFutureClass(objc_classname);
256 __CFSpinUnlock(&__CFBigRuntimeFunnel);
257 }
258
259 const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID) {
260 return __CFRuntimeClassTable[typeID]; // hopelessly unthreadsafe
261 }
262
263 void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID) {
264 __CFSpinLock(&__CFBigRuntimeFunnel);
265 __CFRuntimeClassTable[typeID] = NULL;
266 __CFSpinUnlock(&__CFBigRuntimeFunnel);
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;
280 __private_extern__ uint8_t __CFZombieEnabled = 0;
281 __private_extern__ uint8_t __CFDeallocateZombies = 0;
282 #if !__OBJC2__
283 static void *_original_objc_dealloc = 0;
284 #endif
285
286 void _CFEnableZombies(void) {
287 __CFZombieEnabled = 0xFF;
288 }
289
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
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
300 #define CF_GET_COLLECTABLE_MEMORY_TYPE(x) (0)
301 #endif
302
303 CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, CFIndex extraBytes, unsigned char *category) {
304 CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__);
305 CFRuntimeClass *cls = __CFRuntimeClassTable[typeID];
306 if (NULL == cls) {
307 return NULL;
308 }
309 allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator;
310 if (kCFAllocatorNull == allocator) {
311 return NULL;
312 }
313 Boolean usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault);
314 CFIndex size = sizeof(CFRuntimeBase) + extraBytes + (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef));
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
318 CFRuntimeBase *memory = (CFRuntimeBase *)CFAllocatorAllocate(allocator, size, CF_GET_COLLECTABLE_MEMORY_TYPE(cls));
319 if (NULL == memory) {
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);
326 return NULL;
327 }
328 if (!kCFUseCollectableAllocator || !CF_IS_COLLECTABLE_ALLOCATOR(allocator) || !(CF_GET_COLLECTABLE_MEMORY_TYPE(cls) & __kCFAllocatorGCScannedMemory)) {
329 memset(memory, 0, size);
330 }
331 if (__CFOASafe && category) {
332 __CFSetLastAllocationEventName(memory, (char *)category);
333 } else if (__CFOASafe) {
334 __CFSetLastAllocationEventName(memory, (char *)cls->className);
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
349 if (NULL != cls->init) {
350 (cls->init)(memory);
351 }
352 return memory;
353 }
354
355 void _CFRuntimeInitStaticInstance(void *ptr, CFTypeID typeID) {
356 CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__);
357 if (NULL == __CFRuntimeClassTable[typeID]) {
358 return;
359 }
360 CFRuntimeBase *memory = (CFRuntimeBase *)ptr;
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
383 if (object_getClass((id)cls) == nil) return false;
384 const char *cname = class_getName(cls);
385 if (cname && 0 == strncmp(cname, "_NSZombie_", 10)) return true;
386 return false;
387 }
388
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
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
473 #if 0
474
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
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
498
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
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);
517
518 CFTypeRef CFRetain(CFTypeRef cf) {
519 if (NULL == cf) HALT;
520 if (CF_IS_COLLECTABLE(cf)) {
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 }
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) {
538 if (NULL == cf) HALT;
539 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
540 if (CF_IS_COLLECTABLE(cf)) {
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);
547 }
548 return;
549 }
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 }
558 #endif
559 CFTYPE_OBJC_FUNCDISPATCH0(void, cf, "release");
560 if (cf) __CFGenericAssertIsCF(cf);
561 _CFRelease(cf);
562 #if 0
563 end:;
564 #endif
565 }
566
567
568 __private_extern__ const void *__CFStringCollectionCopy(CFAllocatorRef allocator, const void *ptr) {
569 if (NULL == ptr) HALT;
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) {
581 if (NULL == ptr) HALT;
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.
586 if (auto_zone_is_valid_pointer(auto_zone(), ptr)) {
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.
591 // We're basically inlining CFRetain() here, to avoid extra heap membership tests.
592 _CFRetain(cf);
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) {
617 if (NULL == ptr) HALT;
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.
622 if (auto_zone_is_valid_pointer(auto_zone(), cf)) {
623 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
624 // GC: If this a CF object in the GC heap that is marked uncollectable, then
625 // must balance the retain done in __CFTypeCollectionRetain().
626 // We're basically inlining CFRelease() here, to avoid extra heap membership tests.
627 CFRuntimeClass *cfClass = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)];
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
634 }
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) {
656 if (NULL == cf) HALT;
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) {
670 highBits = __CFDoExternRefOperation(500, (id)cf);
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)
679 if (kCFUseCollectableAllocator && !CF_IS_COLLECTABLE(cf)) {
680 fprintf(stderr, "non-auto object %p passed to _CFRetainGC.\n", cf);
681 HALT;
682 }
683 #endif
684 return kCFUseCollectableAllocator ? cf : CFRetain(cf);
685 }
686
687 void _CFReleaseGC(CFTypeRef cf) {
688 #if defined(DEBUG)
689 if (kCFUseCollectableAllocator && !CF_IS_COLLECTABLE(cf)) {
690 fprintf(stderr, "non-auto object %p passed to _CFReleaseGC.\n", cf);
691 HALT;
692 }
693 #endif
694 if (!kCFUseCollectableAllocator) CFRelease(cf);
695 }
696
697 CFIndex CFGetRetainCount(CFTypeRef cf) {
698 if (NULL == cf) HALT;
699 if (CF_IS_COLLECTABLE(cf)) {
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);
704 }
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)) {
712 objc_assertRegisteredThreadWithCollector();
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 }
728 if (CFGetRetainCount(cf) == 0) {
729 CFLog(kCFLogLevelWarning, CFSTR("object %p with 0 retain-count passed to CFMakeCollectable."), cf);
730 return cf;
731 }
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);
741 }
742 return cf;
743 }
744
745 Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2) {
746 if (NULL == cf1) HALT;
747 if (NULL == cf2) HALT;
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) {
761 if (NULL == cf) HALT;
762 CFTYPE_OBJC_FUNCDISPATCH0(CFHashCode, cf, "hash");
763 __CFGenericAssertIsCF(cf);
764 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash) {
765 return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash(cf);
766 }
767 return (CFHashCode)cf;
768 }
769
770 // definition: produces a normally non-NULL debugging description of the object
771 CFStringRef CFCopyDescription(CFTypeRef cf) {
772 if (NULL == cf) HALT;
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
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) {
792 if (NULL == cf) HALT;
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 }
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;
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();
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);
827 extern void __CFBooleanInitialize(void);
828 extern void __CFCharacterSetInitialize(void);
829 extern void __CFDataInitialize(void);
830 extern void __CFNumberInitialize(void);
831 extern void __CFStorageInitialize(void);
832 extern void __CFErrorInitialize(void);
833 extern void __CFTreeInitialize(void);
834 extern void __CFURLInitialize(void);
835 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
836 extern void __CFMachPortInitialize(void);
837 #endif
838 extern void __CFMessagePortInitialize(void);
839 extern void __CFRunLoopInitialize(void);
840 extern void __CFRunLoopObserverInitialize(void);
841 extern void __CFRunLoopSourceInitialize(void);
842 extern void __CFRunLoopTimerInitialize(void);
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);
849 #if DEPLOYMENT_TARGET_WINDOWS
850 extern void __CFWindowsMessageQueueInitialize(void);
851 extern void __CFWindowsNamedPipeInitialize(void);
852 extern void __CFBaseCleanup(void);
853 #endif
854 extern void __CFStreamInitialize(void);
855
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;
894 const void *__CFArgStuff = NULL;
895 __private_extern__ void *__CFAppleLanguages = NULL;
896
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;
943
944 CF_EXPORT bool kCFUseCollectableAllocator = false;
945
946 __private_extern__ Boolean __CFProphylacticAutofsAccess = false;
947
948 #if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
949 static void __CFInitialize(void) __attribute__ ((constructor));
950 static
951 #endif
952 #if DEPLOYMENT_TARGET_WINDOWS
953 CF_EXPORT
954 #endif
955 void __CFInitialize(void) {
956 static int __done = 0;
957
958 if (!__done) {
959 __done = 1;
960
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
985 #if defined(DEBUG) || defined(ENABLE_ZOMBIES)
986 const char *value = __CFgetenv("NSZombieEnabled");
987 if (value && (*value == 'Y' || *value == 'y')) __CFZombieEnabled = 0xff;
988 value = __CFgetenv("NSDeallocateZombies");
989 if (value && (*value == 'Y' || *value == 'y')) __CFDeallocateZombies = 0xff;
990 #if !__OBJC2__
991 _original_objc_dealloc = (void *)_dealloc;
992 #endif
993
994 value = __CFgetenv("CFZombieLevel");
995 if (NULL != value) {
996 __CFZombieLevel = (uint32_t)strtoul_l(value, NULL, 0, NULL);
997 }
998 if (0x0 == __CFZombieLevel) __CFZombieLevel = 0x0000FC00; // default
999
1000 #endif
1001
1002 __CFRuntimeClassTableSize = 1024;
1003 __CFRuntimeClassTable = (CFRuntimeClass **)calloc(__CFRuntimeClassTableSize, sizeof(CFRuntimeClass *));
1004 __CFRuntimeObjCClassTable = (uintptr_t *)calloc(__CFRuntimeClassTableSize, sizeof(uintptr_t));
1005 __CFBaseInitialize();
1006
1007 _CFRuntimeBridgeClasses(0, "__NSCFType");
1008 for (CFIndex idx = 1; idx < __CFRuntimeClassTableSize; idx++) {
1009 __CFRuntimeObjCClassTable[idx] = __CFRuntimeObjCClassTable[0];
1010 }
1011
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
1019 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
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
1037 CFBasicHashGetTypeID();
1038 CFBagGetTypeID();
1039 #if !__LP64__
1040 // Creating this lazily in CFRetain causes recursive call to CFRetain
1041 __CFRuntimeExternRefCountTable = CFBagCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
1042 #endif
1043
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
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;
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
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
1063 __CFBinaryHeapInitialize();
1064 __CFBitVectorInitialize();
1065 __CFCharacterSetInitialize();
1066 __CFStorageInitialize();
1067 __CFErrorInitialize();
1068 __CFTreeInitialize();
1069 __CFURLInitialize();
1070 __CFBundleInitialize();
1071 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1072 __CFPlugInInitialize();
1073 __CFPlugInInstanceInitialize();
1074 #endif
1075 __CFUUIDInitialize();
1076 __CFMessagePortInitialize();
1077 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1078 __CFMachPortInitialize();
1079 #endif
1080 __CFStreamInitialize();
1081 __CFRunLoopInitialize();
1082 __CFRunLoopObserverInitialize();
1083 __CFRunLoopSourceInitialize();
1084 __CFRunLoopTimerInitialize();
1085
1086
1087 {
1088 CFIndex idx, cnt;
1089 char **args;
1090 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1091 args = *_NSGetArgv();
1092 cnt = *_NSGetArgc();
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
1106 CFIndex count;
1107 CFStringRef *list, buffer[256];
1108 list = (cnt <= 256) ? buffer : (CFStringRef *)malloc(cnt * sizeof(CFStringRef));
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 }
1125
1126 _CFProcessPath(); // cache this early
1127
1128
1129 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1130 __CFOAInitialize();
1131 #endif
1132
1133 if (__CFRuntimeClassTableCount < 256) __CFRuntimeClassTableCount = 256;
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
1152
1153 #if defined(DEBUG) && (DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED)
1154 CFLog(kCFLogLevelWarning, CFSTR("Assertions enabled"));
1155 #endif
1156
1157 __CFProphylacticAutofsAccess = false;
1158 }
1159 }
1160
1161
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 }
1190
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);
1196
1197 // this registers us so subsequent calls to CFBundleGetBundleWithIdentifier will succeed
1198 CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL);
1199 CFRelease(bundleURL);
1200
1201 return bundle;
1202 }
1203
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 ) {
1211 static CFBundleRef cfBundle = NULL;
1212 if (dwReason == DLL_PROCESS_ATTACH) {
1213 __CFInitialize();
1214 cfBundle = RegisterCoreFoundationBundle();
1215 } else if (dwReason == DLL_PROCESS_DETACH) {
1216 __CFStreamCleanup();
1217 __CFSocketCleanup();
1218 __CFUniCharCleanup();
1219 __CFBaseCleanup();
1220 // do these last
1221 if (cfBundle) CFRelease(cfBundle);
1222 __CFStringCleanup();
1223 } else if (dwReason == DLL_THREAD_DETACH) {
1224 __CFFinalizeThreadData(NULL);
1225 }
1226 return TRUE;
1227 }
1228
1229 #endif
1230
1231 CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf) {
1232 if (NULL == cf) return NULL;
1233 Boolean didAuto = false;
1234 #if __LP64__
1235 if (0 == ((CFRuntimeBase *)cf)->_rc && !CF_IS_COLLECTABLE(cf)) return cf; // Constant CFTypeRef
1236 uint32_t lowBits;
1237 do {
1238 lowBits = ((CFRuntimeBase *)cf)->_rc;
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 }
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);
1250 if (__builtin_expect(0 == rcLowBits, 0) && !CF_IS_COLLECTABLE(cf)) return cf; // Constant CFTypeRef
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);
1269 success = OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation);
1270 if (__builtin_expect(success, 1)) {
1271 __CFDoExternRefOperation(350, (id)cf);
1272 }
1273 __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock);
1274 } else {
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 }
1281 }
1282 } while (__builtin_expect(!success, 0));
1283 #endif
1284 if (!didAuto && __builtin_expect(__CFOASafe, 0)) {
1285 __CFRecordAllocationEvent(__kCFRetainEvent, (void *)cf, 0, CFGetRetainCount(cf), NULL);
1286 }
1287 return cf;
1288 }
1289
1290 CF_EXPORT void _CFRelease(CFTypeRef cf) {
1291 CFTypeID typeID = __CFGenericTypeID_inline(cf);
1292 Boolean isAllocator = (__kCFAllocatorTypeID_CONST == typeID);
1293 Boolean didAuto = false;
1294 #if __LP64__
1295 uint32_t lowBits;
1296 do {
1297 lowBits = ((CFRuntimeBase *)cf)->_rc;
1298 if (0 == lowBits) {
1299 if (CF_IS_COLLECTABLE(cf)) auto_zone_release(auto_zone(), (void*)cf);
1300 return; // Constant CFTypeRef
1301 }
1302 if (1 == lowBits) {
1303 // CANNOT WRITE ANY NEW VALUE INTO [CF_RC_BITS] UNTIL AFTER FINALIZATION
1304 CFRuntimeClass *cfClass = __CFRuntimeClassTable[typeID];
1305 if (cfClass->version & _kCFRuntimeResourcefulObject && cfClass->reclaim != NULL) {
1306 cfClass->reclaim(cf);
1307 }
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 }
1322 }
1323 }
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 }
1330 #else
1331 volatile UInt32 *infoLocation = (UInt32 *)&(((CFRuntimeBase *)cf)->_cfinfo);
1332 CFIndex rcLowBits = __CFBitfieldGetValue(*infoLocation, RC_END, RC_START);
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 }
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
1343 // CANNOT WRITE ANY NEW VALUE INTO [CF_RC_BITS] UNTIL AFTER FINALIZATION
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 {
1357 if (isAllocator) {
1358 goto really_free;
1359 } else {
1360 void (*func)(CFTypeRef) = __CFRuntimeClassTable[typeID]->finalize;
1361 if (NULL != func) {
1362 func(cf);
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 }
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);
1383 CFIndex rcHighBitsCnt = __CFDoExternRefOperation(500, (id)cf);
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 }
1389 success = OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation);
1390 if (__builtin_expect(success, 1)) {
1391 __CFDoExternRefOperation(450, (id)cf);
1392 }
1393 __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock);
1394 } else {
1395 prospectiveNewInfo -= (1 << RC_START);
1396 success = OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo, *(int32_t *)&prospectiveNewInfo, (int32_t *)infoLocation);
1397 }
1398 }
1399 } while (__builtin_expect(!success, 0));
1400
1401 #endif
1402 if (!didAuto && __builtin_expect(__CFOASafe, 0)) {
1403 __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, CFGetRetainCount(cf), NULL);
1404 }
1405 return;
1406
1407 really_free:;
1408 if (!didAuto && __builtin_expect(__CFOASafe, 0)) {
1409 // do not use CFGetRetainCount() because cf has been freed if it was an allocator
1410 __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL);
1411 }
1412 // cannot zombify allocators, which get deallocated by __CFAllocatorDeallocate (finalize)
1413 if (isAllocator) {
1414 __CFAllocatorDeallocate((void *)cf);
1415 } else {
1416 CFAllocatorRef allocator = kCFAllocatorSystemDefault;
1417 Boolean usesSystemDefaultAllocator = true;
1418
1419 if (!__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 7, 7)) {
1420 allocator = CFGetAllocator(cf);
1421 usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault);
1422 }
1423
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 }
1433 }
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)));
1504 }
1505 }
1506
1507 if (kCFAllocatorSystemDefault != allocator) {
1508 CFRelease(allocator);
1509 }
1510 }
1511 }
1512
1513 #undef __kCFAllocatorTypeID_CONST
1514 #undef __CFGenericAssertIsCF
1515