[gjs/mozjs102: 47/62] js: Replace class private pointers with reserved slots




commit 854d19d7b1db23b3646d31a2ec0c836fdc73baaf
Author: Philip Chimento <philip chimento gmail com>
Date:   Sat Aug 6 16:19:44 2022 -0700

    js: Replace class private pointers with reserved slots
    
    JS::GetPrivate() and JS::SetPrivate() are going away in SpiderMonkey
    102. The replacement is to stuff the pointer into a JS::PrivateValue and
    store it in an object's reserved slot. This is a change we can make prior
    to the switch.
    
    With SpiderMonkey 102, we are intended to use
    JS::GetMaybePtrFromReservedSlot() to retrieve the pointer. This function
    doesn't exist in SpiderMonkey 91, but it is a small inline function that
    we can open-code in jsapi-util.h.
    
    In most of the cases, we can encapsulate this access into three methods
    of CWrapperPointerOps: has_private(), init_private(), and unset_private().
    (Retrieving the pointer was already encapsulated by the various for_js()
    methods.) This provides better safety anyway, because in init_private()
    we can enforce that there was no pointer already set. We define that
    reserved slot 0 is always the private pointer.
    
    BoxedInstance already used slot 0 for something else, so that moves to
    slot 1.
    
    Based on Evan's commit from the mozjs102 branch.
    
    Co-authored-by: Evan Welsh <contact evanwelsh com>

 gi/boxed.cpp              |  6 ++---
 gi/boxed.h                |  4 ++++
 gi/cwrapper.h             | 56 ++++++++++++++++++++++++++++++++++++++++-------
 gi/function.cpp           |  8 +++----
 gi/fundamental.cpp        |  2 +-
 gi/gerror.cpp             |  2 +-
 gi/gtype.cpp              |  6 ++---
 gi/interface.cpp          |  2 +-
 gi/ns.cpp                 |  8 +++----
 gi/object.cpp             |  5 ++---
 gi/param.cpp              | 23 ++++++++++++-------
 gi/union.cpp              |  2 +-
 gi/wrapperutils.h         | 13 +++++------
 gjs/context.cpp           |  2 +-
 gjs/jsapi-util.h          | 14 ++++++++++++
 gjs/module.cpp            | 11 +++++++---
 modules/cairo-context.cpp |  3 +--
 modules/cairo-path.cpp    |  5 +----
 modules/cairo-pattern.cpp |  4 +++-
 modules/cairo-private.h   | 39 +++++++++++++++++++++------------
 modules/cairo-surface.cpp |  3 ++-
 test/gjs-test-rooting.cpp | 16 +++++++++-----
 22 files changed, 155 insertions(+), 79 deletions(-)
---
diff --git a/gi/boxed.cpp b/gi/boxed.cpp
index 89d97e3a5..c84cd4d46 100644
--- a/gi/boxed.cpp
+++ b/gi/boxed.cpp
@@ -507,7 +507,8 @@ bool BoxedInstance::get_nested_interface_object(
     /* We never actually read the reserved slot, but we put the parent object
      * into it to hold onto the parent object.
      */
-    JS::SetReservedSlot(obj, 0, JS::ObjectValue(*parent_obj));
+    JS::SetReservedSlot(obj, BoxedInstance::PARENT_OBJECT,
+                        JS::ObjectValue(*parent_obj));
 
     value.setObject(*obj);
     return true;
@@ -774,8 +775,7 @@ const struct JSClassOps BoxedBase::class_ops = {
  */
 const struct JSClass BoxedBase::klass = {
     "GObject_Boxed",
-    JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE |
-        JSCLASS_HAS_RESERVED_SLOTS(1),
+    JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_FOREGROUND_FINALIZE,
     &BoxedBase::class_ops
 };
 // clang-format on
diff --git a/gi/boxed.h b/gi/boxed.h
index ef2404a6f..be5e5e0bd 100644
--- a/gi/boxed.h
+++ b/gi/boxed.h
@@ -7,6 +7,7 @@
 
 #include <config.h>
 
+#include <stddef.h>  // for size_t
 #include <stdint.h>
 
 #include <memory>  // for unique_ptr
@@ -158,6 +159,9 @@ class BoxedInstance
     friend class GIWrapperBase<BoxedBase, BoxedPrototype, BoxedInstance>;
     friend class BoxedBase;  // for field_getter, etc.
 
+    // Reserved slots
+    static const size_t PARENT_OBJECT = 1;
+
     bool m_allocated_directly : 1;
     bool m_owning_ptr : 1;  // if set, the JS wrapper owns the C memory referred
                             // to by m_ptr.
diff --git a/gi/cwrapper.h b/gi/cwrapper.h
index 41095b48b..159479e15 100644
--- a/gi/cwrapper.h
+++ b/gi/cwrapper.h
@@ -7,6 +7,7 @@
 #include <config.h>
 
 #include <assert.h>
+#include <stddef.h>  // for size_t
 
 #include <string>
 #include <type_traits>  // for integral_constant
@@ -80,8 +81,10 @@ class CWrapperPointerOps {
      */
     [[nodiscard]] static Wrapped* for_js(JSContext* cx,
                                          JS::HandleObject wrapper) {
-        return static_cast<Wrapped*>(
-            JS_GetInstancePrivate(cx, wrapper, &Base::klass, nullptr));
+        if (!JS_InstanceOf(cx, wrapper, &Base::klass, nullptr))
+            return nullptr;
+
+        return Gjs::maybe_get_private<Wrapped>(wrapper, POINTER);
     }
 
     /*
@@ -134,7 +137,45 @@ class CWrapperPointerOps {
      * (It can return null if no private data has been set yet on the wrapper.)
      */
     [[nodiscard]] static Wrapped* for_js_nocheck(JSObject* wrapper) {
-        return static_cast<Wrapped*>(JS::GetPrivate(wrapper));
+        return Gjs::maybe_get_private<Wrapped>(wrapper, POINTER);
+    }
+
+ protected:
+    // The first reserved slot always stores the private pointer.
+    static const size_t POINTER = 0;
+
+    /*
+     * CWrapperPointerOps::has_private:
+     *
+     * Returns true if a private C pointer has already been associated with the
+     * wrapper object.
+     */
+    [[nodiscard]] static bool has_private(JSObject* wrapper) {
+        return !!Gjs::maybe_get_private<Wrapped>(wrapper, POINTER);
+    }
+
+    /*
+     * CWrapperPointerOps::init_private:
+     *
+     * Call this to initialize the wrapper object's private C pointer. The
+     * pointer should not be null. This should not be called twice, without
+     * calling unset_private() in between.
+     */
+    static void init_private(JSObject* wrapper, Wrapped* ptr) {
+        assert(!has_private(wrapper) &&
+               "wrapper object should be a fresh object");
+        assert(ptr && "private pointer should not be null, use unset_private");
+        JS::SetReservedSlot(wrapper, POINTER, JS::PrivateValue(ptr));
+    }
+
+    /*
+     * CWrapperPointerOps::unset_private:
+     *
+     * Call this to remove the wrapper object's private C pointer. After calling
+     * this, it's okay to call init_private() again.
+     */
+    static void unset_private(JSObject* wrapper) {
+        JS::SetReservedSlot(wrapper, POINTER, JS::UndefinedValue());
     }
 };
 
@@ -213,7 +254,7 @@ class CWrapper : public CWrapperPointerOps<Base, Wrapped> {
         Wrapped* priv = Base::constructor_impl(cx, args);
         if (!priv)
             return false;
-        JS::SetPrivate(object, priv);
+        CWrapperPointerOps<Base, Wrapped>::init_private(object, priv);
 
         args.rval().setObject(*object);
         return true;
@@ -257,8 +298,7 @@ class CWrapper : public CWrapperPointerOps<Base, Wrapped> {
 
         Base::finalize_impl(fop, priv);
 
-        // Remove the pointer from the JSObject
-        JS::SetPrivate(obj, nullptr);
+        CWrapperPointerOps<Base, Wrapped>::unset_private(obj);
     }
 
     static constexpr JSClassOps class_ops = {
@@ -494,8 +534,8 @@ class CWrapper : public CWrapperPointerOps<Base, Wrapped> {
         if (!wrapper)
             return nullptr;
 
-        assert(!JS::GetPrivate(wrapper));
-        JS::SetPrivate(wrapper, Base::copy_ptr(ptr));
+        CWrapperPointerOps<Base, Wrapped>::init_private(wrapper,
+                                                        Base::copy_ptr(ptr));
 
         debug_lifecycle(ptr, wrapper, "from_c_ptr");
 
diff --git a/gi/function.cpp b/gi/function.cpp
index 62e431d8f..af739ecd7 100644
--- a/gi/function.cpp
+++ b/gi/function.cpp
@@ -22,7 +22,6 @@
 #include <js/Class.h>
 #include <js/ErrorReport.h>  // for JS_ReportOutOfMemory
 #include <js/Exception.h>
-#include <js/Object.h>
 #include <js/PropertyDescriptor.h>  // for JSPROP_PERMANENT
 #include <js/PropertySpec.h>
 #include <js/Realm.h>  // for GetRealmFunctionPrototype
@@ -149,8 +148,8 @@ class Function : public CWrapper<Function> {
 
     static constexpr JSClass klass = {
         "GIRepositoryFunction",
-        JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, &Function::class_ops,
-        &Function::class_spec};
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
+        &Function::class_ops, &Function::class_spec};
 
  public:
     GJS_JSAPI_RETURN_CONVENTION
@@ -1326,8 +1325,7 @@ JSObject* Function::create(JSContext* context, GType gtype,
 
     auto* priv = new Function(info);
 
-    g_assert(!JS::GetPrivate(function) && "Function should be a fresh object");
-    JS::SetPrivate(function, priv);
+    Function::init_private(function, priv);
 
     debug_lifecycle(function, priv, "Constructor");
 
diff --git a/gi/fundamental.cpp b/gi/fundamental.cpp
index dcaf3914d..56cf2dc05 100644
--- a/gi/fundamental.cpp
+++ b/gi/fundamental.cpp
@@ -250,7 +250,7 @@ const struct JSClassOps FundamentalBase::class_ops = {
 
 const struct JSClass FundamentalBase::klass = {
     "GFundamental_Object",
-    JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
     &FundamentalBase::class_ops
 };
 // clang-format on
diff --git a/gi/gerror.cpp b/gi/gerror.cpp
index 5aba318f8..ba70005b2 100644
--- a/gi/gerror.cpp
+++ b/gi/gerror.cpp
@@ -193,7 +193,7 @@ const struct JSClassOps ErrorBase::class_ops = {
 
 const struct JSClass ErrorBase::klass = {
     "GLib_Error",
-    JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
     &ErrorBase::class_ops
 };
 
diff --git a/gi/gtype.cpp b/gi/gtype.cpp
index 7e4c39f7e..3b0b8b801 100644
--- a/gi/gtype.cpp
+++ b/gi/gtype.cpp
@@ -12,7 +12,6 @@
 #include <js/CallArgs.h>
 #include <js/Class.h>
 #include <js/GCHashTable.h>         // for WeakCache
-#include <js/Object.h>
 #include <js/PropertyDescriptor.h>  // for JSPROP_PERMANENT
 #include <js/PropertySpec.h>
 #include <js/RootingAPI.h>
@@ -98,7 +97,8 @@ class GTypeObj : public CWrapper<GTypeObj, void> {
         js::ClassSpec::DontDefineConstructor};
 
     static constexpr JSClass klass = {
-        "GIRepositoryGType", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,
+        "GIRepositoryGType",
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
         &GTypeObj::class_ops, &GTypeObj::class_spec};
 
     GJS_JSAPI_RETURN_CONVENTION
@@ -172,7 +172,7 @@ class GTypeObj : public CWrapper<GTypeObj, void> {
         if (!gtype_wrapper)
             return nullptr;
 
-        JS::SetPrivate(gtype_wrapper, GSIZE_TO_POINTER(gtype));
+        GTypeObj::init_private(gtype_wrapper, GSIZE_TO_POINTER(gtype));
 
         gjs->gtype_table().put(gtype, gtype_wrapper);
 
diff --git a/gi/interface.cpp b/gi/interface.cpp
index c08294523..4a94dbda0 100644
--- a/gi/interface.cpp
+++ b/gi/interface.cpp
@@ -178,7 +178,7 @@ const struct JSClassOps InterfaceBase::class_ops = {
 
 const struct JSClass InterfaceBase::klass = {
     "GObject_Interface",
-    JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
     &InterfaceBase::class_ops
 };
 
diff --git a/gi/ns.cpp b/gi/ns.cpp
index 5d59b948e..1b546c3d3 100644
--- a/gi/ns.cpp
+++ b/gi/ns.cpp
@@ -12,7 +12,6 @@
 #include <js/ComparisonOperators.h>
 #include <js/ErrorReport.h>  // for JS_ReportOutOfMemory
 #include <js/Id.h>
-#include <js/Object.h>
 #include <js/PropertyDescriptor.h>  // for JSPROP_READONLY
 #include <js/PropertySpec.h>
 #include <js/RootingAPI.h>
@@ -203,8 +202,8 @@ class Ns : private GjsAutoChar, public CWrapper<Ns> {
 
     static constexpr JSClass klass = {
         "GIRepositoryNamespace",
-        JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE, &Ns::class_ops,
-        &Ns::class_spec};
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
+        &Ns::class_ops, &Ns::class_spec};
 
  public:
     GJS_JSAPI_RETURN_CONVENTION
@@ -219,8 +218,7 @@ class Ns : private GjsAutoChar, public CWrapper<Ns> {
             return nullptr;
 
         auto* priv = new Ns(ns_name);
-        g_assert(!JS::GetPrivate(ns));
-        JS::SetPrivate(ns, priv);
+        Ns::init_private(ns, priv);
 
         gjs_debug_lifecycle(GJS_DEBUG_GNAMESPACE,
                             "ns constructor, obj %p priv %p", ns.get(), priv);
diff --git a/gi/object.cpp b/gi/object.cpp
index 5bf88aa28..6cf97c723 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -31,7 +31,6 @@
 #include <js/GCVector.h>            // for MutableWrappedPtrOperations
 #include <js/HeapAPI.h>
 #include <js/MemoryFunctions.h>     // for AddAssociatedMemory, RemoveAssoci...
-#include <js/Object.h>
 #include <js/PropertyDescriptor.h>  // for JSPROP_PERMANENT, JSPROP_READONLY
 #include <js/String.h>
 #include <js/Symbol.h>
@@ -2539,7 +2538,7 @@ const struct JSClassOps ObjectBase::class_ops = {
 
 const struct JSClass ObjectBase::klass = {
     "GObject_Object",
-    JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
     &ObjectBase::class_ops
 };
 
@@ -2726,7 +2725,7 @@ ObjectInstance* ObjectInstance::new_for_gobject(JSContext* cx, GObject* gobj) {
 
     ObjectInstance* priv = new ObjectInstance(prototype, obj);
 
-    JS::SetPrivate(obj, priv);
+    ObjectBase::init_private(obj, priv);
 
     g_object_ref_sink(gobj);
     priv->associate_js_gobject(cx, obj, gobj);
diff --git a/gi/param.cpp b/gi/param.cpp
index 7ad313346..5f419db99 100644
--- a/gi/param.cpp
+++ b/gi/param.cpp
@@ -4,6 +4,8 @@
 
 #include <config.h>
 
+#include <stddef.h>  // for size_t
+
 #include <girepository.h>
 #include <glib.h>
 
@@ -13,6 +15,7 @@
 #include <js/RootingAPI.h>
 #include <js/TypeDecls.h>
 #include <js/Utility.h>  // for UniqueChars
+#include <js/Value.h>
 #include <jsapi.h>       // for JS_GetPropertyById
 #include <jspubtd.h>     // for JSProto_TypeError
 
@@ -31,6 +34,9 @@
 
 extern struct JSClass gjs_param_class;
 
+// Reserved slots
+static const size_t POINTER = 0;
+
 struct Param : GjsAutoParam {
     explicit Param(GParamSpec* param)
         : GjsAutoParam(param, GjsAutoTakeOwnership()) {}
@@ -38,8 +44,10 @@ struct Param : GjsAutoParam {
 
 [[nodiscard]] static GParamSpec* param_value(JSContext* cx,
                                              JS::HandleObject obj) {
-    auto* priv = static_cast<Param*>(
-        JS_GetInstancePrivate(cx, obj, &gjs_param_class, nullptr));
+    if (!JS_InstanceOf(cx, obj, &gjs_param_class, nullptr))
+        return nullptr;
+
+    auto* priv = Gjs::maybe_get_private<Param>(obj, POINTER);
     return priv ? priv->get() : nullptr;
 }
 
@@ -115,14 +123,14 @@ static bool gjs_param_constructor(JSContext* cx, unsigned argc, JS::Value* vp) {
 }
 
 static void param_finalize(JSFreeOp*, JSObject* obj) {
-    Param* priv = static_cast<Param*>(JS::GetPrivate(obj));
+    Param* priv = Gjs::maybe_get_private<Param>(obj, POINTER);
     gjs_debug_lifecycle(GJS_DEBUG_GPARAM, "finalize, obj %p priv %p", obj,
                         priv);
     if (!priv)
         return; /* wrong class? */
 
     GJS_DEC_COUNTER(param);
-    JS::SetPrivate(obj, nullptr);
+    JS::SetReservedSlot(obj, POINTER, JS::UndefinedValue());
     delete priv;
 }
 
@@ -141,9 +149,8 @@ static const struct JSClassOps gjs_param_class_ops = {
 
 struct JSClass gjs_param_class = {
     "GObject_ParamSpec",
-    JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
-    &gjs_param_class_ops
-};
+    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
+    &gjs_param_class_ops};
 
 GJS_JSAPI_RETURN_CONVENTION
 static JSObject*
@@ -221,7 +228,7 @@ gjs_param_from_g_param(JSContext    *context,
 
     GJS_INC_COUNTER(param);
     auto* priv = new Param(gparam);
-    JS::SetPrivate(obj, priv);
+    JS::SetReservedSlot(obj, POINTER, JS::PrivateValue(priv));
 
     gjs_debug(GJS_DEBUG_GPARAM,
               "JSObject created with param instance %p type %s", gparam,
diff --git a/gi/union.cpp b/gi/union.cpp
index 5096f7a56..b8356c9e3 100644
--- a/gi/union.cpp
+++ b/gi/union.cpp
@@ -148,7 +148,7 @@ const struct JSClassOps UnionBase::class_ops = {
 
 const struct JSClass UnionBase::klass = {
     "GObject_Union",
-    JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,
+    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
     &UnionBase::class_ops
 };
 // clang-format on
diff --git a/gi/wrapperutils.h b/gi/wrapperutils.h
index aa50cdd05..0b87f7cdf 100644
--- a/gi/wrapperutils.h
+++ b/gi/wrapperutils.h
@@ -408,8 +408,7 @@ class GIWrapperBase : public CWrapperPointerOps<Base> {
         else
             priv->to_instance()->finalize_impl(fop, obj);
 
-        // Remove the pointer from the JSObject
-        JS::SetPrivate(obj, nullptr);
+        Base::unset_private(obj);
     }
 
     /*
@@ -902,7 +901,7 @@ class GIWrapperPrototype : public Base {
         // a garbage collection or error happens subsequently, then this object
         // might be traced and we would end up dereferencing a null pointer.
         Prototype* proto = priv.release();
-        JS::SetPrivate(prototype, proto);
+        Prototype::init_private(prototype, proto);
 
         if (!gjs_wrapper_define_gtype_prop(cx, constructor, gtype))
             return nullptr;
@@ -949,7 +948,7 @@ class GIWrapperPrototype : public Base {
             return nullptr;
 
         Prototype* proto = priv.release();
-        JS::SetPrivate(prototype, proto);
+        Prototype::init_private(prototype, proto);
 
         if (!proto->define_static_methods(cx, constructor))
             return nullptr;
@@ -1060,24 +1059,22 @@ class GIWrapperInstance : public Base {
      */
     [[nodiscard]] static Instance* new_for_js_object(JSContext* cx,
                                                      JS::HandleObject obj) {
-        g_assert(!JS::GetPrivate(obj));
         Prototype* prototype = Prototype::for_js_prototype(cx, obj);
         auto* priv = new Instance(prototype, obj);
 
         // Init the private variable before we do anything else. If a garbage
         // collection happens when calling the constructor, then this object
         // might be traced and we would end up dereferencing a null pointer.
-        JS::SetPrivate(obj, priv);
+        Instance::init_private(obj, priv);
 
         return priv;
     }
 
     [[nodiscard]] static Instance* new_for_js_object(Prototype* prototype,
                                                      JS::HandleObject obj) {
-        g_assert(!JS::GetPrivate(obj));
         auto* priv = new Instance(prototype, obj);
 
-        JS::SetPrivate(obj, priv);
+        Instance::init_private(obj, priv);
 
         return priv;
     }
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 4e22825d0..a9dde93db 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -433,7 +433,7 @@ void GjsContextPrivate::dispose(void) {
         m_gtype_table->clear();
 
         /* Do a full GC here before tearing down, since once we do
-         * that we may not have the JS::GetPrivate() to access the
+         * that we may not have the JS::GetReservedSlot(, 0) to access the
          * context
          */
         gjs_debug(GJS_DEBUG_CONTEXT, "Final triggered GC");
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 0724181e3..a891fa42c 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -26,8 +26,10 @@
 #include <js/GCAPI.h>
 #include <js/GCPolicyAPI.h>  // for IgnoreGCPolicy
 #include <js/Id.h>
+#include <js/Object.h>  // for GetReservedSlot
 #include <js/TypeDecls.h>
 #include <js/Utility.h>  // for UniqueChars
+#include <js/Value.h>
 #include <jspubtd.h>     // for JSProtoKey
 
 #include "gjs/macros.h"
@@ -634,6 +636,18 @@ template <typename T>
     return true;
 }
 
+/**
+ * Helper function, backported from future SpiderMonkey's
+ * JS::GetMaybePtrFromReservedSlot(), to get the pointer value (or nullptr if
+ * not set) from an object's reserved slot. The slot must contain either a
+ * JS::PrivateValue(T*) or JS::UndefinedValue.
+ */
+template <typename T>
+inline T* maybe_get_private(JSObject* obj, size_t slot) {
+    JS::Value v = JS::GetReservedSlot(obj, slot);
+    return v.isUndefined() ? nullptr : static_cast<T*>(v.toPrivate());
+}
+
 }  // namespace Gjs
 
 [[nodiscard]] const char* gjs_explain_gc_reason(JS::GCReason reason);
diff --git a/gjs/module.cpp b/gjs/module.cpp
index bcff321af..c8d4b21c0 100644
--- a/gjs/module.cpp
+++ b/gjs/module.cpp
@@ -57,6 +57,9 @@ union Utf8Unit;
 class GjsScriptModule {
     char *m_name;
 
+    // Reserved slots
+    static const size_t POINTER = 0;
+
     GjsScriptModule(const char* name) {
         m_name = g_strdup(name);
         GJS_INC_COUNTER(module);
@@ -73,13 +76,15 @@ class GjsScriptModule {
     /* Private data accessors */
 
     [[nodiscard]] static inline GjsScriptModule* priv(JSObject* module) {
-        return static_cast<GjsScriptModule*>(JS::GetPrivate(module));
+        return Gjs::maybe_get_private<GjsScriptModule>(
+            module, GjsScriptModule::POINTER);
     }
 
     /* Creates a JS module object. Use instead of the class's constructor */
     [[nodiscard]] static JSObject* create(JSContext* cx, const char* name) {
         JSObject* module = JS_NewObject(cx, &GjsScriptModule::klass);
-        JS::SetPrivate(module, new GjsScriptModule(name));
+        JS::SetReservedSlot(module, GjsScriptModule::POINTER,
+                            JS::PrivateValue(new GjsScriptModule(name)));
         return module;
     }
 
@@ -225,7 +230,7 @@ class GjsScriptModule {
 
     static constexpr JSClass klass = {
         "GjsScriptModule",
-        JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &GjsScriptModule::class_ops,
     };
 
diff --git a/modules/cairo-context.cpp b/modules/cairo-context.cpp
index fd6429870..356ba4562 100644
--- a/modules/cairo-context.cpp
+++ b/modules/cairo-context.cpp
@@ -13,7 +13,6 @@
 #include <js/Array.h>  // for JS::NewArrayObject
 #include <js/CallArgs.h>
 #include <js/Conversions.h>
-#include <js/Object.h>
 #include <js/PropertyDescriptor.h>  // for JSPROP_READONLY
 #include <js/PropertySpec.h>
 #include <js/RootingAPI.h>
@@ -357,7 +356,7 @@ bool CairoContext::dispose(JSContext* context, unsigned argc, JS::Value* vp) {
     _GJS_CAIRO_CONTEXT_GET_PRIV_CR_CHECKED(context, argc, vp, rec, obj);
 
     cairo_destroy(cr);
-    JS::SetPrivate(obj, nullptr);
+    CairoContext::unset_private(obj);
 
     rec.rval().setUndefined();
     return true;
diff --git a/modules/cairo-path.cpp b/modules/cairo-path.cpp
index 76a879dfa..415fcf2ef 100644
--- a/modules/cairo-path.cpp
+++ b/modules/cairo-path.cpp
@@ -6,9 +6,7 @@
 #include <config.h>
 
 #include <cairo.h>
-#include <glib.h>  // for g_assert
 
-#include <js/Object.h>
 #include <js/PropertyDescriptor.h>  // for JSPROP_READONLY
 #include <js/PropertySpec.h>
 #include <js/RootingAPI.h>
@@ -38,8 +36,7 @@ JSObject* CairoPath::take_c_ptr(JSContext* cx, cairo_path_t* ptr) {
     if (!wrapper)
         return nullptr;
 
-    g_assert(!JS::GetPrivate(wrapper));
-    JS::SetPrivate(wrapper, ptr);
+    CairoPath::init_private(wrapper, ptr);
 
     debug_lifecycle(ptr, wrapper, "take_c_ptr");
 
diff --git a/modules/cairo-pattern.cpp b/modules/cairo-pattern.cpp
index 1a6d07da3..a92541844 100644
--- a/modules/cairo-pattern.cpp
+++ b/modules/cairo-pattern.cpp
@@ -15,6 +15,7 @@
 #include <js/RootingAPI.h>
 #include <js/TypeDecls.h>
 
+#include "gi/cwrapper.h"
 #include "gjs/jsapi-class.h"
 #include "gjs/jsapi-util.h"
 #include "gjs/macros.h"
@@ -136,5 +137,6 @@ cairo_pattern_t* CairoPattern::for_js(JSContext* cx,
         return nullptr;
     }
 
-    return static_cast<cairo_pattern_t*>(JS::GetPrivate(pattern_wrapper));
+    return Gjs::maybe_get_private<cairo_pattern_t>(pattern_wrapper,
+                                                   CairoPattern::POINTER);
 }
diff --git a/modules/cairo-private.h b/modules/cairo-private.h
index a470a94a5..8e17c83e4 100644
--- a/modules/cairo-private.h
+++ b/modules/cairo-private.h
@@ -69,7 +69,7 @@ class CairoRegion : public CWrapper<CairoRegion, cairo_region_t> {
         CairoRegion::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "Region", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "Region", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoRegion::class_ops, &CairoRegion::class_spec};
 };
 
@@ -109,8 +109,11 @@ class CairoContext : public CWrapper<CairoContext, cairo_t> {
         CairoContext::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "Context", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "Context", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoContext::class_ops, &CairoContext::class_spec};
+
+    GJS_JSAPI_RETURN_CONVENTION
+    static bool dispose(JSContext* cx, unsigned argc, JS::Value* vp);
 };
 
 void gjs_cairo_context_init(void);
@@ -143,7 +146,7 @@ class CairoPath : public CWrapper<CairoPath, cairo_path_t> {
         nullptr,  // finishInit
     };
     static constexpr JSClass klass = {
-        "Path", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "Path", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoPath::class_ops, &CairoPath::class_spec};
 
  public:
@@ -185,7 +188,7 @@ class CairoSurface : public CWrapper<CairoSurface, cairo_surface_t> {
         &CairoSurface::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "Surface", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "Surface", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoSurface::class_ops, &CairoSurface::class_spec};
 
     static cairo_surface_t* copy_ptr(cairo_surface_t* surface) {
@@ -229,7 +232,8 @@ class CairoImageSurface : public CWrapper<CairoImageSurface, cairo_surface_t> {
         &CairoSurface::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "ImageSurface", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "ImageSurface",
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoSurface::class_ops, &CairoImageSurface::class_spec};
 
     static cairo_surface_t* copy_ptr(cairo_surface_t* surface) {
@@ -268,7 +272,8 @@ class CairoPSSurface : public CWrapper<CairoPSSurface, cairo_surface_t> {
         &CairoSurface::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "PSSurface", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "PSSurface",
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoSurface::class_ops, &CairoPSSurface::class_spec};
 
     static cairo_surface_t* copy_ptr(cairo_surface_t* surface) {
@@ -313,7 +318,8 @@ class CairoPDFSurface : public CWrapper<CairoPDFSurface, cairo_surface_t> {
         &CairoSurface::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "PDFSurface", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "PDFSurface",
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoSurface::class_ops, &CairoPDFSurface::class_spec};
 
     static cairo_surface_t* copy_ptr(cairo_surface_t* surface) {
@@ -358,7 +364,8 @@ class CairoSVGSurface : public CWrapper<CairoSVGSurface, cairo_surface_t> {
         &CairoSurface::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "SVGSurface", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "SVGSurface",
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoSurface::class_ops, &CairoSVGSurface::class_spec};
 
     static cairo_surface_t* copy_ptr(cairo_surface_t* surface) {
@@ -410,7 +417,7 @@ class CairoPattern : public CWrapper<CairoPattern, cairo_pattern_t> {
         &CairoPattern::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "Pattern", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "Pattern", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoPattern::class_ops, &CairoPattern::class_spec};
 
     static GType gtype() { return CAIRO_GOBJECT_TYPE_PATTERN; }
@@ -459,7 +466,7 @@ class CairoGradient : public CWrapper<CairoGradient, cairo_pattern_t> {
         &CairoPattern::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "Gradient", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "Gradient", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoPattern::class_ops, &CairoGradient::class_spec};
 
     static void finalize_impl(JSFreeOp*, cairo_pattern_t*) {}
@@ -490,7 +497,8 @@ class CairoLinearGradient
         &CairoPattern::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "LinearGradient", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "LinearGradient",
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoPattern::class_ops, &CairoLinearGradient::class_spec};
 
     static cairo_pattern_t* copy_ptr(cairo_pattern_t* pattern) {
@@ -529,7 +537,8 @@ class CairoRadialGradient
         &CairoPattern::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "RadialGradient", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "RadialGradient",
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoPattern::class_ops, &CairoRadialGradient::class_spec};
 
     static cairo_pattern_t* copy_ptr(cairo_pattern_t* pattern) {
@@ -568,7 +577,8 @@ class CairoSurfacePattern
         &CairoPattern::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "SurfacePattern", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "SurfacePattern",
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoPattern::class_ops, &CairoSurfacePattern::class_spec};
 
     static cairo_pattern_t* copy_ptr(cairo_pattern_t* pattern) {
@@ -605,7 +615,8 @@ class CairoSolidPattern : public CWrapper<CairoSolidPattern, cairo_pattern_t> {
         &CairoPattern::define_gtype_prop,
     };
     static constexpr JSClass klass = {
-        "SolidPattern", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
+        "SolidPattern",
+        JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_BACKGROUND_FINALIZE,
         &CairoPattern::class_ops, &CairoSolidPattern::class_spec};
 
     static cairo_pattern_t* copy_ptr(cairo_pattern_t* pattern) {
diff --git a/modules/cairo-surface.cpp b/modules/cairo-surface.cpp
index 710e8c1bf..51661c3cd 100644
--- a/modules/cairo-surface.cpp
+++ b/modules/cairo-surface.cpp
@@ -272,7 +272,8 @@ cairo_surface_t* CairoSurface::for_js(JSContext* cx,
         return nullptr;
     }
 
-    return static_cast<cairo_surface_t*>(JS::GetPrivate(surface_wrapper));
+    return Gjs::maybe_get_private<cairo_surface_t>(surface_wrapper,
+                                                   CairoSurface::POINTER);
 }
 
 [[nodiscard]] static bool surface_to_g_argument(
diff --git a/test/gjs-test-rooting.cpp b/test/gjs-test-rooting.cpp
index edf8656ba..077f45081 100644
--- a/test/gjs-test-rooting.cpp
+++ b/test/gjs-test-rooting.cpp
@@ -3,6 +3,8 @@
 
 #include <config.h>
 
+#include <stddef.h>  // for size_t
+
 #include <glib.h>
 
 #include <js/Class.h>
@@ -14,6 +16,7 @@
 
 #include "gjs/context-private.h"
 #include "gjs/jsapi-util-root.h"
+#include "gjs/jsapi-util.h"  // for maybe_get_private
 #include "test/gjs-test-utils.h"
 
 // COMPAT: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1553
@@ -26,6 +29,9 @@ static GMutex gc_lock;
 static GCond gc_finished;
 static int gc_counter;
 
+// TestObj reserved slots
+static const size_t POINTER = 0;
+
 #define PARENT(fx) ((GjsUnitTestFixture *)fx)
 struct GjsRootingFixture {
     GjsUnitTestFixture parent;
@@ -37,7 +43,7 @@ struct GjsRootingFixture {
 };
 
 static void test_obj_finalize(JSFreeOp*, JSObject* obj) {
-    bool* finalized_p = static_cast<bool*>(JS::GetPrivate(obj));
+    bool* finalized_p = Gjs::maybe_get_private<bool>(obj, POINTER);
     g_assert_false(*finalized_p);
     *finalized_p = true;
 }
@@ -52,16 +58,14 @@ static const JSClassOps test_obj_class_ops = {
     test_obj_finalize};
 
 static JSClass test_obj_class = {
-    "TestObj",
-    JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,
-    &test_obj_class_ops
-};
+    "TestObj", JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
+    &test_obj_class_ops};
 
 static JSObject *
 test_obj_new(GjsRootingFixture *fx)
 {
     JSObject *retval = JS_NewObject(PARENT(fx)->cx, &test_obj_class);
-    JS::SetPrivate(retval, &fx->finalized);
+    JS::SetReservedSlot(retval, POINTER, JS::PrivateValue(&fx->finalized));
     return retval;
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]