diff --git a/GNUmakefile b/GNUmakefile index ad30fe5..8ea6c09 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -72,6 +72,7 @@ ${LIBOBJC}_HEADER_FILES = \ slot.h\ objc.h\ objc-api.h\ + objc-arc.h\ objc-auto.h\ toydispatch.h endif diff --git a/arc.m b/arc.m index 02501e1..8a6209a 100644 --- a/arc.m +++ b/arc.m @@ -2,6 +2,9 @@ #import "objc/blocks_runtime.h" #import "nsobject.h" #import "selector.h" +#import "visibility.h" +#import "objc/hooks.h" +#import "objc/objc-arc.h" static Class AutoreleasePool; static IMP NewAutoreleasePool; @@ -80,3 +83,182 @@ id objc_storeStrong(id *object, id value) [oldValue release]; return value; } + +//////////////////////////////////////////////////////////////////////////////// +// Weak references +//////////////////////////////////////////////////////////////////////////////// + +typedef struct objc_weak_ref +{ + id obj; + id *ref[4]; + struct objc_weak_ref *next; +} WeakRef; + + +static int weak_ref_compare(const id obj, const WeakRef weak_ref) +{ + return obj == weak_ref.obj; +} + +static uint32_t ptr_hash(const void *ptr) +{ + // Bit-rotate right 4, since the lowest few bits in an object pointer will + // always be 0, which is not so useful for a hash value + return ((uintptr_t)ptr >> 4) | ((uintptr_t)ptr << ((sizeof(id) * 8) - 4)); +} +static int weak_ref_hash(const WeakRef weak_ref) +{ + return ptr_hash(weak_ref.obj); +} +static int weak_ref_is_null(const WeakRef weak_ref) +{ + return weak_ref.obj == NULL; +} +const static WeakRef NullWeakRef; +#define MAP_TABLE_NAME weak_ref +#define MAP_TABLE_COMPARE_FUNCTION weak_ref_compare +#define MAP_TABLE_HASH_KEY ptr_hash +#define MAP_TABLE_HASH_VALUE weak_ref_hash +#define MAP_TABLE_HASH_VALUE weak_ref_hash +#define MAP_TABLE_VALUE_TYPE struct objc_weak_ref +#define MAP_TABLE_VALUE_NULL weak_ref_is_null +#define MAP_TABLE_VALUE_PLACEHOLDER NullWeakRef +#define MAP_TABLE_ACCESS_BY_REFERENCE 1 +#define MAP_TABLE_SINGLE_THREAD 1 +#define MAP_TABLE_NO_LOCK 1 + +#include "hash_table.h" + +static weak_ref_table *weakRefs; +mutex_t weakRefLock; + +PRIVATE void init_arc(void) +{ + weak_ref_initialize(&weakRefs, 128); + INIT_LOCK(weakRefLock); +} + +id objc_storeWeak(id *addr, id obj) +{ + id old = *addr; + LOCK_FOR_SCOPE(&weakRefLock); + WeakRef *oldRef = weak_ref_table_get(weakRefs, old); + while (NULL != oldRef) + { + for (int i=0 ; i<4 ; i++) + { + if (oldRef->ref[i] == addr) + { + oldRef->ref[i] = 0; + oldRef = 0; + break; + } + } + } + if (nil == obj) + { + *addr = obj; + return nil; + } + obj = _objc_weak_load(obj); + if (nil != obj) + { + WeakRef *ref = weak_ref_table_get(weakRefs, obj); + while (NULL != ref) + { + for (int i=0 ; i<4 ; i++) + { + if (0 == ref->ref[i]) + { + ref->ref[i] = addr; + return obj; + } + } + if (ref->next == NULL) + { + break; + } + } + if (NULL != ref) + { + ref->next = calloc(sizeof(WeakRef), 1); + ref->next->ref[0] = addr; + } + else + { + WeakRef newRef = {0}; + newRef.obj = obj; + newRef.ref[0] = addr; + weak_ref_insert(weakRefs, newRef); + } + } + return obj; +} + +static void zeroRefs(WeakRef *ref, BOOL shouldFree) +{ + if (NULL != ref->next) + { + zeroRefs(ref->next, YES); + } + for (int i=0 ; i<4 ; i++) + { + if (0 != ref->ref[i]) + { + *ref->ref[i] = 0; + } + } + if (shouldFree) + { + free(ref); + } + else + { + memset(ref, 0, sizeof(WeakRef)); + } +} + +void objc_delete_weak_refs(id obj) +{ + LOCK_FOR_SCOPE(&weakRefLock); + WeakRef *oldRef = weak_ref_table_get(weakRefs, obj); + if (0 != oldRef) + { + zeroRefs(oldRef, NO); + } +} + +id objc_loadWeakRetained(id* obj) +{ + LOCK_FOR_SCOPE(&weakRefLock); + return objc_retain(*obj); +} + +id objc_loadWeak(id* object) +{ + return objc_autorelease(objc_loadWeakRetained(object)); +} + +void objc_copyWeak(id *dest, id *src) +{ + objc_release(objc_initWeak(dest, objc_loadWeakRetained(src))); +} + +void objc_moveWeak(id *dest, id *src) +{ + // FIXME: src can be zero'd here, removing the relationship between the + // object and the pointer, which can be cheaper. + objc_moveWeak(dest, src); +} + +void objc_destroyWeak(id* obj) +{ + objc_storeWeak(obj, nil); +} + +id objc_initWeak(id *object, id value) +{ + *object = nil; + return objc_storeWeak(object, value); +} diff --git a/caps.c b/caps.c index d1f3c56..e77266d 100644 --- a/caps.c +++ b/caps.c @@ -14,6 +14,7 @@ static const int32_t caps = (1< #include #include +#include #ifdef ENABLE_GC diff --git a/loader.c b/loader.c index a03e6a0..34545b6 100644 --- a/loader.c +++ b/loader.c @@ -15,12 +15,13 @@ PRIVATE mutex_t runtime_mutex; LEGACY void *__objc_runtime_mutex = &runtime_mutex; -void init_selector_tables(void); -void init_protocol_table(void); +void init_alias_table(void); +void init_arc(void); void init_class_tables(void); void init_dispatch_tables(void); -void init_alias_table(void); void init_gc(void); +void init_protocol_table(void); +void init_selector_tables(void); void objc_send_load_message(Class class); /* Number of threads that are alive. */ @@ -57,6 +58,7 @@ void __objc_exec_class(struct objc_module_abi_8 *module) init_class_tables(); init_dispatch_tables(); init_alias_table(); + init_arc(); first_run = NO; } diff --git a/objc/capabilities.h b/objc/capabilities.h index 3cae127..11a5a23 100644 --- a/objc/capabilities.h +++ b/objc/capabilities.h @@ -80,6 +80,11 @@ extern "C" { * alias as the argument. */ #define OBJC_CAP_REGISTERED_COMPATIBILITY_ALIASES 10 +/** + * The runtime supports automatic reference counting, including support for + * __weak references. + */ +#define OBJC_CAP_ARC 11 /** * Macro used to require the existence of a specific capability. This creates diff --git a/objc/hooks.h b/objc/hooks.h index 05a0543..7fdcf88 100644 --- a/objc/hooks.h +++ b/objc/hooks.h @@ -64,3 +64,11 @@ OBJC_HOOK Class (*_objc_class_for_boxing_foreign_exception)(int64_t exceptionCla */ extern struct objc_slot* (*_objc_selector_type_mismatch)(Class cls, SEL selector, struct objc_slot *result); + +/** + * Returns the object if it is not currently in the process of being + * deallocated. Returns nil otherwise. + * + * This hook must be set for weak references to work with automatic reference counting. + */ +OBJC_HOOK id (*_objc_weak_load)(id object); diff --git a/objc/objc-arc.h b/objc/objc-arc.h new file mode 100644 index 0000000..d91ecc8 --- /dev/null +++ b/objc/objc-arc.h @@ -0,0 +1,26 @@ +id objc_autorelease(id obj); +id objc_autoreleaseReturnValue(id obj); +id objc_initWeak(id *object, id value); +id objc_loadWeak(id* object); +id objc_loadWeakRetained(id* obj); +id objc_retain(id obj); +id objc_retainAutorelease(id obj); +id objc_retainAutoreleaseReturnValue(id obj); +id objc_retainAutoreleasedReturnValue(id obj); +id objc_retainBlock(id b); +id objc_storeStrong(id *object, id value); +id objc_storeWeak(id *addr, id obj); +void *objc_autoreleasePoolPush(void); +void objc_autoreleasePoolPop(void *pool); +void objc_copyWeak(id *dest, id *src); +void objc_destroyWeak(id* obj); +void objc_moveWeak(id *dest, id *src); +void objc_release(id obj); +/** + * Mark the object as about to begin deallocation. All subsequent reads of + * weak pointers will return 0. This function should be called in -release, + * before calling [self dealloc]. + * + * Nonstandard extension. + */ +void objc_delete_weak_refs(id obj); diff --git a/string_hash.h b/string_hash.h index 67e37ea..d86ed0b 100644 --- a/string_hash.h +++ b/string_hash.h @@ -7,7 +7,7 @@ __attribute__((unused)) static uint32_t string_hash(const char *str) { - uint32_t hash = 33; + uint32_t hash = 0; int32_t c; while ((c = *str++)) {