Skip to content

Commit b54d3d3

Browse files
committed
[GLIB] Add JSCWeakValue to JavaScriptCore GLib API
https://bugs.webkit.org/show_bug.cgi?id=184041 Reviewed by Michael Catanzaro. Source/JavaScriptCore: This allows to keep a reference to a JavaSCript value without protecting it, and without having a strong reference of the context. When the value is cleared the JSCWeakValue::cleared signal is emitted and jsc_weak_value_get_value() will always return nullptr. * API/glib/JSCWeakValue.cpp: Added. (WeakValueRef::~WeakValueRef): (WeakValueRef::clear): (WeakValueRef::isClear const): (WeakValueRef::isSet const): (WeakValueRef::isPrimitive const): (WeakValueRef::isObject const): (WeakValueRef::isString const): (WeakValueRef::setPrimitive): (WeakValueRef::setObject): (WeakValueRef::setString): (WeakValueRef::object const): (WeakValueRef::primitive const): (WeakValueRef::string const): (weakValueHandleOwner): (jscWeakValueInitialize): (jscWeakValueSetProperty): (jscWeakValueDispose): (jsc_weak_value_class_init): (jsc_weak_value_new): (jsc_weak_value_get_value): * API/glib/JSCWeakValue.h: Added. * API/glib/docs/jsc-glib-4.0-sections.txt: * API/glib/docs/jsc-glib-docs.sgml: * API/glib/jsc.h: * GLib.cmake: Tools: Add test case for JSCWeakValue. * TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp: (weakValueClearedCallback): (testJSCWeakValue): (main): Canonical link: https://commits.webkit.org/199656@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230024 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent 385517a commit b54d3d3

9 files changed

Lines changed: 652 additions & 0 deletions

File tree

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
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 "Weak.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+
// FIXME: move this to its own file to be shared by GLib and Objc APIs.
57+
class WeakValueRef {
58+
public:
59+
~WeakValueRef()
60+
{
61+
clear();
62+
}
63+
64+
void clear()
65+
{
66+
switch (m_tag) {
67+
case WeakTypeTag::NotSet:
68+
return;
69+
case WeakTypeTag::Primitive:
70+
u.m_primitive = JSC::JSValue();
71+
return;
72+
case WeakTypeTag::Object:
73+
u.m_object.clear();
74+
return;
75+
case WeakTypeTag::String:
76+
u.m_string.clear();
77+
return;
78+
}
79+
RELEASE_ASSERT_NOT_REACHED();
80+
}
81+
82+
bool isClear() const
83+
{
84+
switch (m_tag) {
85+
case WeakTypeTag::NotSet:
86+
return true;
87+
case WeakTypeTag::Primitive:
88+
return !u.m_primitive;
89+
case WeakTypeTag::Object:
90+
return !u.m_object;
91+
case WeakTypeTag::String:
92+
return !u.m_string;
93+
}
94+
RELEASE_ASSERT_NOT_REACHED();
95+
}
96+
97+
bool isSet() const { return m_tag != WeakTypeTag::NotSet; }
98+
bool isPrimitive() const { return m_tag == WeakTypeTag::Primitive; }
99+
bool isObject() const { return m_tag == WeakTypeTag::Object; }
100+
bool isString() const { return m_tag == WeakTypeTag::String; }
101+
102+
void setPrimitive(JSC::JSValue primitive)
103+
{
104+
ASSERT(!isSet());
105+
ASSERT(!u.m_primitive);
106+
ASSERT(primitive.isPrimitive());
107+
m_tag = WeakTypeTag::Primitive;
108+
u.m_primitive = primitive;
109+
}
110+
111+
void setObject(JSC::JSObject* object, JSC::WeakHandleOwner& owner, void* context)
112+
{
113+
ASSERT(!isSet());
114+
ASSERT(!u.m_object);
115+
m_tag = WeakTypeTag::Object;
116+
JSC::Weak<JSC::JSObject> weak(object, &owner, context);
117+
u.m_object.swap(weak);
118+
}
119+
120+
void setString(JSC::JSString* string, JSC::WeakHandleOwner& owner, void* context)
121+
{
122+
ASSERT(!isSet());
123+
ASSERT(!u.m_object);
124+
m_tag = WeakTypeTag::String;
125+
JSC::Weak<JSC::JSString> weak(string, &owner, context);
126+
u.m_string.swap(weak);
127+
}
128+
129+
JSC::JSObject* object() const
130+
{
131+
ASSERT(isObject());
132+
return u.m_object.get();
133+
}
134+
135+
JSC::JSValue primitive() const
136+
{
137+
ASSERT(isPrimitive());
138+
return u.m_primitive;
139+
}
140+
141+
JSC::JSString* string() const
142+
{
143+
ASSERT(isString());
144+
return u.m_string.get();
145+
}
146+
147+
private:
148+
enum class WeakTypeTag { NotSet, Primitive, Object, String };
149+
150+
WeakTypeTag m_tag { WeakTypeTag::NotSet };
151+
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+
struct _JSCWeakValuePrivate {
171+
JSC::Weak<JSC::JSGlobalObject> globalObject;
172+
RefPtr<JSC::JSLock> lock;
173+
WeakValueRef weakValueRef;
174+
};
175+
176+
static guint signals[LAST_SIGNAL] = { 0, };
177+
178+
WEBKIT_DEFINE_TYPE(JSCWeakValue, jsc_weak_value, G_TYPE_OBJECT)
179+
180+
static void jscWeakValueClear(JSCWeakValue* weakValue)
181+
{
182+
JSCWeakValuePrivate* priv = weakValue->priv;
183+
priv->globalObject.clear();
184+
priv->weakValueRef.clear();
185+
}
186+
187+
class JSCWeakValueHandleOwner : public JSC::WeakHandleOwner {
188+
public:
189+
void finalize(JSC::Handle<JSC::Unknown>, void* context) override
190+
{
191+
auto* weakValue = JSC_WEAK_VALUE(context);
192+
jscWeakValueClear(weakValue);
193+
g_signal_emit(weakValue, signals[CLEARED], 0, nullptr);
194+
}
195+
};
196+
197+
static JSCWeakValueHandleOwner& weakValueHandleOwner()
198+
{
199+
static NeverDestroyed<JSCWeakValueHandleOwner> jscWeakValueHandleOwner;
200+
return jscWeakValueHandleOwner;
201+
}
202+
203+
static void jscWeakValueInitialize(JSCWeakValue* weakValue, JSCValue* value)
204+
{
205+
JSCWeakValuePrivate* priv = weakValue->priv;
206+
auto* jsContext = jscContextGetJSContext(jsc_value_get_context(value));
207+
JSC::ExecState* exec = toJS(jsContext);
208+
JSC::JSGlobalObject* globalObject = exec->lexicalGlobalObject();
209+
auto& owner = weakValueHandleOwner();
210+
JSC::Weak<JSC::JSGlobalObject> weak(globalObject, &owner, weakValue);
211+
priv->globalObject.swap(weak);
212+
priv->lock = &exec->vm().apiLock();
213+
214+
JSC::JSValue jsValue = toJS(exec, jscValueGetJSValue(value));
215+
if (jsValue.isObject())
216+
priv->weakValueRef.setObject(JSC::jsCast<JSC::JSObject*>(jsValue.asCell()), owner, weakValue);
217+
else if (jsValue.isString())
218+
priv->weakValueRef.setString(JSC::jsCast<JSC::JSString*>(jsValue.asCell()), owner, weakValue);
219+
else
220+
priv->weakValueRef.setPrimitive(jsValue);
221+
}
222+
223+
static void jscWeakValueSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec)
224+
{
225+
switch (propID) {
226+
case PROP_VALUE:
227+
jscWeakValueInitialize(JSC_WEAK_VALUE(object), JSC_VALUE(g_value_get_object(value)));
228+
break;
229+
default:
230+
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
231+
}
232+
}
233+
234+
static void jscWeakValueDispose(GObject* object)
235+
{
236+
JSCWeakValue* weakValue = JSC_WEAK_VALUE(object);
237+
jscWeakValueClear(weakValue);
238+
239+
G_OBJECT_CLASS(jsc_weak_value_parent_class)->dispose(object);
240+
}
241+
242+
static void jsc_weak_value_class_init(JSCWeakValueClass* klass)
243+
{
244+
GObjectClass* objClass = G_OBJECT_CLASS(klass);
245+
objClass->set_property = jscWeakValueSetProperty;
246+
objClass->dispose = jscWeakValueDispose;
247+
248+
/**
249+
* JSCWeakValue:value:
250+
*
251+
* The #JSCValue referencing the JavaScript value.
252+
*/
253+
g_object_class_install_property(objClass,
254+
PROP_VALUE,
255+
g_param_spec_object(
256+
"value",
257+
"JSCValue",
258+
"JSC Value",
259+
JSC_TYPE_VALUE,
260+
static_cast<GParamFlags>(WEBKIT_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
261+
262+
/**
263+
* JSCWeakValue::cleared:
264+
* @weak_value: the #JSCWeakValue
265+
*
266+
* This signal is emitted when the JavaScript value is destroyed.
267+
*/
268+
signals[CLEARED] = g_signal_new(
269+
"cleared",
270+
G_TYPE_FROM_CLASS(klass),
271+
G_SIGNAL_RUN_LAST,
272+
0, nullptr, nullptr,
273+
g_cclosure_marshal_generic,
274+
G_TYPE_NONE, 0,
275+
G_TYPE_NONE);
276+
}
277+
278+
/**
279+
* jsc_weak_value_new:
280+
* @value: a #JSCValue
281+
*
282+
* Create a new #JSCWeakValue for the JavaScript value referenced by @value.
283+
*
284+
* Returns: (transfer full): a new #JSCWeakValue
285+
*/
286+
JSCWeakValue* jsc_weak_value_new(JSCValue* value)
287+
{
288+
g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
289+
290+
return JSC_WEAK_VALUE(g_object_new(JSC_TYPE_WEAK_VALUE, "value", value, nullptr));
291+
}
292+
293+
/**
294+
* jsc_weak_value_get_value:
295+
* @weak_value: a #JSCWeakValue
296+
*
297+
* Get a #JSCValue referencing the JavaScript value of @weak_value.
298+
*
299+
* Returns: (transfer full): a new #JSCValue or %NULL if @weak_value was cleared.
300+
*/
301+
JSCValue* jsc_weak_value_get_value(JSCWeakValue* weakValue)
302+
{
303+
g_return_val_if_fail(JSC_IS_WEAK_VALUE(weakValue), nullptr);
304+
305+
JSCWeakValuePrivate* priv = weakValue->priv;
306+
WTF::Locker<JSC::JSLock> locker(priv->lock.get());
307+
JSC::VM* vm = priv->lock->vm();
308+
if (!vm)
309+
return nullptr;
310+
311+
JSC::JSLockHolder apiLocker(vm);
312+
if (!priv->globalObject || priv->weakValueRef.isClear())
313+
return nullptr;
314+
315+
JSC::JSValue value;
316+
if (priv->weakValueRef.isPrimitive())
317+
value = priv->weakValueRef.primitive();
318+
else if (priv->weakValueRef.isString())
319+
value = priv->weakValueRef.string();
320+
else
321+
value = priv->weakValueRef.object();
322+
323+
JSC::ExecState* exec = priv->globalObject->globalExec();
324+
GRefPtr<JSCContext> context = jscContextGetOrCreate(toGlobalRef(exec));
325+
return jscContextGetOrCreateValue(context.get(), toRef(exec, value)).leakRef();
326+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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+
#if !defined(__JSC_H_INSIDE__) && !defined(JSC_COMPILATION) && !defined(WEBKIT2_COMPILATION)
21+
#error "Only <jsc/jsc.h> can be included directly."
22+
#endif
23+
24+
#ifndef JSCWeakValue_h
25+
#define JSCWeakValue_h
26+
27+
#include <glib-object.h>
28+
#include <jsc/JSCDefines.h>
29+
#include <jsc/JSCValue.h>
30+
31+
G_BEGIN_DECLS
32+
33+
#define JSC_TYPE_WEAK_VALUE (jsc_weak_value_get_type())
34+
#define JSC_WEAK_VALUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), JSC_TYPE_WEAK_VALUE, JSCWeakValue))
35+
#define JSC_IS_WEAK_VALUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), JSC_TYPE_WEAK_VALUE))
36+
#define JSC_WEAK_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), JSC_TYPE_WEAK_VALUE, JSCWeakValueClass))
37+
#define JSC_IS_WEAK_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JSC_TYPE_WEAK_VALUE))
38+
#define JSC_WEAK_VALUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), JSC_TYPE_WEAK_VALUE, JSCWeakValueClass))
39+
40+
typedef struct _JSCWeakValue JSCWeakValue;
41+
typedef struct _JSCWeakValueClass JSCWeakValueClass;
42+
typedef struct _JSCWeakValuePrivate JSCWeakValuePrivate;
43+
44+
struct _JSCWeakValue {
45+
GObject parent;
46+
47+
/*< private >*/
48+
JSCWeakValuePrivate *priv;
49+
};
50+
51+
struct _JSCWeakValueClass {
52+
GObjectClass parent_class;
53+
54+
void (*_jsc_reserved0) (void);
55+
void (*_jsc_reserved1) (void);
56+
void (*_jsc_reserved2) (void);
57+
void (*_jsc_reserved3) (void);
58+
};
59+
60+
JSC_API GType
61+
jsc_weak_value_get_type (void);
62+
63+
JSC_API JSCWeakValue *
64+
jsc_weak_value_new (JSCValue *value);
65+
66+
JSC_API JSCValue *
67+
jsc_weak_value_get_value (JSCWeakValue *weak_value);
68+
69+
G_END_DECLS
70+
71+
#endif /* JSCWeakValue_h */

0 commit comments

Comments
 (0)