Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -659,11 +659,13 @@ AC_SUBST(RUNTIME_AUTORELEASE_M, "runtime/autorelease.m") ]) AC_CHECK_FUNC(objc_constructInstance, [], [ AC_SUBST(RUNTIME_INSTANCE_M, "runtime/instance.m") ]) - AC_CHECK_FUNCS(objc_setAssociatedObject) + AC_CHECK_FUNCS(objc_setAssociatedObject, [], [ + AC_SUBST(RUNTIME_ASSOCIATION_M, "runtime/association.m") + ]) OBJCFLAGS="$old_OBJCFLAGS" ;; esac Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -65,10 +65,11 @@ OF_SELECT_KERNEL_EVENT_OBSERVER_M = @OF_SELECT_KERNEL_EVENT_OBSERVER_M@ REEXPORT_RUNTIME = @REEXPORT_RUNTIME@ REEXPORT_RUNTIME_FRAMEWORK = @REEXPORT_RUNTIME_FRAMEWORK@ RUNTIME = @RUNTIME@ RUNTIME_ARC_TESTS_M = @RUNTIME_ARC_TESTS_M@ +RUNTIME_ASSOCIATION_M = @RUNTIME_ASSOCIATION_M@ RUNTIME_AUTORELEASE_M = @RUNTIME_AUTORELEASE_M@ RUNTIME_FRAMEWORK_LIBS = @RUNTIME_FRAMEWORK_LIBS@ RUNTIME_INSTANCE_M = @RUNTIME_INSTANCE_M@ RUNTIME_LIBS = @RUNTIME_LIBS@ SUBPROCESS = @SUBPROCESS@ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -220,11 +220,11 @@ OFStrFTime.m \ OFStrPTime.m \ OFSubarray.m \ OFSubdata.m \ OFUTF8String.m \ - ${LIBBASES_M} \ + ${RUNTIME_ASSOCIATION_M} \ ${RUNTIME_AUTORELEASE_M} \ ${RUNTIME_INSTANCE_M} \ ${UNICODE_M} \ ${USE_SRCS_TAGGED_POINTERS} SRCS_FILES += OFFileIRIHandler.m Index: src/OFObject.h ================================================================== --- src/OFObject.h +++ src/OFObject.h @@ -1461,10 +1461,23 @@ extern void objc_autoreleasePoolPop(void *_Null_unspecified pool); # ifndef __OBJC2__ extern id _Nullable objc_constructInstance(Class _Nullable class_, void *_Nullable bytes); extern void *_Nullable objc_destructInstance(id _Nullable object); +typedef enum objc_associationPolicy { + OBJC_ASSOCIATION_ASSIGN = 0, + OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, + OBJC_ASSOCIATION_RETAIN = OBJC_ASSOCIATION_RETAIN_NONATOMIC | 0x300, + OBJC_ASSOCIATION_COPY_NONATOMIC = 3, + OBJC_ASSOCIATION_COPY = OBJC_ASSOCIATION_COPY_NONATOMIC | 0x300 +} objc_associationPolicy; +extern void objc_setAssociatedObject(id _Nonnull object, + const void *_Nonnull key, id _Nullable value, + objc_associationPolicy policy); +extern id _Nullable objc_getAssociatedObject(id _Nonnull object, + const void *_Nonnull key); +extern void objc_removeAssociatedObjects(id _Nonnull object); # endif #endif extern id OFAllocObject(Class class_, size_t extraSize, size_t extraAlignment, void *_Nullable *_Nullable extra); extern void OF_NO_RETURN_FUNC OFMethodNotFound(id self, SEL _cmd); Index: src/runtime/association.m ================================================================== --- src/runtime/association.m +++ src/runtime/association.m @@ -13,26 +13,73 @@ * file. */ #include "config.h" -#import "ObjFWRT.h" -#import "private.h" +#ifdef OF_OBJFW_RUNTIME +# import "ObjFWRT.h" +# import "private.h" +#else +# import "OFObject.h" +# import "OFMapTable.h" +#endif struct Association { id object; objc_associationPolicy policy; }; + +#ifdef OF_OBJFW_RUNTIME +typedef struct objc_hashtable objc_hashtable; +#else +typedef OFMapTable objc_hashtable; +static const OFMapTableFunctions defaultFunctions = { NULL }; + +static objc_hashtable * +objc_hashtable_new(uint32_t (*hash)(const void *key), + bool (*equal)(const void *key1, const void *key2), uint32_t size) +{ + return [[OFMapTable alloc] initWithKeyFunctions: defaultFunctions + objectFunctions: defaultFunctions]; +} + +static void +objc_hashtable_set(objc_hashtable *hashtable, const void *key, + const void *object) +{ + return [hashtable setObject: (void *)object forKey: (void *)key]; +} + +static void * +objc_hashtable_get(objc_hashtable *hashtable, const void *key) +{ + return [hashtable objectForKey: (void *)key]; +} + +static void +objc_hashtable_delete(objc_hashtable *hashtable, const void *key) +{ + [hashtable removeObjectForKey: (void *)key]; +} + +static void +objc_hashtable_free(objc_hashtable *hashtable) +{ + [hashtable release]; +} + +# define OBJC_ERROR(...) abort() +#endif #ifdef OF_HAVE_THREADS # define numSlots 8 /* needs to be a power of 2 */ # import "OFPlainMutex.h" static OFSpinlock spinlocks[numSlots]; #else # define numSlots 1 #endif -static struct objc_hashtable *hashtables[numSlots]; +static objc_hashtable *hashtables[numSlots]; static OF_INLINE size_t slotForObject(id object) { return ((size_t)((uintptr_t)object >> 4) & (numSlots - 1)); @@ -89,11 +136,11 @@ if (OFSpinlockLock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to lock spinlock!"); @try { #endif - struct objc_hashtable *objectHashtable; + objc_hashtable *objectHashtable; struct Association *association; objectHashtable = objc_hashtable_get(hashtables[slot], object); if (objectHashtable == NULL) { objectHashtable = objc_hashtable_new(hash, equal, 2); @@ -133,18 +180,19 @@ id objc_getAssociatedObject(id object, const void *key) { size_t slot = slotForObject(object); + id ret; #ifdef OF_HAVE_THREADS if (OFSpinlockLock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to lock spinlock!"); @try { #endif - struct objc_hashtable *objectHashtable; + objc_hashtable *objectHashtable; struct Association *association; objectHashtable = objc_hashtable_get(hashtables[slot], object); if (objectHashtable == NULL) return nil; @@ -154,20 +202,24 @@ return nil; switch (association->policy) { case OBJC_ASSOCIATION_RETAIN: case OBJC_ASSOCIATION_COPY: - return [[association->object retain] autorelease]; + ret = [[association->object retain] autorelease]; + break; default: - return association->object; + ret = association->object; + break; } #ifdef OF_HAVE_THREADS } @finally { if (OFSpinlockUnlock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to unlock spinlock!"); } #endif + + return ret; } void objc_removeAssociatedObjects(id object) { @@ -177,25 +229,33 @@ if (OFSpinlockLock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to lock spinlock!"); @try { #endif - struct objc_hashtable *objectHashtable; + objc_hashtable *objectHashtable; objectHashtable = objc_hashtable_get(hashtables[slot], object); if (objectHashtable == NULL) return; +#ifdef OF_OBJFW_RUNTIME for (uint32_t i = 0; i < objectHashtable->size; i++) { struct Association *association; if (objectHashtable->data[i] == NULL || objectHashtable->data[i] == &objc_deletedBucket) continue; association = (struct Association *) objectHashtable->data[i]->object; +#else + OFMapTableEnumerator *enumerator = + [objectHashtable objectEnumerator]; + void **iter; + while ((iter = [enumerator nextObject]) != NULL) { + struct Association *association = *iter; +#endif switch (association->policy) { case OBJC_ASSOCIATION_RETAIN: case OBJC_ASSOCIATION_RETAIN_NONATOMIC: case OBJC_ASSOCIATION_COPY: Index: src/runtime/instance.m ================================================================== --- src/runtime/instance.m +++ src/runtime/instance.m @@ -21,10 +21,14 @@ # import "ObjFWRT.h" # import "private.h" #else # import #endif + +#ifndef OF_OBJFW_RUNTIME +extern void objc_removeAssociatedObjects(id object); +#endif static SEL constructSelector = NULL; static SEL destructSelector = NULL; static bool @@ -100,11 +104,9 @@ last = destruct; } else break; } -#if defined(OF_OBJFW_RUNTIME) || defined(HAVE_OBJC_SETASSOCIATEDOBJECT) objc_removeAssociatedObjects(object); -#endif return object; } Index: tests/RuntimeTests.m ================================================================== --- tests/RuntimeTests.m +++ tests/RuntimeTests.m @@ -84,11 +84,10 @@ _test.bar = string; OTAssertEqual(_test.bar, string); OTAssertEqual(string.retainCount, 3); } -#if defined(OF_OBJFW_RUNTIME) || defined(HAVE_OBJC_SETASSOCIATEDOBJECT) - (void)testAssociatedObjects { objc_setAssociatedObject(self, testKey, _test, OBJC_ASSOCIATION_ASSIGN); OTAssertEqual(_test.retainCount, 1); @@ -109,11 +108,10 @@ OTAssertEqual(_test.retainCount, 3); objc_removeAssociatedObjects(self); OTAssertEqual(_test.retainCount, 2); } -#endif #ifdef OF_OBJFW_RUNTIME - (void)testTaggedPointers { int classID;