]> git.saurik.com Git - apple/objc4.git/blame - runtime/objc-auto.mm
objc4-646.tar.gz
[apple/objc4.git] / runtime / objc-auto.mm
CommitLineData
2bfd4448 1/*
b3962a83 2 * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
2bfd4448
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
2bfd4448
A
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 */
2bfd4448 23
7257e56c
A
24#include "objc-private.h"
25
cd5f04f5
A
26#include "objc-config.h"
27#include "objc-auto.h"
28#include "objc-accessors.h"
7af964d1
A
29
30#ifndef OBJC_NO_GC
31
cd5f04f5
A
32#include <stdint.h>
33#include <stdbool.h>
34#include <fcntl.h>
35#include <dlfcn.h>
36#include <mach/mach.h>
37#include <mach-o/dyld.h>
38#include <mach-o/nlist.h>
39#include <sys/types.h>
40#include <sys/mman.h>
41#include <libkern/OSAtomic.h>
42#include <auto_zone.h>
43
44#include <Block_private.h>
45#include <dispatch/private.h>
46
47#include "objc-private.h"
48#include "objc-references.h"
49#include "maptable.h"
50#include "message.h"
51#include "objc-gdb.h"
7af964d1 52
8972963c 53#if !defined(NDEBUG) && !__OBJC2__
cd5f04f5 54#include "objc-exception.h"
8972963c 55#endif
2bfd4448
A
56
57
7257e56c 58static auto_zone_t *gc_zone_init(void);
7af964d1
A
59static void gc_block_init(void);
60static void registeredClassTableInit(void);
8972963c 61static BOOL objc_isRegisteredClass(Class candidate);
2bfd4448 62
7257e56c 63int8_t UseGC = -1;
b3962a83 64static BOOL WantsMainThreadFinalization = NO;
2bfd4448 65
7257e56c 66auto_zone_t *gc_zone = nil;
2bfd4448
A
67
68
7af964d1
A
69/* Method prototypes */
70@interface DoesNotExist
71- (const char *)UTF8String;
72- (id)description;
73@end
74
75
8972963c
A
76/***********************************************************************
77* Break-on-error functions
78**********************************************************************/
79
80BREAKPOINT_FUNCTION(
81 void objc_assign_ivar_error(id base, ptrdiff_t offset)
82);
83
84BREAKPOINT_FUNCTION(
85 void objc_assign_global_error(id value, id *slot)
86);
87
88BREAKPOINT_FUNCTION(
89 void objc_exception_during_finalize_error(void)
90);
91
2bfd4448
A
92/***********************************************************************
93* Utility exports
94* Called by various libraries.
95**********************************************************************/
96
b3962a83 97OBJC_EXPORT void objc_set_collection_threshold(size_t threshold) { // Old naming
2bfd4448 98 if (UseGC) {
b3962a83 99 auto_collection_parameters(gc_zone)->collection_threshold = threshold;
2bfd4448
A
100 }
101}
102
b3962a83 103OBJC_EXPORT void objc_setCollectionThreshold(size_t threshold) {
2bfd4448 104 if (UseGC) {
b3962a83 105 auto_collection_parameters(gc_zone)->collection_threshold = threshold;
2bfd4448
A
106 }
107}
108
b3962a83
A
109void objc_setCollectionRatio(size_t ratio) {
110 if (UseGC) {
111 auto_collection_parameters(gc_zone)->full_vs_gen_frequency = ratio;
112 }
113}
114
115void objc_set_collection_ratio(size_t ratio) { // old naming
2bfd4448 116 if (UseGC) {
b3962a83 117 auto_collection_parameters(gc_zone)->full_vs_gen_frequency = ratio;
2bfd4448
A
118 }
119}
120
b3962a83
A
121void objc_finalizeOnMainThread(Class cls) {
122 if (UseGC) {
123 WantsMainThreadFinalization = YES;
7257e56c 124 cls->setShouldFinalizeOnMainThread();
b3962a83
A
125 }
126}
2bfd4448 127
7af964d1
A
128// stack based data structure queued if/when there is main-thread-only finalization work TBD
129typedef struct BatchFinalizeBlock {
130 auto_zone_foreach_object_t foreach;
131 auto_zone_cursor_t cursor;
132 size_t cursor_size;
133 volatile BOOL finished;
134 volatile BOOL started;
135 struct BatchFinalizeBlock *next;
136} BatchFinalizeBlock_t;
137
138// The Main Thread Finalization Work Queue Head
139static struct {
140 pthread_mutex_t mutex;
141 pthread_cond_t condition;
142 BatchFinalizeBlock_t *head;
143 BatchFinalizeBlock_t *tail;
144} MainThreadWorkQ;
145
b3962a83
A
146
147void objc_startCollectorThread(void) {
b3962a83
A
148}
149
150void objc_start_collector_thread(void) {
b3962a83
A
151}
152
153static void batchFinalizeOnMainThread(void);
154
155void objc_collect(unsigned long options) {
156 if (!UseGC) return;
157 BOOL onMainThread = pthread_main_np() ? YES : NO;
8972963c
A
158
159 // while we're here, sneak off and do some finalization work (if any)
160 if (onMainThread) batchFinalizeOnMainThread();
161 // now on with our normally scheduled programming
162 auto_zone_options_t amode = AUTO_ZONE_COLLECT_NO_OPTIONS;
163 if (!(options & OBJC_COLLECT_IF_NEEDED)) {
b3962a83 164 switch (options & 0x3) {
8972963c
A
165 case OBJC_RATIO_COLLECTION: amode = AUTO_ZONE_COLLECT_RATIO_COLLECTION; break;
166 case OBJC_GENERATIONAL_COLLECTION: amode = AUTO_ZONE_COLLECT_GENERATIONAL_COLLECTION; break;
167 case OBJC_FULL_COLLECTION: amode = AUTO_ZONE_COLLECT_FULL_COLLECTION; break;
168 case OBJC_EXHAUSTIVE_COLLECTION: amode = AUTO_ZONE_COLLECT_EXHAUSTIVE_COLLECTION; break;
b3962a83 169 }
8972963c
A
170 amode |= AUTO_ZONE_COLLECT_COALESCE;
171 amode |= AUTO_ZONE_COLLECT_LOCAL_COLLECTION;
b3962a83 172 }
8972963c
A
173 if (options & OBJC_WAIT_UNTIL_DONE) {
174 __block BOOL done = NO;
175 // If executing on the main thread, use the main thread work queue condition to block,
176 // so main thread finalization can complete. Otherwise, use a thread-local condition.
177 pthread_mutex_t localMutex = PTHREAD_MUTEX_INITIALIZER, *mutex = &localMutex;
178 pthread_cond_t localCondition = PTHREAD_COND_INITIALIZER, *condition = &localCondition;
179 if (onMainThread) {
180 mutex = &MainThreadWorkQ.mutex;
181 condition = &MainThreadWorkQ.condition;
182 }
183 pthread_mutex_lock(mutex);
184 auto_zone_collect_and_notify(gc_zone, amode, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
185 pthread_mutex_lock(mutex);
186 done = YES;
187 pthread_cond_signal(condition);
188 pthread_mutex_unlock(mutex);
189 });
190 while (!done) {
191 pthread_cond_wait(condition, mutex);
192 if (onMainThread && MainThreadWorkQ.head) {
193 pthread_mutex_unlock(mutex);
194 batchFinalizeOnMainThread();
195 pthread_mutex_lock(mutex);
196 }
197 }
198 pthread_mutex_unlock(mutex);
199 } else {
200 auto_zone_collect(gc_zone, amode);
b3962a83 201 }
2bfd4448
A
202}
203
204
b3962a83 205// USED BY CF & ONE OTHER
2bfd4448
A
206BOOL objc_isAuto(id object)
207{
b3962a83 208 return UseGC && auto_zone_is_valid_pointer(gc_zone, object) != 0;
2bfd4448
A
209}
210
211
b3962a83
A
212BOOL objc_collectingEnabled(void)
213{
214 return UseGC;
215}
7af964d1 216
b3962a83 217BOOL objc_collecting_enabled(void) // Old naming
2bfd4448
A
218{
219 return UseGC;
220}
221
8972963c
A
222malloc_zone_t *objc_collectableZone(void) {
223 return gc_zone;
224}
225
7af964d1
A
226BOOL objc_dumpHeap(char *filenamebuffer, unsigned long length) {
227 static int counter = 0;
228 ++counter;
229 char buffer[1024];
230 sprintf(buffer, OBJC_HEAP_DUMP_FILENAME_FORMAT, getpid(), counter);
231 if (!_objc_dumpHeap(gc_zone, buffer)) return NO;
232 if (filenamebuffer) {
233 unsigned long blen = strlen(buffer);
234 if (blen < length)
235 strncpy(filenamebuffer, buffer, blen+1);
236 else if (length > 0)
237 filenamebuffer[0] = 0; // give some answer
238 }
239 return YES;
240}
241
2bfd4448
A
242
243/***********************************************************************
244* Memory management.
245* Called by CF and Foundation.
246**********************************************************************/
247
248// Allocate an object in the GC zone, with the given number of extra bytes.
249id objc_allocate_object(Class cls, int extra)
250{
b3962a83
A
251 return class_createInstance(cls, extra);
252}
253
254
255/***********************************************************************
256* Write barrier implementations, optimized for when GC is known to be on
257* Called by the write barrier exports only.
258* These implementations assume GC is on. The exported function must
259* either perform the check itself or be conditionally stomped at
260* startup time.
261**********************************************************************/
262
cd5f04f5 263id objc_assign_strongCast_gc(id value, id *slot) {
5f63f917
A
264 if (!auto_zone_set_write_barrier(gc_zone, (void*)slot, value)) { // stores & returns true if slot points into GC allocated memory
265 auto_zone_root_write_barrier(gc_zone, slot, value); // always stores
266 }
267 return value;
b3962a83
A
268}
269
cd5f04f5 270id objc_assign_global_gc(id value, id *slot) {
b3962a83
A
271 // use explicit root registration.
272 if (value && auto_zone_is_valid_pointer(gc_zone, value)) {
273 if (auto_zone_is_finalized(gc_zone, value)) {
7257e56c 274 _objc_inform("GC: storing an already collected object %p into global memory at %p, break on objc_assign_global_error to debug\n", (void*)value, slot);
b3962a83
A
275 objc_assign_global_error(value, slot);
276 }
277 auto_zone_add_root(gc_zone, slot, value);
278 }
5f63f917
A
279 else
280 *slot = value;
b3962a83 281
5f63f917 282 return value;
b3962a83
A
283}
284
cd5f04f5 285id objc_assign_threadlocal_gc(id value, id *slot)
8972963c
A
286{
287 if (value && auto_zone_is_valid_pointer(gc_zone, value)) {
288 auto_zone_add_root(gc_zone, slot, value);
289 }
290 else {
291 *slot = value;
292 }
293
294 return value;
295}
b3962a83 296
cd5f04f5 297id objc_assign_ivar_gc(id value, id base, ptrdiff_t offset)
b3962a83
A
298{
299 id *slot = (id*) ((char *)base + offset);
300
301 if (value) {
302 if (!auto_zone_set_write_barrier(gc_zone, (char *)base + offset, value)) {
7257e56c 303 _objc_inform("GC: %p + %tu isn't in the auto_zone, break on objc_assign_ivar_error to debug.\n", (void*)base, offset);
b3962a83
A
304 objc_assign_ivar_error(base, offset);
305 }
306 }
5f63f917
A
307 else
308 *slot = value;
b3962a83 309
5f63f917 310 return value;
2bfd4448
A
311}
312
cd5f04f5 313id objc_assign_strongCast_non_gc(id value, id *slot) {
8972963c
A
314 return (*slot = value);
315}
316
cd5f04f5 317id objc_assign_global_non_gc(id value, id *slot) {
8972963c
A
318 return (*slot = value);
319}
320
cd5f04f5 321id objc_assign_threadlocal_non_gc(id value, id *slot) {
8972963c
A
322 return (*slot = value);
323}
324
cd5f04f5 325id objc_assign_ivar_non_gc(id value, id base, ptrdiff_t offset) {
8972963c
A
326 id *slot = (id*) ((char *)base + offset);
327 return (*slot = value);
328}
2bfd4448 329
7257e56c 330
2bfd4448 331/***********************************************************************
7257e56c 332* Non-trivial write barriers
2bfd4448
A
333**********************************************************************/
334
2bfd4448
A
335void *objc_memmove_collectable(void *dst, const void *src, size_t size)
336{
337 if (UseGC) {
338 return auto_zone_write_barrier_memmove(gc_zone, dst, src, size);
339 } else {
340 return memmove(dst, src, size);
341 }
342}
343
7af964d1
A
344BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation) {
345 const BOOL issueMemoryBarrier = NO;
346 if (UseGC)
347 return auto_zone_atomicCompareAndSwapPtr(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, issueMemoryBarrier);
348 else
349 return OSAtomicCompareAndSwapPtr((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
350}
351
352BOOL objc_atomicCompareAndSwapPtrBarrier(id predicate, id replacement, volatile id *objectLocation) {
353 const BOOL issueMemoryBarrier = YES;
354 if (UseGC)
355 return auto_zone_atomicCompareAndSwapPtr(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, issueMemoryBarrier);
356 else
357 return OSAtomicCompareAndSwapPtrBarrier((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
358}
359
b3962a83 360BOOL objc_atomicCompareAndSwapGlobal(id predicate, id replacement, volatile id *objectLocation) {
7af964d1
A
361 const BOOL isGlobal = YES;
362 const BOOL issueMemoryBarrier = NO;
5f63f917 363 if (UseGC)
7af964d1 364 return auto_zone_atomicCompareAndSwap(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, isGlobal, issueMemoryBarrier);
5f63f917
A
365 else
366 return OSAtomicCompareAndSwapPtr((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
b3962a83
A
367}
368
369BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, id replacement, volatile id *objectLocation) {
7af964d1
A
370 const BOOL isGlobal = YES;
371 const BOOL issueMemoryBarrier = YES;
5f63f917 372 if (UseGC)
7af964d1 373 return auto_zone_atomicCompareAndSwap(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, isGlobal, issueMemoryBarrier);
5f63f917
A
374 else
375 return OSAtomicCompareAndSwapPtrBarrier((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
b3962a83
A
376}
377
378BOOL objc_atomicCompareAndSwapInstanceVariable(id predicate, id replacement, volatile id *objectLocation) {
7af964d1
A
379 const BOOL isGlobal = NO;
380 const BOOL issueMemoryBarrier = NO;
5f63f917 381 if (UseGC)
7af964d1 382 return auto_zone_atomicCompareAndSwap(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, isGlobal, issueMemoryBarrier);
5f63f917
A
383 else
384 return OSAtomicCompareAndSwapPtr((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
b3962a83
A
385}
386
387BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, id replacement, volatile id *objectLocation) {
7af964d1
A
388 const BOOL isGlobal = NO;
389 const BOOL issueMemoryBarrier = YES;
5f63f917 390 if (UseGC)
7af964d1 391 return auto_zone_atomicCompareAndSwap(gc_zone, (void *)predicate, (void *)replacement, (void * volatile *)objectLocation, isGlobal, issueMemoryBarrier);
5f63f917
A
392 else
393 return OSAtomicCompareAndSwapPtrBarrier((void *)predicate, (void *)replacement, (void * volatile *)objectLocation);
394}
395
396
397/***********************************************************************
8972963c 398* Weak ivar support
5f63f917
A
399**********************************************************************/
400
cd5f04f5 401id objc_read_weak_gc(id *location) {
8972963c
A
402 id result = *location;
403 if (result) {
7257e56c 404 result = (id)auto_read_weak_reference(gc_zone, (void **)location);
7af964d1 405 }
8972963c 406 return result;
7af964d1 407}
b3962a83 408
cd5f04f5 409id objc_read_weak_non_gc(id *location) {
8972963c
A
410 return *location;
411}
b3962a83 412
cd5f04f5 413id objc_assign_weak_gc(id value, id *location) {
7257e56c 414 auto_assign_weak_reference(gc_zone, value, (const void **)location, nil);
b3962a83
A
415 return value;
416}
417
cd5f04f5 418id objc_assign_weak_non_gc(id value, id *location) {
8972963c 419 return (*location = value);
7af964d1
A
420}
421
7af964d1 422
cd5f04f5 423void gc_fixup_weakreferences(id newObject, id oldObject) {
8972963c 424 // fix up weak references if any.
7257e56c 425 const unsigned char *weakLayout = (const unsigned char *)class_getWeakIvarLayout(newObject->ISA());
8972963c
A
426 if (weakLayout) {
427 void **newPtr = (void **)newObject, **oldPtr = (void **)oldObject;
428 unsigned char byte;
429 while ((byte = *weakLayout++)) {
430 unsigned skips = (byte >> 4);
431 unsigned weaks = (byte & 0x0F);
432 newPtr += skips, oldPtr += skips;
433 while (weaks--) {
7257e56c
A
434 *newPtr = nil;
435 auto_assign_weak_reference(gc_zone, auto_read_weak_reference(gc_zone, oldPtr), (const void **)newPtr, nil);
8972963c
A
436 ++newPtr, ++oldPtr;
437 }
438 }
7af964d1 439 }
7af964d1 440}
2bfd4448 441
7257e56c
A
442
443/***********************************************************************
444* dyld resolver functions for basic GC write barriers
445* dyld calls the resolver function to bind the symbol.
446* We return the GC or non-GC variant as appropriate.
447**********************************************************************/
448
449#define GC_RESOLVER(name) \
450 OBJC_EXPORT void *name##_resolver(void) __asm__("_" #name); \
451 void *name##_resolver(void) \
452 { \
453 __asm__(".symbol_resolver _" #name); \
454 if (UseGC) return (void*)name##_gc; \
455 else return (void*)name##_non_gc; \
456 }
457
458GC_RESOLVER(objc_assign_ivar)
459GC_RESOLVER(objc_assign_strongCast)
460GC_RESOLVER(objc_assign_global)
461GC_RESOLVER(objc_assign_threadlocal)
462GC_RESOLVER(objc_read_weak)
463GC_RESOLVER(objc_assign_weak)
464GC_RESOLVER(objc_getProperty)
465GC_RESOLVER(objc_setProperty)
466GC_RESOLVER(objc_getAssociatedObject)
467GC_RESOLVER(objc_setAssociatedObject)
468GC_RESOLVER(_object_addExternalReference)
469GC_RESOLVER(_object_readExternalReference)
470GC_RESOLVER(_object_removeExternalReference)
471
472
2bfd4448
A
473/***********************************************************************
474* Testing tools
475* Used to isolate resurrection of garbage objects during finalization.
476**********************************************************************/
477BOOL objc_is_finalized(void *ptr) {
7257e56c 478 if (ptr != nil && UseGC) {
b3962a83
A
479 return auto_zone_is_finalized(gc_zone, ptr);
480 }
481 return NO;
2bfd4448
A
482}
483
484
b3962a83 485/***********************************************************************
8972963c
A
486* Stack clearing.
487* Used by top-level thread loops to reduce false pointers from the stack.
b3962a83 488**********************************************************************/
b3962a83
A
489void objc_clear_stack(unsigned long options) {
490 if (!UseGC) return;
7af964d1 491 auto_zone_clear_stack(gc_zone, 0);
b3962a83
A
492}
493
8972963c 494
2bfd4448 495/***********************************************************************
b3962a83 496* Finalization support
2bfd4448
A
497**********************************************************************/
498
b3962a83
A
499// Finalizer crash debugging
500static void *finalizing_object;
7af964d1
A
501
502// finalize a single object without fuss
503// When there are no main-thread-only classes this is used directly
504// Otherwise, it is used indirectly by smarter code that knows main-thread-affinity requirements
505static void finalizeOneObject(void *obj, void *ignored) {
b3962a83 506 id object = (id)obj;
b3962a83 507 finalizing_object = obj;
8972963c 508
7257e56c 509 Class cls = object->ISA();
8972963c 510 CRSetCrashLogMessage2(class_getName(cls));
2bfd4448 511
b3962a83 512 /// call -finalize method.
cd5f04f5 513 ((void(*)(id, SEL))objc_msgSend)(object, @selector(finalize));
8972963c
A
514
515 // Call C++ destructors.
516 // This would be objc_destructInstance() but for performance.
7257e56c 517 if (cls->hasCxxDtor()) {
8972963c
A
518 object_cxxDestruct(object);
519 }
2bfd4448 520
7257e56c
A
521 finalizing_object = nil;
522 CRSetCrashLogMessage2(nil);
2bfd4448
A
523}
524
7af964d1
A
525// finalize object only if it is a main-thread-only object.
526// Called only from the main thread.
527static void finalizeOneMainThreadOnlyObject(void *obj, void *arg) {
b3962a83 528 id object = (id)obj;
7257e56c
A
529 Class cls = object->ISA();
530 if (cls == nil) {
531 _objc_fatal("object with nil ISA passed to finalizeOneMainThreadOnlyObject: %p\n", obj);
b3962a83 532 }
7257e56c
A
533 if (cls->shouldFinalizeOnMainThread()) {
534 finalizeOneObject(obj, nil);
b3962a83 535 }
2bfd4448 536}
2bfd4448 537
7af964d1
A
538// finalize one object only if it is not a main-thread-only object
539// called from any other thread than the main thread
540// Important: if a main-thread-only object is passed, return that fact in the needsMain argument
541static void finalizeOneAnywhereObject(void *obj, void *needsMain) {
b3962a83 542 id object = (id)obj;
7257e56c
A
543 Class cls = object->ISA();
544 bool *needsMainThreadWork = (bool *)needsMain;
545 if (cls == nil) {
546 _objc_fatal("object with nil ISA passed to finalizeOneAnywhereObject: %p\n", obj);
b3962a83 547 }
7257e56c
A
548 if (!cls->shouldFinalizeOnMainThread()) {
549 finalizeOneObject(obj, nil);
b3962a83
A
550 }
551 else {
7af964d1 552 *needsMainThreadWork = true;
2bfd4448 553 }
2bfd4448
A
554}
555
2bfd4448 556
7af964d1
A
557// Utility workhorse.
558// Set up the expensive @try block and ask the collector to hand the next object to
559// our finalizeAnObject function.
560// Track and return a boolean that records whether or not any main thread work is necessary.
561// (When we know that there are no main thread only objects then the boolean isn't even computed)
562static bool batchFinalize(auto_zone_t *zone,
b3962a83
A
563 auto_zone_foreach_object_t foreach,
564 auto_zone_cursor_t cursor,
565 size_t cursor_size,
7af964d1 566 void (*finalizeAnObject)(void *, void*))
b3962a83 567{
8972963c
A
568#if !defined(NDEBUG) && !__OBJC2__
569 // debug: don't call try/catch before exception handlers are installed
cd5f04f5 570 objc_exception_functions_t table = {};
8972963c
A
571 objc_exception_get_functions(&table);
572 assert(table.throw_exc);
573#endif
574
7af964d1 575 bool needsMainThreadWork = false;
b3962a83
A
576 for (;;) {
577 @try {
7af964d1 578 foreach(cursor, finalizeAnObject, &needsMainThreadWork);
b3962a83
A
579 // non-exceptional return means finalization is complete.
580 break;
8972963c
A
581 }
582 @catch (id exception) {
b3962a83 583 // whoops, note exception, then restart at cursor's position
b3962a83
A
584 _objc_inform("GC: -finalize resulted in an exception (%p) being thrown, break on objc_exception_during_finalize_error to debug\n\t%s", exception, (const char*)[[exception description] UTF8String]);
585 objc_exception_during_finalize_error();
8972963c
A
586 }
587 @catch (...) {
588 // whoops, note exception, then restart at cursor's position
589 _objc_inform("GC: -finalize resulted in an exception being thrown, break on objc_exception_during_finalize_error to debug");
590 objc_exception_during_finalize_error();
b3962a83 591 }
2bfd4448 592 }
7af964d1 593 return needsMainThreadWork;
2bfd4448 594}
2bfd4448 595
7af964d1
A
596// Called on main thread-only.
597// Pick up work from global queue.
598// called parasitically by anyone requesting a collection
599// called explicitly when there is known to be main thread only finalization work
600// In both cases we are on the main thread
601// Guard against recursion by something called from a finalizer
602static void batchFinalizeOnMainThread() {
603 pthread_mutex_lock(&MainThreadWorkQ.mutex);
604 if (!MainThreadWorkQ.head || MainThreadWorkQ.head->started) {
605 // No work or we're already here
606 pthread_mutex_unlock(&MainThreadWorkQ.mutex);
2bfd4448
A
607 return;
608 }
7af964d1
A
609 while (MainThreadWorkQ.head) {
610 BatchFinalizeBlock_t *bfb = MainThreadWorkQ.head;
611 bfb->started = YES;
612 pthread_mutex_unlock(&MainThreadWorkQ.mutex);
613
614 batchFinalize(gc_zone, bfb->foreach, bfb->cursor, bfb->cursor_size, finalizeOneMainThreadOnlyObject);
615 // signal the collector thread(s) that finalization has finished.
616 pthread_mutex_lock(&MainThreadWorkQ.mutex);
617 bfb->finished = YES;
618 pthread_cond_broadcast(&MainThreadWorkQ.condition);
619 MainThreadWorkQ.head = bfb->next;
620 }
7257e56c 621 MainThreadWorkQ.tail = nil;
7af964d1 622 pthread_mutex_unlock(&MainThreadWorkQ.mutex);
b3962a83
A
623}
624
7af964d1
A
625
626// Knowing that we possibly have main thread only work to do, first process everything
627// that is not main-thread-only. If we discover main thread only work, queue a work block
628// to the main thread that will do just the main thread only work. Wait for it.
629// Called from a non main thread.
b3962a83
A
630static void batchFinalizeOnTwoThreads(auto_zone_t *zone,
631 auto_zone_foreach_object_t foreach,
632 auto_zone_cursor_t cursor,
633 size_t cursor_size)
634{
635 // First, lets get rid of everything we can on this thread, then ask main thread to help if needed
b3962a83
A
636 char cursor_copy[cursor_size];
637 memcpy(cursor_copy, cursor, cursor_size);
7af964d1 638 bool needsMainThreadFinalization = batchFinalize(zone, foreach, (auto_zone_cursor_t)cursor_copy, cursor_size, finalizeOneAnywhereObject);
b3962a83 639
7af964d1 640 if (! needsMainThreadFinalization)
b3962a83 641 return; // no help needed
2bfd4448 642
b3962a83 643 // set up the control block. Either our ping of main thread with _callOnMainThread will get to it, or
7af964d1
A
644 // an objc_collect(if_needed) will get to it. Either way, this block will be processed on the main thread.
645 BatchFinalizeBlock_t bfb;
646 bfb.foreach = foreach;
647 bfb.cursor = cursor;
648 bfb.cursor_size = cursor_size;
649 bfb.started = NO;
650 bfb.finished = NO;
7257e56c 651 bfb.next = nil;
7af964d1
A
652 pthread_mutex_lock(&MainThreadWorkQ.mutex);
653 if (MainThreadWorkQ.tail) {
654
655 // link to end so that ordering of finalization is preserved.
656 MainThreadWorkQ.tail->next = &bfb;
657 MainThreadWorkQ.tail = &bfb;
658 }
659 else {
660 MainThreadWorkQ.head = &bfb;
661 MainThreadWorkQ.tail = &bfb;
662 }
663 pthread_mutex_unlock(&MainThreadWorkQ.mutex);
2bfd4448 664
b3962a83 665 //printf("----->asking main thread to finalize\n");
7af964d1 666 dispatch_async(dispatch_get_main_queue(), ^{ batchFinalizeOnMainThread(); });
2bfd4448 667
b3962a83 668 // wait for the main thread to finish finalizing instances of classes marked CLS_FINALIZE_ON_MAIN_THREAD.
7af964d1 669 pthread_mutex_lock(&MainThreadWorkQ.mutex);
8972963c
A
670 while (!bfb.finished) {
671 // the main thread might be blocked waiting for a synchronous collection to complete, so wake it here
672 pthread_cond_signal(&MainThreadWorkQ.condition);
673 pthread_cond_wait(&MainThreadWorkQ.condition, &MainThreadWorkQ.mutex);
674 }
7af964d1 675 pthread_mutex_unlock(&MainThreadWorkQ.mutex);
b3962a83
A
676 //printf("<------ main thread finalize done\n");
677
678}
679
680
b3962a83
A
681
682// collector calls this with garbage ready
7af964d1 683// thread collectors, too, so this needs to be thread-safe
b3962a83
A
684static void BatchInvalidate(auto_zone_t *zone,
685 auto_zone_foreach_object_t foreach,
686 auto_zone_cursor_t cursor,
687 size_t cursor_size)
2bfd4448 688{
b3962a83
A
689 if (pthread_main_np() || !WantsMainThreadFinalization) {
690 // Collect all objects. We're either pre-multithreaded on main thread or we're on the collector thread
691 // but no main-thread-only objects have been allocated.
692 batchFinalize(zone, foreach, cursor, cursor_size, finalizeOneObject);
693 }
694 else {
695 // We're on the dedicated thread. Collect some on main thread, the rest here.
696 batchFinalizeOnTwoThreads(zone, foreach, cursor, cursor_size);
2bfd4448 697 }
b3962a83
A
698
699}
700
7af964d1
A
701
702/*
703 * Zombie support
704 * Collector calls into this system when it finds resurrected objects.
705 * This keeps them pitifully alive and leaked, even if they reference garbage.
706 */
707
b3962a83
A
708// idea: keep a side table mapping resurrected object pointers to their original Class, so we don't
709// need to smash anything. alternatively, could use associative references to track against a secondary
710// object with information about the resurrection, such as a stack crawl, etc.
711
712static Class _NSResurrectedObjectClass;
7257e56c 713static NXMapTable *_NSResurrectedObjectMap = nil;
7af964d1 714static pthread_mutex_t _NSResurrectedObjectLock = PTHREAD_MUTEX_INITIALIZER;
b3962a83
A
715
716static Class resurrectedObjectOriginalClass(id object) {
717 Class originalClass;
7af964d1 718 pthread_mutex_lock(&_NSResurrectedObjectLock);
b3962a83 719 originalClass = (Class) NXMapGet(_NSResurrectedObjectMap, object);
7af964d1 720 pthread_mutex_unlock(&_NSResurrectedObjectLock);
b3962a83
A
721 return originalClass;
722}
723
724static id _NSResurrectedObject_classMethod(id self, SEL selector) { return self; }
725
726static id _NSResurrectedObject_instanceMethod(id self, SEL name) {
7257e56c 727 _objc_inform("**resurrected** object %p of class %s being sent message '%s'\n", (void*)self, class_getName(resurrectedObjectOriginalClass(self)), sel_getName(name));
b3962a83 728 return self;
2bfd4448
A
729}
730
b3962a83
A
731static void _NSResurrectedObject_finalize(id self, SEL _cmd) {
732 Class originalClass;
7af964d1 733 pthread_mutex_lock(&_NSResurrectedObjectLock);
b3962a83 734 originalClass = (Class) NXMapRemove(_NSResurrectedObjectMap, self);
7af964d1 735 pthread_mutex_unlock(&_NSResurrectedObjectLock);
7257e56c 736 if (originalClass) _objc_inform("**resurrected** object %p of class %s being finalized\n", (void*)self, class_getName(originalClass));
cd5f04f5 737 _objc_rootFinalize(self);
2bfd4448 738}
2bfd4448 739
b3962a83
A
740static BOOL _NSResurrectedObject_resolveInstanceMethod(id self, SEL _cmd, SEL name) {
741 class_addMethod((Class)self, name, (IMP)_NSResurrectedObject_instanceMethod, "@@:");
742 return YES;
2bfd4448 743}
b3962a83
A
744
745static BOOL _NSResurrectedObject_resolveClassMethod(id self, SEL _cmd, SEL name) {
7257e56c 746 class_addMethod(self->ISA(), name, (IMP)_NSResurrectedObject_classMethod, "@@:");
b3962a83 747 return YES;
2bfd4448 748}
2bfd4448 749
b3962a83
A
750static void _NSResurrectedObject_initialize() {
751 _NSResurrectedObjectMap = NXCreateMapTable(NXPtrValueMapPrototype, 128);
752 _NSResurrectedObjectClass = objc_allocateClassPair(objc_getClass("NSObject"), "_NSResurrectedObject", 0);
753 class_addMethod(_NSResurrectedObjectClass, @selector(finalize), (IMP)_NSResurrectedObject_finalize, "v@:");
7257e56c 754 Class metaClass = _NSResurrectedObjectClass->ISA();
b3962a83
A
755 class_addMethod(metaClass, @selector(resolveInstanceMethod:), (IMP)_NSResurrectedObject_resolveInstanceMethod, "c@::");
756 class_addMethod(metaClass, @selector(resolveClassMethod:), (IMP)_NSResurrectedObject_resolveClassMethod, "c@::");
757 objc_registerClassPair(_NSResurrectedObjectClass);
758}
2bfd4448
A
759
760static void resurrectZombie(auto_zone_t *zone, void *ptr) {
b3962a83 761 id object = (id) ptr;
7257e56c 762 Class cls = object->ISA();
b3962a83
A
763 if (cls != _NSResurrectedObjectClass) {
764 // remember the original class for this instance.
7af964d1 765 pthread_mutex_lock(&_NSResurrectedObjectLock);
b3962a83 766 NXMapInsert(_NSResurrectedObjectMap, ptr, cls);
7af964d1 767 pthread_mutex_unlock(&_NSResurrectedObjectLock);
8972963c 768 object_setClass(object, _NSResurrectedObjectClass);
2bfd4448
A
769 }
770}
771
772/***********************************************************************
b3962a83 773* Pretty printing support
2bfd4448
A
774* For development purposes.
775**********************************************************************/
776
2bfd4448 777
b3962a83 778static char *name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t offset, int withRetainCount);
2bfd4448 779
b3962a83 780static char* objc_name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t offset)
2bfd4448 781{
b3962a83 782 return name_for_address(zone, base, offset, false);
2bfd4448
A
783}
784
8972963c
A
785static const char* objc_name_for_object(auto_zone_t *zone, void *object) {
786 Class cls = *(Class *)object;
787 if (!objc_isRegisteredClass(cls)) return "";
788 return class_getName(cls);
789}
790
b3962a83
A
791/***********************************************************************
792* Collection support
793**********************************************************************/
2bfd4448 794
7af964d1
A
795static BOOL objc_isRegisteredClass(Class candidate);
796
8972963c
A
797static const unsigned char *objc_layout_for_address(auto_zone_t *zone, void *address) {
798 id object = (id)address;
7257e56c
A
799 volatile void *clsptr = (void*)object->ISA();
800 Class cls = (Class)clsptr;
801 return objc_isRegisteredClass(cls) ? _object_getIvarLayout(cls, object) : nil;
2bfd4448
A
802}
803
8972963c
A
804static const unsigned char *objc_weak_layout_for_address(auto_zone_t *zone, void *address) {
805 id object = (id)address;
7257e56c
A
806 volatile void *clsptr = (void*)object->ISA();
807 Class cls = (Class)clsptr;
808 return objc_isRegisteredClass(cls) ? class_getWeakIvarLayout(cls) : nil;
2bfd4448
A
809}
810
cd5f04f5 811void gc_register_datasegment(uintptr_t base, size_t size) {
7af964d1
A
812 auto_zone_register_datasegment(gc_zone, (void*)base, size);
813}
2bfd4448 814
cd5f04f5 815void gc_unregister_datasegment(uintptr_t base, size_t size) {
7af964d1
A
816 auto_zone_unregister_datasegment(gc_zone, (void*)base, size);
817}
2bfd4448 818
2bfd4448 819
7af964d1
A
820/***********************************************************************
821* Initialization
822**********************************************************************/
b3962a83 823
7af964d1 824static void objc_will_grow(auto_zone_t *zone, auto_heap_growth_info_t info) {
8972963c
A
825 if (auto_zone_is_collecting(gc_zone)) {
826 ;
827 }
828 else {
829 auto_zone_collect(gc_zone, AUTO_ZONE_COLLECT_COALESCE|AUTO_ZONE_COLLECT_RATIO_COLLECTION);
2bfd4448
A
830 }
831}
832
833
7257e56c 834static auto_zone_t *gc_zone_init(void)
2bfd4448
A
835{
836 auto_zone_t *result;
8972963c
A
837 static int didOnce = 0;
838 if (!didOnce) {
839 didOnce = 1;
840
841 // initialize the batch finalization queue
7257e56c
A
842 MainThreadWorkQ.head = nil;
843 MainThreadWorkQ.tail = nil;
844 pthread_mutex_init(&MainThreadWorkQ.mutex, nil);
845 pthread_cond_init(&MainThreadWorkQ.condition, nil);
8972963c
A
846 }
847
b3962a83 848 result = auto_zone_create("auto_zone");
2bfd4448 849
7257e56c 850 auto_zone_disable_compaction(result);
8972963c 851
2bfd4448
A
852 auto_collection_control_t *control = auto_collection_parameters(result);
853
854 // set up the magic control parameters
b3962a83
A
855 control->batch_invalidate = BatchInvalidate;
856 control->will_grow = objc_will_grow;
2bfd4448 857 control->resurrect = resurrectZombie;
b3962a83
A
858 control->layout_for_address = objc_layout_for_address;
859 control->weak_layout_for_address = objc_weak_layout_for_address;
2bfd4448 860 control->name_for_address = objc_name_for_address;
8972963c
A
861
862 if (control->version >= sizeof(auto_collection_control_t)) {
863 control->name_for_object = objc_name_for_object;
864 }
b3962a83 865
2bfd4448
A
866 return result;
867}
868
869
7af964d1
A
870/* should be defined in /usr/local/include/libdispatch_private.h. */
871extern void (*dispatch_begin_thread_4GC)(void);
872extern void (*dispatch_end_thread_4GC)(void);
873
8972963c
A
874static void objc_reapThreadLocalBlocks()
875{
876 if (UseGC) auto_zone_reap_all_local_blocks(gc_zone);
877}
878
7af964d1
A
879void objc_registerThreadWithCollector()
880{
881 if (UseGC) auto_zone_register_thread(gc_zone);
882}
883
884void objc_unregisterThreadWithCollector()
885{
886 if (UseGC) auto_zone_unregister_thread(gc_zone);
887}
888
889void objc_assertRegisteredThreadWithCollector()
890{
891 if (UseGC) auto_zone_assert_thread_registered(gc_zone);
892}
893
894// Always called by _objcInit, even if GC is off.
7257e56c 895void gc_init(BOOL wantsGC)
7af964d1 896{
7257e56c 897 assert(UseGC == -1);
8972963c 898 UseGC = wantsGC;
7af964d1
A
899
900 if (PrintGC) {
8972963c 901 _objc_inform("GC: is %s", wantsGC ? "ON" : "OFF");
7af964d1
A
902 }
903
904 if (UseGC) {
7af964d1 905 // Set up the GC zone
7257e56c 906 gc_zone = gc_zone_init();
8972963c
A
907
908 // tell libdispatch to register its threads with the GC.
909 dispatch_begin_thread_4GC = objc_registerThreadWithCollector;
910 dispatch_end_thread_4GC = objc_reapThreadLocalBlocks;
7af964d1 911
7af964d1
A
912 // set up the registered classes list
913 registeredClassTableInit();
914
915 // tell Blocks to use collectable memory. CF will cook up the classes separately.
916 gc_block_init();
cd5f04f5
A
917
918 // Add GC state to crash log reports
919 _objc_inform_on_crash("garbage collection is ON");
7af964d1
A
920 }
921}
922
923
cd5f04f5
A
924// Called by NSObject +load to perform late GC setup
925// This work must wait until after all of libSystem initializes.
926void gc_init2(void)
2bfd4448 927{
cd5f04f5 928 assert(UseGC);
2bfd4448 929
b3962a83
A
930 // create the _NSResurrectedObject class used to track resurrections.
931 _NSResurrectedObject_initialize();
932
cd5f04f5
A
933 // tell libauto to set up its dispatch queues
934 auto_collect_multithreaded(gc_zone);
935}
936
937// Called by Foundation.
938// This function used to initialize NSObject stuff, but now does nothing.
939malloc_zone_t *objc_collect_init(int (*callback)(void) __unused)
940{
2bfd4448
A
941 return (malloc_zone_t *)gc_zone;
942}
943
7af964d1
A
944/*
945 * Support routines for the Block implementation
946 */
947
2bfd4448 948
7af964d1
A
949// The Block runtime now needs to sometimes allocate a Block that is an Object - namely
950// when it neesd to have a finalizer which, for now, is only if there are C++ destructors
951// in the helper function. Hence the isObject parameter.
952// Under GC a -copy message should allocate a refcount 0 block, ergo the isOne parameter.
953static void *block_gc_alloc5(const unsigned long size, const bool isOne, const bool isObject) {
954 auto_memory_type_t type = isObject ? (AUTO_OBJECT|AUTO_MEMORY_SCANNED) : AUTO_MEMORY_SCANNED;
955 return auto_zone_allocate_object(gc_zone, size, type, isOne, false);
956}
957
958// The Blocks runtime keeps track of everything above 1 and so it only calls
959// up to the collector to tell it about the 0->1 transition and then the 1->0 transition
960static void block_gc_setHasRefcount(const void *block, const bool hasRefcount) {
961 if (hasRefcount)
962 auto_zone_retain(gc_zone, (void *)block);
963 else
964 auto_zone_release(gc_zone, (void *)block);
965}
966
967static void block_gc_memmove(void *dst, void *src, unsigned long size) {
968 auto_zone_write_barrier_memmove(gc_zone, dst, src, (size_t)size);
969}
2bfd4448 970
7af964d1 971static void gc_block_init(void) {
7af964d1 972 _Block_use_GC(
8972963c
A
973 block_gc_alloc5,
974 block_gc_setHasRefcount,
975 (void (*)(void *, void **))objc_assign_strongCast_gc,
976 (void (*)(const void *, void *))objc_assign_weak,
977 block_gc_memmove
7af964d1
A
978 );
979}
2bfd4448
A
980
981
982/***********************************************************************
7af964d1
A
983* Track classes.
984* In addition to the global class hashtable (set) indexed by name, we
985* also keep one based purely by pointer when running under Garbage Collection.
986* This allows the background collector to race against objects recycled from TLC.
987* Specifically, the background collector can read the admin byte and see that
988* a thread local object is an object, get scheduled out, and the TLC recovers it,
989* linking it into the cache, then the background collector reads the isa field and
990* finds linkage info. By qualifying all isa fields read we avoid this.
2bfd4448
A
991**********************************************************************/
992
7af964d1
A
993// This is a self-contained hash table of all classes. The first two elements contain the (size-1) and count.
994static volatile Class *AllClasses = nil;
995
996#define SHIFT 3
997#define INITIALSIZE 512
cd5f04f5 998#define REMOVED ~0ul
7af964d1
A
999
1000// Allocate the side table.
1001static void registeredClassTableInit() {
1002 assert(UseGC);
1003 // allocate a collectable (refcount 0) zeroed hunk of unscanned memory
8972963c 1004 uintptr_t *table = (uintptr_t *)auto_zone_allocate_object(gc_zone, INITIALSIZE*sizeof(void *), AUTO_MEMORY_UNSCANNED, true, true);
7af964d1
A
1005 // set initial capacity (as mask)
1006 table[0] = INITIALSIZE - 1;
1007 // set initial count
1008 table[1] = 0;
8972963c 1009 AllClasses = (Class *)table;
7af964d1
A
1010}
1011
1012// Verify that a particular pointer is to a class.
1013// Safe from any thread anytime
1014static BOOL objc_isRegisteredClass(Class candidate) {
1015 assert(UseGC);
8972963c
A
1016 // nil is never a valid ISA.
1017 if (candidate == nil) return NO;
7af964d1
A
1018 // We don't care about a race with another thread adding a class to which we randomly might have a pointer
1019 // Get local copy of classes so that we're immune from updates.
1020 // We keep the size of the list as the first element so there is no race as the list & size get updated.
1021 uintptr_t *allClasses = (uintptr_t *)AllClasses;
1022 // Slot 0 is always the size of the list in log 2 masked terms (e.g. size - 1) where size is always power of 2
1023 // Slot 1 is count
1024 uintptr_t slot = (((uintptr_t)candidate) >> SHIFT) & allClasses[0];
1025 // avoid slot 0 and 1
1026 if (slot < 2) slot = 2;
1027 for(;;) {
1028 long int slotValue = allClasses[slot];
1029 if (slotValue == (long int)candidate) {
1030 return YES;
1031 }
1032 if (slotValue == 0) {
1033 return NO;
1034 }
1035 ++slot;
1036 if (slot > allClasses[0])
1037 slot = 2; // skip size, count
1038 }
1039}
1040
1041// Utility used when growing
1042// Assumes lock held
1043static void addClassHelper(uintptr_t *table, uintptr_t candidate) {
1044 uintptr_t slot = (((long int)candidate) >> SHIFT) & table[0];
1045 if (slot < 2) slot = 2;
1046 for(;;) {
1047 uintptr_t slotValue = table[slot];
1048 if (slotValue == 0) {
1049 table[slot] = candidate;
1050 ++table[1];
1051 return;
1052 }
1053 ++slot;
1054 if (slot > table[0])
1055 slot = 2; // skip size, count
1056 }
2bfd4448
A
1057}
1058
7af964d1 1059// lock held by callers
7af964d1
A
1060void objc_addRegisteredClass(Class candidate) {
1061 if (!UseGC) return;
1062 uintptr_t *table = (uintptr_t *)AllClasses;
1063 // Slot 0 is always the size of the list in log 2 masked terms (e.g. size - 1) where size is always power of 2
1064 // Slot 1 is count - always non-zero
1065 uintptr_t slot = (((long int)candidate) >> SHIFT) & table[0];
1066 if (slot < 2) slot = 2;
1067 for(;;) {
1068 uintptr_t slotValue = table[slot];
1069 assert(slotValue != (uintptr_t)candidate);
1070 if (slotValue == REMOVED) {
1071 table[slot] = (long)candidate;
1072 return;
1073 }
1074 else if (slotValue == 0) {
1075 table[slot] = (long)candidate;
1076 if (2*++table[1] > table[0]) { // add to count; check if we cross 50% utilization
1077 // grow
1078 uintptr_t oldSize = table[0]+1;
8972963c 1079 uintptr_t *newTable = (uintptr_t *)auto_zone_allocate_object(gc_zone, oldSize*2*sizeof(void *), AUTO_MEMORY_UNSCANNED, true, true);
7af964d1
A
1080 uintptr_t i;
1081 newTable[0] = 2*oldSize - 1;
1082 newTable[1] = 0;
1083 for (i = 2; i < oldSize; ++i) {
1084 if (table[i] && table[i] != REMOVED)
1085 addClassHelper(newTable, table[i]);
1086 }
8972963c
A
1087 AllClasses = (Class *)newTable;
1088 // let the old table be collected when other threads are no longer reading it.
1089 auto_zone_release(gc_zone, (void *)table);
7af964d1
A
1090 }
1091 return;
1092 }
1093 ++slot;
1094 if (slot > table[0])
1095 slot = 2; // skip size, count
1096 }
1097}
1098
1099// lock held by callers
7af964d1
A
1100void objc_removeRegisteredClass(Class candidate) {
1101 if (!UseGC) return;
1102 uintptr_t *table = (uintptr_t *)AllClasses;
1103 // Slot 0 is always the size of the list in log 2 masked terms (e.g. size - 1) where size is always power of 2
1104 // Slot 1 is count - always non-zero
1105 uintptr_t slot = (((uintptr_t)candidate) >> SHIFT) & table[0];
1106 if (slot < 2) slot = 2;
1107 for(;;) {
1108 uintptr_t slotValue = table[slot];
1109 if (slotValue == (uintptr_t)candidate) {
1110 table[slot] = REMOVED; // if next slot == 0 we could set to 0 here and decr count
1111 return;
1112 }
1113 assert(slotValue != 0);
1114 ++slot;
1115 if (slot > table[0])
1116 slot = 2; // skip size, count
1117 }
1118}
1119
1120
1121/***********************************************************************
1122* Debugging - support for smart printouts when errors occur
1123**********************************************************************/
1124
1125
2bfd4448
A
1126static malloc_zone_t *objc_debug_zone(void)
1127{
7257e56c 1128 static malloc_zone_t *z = nil;
2bfd4448 1129 if (!z) {
8070259c 1130 z = malloc_create_zone(PAGE_MAX_SIZE, 0);
2bfd4448
A
1131 malloc_set_zone_name(z, "objc-auto debug");
1132 }
1133 return z;
1134}
1135
b3962a83 1136static char *_malloc_append_unsigned(uintptr_t value, unsigned base, char *head) {
2bfd4448
A
1137 if (!value) {
1138 head[0] = '0';
1139 } else {
1140 if (value >= base) head = _malloc_append_unsigned(value / base, base, head);
1141 value = value % base;
1142 head[0] = (value < 10) ? '0' + value : 'a' + value - 10;
1143 }
1144 return head+1;
1145}
1146
7af964d1 1147static void strlcati(char *str, uintptr_t value, size_t bufSize)
2bfd4448 1148{
7af964d1
A
1149 if ( (bufSize - strlen(str)) < 30)
1150 return;
2bfd4448
A
1151 str = _malloc_append_unsigned(value, 10, str + strlen(str));
1152 str[0] = '\0';
1153}
1154
2bfd4448 1155
b3962a83 1156static Ivar ivar_for_offset(Class cls, vm_address_t offset)
2bfd4448 1157{
cd5f04f5
A
1158 unsigned i;
1159 vm_address_t ivar_offset;
b3962a83
A
1160 Ivar super_ivar, result;
1161 Ivar *ivars;
1162 unsigned int ivar_count;
2bfd4448 1163
7257e56c 1164 if (!cls) return nil;
2bfd4448
A
1165
1166 // scan base classes FIRST
7257e56c 1167 super_ivar = ivar_for_offset(cls->superclass, offset);
2bfd4448
A
1168 // result is best-effort; our ivars may be closer
1169
b3962a83
A
1170 ivars = class_copyIvarList(cls, &ivar_count);
1171 if (ivars && ivar_count) {
1172 // Try our first ivar. If it's too big, use super's best ivar.
7af964d1 1173 // (lose 64-bit precision)
b3962a83
A
1174 ivar_offset = ivar_getOffset(ivars[0]);
1175 if (ivar_offset > offset) result = super_ivar;
1176 else if (ivar_offset == offset) result = ivars[0];
7257e56c 1177 else result = nil;
b3962a83
A
1178
1179 // Try our other ivars. If any is too big, use the previous.
7257e56c 1180 for (i = 1; result == nil && i < ivar_count; i++) {
b3962a83
A
1181 ivar_offset = ivar_getOffset(ivars[i]);
1182 if (ivar_offset == offset) {
1183 result = ivars[i];
1184 } else if (ivar_offset > offset) {
1185 result = ivars[i - 1];
1186 }
2bfd4448 1187 }
2bfd4448 1188
b3962a83 1189 // Found nothing. Return our last ivar.
7257e56c 1190 if (result == nil)
b3962a83
A
1191 result = ivars[ivar_count - 1];
1192
1193 free(ivars);
1194 } else {
1195 result = super_ivar;
1196 }
1197
1198 return result;
2bfd4448
A
1199}
1200
7af964d1 1201static void append_ivar_at_offset(char *buf, Class cls, vm_address_t offset, size_t bufSize)
2bfd4448 1202{
7257e56c 1203 Ivar ivar = nil;
2bfd4448
A
1204
1205 if (offset == 0) return; // don't bother with isa
b3962a83 1206 if (offset >= class_getInstanceSize(cls)) {
7af964d1
A
1207 strlcat(buf, ".<extra>+", bufSize);
1208 strlcati(buf, offset, bufSize);
2bfd4448
A
1209 return;
1210 }
1211
1212 ivar = ivar_for_offset(cls, offset);
1213 if (!ivar) {
7af964d1 1214 strlcat(buf, ".<?>", bufSize);
2bfd4448
A
1215 return;
1216 }
1217
1218 // fixme doesn't handle structs etc.
1219
7af964d1 1220 strlcat(buf, ".", bufSize);
b3962a83 1221 const char *ivar_name = ivar_getName(ivar);
7af964d1
A
1222 if (ivar_name) strlcat(buf, ivar_name, bufSize);
1223 else strlcat(buf, "<anonymous ivar>", bufSize);
2bfd4448 1224
b3962a83 1225 offset -= ivar_getOffset(ivar);
2bfd4448 1226 if (offset > 0) {
7af964d1
A
1227 strlcat(buf, "+", bufSize);
1228 strlcati(buf, offset, bufSize);
2bfd4448
A
1229 }
1230}
1231
1232
1233static const char *cf_class_for_object(void *cfobj)
1234{
1235 // ick - we don't link against CF anymore
1236
7257e56c
A
1237 struct fake_cfclass {
1238 size_t version;
1239 const char *className;
1240 // don't care about the rest
1241 };
1242
b3962a83
A
1243 const char *result;
1244 void *dlh;
1245 size_t (*CFGetTypeID)(void *);
7257e56c 1246 fake_cfclass * (*_CFRuntimeGetClassWithTypeID)(size_t);
b3962a83
A
1247
1248 result = "anonymous_NSCFType";
2bfd4448 1249
b3962a83
A
1250 dlh = dlopen("/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", RTLD_LAZY | RTLD_NOLOAD | RTLD_FIRST);
1251 if (!dlh) return result;
2bfd4448 1252
b3962a83 1253 CFGetTypeID = (size_t(*)(void*)) dlsym(dlh, "CFGetTypeID");
7257e56c 1254 _CFRuntimeGetClassWithTypeID = (fake_cfclass*(*)(size_t)) dlsym(dlh, "_CFRuntimeGetClassWithTypeID");
b3962a83
A
1255
1256 if (CFGetTypeID && _CFRuntimeGetClassWithTypeID) {
7257e56c
A
1257 size_t cfid = (*CFGetTypeID)(cfobj);
1258 result = (*_CFRuntimeGetClassWithTypeID)(cfid)->className;
b3962a83 1259 }
2bfd4448 1260
b3962a83
A
1261 dlclose(dlh);
1262 return result;
2bfd4448
A
1263}
1264
1265
1266static char *name_for_address(auto_zone_t *zone, vm_address_t base, vm_address_t offset, int withRetainCount)
1267{
1268#define APPEND_SIZE(s) \
7af964d1
A
1269 strlcat(buf, "[", sizeof(buf)); \
1270 strlcati(buf, s, sizeof(buf)); \
1271 strlcat(buf, "]", sizeof(buf));
2bfd4448 1272
7af964d1 1273 char buf[1500];
2bfd4448
A
1274 char *result;
1275
1276 buf[0] = '\0';
1277
b3962a83 1278 size_t size =
7af964d1 1279 auto_zone_size(zone, (void *)base);
2bfd4448 1280 auto_memory_type_t type = size ?
7af964d1 1281 auto_zone_get_layout_type(zone, (void *)base) : AUTO_TYPE_UNKNOWN;
2bfd4448 1282 unsigned int refcount = size ?
7af964d1 1283 auto_zone_retain_count(zone, (void *)base) : 0;
2bfd4448
A
1284
1285 switch (type) {
1286 case AUTO_OBJECT_SCANNED:
8972963c
A
1287 case AUTO_OBJECT_UNSCANNED:
1288 case AUTO_OBJECT_ALL_POINTERS: {
b3962a83 1289 const char *class_name = object_getClassName((id)base);
7af964d1
A
1290 if ((0 == strcmp(class_name, "__NSCFType")) || (0 == strcmp(class_name, "NSCFType"))) {
1291 strlcat(buf, cf_class_for_object((void *)base), sizeof(buf));
2bfd4448 1292 } else {
7af964d1 1293 strlcat(buf, class_name, sizeof(buf));
2bfd4448
A
1294 }
1295 if (offset) {
7257e56c 1296 append_ivar_at_offset(buf, ((id)base)->ISA(), offset, sizeof(buf));
2bfd4448
A
1297 }
1298 APPEND_SIZE(size);
1299 break;
1300 }
1301 case AUTO_MEMORY_SCANNED:
7af964d1 1302 strlcat(buf, "{conservative-block}", sizeof(buf));
2bfd4448
A
1303 APPEND_SIZE(size);
1304 break;
1305 case AUTO_MEMORY_UNSCANNED:
7af964d1 1306 strlcat(buf, "{no-pointers-block}", sizeof(buf));
2bfd4448
A
1307 APPEND_SIZE(size);
1308 break;
8972963c
A
1309 case AUTO_MEMORY_ALL_POINTERS:
1310 strlcat(buf, "{all-pointers-block}", sizeof(buf));
1311 APPEND_SIZE(size);
1312 break;
1313 case AUTO_MEMORY_ALL_WEAK_POINTERS:
1314 strlcat(buf, "{all-weak-pointers-block}", sizeof(buf));
1315 APPEND_SIZE(size);
1316 break;
1317 case AUTO_TYPE_UNKNOWN:
1318 strlcat(buf, "{uncollectable-memory}", sizeof(buf));
1319 break;
2bfd4448 1320 default:
8972963c 1321 strlcat(buf, "{unknown-memory-type}", sizeof(buf));
2bfd4448
A
1322 }
1323
1324 if (withRetainCount && refcount > 0) {
7af964d1
A
1325 strlcat(buf, " [[refcount=", sizeof(buf));
1326 strlcati(buf, refcount, sizeof(buf));
1327 strlcat(buf, "]]", sizeof(buf));
2bfd4448
A
1328 }
1329
cd5f04f5 1330 size_t len = 1 + strlen(buf);
7257e56c 1331 result = (char *)malloc_zone_malloc(objc_debug_zone(), len);
cd5f04f5 1332 memcpy(result, buf, len);
2bfd4448
A
1333 return result;
1334
1335#undef APPEND_SIZE
1336}
1337
1338
2bfd4448 1339
2bfd4448 1340
2bfd4448 1341
7af964d1 1342#endif