Overview
Comment: | runtime: Add support for associated objects |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
18ae339de1648d257408143f2607715d |
User & Date: | js on 2024-02-03 14:41:14 |
Other Links: | manifest | tags |
References
2024-02-03
| ||
14:43 | • Fixed ticket [4b06796f49]: runtime: Add objc_setAssociatedObject() plus 5 other changes artifact: 9884ff5a3c user: js | |
Context
2024-02-03
| ||
15:50 | objc_destructInstance: Remove associated objects check-in: 5f4d70c69f user: js tags: trunk | |
14:41 | runtime: Add support for associated objects check-in: 18ae339de1 user: js tags: trunk | |
11:37 | Minor documentation improvement check-in: e03c6b5d9d user: js tags: trunk | |
Changes
Modified src/runtime/Makefile from [af674fab60] to [692471a52e].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | include ../../extra.mk SUBDIRS = lookup-asm DISTCLEAN = Info.plist SHARED_LIB = ${OBJFWRT_SHARED_LIB} STATIC_LIB = ${OBJFWRT_STATIC_LIB} FRAMEWORK = ${OBJFWRT_FRAMEWORK} LIB_MAJOR = ${OBJFWRT_LIB_MAJOR} LIB_MINOR = ${OBJFWRT_LIB_MINOR} LIB_PATCH = ${OBJFWRT_LIB_PATCH} SRCS = arc.m \ autorelease.m \ category.m \ class.m \ dtable.m \ exception.m \ hashtable.m \ init.m \ | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | include ../../extra.mk SUBDIRS = lookup-asm DISTCLEAN = Info.plist SHARED_LIB = ${OBJFWRT_SHARED_LIB} STATIC_LIB = ${OBJFWRT_STATIC_LIB} FRAMEWORK = ${OBJFWRT_FRAMEWORK} LIB_MAJOR = ${OBJFWRT_LIB_MAJOR} LIB_MINOR = ${OBJFWRT_LIB_MINOR} LIB_PATCH = ${OBJFWRT_LIB_PATCH} SRCS = arc.m \ association.m \ autorelease.m \ category.m \ class.m \ dtable.m \ exception.m \ hashtable.m \ init.m \ |
︙ | ︙ |
Modified src/runtime/ObjFWRT.h from [5258b334d9] to [8884ec4c85].
︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 178 179 180 181 182 | #ifdef __cplusplus Class _Nonnull class_; #else Class _Nonnull class; #endif }; #ifdef __cplusplus extern "C" { #endif /** * @brief Registers a selector with the specified name with the runtime. * | > > > > > > > > > > > > > > > > | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | #ifdef __cplusplus Class _Nonnull class_; #else Class _Nonnull class; #endif }; /** * @brief A policy for object association, see @ref objc_setAssociatedObject. */ typedef enum objc_associationPolicy { /** @brief Associate the object like an assigned property. */ OBJC_ASSOCIATION_ASSIGN = 0, /** @brief Associate the object like a retained, nonatomic property. */ OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /** @brief Associate the object like a retained property. */ OBJC_ASSOCIATION_RETAIN = OBJC_ASSOCIATION_RETAIN_NONATOMIC | 0x300, /** @brief Associate the object like a copied, nonatomic property. */ OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /** @brief Associate the object like a copied property. */ OBJC_ASSOCIATION_COPY = OBJC_ASSOCIATION_COPY_NONATOMIC | 0x300 } objc_associationPolicy; #ifdef __cplusplus extern "C" { #endif /** * @brief Registers a selector with the specified name with the runtime. * |
︙ | ︙ | |||
642 643 644 645 646 647 648 649 650 651 652 653 654 655 | * * @param class_ The tag ID for the tagged pointer class to use * @param value The value the tagged pointer should have * @return A tagged pointer, or `nil` if it could not be created */ extern id _Nullable objc_createTaggedPointer(int class_, uintptr_t value); /* * Used by the compiler, but can also be called manually. * * These declarations are also required to prevent Clang's implicit * declarations which include __declspec(dllimport) on Windows. */ struct objc_module; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 | * * @param class_ The tag ID for the tagged pointer class to use * @param value The value the tagged pointer should have * @return A tagged pointer, or `nil` if it could not be created */ extern id _Nullable objc_createTaggedPointer(int class_, uintptr_t value); /** * @brief Sets an associated object on the specified object for the specified * key. * * @param object The object on which to set an associated object * @param key A unique pointer to use as the key for the association * @param value The object to associate with the specified object * @param policy The association policy, see @ref objc_associationPolicy */ extern void objc_setAssociatedObject(id _Nonnull object, const void *_Nonnull key, id _Nullable value, objc_associationPolicy policy); /** * @brief Returns the associated object on the specified object for the * specified key. * * @param object The object on which to get the associated object * @param key The key of the association * @return The associated object on the specified object for the specified key */ extern id _Nullable objc_getAssociatedObject(id _Nonnull object, const void *_Nonnull key); /** * @brief Removes all associated objects for the specified object. * * @param object The object on which to remove all associated objects */ extern void objc_removeAssociatedObjects(id _Nonnull object); /* * Used by the compiler, but can also be called manually. * * These declarations are also required to prevent Clang's implicit * declarations which include __declspec(dllimport) on Windows. */ struct objc_module; |
︙ | ︙ |
Added src/runtime/association.m version [b558229b1e].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | /* * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im> * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in * the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" #import "ObjFWRT.h" #import "private.h" #ifdef OF_HAVE_THREADS # import "OFPlainMutex.h" # define numSpinlocks 8 /* needs to be a power of 2 */ static OFSpinlock spinlocks[numSpinlocks]; static OF_INLINE size_t spinlockSlot(id object) { return ((size_t)((uintptr_t)object >> 4) & (numSpinlocks - 1)); } #endif struct Association { id object; objc_associationPolicy policy; }; static struct objc_hashtable *hashtable; static uint32_t hash(const void *object) { return (uint32_t)(uintptr_t)object; } static bool equal(const void *object1, const void *object2) { return (object1 == object2); } OF_CONSTRUCTOR() { hashtable = objc_hashtable_new(hash, equal, 2); #ifdef OF_HAVE_THREADS for (size_t i = 0; i < numSpinlocks; i++) if (OFSpinlockNew(&spinlocks[i]) != 0) OBJC_ERROR("Failed to create spinlocks!"); #endif } void objc_setAssociatedObject(id object, const void *key, id value, objc_associationPolicy policy) { #ifdef OF_HAVE_THREADS size_t slot; #endif switch (policy) { case OBJC_ASSOCIATION_ASSIGN: break; case OBJC_ASSOCIATION_RETAIN: case OBJC_ASSOCIATION_RETAIN_NONATOMIC: value = [value retain]; break; case OBJC_ASSOCIATION_COPY: case OBJC_ASSOCIATION_COPY_NONATOMIC: value = [value copy]; break; default: /* Don't know what to do, so do nothing. */ return; } #ifdef OF_HAVE_THREADS slot = spinlockSlot(object); if (OFSpinlockLock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to lock spinlock!"); @try { #endif struct objc_hashtable *objectHashtable; struct Association *association; objectHashtable = objc_hashtable_get(hashtable, object); if (objectHashtable == NULL) { objectHashtable = objc_hashtable_new(hash, equal, 2); objc_hashtable_set(hashtable, object, objectHashtable); } association = objc_hashtable_get(objectHashtable, key); if (association != NULL) { switch (association->policy) { case OBJC_ASSOCIATION_RETAIN: case OBJC_ASSOCIATION_RETAIN_NONATOMIC: case OBJC_ASSOCIATION_COPY: case OBJC_ASSOCIATION_COPY_NONATOMIC: [association->object release]; break; default: break; } } else { association = malloc(sizeof(*association)); if (association == NULL) OBJC_ERROR("Failed to allocate association!"); objc_hashtable_set(objectHashtable, key, association); } association->policy = policy; association->object = value; #ifdef OF_HAVE_THREADS } @finally { if (OFSpinlockUnlock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to unlock spinlock!"); } #endif } id objc_getAssociatedObject(id object, const void *key) { #ifdef OF_HAVE_THREADS size_t slot = spinlockSlot(object); if (OFSpinlockLock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to lock spinlock!"); @try { #endif struct objc_hashtable *objectHashtable; struct Association *association; objectHashtable = objc_hashtable_get(hashtable, object); if (objectHashtable == NULL) return nil; association = objc_hashtable_get(objectHashtable, key); if (association == NULL) return nil; switch (association->policy) { case OBJC_ASSOCIATION_RETAIN: case OBJC_ASSOCIATION_COPY: return [[association->object retain] autorelease]; default: return association->object; } #ifdef OF_HAVE_THREADS } @finally { if (OFSpinlockUnlock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to unlock spinlock!"); } #endif } void objc_removeAssociatedObjects(id object) { #ifdef OF_HAVE_THREADS size_t slot = spinlockSlot(object); if (OFSpinlockLock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to lock spinlock!"); @try { #endif struct objc_hashtable *objectHashtable; objectHashtable = objc_hashtable_get(hashtable, object); if (objectHashtable == NULL) return; 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; switch (association->policy) { case OBJC_ASSOCIATION_RETAIN: case OBJC_ASSOCIATION_RETAIN_NONATOMIC: case OBJC_ASSOCIATION_COPY: case OBJC_ASSOCIATION_COPY_NONATOMIC: [association->object release]; break; default: break; } free(association); } objc_hashtable_delete(hashtable, object); #ifdef OF_HAVE_THREADS } @finally { if (OFSpinlockUnlock(&spinlocks[slot]) != 0) OBJC_ERROR("Failed to unlock spinlock!"); } #endif } |
Modified src/runtime/class.m from [99bd522eda] to [628f2b08c1].
︙ | ︙ | |||
599 600 601 602 603 604 605 | void *class; if (j >= count) { objc_globalMutex_unlock(); return j; } | | > | 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 | void *class; if (j >= count) { objc_globalMutex_unlock(); return j; } if (classes->data[i] == NULL || classes->data[i] == &objc_deletedBucket) continue; if (strcmp(classes->data[i]->key, "Protocol") == 0) continue; class = (Class)classes->data[i]->object; |
︙ | ︙ |
Modified src/runtime/property.m from [eac1d5ea2f] to [20ebfada38].
︙ | ︙ | |||
26 27 28 29 30 31 32 | static OFSpinlock spinlocks[numSpinlocks]; static OF_INLINE size_t spinlockSlot(const void *ptr) { return ((size_t)((uintptr_t)ptr >> 4) & (numSpinlocks - 1)); } | < < | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | static OFSpinlock spinlocks[numSpinlocks]; static OF_INLINE size_t spinlockSlot(const void *ptr) { return ((size_t)((uintptr_t)ptr >> 4) & (numSpinlocks - 1)); } OF_CONSTRUCTOR() { for (size_t i = 0; i < numSpinlocks; i++) if (OFSpinlockNew(&spinlocks[i]) != 0) OBJC_ERROR("Failed to create spinlocks!"); } #endif |
︙ | ︙ |
Modified tests/RuntimeTests.m from [38c7914698] to [99fca0e28e].
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | */ #include "config.h" #import "TestsAppDelegate.h" static OFString *const module = @"Runtime"; @interface OFObject (SuperTest) - (id)superTest; @end @interface RuntimeTest: OFObject { | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | */ #include "config.h" #import "TestsAppDelegate.h" static OFString *const module = @"Runtime"; static void *testKey = &testKey; @interface OFObject (SuperTest) - (id)superTest; @end @interface RuntimeTest: OFObject { |
︙ | ︙ | |||
84 85 86 87 88 89 90 91 92 93 94 95 96 97 | TEST(@"copy, nonatomic properties", [test.foo isEqual: foo] && test.foo != foo && test.foo.retainCount == 1) test.bar = string; TEST(@"retain, atomic properties", test.bar == string && string.retainCount == 3) #ifdef OF_OBJFW_RUNTIME if (sizeof(uintptr_t) == 8) value = 0xDEADBEEFDEADBEF; else if (sizeof(uintptr_t) == 4) value = 0xDEADBEF; else abort(); | > > > > > > > > > > > > > > > | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | TEST(@"copy, nonatomic properties", [test.foo isEqual: foo] && test.foo != foo && test.foo.retainCount == 1) test.bar = string; TEST(@"retain, atomic properties", test.bar == string && string.retainCount == 3) TEST(@"Associated objects", R(objc_setAssociatedObject(self, testKey, test, OBJC_ASSOCIATION_ASSIGN)) && test.retainCount == 2 && R(objc_setAssociatedObject(self, testKey, test, OBJC_ASSOCIATION_RETAIN)) && test.retainCount == 3 && objc_getAssociatedObject(self, testKey) == test && test.retainCount == 4 && R(objc_setAssociatedObject(self, testKey, test, OBJC_ASSOCIATION_ASSIGN)) && test.retainCount == 3 && R(objc_setAssociatedObject(self, testKey, test, OBJC_ASSOCIATION_RETAIN_NONATOMIC)) && test.retainCount == 4 && objc_getAssociatedObject(self, testKey) == test && test.retainCount == 4 && R(objc_removeAssociatedObjects(self)) && test.retainCount == 3) #ifdef OF_OBJFW_RUNTIME if (sizeof(uintptr_t) == 8) value = 0xDEADBEEFDEADBEF; else if (sizeof(uintptr_t) == 4) value = 0xDEADBEF; else abort(); |
︙ | ︙ |