[gjs/ewlsh/allocate-nullptrs] Allocate nullptrs directly




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

    Allocate nullptrs directly

 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]