]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-references.mm
objc4-680.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() method
191 // lazily allocates it.
192
193 class AssociationsManager {
194 static spinlock_t _lock;
195 static AssociationsHashMap *_map; // associative references: object pointer -> PtrPtrHashMap.
196 public:
197 AssociationsManager() { _lock.lock(); }
198 ~AssociationsManager() { _lock.unlock(); }
199
200 AssociationsHashMap &associations() {
201 if (_map == NULL)
202 _map = new AssociationsHashMap();
203 return *_map;
204 }
205 };
206
207 spinlock_t AssociationsManager::_lock;
208 AssociationsHashMap *AssociationsManager::_map = NULL;
209
210 // expanded policy bits.
211
212 enum {
213 OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
214 OBJC_ASSOCIATION_SETTER_RETAIN = 1,
215 OBJC_ASSOCIATION_SETTER_COPY = 3, // NOTE: both bits are set, so we can simply test 1 bit in releaseValue below.
216 OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
217 OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
218 OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
219 };
220
221 id _object_get_associative_reference(id object, void *key) {
222 id value = nil;
223 uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
224 {
225 AssociationsManager manager;
226 AssociationsHashMap &associations(manager.associations());
227 disguised_ptr_t disguised_object = DISGUISE(object);
228 AssociationsHashMap::iterator i = associations.find(disguised_object);
229 if (i != associations.end()) {
230 ObjectAssociationMap *refs = i->second;
231 ObjectAssociationMap::iterator j = refs->find(key);
232 if (j != refs->end()) {
233 ObjcAssociation &entry = j->second;
234 value = entry.value();
235 policy = entry.policy();
236 if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
237 }
238 }
239 }
240 if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
241 ((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
242 }
243 return value;
244 }
245
246 static id acquireValue(id value, uintptr_t policy) {
247 switch (policy & 0xFF) {
248 case OBJC_ASSOCIATION_SETTER_RETAIN:
249 return ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
250 case OBJC_ASSOCIATION_SETTER_COPY:
251 return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
252 }
253 return value;
254 }
255
256 static void releaseValue(id value, uintptr_t policy) {
257 if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
258 ((id(*)(id, SEL))objc_msgSend)(value, SEL_release);
259 }
260 }
261
262 struct ReleaseValue {
263 void operator() (ObjcAssociation &association) {
264 releaseValue(association.value(), association.policy());
265 }
266 };
267
268 void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
269 // retain the new value (if any) outside the lock.
270 ObjcAssociation old_association(0, nil);
271 id new_value = value ? acquireValue(value, policy) : nil;
272 {
273 AssociationsManager manager;
274 AssociationsHashMap &associations(manager.associations());
275 disguised_ptr_t disguised_object = DISGUISE(object);
276 if (new_value) {
277 // break any existing association.
278 AssociationsHashMap::iterator i = associations.find(disguised_object);
279 if (i != associations.end()) {
280 // secondary table exists
281 ObjectAssociationMap *refs = i->second;
282 ObjectAssociationMap::iterator j = refs->find(key);
283 if (j != refs->end()) {
284 old_association = j->second;
285 j->second = ObjcAssociation(policy, new_value);
286 } else {
287 (*refs)[key] = ObjcAssociation(policy, new_value);
288 }
289 } else {
290 // create the new association (first time).
291 ObjectAssociationMap *refs = new ObjectAssociationMap;
292 associations[disguised_object] = refs;
293 (*refs)[key] = ObjcAssociation(policy, new_value);
294 object->setHasAssociatedObjects();
295 }
296 } else {
297 // setting the association to nil breaks the association.
298 AssociationsHashMap::iterator i = associations.find(disguised_object);
299 if (i != associations.end()) {
300 ObjectAssociationMap *refs = i->second;
301 ObjectAssociationMap::iterator j = refs->find(key);
302 if (j != refs->end()) {
303 old_association = j->second;
304 refs->erase(j);
305 }
306 }
307 }
308 }
309 // release the old value (outside of the lock).
310 if (old_association.hasValue()) ReleaseValue()(old_association);
311 }
312
313 void _object_remove_assocations(id object) {
314 vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
315 {
316 AssociationsManager manager;
317 AssociationsHashMap &associations(manager.associations());
318 if (associations.size() == 0) return;
319 disguised_ptr_t disguised_object = DISGUISE(object);
320 AssociationsHashMap::iterator i = associations.find(disguised_object);
321 if (i != associations.end()) {
322 // copy all of the associations that need to be removed.
323 ObjectAssociationMap *refs = i->second;
324 for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
325 elements.push_back(j->second);
326 }
327 // remove the secondary table.
328 delete refs;
329 associations.erase(i);
330 }
331 }
332 // the calls to releaseValue() happen outside of the lock.
333 for_each(elements.begin(), elements.end(), ReleaseValue());
334 }