]> git.saurik.com Git - apple/cf.git/blob - Base.subproj/CFRuntime.c
CF-368.25.tar.gz
[apple/cf.git] / Base.subproj / CFRuntime.c
1 /*
2 * Copyright (c) 2005 Apple Computer, 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 /* CFRuntime.c
24 Copyright 1999-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
26 */
27
28 #define ENABLE_ZOMBIES 1
29
30 #include "CFRuntime.h"
31 #include "CFInternal.h"
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #if defined(__MACH__)
36 #include <dlfcn.h>
37 #include <monitor.h>
38 #include <crt_externs.h>
39 #include <objc/objc-auto.h>
40 #include <objc/objc-runtime.h>
41 #else
42 #endif
43
44 #if defined(__MACH__)
45 extern void __CFRecordAllocationEvent(int eventnum, void *ptr, int size, int data, const char *classname);
46 #else
47 #define __CFRecordAllocationEvent(a, b, c, d, e)
48 #endif
49
50 #if defined(__MACH__)
51 extern BOOL objc_isAuto(id object);
52 extern void* objc_assign_ivar_address_CF(void *value, void *base, void **slot);
53 extern void* objc_assign_strongCast_CF(void* value, void **slot);
54 #endif
55
56 enum {
57 // retain/release recording constants -- must match values
58 // used by OA for now; probably will change in the future
59 __kCFRetainEvent = 28,
60 __kCFReleaseEvent = 29
61 };
62
63 /* On Win32 we should use _msize instead of malloc_size
64 * (Aleksey Dukhnyakov)
65 */
66 #if defined(__WIN32__)
67 #include <malloc.h>
68 CF_INLINE size_t malloc_size(void *memblock) {
69 return _msize(memblock);
70 }
71 #else
72 #include <malloc/malloc.h>
73 #endif
74
75 #if defined(__MACH__)
76
77 bool __CFOASafe = false;
78
79 void __CFOAInitialize(void) {
80 static void (*dyfunc)(void) = (void *)0xFFFFFFFF;
81 if (NULL == getenv("OAKeepAllocationStatistics")) return;
82 if ((void *)0xFFFFFFFF == dyfunc) {
83 dyfunc = dlsym(RTLD_DEFAULT, "_OAInitialize");
84 }
85 if (NULL != dyfunc) {
86 dyfunc();
87 __CFOASafe = true;
88 }
89 }
90
91 void __CFRecordAllocationEvent(int eventnum, void *ptr, int size, int data, const char *classname) {
92 static void (*dyfunc)(int, void *, int, int, const char *) = (void *)0xFFFFFFFF;
93 if (!__CFOASafe) return;
94 if ((void *)0xFFFFFFFF == dyfunc) {
95 dyfunc = dlsym(RTLD_DEFAULT, "_OARecordAllocationEvent");
96 }
97 if (NULL != dyfunc) {
98 dyfunc(eventnum, ptr, size, data, classname);
99 }
100 }
101
102 void __CFSetLastAllocationEventName(void *ptr, const char *classname) {
103 static void (*dyfunc)(void *, const char *) = (void *)0xFFFFFFFF;
104 if (!__CFOASafe) return;
105 if ((void *)0xFFFFFFFF == dyfunc) {
106 dyfunc = dlsym(RTLD_DEFAULT, "_OASetLastAllocationEventName");
107 }
108 if (NULL != dyfunc) {
109 dyfunc(ptr, classname);
110 }
111 }
112 #endif
113
114 extern void __HALT(void);
115
116 static CFTypeID __kCFNotATypeTypeID = _kCFRuntimeNotATypeID;
117
118 static const CFRuntimeClass __CFNotATypeClass = {
119 0,
120 "Not A Type",
121 (void *)__HALT,
122 (void *)__HALT,
123 (void *)__HALT,
124 (void *)__HALT,
125 (void *)__HALT,
126 (void *)__HALT,
127 (void *)__HALT
128 };
129
130 static CFTypeID __kCFTypeTypeID = _kCFRuntimeNotATypeID;
131
132 static const CFRuntimeClass __CFTypeClass = {
133 0,
134 "CFType",
135 (void *)__HALT,
136 (void *)__HALT,
137 (void *)__HALT,
138 (void *)__HALT,
139 (void *)__HALT,
140 (void *)__HALT,
141 (void *)__HALT
142 };
143
144 /* bits 15-8 in the CFRuntimeBase _info are type */
145 /* bits 7-0 in the CFRuntimeBase are reserved for CF's use */
146
147 static CFRuntimeClass * __CFRuntimeClassTable[__CFMaxRuntimeTypes] = {NULL};
148 static int32_t __CFRuntimeClassTableCount = 0;
149
150 #if defined(__MACH__)
151
152 #if !defined(__ppc__)
153 __private_extern__ void * (*__CFSendObjCMsg)(const void *, SEL, ...) = NULL;
154 #endif
155
156 __private_extern__ malloc_zone_t *__CFCollectableZone = NULL;
157
158 static bool objc_isCollectable_nope(void* obj) { return false; }
159 bool (*__CFObjCIsCollectable)(void *) = NULL;
160
161 static const void* objc_AssignIvar_none(const void *value, void *base, const void **slot) { return (*slot = value); }
162 const void* (*__CFObjCAssignIvar)(const void *value, const void *base, const void **slot) = objc_AssignIvar_none;
163
164 static const void* objc_StrongAssign_none(const void *value, const void **slot) { return (*slot = value); }
165 const void* (*__CFObjCStrongAssign)(const void *value, const void **slot) = objc_StrongAssign_none;
166
167 void* (*__CFObjCMemmoveCollectable)(void *dst, const void *, unsigned) = memmove;
168
169 // GC: to be moved to objc if necessary.
170 static void objc_WriteBarrierRange_none(void *ptr, unsigned size) {}
171 static void objc_WriteBarrierRange_auto(void *ptr, unsigned size) { auto_zone_write_barrier_range(__CFCollectableZone, ptr, size); }
172 void (*__CFObjCWriteBarrierRange)(void *, unsigned) = objc_WriteBarrierRange_none;
173
174 // Temporarily disabled __private_extern__
175 #warning Ali, be sure to reexamine this
176 struct objc_class *__CFRuntimeObjCClassTable[__CFMaxRuntimeTypes] = {NULL};
177
178 #endif
179
180 // Compiler uses this symbol name
181 int __CFConstantStringClassReference[10] = {0};
182
183 #if defined(__MACH__)
184 static struct objc_class __CFNSTypeClass = {{0, 0}, NULL, {0, 0, 0, 0, 0, 0, 0}};
185 #endif
186
187 //static CFSpinLock_t __CFRuntimeLock = 0;
188
189 CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls) {
190 // version field must be 0
191 // className must be pure ASCII string, non-null
192 if (__CFMaxRuntimeTypes <= __CFRuntimeClassTableCount) {
193 CFLog(0, CFSTR("*** CoreFoundation class table full; registration failing for class '%s'. Program will crash soon."), cls->className);
194 return _kCFRuntimeNotATypeID;
195 }
196 __CFRuntimeClassTable[__CFRuntimeClassTableCount++] = (CFRuntimeClass *)cls;
197 return __CFRuntimeClassTableCount - 1;
198 }
199
200 void _CFRuntimeInitializeClassForBridging(CFTypeID typeID) {
201 __CFRuntimeObjCClassTable[typeID] = (struct objc_class *)calloc(sizeof(struct objc_class), 1);
202 }
203
204 Boolean _CFRuntimeSetupBridging(CFTypeID typeID, struct objc_class *mainClass, struct objc_class *subClass) {
205 void *isa = __CFISAForTypeID(typeID);
206 memmove(isa, subClass, sizeof(struct objc_class));
207 class_poseAs(isa, mainClass);
208 return true;
209 }
210
211 const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID) {
212 return __CFRuntimeClassTable[typeID];
213 }
214
215 void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID) {
216 __CFRuntimeClassTable[typeID] = NULL;
217 }
218
219
220 #if defined(DEBUG) || defined(ENABLE_ZOMBIES)
221
222 /* CFZombieLevel levels:
223 * bit 0: scribble deallocated CF object memory
224 * bit 1: do not scribble on CFRuntimeBase header (when bit 0)
225 * bit 4: do not free CF objects
226 * bit 7: use 3rd-order byte as scribble byte for dealloc (otherwise 0xFC)
227 * bit 16: scribble allocated CF object memory
228 * bit 23: use 1st-order byte as scribble byte for alloc (otherwise 0xCF)
229 */
230
231 static uint32_t __CFZombieLevel = 0x0;
232
233 static void __CFZombifyAllocatedMemory(void *cf) {
234 if (__CFZombieLevel & (1 << 16)) {
235 void *ptr = cf;
236 size_t size = malloc_size(cf);
237 uint8_t byte = 0xCF;
238 if (__CFZombieLevel & (1 << 23)) {
239 byte = (__CFZombieLevel >> 24) & 0xFF;
240 }
241 memset(ptr, byte, size);
242 }
243 }
244
245 static void __CFZombifyDeallocatedMemory(void *cf) {
246 if (__CFZombieLevel & (1 << 0)) {
247 void *ptr = cf;
248 size_t size = malloc_size(cf);
249 uint8_t byte = 0xFC;
250 if (__CFZombieLevel & (1 << 1)) {
251 ptr += sizeof(CFRuntimeBase);
252 size -= sizeof(CFRuntimeBase);
253 }
254 if (__CFZombieLevel & (1 << 7)) {
255 byte = (__CFZombieLevel >> 8) & 0xFF;
256 }
257 memset(ptr, byte, size);
258 }
259 }
260
261 #endif /* DEBUG */
262
263 // XXX_PCB: use the class version field as a bitmask, to allow classes to opt-in for GC scanning.
264
265 CF_INLINE CFOptionFlags CF_GET_COLLECTABLE_MEMORY_TYPE(const CFRuntimeClass *cls)
266 {
267 return (cls->version & _kCFRuntimeScannedObject) ? AUTO_OBJECT_SCANNED : AUTO_OBJECT_UNSCANNED;
268 }
269
270 CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, uint32_t extraBytes, unsigned char *category) {
271 CFRuntimeBase *memory;
272 Boolean usesSystemDefaultAllocator;
273 int32_t size;
274
275 CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__);
276
277 if (NULL == __CFRuntimeClassTable[typeID]) {
278 return NULL;
279 }
280 allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator;
281 usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault);
282 extraBytes = (extraBytes + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1);
283 size = sizeof(CFRuntimeBase) + extraBytes + (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef));
284 // CFType version 0 objects are unscanned by default since they don't have write-barriers and hard retain their innards
285 // CFType version 1 objects are scanned and use hand coded write-barriers to store collectable storage within
286 memory = CFAllocatorAllocate(allocator, size, CF_GET_COLLECTABLE_MEMORY_TYPE(__CFRuntimeClassTable[typeID]));
287 if (NULL == memory) {
288 return NULL;
289 }
290 #if defined(DEBUG) || defined(ENABLE_ZOMBIES)
291 __CFZombifyAllocatedMemory((void *)memory);
292 #endif
293 if (__CFOASafe && category) {
294 __CFSetLastAllocationEventName(memory, category);
295 } else if (__CFOASafe) {
296 __CFSetLastAllocationEventName(memory, __CFRuntimeClassTable[typeID]->className);
297 }
298 if (!usesSystemDefaultAllocator) {
299 // add space to hold allocator ref for non-standard allocators.
300 // (this screws up 8 byte alignment but seems to work)
301 *(CFAllocatorRef *)((char *)memory) = CFRetain(allocator);
302 memory = (CFRuntimeBase *)((char *)memory + sizeof(CFAllocatorRef));
303 }
304 memory->_isa = __CFISAForTypeID(typeID);
305 memory->_rc = 1;
306 memory->_info = 0;
307 __CFBitfieldSetValue(memory->_info, 15, 8, typeID);
308 if (usesSystemDefaultAllocator) {
309 __CFBitfieldSetValue(memory->_info, 7, 7, 1);
310 }
311 if (NULL != __CFRuntimeClassTable[typeID]->init) {
312 (__CFRuntimeClassTable[typeID]->init)(memory);
313 }
314 return memory;
315 }
316
317 void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID typeID) {
318 __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_info, 15, 8, typeID);
319 }
320
321 CFTypeID __CFGenericTypeID(const void *cf) {
322 return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 15, 8);
323 }
324
325 CF_INLINE CFTypeID __CFGenericTypeID_inline(const void *cf) {
326 return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 15, 8);
327 }
328
329 CFTypeID CFTypeGetTypeID(void) {
330 return __kCFTypeTypeID;
331 }
332
333 __private_extern__ void __CFGenericValidateType_(CFTypeRef cf, CFTypeID type, const char *func) {
334 if (cf && CF_IS_OBJC(type, cf)) return;
335 CFAssert2((cf != NULL) && (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]) && (__kCFNotATypeTypeID != __CFGenericTypeID_inline(cf)) && (__kCFTypeTypeID != __CFGenericTypeID_inline(cf)), __kCFLogAssertion, "%s(): pointer 0x%x is not a CF object", func, cf); \
336 CFAssert3(__CFGenericTypeID_inline(cf) == type, __kCFLogAssertion, "%s(): pointer 0x%x is not a %s", func, cf, __CFRuntimeClassTable[type]->className); \
337 }
338
339 #define __CFGenericAssertIsCF(cf) \
340 CFAssert2(cf != NULL && (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]) && (__kCFNotATypeTypeID != __CFGenericTypeID_inline(cf)) && (__kCFTypeTypeID != __CFGenericTypeID_inline(cf)), __kCFLogAssertion, "%s(): pointer 0x%x is not a CF object", __PRETTY_FUNCTION__, cf);
341
342 #if !defined(__MACH__)
343
344 #define CFTYPE_IS_OBJC(obj) (false)
345 #define CFTYPE_OBJC_FUNCDISPATCH0(rettype, obj, sel) do {} while (0)
346 #define CFTYPE_OBJC_FUNCDISPATCH1(rettype, obj, sel, a1) do {} while (0)
347
348 #endif
349
350 #if defined(__MACH__)
351
352 CF_INLINE int CFTYPE_IS_OBJC(const void *obj) {
353 CFTypeID typeID = __CFGenericTypeID_inline(obj);
354 return CF_IS_OBJC(typeID, obj);
355 }
356
357 #define CFTYPE_OBJC_FUNCDISPATCH0(rettype, obj, sel) \
358 if (CFTYPE_IS_OBJC(obj)) \
359 {rettype (*func)(void *, SEL) = (void *)__CFSendObjCMsg; \
360 static SEL s = NULL; if (!s) s = sel_registerName(sel); \
361 return func((void *)obj, s);}
362 #define CFTYPE_OBJC_FUNCDISPATCH1(rettype, obj, sel, a1) \
363 if (CFTYPE_IS_OBJC(obj)) \
364 {rettype (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg; \
365 static SEL s = NULL; if (!s) s = sel_registerName(sel); \
366 return func((void *)obj, s, (a1));}
367
368 #endif
369
370 CFTypeID CFGetTypeID(CFTypeRef cf) {
371 #if defined(DEBUG)
372 if (NULL == cf) HALT;
373 #endif
374 CFTYPE_OBJC_FUNCDISPATCH0(CFTypeID, cf, "_cfTypeID");
375 __CFGenericAssertIsCF(cf);
376 return __CFGenericTypeID_inline(cf);
377 }
378
379 CFStringRef CFCopyTypeIDDescription(CFTypeID type) {
380 CFAssert2((NULL != __CFRuntimeClassTable[type]) && __kCFNotATypeTypeID != type && __kCFTypeTypeID != type, __kCFLogAssertion, "%s(): type %d is not a CF type ID", __PRETTY_FUNCTION__, type);
381 return CFStringCreateWithCString(kCFAllocatorDefault, __CFRuntimeClassTable[type]->className, kCFStringEncodingASCII);
382 }
383
384 static CFSpinLock_t __CFGlobalRetainLock = 0;
385 static CFMutableDictionaryRef __CFRuntimeExternRefCountTable = NULL;
386
387 #define DISGUISE(object) ((void *)(((unsigned)object) + 1))
388 #define UNDISGUISE(disguised) ((id)(((unsigned)disguised) - 1))
389
390 extern void _CFDictionaryIncrementValue(CFMutableDictionaryRef dict, const void *key);
391 extern int _CFDictionaryDecrementValue(CFMutableDictionaryRef dict, const void *key);
392
393 // Bit 31 (highest bit) in second word of cf instance indicates external ref count
394
395 extern void _CFRelease(CFTypeRef cf);
396 extern CFTypeRef _CFRetain(CFTypeRef cf);
397 extern CFHashCode _CFHash(CFTypeRef cf);
398
399 CFTypeRef CFRetain(CFTypeRef cf) {
400 // always honor CFRetain's with a hard reference
401 if (CF_IS_COLLECTABLE(cf)) {
402 auto_zone_retain(__CFCollectableZone, (void*)cf);
403 return cf;
404 }
405 // XXX_PCB some Objc objects aren't really reference counted, perhaps they should be able to make that distinction?
406 CFTYPE_OBJC_FUNCDISPATCH0(CFTypeRef, cf, "retain");
407 __CFGenericAssertIsCF(cf);
408 return _CFRetain(cf);
409 }
410
411 __private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf);
412
413 void CFRelease(CFTypeRef cf) {
414 // make sure we get rid of the hard reference if called
415 if (CF_IS_COLLECTABLE(cf)) {
416 auto_zone_release(__CFCollectableZone, (void*)cf);
417 return;
418 }
419 // XXX_PCB some objects aren't really reference counted.
420 CFTYPE_OBJC_FUNCDISPATCH0(void, cf, "release");
421 __CFGenericAssertIsCF(cf);
422 _CFRelease(cf);
423 }
424
425 static uint64_t __CFGetFullRetainCount(CFTypeRef cf) {
426 uint32_t lowBits = 0;
427 uint64_t highBits = 0, compositeRC;
428 lowBits = ((CFRuntimeBase *)cf)->_rc;
429 if (0 == lowBits) {
430 return (uint64_t)0x00ffffffffffffffULL;
431 }
432 if ((lowBits & 0x08000) != 0) {
433 highBits = (uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf));
434 }
435 compositeRC = (lowBits & 0x7fff) + (highBits << 15);
436 return compositeRC;
437 }
438
439 CFTypeRef _CFRetainGC(CFTypeRef cf)
440 {
441 #if defined(DEBUG)
442 if (CF_USING_COLLECTABLE_MEMORY && !CF_IS_COLLECTABLE(cf)) {
443 fprintf(stderr, "non-auto object %p passed to _CFRetainGC.\n", cf);
444 HALT;
445 }
446 #endif
447 return CF_USING_COLLECTABLE_MEMORY ? cf : CFRetain(cf);
448 }
449
450 void _CFReleaseGC(CFTypeRef cf)
451 {
452 #if defined(DEBUG)
453 if (CF_USING_COLLECTABLE_MEMORY && !CF_IS_COLLECTABLE(cf)) {
454 fprintf(stderr, "non-auto object %p passed to _CFReleaseGC.\n", cf);
455 HALT;
456 }
457 #endif
458 if (!CF_USING_COLLECTABLE_MEMORY) CFRelease(cf);
459 }
460
461 CFIndex CFGetRetainCount(CFTypeRef cf) {
462 uint64_t rc;
463 CFIndex result;
464 #if defined(DEBUG)
465 if (NULL == cf) HALT;
466 #endif
467 if (CF_IS_COLLECTABLE(cf)) {
468 return auto_zone_retain_count(__CFCollectableZone, cf);
469 }
470 CFTYPE_OBJC_FUNCDISPATCH0(CFIndex, cf, "retainCount");
471 __CFGenericAssertIsCF(cf);
472 rc = __CFGetFullRetainCount(cf);
473 result = (rc < (uint64_t)0x7FFFFFFF) ? (CFIndex)rc : (CFIndex)0x7FFFFFFF;
474 return result;
475 }
476
477 CFTypeRef CFMakeCollectable(CFTypeRef cf)
478 {
479 if (!cf) return NULL;
480 if (CF_USING_COLLECTABLE_MEMORY) {
481 #if defined(DEBUG)
482 CFAllocatorRef allocator = CFGetAllocator(cf);
483 if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
484 CFLog(0, CFSTR("object %p with non-GC allocator %p passed to CFMakeCollected."), cf, allocator);
485 HALT;
486 }
487 #endif
488 if (CFGetRetainCount(cf) == 0) {
489 CFLog(0, CFSTR("object %p with 0 retain-count passed to CFMakeCollected."), cf);
490 return cf;
491 }
492 CFRelease(cf);
493 }
494 return cf;
495 }
496
497 Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2) {
498 #if defined(DEBUG)
499 if (NULL == cf1) HALT;
500 if (NULL == cf2) HALT;
501 #endif
502 if (cf1 == cf2) return true;
503 CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf1, "isEqual:", cf2);
504 CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf2, "isEqual:", cf1);
505 __CFGenericAssertIsCF(cf1);
506 __CFGenericAssertIsCF(cf2);
507 if (__CFGenericTypeID_inline(cf1) != __CFGenericTypeID_inline(cf2)) return false;
508 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal) {
509 return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal(cf1, cf2);
510 }
511 return false;
512 }
513
514 CFHashCode CFHash(CFTypeRef cf) {
515 CFTYPE_OBJC_FUNCDISPATCH0(CFHashCode, cf, "hash");
516 __CFGenericAssertIsCF(cf);
517 return _CFHash(cf);
518 }
519
520 // definition: produces a normally non-NULL debugging description of the object
521 CFStringRef CFCopyDescription(CFTypeRef cf) {
522 #if defined(DEBUG)
523 if (NULL == cf) HALT;
524 #endif
525 if (CFTYPE_IS_OBJC(cf)) {
526 static SEL s = NULL;
527 CFStringRef (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg;
528 if (!s) s = sel_registerName("_copyDescription");
529 CFStringRef result = func((void *)cf, s);
530 if (result && CF_USING_COLLECTABLE_MEMORY) CFRetain(result); // needs hard retain
531 return result;
532 }
533 // CFTYPE_OBJC_FUNCDISPATCH0(CFStringRef, cf, "_copyDescription"); // XXX returns 0 refcounted item under GC
534 __CFGenericAssertIsCF(cf);
535 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyDebugDesc) {
536 CFStringRef result;
537 result = __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyDebugDesc(cf);
538 if (NULL != result) return result;
539 }
540 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<%s %p [%p]>"), __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->className, cf, CFGetAllocator(cf));
541 }
542
543 // Definition: if type produces a formatting description, return that string, otherwise NULL
544 __private_extern__ CFStringRef __CFCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
545 #if defined(DEBUG)
546 if (NULL == cf) HALT;
547 #endif
548 #if defined(__MACH__)
549 if (CFTYPE_IS_OBJC(cf)) {
550 static SEL s = NULL, r = NULL;
551 CFStringRef (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg;
552 if (!s) s = sel_registerName("_copyFormattingDescription:");
553 if (!r) r = sel_registerName("respondsToSelector:");
554 if (s && func((void *)cf, r, s)) return func((void *)cf, s, formatOptions);
555 return NULL;
556 }
557 #endif
558 __CFGenericAssertIsCF(cf);
559 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyFormattingDesc) {
560 return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyFormattingDesc(cf, formatOptions);
561 }
562 return NULL;
563 }
564
565 extern CFAllocatorRef __CFAllocatorGetAllocator(CFTypeRef);
566
567 CFAllocatorRef CFGetAllocator(CFTypeRef cf) {
568 #if defined(DEBUG)
569 if (NULL == cf) HALT;
570 #endif
571 // CF: need to get allocator from objc objects in better way...how?
572 // -> bridging of CFAllocators and malloc_zone_t will help this
573 if (CFTYPE_IS_OBJC(cf)) return __CFGetDefaultAllocator();
574 if (__kCFAllocatorTypeID_CONST == __CFGenericTypeID_inline(cf)) {
575 return __CFAllocatorGetAllocator(cf);
576 }
577 return __CFGetAllocator(cf);
578 }
579
580 extern void __CFBaseInitialize(void);
581 extern void __CFNullInitialize(void);
582 extern void __CFAllocatorInitialize(void);
583 extern void __CFStringInitialize(void);
584 extern void __CFArrayInitialize(void);
585 extern void __CFBagInitialize(void);
586 extern void __CFBooleanInitialize(void);
587 extern void __CFCharacterSetInitialize(void);
588 extern void __CFDataInitialize(void);
589 extern void __CFDateInitialize(void);
590 extern void __CFDictionaryInitialize(void);
591 extern void __CFNumberInitialize(void);
592 extern void __CFSetInitialize(void);
593 extern void __CFStorageInitialize(void);
594 extern void __CFTimeZoneInitialize(void);
595 extern void __CFTreeInitialize(void);
596 extern void __CFURLInitialize(void);
597 extern void __CFXMLNodeInitialize(void);
598 extern void __CFXMLParserInitialize(void);
599 extern void __CFLocaleInitialize(void);
600 extern void __CFCalendarInitialize(void);
601 extern void __CFNumberFormatterInitialize(void);
602 extern void __CFDateFormatterInitialize(void);
603 #if defined(__MACH__)
604 extern void __CFMessagePortInitialize(void);
605 extern void __CFMachPortInitialize(void);
606 #endif
607 #if defined(__MACH__) || defined(__WIN32__)
608 extern void __CFRunLoopInitialize(void);
609 extern void __CFRunLoopObserverInitialize(void);
610 extern void __CFRunLoopSourceInitialize(void);
611 extern void __CFRunLoopTimerInitialize(void);
612 extern void __CFSocketInitialize(void);
613 #endif
614 extern void __CFBundleInitialize(void);
615 extern void __CFPlugInInitialize(void);
616 extern void __CFPlugInInstanceInitialize(void);
617 extern void __CFUUIDInitialize(void);
618 extern void __CFBinaryHeapInitialize(void);
619 extern void __CFBitVectorInitialize(void);
620 #if defined(__WIN32__)
621 extern void __CFWindowsMessageQueueInitialize(void);
622 extern void __CFBaseCleanup(void);
623 #endif
624 extern void __CFStreamInitialize(void);
625 #if defined(__MACH__)
626 extern void __CFPreferencesDomainInitialize(void);
627 extern void __CFUserNotificationInitialize(void);
628 #endif
629
630 #if defined(DEBUG)
631 #define DO_SYSCALL_TRACE_HELPERS 1
632 #endif
633 #if defined(DO_SYSCALL_TRACE_HELPERS) && defined(__MACH__)
634 extern void ptrace(int, int, int, int);
635 #define SYSCALL_TRACE(N) do ptrace(N, 0, 0, 0); while (0)
636 #else
637 #define SYSCALL_TRACE(N) do {} while (0)
638 #endif
639
640 #if defined(__MACH__) && defined(PROFILE)
641 static void _CF_mcleanup(void) {
642 monitor(0,0,0,0,0);
643 }
644 #endif
645
646 const void *__CFArgStuff = NULL;
647 __private_extern__ void *__CFAppleLanguages = NULL;
648 __private_extern__ void *__CFSessionID = NULL;
649
650 #if defined(__LINUX__) || defined(__FREEBSD__)
651 static void __CFInitialize(void) __attribute__ ((constructor));
652 static
653 #endif
654 #if defined(__WIN32__)
655 CF_EXPORT
656 #endif
657 void __CFInitialize(void) {
658 static int __done = 0;
659 if (sizeof(int) != sizeof(long) || 4 != sizeof(long)) __HALT();
660
661 if (!__done) {
662 __done = 1;
663 SYSCALL_TRACE(0xC000);
664 {
665 kCFUseCollectableAllocator = objc_collecting_enabled();
666 if (kCFUseCollectableAllocator) {
667 __CFCollectableZone = auto_zone();
668 __CFObjCIsCollectable = objc_isAuto;
669 __CFObjCAssignIvar = objc_assign_ivar_address_CF;
670 __CFObjCStrongAssign = objc_assign_strongCast_CF;
671 __CFObjCMemmoveCollectable = objc_memmove_collectable;
672 __CFObjCWriteBarrierRange = objc_WriteBarrierRange_auto;
673 }
674 }
675 #if defined(DEBUG) || defined(ENABLE_ZOMBIES)
676 {
677 const char *value = getenv("CFZombieLevel");
678 if (NULL != value) {
679 __CFZombieLevel = strtoul(value, NULL, 0);
680 }
681 if (0x0 == __CFZombieLevel) __CFZombieLevel = 0xCF00FC00; // default
682 }
683 #endif
684 #if defined(__MACH__) && defined(PROFILE)
685 {
686 const char *v = getenv("DYLD_IMAGE_SUFFIX");
687 const char *p = getenv("CFPROF_ENABLE");
688 // ckane: People were upset that I added this feature to allow for the profiling of
689 // libraries using unprofiled apps/executables, so ensure they cannot get this accidentally.
690 if (v && p && 0 == strcmp("_profile", v) && 0 == strcmp(crypt(p + 2, p) + 2, "eQJhkVvMm.w")) {
691 // Unfortunately, no way to know if this has already been done,
692 // or I would not do it. Not much information will be lost.
693 atexit(_CF_mcleanup);
694 moninit();
695 }
696 }
697 #endif
698
699 __CFBaseInitialize();
700
701 #if defined(__MACH__)
702 {
703 CFIndex idx;
704 for (idx = 0; idx < __CFMaxRuntimeTypes; idx++) {
705 __CFRuntimeObjCClassTable[idx] = &__CFNSTypeClass;
706 }
707 }
708 #endif
709
710 /* Here so that two runtime classes get indices 0, 1. */
711 __kCFNotATypeTypeID = _CFRuntimeRegisterClass(&__CFNotATypeClass);
712 __kCFTypeTypeID = _CFRuntimeRegisterClass(&__CFTypeClass);
713
714 /* Here so that __kCFAllocatorTypeID gets index 2. */
715 __CFAllocatorInitialize();
716
717 /* Basic collections need to be up before CFString. */
718 __CFDictionaryInitialize();
719 __CFArrayInitialize();
720 __CFDataInitialize();
721 __CFSetInitialize();
722
723 #if defined(__MACH__)
724 {
725 CFIndex idx, cnt;
726 char **args = *_NSGetArgv();
727 cnt = *_NSGetArgc();
728 for (idx = 1; idx < cnt - 1; idx++) {
729 if (0 == strcmp(args[idx], "-AppleLanguages")) {
730 CFIndex length = strlen(args[idx + 1]);
731 __CFAppleLanguages = malloc(length + 1);
732 memmove(__CFAppleLanguages, args[idx + 1], length + 1);
733 break;
734 }
735 }
736 }
737 #endif
738
739
740 // Creating this lazily in CFRetain causes recursive call to CFRetain
741 __CFRuntimeExternRefCountTable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL);
742
743 /*** _CFRuntimeCreateInstance() can finally be called generally after this line. ***/
744
745 __CFStringInitialize(); // CFString's TypeID must be 0x7, now and forever
746 __CFNullInitialize(); // See above for hard-coding of this position
747 __CFBooleanInitialize(); // See above for hard-coding of this position
748 __CFNumberInitialize(); // See above for hard-coding of this position
749 __CFDateInitialize(); // See above for hard-coding of this position
750 __CFTimeZoneInitialize(); // See above for hard-coding of this position
751
752 __CFBinaryHeapInitialize();
753 __CFBitVectorInitialize();
754 __CFBagInitialize();
755 __CFCharacterSetInitialize();
756 __CFStorageInitialize();
757 __CFTreeInitialize();
758 __CFURLInitialize();
759 __CFXMLNodeInitialize();
760 __CFXMLParserInitialize();
761 __CFBundleInitialize();
762 __CFPlugInInitialize();
763 __CFPlugInInstanceInitialize();
764 __CFUUIDInitialize();
765 #if defined(__MACH__)
766 __CFMessagePortInitialize();
767 __CFMachPortInitialize();
768 #endif
769 #if defined(__MACH__) || defined(__WIN32__)
770 __CFRunLoopInitialize();
771 __CFRunLoopObserverInitialize();
772 __CFRunLoopSourceInitialize();
773 __CFRunLoopTimerInitialize();
774 __CFSocketInitialize();
775 #endif
776 __CFStreamInitialize();
777 #if defined(__MACH__)
778 __CFPreferencesDomainInitialize();
779 #endif // __MACH__
780
781
782 SYSCALL_TRACE(0xC001);
783
784 #if defined(__MACH__)
785 {
786 CFIndex idx, cnt;
787 char **args = *_NSGetArgv();
788 CFIndex count;
789 cnt = *_NSGetArgc();
790 CFStringRef *list, buffer[256];
791 list = (cnt <= 256) ? buffer : malloc(cnt * sizeof(CFStringRef));
792 for (idx = 0, count = 0; idx < cnt && args[idx]; idx++) {
793 list[count] = CFStringCreateWithCString(kCFAllocatorSystemDefault, args[idx], kCFStringEncodingUTF8);
794 if (NULL == list[count]) {
795 list[count] = CFStringCreateWithCString(kCFAllocatorSystemDefault, args[idx], kCFStringEncodingISOLatin1);
796 // We CANNOT use the string SystemEncoding here;
797 // Do not argue: it is not initialized yet, but these
798 // arguments MUST be initialized before it is.
799 // We should just ignore the argument if the UTF-8
800 // conversion fails, but out of charity we try once
801 // more with ISO Latin1, a standard unix encoding.
802 }
803 if (NULL != list[count]) count++;
804 }
805 __CFArgStuff = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)list, count, &kCFTypeArrayCallBacks);
806 }
807
808 __CFSessionID = getenv("SECURITYSESSIONID");
809 #endif
810 _CFProcessPath(); // cache this early
811
812 #if defined(__MACH__)
813 __CFOAInitialize();
814 SYSCALL_TRACE(0xC003);
815 #endif
816
817 if (__CFRuntimeClassTableCount < 100) __CFRuntimeClassTableCount = 100;
818
819 #if defined(DEBUG) && !defined(__WIN32__)
820 // Don't log on MacOS 8 as this will create a log file unnecessarily
821 CFLog (0, CFSTR("Assertions enabled"));
822 #endif
823 SYSCALL_TRACE(0xC0FF);
824 }
825 }
826
827 #if defined(__WIN32__)
828
829 /* We have to call __CFInitialize when library is attached to the process.
830 * (Sergey Zubarev)
831 */
832 WINBOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) {
833 if (dwReason == DLL_PROCESS_ATTACH) {
834 __CFInitialize();
835 } else if (dwReason == DLL_PROCESS_DETACH) {
836 __CFStringCleanup();
837 __CFSocketCleanup();
838 __CFUniCharCleanup();
839 __CFStreamCleanup();
840 __CFBaseCleanup();
841 } else if (dwReason == DLL_THREAD_DETACH) {
842 __CFFinalizeThreadData(NULL);
843 }
844 return TRUE;
845 }
846
847 #endif
848
849
850 // Functions that avoid ObC dispatch and CF type validation, for use by NSNotifyingCFArray, etc.
851 // Hopefully all of this will just go away. 3321464. M.P. To Do - 7/9/03
852
853 Boolean _CFEqual(CFTypeRef cf1, CFTypeRef cf2) {
854 #if defined(DEBUG)
855 if (NULL == cf1) HALT;
856 if (NULL == cf2) HALT;
857 #endif
858 if (cf1 == cf2) return true;
859 __CFGenericAssertIsCF(cf1);
860 __CFGenericAssertIsCF(cf2);
861 if (__CFGenericTypeID_inline(cf1) != __CFGenericTypeID_inline(cf2)) return false;
862 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal) {
863 return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal(cf1, cf2);
864 }
865 return false;
866 }
867
868 CFIndex _CFGetRetainCount(CFTypeRef cf) {
869 uint64_t rc;
870 CFIndex result;
871 rc = __CFGetFullRetainCount(cf);
872 result = (rc < (uint64_t)0x7FFFFFFF) ? (CFIndex)rc : (CFIndex)0x7FFFFFFF;
873 return result;
874 }
875
876 CFHashCode _CFHash(CFTypeRef cf) {
877 #if defined(DEBUG)
878 if (NULL == cf) HALT;
879 #endif
880 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash) {
881 return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash(cf);
882 }
883 return (CFHashCode)cf;
884 }
885
886 CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf) {
887 CFIndex lowBits = 0;
888 #if defined(DEBUG)
889 if (NULL == cf) HALT;
890 #endif
891 __CFSpinLock(&__CFGlobalRetainLock);
892 lowBits = ((CFRuntimeBase *)cf)->_rc;
893 if (__builtin_expect(0 == lowBits, 0)) { // Constant CFTypeRef
894 __CFSpinUnlock(&__CFGlobalRetainLock);
895 return cf;
896 }
897 lowBits++;
898 if (__builtin_expect((lowBits & 0x07fff) == 0, 0)) {
899 // Roll over another bit to the external ref count
900 _CFDictionaryIncrementValue(__CFRuntimeExternRefCountTable, DISGUISE(cf));
901 lowBits = 0x8000; // Bit 16 indicates external ref count
902 }
903 ((CFRuntimeBase *)cf)->_rc = lowBits;
904 __CFSpinUnlock(&__CFGlobalRetainLock);
905 if (__builtin_expect(__CFOASafe, 0)) {
906 uint64_t compositeRC;
907 compositeRC = (lowBits & 0x7fff) + ((uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)) << 15);
908 if (compositeRC > (uint64_t)0x7fffffff) compositeRC = (uint64_t)0x7fffffff;
909 __CFRecordAllocationEvent(__kCFRetainEvent, (void *)cf, 0, compositeRC, NULL);
910 }
911 return cf;
912 }
913
914 CF_EXPORT void _CFRelease(CFTypeRef cf) {
915 CFIndex lowBits = 0;
916 #if defined(DEBUG)
917 if (NULL == cf) HALT;
918 #endif
919 __CFSpinLock(&__CFGlobalRetainLock);
920 lowBits = ((CFRuntimeBase *)cf)->_rc;
921 if (__builtin_expect(0 == lowBits, 0)) { // Constant CFTypeRef
922 __CFSpinUnlock(&__CFGlobalRetainLock);
923 return;
924 }
925 if (__builtin_expect(1 == lowBits, 0)) {
926 __CFSpinUnlock(&__CFGlobalRetainLock);
927 if (__builtin_expect(__CFOASafe, 0)) __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL);
928 if (__builtin_expect(__kCFAllocatorTypeID_CONST == __CFGenericTypeID_inline(cf), 0)) {
929 #if defined(DEBUG) || defined(ENABLE_ZOMBIES)
930 __CFZombifyDeallocatedMemory((void *)cf);
931 if (!(__CFZombieLevel & (1 << 4))) {
932 __CFAllocatorDeallocate((void *)cf);
933 }
934 #else
935 __CFAllocatorDeallocate((void *)cf);
936 #endif
937 } else {
938 CFAllocatorRef allocator;
939 // ((CFRuntimeBase *)cf)->_rc = 0;
940 if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->finalize) {
941 __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->finalize(cf);
942 }
943 if (__builtin_expect(__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 7, 7), 1)) {
944 allocator = kCFAllocatorSystemDefault;
945 } else {
946 allocator = CFGetAllocator(cf);
947 (intptr_t)cf -= sizeof(CFAllocatorRef);
948 }
949 #if defined(DEBUG) || defined(ENABLE_ZOMBIES)
950 __CFZombifyDeallocatedMemory((void *)cf);
951 if (!(__CFZombieLevel & (1 << 4))) {
952 CFAllocatorDeallocate(allocator, (void *)cf);
953 }
954 #else
955 CFAllocatorDeallocate(allocator, (void *)cf);
956 #endif
957 if (kCFAllocatorSystemDefault != allocator) {
958 CFRelease(allocator);
959 }
960 }
961 } else {
962 if (__builtin_expect(0x8000 == lowBits, 0)) {
963 // Time to remove a bit from the external ref count
964 if (0 == _CFDictionaryDecrementValue(__CFRuntimeExternRefCountTable, DISGUISE(cf))) {
965 lowBits = 0x07fff;
966 } else {
967 lowBits = 0x0ffff;
968 }
969 } else {
970 lowBits--;
971 }
972 ((CFRuntimeBase *)cf)->_rc = lowBits;
973 __CFSpinUnlock(&__CFGlobalRetainLock);
974 if (__builtin_expect(__CFOASafe, 0)) {
975 uint64_t compositeRC;
976 compositeRC = (lowBits & 0x7fff) + ((uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)) << 15);
977 if (compositeRC > (uint64_t)0x7fffffff) compositeRC = (uint64_t)0x7fffffff;
978 __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, compositeRC, NULL);
979 }
980 }
981 }
982
983 #undef DO_SYSCALL_TRACE_HELPERS
984 #undef SYSCALL_TRACE
985 #undef __kCFAllocatorTypeID_CONST
986 #undef __CFGenericAssertIsCF