]> git.saurik.com Git - apple/cf.git/blame - CFRuntime.c
CF-550.43.tar.gz
[apple/cf.git] / CFRuntime.c
CommitLineData
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
54extern Class _objc_getFreedObjectClass(void);
55#endif
bd5b749c 56
cf7d2af9
A
57#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
58extern 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
65extern void instrumentObjcMessageSends(BOOL flag);
66#endif
67
68#if DEPLOYMENT_TARGET_WINDOWS
69#include <Shellapi.h>
70#endif
bd5b749c
A
71
72enum {
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
87bool __CFOASafe = false;
88
89void (*__CFObjectAllocRecordAllocationFunction)(int, void *, int64_t , uint64_t, const char *) = NULL;
90void (*__CFObjectAllocSetLastAllocEventNameFunction)(void *, const char *) = NULL;
91
92void __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
106void __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
111void __CFSetLastAllocationEventName(void *ptr, const char *classname) {
112 if (!__CFOASafe || !__CFObjectAllocSetLastAllocEventNameFunction) return;
113 __CFObjectAllocSetLastAllocEventNameFunction(ptr, classname);
114}
115
116#endif
bd5b749c
A
117
118extern void __HALT(void);
119
120static CFTypeID __kCFNotATypeTypeID = _kCFRuntimeNotATypeID;
121
122#if !defined (__cplusplus)
123static 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
135static CFTypeID __kCFTypeTypeID = _kCFRuntimeNotATypeID;
136
137static 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
149void SIG1(CFTypeRef){__HALT();};;
150CFTypeRef SIG2(CFAllocatorRef,CFTypeRef){__HALT();return NULL;};
151Boolean SIG3(CFTypeRef,CFTypeRef){__HALT();return FALSE;};
152CFHashCode SIG4(CFTypeRef){__HALT(); return 0;};
153CFStringRef SIG5(CFTypeRef,CFDictionaryRef){__HALT();return NULL;};
154CFStringRef SIG6(CFTypeRef){__HALT();return NULL;};
155
156static 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
168static CFTypeID __kCFTypeTypeID = _kCFRuntimeNotATypeID;
169
170static 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
184static CFSpinLock_t __CFBigRuntimeFunnel = CFSpinLockInit;
bd5b749c
A
185static CFRuntimeClass ** __CFRuntimeClassTable = NULL;
186int32_t __CFRuntimeClassTableSize = 0;
187static int32_t __CFRuntimeClassTableCount = 0;
188
cf7d2af9 189uintptr_t *__CFRuntimeObjCClassTable = NULL;
bd5b749c 190
cf7d2af9 191__private_extern__ void * (*__CFSendObjCMsg)(const void *, SEL, ...) = NULL;
bd5b749c
A
192
193bool (*__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__
197int __CFConstantStringClassReference[24] = {0};
198#else
199int __CFConstantStringClassReference[12] = {0};
200#endif
201
cf7d2af9
A
202
203Boolean _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
210CFTypeID _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
253void _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
259const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID) {
cf7d2af9 260 return __CFRuntimeClassTable[typeID]; // hopelessly unthreadsafe
bd5b749c
A
261}
262
263void _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
279static uint32_t __CFZombieLevel = 0x0;
cf7d2af9
A
280__private_extern__ uint8_t __CFZombieEnabled = 0;
281__private_extern__ uint8_t __CFDeallocateZombies = 0;
282#if !__OBJC2__
bd5b749c 283static void *_original_objc_dealloc = 0;
cf7d2af9 284#endif
bd5b749c 285
f64f9b69
A
286void _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
295CF_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
303CFTypeRef _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
355void _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
371void _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
390enum {
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
408static struct {
409 CFSpinLock_t lock;
410 CFBasicHashRef table;
411 uint8_t padding[64 - sizeof(CFBasicHashRef) - sizeof(CFSpinLock_t)];
412} __NSRetainCounters[NUM_EXTERN_TABLES];
413
414CF_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
452CFTypeID __CFGenericTypeID(const void *cf) {
453 return (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF;
454}
455
456CF_INLINE CFTypeID __CFGenericTypeID_inline(const void *cf) {
457 return (*(uint32_t *)(((CFRuntimeBase *)cf)->_cfinfo) >> 8) & 0xFFFF;
458}
459
460CFTypeID 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
481CF_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
499CFTypeID 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
508CFStringRef 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
515CF_EXPORT void _CFRelease(CFTypeRef cf);
516CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf);
bd5b749c
A
517
518CFTypeRef 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
537void 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
578extern 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__
651static CFSpinLock_t __CFRuntimeExternRefCountTableLock = CFSpinLockInit;
652static CFMutableBagRef __CFRuntimeExternRefCountTable = NULL;
653#endif
654
655static 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
677CFTypeRef _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
687void _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
697CFIndex 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
709CFTypeRef 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
737CFTypeRef 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
745Boolean 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
760CFHashCode 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
771CFStringRef 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
809extern CFAllocatorRef __CFAllocatorGetAllocator(CFTypeRef);
810
811CFAllocatorRef 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
822extern void __CFBaseInitialize(void);
823extern void __CFNullInitialize(void);
824extern void __CFAllocatorInitialize(void);
825extern void __CFStringInitialize(void);
826extern void __CFArrayInitialize(void);
bd5b749c
A
827extern void __CFBooleanInitialize(void);
828extern void __CFCharacterSetInitialize(void);
829extern void __CFDataInitialize(void);
bd5b749c 830extern void __CFNumberInitialize(void);
bd5b749c
A
831extern void __CFStorageInitialize(void);
832extern void __CFErrorInitialize(void);
bd5b749c
A
833extern void __CFTreeInitialize(void);
834extern void __CFURLInitialize(void);
cf7d2af9 835#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
bd5b749c
A
836extern void __CFMachPortInitialize(void);
837#endif
bd5b749c 838extern void __CFMessagePortInitialize(void);
bd5b749c
A
839extern void __CFRunLoopInitialize(void);
840extern void __CFRunLoopObserverInitialize(void);
841extern void __CFRunLoopSourceInitialize(void);
842extern void __CFRunLoopTimerInitialize(void);
bd5b749c
A
843extern void __CFBundleInitialize(void);
844extern void __CFPlugInInitialize(void);
845extern void __CFPlugInInstanceInitialize(void);
846extern void __CFUUIDInitialize(void);
847extern void __CFBinaryHeapInitialize(void);
848extern void __CFBitVectorInitialize(void);
cf7d2af9 849#if DEPLOYMENT_TARGET_WINDOWS
f64f9b69
A
850extern void __CFWindowsMessageQueueInitialize(void);
851extern void __CFWindowsNamedPipeInitialize(void);
cf7d2af9
A
852extern void __CFBaseCleanup(void);
853#endif
bd5b749c
A
854extern 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;
861asm(".desc ___crashreporter_info__, 0x10");
862
863static void __01121__(void) {
864 __CF120291 = pthread_is_threaded_np() ? true : false;
865}
866
867static 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
893CF_EXPORT const void *__CFArgStuff;
bd5b749c
A
894const void *__CFArgStuff = NULL;
895__private_extern__ void *__CFAppleLanguages = NULL;
896
cf7d2af9
A
897static 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
941CF_EXPORT pthread_t _CFMainPThread;
942pthread_t _CFMainPThread = kNilPthreadT;
bd5b749c 943
cf7d2af9
A
944CF_EXPORT bool kCFUseCollectableAllocator = false;
945
946__private_extern__ Boolean __CFProphylacticAutofsAccess = false;
bd5b749c
A
947
948#if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
949static void __CFInitialize(void) __attribute__ ((constructor));
950static
951#endif
cf7d2af9
A
952#if DEPLOYMENT_TARGET_WINDOWS
953CF_EXPORT
954#endif
bd5b749c
A
955void __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
1164static 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
1210int 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
1231CF_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
1290CF_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