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>
32 // wrap all the murky C++ details in a namespace to get them out of the way.
34 namespace objc_references_support {
35 struct ObjcPointerEqual {
36 bool operator()(void *p1, void *p2) const {
41 struct ObjcPointerClear {
42 void operator() (void *pointer) {
43 void **location = (void **)pointer;
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) { }
147 // typedef vector<void *, ObjcAllocator<void *> > PtrVector;
148 // typedef hash_set<void *, ObjcPointerHash, ObjcPointerEqual, ObjcAllocator<void *> > PtrHashSet;
149 // typedef hash_map<void *, void *, ObjcPointerHash, ObjcPointerEqual, ObjcAllocator<void *> > PtrPtrHashMap;
151 typedef hash_map<void *, ObjcAssociation> ObjectAssocationHashMap;
152 typedef hash_map<void *, ObjectAssocationHashMap> AssocationsHashMap;
154 typedef hash_map<void *, ObjcAssociation, ObjcPointerHash, ObjcPointerEqual, ObjcAllocator<void *> > ObjectAssocationHashMap;
155 typedef hash_map<void *, ObjectAssocationHashMap, ObjcPointerHash, ObjcPointerEqual, ObjcAllocator<void *> > AssocationsHashMap;
159 using namespace objc_references_support;
161 // class AssociationsManager manages a lock / hash table singleton pair.
162 // Allocating an instance acquires the lock, and calling its assocations() method
163 // lazily allocates it.
165 class AssociationsManager {
166 static OSSpinLock _lock;
167 static AssocationsHashMap *_map; // associative references: object pointer -> PtrPtrHashMap.
169 AssociationsManager() { OSSpinLockLock(&_lock); }
170 ~AssociationsManager() { OSSpinLockUnlock(&_lock); }
172 AssocationsHashMap &associations() {
174 _map = new(::_malloc_internal(sizeof(AssocationsHashMap))) AssocationsHashMap();
179 OSSpinLock AssociationsManager::_lock = OS_SPINLOCK_INIT;
180 AssocationsHashMap *AssociationsManager::_map = NULL;
182 // expanded policy bits.
185 OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
186 OBJC_ASSOCIATION_SETTER_RETAIN = 1,
187 OBJC_ASSOCIATION_SETTER_COPY = 3, // NOTE: both bits are set, so we can simply test 1 bit in releaseValue below.
188 OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
189 OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
190 OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
193 __private_extern__ id _object_get_associative_reference(id object, void *key) {
195 uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
197 AssociationsManager manager;
198 AssocationsHashMap &associations(manager.associations());
199 AssocationsHashMap::iterator i = associations.find(object);
200 if (i != associations.end()) {
201 ObjectAssocationHashMap &refs = i->second;
202 ObjectAssocationHashMap::iterator j = refs.find(key);
203 if (j != refs.end()) {
204 ObjcAssociation &entry = j->second;
205 value = (id)entry.value;
206 policy = entry.policy;
207 if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) objc_msgSend(value, SEL_retain);
211 if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
212 objc_msgSend(value, SEL_autorelease);
217 static id acquireValue(id value, uintptr_t policy) {
218 switch (policy & 0xFF) {
219 case OBJC_ASSOCIATION_SETTER_RETAIN:
220 return objc_msgSend(value, SEL_retain);
221 case OBJC_ASSOCIATION_SETTER_COPY:
222 return objc_msgSend(value, SEL_copy);
227 static void releaseValue(id value, uintptr_t policy) {
228 if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
229 objc_msgSend(value, SEL_release);
233 struct ReleaseValue {
234 void operator() (ObjcAssociation &association) {
235 releaseValue(association.value, association.policy);
239 __private_extern__ void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
240 // retain the new value (if any) outside the lock.
241 uintptr_t old_policy = 0; // NOTE: old_policy is always assigned to when old_value is non-nil.
242 id new_value = value ? acquireValue(value, policy) : nil, old_value = nil;
244 AssociationsManager manager;
245 AssocationsHashMap &associations(manager.associations());
247 // break any existing association.
248 AssocationsHashMap::iterator i = associations.find(object);
249 if (i != associations.end()) {
250 // secondary table exists
251 ObjectAssocationHashMap &refs = i->second;
252 ObjectAssocationHashMap::iterator j = refs.find(key);
253 if (j != refs.end()) {
254 ObjcAssociation &old_entry = j->second;
255 old_policy = old_entry.policy;
256 old_value = old_entry.value;
257 old_entry.policy = policy;
258 old_entry.value = new_value;
260 refs[key] = ObjcAssociation(policy, new_value);
263 // create the new association (first time).
264 associations[object][key] = ObjcAssociation(policy, new_value);
265 _class_assertInstancesHaveAssociatedObjects(object->isa);
268 // setting the association to nil breaks the association.
269 AssocationsHashMap::iterator i = associations.find(object);
270 if (i != associations.end()) {
271 ObjectAssocationHashMap &refs = i->second;
272 ObjectAssocationHashMap::iterator j = refs.find(key);
273 if (j != refs.end()) {
274 ObjcAssociation &old_entry = j->second;
275 old_policy = old_entry.policy;
276 old_value = (id) old_entry.value;
282 // release the old value (outside of the lock).
283 if (old_value) releaseValue(old_value, old_policy);
286 __private_extern__ void _object_remove_assocations(id object) {
287 vector<ObjcAssociation> elements;
289 AssociationsManager manager;
290 AssocationsHashMap &associations(manager.associations());
291 if (associations.size() == 0) return;
292 AssocationsHashMap::iterator i = associations.find(object);
293 if (i != associations.end()) {
294 // copy all of the associations that need to be removed.
295 ObjectAssocationHashMap &refs = i->second;
296 for (ObjectAssocationHashMap::iterator j = refs.begin(); j != refs.end(); ++j) {
297 elements.push_back(j->second);
299 // remove the secondary table.
300 associations.erase(i);
303 // the calls to releaseValue() happen outside of the lock.
304 for_each(elements.begin(), elements.end(), ReleaseValue());