]> git.saurik.com Git - apple/objc4.git/blame - runtime/objc-references.mm
objc4-818.2.tar.gz
[apple/objc4.git] / runtime / objc-references.mm
CommitLineData
7af964d1
A
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>
fe06a513 30#include <map>
1807f628 31#include "DenseMapExtras.h"
7af964d1 32
1807f628 33// expanded policy bits.
cd5f04f5 34
1807f628
A
35enum {
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),
34d5b5e8
A
41 OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8),
42 OBJC_ASSOCIATION_SYSTEM_OBJECT = _OBJC_ASSOCIATION_SYSTEM_OBJECT, // 1 << 16
1807f628 43};
7af964d1 44
1807f628 45spinlock_t AssociationsManagerLock;
7af964d1 46
1807f628 47namespace objc {
7af964d1 48
1807f628
A
49class ObjcAssociation {
50 uintptr_t _policy;
51 id _value;
52public:
53 ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
54 ObjcAssociation() : _policy(0), _value(nil) {}
55 ObjcAssociation(const ObjcAssociation &other) = default;
56 ObjcAssociation &operator=(const ObjcAssociation &other) = default;
57 ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
58 swap(other);
59 }
60
61 inline void swap(ObjcAssociation &other) {
62 std::swap(_policy, other._policy);
63 std::swap(_value, other._value);
64 }
7af964d1 65
1807f628
A
66 inline uintptr_t policy() const { return _policy; }
67 inline id value() const { return _value; }
68
69 inline void acquireValue() {
70 if (_value) {
71 switch (_policy & 0xFF) {
72 case OBJC_ASSOCIATION_SETTER_RETAIN:
73 _value = objc_retain(_value);
74 break;
75 case OBJC_ASSOCIATION_SETTER_COPY:
76 _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
77 break;
78 }
79 }
80 }
7af964d1 81
1807f628
A
82 inline void releaseHeldValue() {
83 if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
84 objc_release(_value);
7af964d1 85 }
1807f628 86 }
7af964d1 87
1807f628
A
88 inline void retainReturnedValue() {
89 if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
90 objc_retain(_value);
7af964d1 91 }
1807f628 92 }
7af964d1 93
1807f628
A
94 inline id autoreleaseReturnedValue() {
95 if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
96 return objc_autorelease(_value);
97 }
98 return _value;
99 }
100};
7af964d1 101
1807f628
A
102typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
103typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
7af964d1
A
104
105// class AssociationsManager manages a lock / hash table singleton pair.
1807f628 106// Allocating an instance acquires the lock
7af964d1
A
107
108class AssociationsManager {
1807f628
A
109 using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
110 static Storage _mapStorage;
111
7af964d1 112public:
bd8dfcfc
A
113 AssociationsManager() { AssociationsManagerLock.lock(); }
114 ~AssociationsManager() { AssociationsManagerLock.unlock(); }
1807f628
A
115
116 AssociationsHashMap &get() {
117 return _mapStorage.get();
118 }
119
120 static void init() {
121 _mapStorage.init();
7af964d1
A
122 }
123};
124
1807f628 125AssociationsManager::Storage AssociationsManager::_mapStorage;
7af964d1 126
1807f628 127} // namespace objc
7af964d1 128
1807f628
A
129using namespace objc;
130
131void
132_objc_associations_init()
133{
134 AssociationsManager::init();
135}
136
137id
138_object_get_associative_reference(id object, const void *key)
139{
140 ObjcAssociation association{};
7af964d1 141
7af964d1
A
142 {
143 AssociationsManager manager;
1807f628
A
144 AssociationsHashMap &associations(manager.get());
145 AssociationsHashMap::iterator i = associations.find((objc_object *)object);
7af964d1 146 if (i != associations.end()) {
1807f628
A
147 ObjectAssociationMap &refs = i->second;
148 ObjectAssociationMap::iterator j = refs.find(key);
149 if (j != refs.end()) {
150 association = j->second;
151 association.retainReturnedValue();
7af964d1
A
152 }
153 }
154 }
7af964d1 155
1807f628 156 return association.autoreleaseReturnedValue();
7af964d1
A
157}
158
1807f628
A
159void
160_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
161{
13ba007e
A
162 // This code used to work when nil was passed for object and key. Some code
163 // probably relies on that to not crash. Check and handle it explicitly.
164 // rdar://problem/44094390
165 if (!object && !value) return;
1807f628 166
13ba007e
A
167 if (object->getIsa()->forbidsAssociatedObjects())
168 _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
1807f628
A
169
170 DisguisedPtr<objc_object> disguised{(objc_object *)object};
171 ObjcAssociation association{policy, value};
172
7af964d1 173 // retain the new value (if any) outside the lock.
1807f628
A
174 association.acquireValue();
175
34d5b5e8 176 bool isFirstAssociation = false;
7af964d1
A
177 {
178 AssociationsManager manager;
1807f628
A
179 AssociationsHashMap &associations(manager.get());
180
181 if (value) {
182 auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
183 if (refs_result.second) {
184 /* it's the first association we make */
34d5b5e8 185 isFirstAssociation = true;
7af964d1 186 }
1807f628
A
187
188 /* establish or replace the association */
189 auto &refs = refs_result.first->second;
190 auto result = refs.try_emplace(key, std::move(association));
191 if (!result.second) {
192 association.swap(result.first->second);
193 }
7af964d1 194 } else {
1807f628
A
195 auto refs_it = associations.find(disguised);
196 if (refs_it != associations.end()) {
197 auto &refs = refs_it->second;
198 auto it = refs.find(key);
199 if (it != refs.end()) {
200 association.swap(it->second);
201 refs.erase(it);
202 if (refs.size() == 0) {
203 associations.erase(refs_it);
204
205 }
7af964d1
A
206 }
207 }
208 }
209 }
1807f628 210
34d5b5e8
A
211 // Call setHasAssociatedObjects outside the lock, since this
212 // will call the object's _noteAssociatedObjects method if it
213 // has one, and this may trigger +initialize which might do
214 // arbitrary stuff, including setting more associated objects.
215 if (isFirstAssociation)
216 object->setHasAssociatedObjects();
217
7af964d1 218 // release the old value (outside of the lock).
1807f628 219 association.releaseHeldValue();
7af964d1
A
220}
221
1807f628
A
222// Unlike setting/getting an associated reference,
223// this function is performance sensitive because of
224// raw isa objects (such as OS Objects) that can't track
225// whether they have associated objects.
226void
34d5b5e8 227_object_remove_assocations(id object, bool deallocating)
1807f628
A
228{
229 ObjectAssociationMap refs{};
230
7af964d1
A
231 {
232 AssociationsManager manager;
1807f628
A
233 AssociationsHashMap &associations(manager.get());
234 AssociationsHashMap::iterator i = associations.find((objc_object *)object);
7af964d1 235 if (i != associations.end()) {
1807f628 236 refs.swap(i->second);
34d5b5e8
A
237
238 // If we are not deallocating, then SYSTEM_OBJECT associations are preserved.
239 bool didReInsert = false;
240 if (!deallocating) {
241 for (auto &ref: refs) {
242 if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
243 i->second.insert(ref);
244 didReInsert = true;
245 }
246 }
247 }
248 if (!didReInsert)
249 associations.erase(i);
7af964d1
A
250 }
251 }
1807f628 252
34d5b5e8
A
253 // Associations to be released after the normal ones.
254 SmallVector<ObjcAssociation *, 4> laterRefs;
255
1807f628
A
256 // release everything (outside of the lock).
257 for (auto &i: refs) {
34d5b5e8
A
258 if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
259 // If we are not deallocating, then RELEASE_LATER associations don't get released.
260 if (deallocating)
261 laterRefs.append(&i.second);
262 } else {
263 i.second.releaseHeldValue();
264 }
265 }
266 for (auto *later: laterRefs) {
267 later->releaseHeldValue();
1807f628 268 }
7af964d1 269}