[GLIB] Add JSCWeakValue to JavaScriptCore GLib API
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Mar 2018 06:21:57 +0000 (06:21 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Mar 2018 06:21:57 +0000 (06:21 +0000)
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):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230024 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/API/glib/JSCWeakValue.cpp [new file with mode: 0644]
Source/JavaScriptCore/API/glib/JSCWeakValue.h [new file with mode: 0644]
Source/JavaScriptCore/API/glib/docs/jsc-glib-4.0-sections.txt
Source/JavaScriptCore/API/glib/docs/jsc-glib-docs.sgml
Source/JavaScriptCore/API/glib/jsc.h
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/GLib.cmake
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp

diff --git a/Source/JavaScriptCore/API/glib/JSCWeakValue.cpp b/Source/JavaScriptCore/API/glib/JSCWeakValue.cpp
new file mode 100644 (file)
index 0000000..29cd340
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2018 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "JSCWeakValue.h"
+
+#include "APICast.h"
+#include "JSCContextPrivate.h"
+#include "JSCInlines.h"
+#include "JSCValuePrivate.h"
+#include "Weak.h"
+#include "WeakHandleOwner.h"
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/glib/WTFGType.h>
+
+/**
+ * SECTION: JSCWeakValue
+ * @short_description: JavaScript weak value
+ * @title: JSCWeakValue
+ * @see_also: JSCValue
+ *
+ * JSCWeakValue represents a weak reference to a value in a #JSCContext. It can be used
+ * to keep a reference to a JavaScript value without protecting it from being garbage
+ * collected and without referencing the #JSCContext either.
+ */
+
+enum {
+    PROP_0,
+
+    PROP_VALUE,
+};
+
+enum {
+    CLEARED,
+
+    LAST_SIGNAL
+};
+
+// FIXME: move this to its own file to be shared by GLib and Objc APIs.
+class WeakValueRef {
+public:
+    ~WeakValueRef()
+    {
+        clear();
+    }
+
+    void clear()
+    {
+        switch (m_tag) {
+        case WeakTypeTag::NotSet:
+            return;
+        case WeakTypeTag::Primitive:
+            u.m_primitive = JSC::JSValue();
+            return;
+        case WeakTypeTag::Object:
+            u.m_object.clear();
+            return;
+        case WeakTypeTag::String:
+            u.m_string.clear();
+            return;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+
+    bool isClear() const
+    {
+        switch (m_tag) {
+        case WeakTypeTag::NotSet:
+            return true;
+        case WeakTypeTag::Primitive:
+            return !u.m_primitive;
+        case WeakTypeTag::Object:
+            return !u.m_object;
+        case WeakTypeTag::String:
+            return !u.m_string;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+
+    bool isSet() const { return m_tag != WeakTypeTag::NotSet; }
+    bool isPrimitive() const { return m_tag == WeakTypeTag::Primitive; }
+    bool isObject() const { return m_tag == WeakTypeTag::Object; }
+    bool isString() const { return m_tag == WeakTypeTag::String; }
+
+    void setPrimitive(JSC::JSValue primitive)
+    {
+        ASSERT(!isSet());
+        ASSERT(!u.m_primitive);
+        ASSERT(primitive.isPrimitive());
+        m_tag = WeakTypeTag::Primitive;
+        u.m_primitive = primitive;
+    }
+
+    void setObject(JSC::JSObject* object, JSC::WeakHandleOwner& owner, void* context)
+    {
+        ASSERT(!isSet());
+        ASSERT(!u.m_object);
+        m_tag = WeakTypeTag::Object;
+        JSC::Weak<JSC::JSObject> weak(object, &owner, context);
+        u.m_object.swap(weak);
+    }
+
+    void setString(JSC::JSString* string, JSC::WeakHandleOwner& owner, void* context)
+    {
+        ASSERT(!isSet());
+        ASSERT(!u.m_object);
+        m_tag = WeakTypeTag::String;
+        JSC::Weak<JSC::JSString> weak(string, &owner, context);
+        u.m_string.swap(weak);
+    }
+
+    JSC::JSObject* object() const
+    {
+        ASSERT(isObject());
+        return u.m_object.get();
+    }
+
+    JSC::JSValue primitive() const
+    {
+        ASSERT(isPrimitive());
+        return u.m_primitive;
+    }
+
+    JSC::JSString* string() const
+    {
+        ASSERT(isString());
+        return u.m_string.get();
+    }
+
+private:
+    enum class WeakTypeTag { NotSet, Primitive, Object, String };
+
+    WeakTypeTag m_tag { WeakTypeTag::NotSet };
+
+    union WeakValueUnion {
+    public:
+        WeakValueUnion()
+            : m_primitive(JSC::JSValue())
+        {
+        }
+
+        ~WeakValueUnion()
+        {
+            ASSERT(!m_primitive);
+        }
+
+        JSC::JSValue m_primitive;
+        JSC::Weak<JSC::JSObject> m_object;
+        JSC::Weak<JSC::JSString> m_string;
+    } u;
+};
+
+struct _JSCWeakValuePrivate {
+    JSC::Weak<JSC::JSGlobalObject> globalObject;
+    RefPtr<JSC::JSLock> lock;
+    WeakValueRef weakValueRef;
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+WEBKIT_DEFINE_TYPE(JSCWeakValue, jsc_weak_value, G_TYPE_OBJECT)
+
+static void jscWeakValueClear(JSCWeakValue* weakValue)
+{
+    JSCWeakValuePrivate* priv = weakValue->priv;
+    priv->globalObject.clear();
+    priv->weakValueRef.clear();
+}
+
+class JSCWeakValueHandleOwner : public JSC::WeakHandleOwner {
+public:
+    void finalize(JSC::Handle<JSC::Unknown>, void* context) override
+    {
+        auto* weakValue = JSC_WEAK_VALUE(context);
+        jscWeakValueClear(weakValue);
+        g_signal_emit(weakValue, signals[CLEARED], 0, nullptr);
+    }
+};
+
+static JSCWeakValueHandleOwner& weakValueHandleOwner()
+{
+    static NeverDestroyed<JSCWeakValueHandleOwner> jscWeakValueHandleOwner;
+    return jscWeakValueHandleOwner;
+}
+
+static void jscWeakValueInitialize(JSCWeakValue* weakValue, JSCValue* value)
+{
+    JSCWeakValuePrivate* priv = weakValue->priv;
+    auto* jsContext = jscContextGetJSContext(jsc_value_get_context(value));
+    JSC::ExecState* exec = toJS(jsContext);
+    JSC::JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+    auto& owner = weakValueHandleOwner();
+    JSC::Weak<JSC::JSGlobalObject> weak(globalObject, &owner, weakValue);
+    priv->globalObject.swap(weak);
+    priv->lock = &exec->vm().apiLock();
+
+    JSC::JSValue jsValue = toJS(exec, jscValueGetJSValue(value));
+    if (jsValue.isObject())
+        priv->weakValueRef.setObject(JSC::jsCast<JSC::JSObject*>(jsValue.asCell()), owner, weakValue);
+    else if (jsValue.isString())
+        priv->weakValueRef.setString(JSC::jsCast<JSC::JSString*>(jsValue.asCell()), owner, weakValue);
+    else
+        priv->weakValueRef.setPrimitive(jsValue);
+}
+
+static void jscWeakValueSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec)
+{
+    switch (propID) {
+    case PROP_VALUE:
+        jscWeakValueInitialize(JSC_WEAK_VALUE(object), JSC_VALUE(g_value_get_object(value)));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
+    }
+}
+
+static void jscWeakValueDispose(GObject* object)
+{
+    JSCWeakValue* weakValue = JSC_WEAK_VALUE(object);
+    jscWeakValueClear(weakValue);
+
+    G_OBJECT_CLASS(jsc_weak_value_parent_class)->dispose(object);
+}
+
+static void jsc_weak_value_class_init(JSCWeakValueClass* klass)
+{
+    GObjectClass* objClass = G_OBJECT_CLASS(klass);
+    objClass->set_property = jscWeakValueSetProperty;
+    objClass->dispose = jscWeakValueDispose;
+
+    /**
+     * JSCWeakValue:value:
+     *
+     * The #JSCValue referencing the JavaScript value.
+     */
+    g_object_class_install_property(objClass,
+        PROP_VALUE,
+        g_param_spec_object(
+            "value",
+            "JSCValue",
+            "JSC Value",
+            JSC_TYPE_VALUE,
+            static_cast<GParamFlags>(WEBKIT_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
+
+    /**
+     * JSCWeakValue::cleared:
+     * @weak_value: the #JSCWeakValue
+     *
+     * This signal is emitted when the JavaScript value is destroyed.
+     */
+    signals[CLEARED] = g_signal_new(
+        "cleared",
+        G_TYPE_FROM_CLASS(klass),
+        G_SIGNAL_RUN_LAST,
+        0, nullptr, nullptr,
+        g_cclosure_marshal_generic,
+        G_TYPE_NONE, 0,
+        G_TYPE_NONE);
+}
+
+/**
+ * jsc_weak_value_new:
+ * @value: a #JSCValue
+ *
+ * Create a new #JSCWeakValue for the JavaScript value referenced by @value.
+ *
+ * Returns: (transfer full): a new #JSCWeakValue
+ */
+JSCWeakValue* jsc_weak_value_new(JSCValue* value)
+{
+    g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
+
+    return JSC_WEAK_VALUE(g_object_new(JSC_TYPE_WEAK_VALUE, "value", value, nullptr));
+}
+
+/**
+ * jsc_weak_value_get_value:
+ * @weak_value: a #JSCWeakValue
+ *
+ * Get a #JSCValue referencing the JavaScript value of @weak_value.
+ *
+ * Returns: (transfer full): a new #JSCValue or %NULL if @weak_value was cleared.
+ */
+JSCValue* jsc_weak_value_get_value(JSCWeakValue* weakValue)
+{
+    g_return_val_if_fail(JSC_IS_WEAK_VALUE(weakValue), nullptr);
+
+    JSCWeakValuePrivate* priv = weakValue->priv;
+    WTF::Locker<JSC::JSLock> locker(priv->lock.get());
+    JSC::VM* vm = priv->lock->vm();
+    if (!vm)
+        return nullptr;
+
+    JSC::JSLockHolder apiLocker(vm);
+    if (!priv->globalObject || priv->weakValueRef.isClear())
+        return nullptr;
+
+    JSC::JSValue value;
+    if (priv->weakValueRef.isPrimitive())
+        value = priv->weakValueRef.primitive();
+    else if (priv->weakValueRef.isString())
+        value = priv->weakValueRef.string();
+    else
+        value = priv->weakValueRef.object();
+
+    JSC::ExecState* exec = priv->globalObject->globalExec();
+    GRefPtr<JSCContext> context = jscContextGetOrCreate(toGlobalRef(exec));
+    return jscContextGetOrCreateValue(context.get(), toRef(exec, value)).leakRef();
+}
diff --git a/Source/JavaScriptCore/API/glib/JSCWeakValue.h b/Source/JavaScriptCore/API/glib/JSCWeakValue.h
new file mode 100644 (file)
index 0000000..eec0589
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#if !defined(__JSC_H_INSIDE__) && !defined(JSC_COMPILATION) && !defined(WEBKIT2_COMPILATION)
+#error "Only <jsc/jsc.h> can be included directly."
+#endif
+
+#ifndef JSCWeakValue_h
+#define JSCWeakValue_h
+
+#include <glib-object.h>
+#include <jsc/JSCDefines.h>
+#include <jsc/JSCValue.h>
+
+G_BEGIN_DECLS
+
+#define JSC_TYPE_WEAK_VALUE            (jsc_weak_value_get_type())
+#define JSC_WEAK_VALUE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), JSC_TYPE_WEAK_VALUE, JSCWeakValue))
+#define JSC_IS_WEAK_VALUE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), JSC_TYPE_WEAK_VALUE))
+#define JSC_WEAK_VALUE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  JSC_TYPE_WEAK_VALUE, JSCWeakValueClass))
+#define JSC_IS_WEAK_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  JSC_TYPE_WEAK_VALUE))
+#define JSC_WEAK_VALUE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  JSC_TYPE_WEAK_VALUE, JSCWeakValueClass))
+
+typedef struct _JSCWeakValue JSCWeakValue;
+typedef struct _JSCWeakValueClass JSCWeakValueClass;
+typedef struct _JSCWeakValuePrivate JSCWeakValuePrivate;
+
+struct _JSCWeakValue {
+    GObject parent;
+
+    /*< private >*/
+    JSCWeakValuePrivate *priv;
+};
+
+struct _JSCWeakValueClass {
+    GObjectClass parent_class;
+
+    void (*_jsc_reserved0) (void);
+    void (*_jsc_reserved1) (void);
+    void (*_jsc_reserved2) (void);
+    void (*_jsc_reserved3) (void);
+};
+
+JSC_API GType
+jsc_weak_value_get_type  (void);
+
+JSC_API JSCWeakValue *
+jsc_weak_value_new       (JSCValue     *value);
+
+JSC_API JSCValue *
+jsc_weak_value_get_value (JSCWeakValue *weak_value);
+
+G_END_DECLS
+
+#endif /* JSCWeakValue_h */
index 1f7353a..c2e3e3e 100644 (file)
@@ -109,6 +109,27 @@ jsc_value_get_type
 </SECTION>
 
 <SECTION>
+<FILE>JSCWeakValue</FILE>
+<TITLE>JSCWeakValue</TITLE>
+JSCWeakValue
+jsc_weak_value_new
+jsc_weak_value_get_value
+
+<SUBSECTION Standard>
+JSCWeakValueClass
+JSC_TYPE_WEAK_VALUE
+JSC_WEAK_VALUE
+JSC_IS_WEAK_VALUE
+JSC_WEAK_VALUE_CLASS
+JSC_IS_WEAK_VALUE_CLASS
+JSC_WEAK_VALUE_GET_CLASS
+
+<SUBSECTION Private>
+JSCWeakValuePrivate
+jsc_weak_value_get_type
+</SECTION>
+
+<SECTION>
 <FILE>JSCException</FILE>
 <TITLE>JSCException</TITLE>
 JSCException
index ac532b0..4853a10 100644 (file)
@@ -14,6 +14,7 @@
     <xi:include href="xml/JSCVirtualMachine.xml"/>
     <xi:include href="xml/JSCContext.xml"/>
     <xi:include href="xml/JSCValue.xml"/>
+    <xi:include href="xml/JSCWeakValue.xml"/>
     <xi:include href="xml/JSCException.xml"/>
     <xi:include href="xml/JSCClass.xml"/>
     <xi:include href="xml/JSCVersion.xml"/>
index b7ceaef..8a28452 100644 (file)
@@ -29,6 +29,7 @@
 #include <jsc/JSCValue.h>
 #include <jsc/JSCVersion.h>
 #include <jsc/JSCVirtualMachine.h>
+#include <jsc/JSCWeakValue.h>
 
 #include <jsc/JSCAutocleanups.h>
 
index bbc6279..b1b089a 100644 (file)
@@ -1,3 +1,41 @@
+2018-03-27  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GLIB] Add JSCWeakValue to JavaScriptCore GLib API
+        https://bugs.webkit.org/show_bug.cgi?id=184041
+
+        Reviewed by Michael Catanzaro.
+
+        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:
+
 2018-03-27  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [DFG] Remove unnecessary USE(JSVALUE32_64) / USE(JSVALUE64)
index 8f140d5..243ef3d 100644 (file)
@@ -10,6 +10,7 @@ list(APPEND JavaScriptCore_SOURCES
     API/glib/JSCValue.cpp
     API/glib/JSCVersion.cpp
     API/glib/JSCVirtualMachine.cpp
+    API/glib/JSCWeakValue.cpp
     API/glib/JSCWrapperMap.cpp
 )
 
@@ -28,6 +29,7 @@ set(JavaScriptCore_INSTALLED_HEADERS
     ${JAVASCRIPTCORE_DIR}/API/glib/JSCException.h
     ${JAVASCRIPTCORE_DIR}/API/glib/JSCValue.h
     ${JAVASCRIPTCORE_DIR}/API/glib/JSCVirtualMachine.h
+    ${JAVASCRIPTCORE_DIR}/API/glib/JSCWeakValue.h
     ${JAVASCRIPTCORE_DIR}/API/glib/jsc.h
 )
 
index 9a0e835..f70ca02 100644 (file)
@@ -1,3 +1,17 @@
+2018-03-27  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GLIB] Add JSCWeakValue to JavaScriptCore GLib API
+        https://bugs.webkit.org/show_bug.cgi?id=184041
+
+        Reviewed by Michael Catanzaro.
+
+        Add test case for JSCWeakValue.
+
+        * TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp:
+        (weakValueClearedCallback):
+        (testJSCWeakValue):
+        (main):
+
 2018-03-27  Zalan Bujtas  <zalan@apple.com>
 
         [LayoutReloaded] Start using window.collectTextRuns() to layout text lines in inline formatting context
index 0d667fa..729fd6c 100644 (file)
@@ -1748,6 +1748,183 @@ static void testJSCGarbageCollector()
     }
 }
 
+static void weakValueClearedCallback(JSCWeakValue* weakValue, bool* weakValueCleared)
+{
+    *weakValueCleared = true;
+    g_assert_null(jsc_weak_value_get_value(weakValue));
+}
+
+static void testJSCWeakValue()
+{
+    {
+        LeakChecker checker;
+        GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+        checker.watch(context.get());
+        ExceptionHandler exceptionHandler(context.get());
+
+        GRefPtr<JSCValue> object = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr));
+        checker.watch(object.get());
+
+        GRefPtr<JSCWeakValue> weak = adoptGRef(jsc_weak_value_new(object.get()));
+        checker.watch(weak.get());
+        bool weakValueCleared = false;
+        g_signal_connect(weak.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakValueCleared);
+
+        jsc_context_set_value(context.get(), "foo", object.get());
+        jscContextGarbageCollect(context.get());
+        g_assert_false(weakValueCleared);
+
+        GRefPtr<JSCValue> foo = adoptGRef(jsc_context_get_value(context.get(), "foo"));
+        checker.watch(foo.get());
+        g_assert_true(object.get() == foo.get());
+
+        GRefPtr<JSCValue> weakFoo = adoptGRef(jsc_weak_value_get_value(weak.get()));
+        checker.watch(weakFoo.get());
+        g_assert_true(foo.get() == weakFoo.get());
+
+        GRefPtr<JSCValue> undefinedValue = adoptGRef(jsc_value_new_undefined(context.get()));
+        checker.watch(undefinedValue.get());
+        jsc_context_set_value(context.get(), "foo", undefinedValue.get());
+        weakFoo = nullptr;
+        foo = nullptr;
+        object = nullptr;
+
+        // The value is still reachable, but unprotected.
+        g_assert_false(weakValueCleared);
+        weakFoo = adoptGRef(jsc_weak_value_get_value(weak.get()));
+        checker.watch(weakFoo.get());
+        g_assert_true(jsc_value_is_object(weakFoo.get()));
+        weakFoo = nullptr;
+
+        jscContextGarbageCollect(context.get());
+        g_assert_true(weakValueCleared);
+        g_assert_null(jsc_weak_value_get_value(weak.get()));
+    }
+
+    {
+        LeakChecker checker;
+        GRefPtr<JSCWeakValue> weakObject;
+        bool weakValueCleared = false;
+        {
+            GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+            checker.watch(context.get());
+            ExceptionHandler exceptionHandler(context.get());
+
+            GRefPtr<JSCValue> object = adoptGRef(jsc_context_evaluate(context.get(), "obj = {};"));
+            checker.watch(object.get());
+            g_assert_true(JSC_IS_VALUE(object.get()));
+            g_assert_true(jsc_value_is_object(object.get()));
+
+            weakObject = adoptGRef(jsc_weak_value_new(object.get()));
+            checker.watch(weakObject.get());
+            g_signal_connect(weakObject.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakValueCleared);
+
+            object = adoptGRef(jsc_context_evaluate(context.get(), "obj = null"));
+            checker.watch(object.get());
+            g_assert_false(weakValueCleared);
+        }
+
+        g_assert_true(weakValueCleared);
+        g_assert_null(jsc_weak_value_get_value(weakObject.get()));
+    }
+
+    {
+        LeakChecker checker;
+        GRefPtr<JSCWeakValue> weakObj;
+        bool weakObjValueCleared = false;
+        GRefPtr<JSCWeakValue> weakStr;
+        bool weakStrValueCleared = false;
+        GRefPtr<JSCWeakValue> weakPrimitive;
+        bool weakPrimitiveValueCleared = false;
+        {
+            GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+            checker.watch(context.get());
+            ExceptionHandler exceptionHandler(context.get());
+
+            GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "obj = { 'foo' : 'bar' }; str = 'Hello World'; primitive = 25;"));
+            checker.watch(result.get());
+
+            GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "obj"));
+            checker.watch(value.get());
+            weakObj = adoptGRef(jsc_weak_value_new(value.get()));
+            checker.watch(weakObj.get());
+            g_signal_connect(weakObj.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakObjValueCleared);
+
+            value = adoptGRef(jsc_context_get_value(context.get(), "str"));
+            checker.watch(value.get());
+            weakStr = adoptGRef(jsc_weak_value_new(value.get()));
+            checker.watch(weakStr.get());
+            g_signal_connect(weakStr.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakStrValueCleared);
+
+            value = adoptGRef(jsc_context_get_value(context.get(), "primitive"));
+            checker.watch(value.get());
+            weakPrimitive = adoptGRef(jsc_weak_value_new(value.get()));
+            checker.watch(weakPrimitive.get());
+            g_signal_connect(weakPrimitive.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakPrimitiveValueCleared);
+
+            value = nullptr;
+            jscContextGarbageCollect(context.get());
+            g_assert_false(weakObjValueCleared);
+            g_assert_false(weakStrValueCleared);
+            g_assert_false(weakPrimitiveValueCleared);
+
+            value = adoptGRef(jsc_weak_value_get_value(weakObj.get()));
+            checker.watch(value.get());
+            g_assert_true(jsc_value_is_object(value.get()));
+
+            value = adoptGRef(jsc_weak_value_get_value(weakStr.get()));
+            checker.watch(value.get());
+            g_assert_true(jsc_value_is_string(value.get()));
+
+            value = adoptGRef(jsc_weak_value_get_value(weakPrimitive.get()));
+            checker.watch(value.get());
+            g_assert_true(jsc_value_is_number(value.get()));
+            value = nullptr;
+
+            result = adoptGRef(jsc_context_evaluate(context.get(), "str = undefined"));
+            checker.watch(result.get());
+            jscContextGarbageCollect(context.get());
+
+            g_assert_true(weakStrValueCleared);
+            g_assert_false(weakObjValueCleared);
+            g_assert_false(weakPrimitiveValueCleared);
+            g_assert_null(jsc_weak_value_get_value(weakStr.get()));
+
+            result = adoptGRef(jsc_context_evaluate(context.get(), "f = undefined"));
+            checker.watch(result.get());
+            jscContextGarbageCollect(context.get());
+
+            // Non-string primitve values are not garbage collected, the weak value
+            // will be cleared when the global object is destroyed.
+            g_assert_false(weakPrimitiveValueCleared);
+            g_assert_false(weakObjValueCleared);
+            g_assert_true(weakStrValueCleared);
+
+            value = adoptGRef(jsc_weak_value_get_value(weakPrimitive.get()));
+            checker.watch(value.get());
+            g_assert_true(jsc_value_is_number(value.get()));
+            value = nullptr;
+
+            result = adoptGRef(jsc_context_evaluate(context.get(), "obj = undefined"));
+            checker.watch(result.get());
+            jscContextGarbageCollect(context.get());
+
+            g_assert_true(weakObjValueCleared);
+            g_assert_true(weakStrValueCleared);
+            g_assert_false(weakPrimitiveValueCleared);
+            g_assert_null(jsc_weak_value_get_value(weakObj.get()));
+            weakObjValueCleared = false;
+            weakStrValueCleared = false;
+        }
+
+        // Context is now destroyed, only the primitive value should be notified.
+        g_assert_true(weakPrimitiveValueCleared);
+        g_assert_false(weakObjValueCleared);
+        g_assert_false(weakStrValueCleared);
+        g_assert_null(jsc_weak_value_get_value(weakPrimitive.get()));
+    }
+}
+
 static void testsJSCVirtualMachine()
 {
     {
@@ -1860,6 +2037,7 @@ int main(int argc, char** argv)
     g_test_add_func("/jsc/exceptions", testJSCExceptions);
     g_test_add_func("/jsc/promises", testJSCPromises);
     g_test_add_func("/jsc/garbage-collector", testJSCGarbageCollector);
+    g_test_add_func("/jsc/weak-value", testJSCWeakValue);
     g_test_add_func("/jsc/vm", testsJSCVirtualMachine);
 #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
     g_test_add_func("/jsc/autocleanups", testsJSCAutocleanups);