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 # include <unordered_map>
35 # include <tr1/unordered_map>
40 // wrap all the murky C++ details in a namespace to get them out of the way.
42 namespace objc_references_support {
43 struct DisguisedPointerEqual {
44 bool operator()(uintptr_t p1, uintptr_t p2) const {
49 struct DisguisedPointerHash {
50 uintptr_t operator()(uintptr_t k) const {
51 // borrowed from CFSet.c
53 uintptr_t a = 0x4368726973746F70ULL;
54 uintptr_t b = 0x686572204B616E65ULL;
56 uintptr_t a = 0x4B616E65UL;
57 uintptr_t b = 0x4B616E65UL;
62 a -= b; a -= c; a ^= (c >> 43);
63 b -= c; b -= a; b ^= (a << 9);
64 c -= a; c -= b; c ^= (b >> 8);
65 a -= b; a -= c; a ^= (c >> 38);
66 b -= c; b -= a; b ^= (a << 23);
67 c -= a; c -= b; c ^= (b >> 5);
68 a -= b; a -= c; a ^= (c >> 35);
69 b -= c; b -= a; b ^= (a << 49);
70 c -= a; c -= b; c ^= (b >> 11);
71 a -= b; a -= c; a ^= (c >> 12);
72 b -= c; b -= a; b ^= (a << 18);
73 c -= a; c -= b; c ^= (b >> 22);
75 a -= b; a -= c; a ^= (c >> 13);
76 b -= c; b -= a; b ^= (a << 8);
77 c -= a; c -= b; c ^= (b >> 13);
78 a -= b; a -= c; a ^= (c >> 12);
79 b -= c; b -= a; b ^= (a << 16);
80 c -= a; c -= b; c ^= (b >> 5);
81 a -= b; a -= c; a ^= (c >> 3);
82 b -= c; b -= a; b ^= (a << 10);
83 c -= a; c -= b; c ^= (b >> 15);
89 struct ObjectPointerLess {
90 bool operator()(const void *p1, const void *p2) const {
95 struct ObjcPointerHash {
96 uintptr_t operator()(void *p) const {
97 return DisguisedPointerHash()(uintptr_t(p));
101 // STL allocator that uses the runtime's internal allocator.
103 template <typename T> struct ObjcAllocator {
104 typedef T value_type;
105 typedef value_type* pointer;
106 typedef const value_type *const_pointer;
107 typedef value_type& reference;
108 typedef const value_type& const_reference;
109 typedef size_t size_type;
110 typedef ptrdiff_t difference_type;
112 template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
114 template <typename U> ObjcAllocator(const ObjcAllocator<U>&) {}
116 ObjcAllocator(const ObjcAllocator&) {}
119 pointer address(reference x) const { return &x; }
120 const_pointer address(const_reference x) const {
124 pointer allocate(size_type n, const_pointer = 0) {
125 return static_cast<pointer>(::malloc(n * sizeof(T)));
128 void deallocate(pointer p, size_type) { ::free(p); }
130 size_type max_size() const {
131 return static_cast<size_type>(-1) / sizeof(T);
134 void construct(pointer p, const value_type& x) {
135 new(p) value_type(x);
138 void destroy(pointer p) { p->~value_type(); }
140 void operator=(const ObjcAllocator&);
144 template<> struct ObjcAllocator<void> {
145 typedef void value_type;
146 typedef void* pointer;
147 typedef const void *const_pointer;
148 template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
151 typedef uintptr_t disguised_ptr_t;
152 inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
153 inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); }
155 class ObjcAssociation {
159 ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
160 ObjcAssociation() : _policy(0), _value(nil) {}
162 uintptr_t policy() const { return _policy; }
163 id value() const { return _value; }
165 bool hasValue() { return _value != nil; }
169 typedef hash_map<void *, ObjcAssociation> ObjectAssociationMap;
170 typedef hash_map<disguised_ptr_t, ObjectAssociationMap *> AssociationsHashMap;
172 typedef ObjcAllocator<std::pair<void * const, ObjcAssociation> > ObjectAssociationMapAllocator;
173 class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
175 void *operator new(size_t n) { return ::malloc(n); }
176 void operator delete(void *ptr) { ::free(ptr); }
178 typedef ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap*> > AssociationsHashMapAllocator;
179 class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
181 void *operator new(size_t n) { return ::malloc(n); }
182 void operator delete(void *ptr) { ::free(ptr); }
187 using namespace objc_references_support;
189 // class AssociationsManager manages a lock / hash table singleton pair.
190 // Allocating an instance acquires the lock, and calling its assocations()
191 // method lazily allocates the hash table.
193 spinlock_t AssociationsManagerLock;
195 class AssociationsManager {
196 // associative references: object pointer -> PtrPtrHashMap.
197 static AssociationsHashMap *_map;
199 AssociationsManager() { AssociationsManagerLock.lock(); }
200 ~AssociationsManager() { AssociationsManagerLock.unlock(); }
202 AssociationsHashMap &associations() {
204 _map = new AssociationsHashMap();
209 AssociationsHashMap *AssociationsManager::_map = NULL;
211 // expanded policy bits.
214 OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
215 OBJC_ASSOCIATION_SETTER_RETAIN = 1,
216 OBJC_ASSOCIATION_SETTER_COPY = 3, // NOTE: both bits are set, so we can simply test 1 bit in releaseValue below.
217 OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
218 OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
219 OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
222 id _object_get_associative_reference(id object, void *key) {
224 uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
226 AssociationsManager manager;
227 AssociationsHashMap &associations(manager.associations());
228 disguised_ptr_t disguised_object = DISGUISE(object);
229 AssociationsHashMap::iterator i = associations.find(disguised_object);
230 if (i != associations.end()) {
231 ObjectAssociationMap *refs = i->second;
232 ObjectAssociationMap::iterator j = refs->find(key);
233 if (j != refs->end()) {
234 ObjcAssociation &entry = j->second;
235 value = entry.value();
236 policy = entry.policy();
237 if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
241 if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
242 ((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
247 static id acquireValue(id value, uintptr_t policy) {
248 switch (policy & 0xFF) {
249 case OBJC_ASSOCIATION_SETTER_RETAIN:
250 return ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
251 case OBJC_ASSOCIATION_SETTER_COPY:
252 return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
257 static void releaseValue(id value, uintptr_t policy) {
258 if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
259 ((id(*)(id, SEL))objc_msgSend)(value, SEL_release);
263 struct ReleaseValue {
264 void operator() (ObjcAssociation &association) {
265 releaseValue(association.value(), association.policy());
269 void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
270 // retain the new value (if any) outside the lock.
271 ObjcAssociation old_association(0, nil);
272 id new_value = value ? acquireValue(value, policy) : nil;
274 AssociationsManager manager;
275 AssociationsHashMap &associations(manager.associations());
276 disguised_ptr_t disguised_object = DISGUISE(object);
278 // break any existing association.
279 AssociationsHashMap::iterator i = associations.find(disguised_object);
280 if (i != associations.end()) {
281 // secondary table exists
282 ObjectAssociationMap *refs = i->second;
283 ObjectAssociationMap::iterator j = refs->find(key);
284 if (j != refs->end()) {
285 old_association = j->second;
286 j->second = ObjcAssociation(policy, new_value);
288 (*refs)[key] = ObjcAssociation(policy, new_value);
291 // create the new association (first time).
292 ObjectAssociationMap *refs = new ObjectAssociationMap;
293 associations[disguised_object] = refs;
294 (*refs)[key] = ObjcAssociation(policy, new_value);
295 object->setHasAssociatedObjects();
298 // setting the association to nil breaks the association.
299 AssociationsHashMap::iterator i = associations.find(disguised_object);
300 if (i != associations.end()) {
301 ObjectAssociationMap *refs = i->second;
302 ObjectAssociationMap::iterator j = refs->find(key);
303 if (j != refs->end()) {
304 old_association = j->second;
310 // release the old value (outside of the lock).
311 if (old_association.hasValue()) ReleaseValue()(old_association);
314 void _object_remove_assocations(id object) {
315 vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
317 AssociationsManager manager;
318 AssociationsHashMap &associations(manager.associations());
319 if (associations.size() == 0) return;
320 disguised_ptr_t disguised_object = DISGUISE(object);
321 AssociationsHashMap::iterator i = associations.find(disguised_object);
322 if (i != associations.end()) {
323 // copy all of the associations that need to be removed.
324 ObjectAssociationMap *refs = i->second;
325 for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
326 elements.push_back(j->second);
328 // remove the secondary table.
330 associations.erase(i);
333 // the calls to releaseValue() happen outside of the lock.
334 for_each(elements.begin(), elements.end(), ReleaseValue());