2 * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 Implementation of the weak / associative references for non-GC mode.
28 #include "objc-private.h"
29 #include <objc/message.h>
33 // wrap all the murky C++ details in a namespace to get them out of the way.
35 namespace objc_references_support {
36 struct ObjcPointerEqual {
37 bool operator()(void *p1, void *p2) const {
42 struct ObjectPointerLess {
43 bool operator()(const void *p1, const void *p2) const {
48 struct ObjcPointerHash {
49 uintptr_t operator()(void *p) const {
50 uintptr_t k = (uintptr_t)p;
52 // borrowed from CFSet.c
54 uintptr_t a = 0x4368726973746F70ULL;
55 uintptr_t b = 0x686572204B616E65ULL;
57 uintptr_t a = 0x4B616E65UL;
58 uintptr_t b = 0x4B616E65UL;
63 a -= b; a -= c; a ^= (c >> 43);
64 b -= c; b -= a; b ^= (a << 9);
65 c -= a; c -= b; c ^= (b >> 8);
66 a -= b; a -= c; a ^= (c >> 38);
67 b -= c; b -= a; b ^= (a << 23);
68 c -= a; c -= b; c ^= (b >> 5);
69 a -= b; a -= c; a ^= (c >> 35);
70 b -= c; b -= a; b ^= (a << 49);
71 c -= a; c -= b; c ^= (b >> 11);
72 a -= b; a -= c; a ^= (c >> 12);
73 b -= c; b -= a; b ^= (a << 18);
74 c -= a; c -= b; c ^= (b >> 22);
76 a -= b; a -= c; a ^= (c >> 13);
77 b -= c; b -= a; b ^= (a << 8);
78 c -= a; c -= b; c ^= (b >> 13);
79 a -= b; a -= c; a ^= (c >> 12);
80 b -= c; b -= a; b ^= (a << 16);
81 c -= a; c -= b; c ^= (b >> 5);
82 a -= b; a -= c; a ^= (c >> 3);
83 b -= c; b -= a; b ^= (a << 10);
84 c -= a; c -= b; c ^= (b >> 15);
90 // STL allocator that uses the runtime's internal allocator.
92 template <typename T> struct ObjcAllocator {
94 typedef value_type* pointer;
95 typedef const value_type *const_pointer;
96 typedef value_type& reference;
97 typedef const value_type& const_reference;
98 typedef size_t size_type;
99 typedef ptrdiff_t difference_type;
101 template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
103 template <typename U> ObjcAllocator(const ObjcAllocator<U>&) {}
105 ObjcAllocator(const ObjcAllocator&) {}
108 pointer address(reference x) const { return &x; }
109 const_pointer address(const_reference x) const {
113 pointer allocate(size_type n, const_pointer = 0) {
114 return static_cast<pointer>(::_malloc_internal(n * sizeof(T)));
117 void deallocate(pointer p, size_type) { ::_free_internal(p); }
119 size_type max_size() const {
120 return static_cast<size_type>(-1) / sizeof(T);
123 void construct(pointer p, const value_type& x) {
124 new(p) value_type(x);
127 void destroy(pointer p) { p->~value_type(); }
129 void operator=(const ObjcAllocator&);
133 template<> struct ObjcAllocator<void> {
134 typedef void value_type;
135 typedef void* pointer;
136 typedef const void *const_pointer;
137 template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
140 struct ObjcAssociation {
143 ObjcAssociation(uintptr_t newPolicy, id newValue) : policy(newPolicy), value(newValue) { }
144 ObjcAssociation() : policy(0), value(0) { }
148 typedef hash_map<void *, ObjcAssociation> ObjectAssociationMap;
149 typedef hash_map<void *, ObjectAssociationMap *> AssociationsHashMap;
151 class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjcAllocator<std::pair<void * const, ObjcAssociation> > > {
153 void *operator new(size_t n) { return ::_malloc_internal(n); }
154 void operator delete(void *ptr) { ::_free_internal(ptr); }
156 typedef hash_map<void *, ObjectAssociationMap *, ObjcPointerHash, ObjcPointerEqual, ObjcAllocator<void *> > AssociationsHashMap;
160 using namespace objc_references_support;
162 // class AssociationsManager manages a lock / hash table singleton pair.
163 // Allocating an instance acquires the lock, and calling its assocations() method
164 // lazily allocates it.
166 class AssociationsManager {
167 static OSSpinLock _lock;
168 static AssociationsHashMap *_map; // associative references: object pointer -> PtrPtrHashMap.
170 AssociationsManager() { OSSpinLockLock(&_lock); }
171 ~AssociationsManager() { OSSpinLockUnlock(&_lock); }
173 AssociationsHashMap &associations() {
175 _map = new(::_malloc_internal(sizeof(AssociationsHashMap))) AssociationsHashMap();
180 OSSpinLock AssociationsManager::_lock = OS_SPINLOCK_INIT;
181 AssociationsHashMap *AssociationsManager::_map = NULL;
183 // expanded policy bits.
186 OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
187 OBJC_ASSOCIATION_SETTER_RETAIN = 1,
188 OBJC_ASSOCIATION_SETTER_COPY = 3, // NOTE: both bits are set, so we can simply test 1 bit in releaseValue below.
189 OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
190 OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
191 OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
194 PRIVATE_EXTERN id _object_get_associative_reference(id object, void *key) {
196 uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
198 AssociationsManager manager;
199 AssociationsHashMap &associations(manager.associations());
200 AssociationsHashMap::iterator i = associations.find(object);
201 if (i != associations.end()) {
202 ObjectAssociationMap *refs = i->second;
203 ObjectAssociationMap::iterator j = refs->find(key);
204 if (j != refs->end()) {
205 ObjcAssociation &entry = j->second;
206 value = (id)entry.value;
207 policy = entry.policy;
208 if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) objc_msgSend(value, SEL_retain);
212 if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
213 objc_msgSend(value, SEL_autorelease);
218 static id acquireValue(id value, uintptr_t policy) {
219 switch (policy & 0xFF) {
220 case OBJC_ASSOCIATION_SETTER_RETAIN:
221 return objc_msgSend(value, SEL_retain);
222 case OBJC_ASSOCIATION_SETTER_COPY:
223 return objc_msgSend(value, SEL_copy);
228 static void releaseValue(id value, uintptr_t policy) {
229 if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
230 objc_msgSend(value, SEL_release);
234 struct ReleaseValue {
235 void operator() (ObjcAssociation &association) {
236 releaseValue(association.value, association.policy);
240 PRIVATE_EXTERN void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
241 // retain the new value (if any) outside the lock.
242 uintptr_t old_policy = 0; // NOTE: old_policy is always assigned to when old_value is non-nil.
243 id new_value = value ? acquireValue(value, policy) : nil, old_value = nil;
245 AssociationsManager manager;
246 AssociationsHashMap &associations(manager.associations());
248 // break any existing association.
249 AssociationsHashMap::iterator i = associations.find(object);
250 if (i != associations.end()) {
251 // secondary table exists
252 ObjectAssociationMap *refs = i->second;
253 ObjectAssociationMap::iterator j = refs->find(key);
254 if (j != refs->end()) {
255 ObjcAssociation &old_entry = j->second;
256 old_policy = old_entry.policy;
257 old_value = old_entry.value;
258 old_entry.policy = policy;
259 old_entry.value = new_value;
261 (*refs)[key] = ObjcAssociation(policy, new_value);
264 // create the new association (first time).
265 ObjectAssociationMap *refs = new ObjectAssociationMap;
266 associations[object] = refs;
267 (*refs)[key] = ObjcAssociation(policy, new_value);
268 _class_setInstancesHaveAssociatedObjects(_object_getClass(object));
271 // setting the association to nil breaks the association.
272 AssociationsHashMap::iterator i = associations.find(object);
273 if (i != associations.end()) {
274 ObjectAssociationMap *refs = i->second;
275 ObjectAssociationMap::iterator j = refs->find(key);
276 if (j != refs->end()) {
277 ObjcAssociation &old_entry = j->second;
278 old_policy = old_entry.policy;
279 old_value = (id) old_entry.value;
285 // release the old value (outside of the lock).
286 if (old_value) releaseValue(old_value, old_policy);
289 PRIVATE_EXTERN void _object_remove_assocations(id object) {
290 vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
292 AssociationsManager manager;
293 AssociationsHashMap &associations(manager.associations());
294 if (associations.size() == 0) return;
295 AssociationsHashMap::iterator i = associations.find(object);
296 if (i != associations.end()) {
297 // copy all of the associations that need to be removed.
298 ObjectAssociationMap *refs = i->second;
299 for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
300 elements.push_back(j->second);
302 // remove the secondary table.
304 associations.erase(i);
307 // the calls to releaseValue() happen outside of the lock.
308 for_each(elements.begin(), elements.end(), ReleaseValue());