[gjs: 4/26] wrapperutils: Factor out to_c_ptr() and transfer_to_gi_argument()



commit bd80e62d5bc8fc3e3e6a6dea5dfa2dad7a7e41eb
Author: Philip Chimento <philip chimento gmail com>
Date:   Sun Mar 31 18:10:45 2019 -0700

    wrapperutils: Factor out to_c_ptr() and transfer_to_gi_argument()
    
    This adds two methods to GIWrapperBase:
    
    - to_c_ptr() takes the place of the gjs_g_foo_from_foo() functions (e.g.
      gjs_g_object_from_object().
    - transfer_to_gi_argument() implements the most common use case of
      to_c_ptr(), used in arg.cpp and function.cpp to transfer values from
      JS to GIArgument in order to pass into an introspected function, or
      to return from a callback implemented in JS.
    
    (In object.cpp we have to provide slightly modified versions of those
    methods, since we have to deal with the case where the JS wrapper is
    still alive but the GObject has been disposed.)
    
    This is part of the plan to gradually provide more typesafe, C++-style
    APIs for all of the GI wrapper classes. It also moves code out of the
    long confusing switch statements in arg.cpp and function.cpp.

 gi/arg.cpp         | 105 +++++++++++++----------------------------------------
 gi/boxed.cpp       |  22 +++++------
 gi/boxed.h         |   6 +--
 gi/function.cpp    |  55 +++++++++-------------------
 gi/fundamental.cpp |  30 ++-------------
 gi/fundamental.h   |   8 +---
 gi/gerror.cpp      |  54 ++++++++++++++++++---------
 gi/gerror.h        |  19 ++++++++--
 gi/object.cpp      |  60 +++++++++++++++++++++++++-----
 gi/object.h        |  19 ++++++++--
 gi/union.cpp       |  20 ++++------
 gi/union.h         |   7 ++--
 gi/value.cpp       |  23 +++++++-----
 gi/wrapperutils.h  |  73 +++++++++++++++++++++++++++++++++++++
 gjs/byteArray.cpp  |   4 +-
 modules/system.cpp |  11 +++---
 16 files changed, 285 insertions(+), 231 deletions(-)
---
diff --git a/gi/arg.cpp b/gi/arg.cpp
index ea56457d..b8039943 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -23,7 +23,6 @@
 
 #include <config.h>
 
-#include <mozilla/Unused.h>
 #include <cmath>
 #include <cstdlib>
 
@@ -1571,14 +1570,9 @@ gjs_value_to_g_argument(JSContext      *context,
             arg->v_pointer = NULL;
         } else if (value.isObject()) {
             JS::RootedObject obj(context, &value.toObject());
-            if (gjs_typecheck_gerror(context, obj, true)) {
-                arg->v_pointer = gjs_gerror_from_error(context, obj);
-
-                if (transfer != GI_TRANSFER_NOTHING)
-                    arg->v_pointer = g_error_copy ((const GError *) arg->v_pointer);
-            } else {
+            if (!ErrorBase::transfer_to_gi_argument(context, obj, arg,
+                                                    GI_DIRECTION_IN, transfer))
                 wrong = true;
-            }
         } else {
             wrong = true;
             report_type_mismatch = true;
@@ -1706,66 +1700,28 @@ gjs_value_to_g_argument(JSContext      *context,
                         JS_IsUint8Array(obj)) {
                         arg->v_pointer = gjs_byte_array_get_bytes(obj);
                     } else if (g_type_is_a(gtype, G_TYPE_ERROR)) {
-                        if (!gjs_typecheck_gerror(context, obj, true)) {
-                            arg->v_pointer = NULL;
+                        if (!ErrorBase::transfer_to_gi_argument(
+                                context, obj, arg, GI_DIRECTION_IN, transfer))
                             wrong = true;
-                        } else {
-                            arg->v_pointer = gjs_gerror_from_error(context, obj);
-                        }
                     } else {
-                        if (!gjs_typecheck_boxed(context, obj, interface_info,
-                                                 gtype, true)) {
-                            arg->v_pointer = NULL;
+                        if (!BoxedBase::transfer_to_gi_argument(
+                                context, obj, arg, GI_DIRECTION_IN, transfer,
+                                gtype, interface_info))
                             wrong = true;
-                        } else {
-                            arg->v_pointer = gjs_c_struct_from_boxed(context, obj);
-                        }
-                    }
-
-                    if (!wrong && transfer != GI_TRANSFER_NOTHING) {
-                        if (g_type_is_a(gtype, G_TYPE_BOXED))
-                            arg->v_pointer = g_boxed_copy (gtype, arg->v_pointer);
-                        else if (g_type_is_a(gtype, G_TYPE_VARIANT))
-                            g_variant_ref ((GVariant *) arg->v_pointer);
-                        else {
-                            gjs_throw(context,
-                                      "Can't transfer ownership of a structure type not registered as 
boxed");
-                            arg->v_pointer = NULL;
-                            wrong = true;
-                        }
                     }
 
                 } else if (interface_type == GI_INFO_TYPE_UNION) {
-                    if (gjs_typecheck_union(context, obj, interface_info, gtype, true)) {
-                        arg->v_pointer = gjs_c_union_from_union(context, obj);
-
-                        if (transfer != GI_TRANSFER_NOTHING) {
-                            if (g_type_is_a(gtype, G_TYPE_BOXED))
-                                arg->v_pointer = g_boxed_copy (gtype, arg->v_pointer);
-                            else {
-                                gjs_throw(context,
-                                          "Can't transfer ownership of a union type not registered as 
boxed");
-
-                                arg->v_pointer = NULL;
-                                wrong = true;
-                            }
-                        }
-                    } else {
-                        arg->v_pointer = NULL;
+                    if (!UnionBase::transfer_to_gi_argument(
+                            context, obj, arg, GI_DIRECTION_IN, transfer, gtype,
+                            interface_info))
                         wrong = true;
-                    }
 
                 } else if (gtype != G_TYPE_NONE) {
                     if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
-                        if (gjs_typecheck_object(context, obj, gtype, true)) {
-                            arg->v_pointer = gjs_g_object_from_object(context, obj);
-
-                            if (transfer != GI_TRANSFER_NOTHING)
-                                g_object_ref(G_OBJECT(arg->v_pointer));
-                        } else {
-                            arg->v_pointer = NULL;
+                        if (!ObjectBase::transfer_to_gi_argument(
+                                context, obj, arg, GI_DIRECTION_IN, transfer,
+                                gtype))
                             wrong = true;
-                        }
                     } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
                         if (gjs_typecheck_param(context, obj, gtype, true)) {
                             arg->v_pointer = gjs_g_param_from_param(context, obj);
@@ -1790,34 +1746,25 @@ gjs_value_to_g_argument(JSContext      *context,
                                       interface_type);
                         }
                     } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
-                        if (gjs_typecheck_fundamental(context, obj, gtype, true)) {
-                            arg->v_pointer = gjs_g_fundamental_from_object(context, obj);
-
-                            if (transfer != GI_TRANSFER_NOTHING)
-                                gjs_fundamental_ref(context, arg->v_pointer);
-                        } else {
-                            arg->v_pointer = NULL;
+                        if (!FundamentalBase::transfer_to_gi_argument(
+                                context, obj, arg, GI_DIRECTION_IN, transfer,
+                                gtype))
                             wrong = true;
-                        }
                     } else if (G_TYPE_IS_INTERFACE(gtype)) {
                         /* Could be a GObject interface that's missing a prerequisite, or could
                            be a fundamental */
                         if (gjs_typecheck_object(context, obj, gtype, false)) {
-                            arg->v_pointer = gjs_g_object_from_object(context, obj);
-
-                            if (transfer != GI_TRANSFER_NOTHING)
-                                g_object_ref(arg->v_pointer);
-                        } else if (gjs_typecheck_fundamental(context, obj, gtype, false)) {
-                            arg->v_pointer = gjs_g_fundamental_from_object(context, obj);
-
-                            if (transfer != GI_TRANSFER_NOTHING)
-                                gjs_fundamental_ref(context, arg->v_pointer);
+                            if (!ObjectBase::transfer_to_gi_argument(
+                                    context, obj, arg, GI_DIRECTION_IN,
+                                    transfer, gtype))
+                                wrong = true;
                         } else {
-                            /* Call again with throw=true to set the exception */
-                            mozilla::Unused << gjs_typecheck_object(
-                                context, obj, gtype, true);
-                            arg->v_pointer = NULL;
-                            wrong = true;
+                            // If this typecheck fails, then it's neither an
+                            // object nor a fundamental
+                            if (!FundamentalBase::transfer_to_gi_argument(
+                                    context, obj, arg, GI_DIRECTION_IN,
+                                    transfer, gtype))
+                                wrong = true;
                         }
                     } else {
                         gjs_throw(context, "Unhandled GType %s unpacking GArgument from Object",
diff --git a/gi/boxed.cpp b/gi/boxed.cpp
index 2b5114dd..bf2b926f 100644
--- a/gi/boxed.cpp
+++ b/gi/boxed.cpp
@@ -1035,18 +1035,16 @@ bool BoxedInstance::init_from_c_struct(JSContext* cx, void* gboxed) {
     return false;
 }
 
-void*
-gjs_c_struct_from_boxed(JSContext       *context,
-                        JS::HandleObject obj)
-{
-    if (!obj)
-        return NULL;
-
-    BoxedBase* priv = BoxedBase::for_js_typecheck(context, obj);
-    if (!priv || !priv->check_is_instance(context, "get a boxed pointer"))
-        return NULL;
-
-    return priv->to_instance()->ptr();
+void* BoxedInstance::copy_ptr(JSContext* cx, GType gtype, void* ptr) {
+    if (g_type_is_a(gtype, G_TYPE_BOXED))
+        return g_boxed_copy(gtype, ptr);
+    if (g_type_is_a(gtype, G_TYPE_VARIANT))
+        return g_variant_ref(static_cast<GVariant*>(ptr));
+
+    gjs_throw(cx,
+              "Can't transfer ownership of a structure type not registered as "
+              "boxed");
+    return nullptr;
 }
 
 bool
diff --git a/gi/boxed.h b/gi/boxed.h
index be269e49..4940b1ac 100644
--- a/gi/boxed.h
+++ b/gi/boxed.h
@@ -190,6 +190,9 @@ class BoxedInstance
                                      GIBaseInfo* interface_info,
                                      JS::HandleValue value);
 
+    GJS_JSAPI_RETURN_CONVENTION
+    static void* copy_ptr(JSContext* cx, GType gtype, void* ptr);
+
     // JS property accessors
 
     GJS_JSAPI_RETURN_CONVENTION
@@ -223,9 +226,6 @@ bool gjs_define_boxed_class(JSContext* cx, JS::HandleObject in_object,
                             GIStructInfo* info);
 
 GJS_JSAPI_RETURN_CONVENTION
-void*     gjs_c_struct_from_boxed      (JSContext             *context,
-                                        JS::HandleObject       obj);
-GJS_JSAPI_RETURN_CONVENTION
 JSObject* gjs_boxed_from_c_struct      (JSContext             *context,
                                         GIStructInfo          *info,
                                         void                  *gboxed,
diff --git a/gi/function.cpp b/gi/function.cpp
index ef708f0c..a2b086f9 100644
--- a/gi/function.cpp
+++ b/gi/function.cpp
@@ -633,12 +633,9 @@ gjs_fill_method_instance(JSContext       *context,
     if (type == GI_INFO_TYPE_STRUCT || type == GI_INFO_TYPE_BOXED) {
         /* GError must be special cased */
         if (g_type_is_a(gtype, G_TYPE_ERROR)) {
-            if (!gjs_typecheck_gerror(context, obj, true))
+            if (!ErrorBase::transfer_to_gi_argument(context, obj, out_arg,
+                                                    GI_DIRECTION_OUT, transfer))
                 return false;
-
-            out_arg->v_pointer = gjs_gerror_from_error(context, obj);
-            if (transfer == GI_TRANSFER_EVERYTHING)
-                out_arg->v_pointer = g_error_copy ((GError*) out_arg->v_pointer);
         } else if (type == GI_INFO_TYPE_STRUCT &&
                    g_struct_info_is_gtype_struct((GIStructInfo*) container)) {
             /* And so do GType structures */
@@ -666,36 +663,24 @@ gjs_fill_method_instance(JSContext       *context,
 
             out_arg->v_pointer = klass;
         } else {
-            if (!gjs_typecheck_boxed(context, obj, container, gtype, true))
+            if (!BoxedBase::transfer_to_gi_argument(context, obj, out_arg,
+                                                    GI_DIRECTION_OUT, transfer,
+                                                    gtype, container))
                 return false;
-
-            out_arg->v_pointer = gjs_c_struct_from_boxed(context, obj);
-            if (transfer == GI_TRANSFER_EVERYTHING) {
-                if (gtype != G_TYPE_NONE)
-                    out_arg->v_pointer = g_boxed_copy (gtype, out_arg->v_pointer);
-                else {
-                    gjs_throw (context, "Cannot transfer ownership of instance argument for non boxed 
structure");
-                    return false;
-                }
-            }
         }
 
     } else if (type == GI_INFO_TYPE_UNION) {
-        if (!gjs_typecheck_union(context, obj, container, gtype, true))
+        if (!UnionBase::transfer_to_gi_argument(context, obj, out_arg,
+                                                GI_DIRECTION_OUT, transfer,
+                                                gtype, container))
             return false;
 
-        out_arg->v_pointer = gjs_c_union_from_union(context, obj);
-        if (transfer == GI_TRANSFER_EVERYTHING)
-            out_arg->v_pointer = g_boxed_copy (gtype, out_arg->v_pointer);
-
     } else if (type == GI_INFO_TYPE_OBJECT || type == GI_INFO_TYPE_INTERFACE) {
         if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
-            if (!gjs_typecheck_object(context, obj, gtype, true))
+            if (!ObjectBase::transfer_to_gi_argument(
+                    context, obj, out_arg, GI_DIRECTION_OUT, transfer, gtype))
                 return false;
-            out_arg->v_pointer = gjs_g_object_from_object(context, obj);
             is_gobject = true;
-            if (transfer == GI_TRANSFER_EVERYTHING)
-                g_object_ref (out_arg->v_pointer);
         } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
             if (!gjs_typecheck_param(context, obj, G_TYPE_PARAM, true))
                 return false;
@@ -704,25 +689,21 @@ gjs_fill_method_instance(JSContext       *context,
                 g_param_spec_ref ((GParamSpec*) out_arg->v_pointer);
         } else if (G_TYPE_IS_INTERFACE(gtype)) {
             if (gjs_typecheck_is_object(context, obj, false)) {
-                if (!gjs_typecheck_object(context, obj, gtype, true))
+                if (!ObjectBase::transfer_to_gi_argument(context, obj, out_arg,
+                                                         GI_DIRECTION_OUT,
+                                                         transfer, gtype))
                     return false;
-                out_arg->v_pointer = gjs_g_object_from_object(context, obj);
                 is_gobject = true;
-                if (transfer == GI_TRANSFER_EVERYTHING)
-                    g_object_ref (out_arg->v_pointer);
             } else {
-                if (!gjs_typecheck_fundamental(context, obj, gtype, true))
+                if (!FundamentalBase::transfer_to_gi_argument(
+                        context, obj, out_arg, GI_DIRECTION_OUT, transfer,
+                        gtype))
                     return false;
-                out_arg->v_pointer = gjs_g_fundamental_from_object(context, obj);
-                if (transfer == GI_TRANSFER_EVERYTHING)
-                    gjs_fundamental_ref (context, out_arg->v_pointer);
             }
         } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
-            if (!gjs_typecheck_fundamental(context, obj, gtype, true))
+            if (!FundamentalBase::transfer_to_gi_argument(
+                    context, obj, out_arg, GI_DIRECTION_OUT, transfer, gtype))
                 return false;
-            out_arg->v_pointer = gjs_g_fundamental_from_object(context, obj);
-            if (transfer == GI_TRANSFER_EVERYTHING)
-                gjs_fundamental_ref (context, out_arg->v_pointer);
         } else {
             gjs_throw_custom(context, JSProto_TypeError, nullptr,
                              "%s.%s is not an object instance neither a fundamental instance of a supported 
type",
diff --git a/gi/fundamental.cpp b/gi/fundamental.cpp
index a1c0bf5d..e7505cdc 100644
--- a/gi/fundamental.cpp
+++ b/gi/fundamental.cpp
@@ -503,27 +503,6 @@ bool FundamentalBase::to_gvalue(JSContext* cx, JS::HandleObject obj,
     return true;
 }
 
-void*
-gjs_g_fundamental_from_object(JSContext       *context,
-                              JS::HandleObject obj)
-{
-    if (!obj)
-        return NULL;
-
-    auto* priv = FundamentalBase::for_js(context, obj);
-
-    if (priv == NULL) {
-        gjs_throw(context,
-                  "No introspection information for %p", obj.get());
-        return NULL;
-    }
-
-    if (!priv->check_is_instance(context, "convert to a fundamental instance"))
-        return NULL;
-
-    return priv->to_instance()->ptr();
-}
-
 bool
 gjs_typecheck_fundamental(JSContext       *context,
                           JS::HandleObject object,
@@ -537,12 +516,9 @@ gjs_typecheck_fundamental(JSContext       *context,
                                       FundamentalBase::TypecheckNoThrow());
 }
 
-void *
-gjs_fundamental_ref(JSContext     *context,
-                    void          *gfundamental)
-{
-    auto* priv = FundamentalPrototype::for_gtype(
-        context, G_TYPE_FROM_INSTANCE(gfundamental));
+void* FundamentalInstance::copy_ptr(JSContext* cx, GType gtype,
+                                    void* gfundamental) {
+    auto* priv = FundamentalPrototype::for_gtype(cx, gtype);
     return priv->call_ref_function(gfundamental);
 }
 
diff --git a/gi/fundamental.h b/gi/fundamental.h
index 94b1dffe..cafe0405 100644
--- a/gi/fundamental.h
+++ b/gi/fundamental.h
@@ -166,6 +166,8 @@ class FundamentalInstance
     GJS_JSAPI_RETURN_CONVENTION
     bool associate_js_instance(JSContext* cx, JSObject* object,
                                void* gfundamental);
+
+    static void* copy_ptr(JSContext* cx, GType gtype, void* gfundamental);
 };
 
 G_BEGIN_DECLS
@@ -182,10 +184,6 @@ JSObject* gjs_object_from_g_fundamental      (JSContext     *context,
                                               GIObjectInfo  *info,
                                               void          *fobj);
 
-GJS_JSAPI_RETURN_CONVENTION
-void     *gjs_g_fundamental_from_object(JSContext       *context,
-                                        JS::HandleObject obj);
-
 GJS_JSAPI_RETURN_CONVENTION
 JSObject *gjs_fundamental_from_g_value       (JSContext     *context,
                                               const GValue  *value,
@@ -197,8 +195,6 @@ bool      gjs_typecheck_fundamental(JSContext       *context,
                                     GType            expected_gtype,
                                     bool             throw_error);
 
-void*     gjs_fundamental_ref                (JSContext     *context,
-                                              void          *fobj);
 void      gjs_fundamental_unref              (JSContext     *context,
                                               void          *fobj);
 
diff --git a/gi/gerror.cpp b/gi/gerror.cpp
index 88641918..9af86fe0 100644
--- a/gi/gerror.cpp
+++ b/gi/gerror.cpp
@@ -136,8 +136,9 @@ bool ErrorBase::to_string(JSContext* context, unsigned argc, JS::Value* vp) {
     // An error created via `new GLib.Error` will have a Boxed* private pointer,
     // not an Error*, so we can't call regular gjs_gerror_to_string() on it.
     if (gjs_typecheck_boxed(context, self, nullptr, G_TYPE_ERROR, false)) {
-        auto* gerror =
-            static_cast<GError*>(gjs_c_struct_from_boxed(context, self));
+        auto* gerror = BoxedBase::to_c_ptr<GError>(context, self);
+        if (!gerror)
+            return false;
         descr =
             g_strdup_printf("GLib.Error %s: %s",
                             g_quark_to_string(gerror->domain), gerror->message);
@@ -396,28 +397,43 @@ gjs_error_from_gerror(JSContext             *context,
     return obj;
 }
 
-GError*
-gjs_gerror_from_error(JSContext       *context,
-                      JS::HandleObject obj)
-{
-    if (!obj)
-        return NULL;
-
+GError* ErrorBase::to_c_ptr(JSContext* cx, JS::HandleObject obj) {
     /* If this is a plain GBoxed (i.e. a GError without metadata),
        delegate marshalling.
     */
-    if (gjs_typecheck_boxed (context, obj, NULL, G_TYPE_ERROR, false))
-        return (GError*) gjs_c_struct_from_boxed (context, obj);
+    if (gjs_typecheck_boxed(cx, obj, nullptr, G_TYPE_ERROR, false))
+        return BoxedBase::to_c_ptr<GError>(cx, obj);
 
-    ErrorBase* priv = ErrorBase::for_js_typecheck(context, obj);
+    return GIWrapperBase::to_c_ptr<GError>(cx, obj);
+}
 
-    if (priv == NULL)
-        return NULL;
+bool ErrorBase::transfer_to_gi_argument(JSContext* cx, JS::HandleObject obj,
+                                        GIArgument* arg,
+                                        GIDirection transfer_direction,
+                                        GITransfer transfer_ownership) {
+    g_assert(transfer_direction != GI_DIRECTION_INOUT &&
+             "transfer_to_gi_argument() must choose between in or out");
 
-    if (!priv->check_is_instance(context, "convert to a boxed instance"))
-        return NULL;
+    if (!gjs_typecheck_gerror(cx, obj, true)) {
+        arg->v_pointer = nullptr;
+        return false;
+    }
+
+    arg->v_pointer = ErrorBase::to_c_ptr(cx, obj);
+    if (!arg->v_pointer)
+        return false;
+
+    if ((transfer_direction == GI_DIRECTION_IN &&
+         transfer_ownership != GI_TRANSFER_NOTHING) ||
+        (transfer_direction == GI_DIRECTION_OUT &&
+         transfer_ownership == GI_TRANSFER_EVERYTHING)) {
+        arg->v_pointer =
+            ErrorInstance::copy_ptr(cx, G_TYPE_ERROR, arg->v_pointer);
+        if (!arg->v_pointer)
+            return false;
+    }
 
-    return priv->to_instance()->ptr();
+    return true;
 }
 
 bool
@@ -439,7 +455,9 @@ gjs_gerror_make_from_error(JSContext       *cx,
 {
     if (gjs_typecheck_gerror(cx, obj, false)) {
         /* This is already a GError, just copy it */
-        GError *inner = gjs_gerror_from_error(cx, obj);
+        GError* inner = ErrorBase::to_c_ptr(cx, obj);
+        if (!inner)
+            return nullptr;
         return g_error_copy(inner);
     }
 
diff --git a/gi/gerror.h b/gi/gerror.h
index 6c5cd241..16b31eff 100644
--- a/gi/gerror.h
+++ b/gi/gerror.h
@@ -89,6 +89,17 @@ class ErrorBase
  public:
     GJS_JSAPI_RETURN_CONVENTION
     static bool to_string(JSContext* cx, unsigned argc, JS::Value* vp);
+
+    // Helper methods
+
+    GJS_JSAPI_RETURN_CONVENTION
+    static GError* to_c_ptr(JSContext* cx, JS::HandleObject obj);
+
+    GJS_JSAPI_RETURN_CONVENTION
+    static bool transfer_to_gi_argument(JSContext* cx, JS::HandleObject obj,
+                                        GIArgument* arg,
+                                        GIDirection transfer_direction,
+                                        GITransfer transfer_ownership);
 };
 
 class ErrorPrototype : public GIWrapperPrototype<ErrorBase, ErrorPrototype,
@@ -120,6 +131,11 @@ class ErrorInstance : public GIWrapperInstance<ErrorBase, ErrorPrototype,
 
  public:
     void copy_gerror(GError* other) { m_ptr = g_error_copy(other); }
+    GJS_JSAPI_RETURN_CONVENTION
+    static GError* copy_ptr(JSContext* cx G_GNUC_UNUSED,
+                            GType gtype G_GNUC_UNUSED, void* ptr) {
+        return g_error_copy(static_cast<GError*>(ptr));
+    }
 
     // Accessors
 
@@ -140,9 +156,6 @@ GJS_JSAPI_RETURN_CONVENTION
 bool gjs_define_error_class(JSContext* cx, JS::HandleObject in_object,
                             GIEnumInfo* info);
 GJS_JSAPI_RETURN_CONVENTION
-GError*   gjs_gerror_from_error        (JSContext             *context,
-                                        JS::HandleObject       obj);
-GJS_JSAPI_RETURN_CONVENTION
 JSObject* gjs_error_from_gerror        (JSContext             *context,
                                         GError                *gerror,
                                         bool                   add_stack);
diff --git a/gi/object.cpp b/gi/object.cpp
index e9eba75a..d7db7be4 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -2021,22 +2021,62 @@ gjs_object_from_g_object(JSContext    *context,
     return priv->wrapper();
 }
 
-GObject*
-gjs_g_object_from_object(JSContext       *cx,
-                         JS::HandleObject obj)
-{
-    if (!obj)
-        return NULL;
+// Replaces GIWrapperBase::to_c_ptr(). The GIWrapperBase version is deleted.
+bool ObjectBase::to_c_ptr(JSContext* cx, JS::HandleObject obj, GObject** ptr) {
+    g_assert(ptr);
 
     auto* priv = ObjectBase::for_js(cx, obj);
     if (!priv || priv->is_prototype())
-        return nullptr;
+        return false;
 
     ObjectInstance* instance = priv->to_instance();
-    if (!instance->check_gobject_disposed("access"))
-        return nullptr;
+    if (!instance->check_gobject_disposed("access")) {
+        *ptr = nullptr;
+        return true;
+    }
+
+    *ptr = instance->ptr();
+    return true;
+}
+
+// Overrides GIWrapperBase::transfer_to_gi_argument().
+bool ObjectBase::transfer_to_gi_argument(JSContext* cx, JS::HandleObject obj,
+                                         GIArgument* arg,
+                                         GIDirection transfer_direction,
+                                         GITransfer transfer_ownership,
+                                         GType expected_gtype,
+                                         GIBaseInfo* expected_info) {
+    g_assert(transfer_direction != GI_DIRECTION_INOUT &&
+             "transfer_to_gi_argument() must choose between in or out");
+
+    if (!ObjectBase::typecheck(cx, obj, expected_info, expected_gtype)) {
+        arg->v_pointer = nullptr;
+        return false;
+    }
+
+    GObject* ptr;
+    if (!ObjectBase::to_c_ptr(cx, obj, &ptr))
+        return false;
+
+    // Pointer can be null if object was already disposed by C code
+    if (!ptr) {
+        arg->v_pointer = nullptr;
+        return true;
+    }
+
+    arg->v_pointer = ptr;
+
+    if ((transfer_direction == GI_DIRECTION_IN &&
+         transfer_ownership != GI_TRANSFER_NOTHING) ||
+        (transfer_direction == GI_DIRECTION_OUT &&
+         transfer_ownership == GI_TRANSFER_EVERYTHING)) {
+        arg->v_pointer =
+            ObjectInstance::copy_ptr(cx, expected_gtype, arg->v_pointer);
+        if (!arg->v_pointer)
+            return false;
+    }
 
-    return instance->ptr();
+    return true;
 }
 
 bool
diff --git a/gi/object.h b/gi/object.h
index 532a6d38..b56452c9 100644
--- a/gi/object.h
+++ b/gi/object.h
@@ -98,6 +98,17 @@ class ObjectBase
     static const struct JSClass klass;
     static JSFunctionSpec proto_methods[];
 
+    static GObject* to_c_ptr(JSContext* cx, JS::HandleObject obj) = delete;
+    GJS_JSAPI_RETURN_CONVENTION
+    static bool to_c_ptr(JSContext* cx, JS::HandleObject obj, GObject** ptr);
+    GJS_JSAPI_RETURN_CONVENTION
+    static bool transfer_to_gi_argument(JSContext* cx, JS::HandleObject obj,
+                                        GIArgument* arg,
+                                        GIDirection transfer_direction,
+                                        GITransfer transfer_ownership,
+                                        GType expected_gtype,
+                                        GIBaseInfo* expected_info = nullptr);
+
  private:
     // This is used in debug methods only.
     GJS_USE const JSObject* jsobj_addr(void) const;
@@ -325,6 +336,10 @@ class ObjectInstance : public GIWrapperInstance<ObjectBase, ObjectPrototype,
  public:
     void ensure_uses_toggle_ref(JSContext* cx);
     GJS_USE bool check_gobject_disposed(const char* for_what) const;
+    static GObject* copy_ptr(JSContext* G_GNUC_UNUSED, GType G_GNUC_UNUSED,
+                             void* ptr) {
+        return G_OBJECT(g_object_ref(G_OBJECT(ptr)));
+    }
 
     /* Methods to manipulate the linked list of instances */
 
@@ -424,10 +439,6 @@ GJS_JSAPI_RETURN_CONVENTION
 JSObject* gjs_object_from_g_object      (JSContext     *context,
                                          GObject       *gobj);
 
-GJS_JSAPI_RETURN_CONVENTION
-GObject  *gjs_g_object_from_object(JSContext       *context,
-                                   JS::HandleObject obj);
-
 GJS_USE
 bool      gjs_typecheck_object(JSContext       *context,
                                JS::HandleObject obj,
diff --git a/gi/union.cpp b/gi/union.cpp
index 320096cc..ee112f26 100644
--- a/gi/union.cpp
+++ b/gi/union.cpp
@@ -136,7 +136,7 @@ union_new(JSContext       *context,
                 return NULL;
             } else {
                 JS::RootedObject rval_obj(context, &rval.toObject());
-                return gjs_c_union_from_union(context, rval_obj);
+                return UnionBase::to_c_ptr(context, rval_obj);
             }
         }
     }
@@ -244,18 +244,14 @@ gjs_union_from_c_union(JSContext    *context,
     return obj;
 }
 
-void*
-gjs_c_union_from_union(JSContext       *context,
-                       JS::HandleObject obj)
-{
-    if (!obj)
-        return NULL;
-
-    UnionInstance* priv = UnionInstance::for_js(context, obj);
-    if (!priv)
-        return nullptr;
+void* UnionInstance::copy_ptr(JSContext* cx, GType gtype, void* ptr) {
+    if (g_type_is_a(gtype, G_TYPE_BOXED))
+        return g_boxed_copy(gtype, ptr);
 
-    return priv->ptr();
+    gjs_throw(cx,
+              "Can't transfer ownership of a union type not registered as "
+              "boxed");
+    return nullptr;
 }
 
 bool
diff --git a/gi/union.h b/gi/union.h
index 2713a0a9..e58106ac 100644
--- a/gi/union.h
+++ b/gi/union.h
@@ -90,6 +90,9 @@ class UnionInstance
      * pointer.
      */
     void copy_union(void* ptr) { m_ptr = g_boxed_copy(gtype(), ptr); }
+
+    GJS_JSAPI_RETURN_CONVENTION
+    static void* copy_ptr(JSContext* cx, GType gtype, void* ptr);
 };
 
 G_BEGIN_DECLS
@@ -99,10 +102,6 @@ bool gjs_define_union_class(JSContext       *context,
                             JS::HandleObject in_object,
                             GIUnionInfo     *info);
 
-GJS_JSAPI_RETURN_CONVENTION
-void     *gjs_c_union_from_union(JSContext       *context,
-                                 JS::HandleObject obj);
-
 GJS_JSAPI_RETURN_CONVENTION
 JSObject* gjs_union_from_c_union       (JSContext    *context,
                                         GIUnionInfo  *info,
diff --git a/gi/value.cpp b/gi/value.cpp
index bf174326..43613d21 100644
--- a/gi/value.cpp
+++ b/gi/value.cpp
@@ -462,11 +462,11 @@ gjs_value_to_g_value_internal(JSContext      *context,
             /* nothing to do */
         } else if (value.isObject()) {
             JS::RootedObject obj(context, &value.toObject());
-
-            if (!gjs_typecheck_object(context, obj, gtype, true))
+            if (!gjs_typecheck_object(context, obj, gtype, true) ||
+                !ObjectBase::to_c_ptr(context, obj, &gobj))
                 return false;
-
-            gobj = gjs_g_object_from_object(context, obj);
+            if (!gobj)
+                return true;  // treat disposed object as if value.isNull()
         } else {
             return throw_expect_type(context, value, "object", gtype);
         }
@@ -529,10 +529,9 @@ gjs_value_to_g_value_internal(JSContext      *context,
 
             if (g_type_is_a(gtype, G_TYPE_ERROR)) {
                 /* special case GError */
-                if (!gjs_typecheck_gerror(context, obj, true))
+                gboxed = ErrorBase::to_c_ptr(context, obj);
+                if (!gboxed)
                     return false;
-
-                gboxed = gjs_gerror_from_error(context, obj);
             } else {
                 GIBaseInfo *registered = g_irepository_find_by_gtype (NULL, gtype);
 
@@ -566,13 +565,15 @@ gjs_value_to_g_value_internal(JSContext      *context,
                 */
                 if (!gboxed) {
                     if (gjs_typecheck_union(context, obj, NULL, gtype, false)) {
-                        gboxed = gjs_c_union_from_union(context, obj);
+                        gboxed = UnionBase::to_c_ptr(context, obj);
                     } else {
                         if (!gjs_typecheck_boxed(context, obj, NULL, gtype, true))
                             return false;
 
-                        gboxed = gjs_c_struct_from_boxed(context, obj);
+                        gboxed = BoxedBase::to_c_ptr(context, obj);
                     }
+                    if (!gboxed)
+                        return false;
                 }
             }
         } else {
@@ -594,7 +595,9 @@ gjs_value_to_g_value_internal(JSContext      *context,
             if (!gjs_typecheck_boxed(context, obj, NULL, G_TYPE_VARIANT, true))
                 return false;
 
-            variant = (GVariant*) gjs_c_struct_from_boxed(context, obj);
+            variant = BoxedBase::to_c_ptr<GVariant>(context, obj);
+            if (!variant)
+                return false;
         } else {
             return throw_expect_type(context, value, "boxed type", gtype);
         }
diff --git a/gi/wrapperutils.h b/gi/wrapperutils.h
index d207848c..2837df2b 100644
--- a/gi/wrapperutils.h
+++ b/gi/wrapperutils.h
@@ -557,6 +557,79 @@ class GIWrapperBase {
         return false;
     }
 
+    /*
+     * GIWrapperBase::to_c_ptr:
+     *
+     * Returns the underlying C pointer of the wrapped object, or throws a JS
+     * exception if that is not possible (for example, the passed-in JS object
+     * is the prototype.)
+     *
+     * Includes a JS typecheck (but without any extra typecheck of the GType or
+     * introspection info that you would get from GIWrapperBase::typecheck(), so
+     * if you want that you still have to do the typecheck before calling this
+     * method.)
+     */
+    template <typename T = void>
+    GJS_JSAPI_RETURN_CONVENTION static T* to_c_ptr(JSContext* cx,
+                                                   JS::HandleObject obj) {
+        Base* priv = Base::for_js_typecheck(cx, obj);
+        if (!priv || !priv->check_is_instance(cx, "get a C pointer"))
+            return nullptr;
+
+        return static_cast<T*>(priv->to_instance()->ptr());
+    }
+
+    /*
+     * GIWrapperBase::transfer_to_gi_argument:
+     * @arg: #GIArgument to fill with the value from @obj
+     * @transfer_direction: Either %GI_DIRECTION_IN or %GI_DIRECTION_OUT
+     * @transfer_ownership: #GITransfer value specifying whether @arg should
+     *   copy or acquire a reference to the value or not
+     * @expected_gtype: #GType to perform a typecheck with
+     * @expected_info: Introspection info to perform a typecheck with
+     *
+     * Prepares @arg for passing the value from @obj into C code. It will get a
+     * C pointer from @obj and assign it to @arg->v_pointer, taking a reference
+     * with GIWrapperInstance::copy_ptr() if @transfer_direction and
+     * @transfer_ownership indicate that it should.
+     *
+     * Includes a typecheck using GIWrapperBase::typecheck(), to which
+     * @expected_gtype and @expected_info are passed.
+     *
+     * If returning false, then @arg->v_pointer is null.
+     */
+    GJS_JSAPI_RETURN_CONVENTION
+    static bool transfer_to_gi_argument(JSContext* cx, JS::HandleObject obj,
+                                        GIArgument* arg,
+                                        GIDirection transfer_direction,
+                                        GITransfer transfer_ownership,
+                                        GType expected_gtype,
+                                        GIBaseInfo* expected_info = nullptr) {
+        g_assert(transfer_direction != GI_DIRECTION_INOUT &&
+                 "transfer_to_gi_argument() must choose between in or out");
+
+        if (!Base::typecheck(cx, obj, expected_info, expected_gtype)) {
+            arg->v_pointer = nullptr;
+            return false;
+        }
+
+        arg->v_pointer = Base::to_c_ptr(cx, obj);
+        if (!arg->v_pointer)
+            return false;
+
+        if ((transfer_direction == GI_DIRECTION_IN &&
+             transfer_ownership != GI_TRANSFER_NOTHING) ||
+            (transfer_direction == GI_DIRECTION_OUT &&
+             transfer_ownership == GI_TRANSFER_EVERYTHING)) {
+            arg->v_pointer =
+                Instance::copy_ptr(cx, expected_gtype, arg->v_pointer);
+            if (!arg->v_pointer)
+                return false;
+        }
+
+        return true;
+    }
+
     // Public typecheck API
 
     struct TypecheckNoThrow {};
diff --git a/gjs/byteArray.cpp b/gjs/byteArray.cpp
index 05b58cd2..78316f70 100644
--- a/gjs/byteArray.cpp
+++ b/gjs/byteArray.cpp
@@ -301,7 +301,9 @@ from_gbytes_func(JSContext *context,
     if (!gjs_typecheck_boxed(context, bytes_obj, NULL, G_TYPE_BYTES, true))
         return false;
 
-    gbytes = (GBytes*) gjs_c_struct_from_boxed(context, bytes_obj);
+    gbytes = BoxedBase::to_c_ptr<GBytes>(context, bytes_obj);
+    if (!gbytes)
+        return false;
 
     size_t len;
     const void* data = g_bytes_get_data(gbytes, &len);
diff --git a/modules/system.cpp b/modules/system.cpp
index 430b0288..8d523991 100644
--- a/modules/system.cpp
+++ b/modules/system.cpp
@@ -71,12 +71,13 @@ gjs_refcount(JSContext *context,
                              "object", &target_obj))
         return false;
 
-    if (!gjs_typecheck_object(context, target_obj, G_TYPE_OBJECT, true))
-        return false;
-
-    obj = gjs_g_object_from_object(context, target_obj);
-    if (obj == NULL)
+    if (!ObjectBase::to_c_ptr(context, target_obj, &obj))
         return false;
+    if (!obj) {
+        // Object already disposed, treat as refcount 0
+        argv.rval().setInt32(0);
+        return true;
+    }
 
     argv.rval().setInt32(obj->ref_count);
     return true;



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