]> git.saurik.com Git - apple/javascriptcore.git/blame - API/JSManagedValue.mm
JavaScriptCore-7600.1.4.16.1.tar.gz
[apple/javascriptcore.git] / API / JSManagedValue.mm
CommitLineData
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
41class JSManagedValueHandleOwner : public JSC::WeakHandleOwner {
42public:
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
47static JSManagedValueHandleOwner* managedValueHandleOwner()
48{
81345200 49 DEPRECATED_DEFINE_STATIC_LOCAL(JSManagedValueHandleOwner, jsManagedValueHandleOwner, ());
93a37866
A
50 return &jsManagedValueHandleOwner;
51}
52
12899fa2
A
53class WeakValueRef {
54public:
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
148private:
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
297bool 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
303void 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