[gjs/ewlsh/allocate-nullptrs] Allocate structs which contain pointers.




commit 0a66789da527e8425268faa1f7f0948f439bc115
Author: Evan Welsh <contact evanwelsh com>
Date:   Fri Sep 3 21:40:13 2021 -0700

    Allocate 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 #79, #109

 gi/arg.cpp   | 10 ++++++----
 gi/boxed.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
 gi/boxed.h   | 18 ++++++++++++++++--
 3 files changed, 74 insertions(+), 16 deletions(-)
---
diff --git a/gi/arg.cpp b/gi/arg.cpp
index cff40e10..d7decad0 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -2749,10 +2749,12 @@ gjs_value_from_g_argument (JSContext             *context,
                 return gjs_array_from_zero_terminated_c_array(
                     context, value_p, param_info, gjs_arg_get<void*>(arg));
             } else {
-                /* arrays with length are handled outside of this function */
-                g_assert(((void) "Use gjs_value_from_explicit_array() for "
-                          "arrays with length param",
-                          g_type_info_get_array_length(type_info) == -1));
+                if (g_type_info_get_array_length(type_info) != -1) {
+                    gjs_throw(context,
+                              "Arrays with unknown length cannot be returned.");
+                    return false;
+                }
+
                 return gjs_array_from_fixed_size_array(
                     context, value_p, type_info, gjs_arg_get<void*>(arg));
             }
diff --git a/gi/boxed.cpp b/gi/boxed.cpp
index 7327c317..b038591a 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 (get_prototype()->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((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,30 @@ 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 =
+            this->get_field_info(cx, length_field_ix);
+        GjsAutoTypeInfo length_type_info =
+            g_field_info_get_type(length_field_info);
+        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;
+        }
+        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 +603,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((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 +780,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 +788,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 +806,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(
+                            (GIStructInfo*)interface,
+                            Gjs::DirectAllocationPolicy::ALLOCATE_POINTERS))
                         is_simple = false;
                     break;
                 case GI_INFO_TYPE_UNION:
@@ -836,7 +873,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 +887,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 +899,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;


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