]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-references.mm
objc4-781.2.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 #include "DenseMapExtras.h"
32
33 // expanded policy bits.
34
35 enum {
36 OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
37 OBJC_ASSOCIATION_SETTER_RETAIN = 1,
38 OBJC_ASSOCIATION_SETTER_COPY = 3, // NOTE: both bits are set, so we can simply test 1 bit in releaseValue below.
39 OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
40 OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
41 OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
42 };
43
44 spinlock_t AssociationsManagerLock;
45
46 namespace objc {
47
48 class ObjcAssociation {
49 uintptr_t _policy;
50 id _value;
51 public:
52 ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
53 ObjcAssociation() : _policy(0), _value(nil) {}
54 ObjcAssociation(const ObjcAssociation &other) = default;
55 ObjcAssociation &operator=(const ObjcAssociation &other) = default;
56 ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
57 swap(other);
58 }
59
60 inline void swap(ObjcAssociation &other) {
61 std::swap(_policy, other._policy);
62 std::swap(_value, other._value);
63 }
64
65 inline uintptr_t policy() const { return _policy; }
66 inline id value() const { return _value; }
67
68 inline void acquireValue() {
69 if (_value) {
70 switch (_policy & 0xFF) {
71 case OBJC_ASSOCIATION_SETTER_RETAIN:
72 _value = objc_retain(_value);
73 break;
74 case OBJC_ASSOCIATION_SETTER_COPY:
75 _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
76 break;
77 }
78 }
79 }
80
81 inline void releaseHeldValue() {
82 if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
83 objc_release(_value);
84 }
85 }
86
87 inline void retainReturnedValue() {
88 if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
89 objc_retain(_value);
90 }
91 }
92
93 inline id autoreleaseReturnedValue() {
94 if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
95 return objc_autorelease(_value);
96 }
97 return _value;
98 }
99 };
100
101 typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
102 typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
103
104 // class AssociationsManager manages a lock / hash table singleton pair.
105 // Allocating an instance acquires the lock
106
107 class AssociationsManager {
108 using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
109 static Storage _mapStorage;
110
111 public:
112 AssociationsManager() { AssociationsManagerLock.lock(); }
113 ~AssociationsManager() { AssociationsManagerLock.unlock(); }
114
115 AssociationsHashMap &get() {
116 return _mapStorage.get();
117 }
118
119 static void init() {
120 _mapStorage.init();
121 }
122 };
123
124 AssociationsManager::Storage AssociationsManager::_mapStorage;
125
126 } // namespace objc
127
128 using namespace objc;
129
130 void
131 _objc_associations_init()
132 {
133 AssociationsManager::init();
134 }
135
136 id
137 _object_get_associative_reference(id object, const void *key)
138 {
139 ObjcAssociation association{};
140
141 {
142 AssociationsManager manager;
143 AssociationsHashMap &associations(manager.get());
144 AssociationsHashMap::iterator i = associations.find((objc_object *)object);
145 if (i != associations.end()) {
146 ObjectAssociationMap &refs = i->second;
147 ObjectAssociationMap::iterator j = refs.find(key);
148 if (j != refs.end()) {
149 association = j->second;
150 association.retainReturnedValue();
151 }
152 }
153 }
154
155 return association.autoreleaseReturnedValue();
156 }
157
158 void
159 _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
160 {
161 // This code used to work when nil was passed for object and key. Some code
162 // probably relies on that to not crash. Check and handle it explicitly.
163 // rdar://problem/44094390
164 if (!object && !value) return;
165
166 if (object->getIsa()->forbidsAssociatedObjects())
167 _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
168
169 DisguisedPtr<objc_object> disguised{(objc_object *)object};
170 ObjcAssociation association{policy, value};
171
172 // retain the new value (if any) outside the lock.
173 association.acquireValue();
174
175 {
176 AssociationsManager manager;
177 AssociationsHashMap &associations(manager.get());
178
179 if (value) {
180 auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
181 if (refs_result.second) {
182 /* it's the first association we make */
183 object->setHasAssociatedObjects();
184 }
185
186 /* establish or replace the association */
187 auto &refs = refs_result.first->second;
188 auto result = refs.try_emplace(key, std::move(association));
189 if (!result.second) {
190 association.swap(result.first->second);
191 }
192 } else {
193 auto refs_it = associations.find(disguised);
194 if (refs_it != associations.end()) {
195 auto &refs = refs_it->second;
196 auto it = refs.find(key);
197 if (it != refs.end()) {
198 association.swap(it->second);
199 refs.erase(it);
200 if (refs.size() == 0) {
201 associations.erase(refs_it);
202
203 }
204 }
205 }
206 }
207 }
208
209 // release the old value (outside of the lock).
210 association.releaseHeldValue();
211 }
212
213 // Unlike setting/getting an associated reference,
214 // this function is performance sensitive because of
215 // raw isa objects (such as OS Objects) that can't track
216 // whether they have associated objects.
217 void
218 _object_remove_assocations(id object)
219 {
220 ObjectAssociationMap refs{};
221
222 {
223 AssociationsManager manager;
224 AssociationsHashMap &associations(manager.get());
225 AssociationsHashMap::iterator i = associations.find((objc_object *)object);
226 if (i != associations.end()) {
227 refs.swap(i->second);
228 associations.erase(i);
229 }
230 }
231
232 // release everything (outside of the lock).
233 for (auto &i: refs) {
234 i.second.releaseHeldValue();
235 }
236 }