[gjs/ewlsh/allocate-nullptrs: 13/14] gi: Allow directly allocating structs which contain pointers




commit f9ac56a9c650b58ee8c4049fa7617f2477837a99
Author: Evan Welsh <contact evanwelsh com>
Date:   Sat Sep 11 13:12:34 2021 -0700

    gi: Allow directly allocating structs which contain pointers
    
    Because structs with pointers are now allocatable,
    we also have to fix a gap in our conversion of fixed
    length arrays within structs.
    
    For backwards compatibility, structs will continue to
    resolve direct allocation without pointers as their
    first constructor, their "default" constructor next,
    and finally direct allocation with pointers.
    
    Fixes #109

 gi/boxed.cpp                      | 63 ++++++++++++++++++++++++++++++++-------
 gi/boxed.h                        | 18 +++++++++--
 installed-tests/js/testGObject.js |  9 ++++++
 3 files changed, 78 insertions(+), 12 deletions(-)
---
diff --git a/gi/boxed.cpp b/gi/boxed.cpp
index 7327c317..6d1fb400 100644
--- a/gi/boxed.cpp
+++ b/gi/boxed.cpp
@@ -25,6 +25,7 @@
 #include <jsapi.h>  // for IdVector, JS_AtomizeAndPinJSString
 #include <mozilla/HashTable.h>
 
+#include "gi/arg-cache.h"
 #include "gi/arg-inl.h"
 #include "gi/arg.h"
 #include "gi/boxed.h"
@@ -45,7 +46,8 @@ BoxedInstance::BoxedInstance(JSContext* cx, JS::HandleObject obj)
     GJS_INC_COUNTER(boxed_instance);
 }
 
-[[nodiscard]] static bool struct_is_simple(GIStructInfo* info);
+[[nodiscard]] static bool struct_is_simple(
+    GIStructInfo* info, Gjs::DirectAllocationPolicy allocation_policy);
 
 // See GIWrapperBase::resolve().
 bool BoxedPrototype::resolve_impl(JSContext* cx, JS::HandleObject obj,
@@ -366,7 +368,8 @@ bool BoxedInstance::constructor_impl(JSContext* context, JS::HandleObject obj,
 
         debug_lifecycle("Boxed pointer created from zero-args constructor");
 
-    } else if (proto->can_allocate_directly()) {
+    } else if (proto->can_allocate_directly(
+                   Gjs::DirectAllocationPolicy::NO_POINTERS)) {
         allocate_directly();
     } else if (proto->has_default_constructor()) {
         /* for simplicity, we simply delegate all the work to the actual JS
@@ -391,6 +394,8 @@ bool BoxedInstance::constructor_impl(JSContext* context, JS::HandleObject obj,
             "boxed object discarded");
 
         return true;
+    } else if (proto->can_allocate_directly()) {
+        allocate_directly();
     } else {
         gjs_throw(context,
                   "Unable to construct struct type %s since it has no default "
@@ -476,7 +481,8 @@ bool BoxedInstance::get_nested_interface_object(
     GIBaseInfo* interface_info, JS::MutableHandleValue value) const {
     int offset;
 
-    if (!struct_is_simple ((GIStructInfo *)interface_info)) {
+    if (!struct_is_simple(reinterpret_cast<GIStructInfo*>(interface_info),
+                          Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS)) {
         gjs_throw(context, "Reading field %s.%s is not supported", name(),
                   g_base_info_get_name(field_info));
 
@@ -552,6 +558,31 @@ bool BoxedInstance::field_getter_impl(JSContext* cx, JSObject* obj,
         return false;
     }
 
+    if (g_type_info_get_tag(type_info) == GI_TYPE_TAG_ARRAY &&
+        g_type_info_get_array_length(type_info) != -1) {
+        auto length_field_ix = g_type_info_get_array_length(type_info);
+        GjsAutoFieldInfo length_field_info =
+            get_field_info(cx, length_field_ix);
+        if (!length_field_info) {
+            gjs_throw(cx, "Reading field %s.%s is not supported", name(),
+                      g_base_info_get_name(length_field_info));
+            return false;
+        }
+
+        GIArgument length_arg;
+        if (!g_field_info_get_field(length_field_info, m_ptr, &length_arg)) {
+            gjs_throw(cx, "Reading field %s.%s is not supported", name(),
+                      g_base_info_get_name(length_field_info));
+            return false;
+        }
+
+        GjsAutoTypeInfo length_type_info =
+            g_field_info_get_type(length_field_info);
+        size_t length = gjs_g_argument_get_array_length(
+            g_type_info_get_tag(length_type_info), &length_arg);
+        return gjs_value_from_explicit_array(cx, rval, type_info, &arg, length);
+    }
+
     return gjs_value_from_g_argument(cx, rval, type_info, &arg, true);
 }
 
@@ -573,7 +604,8 @@ bool BoxedInstance::set_nested_interface_object(JSContext* context,
                                                 JS::HandleValue value) {
     int offset;
 
-    if (!struct_is_simple ((GIStructInfo *)interface_info)) {
+    if (!struct_is_simple(reinterpret_cast<GIStructInfo*>(interface_info),
+                          Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS)) {
         gjs_throw(context, "Writing field %s.%s is not supported", name(),
                   g_base_info_get_name(field_info));
 
@@ -749,7 +781,7 @@ const struct JSClass BoxedBase::klass = {
 // clang-format on
 
 [[nodiscard]] static bool type_can_be_allocated_directly(
-    GITypeInfo* type_info) {
+    GITypeInfo* type_info, Gjs::DirectAllocationPolicy allocation_policy) {
     bool is_simple = true;
 
     if (g_type_info_is_pointer(type_info)) {
@@ -757,9 +789,13 @@ const struct JSClass BoxedBase::klass = {
             g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_C) {
             GjsAutoBaseInfo param_info =
                 g_type_info_get_param_type(type_info, 0);
-            is_simple = type_can_be_allocated_directly(param_info);
+            is_simple =
+                type_can_be_allocated_directly(param_info, allocation_policy);
         } else if (g_type_info_get_tag(type_info) == GI_TYPE_TAG_VOID) {
             return true;
+        } else if (allocation_policy ==
+                   Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS) {
+            return true;
         } else {
             is_simple = false;
         }
@@ -771,7 +807,9 @@ const struct JSClass BoxedBase::klass = {
             switch (g_base_info_get_type(interface)) {
                 case GI_INFO_TYPE_BOXED:
                 case GI_INFO_TYPE_STRUCT:
-                    if (!struct_is_simple((GIStructInfo *)interface))
+                    if (!struct_is_simple(
+                            interface.as<GIStructInfo>(),
+                            Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS))
                         is_simple = false;
                     break;
                 case GI_INFO_TYPE_UNION:
@@ -836,7 +874,8 @@ const struct JSClass BoxedBase::klass = {
  * type that we know how to assign to. If so, then we can allocate and free
  * instances without needing a constructor.
  */
-[[nodiscard]] static bool struct_is_simple(GIStructInfo* info) {
+[[nodiscard]] static bool struct_is_simple(
+    GIStructInfo* info, Gjs::DirectAllocationPolicy allocation_policy) {
     int n_fields = g_struct_info_get_n_fields(info);
     bool is_simple = true;
     int i;
@@ -849,7 +888,8 @@ const struct JSClass BoxedBase::klass = {
         GjsAutoBaseInfo field_info = g_struct_info_get_field(info, i);
         GjsAutoBaseInfo type_info = g_field_info_get_type(field_info);
 
-        is_simple = type_can_be_allocated_directly(type_info);
+        is_simple =
+            type_can_be_allocated_directly(type_info, allocation_policy);
     }
 
     return is_simple;
@@ -860,7 +900,10 @@ BoxedPrototype::BoxedPrototype(GIStructInfo* info, GType gtype)
       m_zero_args_constructor(-1),
       m_default_constructor(-1),
       m_default_constructor_name(JSID_VOID),
-      m_can_allocate_directly(struct_is_simple(info)) {
+      m_can_allocate_directly(
+          struct_is_simple(info, Gjs::DirectAllocationPolicy::NO_POINTERS)),
+      m_can_allocate_directly_with_pointers(struct_is_simple(
+          info, Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS)) {
     GJS_INC_COUNTER(boxed_prototype);
 }
 
diff --git a/gi/boxed.h b/gi/boxed.h
index 7c1cd49c..6287aea9 100644
--- a/gi/boxed.h
+++ b/gi/boxed.h
@@ -37,6 +37,13 @@ namespace js {
 class SystemAllocPolicy;
 }
 
+namespace Gjs {
+enum class DirectAllocationPolicy {
+    NO_POINTERS,
+    ALLOCATE_POINTERS,
+};
+};
+
 /* To conserve memory, we have two different kinds of private data for GBoxed
  * JS wrappers: BoxedInstance, and BoxedPrototype. Both inherit from BoxedBase
  * for their common functionality. For more information, see the notes in
@@ -90,6 +97,7 @@ class BoxedPrototype : public GIWrapperPrototype<BoxedBase, BoxedPrototype,
     JS::Heap<jsid> m_default_constructor_name;
     std::unique_ptr<FieldMap> m_field_map;
     bool m_can_allocate_directly : 1;
+    bool m_can_allocate_directly_with_pointers : 1;
 
     explicit BoxedPrototype(GIStructInfo* info, GType gtype);
     ~BoxedPrototype(void);
@@ -101,8 +109,14 @@ class BoxedPrototype : public GIWrapperPrototype<BoxedBase, BoxedPrototype,
     // Accessors
 
  public:
-    [[nodiscard]] bool can_allocate_directly() const {
-        return m_can_allocate_directly;
+    [[nodiscard]] bool can_allocate_directly(
+        Gjs::DirectAllocationPolicy allocation_policy =
+            Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS) const {
+        if (allocation_policy == Gjs::DirectAllocationPolicy::NO_POINTERS) {
+            return m_can_allocate_directly;
+        }
+
+        return m_can_allocate_directly_with_pointers;
     }
     [[nodiscard]] bool has_zero_args_constructor() const {
         return m_zero_args_constructor >= 0;
diff --git a/installed-tests/js/testGObject.js b/installed-tests/js/testGObject.js
index 885a93fb..d6aafbbc 100644
--- a/installed-tests/js/testGObject.js
+++ b/installed-tests/js/testGObject.js
@@ -70,4 +70,13 @@ describe('GObject should', function () {
             expect(gtype.name).toEqual(type);
         });
     });
+
+    it('be able to query signals', function () {
+        const query = GObject.signal_query(1);
+
+        expect(query instanceof GObject.SignalQuery).toBeTruthy();
+        expect(query.param_types).not.toBeNull();
+        expect(Array.isArray(query.param_types)).toBeTruthy();
+        expect(query.signal_id).toBe(1);
+    });
 });


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