]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-references.mm
objc4-493.11.tar.gz
[apple/objc4.git] / runtime / objc-references.mm
1 /*
2 * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 Implementation of the weak / associative references for non-GC mode.
25 */
26
27
28 #include "objc-private.h"
29 #include <objc/message.h>
30 #include <map>
31
32
33 // wrap all the murky C++ details in a namespace to get them out of the way.
34
35 namespace objc_references_support {
36 struct ObjcPointerEqual {
37 bool operator()(void *p1, void *p2) const {
38 return p1 == p2;
39 }
40 };
41
42 struct ObjectPointerLess {
43 bool operator()(const void *p1, const void *p2) const {
44 return p1 < p2;
45 }
46 };
47
48 struct ObjcPointerHash {
49 uintptr_t operator()(void *p) const {
50 uintptr_t k = (uintptr_t)p;
51
52 // borrowed from CFSet.c
53 #if __LP64__
54 uintptr_t a = 0x4368726973746F70ULL;
55 uintptr_t b = 0x686572204B616E65ULL;
56 #else
57 uintptr_t a = 0x4B616E65UL;
58 uintptr_t b = 0x4B616E65UL;
59 #endif
60 uintptr_t c = 1;
61 a += k;
62 #if __LP64__
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);
75 #else
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);
85 #endif
86 return c;
87 }
88 };
89
90 // STL allocator that uses the runtime's internal allocator.
91
92 template <typename T> struct ObjcAllocator {
93 typedef T value_type;
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;
100
101 template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
102
103 template <typename U> ObjcAllocator(const ObjcAllocator<U>&) {}
104 ObjcAllocator() {}
105 ObjcAllocator(const ObjcAllocator&) {}
106 ~ObjcAllocator() {}
107
108 pointer address(reference x) const { return &x; }
109 const_pointer address(const_reference x) const {
110 return x;
111 }
112
113 pointer allocate(size_type n, const_pointer = 0) {
114 return static_cast<pointer>(::_malloc_internal(n * sizeof(T)));
115 }
116
117 void deallocate(pointer p, size_type) { ::_free_internal(p); }
118
119 size_type max_size() const {
120 return static_cast<size_type>(-1) / sizeof(T);
121 }
122
123 void construct(pointer p, const value_type& x) {
124 new(p) value_type(x);
125 }
126
127 void destroy(pointer p) { p->~value_type(); }
128
129 void operator=(const ObjcAllocator&);
130
131 };
132
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; };
138 };
139
140 struct ObjcAssociation {
141 uintptr_t policy;
142 id value;
143 ObjcAssociation(uintptr_t newPolicy, id newValue) : policy(newPolicy), value(newValue) { }
144 ObjcAssociation() : policy(0), value(0) { }
145 };
146
147 #if TARGET_OS_WIN32
148 typedef hash_map<void *, ObjcAssociation> ObjectAssociationMap;
149 typedef hash_map<void *, ObjectAssociationMap *> AssociationsHashMap;
150 #else
151 class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjcAllocator<std::pair<void * const, ObjcAssociation> > > {
152 public:
153 void *operator new(size_t n) { return ::_malloc_internal(n); }
154 void operator delete(void *ptr) { ::_free_internal(ptr); }
155 };
156 typedef hash_map<void *, ObjectAssociationMap *, ObjcPointerHash, ObjcPointerEqual, ObjcAllocator<void *> > AssociationsHashMap;
157 #endif
158 }
159
160 using namespace objc_references_support;
161
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.
165
166 class AssociationsManager {
167 static OSSpinLock _lock;
168 static AssociationsHashMap *_map; // associative references: object pointer -> PtrPtrHashMap.
169 public:
170 AssociationsManager() { OSSpinLockLock(&_lock); }
171 ~AssociationsManager() { OSSpinLockUnlock(&_lock); }
172
173 AssociationsHashMap &associations() {
174 if (_map == NULL)
175 _map = new(::_malloc_internal(sizeof(AssociationsHashMap))) AssociationsHashMap();
176 return *_map;
177 }
178 };
179
180 OSSpinLock AssociationsManager::_lock = OS_SPINLOCK_INIT;
181 AssociationsHashMap *AssociationsManager::_map = NULL;
182
183 // expanded policy bits.
184
185 enum {
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)
192 };
193
194 PRIVATE_EXTERN id _object_get_associative_reference(id object, void *key) {
195 id value = nil;
196 uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
197 {
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);
209 }
210 }
211 }
212 if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
213 objc_msgSend(value, SEL_autorelease);
214 }
215 return value;
216 }
217
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);
224 }
225 return value;
226 }
227
228 static void releaseValue(id value, uintptr_t policy) {
229 if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
230 objc_msgSend(value, SEL_release);
231 }
232 }
233
234 struct ReleaseValue {
235 void operator() (ObjcAssociation &association) {
236 releaseValue(association.value, association.policy);
237 }
238 };
239
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;
244 {
245 AssociationsManager manager;
246 AssociationsHashMap &associations(manager.associations());
247 if (new_value) {
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;
260 } else {
261 (*refs)[key] = ObjcAssociation(policy, new_value);
262 }
263 } else {
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));
269 }
270 } else {
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;
280 refs->erase(j);
281 }
282 }
283 }
284 }
285 // release the old value (outside of the lock).
286 if (old_value) releaseValue(old_value, old_policy);
287 }
288
289 PRIVATE_EXTERN void _object_remove_assocations(id object) {
290 vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
291 {
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);
301 }
302 // remove the secondary table.
303 delete refs;
304 associations.erase(i);
305 }
306 }
307 // the calls to releaseValue() happen outside of the lock.
308 for_each(elements.begin(), elements.end(), ReleaseValue());
309 }