]>
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" | |
34 | #import "JSCJSValueInlines.h" | |
35 | #import "JSContextInternal.h" | |
36 | #import "JSValueInternal.h" | |
37 | #import "Weak.h" | |
38 | #import "WeakHandleOwner.h" | |
39 | #import "ObjcRuntimeExtras.h" | |
40 | ||
41 | class JSManagedValueHandleOwner : public JSC::WeakHandleOwner { | |
42 | public: | |
43 | virtual void finalize(JSC::Handle<JSC::Unknown>, void* context); | |
44 | virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&); | |
45 | }; | |
46 | ||
47 | static JSManagedValueHandleOwner* managedValueHandleOwner() | |
48 | { | |
49 | DEFINE_STATIC_LOCAL(JSManagedValueHandleOwner, jsManagedValueHandleOwner, ()); | |
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 A |
170 | JSC::Weak<JSC::JSGlobalObject> m_globalObject; |
171 | WeakValueRef m_weakValue; | |
93a37866 A |
172 | } |
173 | ||
174 | + (JSManagedValue *)managedValueWithValue:(JSValue *)value | |
175 | { | |
176 | return [[[self alloc] initWithValue:value] autorelease]; | |
177 | } | |
178 | ||
179 | - (id)init | |
180 | { | |
181 | return [self initWithValue:nil]; | |
182 | } | |
183 | ||
184 | - (id)initWithValue:(JSValue *)value | |
185 | { | |
186 | self = [super init]; | |
187 | if (!self) | |
188 | return nil; | |
189 | ||
12899fa2 A |
190 | if (!value) |
191 | return self; | |
192 | ||
193 | JSC::ExecState* exec = toJS([value.context JSGlobalContextRef]); | |
194 | JSC::JSGlobalObject* globalObject = exec->lexicalGlobalObject(); | |
195 | JSC::Weak<JSC::JSGlobalObject> weak(globalObject, managedValueHandleOwner(), self); | |
196 | m_globalObject.swap(weak); | |
197 | ||
198 | JSC::JSValue jsValue = toJS(exec, [value JSValueRef]); | |
199 | if (jsValue.isObject()) | |
200 | m_weakValue.setObject(JSC::jsCast<JSC::JSObject*>(jsValue.asCell()), self); | |
201 | else if (jsValue.isString()) | |
202 | m_weakValue.setString(JSC::jsCast<JSC::JSString*>(jsValue.asCell()), self); | |
203 | else | |
204 | m_weakValue.setPrimitive(jsValue); | |
93a37866 A |
205 | return self; |
206 | } | |
207 | ||
208 | - (JSValue *)value | |
209 | { | |
12899fa2 A |
210 | if (!m_globalObject) |
211 | return nil; | |
212 | if (m_weakValue.isClear()) | |
93a37866 | 213 | return nil; |
12899fa2 A |
214 | JSC::ExecState* exec = m_globalObject->globalExec(); |
215 | JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(exec)]; | |
216 | JSC::JSValue value; | |
217 | if (m_weakValue.isPrimitive()) | |
218 | value = m_weakValue.primitive(); | |
219 | else if (m_weakValue.isString()) | |
220 | value = m_weakValue.string(); | |
221 | else | |
222 | value = m_weakValue.object(); | |
223 | return [JSValue valueWithJSValueRef:toRef(exec, value) inContext:context]; | |
93a37866 A |
224 | } |
225 | ||
226 | - (void)disconnectValue | |
227 | { | |
12899fa2 A |
228 | m_globalObject.clear(); |
229 | m_weakValue.clear(); | |
93a37866 A |
230 | } |
231 | ||
232 | @end | |
233 | ||
234 | @interface JSManagedValue (PrivateMethods) | |
235 | - (void)disconnectValue; | |
236 | @end | |
237 | ||
238 | bool JSManagedValueHandleOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor& visitor) | |
239 | { | |
240 | JSManagedValue *managedValue = static_cast<JSManagedValue *>(context); | |
241 | return visitor.containsOpaqueRoot(managedValue); | |
242 | } | |
243 | ||
244 | void JSManagedValueHandleOwner::finalize(JSC::Handle<JSC::Unknown>, void* context) | |
245 | { | |
246 | JSManagedValue *managedValue = static_cast<JSManagedValue *>(context); | |
247 | [managedValue disconnectValue]; | |
248 | } | |
249 | ||
250 | #endif // JSC_OBJC_API_ENABLED |