[gjs: 2/26] value: Support marshalling custom fundamental types



commit 067e7d753cc7c8e912be0575d7a99c75d7870aed
Author: Philip Chimento <philip chimento gmail com>
Date:   Fri Jan 4 18:45:28 2019 -0800

    value: Support marshalling custom fundamental types
    
    Previously, passing a custom fundamental type to a function with a
    GValue argument would throw a JS exception.
    
    Fundamental types in g-i have a "set_value_function" which tells how to
    convert them to a GValue. We were already storing a pointer to this
    function in FundamentalPrototype, but never used it anywhere. This adds
    API to FundamentalBase and a code path in gjs_value_to_g_value() which
    calls the set_value_function.
    
    Arguably this should be tried before the last-resort transform-to-int
    code in gjs_value_to_g_value_internal(), but it's not clear that that
    would preserve existing behaviour.

 gi/fundamental.cpp                    | 10 ++++++++++
 gi/fundamental.h                      | 13 +++++++++++++
 gi/value.cpp                          |  9 +++++++++
 gi/wrapperutils.h                     |  1 +
 installed-tests/js/testFundamental.js |  7 ++++++-
 5 files changed, 39 insertions(+), 1 deletion(-)
---
diff --git a/gi/fundamental.cpp b/gi/fundamental.cpp
index 0d5d3f6d..a1c0bf5d 100644
--- a/gi/fundamental.cpp
+++ b/gi/fundamental.cpp
@@ -493,6 +493,16 @@ gjs_fundamental_from_g_value(JSContext    *context,
     return gjs_object_from_g_fundamental(context, proto_priv->info(), fobj);
 }
 
+bool FundamentalBase::to_gvalue(JSContext* cx, JS::HandleObject obj,
+                                GValue* gvalue) {
+    auto* priv = FundamentalBase::for_js_typecheck(cx, obj);
+    if (!priv || !priv->check_is_instance(cx, "convert to GValue"))
+        return false;
+
+    priv->to_instance()->set_value(gvalue);
+    return true;
+}
+
 void*
 gjs_g_fundamental_from_object(JSContext       *context,
                               JS::HandleObject obj)
diff --git a/gi/fundamental.h b/gi/fundamental.h
index 5c04558b..94b1dffe 100644
--- a/gi/fundamental.h
+++ b/gi/fundamental.h
@@ -62,6 +62,12 @@ class FundamentalBase
     // Helper methods
 
     GJS_USE const char* to_string_kind(void) const { return "fundamental"; }
+
+    // Public API
+
+ public:
+    GJS_JSAPI_RETURN_CONVENTION
+    static bool to_gvalue(JSContext* cx, JS::HandleObject obj, GValue* gvalue);
 };
 
 class FundamentalPrototype
@@ -101,6 +107,9 @@ class FundamentalPrototype
     GJS_USE void* call_get_value_function(const GValue* value) const {
         return m_get_value_function(value);
     }
+    void call_set_value_function(GValue* value, void* object) const {
+        m_set_value_function(value, object);
+    }
 
     // Helper methods
 
@@ -125,6 +134,7 @@ class FundamentalPrototype
 class FundamentalInstance
     : public GIWrapperInstance<FundamentalBase, FundamentalPrototype,
                                FundamentalInstance> {
+    friend class FundamentalBase;  // for set_value()
     friend class GIWrapperInstance<FundamentalBase, FundamentalPrototype,
                                    FundamentalInstance>;
     friend class GIWrapperBase<FundamentalBase, FundamentalPrototype,
@@ -142,6 +152,9 @@ class FundamentalInstance
 
     void ref(void) { get_prototype()->call_ref_function(m_ptr); }
     void unref(void) { get_prototype()->call_unref_function(m_ptr); }
+    void set_value(GValue* gvalue) const {
+        get_prototype()->call_set_value_function(gvalue, m_ptr);
+    }
 
     // JS constructor
 
diff --git a/gi/value.cpp b/gi/value.cpp
index 1ad596e3..bf174326 100644
--- a/gi/value.cpp
+++ b/gi/value.cpp
@@ -683,6 +683,15 @@ gjs_value_to_g_value_internal(JSContext      *context,
         } else {
             return throw_expect_type(context, value, "integer");
         }
+    } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
+        // The gtype is none of the above, it should be derived from a custom
+        // fundamental type.
+        if (!value.isObject())
+            return throw_expect_type(context, value, "object", gtype);
+
+        JS::RootedObject fundamental_object(context, &value.toObject());
+        if (!FundamentalBase::to_gvalue(context, fundamental_object, gvalue))
+            return false;
     } else {
         gjs_debug(GJS_DEBUG_GCLOSURE, "JS::Value is number %d gtype fundamental %d transformable to int %d 
from int %d",
                   value.isNumber(),
diff --git a/gi/wrapperutils.h b/gi/wrapperutils.h
index c288a4a0..225dc0fa 100644
--- a/gi/wrapperutils.h
+++ b/gi/wrapperutils.h
@@ -26,6 +26,7 @@
 #define GI_WRAPPERUTILS_H_
 
 #include "gjs/context-private.h"
+#include "gjs/jsapi-class.h"
 #include "gjs/jsapi-util.h"
 #include "gjs/macros.h"
 #include "util/log.h"
diff --git a/installed-tests/js/testFundamental.js b/installed-tests/js/testFundamental.js
index fc08de0f..a947f836 100644
--- a/installed-tests/js/testFundamental.js
+++ b/installed-tests/js/testFundamental.js
@@ -1,4 +1,4 @@
-const Regress = imports.gi.Regress;
+const {GObject, Regress} = imports.gi;
 
 describe('Fundamental type support', function () {
     it('constructs a subtype of a fundamental type', function () {
@@ -8,4 +8,9 @@ describe('Fundamental type support', function () {
     it('constructs a subtype of a hidden (no introspection data) fundamental type', function() {
         expect(() => Regress.test_create_fundamental_hidden_class_instance()).not.toThrow();
     });
+
+    it('can marshal a subtype of a custom fundamental type into a GValue', function () {
+        const fund = new Regress.TestFundamentalSubObject('plop');
+        expect(() => GObject.strdup_value_contents(fund)).not.toThrow();
+    });
 });


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