]> git.saurik.com Git - apple/javascriptcore.git/blob - API/JSManagedValue.mm
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / API / JSManagedValue.mm
1 /*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26
27 #import "config.h"
28 #import "JSManagedValue.h"
29
30 #if JSC_OBJC_API_ENABLED
31
32 #import "APICast.h"
33 #import "Heap.h"
34 #import "JSContextInternal.h"
35 #import "JSValueInternal.h"
36 #import "Weak.h"
37 #import "WeakHandleOwner.h"
38 #import "ObjcRuntimeExtras.h"
39 #import "JSCInlines.h"
40 #import <wtf/spi/cocoa/NSMapTableSPI.h>
41
42 class JSManagedValueHandleOwner : public JSC::WeakHandleOwner {
43 public:
44 virtual void finalize(JSC::Handle<JSC::Unknown>, void* context) override;
45 virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&) override;
46 };
47
48 static JSManagedValueHandleOwner* managedValueHandleOwner()
49 {
50 DEPRECATED_DEFINE_STATIC_LOCAL(JSManagedValueHandleOwner, jsManagedValueHandleOwner, ());
51 return &jsManagedValueHandleOwner;
52 }
53
54 class WeakValueRef {
55 public:
56 WeakValueRef()
57 : m_tag(NotSet)
58 {
59 }
60
61 ~WeakValueRef()
62 {
63 clear();
64 }
65
66 void clear()
67 {
68 switch (m_tag) {
69 case NotSet:
70 return;
71 case Primitive:
72 u.m_primitive = JSC::JSValue();
73 return;
74 case Object:
75 u.m_object.clear();
76 return;
77 case String:
78 u.m_string.clear();
79 return;
80 }
81 RELEASE_ASSERT_NOT_REACHED();
82 }
83
84 bool isClear() const
85 {
86 switch (m_tag) {
87 case NotSet:
88 return true;
89 case Primitive:
90 return !u.m_primitive;
91 case Object:
92 return !u.m_object;
93 case String:
94 return !u.m_string;
95 }
96 RELEASE_ASSERT_NOT_REACHED();
97 }
98
99 bool isSet() const { return m_tag != NotSet; }
100 bool isPrimitive() const { return m_tag == Primitive; }
101 bool isObject() const { return m_tag == Object; }
102 bool isString() const { return m_tag == String; }
103
104 void setPrimitive(JSC::JSValue primitive)
105 {
106 ASSERT(!isSet());
107 ASSERT(!u.m_primitive);
108 ASSERT(primitive.isPrimitive());
109 m_tag = Primitive;
110 u.m_primitive = primitive;
111 }
112
113 void setObject(JSC::JSObject* object, void* context)
114 {
115 ASSERT(!isSet());
116 ASSERT(!u.m_object);
117 m_tag = Object;
118 JSC::Weak<JSC::JSObject> weak(object, managedValueHandleOwner(), context);
119 u.m_object.swap(weak);
120 }
121
122 void setString(JSC::JSString* string, void* context)
123 {
124 ASSERT(!isSet());
125 ASSERT(!u.m_object);
126 m_tag = String;
127 JSC::Weak<JSC::JSString> weak(string, managedValueHandleOwner(), context);
128 u.m_string.swap(weak);
129 }
130
131 JSC::JSObject* object()
132 {
133 ASSERT(isObject());
134 return u.m_object.get();
135 }
136
137 JSC::JSValue primitive()
138 {
139 ASSERT(isPrimitive());
140 return u.m_primitive;
141 }
142
143 JSC::JSString* string()
144 {
145 ASSERT(isString());
146 return u.m_string.get();
147 }
148
149 private:
150 enum WeakTypeTag { NotSet, Primitive, Object, String };
151 WeakTypeTag m_tag;
152 union WeakValueUnion {
153 public:
154 WeakValueUnion ()
155 : m_primitive(JSC::JSValue())
156 {
157 }
158
159 ~WeakValueUnion()
160 {
161 ASSERT(!m_primitive);
162 }
163
164 JSC::JSValue m_primitive;
165 JSC::Weak<JSC::JSObject> m_object;
166 JSC::Weak<JSC::JSString> m_string;
167 } u;
168 };
169
170 @implementation JSManagedValue {
171 JSC::Weak<JSC::JSGlobalObject> m_globalObject;
172 RefPtr<JSC::JSLock> m_lock;
173 WeakValueRef m_weakValue;
174 NSMapTable *m_owners;
175 }
176
177 + (JSManagedValue *)managedValueWithValue:(JSValue *)value
178 {
179 return [[[self alloc] initWithValue:value] autorelease];
180 }
181
182 + (JSManagedValue *)managedValueWithValue:(JSValue *)value andOwner:(id)owner
183 {
184 JSManagedValue *managedValue = [[self alloc] initWithValue:value];
185 [value.context.virtualMachine addManagedReference:managedValue withOwner:owner];
186 return [managedValue autorelease];
187 }
188
189 - (instancetype)init
190 {
191 return [self initWithValue:nil];
192 }
193
194 - (instancetype)initWithValue:(JSValue *)value
195 {
196 self = [super init];
197 if (!self)
198 return nil;
199
200 if (!value)
201 return self;
202
203 JSC::ExecState* exec = toJS([value.context JSGlobalContextRef]);
204 JSC::JSGlobalObject* globalObject = exec->lexicalGlobalObject();
205 JSC::Weak<JSC::JSGlobalObject> weak(globalObject, managedValueHandleOwner(), self);
206 m_globalObject.swap(weak);
207
208 m_lock = &exec->vm().apiLock();
209
210 NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
211 NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality;
212 m_owners = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:1];
213
214 JSC::JSValue jsValue = toJS(exec, [value JSValueRef]);
215 if (jsValue.isObject())
216 m_weakValue.setObject(JSC::jsCast<JSC::JSObject*>(jsValue.asCell()), self);
217 else if (jsValue.isString())
218 m_weakValue.setString(JSC::jsCast<JSC::JSString*>(jsValue.asCell()), self);
219 else
220 m_weakValue.setPrimitive(jsValue);
221 return self;
222 }
223
224 - (void)dealloc
225 {
226 JSVirtualMachine *virtualMachine = [[[self value] context] virtualMachine];
227 if (virtualMachine) {
228 NSMapTable *copy = [m_owners copy];
229 for (id owner in [copy keyEnumerator]) {
230 size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, owner));
231 while (count--)
232 [virtualMachine removeManagedReference:self withOwner:owner];
233 }
234 [copy release];
235 }
236
237 [self disconnectValue];
238 [m_owners release];
239 [super dealloc];
240 }
241
242 - (void)didAddOwner:(id)owner
243 {
244 size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, owner));
245 NSMapInsert(m_owners, owner, reinterpret_cast<void*>(count + 1));
246 }
247
248 - (void)didRemoveOwner:(id)owner
249 {
250 size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, owner));
251
252 if (!count)
253 return;
254
255 if (count == 1) {
256 NSMapRemove(m_owners, owner);
257 return;
258 }
259
260 NSMapInsert(m_owners, owner, reinterpret_cast<void*>(count - 1));
261 }
262
263 - (JSValue *)value
264 {
265 WTF::Locker<JSC::JSLock> locker(m_lock.get());
266 if (!m_lock->vm())
267 return nil;
268
269 JSC::JSLockHolder apiLocker(m_lock->vm());
270 if (!m_globalObject)
271 return nil;
272 if (m_weakValue.isClear())
273 return nil;
274 JSC::ExecState* exec = m_globalObject->globalExec();
275 JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(exec)];
276 JSC::JSValue value;
277 if (m_weakValue.isPrimitive())
278 value = m_weakValue.primitive();
279 else if (m_weakValue.isString())
280 value = m_weakValue.string();
281 else
282 value = m_weakValue.object();
283 return [JSValue valueWithJSValueRef:toRef(exec, value) inContext:context];
284 }
285
286 - (void)disconnectValue
287 {
288 m_globalObject.clear();
289 m_weakValue.clear();
290 }
291
292 @end
293
294 @interface JSManagedValue (PrivateMethods)
295 - (void)disconnectValue;
296 @end
297
298 bool JSManagedValueHandleOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor& visitor)
299 {
300 JSManagedValue *managedValue = static_cast<JSManagedValue *>(context);
301 return visitor.containsOpaqueRoot(managedValue);
302 }
303
304 void JSManagedValueHandleOwner::finalize(JSC::Handle<JSC::Unknown>, void* context)
305 {
306 JSManagedValue *managedValue = static_cast<JSManagedValue *>(context);
307 [managedValue disconnectValue];
308 }
309
310 #endif // JSC_OBJC_API_ENABLED