[gjs: 2/3] arg: Support flat arrays of structs



commit faaa4221aaa15fb7542cd56c84b3b0187ea0bd88
Author: Philip Chimento <philip chimento gmail com>
Date:   Sat Oct 26 15:57:34 2019 -0700

    arg: Support flat arrays of structs
    
    This is actually possible without a cleanup list, contrary to what was
    previously asserted in #44.
    
    It's possible to pass a flat array of C data if the array element type
    is a struct or union not registered as a boxed type. In this case,
    g_type_info_get_pointer() will be false for the GI info of the function
    argument. We detect this case and create a flat array where the structs
    are copied by value.
    
    We preserve the existing special case for flat GValue arrays, since we
    need to use gjs_value_to_g_value() there instead of
    gjs_value_to_g_argument(), and doing both in the same function would
    make the code less readable.
    
    See #44.

 gi/arg.cpp                        | 66 +++++++++++++++++++++++++++++++++++----
 installed-tests/js/testRegress.js |  8 ++---
 2 files changed, 64 insertions(+), 10 deletions(-)
---
diff --git a/gi/arg.cpp b/gi/arg.cpp
index d5539d52..aa3efc41 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -930,6 +930,50 @@ gjs_array_to_ptrarray(JSContext   *context,
     return true;
 }
 
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_array_to_flat_struct_array(JSContext* cx,
+                                           JS::HandleValue array_value,
+                                           unsigned length,
+                                           GITypeInfo* param_info,
+                                           GIBaseInfo* interface_info,
+                                           GIInfoType info_type, void** arr_p) {
+    g_assert(
+        (info_type == GI_INFO_TYPE_STRUCT || info_type == GI_INFO_TYPE_UNION) &&
+        "Only flat arrays of unboxed structs or unions are supported");
+    size_t struct_size;
+    if (info_type == GI_INFO_TYPE_UNION)
+        struct_size = g_union_info_get_size(interface_info);
+    else
+        struct_size = g_struct_info_get_size(interface_info);
+
+    GjsAutoPointer<uint8_t, void, g_free> flat_array =
+        g_new0(uint8_t, struct_size * length);
+
+    JS::RootedObject array(cx, &array_value.toObject());
+    JS::RootedValue elem(cx);
+    for (unsigned i = 0; i < length; i++) {
+        elem = JS::UndefinedValue();
+
+        if (!JS_GetElement(cx, array, i, &elem)) {
+            gjs_throw(cx, "Missing array element %u", i);
+            return false;
+        }
+
+        GIArgument arg;
+        if (!gjs_value_to_g_argument(cx, elem, param_info,
+                                     /* arg_name = */ nullptr,
+                                     GJS_ARGUMENT_ARRAY_ELEMENT,
+                                     GI_TRANSFER_NOTHING,
+                                     /* may_be_null = */ false, &arg))
+            return false;
+
+        memcpy(&flat_array[struct_size * i], arg.v_pointer, struct_size);
+    }
+
+    *arr_p = flat_array.release();
+    return true;
+}
+
 GJS_JSAPI_RETURN_CONVENTION
 static bool
 gjs_array_to_flat_gvalue_array(JSContext   *context,
@@ -1116,18 +1160,17 @@ static bool gjs_array_to_array(JSContext* context, JS::HandleValue array_value,
 
     /* Everything else is a pointer type */
     case GI_TYPE_TAG_INTERFACE:
-        // Flat arrays of structures are not supported yet; see
-        // https://gitlab.gnome.org/GNOME/gjs/issues/44
         if (!g_type_info_is_pointer(param_info)) {
             GjsAutoBaseInfo interface_info =
                 g_type_info_get_interface(param_info);
             GIInfoType info_type = g_base_info_get_type(interface_info);
             if (info_type == GI_INFO_TYPE_STRUCT ||
                 info_type == GI_INFO_TYPE_UNION) {
-                gjs_throw(context,
-                      "Flat array of type %s is not currently supported",
-                      interface_info.name());
-                return false;
+                // Ignore transfer in the case of a flat struct array. Structs
+                // are copied by value.
+                return gjs_array_to_flat_struct_array(
+                    context, array_value, length, param_info, interface_info,
+                    info_type, arr_p);
             }
         }
         /* fall through */
@@ -3352,6 +3395,17 @@ gjs_g_arg_release_internal(JSContext  *context,
                 break;
 
             case GI_TYPE_TAG_INTERFACE:
+                if (!g_type_info_is_pointer(param_info)) {
+                    GjsAutoBaseInfo interface_info =
+                        g_type_info_get_interface(param_info);
+                    GIInfoType info_type = g_base_info_get_type(interface_info);
+                    if (info_type == GI_INFO_TYPE_STRUCT ||
+                        info_type == GI_INFO_TYPE_UNION) {
+                        g_free(arg->v_pointer);
+                        break;
+                    }
+                }
+                /* fall through */
             case GI_TYPE_TAG_GLIST:
             case GI_TYPE_TAG_GSLIST:
             case GI_TYPE_TAG_ARRAY:
diff --git a/installed-tests/js/testRegress.js b/installed-tests/js/testRegress.js
index f30539b2..1aa60e9b 100644
--- a/installed-tests/js/testRegress.js
+++ b/installed-tests/js/testRegress.js
@@ -1590,16 +1590,16 @@ describe('Life, the Universe and Everything', function () {
             expect(Regress.test_array_struct_out_caller_alloc()).toEqual([]);
         }).pend('Not supported');
 
-        xit('transfer-full in parameter', function () {
+        it('transfer-full in parameter', function () {
             const array = [201, 202].map(some_int =>
                 new Regress.TestStructA({some_int}));
             expect(() => Regress.test_array_struct_in_full(array)).not.toThrow();
-        }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/44');
+        });
 
-        xit('transfer-none in parameter', function () {
+        it('transfer-none in parameter', function () {
             const array = [301, 302, 303].map(some_int =>
                 new Regress.TestStructA({some_int}));
             expect(() => Regress.test_array_struct_in_none(array)).not.toThrow();
-        }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/44');
+        });
     });
 });


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