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