2 * Copyright (c) 2010 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1999-2009, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
29 #define ENABLE_ZOMBIES 1
31 #include <CoreFoundation/CFRuntime.h>
32 #include "CFInternal.h"
33 #include "CFBasicHash.h"
37 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
39 #include <mach-o/dyld.h>
40 #include <mach/mach.h>
41 #include <crt_externs.h>
43 #include <objc/runtime.h>
45 #include <CoreFoundation/CFStringDefaultEncoding.h>
46 #define objc_isAuto (0)
48 #include <objc/runtime.h>
51 #if DEPLOYMENT_TARGET_WINDOWS
52 #define _objc_getFreedObjectClass() 0
54 extern Class
_objc_getFreedObjectClass(void);
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
);
60 #define __CFRecordAllocationEvent(a, b, c, d, e) ((void)0)
64 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
65 extern void instrumentObjcMessageSends(BOOL flag
);
68 #if DEPLOYMENT_TARGET_WINDOWS
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
79 #if DEPLOYMENT_TARGET_WINDOWS
82 #include <malloc/malloc.h>
85 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
87 bool __CFOASafe
= false;
89 void (*__CFObjectAllocRecordAllocationFunction
)(int, void *, int64_t , uint64_t, const char *) = NULL
;
90 void (*__CFObjectAllocSetLastAllocEventNameFunction
)(void *, const char *) = NULL
;
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");
100 __CFObjectAllocRecordAllocationFunction
= dlsym(RTLD_DEFAULT
, "_OARecordAllocationEvent");
101 __CFObjectAllocSetLastAllocEventNameFunction
= dlsym(RTLD_DEFAULT
, "_OASetLastAllocationEventName");
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
);
111 void __CFSetLastAllocationEventName(void *ptr
, const char *classname
) {
112 if (!__CFOASafe
|| !__CFObjectAllocSetLastAllocEventNameFunction
) return;
113 __CFObjectAllocSetLastAllocEventNameFunction(ptr
, classname
);
118 extern void __HALT(void);
120 static CFTypeID __kCFNotATypeTypeID
= _kCFRuntimeNotATypeID
;
122 #if !defined (__cplusplus)
123 static const CFRuntimeClass __CFNotATypeClass
= {
135 static CFTypeID __kCFTypeTypeID
= _kCFRuntimeNotATypeID
;
137 static const CFRuntimeClass __CFTypeClass
= {
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
;};
156 static const CFRuntimeClass __CFNotATypeClass
= {
168 static CFTypeID __kCFTypeTypeID
= _kCFRuntimeNotATypeID
;
170 static const CFRuntimeClass __CFTypeClass
= {
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;
189 uintptr_t *__CFRuntimeObjCClassTable
= NULL
;
191 __private_extern__
void * (*__CFSendObjCMsg
)(const void *, SEL
, ...) = NULL
;
193 bool (*__CFObjCIsCollectable
)(void *) = NULL
;
195 // Compiler uses this symbol name; must match compiler built-in decl, so we use 'int'
197 int __CFConstantStringClassReference
[24] = {0};
199 int __CFConstantStringClassReference
[12] = {0};
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
);
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
;
219 if (__CFRuntimeClassTableSize
<= __CFRuntimeClassTableCount
) {
220 int32_t old_size
= __CFRuntimeClassTableSize
;
221 int32_t new_size
= __CFRuntimeClassTableSize
* 4;
223 void *new_table1
= calloc(new_size
, sizeof(CFRuntimeClass
*));
224 memmove(new_table1
, __CFRuntimeClassTable
, old_size
* sizeof(CFRuntimeClass
*));
225 __CFRuntimeClassTable
= (CFRuntimeClass
**)new_table1
;
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];
232 __CFRuntimeObjCClassTable
= (uintptr_t *)new_table2
;
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.
247 __CFRuntimeClassTable
[__CFRuntimeClassTableCount
++] = (CFRuntimeClass
*)cls
;
248 CFTypeID typeID
= __CFRuntimeClassTableCount
- 1;
249 __CFSpinUnlock(&__CFBigRuntimeFunnel
);
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
);
259 const CFRuntimeClass
* _CFRuntimeGetClassWithTypeID(CFTypeID typeID
) {
260 return __CFRuntimeClassTable
[typeID
]; // hopelessly unthreadsafe
263 void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID
) {
264 __CFSpinLock(&__CFBigRuntimeFunnel
);
265 __CFRuntimeClassTable
[typeID
] = NULL
;
266 __CFSpinUnlock(&__CFBigRuntimeFunnel
);
270 #if defined(DEBUG) || defined(ENABLE_ZOMBIES)
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)
279 static uint32_t __CFZombieLevel
= 0x0;
280 __private_extern__
uint8_t __CFZombieEnabled
= 0;
281 __private_extern__
uint8_t __CFDeallocateZombies
= 0;
283 static void *_original_objc_dealloc
= 0;
286 void _CFEnableZombies(void) {
287 __CFZombieEnabled
= 0xFF;
292 // XXX_PCB: use the class version field as a bitmask, to allow classes to opt-in for GC scanning.
294 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
295 CF_INLINE CFOptionFlags
CF_GET_COLLECTABLE_MEMORY_TYPE(const CFRuntimeClass
*cls
)
297 return ((cls
->version
& _kCFRuntimeScannedObject
) ? __kCFAllocatorGCScannedMemory
: 0) | __kCFAllocatorGCObjectMemory
;
300 #define CF_GET_COLLECTABLE_MEMORY_TYPE(x) (0)
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
];
309 allocator
= (NULL
== allocator
) ? __CFGetDefaultAllocator() : allocator
;
310 if (kCFAllocatorNull
== allocator
) {
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
);
322 CFLog(kCFLogLevelCritical
, CFSTR("%@"), msg
);
328 if (!kCFUseCollectableAllocator
|| !CF_IS_COLLECTABLE_ALLOCATOR(allocator
) || !(CF_GET_COLLECTABLE_MEMORY_TYPE(cls
) & __kCFAllocatorGCScannedMemory
)) {
329 memset(memory
, 0, size
);
331 if (__CFOASafe
&& category
) {
332 __CFSetLastAllocationEventName(memory
, (char *)category
);
333 } else if (__CFOASafe
) {
334 __CFSetLastAllocationEventName(memory
, (char *)cls
->className
);
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
));
342 memory
->_cfisa
= __CFISAForTypeID(typeID
);
344 *(uint32_t *)(memory
->_cfinfo
) = (uint32_t)((0 << 24) + ((typeID
& 0xFFFF) << 8) + (usesSystemDefaultAllocator
? 0x80 : 0x00));
347 *(uint32_t *)(memory
->_cfinfo
) = (uint32_t)((1 << 24) + ((typeID
& 0xFFFF) << 8) + (usesSystemDefaultAllocator
? 0x80 : 0x00));
349 if (NULL
!= cls
->init
) {
355 void _CFRuntimeInitStaticInstance(void *ptr
, CFTypeID typeID
) {
356 CFAssert1(typeID
!= _kCFRuntimeNotATypeID
, __kCFLogAssertion
, "%s(): Uninitialized type id", __PRETTY_FUNCTION__
);
357 if (NULL
== __CFRuntimeClassTable
[typeID
]) {
360 CFRuntimeBase
*memory
= (CFRuntimeBase
*)ptr
;
361 memory
->_cfisa
= __CFISAForTypeID(typeID
);
362 *(uint32_t *)(memory
->_cfinfo
) = (uint32_t)((0 << 24) + ((typeID
& 0xFFFF) << 8) + 0x80);
366 if (NULL
!= __CFRuntimeClassTable
[typeID
]->init
) {
367 (__CFRuntimeClassTable
[typeID
]->init
)(memory
);
371 void _CFRuntimeSetInstanceTypeID(CFTypeRef cf
, CFTypeID typeID
) {
372 *(uint16_t *)(((CFRuntimeBase
*)cf
)->_cfinfo
+ 1) = (uint16_t)(typeID
& 0xFFFF);
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;
391 __kCFObjectRetainedEvent
= 12,
392 __kCFObjectReleasedEvent
= 13
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
405 // we disguise pointers so that programs like 'leaks' forget about these references
406 #define DISGUISE(O) (~(uintptr_t)(O))
410 CFBasicHashRef table
;
411 uint8_t padding
[64 - sizeof(CFBasicHashRef
) - sizeof(CFSpinLock_t
)];
412 } __NSRetainCounters
[NUM_EXTERN_TABLES
];
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
422 int thr
= pthread_is_threaded_np();
424 CFSpinLock_t
*lock
= &__NSRetainCounters
[idx
].lock
;
425 CFBasicHashRef table
= __NSRetainCounters
[idx
].table
;
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
);
443 if (thr
) __CFSpinLock(lock
);
444 count
= (uintptr_t)CFBasicHashGetCountOfKey(table
, disguised
);
445 if (thr
) __CFSpinUnlock(lock
);
452 CFTypeID
__CFGenericTypeID(const void *cf
) {
453 return (*(uint32_t *)(((CFRuntimeBase
*)cf
)->_cfinfo
) >> 8) & 0xFFFF;
456 CF_INLINE CFTypeID
__CFGenericTypeID_inline(const void *cf
) {
457 return (*(uint32_t *)(((CFRuntimeBase
*)cf
)->_cfinfo
) >> 8) & 0xFFFF;
460 CFTypeID
CFTypeGetTypeID(void) {
461 return __kCFTypeTypeID
;
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
); \
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);
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)
481 CF_INLINE
int CFTYPE_IS_OBJC(const void *obj
) {
482 CFTypeID typeID
= __CFGenericTypeID_inline(obj
);
483 return CF_IS_OBJC(typeID
, obj
);
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));}
499 CFTypeID
CFGetTypeID(CFTypeRef cf
) {
501 if (NULL
== cf
) HALT
;
503 CFTYPE_OBJC_FUNCDISPATCH0(CFTypeID
, cf
, "_cfTypeID");
504 __CFGenericAssertIsCF(cf
);
505 return __CFGenericTypeID_inline(cf
);
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
);
513 // Bit 31 (highest bit) in second word of cf instance indicates external ref count
515 CF_EXPORT
void _CFRelease(CFTypeRef cf
);
516 CF_EXPORT CFTypeRef
_CFRetain(CFTypeRef cf
);
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
);
526 // special case CF objects for performance.
527 return _CFRetain(cf
);
530 CFTYPE_OBJC_FUNCDISPATCH0(CFTypeRef
, cf
, "retain");
531 if (cf
) __CFGenericAssertIsCF(cf
);
532 return _CFRetain(cf
);
535 __private_extern__
void __CFAllocatorDeallocate(CFTypeRef cf
);
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
);
545 // special-case CF objects for better performance.
552 void **addrs
[2] = {&&start
, &&end
};
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]);
559 CFTYPE_OBJC_FUNCDISPATCH0(void, cf
, "release");
560 if (cf
) __CFGenericAssertIsCF(cf
);
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
);
575 return (const void *)result
;
578 extern void CFCollection_non_gc_storage_error(void);
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.
595 ; // don't retain normal CF objects
598 // support constant CFTypeRef objects.
600 uint32_t lowBits
= ((CFRuntimeBase
*)cf
)->_rc
;
602 uint32_t lowBits
= ((CFRuntimeBase
*)cf
)->_cfinfo
[CF_RC_BITS
];
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.
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.
633 // avoid releasing normal CF objects. Like other collections, for example
638 // support constant CFTypeRef objects.
640 uint32_t lowBits
= ((CFRuntimeBase
*)cf
)->_rc
;
642 uint32_t lowBits
= ((CFRuntimeBase
*)cf
)->_cfinfo
[CF_RC_BITS
];
644 if (lowBits
== 0) return;
651 static CFSpinLock_t __CFRuntimeExternRefCountTableLock
= CFSpinLockInit
;
652 static CFMutableBagRef __CFRuntimeExternRefCountTable
= NULL
;
655 static uint64_t __CFGetFullRetainCount(CFTypeRef cf
) {
656 if (NULL
== cf
) HALT
;
658 uint32_t lowBits
= ((CFRuntimeBase
*)cf
)->_rc
;
660 return (uint64_t)0x0fffffffffffffffULL
;
664 uint32_t lowBits
= ((CFRuntimeBase
*)cf
)->_cfinfo
[CF_RC_BITS
];
666 return (uint64_t)0x0fffffffffffffffULL
;
668 uint64_t highBits
= 0;
669 if ((lowBits
& 0x80) != 0) {
670 highBits
= __CFDoExternRefOperation(500, (id
)cf
);
672 uint64_t compositeRC
= (lowBits
& 0x7f) + (highBits
<< 6);
677 CFTypeRef
_CFRetainGC(CFTypeRef cf
) {
679 if (kCFUseCollectableAllocator
&& !CF_IS_COLLECTABLE(cf
)) {
680 fprintf(stderr
, "non-auto object %p passed to _CFRetainGC.\n", cf
);
684 return kCFUseCollectableAllocator
? cf
: CFRetain(cf
);
687 void _CFReleaseGC(CFTypeRef cf
) {
689 if (kCFUseCollectableAllocator
&& !CF_IS_COLLECTABLE(cf
)) {
690 fprintf(stderr
, "non-auto object %p passed to _CFReleaseGC.\n", cf
);
694 if (!kCFUseCollectableAllocator
) CFRelease(cf
);
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
);
702 CFTYPE_OBJC_FUNCDISPATCH0(CFIndex
, cf
, "retainCount");
703 __CFGenericAssertIsCF(cf
);
705 uint64_t rc
= __CFGetFullRetainCount(cf
);
706 return (rc
< (uint64_t)LONG_MAX
) ? (CFIndex
)rc
: (CFIndex
)LONG_MAX
;
709 CFTypeRef
CFMakeCollectable(CFTypeRef cf
) {
710 if (NULL
== cf
) return NULL
;
711 if (CF_IS_COLLECTABLE(cf
)) {
712 objc_assertRegisteredThreadWithCollector();
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
);
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
);
728 if (CFGetRetainCount(cf
) == 0) {
729 CFLog(kCFLogLevelWarning
, CFSTR("object %p with 0 retain-count passed to CFMakeCollectable."), cf
);
737 CFTypeRef
CFMakeUncollectable(CFTypeRef cf
) {
738 if (NULL
== cf
) return NULL
;
739 if (CF_IS_COLLECTABLE(cf
)) {
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
);
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
);
767 return (CFHashCode
)cf
;
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
)) {
775 CFStringRef (*func
)(void *, SEL
) = (CFStringRef (*)(void *, SEL
))objc_msgSend
;
776 if (!s
) s
= sel_registerName("_copyDescription");
777 CFStringRef result
= func((void *)cf
, s
);
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
) {
784 result
= __CFRuntimeClassTable
[__CFGenericTypeID_inline(cf
)]->copyDebugDesc(cf
);
785 if (NULL
!= result
) return result
;
787 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<%s %p [%p]>"), __CFRuntimeClassTable
[__CFGenericTypeID_inline(cf
)]->className
, cf
, CFGetAllocator(cf
));
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
);
802 __CFGenericAssertIsCF(cf
);
803 if (NULL
!= __CFRuntimeClassTable
[__CFGenericTypeID_inline(cf
)]->copyFormattingDesc
) {
804 return __CFRuntimeClassTable
[__CFGenericTypeID_inline(cf
)]->copyFormattingDesc(cf
, formatOptions
);
809 extern CFAllocatorRef
__CFAllocatorGetAllocator(CFTypeRef
);
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
);
819 return __CFGetAllocator(cf
);
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);
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);
854 extern void __CFStreamInitialize(void);
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");
863 static void __01121__(void) {
864 __CF120291
= pthread_is_threaded_np() ? true : false;
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.
875 __crashreporter_info__
= "*** multi-threaded process forked ***";
877 __crashreporter_info__
= "*** single-threaded process forked ***";
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"
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);
893 CF_EXPORT
const void *__CFArgStuff
;
894 const void *__CFArgStuff
= NULL
;
895 __private_extern__
void *__CFAppleLanguages
= 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
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
;
934 #if DEPLOYMENT_TARGET_WINDOWS
935 #define kNilPthreadT { nil, nil }
937 #define kNilPthreadT (pthread_t)0
941 CF_EXPORT pthread_t _CFMainPThread
;
942 pthread_t _CFMainPThread
= kNilPthreadT
;
944 CF_EXPORT
bool kCFUseCollectableAllocator
= false;
946 __private_extern__ Boolean __CFProphylacticAutofsAccess
= false;
948 #if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
949 static void __CFInitialize(void) __attribute__ ((constructor
));
952 #if DEPLOYMENT_TARGET_WINDOWS
955 void __CFInitialize(void) {
956 static int __done
= 0;
961 if (!pthread_main_np()) HALT
; // CoreFoundation must be initialized on the main thread
963 _CFMainPThread
= pthread_self();
965 __CFProphylacticAutofsAccess
= true;
967 for (CFIndex idx
= 0; idx
< sizeof(__CFEnv
) / sizeof(__CFEnv
[0]); idx
++) {
968 __CFEnv
[idx
].value
= __CFEnv
[idx
].name
? getenv(__CFEnv
[idx
].name
) : NULL
;
971 kCFUseCollectableAllocator
= objc_collectingEnabled();
972 if (kCFUseCollectableAllocator
) {
973 __CFObjCIsCollectable
= (bool (*)(void *))objc_isAuto
;
975 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
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
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;
991 _original_objc_dealloc
= (void *)_dealloc
;
994 value
= __CFgetenv("CFZombieLevel");
996 __CFZombieLevel
= (uint32_t)strtoul_l(value
, NULL
, 0, NULL
);
998 if (0x0 == __CFZombieLevel
) __CFZombieLevel
= 0x0000FC00; // default
1002 __CFRuntimeClassTableSize
= 1024;
1003 __CFRuntimeClassTable
= (CFRuntimeClass
**)calloc(__CFRuntimeClassTableSize
, sizeof(CFRuntimeClass
*));
1004 __CFRuntimeObjCClassTable
= (uintptr_t *)calloc(__CFRuntimeClassTableSize
, sizeof(uintptr_t));
1005 __CFBaseInitialize();
1007 _CFRuntimeBridgeClasses(0, "__NSCFType");
1008 for (CFIndex idx
= 1; idx
< __CFRuntimeClassTableSize
; idx
++) {
1009 __CFRuntimeObjCClassTable
[idx
] = __CFRuntimeObjCClassTable
[0];
1012 /* Here so that two runtime classes get indices 0, 1. */
1013 __kCFNotATypeTypeID
= _CFRuntimeRegisterClass(&__CFNotATypeClass
);
1014 __kCFTypeTypeID
= _CFRuntimeRegisterClass(&__CFTypeClass
);
1016 /* Here so that __kCFAllocatorTypeID gets index 2. */
1017 __CFAllocatorInitialize();
1019 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
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);
1037 CFBasicHashGetTypeID();
1040 // Creating this lazily in CFRetain causes recursive call to CFRetain
1041 __CFRuntimeExternRefCountTable
= CFBagCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
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
;
1050 /*** _CFRuntimeCreateInstance() can finally be called generally after this line. ***/
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
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();
1075 __CFUUIDInitialize();
1076 __CFMessagePortInitialize();
1077 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1078 __CFMachPortInitialize();
1080 __CFStreamInitialize();
1081 __CFRunLoopInitialize();
1082 __CFRunLoopObserverInitialize();
1083 __CFRunLoopSourceInitialize();
1084 __CFRunLoopTimerInitialize();
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
);
1102 printf("CF - Error converting command line arg string to ascii: %x\n", (unsigned int)wideArgs
[y
]);
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.
1121 if (NULL
!= list
[count
]) count
++;
1123 __CFArgStuff
= CFArrayCreate(kCFAllocatorSystemDefault
, (const void **)list
, count
, &kCFTypeArrayCallBacks
);
1126 _CFProcessPath(); // cache this early
1129 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1133 if (__CFRuntimeClassTableCount
< 256) __CFRuntimeClassTableCount
= 256;
1134 __CFSendObjCMsg
= (void *(*)(const void *, SEL
, ...))objc_msgSend
;
1136 #if DEPLOYMENT_TARGET_MACOSX
1137 #elif DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_EMBEDDED
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";
1148 #elif DEPLOYMENT_TARGET_WINDOWS
1153 #if defined(DEBUG) && (DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED)
1154 CFLog(kCFLogLevelWarning
, CFSTR("Assertions enabled"));
1157 __CFProphylacticAutofsAccess
= false;
1162 #if DEPLOYMENT_TARGET_WINDOWS
1164 static CFBundleRef
RegisterCoreFoundationBundle(void) {
1166 // might be nice to get this from the project file at some point
1167 wchar_t *DLLFileName
= (wchar_t *)L
"CoreFoundation_debug.dll";
1169 wchar_t *DLLFileName
= (wchar_t *)L
"CoreFoundation.dll";
1171 wchar_t path
[MAX_PATH
+1];
1172 path
[0] = path
[1] = 0;
1175 HMODULE ourModule
= GetModuleHandleW(DLLFileName
);
1177 CFAssert(ourModule
, __kCFLogAssertion
, "GetModuleHandle failed");
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
);
1183 // strip off last component, the DLL name
1184 for (idx
= wResult
- 1; idx
; idx
--) {
1185 if ('\\' == path
[idx
]) {
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
);
1197 // this registers us so subsequent calls to CFBundleGetBundleWithIdentifier will succeed
1198 CFBundleRef bundle
= CFBundleCreate(kCFAllocatorSystemDefault
, bundleURL
);
1199 CFRelease(bundleURL
);
1205 #define DLL_PROCESS_ATTACH 1
1206 #define DLL_THREAD_ATTACH 2
1207 #define DLL_THREAD_DETACH 3
1208 #define DLL_PROCESS_DETACH 0
1210 int DllMain( HINSTANCE hInstance
, DWORD dwReason
, LPVOID pReserved
) {
1211 static CFBundleRef cfBundle
= NULL
;
1212 if (dwReason
== DLL_PROCESS_ATTACH
) {
1214 cfBundle
= RegisterCoreFoundationBundle();
1215 } else if (dwReason
== DLL_PROCESS_DETACH
) {
1216 __CFStreamCleanup();
1217 __CFSocketCleanup();
1218 __CFUniCharCleanup();
1221 if (cfBundle
) CFRelease(cfBundle
);
1222 __CFStringCleanup();
1223 } else if (dwReason
== DLL_THREAD_DETACH
) {
1224 __CFFinalizeThreadData(NULL
);
1231 CF_EXPORT CFTypeRef
_CFRetain(CFTypeRef cf
) {
1232 if (NULL
== cf
) return NULL
;
1233 Boolean didAuto
= false;
1235 if (0 == ((CFRuntimeBase
*)cf
)->_rc
&& !CF_IS_COLLECTABLE(cf
)) return cf
; // Constant CFTypeRef
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
);
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
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.
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
);
1273 __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock
);
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
);
1282 } while (__builtin_expect(!success
, 0));
1284 if (!didAuto
&& __builtin_expect(__CFOASafe
, 0)) {
1285 __CFRecordAllocationEvent(__kCFRetainEvent
, (void *)cf
, 0, CFGetRetainCount(cf
), NULL
);
1290 CF_EXPORT
void _CFRelease(CFTypeRef cf
) {
1291 CFTypeID typeID
= __CFGenericTypeID_inline(cf
);
1292 Boolean isAllocator
= (__kCFAllocatorTypeID_CONST
== typeID
);
1293 Boolean didAuto
= false;
1297 lowBits
= ((CFRuntimeBase
*)cf
)->_rc
;
1299 if (CF_IS_COLLECTABLE(cf
)) auto_zone_release(auto_zone(), (void*)cf
);
1300 return; // Constant CFTypeRef
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
);
1308 if (!CF_IS_COLLECTABLE(cf
)) {
1309 void (*func
)(CFTypeRef
) = __CFRuntimeClassTable
[typeID
]->finalize
;
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
)) {
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
);
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
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
);
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.
1353 auto_zone_release(auto_zone(), (void*)cf
);
1360 void (*func
)(CFTypeRef
) = __CFRuntimeClassTable
[typeID
]->finalize
;
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)) {
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);
1387 __CFBitfieldSetValue(prospectiveNewInfo
, RC_END
, RC_START
, ((1 << 6) | (1 << 7)) - 1);
1389 success
= OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo
, *(int32_t *)&prospectiveNewInfo
, (int32_t *)infoLocation
);
1390 if (__builtin_expect(success
, 1)) {
1391 __CFDoExternRefOperation(450, (id
)cf
);
1393 __CFSpinUnlock(&__CFRuntimeExternRefCountTableLock
);
1395 prospectiveNewInfo
-= (1 << RC_START
);
1396 success
= OSAtomicCompareAndSwap32Barrier(*(int32_t *)&initialCheckInfo
, *(int32_t *)&prospectiveNewInfo
, (int32_t *)infoLocation
);
1399 } while (__builtin_expect(!success
, 0));
1402 if (!didAuto
&& __builtin_expect(__CFOASafe
, 0)) {
1403 __CFRecordAllocationEvent(__kCFReleaseEvent
, (void *)cf
, 0, CFGetRetainCount(cf
), NULL
);
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
);
1412 // cannot zombify allocators, which get deallocated by __CFAllocatorDeallocate (finalize)
1414 __CFAllocatorDeallocate((void *)cf
);
1416 CFAllocatorRef allocator
= kCFAllocatorSystemDefault
;
1417 Boolean usesSystemDefaultAllocator
= true;
1419 if (!__CFBitfieldGetValue(((const CFRuntimeBase
*)cf
)->_cfinfo
[CF_INFO_BITS
], 7, 7)) {
1420 allocator
= CFGetAllocator(cf
);
1421 usesSystemDefaultAllocator
= (allocator
== kCFAllocatorSystemDefault
);
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
;
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
);
1440 if (!name
) name
= "$class-unknown$";
1442 asprintf(&cname
, "_NSZombie_%s", name
);
1443 Class zclass
= (Class
)objc_lookUpClass(cname
);
1445 zclass
= objc_duplicateClass((Class
)objc_lookUpClass("_NSZombie_"), cname
, 0);
1449 #if DEPLOYMENT_TARGET_MACOSX
1450 if (object_getClass((id
)cls
)) {
1451 objc_destructInstance((id
)cf
);
1454 if (__CFDeallocateZombies
) {
1456 object_setClass((id
)cf
, zclass
);
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
);
1464 CFAllocatorDeallocate(allocator
, (uint8_t *)cf
- (usesSystemDefaultAllocator
? 0 : sizeof(CFAllocatorRef
)));
1466 object_setClass((id
)cf
, zclass
);
1470 extern uintptr_t __CFFindPointer(uintptr_t ptr
, uintptr_t start
);
1471 uintptr_t res
= __CFFindPointer((uintptr_t)cf
, 0);
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
);
1476 res
= __CFFindPointer((uintptr_t)cf
, res
+ 1);
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
);
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
));
1497 if (__CFZombieLevel
& (1 << 7)) {
1498 byte
= (__CFZombieLevel
>> 8) & 0xFF;
1500 memset(ptr
, byte
, size
);
1502 if (kCFUseCollectableAllocator
|| !(__CFZombieLevel
& (1 << 4))) {
1503 CFAllocatorDeallocate(allocator
, (uint8_t *)cf
- (usesSystemDefaultAllocator
? 0 : sizeof(CFAllocatorRef
)));
1507 if (kCFAllocatorSystemDefault
!= allocator
) {
1508 CFRelease(allocator
);
1513 #undef __kCFAllocatorTypeID_CONST
1514 #undef __CFGenericAssertIsCF