diff --git a/Protocol2.m b/Protocol2.m index 8ee8bff..bd184cb 100644 --- a/Protocol2.m +++ b/Protocol2.m @@ -5,10 +5,6 @@ #include @implementation Protocol -+ (void)load -{ - objc_clear_class_flag(self, objc_class_flag_plane_aware); -} // FIXME: This needs removing, but it's included for now because GNUstep's // implementation of +[NSObject conformsToProtocol:] calls it. - (BOOL)conformsTo: (Protocol*)p @@ -23,12 +19,6 @@ + (Class)class { return self; } - (id)self { return self; } @end -@implementation Protocol2 -+ (void)load -{ - objc_clear_class_flag(self, objc_class_flag_plane_aware); -} -@end /** * This class exists for the sole reason that the legacy GNU ABI did not diff --git a/arc.m b/arc.m index 1a0a7b6..e61a569 100644 --- a/arc.m +++ b/arc.m @@ -2,6 +2,7 @@ #import "objc/runtime.h" #import "objc/blocks_runtime.h" #import "nsobject.h" +#import "class.h" #import "selector.h" #import "visibility.h" #import "objc/hooks.h" @@ -24,6 +25,46 @@ static IMP NewAutoreleasePool; static IMP DeleteAutoreleasePool; static IMP AutoreleaseAdd; +extern BOOL FastARCRetain; +extern BOOL FastARCRelease; +extern BOOL FastARCAutorelease; + +static inline id retain(id obj) +{ + if (objc_test_class_flag(obj->isa, objc_class_flag_fast_arc)) + { + intptr_t *refCount = ((intptr_t*)obj) - 1; + __sync_fetch_and_add(refCount, 1); + return obj; + } + return [obj retain]; +} + +static inline void release(id obj) +{ + if (objc_test_class_flag(obj->isa, objc_class_flag_fast_arc)) + { + intptr_t *refCount = ((intptr_t*)obj) - 1; + if (__sync_fetch_and_sub(refCount, 1) < 0) + { + objc_delete_weak_refs(obj); + [obj dealloc]; + } + return; + } + [obj release]; +} + +static inline id autorelease(id obj) +{ + if (objc_test_class_flag(obj->isa, objc_class_flag_fast_arc)) + { + AutoreleaseAdd(AutoreleasePool, SELECTOR(addObject:), obj); + return obj; + } + return [obj autorelease]; +} + void *objc_autoreleasePoolPush(void) { @@ -54,10 +95,9 @@ void objc_autoreleasePoolPop(void *pool) id objc_autorelease(id obj) { - return [obj autorelease]; if (nil != obj) { - AutoreleaseAdd(AutoreleasePool, SELECTOR(addObject:), obj); + obj = autorelease(obj); } return obj; } @@ -100,7 +140,8 @@ id objc_retainAutoreleasedReturnValue(id obj) id objc_retain(id obj) { - return [obj retain]; + if (nil == obj) { return nil; } + return retain(obj); } id objc_retainAutorelease(id obj) @@ -121,15 +162,16 @@ id objc_retainBlock(id b) void objc_release(id obj) { - [obj release]; + if (nil == obj) { return; } + release(obj); } -id objc_storeStrong(id *object, id value) +id objc_storeStrong(id *addr, id value) { - value = [value retain]; - id oldValue = *object; - *object = value; - [oldValue release]; + value = objc_retain(value); + id oldValue = *addr; + *addr = value; + objc_release(oldValue); return value; } diff --git a/class.h b/class.h index 56f48bf..f24a811 100644 --- a/class.h +++ b/class.h @@ -168,12 +168,10 @@ enum objc_class_flags */ objc_class_flag_user_created = (1<<5), /** - * Instances of this class have a reference count and plane ID prepended to - * them. The default for this is set for classes, unset for metaclasses. - * It should be cleared by protocols, constant strings, and objects not - * allocated by NSAllocateObject(). + * Instances of this class are provide ARC-safe retain / release / + * autorelease implementations. */ - objc_class_flag_plane_aware = (1<<6), + objc_class_flag_fast_arc = (1<<6), /** * This class is a hidden class (should not be registered in the class * table nor returned from object_getClass()). @@ -185,16 +183,25 @@ enum objc_class_flags objc_class_flag_assoc_class = (1<<8) }; +/** + * Sets the specific class flag. Note: This is not atomic. + */ static inline void objc_set_class_flag(struct objc_class *aClass, enum objc_class_flags flag) { aClass->info |= (unsigned long)flag; } +/** + * Unsets the specific class flag. Note: This is not atomic. + */ static inline void objc_clear_class_flag(struct objc_class *aClass, enum objc_class_flags flag) { aClass->info &= ~(unsigned long)flag; } +/** + * Checks whether a specific class flag is set. + */ static inline BOOL objc_test_class_flag(struct objc_class *aClass, enum objc_class_flags flag) { diff --git a/dtable.c b/dtable.c index 2b93cc4..3183061 100644 --- a/dtable.c +++ b/dtable.c @@ -16,9 +16,62 @@ PRIVATE dtable_t uninstalled_dtable; /** Head of the list of temporary dtables. Protected by initialize_lock. */ PRIVATE InitializingDtable *temporary_dtables; +/** Lock used to protect the temporary dtables list. */ PRIVATE mutex_t initialize_lock; +/** The size of the largest dtable, rounded up to the nearest power of two. */ static uint32_t dtable_depth = 8; +struct objc_slot* objc_get_slot(Class cls, SEL selector); + +/** + * Returns YES if the class implements a method for the specified selector, NO + * otherwise. + */ +static BOOL ownsMethod(Class cls, SEL sel) +{ + struct objc_slot *slot = objc_get_slot(cls, sel); + if ((NULL != slot) && (slot->owner == cls)) + { + return YES; + } + return NO; +} + +/** + * Checks whether the class implements memory management methods, and whether + * they are safe to use with ARC. + */ +static void checkARCAccessors(Class cls) +{ + static SEL retain, release, autorelease, isARC; + if (NULL == retain) + { + retain = sel_registerName("retain"); + release = sel_registerName("release"); + autorelease = sel_registerName("autorelease"); + isARC = sel_registerName("_ARCCompliantRetainRelease"); + } + if (ownsMethod(cls, isARC)) + { + objc_set_class_flag(cls, objc_class_flag_fast_arc); + return; + } + struct objc_slot *slot = objc_get_slot(cls, retain); + if (!ownsMethod(slot->owner, isARC)) + { + objc_clear_class_flag(cls, objc_class_flag_fast_arc); + } + slot = objc_get_slot(cls, release); + if (!ownsMethod(slot->owner, isARC)) + { + objc_clear_class_flag(cls, objc_class_flag_fast_arc); + } + slot = objc_get_slot(cls, autorelease); + if (!ownsMethod(slot->owner, isARC)) + { + objc_clear_class_flag(cls, objc_class_flag_fast_arc); + } +} static void collectMethodsForMethodListToSparseArray( struct objc_method_list *list, @@ -418,6 +471,7 @@ PRIVATE void objc_update_dtable_for_class(Class cls) // Methods now contains only the new methods for this class. mergeMethodsFromSuperclass(cls, cls, methods); SparseArrayDestroy(methods); + checkARCAccessors(cls); } PRIVATE void add_method_list_to_class(Class cls, @@ -434,6 +488,7 @@ PRIVATE void add_method_list_to_class(Class cls, // Methods now contains only the new methods for this class. mergeMethodsFromSuperclass(cls, cls, methods); SparseArrayDestroy(methods); + checkARCAccessors(cls); } static dtable_t create_dtable_for_class(Class class, dtable_t root_dtable) @@ -645,6 +700,7 @@ PRIVATE void objc_send_initialize(id object) { meta->dtable = dtable; class->dtable = class_dtable; + checkARCAccessors(class); UNLOCK(&initialize_lock); return; } @@ -662,6 +718,8 @@ PRIVATE void objc_send_initialize(id object) temporary_dtables = &meta_buffer; UNLOCK(&initialize_lock); + checkARCAccessors(class); + // Store the buffer in the temporary dtables list. Note that it is safe to // insert it into a global list, even though it's a temporary variable, // because we will clean it up after this function. diff --git a/gc_boehm.c b/gc_boehm.c index ed1329d..96ac960 100644 --- a/gc_boehm.c +++ b/gc_boehm.c @@ -62,6 +62,8 @@ struct objc_slot* objc_get_slot(Class cls, SEL selector); #define __sync_swap __sync_lock_test_and_set #endif +void call_cxx_destruct(id obj); + #ifdef NO_EXECINFO static inline void dump_stack(char *msg, void *addr) {} #else @@ -306,8 +308,6 @@ id objc_assign_weak(id value, id *location) } static SEL finalize; -static SEL cxx_destruct; - Class zombie_class; @@ -327,17 +327,13 @@ static void runFinalize(void *addr, void *context) abort(); } //fprintf(stderr, "FINALIZING %p (%s)\n", addr, ((id)addr)->isa->name); - if (Nil == ((id)addr)->isa) { return; } - struct objc_slot *slot = objc_get_slot(obj->isa, cxx_destruct); - if (NULL != slot) - { - slot->method(obj, cxx_destruct); - } - slot = objc_get_slot(obj->isa, finalize); + if (Nil == obj->isa) { return; } + struct objc_slot *slot = objc_get_slot(obj->isa, finalize); if (NULL != slot) { slot->method(obj, finalize); } + call_cxx_destruct(obj); *(void**)addr = zombie_class; } @@ -728,6 +724,5 @@ PRIVATE void enableGC(BOOL exclude) gc = &gc_ops_boehm; refcount_initialize(&refcounts, 4096); finalize = sel_registerName("finalize"); - cxx_destruct = sel_registerName(".cxx_destruct"); GC_finalizer_notifier = runFinalizers; } diff --git a/gc_none.c b/gc_none.c index 569e5b3..c9c8796 100644 --- a/gc_none.c +++ b/gc_none.c @@ -7,7 +7,8 @@ static id allocate_class(Class cls, size_t extraBytes) { - return calloc(cls->instance_size + extraBytes, 1); + intptr_t *addr = calloc(cls->instance_size + extraBytes + sizeof(intptr_t), 1); + return (id)(addr + 1); } static void *alloc(size_t size) diff --git a/runtime.c b/runtime.c index 290e3fb..5467be7 100644 --- a/runtime.c +++ b/runtime.c @@ -22,6 +22,52 @@ struct objc_slot *objc_get_slot(Class cls, SEL selector); #define CHECK_ARG(arg) if (0 == arg) { return 0; } +/** + * Calls C++ destructors in the correct order. + */ +PRIVATE void call_cxx_destruct(id obj) +{ + static SEL cxx_destruct; + if (NULL == cxx_destruct) + { + cxx_destruct = sel_registerName(".cxx_destruct"); + } + // Don't call object_getClass(), because we want to get hidden classes too + Class cls = obj->isa; + + while (cls) + { + struct objc_slot *slot = objc_get_slot(cls, cxx_destruct); + cls = Nil; + if (NULL != slot) + { + slot->method(obj, cxx_destruct); + cls = slot->owner->super_class; + } + } +} + +static void call_cxx_construct_for_class(Class cls, id obj) +{ + static SEL cxx_construct; + if (NULL == cxx_construct) + { + cxx_construct = sel_registerName(".cxx_contruct"); + } + struct objc_slot *slot = objc_get_slot(cls, cxx_construct); + cls = slot->owner->super_class; + if (Nil != cls) + { + call_cxx_construct_for_class(cls, obj); + } + slot->method(obj, cxx_construct); +} + +PRIVATE void call_cxx_construct(id obj) +{ + call_cxx_construct_for_class(obj->isa, obj); +} + /** * Looks up the instance method in a specific class, without recursing into * superclasses. @@ -277,6 +323,7 @@ id class_createInstance(Class cls, size_t extraBytes) if (Nil == cls) { return nil; } id obj = gc->allocate_class(cls, extraBytes); obj->isa = cls; + call_cxx_construct(obj); return obj; } @@ -290,7 +337,8 @@ id object_copy(id obj, size_t size) id object_dispose(id obj) { - free(obj); + call_cxx_destruct(obj); + gc->free(obj); return nil; } @@ -673,3 +721,4 @@ void objc_registerClassPair(Class cls) LOCK_RUNTIME_FOR_SCOPE(); class_table_insert(cls); } +