1 | /* |
2 | * Copyright (C) 2018 Igalia S.L. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public License |
15 | * along with this library; see the file COPYING.LIB. If not, write to |
16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | * Boston, MA 02110-1301, USA. |
18 | */ |
19 | |
20 | #include "config.h" |
21 | #include "JSCWeakValue.h" |
22 | |
23 | #include "APICast.h" |
24 | #include "JSCContextPrivate.h" |
25 | #include "JSCInlines.h" |
26 | #include "JSCValuePrivate.h" |
27 | #include "JSWeakValue.h" |
28 | #include "WeakHandleOwner.h" |
29 | #include <wtf/glib/GRefPtr.h> |
30 | #include <wtf/glib/GUniquePtr.h> |
31 | #include <wtf/glib/WTFGType.h> |
32 | |
33 | /** |
34 | * SECTION: JSCWeakValue |
35 | * @short_description: JavaScript weak value |
36 | * @title: JSCWeakValue |
37 | * @see_also: JSCValue |
38 | * |
39 | * JSCWeakValue represents a weak reference to a value in a #JSCContext. It can be used |
40 | * to keep a reference to a JavaScript value without protecting it from being garbage |
41 | * collected and without referencing the #JSCContext either. |
42 | */ |
43 | |
44 | enum { |
45 | PROP_0, |
46 | |
47 | PROP_VALUE, |
48 | }; |
49 | |
50 | enum { |
51 | CLEARED, |
52 | |
53 | LAST_SIGNAL |
54 | }; |
55 | |
56 | struct _JSCWeakValuePrivate { |
57 | JSC::Weak<JSC::JSGlobalObject> globalObject; |
58 | RefPtr<JSC::JSLock> lock; |
59 | JSC::JSWeakValue weakValueRef; |
60 | }; |
61 | |
62 | static guint signals[LAST_SIGNAL] = { 0, }; |
63 | |
64 | WEBKIT_DEFINE_TYPE(JSCWeakValue, jsc_weak_value, G_TYPE_OBJECT) |
65 | |
66 | static void jscWeakValueClear(JSCWeakValue* weakValue) |
67 | { |
68 | JSCWeakValuePrivate* priv = weakValue->priv; |
69 | priv->globalObject.clear(); |
70 | priv->weakValueRef.clear(); |
71 | } |
72 | |
73 | class JSCWeakValueHandleOwner : public JSC::WeakHandleOwner { |
74 | public: |
75 | void finalize(JSC::Handle<JSC::Unknown>, void* context) override |
76 | { |
77 | auto* weakValue = JSC_WEAK_VALUE(context); |
78 | jscWeakValueClear(weakValue); |
79 | g_signal_emit(weakValue, signals[CLEARED], 0, nullptr); |
80 | } |
81 | }; |
82 | |
83 | static JSCWeakValueHandleOwner& weakValueHandleOwner() |
84 | { |
85 | static NeverDestroyed<JSCWeakValueHandleOwner> jscWeakValueHandleOwner; |
86 | return jscWeakValueHandleOwner; |
87 | } |
88 | |
89 | static void jscWeakValueInitialize(JSCWeakValue* weakValue, JSCValue* value) |
90 | { |
91 | JSCWeakValuePrivate* priv = weakValue->priv; |
92 | auto* jsContext = jscContextGetJSContext(jsc_value_get_context(value)); |
93 | JSC::ExecState* exec = toJS(jsContext); |
94 | JSC::JSGlobalObject* globalObject = exec->lexicalGlobalObject(); |
95 | auto& owner = weakValueHandleOwner(); |
96 | JSC::Weak<JSC::JSGlobalObject> weak(globalObject, &owner, weakValue); |
97 | priv->globalObject.swap(weak); |
98 | priv->lock = &exec->vm().apiLock(); |
99 | |
100 | JSC::JSValue jsValue = toJS(exec, jscValueGetJSValue(value)); |
101 | if (jsValue.isObject()) |
102 | priv->weakValueRef.setObject(JSC::jsCast<JSC::JSObject*>(jsValue.asCell()), owner, weakValue); |
103 | else if (jsValue.isString()) |
104 | priv->weakValueRef.setString(JSC::jsCast<JSC::JSString*>(jsValue.asCell()), owner, weakValue); |
105 | else |
106 | priv->weakValueRef.setPrimitive(jsValue); |
107 | } |
108 | |
109 | static void jscWeakValueSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec) |
110 | { |
111 | switch (propID) { |
112 | case PROP_VALUE: |
113 | jscWeakValueInitialize(JSC_WEAK_VALUE(object), JSC_VALUE(g_value_get_object(value))); |
114 | break; |
115 | default: |
116 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec); |
117 | } |
118 | } |
119 | |
120 | static void jscWeakValueDispose(GObject* object) |
121 | { |
122 | JSCWeakValue* weakValue = JSC_WEAK_VALUE(object); |
123 | jscWeakValueClear(weakValue); |
124 | |
125 | G_OBJECT_CLASS(jsc_weak_value_parent_class)->dispose(object); |
126 | } |
127 | |
128 | static void jsc_weak_value_class_init(JSCWeakValueClass* klass) |
129 | { |
130 | GObjectClass* objClass = G_OBJECT_CLASS(klass); |
131 | objClass->set_property = jscWeakValueSetProperty; |
132 | objClass->dispose = jscWeakValueDispose; |
133 | |
134 | /** |
135 | * JSCWeakValue:value: |
136 | * |
137 | * The #JSCValue referencing the JavaScript value. |
138 | */ |
139 | g_object_class_install_property(objClass, |
140 | PROP_VALUE, |
141 | g_param_spec_object( |
142 | "value" , |
143 | "JSCValue" , |
144 | "JSC Value" , |
145 | JSC_TYPE_VALUE, |
146 | static_cast<GParamFlags>(WEBKIT_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY))); |
147 | |
148 | /** |
149 | * JSCWeakValue::cleared: |
150 | * @weak_value: the #JSCWeakValue |
151 | * |
152 | * This signal is emitted when the JavaScript value is destroyed. |
153 | */ |
154 | signals[CLEARED] = g_signal_new( |
155 | "cleared" , |
156 | G_TYPE_FROM_CLASS(klass), |
157 | G_SIGNAL_RUN_LAST, |
158 | 0, nullptr, nullptr, |
159 | g_cclosure_marshal_generic, |
160 | G_TYPE_NONE, 0, |
161 | G_TYPE_NONE); |
162 | } |
163 | |
164 | /** |
165 | * jsc_weak_value_new: |
166 | * @value: a #JSCValue |
167 | * |
168 | * Create a new #JSCWeakValue for the JavaScript value referenced by @value. |
169 | * |
170 | * Returns: (transfer full): a new #JSCWeakValue |
171 | */ |
172 | JSCWeakValue* jsc_weak_value_new(JSCValue* value) |
173 | { |
174 | g_return_val_if_fail(JSC_IS_VALUE(value), nullptr); |
175 | |
176 | return JSC_WEAK_VALUE(g_object_new(JSC_TYPE_WEAK_VALUE, "value" , value, nullptr)); |
177 | } |
178 | |
179 | /** |
180 | * jsc_weak_value_get_value: |
181 | * @weak_value: a #JSCWeakValue |
182 | * |
183 | * Get a #JSCValue referencing the JavaScript value of @weak_value. |
184 | * |
185 | * Returns: (transfer full): a new #JSCValue or %NULL if @weak_value was cleared. |
186 | */ |
187 | JSCValue* jsc_weak_value_get_value(JSCWeakValue* weakValue) |
188 | { |
189 | g_return_val_if_fail(JSC_IS_WEAK_VALUE(weakValue), nullptr); |
190 | |
191 | JSCWeakValuePrivate* priv = weakValue->priv; |
192 | WTF::Locker<JSC::JSLock> locker(priv->lock.get()); |
193 | JSC::VM* vm = priv->lock->vm(); |
194 | if (!vm) |
195 | return nullptr; |
196 | |
197 | JSC::JSLockHolder apiLocker(vm); |
198 | if (!priv->globalObject || priv->weakValueRef.isClear()) |
199 | return nullptr; |
200 | |
201 | JSC::JSValue value; |
202 | if (priv->weakValueRef.isPrimitive()) |
203 | value = priv->weakValueRef.primitive(); |
204 | else if (priv->weakValueRef.isString()) |
205 | value = priv->weakValueRef.string(); |
206 | else |
207 | value = priv->weakValueRef.object(); |
208 | |
209 | JSC::ExecState* exec = priv->globalObject->globalExec(); |
210 | GRefPtr<JSCContext> context = jscContextGetOrCreate(toGlobalRef(exec)); |
211 | return jscContextGetOrCreateValue(context.get(), toRef(exec, value)).leakRef(); |
212 | } |
213 | |