]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-references.mm
objc4-723.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 #if _LIBCPP_VERSION
33 # include <unordered_map>
34 #else
35 # include <tr1/unordered_map>
36 using namespace tr1;
37 #endif
38
39
40 // wrap all the murky C++ details in a namespace to get them out of the way.
41
42 namespace objc_references_support {
43 struct DisguisedPointerEqual {
44 bool operator()(uintptr_t p1, uintptr_t p2) const {
45 return p1 == p2;
46 }
47 };
48
49 struct DisguisedPointerHash {
50 uintptr_t operator()(uintptr_t k) const {
51 // borrowed from CFSet.c
52 #if __LP64__
53 uintptr_t a = 0x4368726973746F70ULL;
54 uintptr_t b = 0x686572204B616E65ULL;
55 #else
56 uintptr_t a = 0x4B616E65UL;
57 uintptr_t b = 0x4B616E65UL;
58 #endif
59 uintptr_t c = 1;
60 a += k;
61 #if __LP64__
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);
74 #else
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);
84 #endif
85 return c;
86 }
87 };
88
89 struct ObjectPointerLess {
90 bool operator()(const void *p1, const void *p2) const {
91 return p1 < p2;
92 }
93 };
94
95 struct ObjcPointerHash {
96 uintptr_t operator()(void *p) const {
97 return DisguisedPointerHash()(uintptr_t(p));
98 }
99 };
100
101 // STL allocator that uses the runtime's internal allocator.
102
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;
111
112 template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
113
114 template <typename U> ObjcAllocator(const ObjcAllocator<U>&) {}
115 ObjcAllocator() {}
116 ObjcAllocator(const ObjcAllocator&) {}
117 ~ObjcAllocator() {}
118
119 pointer address(reference x) const { return &x; }
120 const_pointer address(const_reference x) const {
121 return x;
122 }
123
124 pointer allocate(size_type n, const_pointer = 0) {
125 return static_cast<pointer>(::malloc(n * sizeof(T)));
126 }
127
128 void deallocate(pointer p, size_type) { ::free(p); }
129
130 size_type max_size() const {
131 return static_cast<size_type>(-1) / sizeof(T);
132 }
133
134 void construct(pointer p, const value_type& x) {
135 new(p) value_type(x);
136 }
137
138 void destroy(pointer p) { p->~value_type(); }
139
140 void operator=(const ObjcAllocator&);
141
142 };
143
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; };
149 };
150
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); }
154
155 class ObjcAssociation {
156 uintptr_t _policy;
157 id _value;
158 public:
159 ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
160 ObjcAssociation() : _policy(0), _value(nil) {}
161
162 uintptr_t policy() const { return _policy; }
163 id value() const { return _value; }
164
165 bool hasValue() { return _value != nil; }
166 };
167
168 #if TARGET_OS_WIN32
169 typedef hash_map<void *, ObjcAssociation> ObjectAssociationMap;
170 typedef hash_map<disguised_ptr_t, ObjectAssociationMap *> AssociationsHashMap;
171 #else
172 typedef ObjcAllocator<std::pair<void * const, ObjcAssociation> > ObjectAssociationMapAllocator;
173 class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
174 public:
175 void *operator new(size_t n) { return ::malloc(n); }
176 void operator delete(void *ptr) { ::free(ptr); }
177 };
178 typedef ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap*> > AssociationsHashMapAllocator;
179 class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
180 public:
181 void *operator new(size_t n) { return ::malloc(n); }
182 void operator delete(void *ptr) { ::free(ptr); }
183 };
184 #endif
185 }
186
187 using namespace objc_references_support;
188
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.
192
193 spinlock_t AssociationsManagerLock;
194
195 class AssociationsManager {
196 // associative references: object pointer -> PtrPtrHashMap.
197 static AssociationsHashMap *_map;
198 public:
199 AssociationsManager() { AssociationsManagerLock.lock(); }
200 ~AssociationsManager() { AssociationsManagerLock.unlock(); }
201
202 AssociationsHashMap &associations() {
203 if (_map == NULL)
204 _map = new AssociationsHashMap();
205 return *_map;
206 }
207 };
208
209 AssociationsHashMap *AssociationsManager::_map = NULL;
210
211 // expanded policy bits.
212
213 enum {
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)
220 };
221
222 id _object_get_associative_reference(id object, void *key) {
223 id value = nil;
224 uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
225 {
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) {
238 objc_retain(value);
239 }
240 }
241 }
242 }
243 if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
244 objc_autorelease(value);
245 }
246 return value;
247 }
248
249 static id acquireValue(id value, uintptr_t policy) {
250 switch (policy & 0xFF) {
251 case OBJC_ASSOCIATION_SETTER_RETAIN:
252 return objc_retain(value);
253 case OBJC_ASSOCIATION_SETTER_COPY:
254 return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
255 }
256 return value;
257 }
258
259 static void releaseValue(id value, uintptr_t policy) {
260 if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
261 return objc_release(value);
262 }
263 }
264
265 struct ReleaseValue {
266 void operator() (ObjcAssociation &association) {
267 releaseValue(association.value(), association.policy());
268 }
269 };
270
271 void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
272 // retain the new value (if any) outside the lock.
273 ObjcAssociation old_association(0, nil);
274 id new_value = value ? acquireValue(value, policy) : nil;
275 {
276 AssociationsManager manager;
277 AssociationsHashMap &associations(manager.associations());
278 disguised_ptr_t disguised_object = DISGUISE(object);
279 if (new_value) {
280 // break any existing association.
281 AssociationsHashMap::iterator i = associations.find(disguised_object);
282 if (i != associations.end()) {
283 // secondary table exists
284 ObjectAssociationMap *refs = i->second;
285 ObjectAssociationMap::iterator j = refs->find(key);
286 if (j != refs->end()) {
287 old_association = j->second;
288 j->second = ObjcAssociation(policy, new_value);
289 } else {
290 (*refs)[key] = ObjcAssociation(policy, new_value);
291 }
292 } else {
293 // create the new association (first time).
294 ObjectAssociationMap *refs = new ObjectAssociationMap;
295 associations[disguised_object] = refs;
296 (*refs)[key] = ObjcAssociation(policy, new_value);
297 object->setHasAssociatedObjects();
298 }
299 } else {
300 // setting the association to nil breaks the association.
301 AssociationsHashMap::iterator i = associations.find(disguised_object);
302 if (i != associations.end()) {
303 ObjectAssociationMap *refs = i->second;
304 ObjectAssociationMap::iterator j = refs->find(key);
305 if (j != refs->end()) {
306 old_association = j->second;
307 refs->erase(j);
308 }
309 }
310 }
311 }
312 // release the old value (outside of the lock).
313 if (old_association.hasValue()) ReleaseValue()(old_association);
314 }
315
316 void _object_remove_assocations(id object) {
317 vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
318 {
319 AssociationsManager manager;
320 AssociationsHashMap &associations(manager.associations());
321 if (associations.size() == 0) return;
322 disguised_ptr_t disguised_object = DISGUISE(object);
323 AssociationsHashMap::iterator i = associations.find(disguised_object);
324 if (i != associations.end()) {
325 // copy all of the associations that need to be removed.
326 ObjectAssociationMap *refs = i->second;
327 for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
328 elements.push_back(j->second);
329 }
330 // remove the secondary table.
331 delete refs;
332 associations.erase(i);
333 }
334 }
335 // the calls to releaseValue() happen outside of the lock.
336 for_each(elements.begin(), elements.end(), ReleaseValue());
337 }