]>
Commit | Line | Data |
---|---|---|
93a37866 A |
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" | |
93a37866 A |
34 | #import "JSContextInternal.h" |
35 | #import "JSValueInternal.h" | |
36 | #import "Weak.h" | |
37 | #import "WeakHandleOwner.h" | |
38 | #import "ObjcRuntimeExtras.h" | |
81345200 | 39 | #import "JSCInlines.h" |
93a37866 A |
40 | |
41 | class JSManagedValueHandleOwner : public JSC::WeakHandleOwner { | |
42 | public: | |
81345200 A |
43 | virtual void finalize(JSC::Handle<JSC::Unknown>, void* context) override; |
44 | virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&) override; | |
93a37866 A |
45 | }; |
46 | ||
47 | static JSManagedValueHandleOwner* managedValueHandleOwner() | |
48 | { | |
81345200 | 49 | DEPRECATED_DEFINE_STATIC_LOCAL(JSManagedValueHandleOwner, jsManagedValueHandleOwner, ()); |
93a37866 A |
50 | return &jsManagedValueHandleOwner; |
51 | } | |
52 | ||
12899fa2 A |
53 | class WeakValueRef { |
54 | public: | |
55 | WeakValueRef() | |
56 | : m_tag(NotSet) | |
57 | { | |
58 | } | |
59 | ||
60 | ~WeakValueRef() | |
61 | { | |
62 | clear(); | |
63 | } | |
64 | ||
65 | void clear() | |
66 | { | |
67 | switch (m_tag) { | |
68 | case NotSet: | |
69 | return; | |
70 | case Primitive: | |
71 | u.m_primitive = JSC::JSValue(); | |
72 | return; | |
73 | case Object: | |
74 | u.m_object.clear(); | |
75 | return; | |
76 | case String: | |
77 | u.m_string.clear(); | |
78 | return; | |
79 | } | |
80 | RELEASE_ASSERT_NOT_REACHED(); | |
81 | } | |
82 | ||
83 | bool isClear() const | |
84 | { | |
85 | switch (m_tag) { | |
86 | case NotSet: | |
87 | return true; | |
88 | case Primitive: | |
89 | return !u.m_primitive; | |
90 | case Object: | |
91 | return !u.m_object; | |
92 | case String: | |
93 | return !u.m_string; | |
94 | } | |
95 | RELEASE_ASSERT_NOT_REACHED(); | |
96 | } | |
97 | ||
98 | bool isSet() const { return m_tag != NotSet; } | |
99 | bool isPrimitive() const { return m_tag == Primitive; } | |
100 | bool isObject() const { return m_tag == Object; } | |
101 | bool isString() const { return m_tag == String; } | |
102 | ||
103 | void setPrimitive(JSC::JSValue primitive) | |
104 | { | |
105 | ASSERT(!isSet()); | |
106 | ASSERT(!u.m_primitive); | |
107 | ASSERT(primitive.isPrimitive()); | |
108 | m_tag = Primitive; | |
109 | u.m_primitive = primitive; | |
110 | } | |
111 | ||
112 | void setObject(JSC::JSObject* object, void* context) | |
113 | { | |
114 | ASSERT(!isSet()); | |
115 | ASSERT(!u.m_object); | |
116 | m_tag = Object; | |
117 | JSC::Weak<JSC::JSObject> weak(object, managedValueHandleOwner(), context); | |
118 | u.m_object.swap(weak); | |
119 | } | |
120 | ||
121 | void setString(JSC::JSString* string, void* context) | |
122 | { | |
123 | ASSERT(!isSet()); | |
124 | ASSERT(!u.m_object); | |
125 | m_tag = String; | |
126 | JSC::Weak<JSC::JSString> weak(string, managedValueHandleOwner(), context); | |
127 | u.m_string.swap(weak); | |
128 | } | |
129 | ||
130 | JSC::JSObject* object() | |
131 | { | |
132 | ASSERT(isObject()); | |
133 | return u.m_object.get(); | |
134 | } | |
135 | ||
136 | JSC::JSValue primitive() | |
137 | { | |
138 | ASSERT(isPrimitive()); | |
139 | return u.m_primitive; | |
140 | } | |
141 | ||
142 | JSC::JSString* string() | |
143 | { | |
144 | ASSERT(isString()); | |
145 | return u.m_string.get(); | |
146 | } | |
147 | ||
148 | private: | |
149 | enum WeakTypeTag { NotSet, Primitive, Object, String }; | |
150 | WeakTypeTag m_tag; | |
151 | union WeakValueUnion { | |
152 | public: | |
153 | WeakValueUnion () | |
154 | : m_primitive(JSC::JSValue()) | |
155 | { | |
156 | } | |
157 | ||
158 | ~WeakValueUnion() | |
159 | { | |
160 | ASSERT(!m_primitive); | |
161 | } | |
162 | ||
163 | JSC::JSValue m_primitive; | |
164 | JSC::Weak<JSC::JSObject> m_object; | |
165 | JSC::Weak<JSC::JSString> m_string; | |
166 | } u; | |
167 | }; | |
168 | ||
93a37866 | 169 | @implementation JSManagedValue { |
12899fa2 | 170 | JSC::Weak<JSC::JSGlobalObject> m_globalObject; |
81345200 | 171 | RefPtr<JSC::JSLock> m_lock; |
12899fa2 | 172 | WeakValueRef m_weakValue; |
81345200 | 173 | NSMapTable *m_owners; |
93a37866 A |
174 | } |
175 | ||
176 | + (JSManagedValue *)managedValueWithValue:(JSValue *)value | |
177 | { | |
178 | return [[[self alloc] initWithValue:value] autorelease]; | |
179 | } | |
180 | ||
81345200 A |
181 | + (JSManagedValue *)managedValueWithValue:(JSValue *)value andOwner:(id)owner |
182 | { | |
183 | JSManagedValue *managedValue = [[self alloc] initWithValue:value]; | |
184 | [value.context.virtualMachine addManagedReference:managedValue withOwner:owner]; | |
185 | return [managedValue autorelease]; | |
186 | } | |
187 | ||
188 | - (instancetype)init | |
93a37866 A |
189 | { |
190 | return [self initWithValue:nil]; | |
191 | } | |
192 | ||
81345200 | 193 | - (instancetype)initWithValue:(JSValue *)value |
93a37866 A |
194 | { |
195 | self = [super init]; | |
196 | if (!self) | |
197 | return nil; | |
198 | ||
12899fa2 A |
199 | if (!value) |
200 | return self; | |
201 | ||
202 | JSC::ExecState* exec = toJS([value.context JSGlobalContextRef]); | |
203 | JSC::JSGlobalObject* globalObject = exec->lexicalGlobalObject(); | |
204 | JSC::Weak<JSC::JSGlobalObject> weak(globalObject, managedValueHandleOwner(), self); | |
205 | m_globalObject.swap(weak); | |
206 | ||
81345200 A |
207 | m_lock = &exec->vm().apiLock(); |
208 | ||
209 | NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality; | |
210 | NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality; | |
211 | m_owners = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:1]; | |
212 | ||
12899fa2 A |
213 | JSC::JSValue jsValue = toJS(exec, [value JSValueRef]); |
214 | if (jsValue.isObject()) | |
215 | m_weakValue.setObject(JSC::jsCast<JSC::JSObject*>(jsValue.asCell()), self); | |
216 | else if (jsValue.isString()) | |
217 | m_weakValue.setString(JSC::jsCast<JSC::JSString*>(jsValue.asCell()), self); | |
218 | else | |
219 | m_weakValue.setPrimitive(jsValue); | |
93a37866 A |
220 | return self; |
221 | } | |
222 | ||
81345200 A |
223 | - (void)dealloc |
224 | { | |
225 | JSVirtualMachine *virtualMachine = [[[self value] context] virtualMachine]; | |
226 | if (virtualMachine) { | |
227 | NSMapTable *copy = [m_owners copy]; | |
228 | for (id owner in [copy keyEnumerator]) { | |
229 | size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, owner)); | |
230 | while (count--) | |
231 | [virtualMachine removeManagedReference:self withOwner:owner]; | |
232 | } | |
233 | [copy release]; | |
234 | } | |
235 | ||
236 | [self disconnectValue]; | |
237 | [m_owners release]; | |
238 | [super dealloc]; | |
239 | } | |
240 | ||
241 | - (void)didAddOwner:(id)owner | |
242 | { | |
243 | size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, owner)); | |
244 | NSMapInsert(m_owners, owner, reinterpret_cast<void*>(count + 1)); | |
245 | } | |
246 | ||
247 | - (void)didRemoveOwner:(id)owner | |
248 | { | |
249 | size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, owner)); | |
250 | ||
251 | if (!count) | |
252 | return; | |
253 | ||
254 | if (count == 1) { | |
255 | NSMapRemove(m_owners, owner); | |
256 | return; | |
257 | } | |
258 | ||
259 | NSMapInsert(m_owners, owner, reinterpret_cast<void*>(count - 1)); | |
260 | } | |
261 | ||
93a37866 A |
262 | - (JSValue *)value |
263 | { | |
81345200 A |
264 | WTF::Locker<JSC::JSLock> locker(m_lock.get()); |
265 | if (!m_lock->vm()) | |
266 | return nil; | |
267 | ||
268 | JSC::JSLockHolder apiLocker(m_lock->vm()); | |
12899fa2 A |
269 | if (!m_globalObject) |
270 | return nil; | |
271 | if (m_weakValue.isClear()) | |
93a37866 | 272 | return nil; |
12899fa2 A |
273 | JSC::ExecState* exec = m_globalObject->globalExec(); |
274 | JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(exec)]; | |
275 | JSC::JSValue value; | |
276 | if (m_weakValue.isPrimitive()) | |
277 | value = m_weakValue.primitive(); | |
278 | else if (m_weakValue.isString()) | |
279 | value = m_weakValue.string(); | |
280 | else | |
281 | value = m_weakValue.object(); | |
282 | return [JSValue valueWithJSValueRef:toRef(exec, value) inContext:context]; | |
93a37866 A |
283 | } |
284 | ||
285 | - (void)disconnectValue | |
286 | { | |
12899fa2 A |
287 | m_globalObject.clear(); |
288 | m_weakValue.clear(); | |
93a37866 A |
289 | } |
290 | ||
291 | @end | |
292 | ||
293 | @interface JSManagedValue (PrivateMethods) | |
294 | - (void)disconnectValue; | |
295 | @end | |
296 | ||
297 | bool JSManagedValueHandleOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor& visitor) | |
298 | { | |
299 | JSManagedValue *managedValue = static_cast<JSManagedValue *>(context); | |
300 | return visitor.containsOpaqueRoot(managedValue); | |
301 | } | |
302 | ||
303 | void JSManagedValueHandleOwner::finalize(JSC::Handle<JSC::Unknown>, void* context) | |
304 | { | |
305 | JSManagedValue *managedValue = static_cast<JSManagedValue *>(context); | |
306 | [managedValue disconnectValue]; | |
307 | } | |
308 | ||
309 | #endif // JSC_OBJC_API_ENABLED |