[gjs] Support in arrays of any type



commit f60d0a1f8cdb484486a43b1f596c4c1bc4930c07
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Sun Apr 3 18:25:48 2011 +0200

    Support in arrays of any type
    
    Adds support for converting JS Arrays to C arrays of any type, not
    just integers as before. Includes new infrastructure for releasing
    said arrays.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=646632

 gi/arg.c                     |  182 +++++++++++++++++++++++++++++++++++++++++-
 gi/arg.h                     |    8 ++-
 gi/function.c                |   25 +++++-
 test/js/testGIMarshalling.js |   27 ++++++
 4 files changed, 233 insertions(+), 9 deletions(-)
---
diff --git a/gi/arg.c b/gi/arg.c
index aa1bba8..15de8fc 100644
--- a/gi/arg.c
+++ b/gi/arg.c
@@ -576,9 +576,112 @@ gjs_array_to_intarray(JSContext   *context,
 }
 
 static JSBool
+gjs_array_to_floatarray(JSContext   *context,
+                        jsval        array_value,
+                        unsigned int length,
+                        void       **arr_p,
+                        gboolean     is_double)
+{
+    unsigned int i;
+    void *result = g_malloc0(length * (is_double ? sizeof(double) : sizeof(float)));
+
+    for (i = 0; i < length; ++i) {
+        jsval elem;
+        jsdouble val;
+        JSBool success;
+
+        elem = JSVAL_VOID;
+        if (!JS_GetElement(context, JSVAL_TO_OBJECT(array_value),
+                           i, &elem)) {
+            g_free(result);
+            gjs_throw(context,
+                      "Missing array element %u",
+                      i);
+            return JS_FALSE;
+        }
+
+        /* do whatever sign extension is appropriate */
+        success = JS_ValueToNumber(context, elem, &val);
+
+        if (!success) {
+            g_free(result);
+            gjs_throw(context,
+                      "Invalid element in array");
+            return JS_FALSE;
+        }
+
+        /* Note that this is truncating assignment. */
+        if (is_double) {
+            double *darray = (double*)result;
+            darray[i] = val;
+        } else {
+            float *farray = (float*)result;
+            farray[i] = val;
+        }
+    }
+
+    *arr_p = result;
+
+    return JS_TRUE;
+}
+
+static JSBool
+gjs_array_to_ptrarray(JSContext   *context,
+                      jsval        array_value,
+                      unsigned int length,
+                      GITransfer   transfer,
+                      GITypeInfo  *param_info,
+                      void       **arr_p)
+{
+    unsigned int i;
+
+    void **array = g_malloc0(length * sizeof(gpointer));
+
+    for (i = 0; i < length; i++) {
+        jsval elem;
+        GIArgument arg;
+        arg.v_pointer = NULL;
+
+        JSBool success;
+
+        elem = JSVAL_VOID;
+        if (!JS_GetElement(context, JSVAL_TO_OBJECT(array_value),
+                           i, &elem)) {
+            g_free(array);
+            gjs_throw(context,
+                      "Missing array element %u",
+                      i);
+            return JS_FALSE;
+        }
+
+        success = gjs_value_to_g_argument (context,
+                                           elem,
+                                           param_info,
+                                           NULL, /* arg name */
+                                           GJS_ARGUMENT_ARRAY_ELEMENT,
+                                           transfer,
+                                           FALSE, /* absent better information, FALSE for now */
+                                           &arg);
+
+        if (!success) {
+            g_free(array);
+            gjs_throw(context,
+                      "Invalid element in array");
+            return JS_FALSE;
+        }
+
+        array[i] = arg.v_pointer;
+    }
+
+    *arr_p = array;
+    return JS_TRUE;
+} 
+
+static JSBool
 gjs_array_to_array(JSContext   *context,
                    jsval        array_value,
                    unsigned int length,
+                   GITransfer   transfer,
                    GITypeInfo  *param_info,
                    void       **arr_p)
 {
@@ -588,6 +691,14 @@ gjs_array_to_array(JSContext   *context,
     element_type = g_type_info_get_tag(param_info);
     element_type = replace_gtype(element_type);
 
+    if (element_type == GI_TYPE_TAG_INTERFACE) {
+        GIBaseInfo *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_ENUM || info_type == GI_INFO_TYPE_FLAGS)
+            element_type = g_enum_info_get_storage_type ((GIEnumInfo*) interface_info);
+        g_base_info_unref(interface_info);
+    }
+
     switch (element_type) {
     case GI_TYPE_TAG_UTF8:
         return gjs_array_to_strv (context, array_value, length, arr_p);
@@ -609,6 +720,25 @@ gjs_array_to_array(JSContext   *context,
     case GI_TYPE_TAG_INT32:
         return gjs_array_to_intarray
             (context, array_value, length, arr_p, 4, SIGNED);
+    case GI_TYPE_TAG_FLOAT:
+        return gjs_array_to_floatarray
+            (context, array_value, length, arr_p, FALSE);
+    case GI_TYPE_TAG_DOUBLE:
+        return gjs_array_to_floatarray
+            (context, array_value, length, arr_p, TRUE);
+
+    /* Everything else is a pointer type */
+    case GI_TYPE_TAG_INTERFACE:
+    case GI_TYPE_TAG_ARRAY:
+    case GI_TYPE_TAG_GLIST:
+    case GI_TYPE_TAG_GSLIST:
+    case GI_TYPE_TAG_GHASH:
+        return gjs_array_to_ptrarray(context,
+                                     array_value,
+                                     length,
+                                     transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer,
+                                     param_info,
+                                     arr_p);
     default:
         gjs_throw(context,
                   "Unhandled array element type %d", element_type);
@@ -658,6 +788,7 @@ gjs_array_to_g_array(JSContext   *context,
     if (!gjs_array_to_array (context,
                              array_value,
                              length,
+                             GI_TRANSFER_NOTHING, /* always good for the types we handle */
                              param_info,
                              &contents))
       return JS_FALSE;
@@ -703,6 +834,8 @@ get_argument_display_name(const char     *arg_name,
         return g_strdup("List element");
     case GJS_ARGUMENT_HASH_ELEMENT:
         return g_strdup("Hash element");
+    case GJS_ARGUMENT_ARRAY_ELEMENT:
+        return g_strdup("Array element");
     }
 
     g_assert_not_reached ();
@@ -1203,6 +1336,7 @@ gjs_value_to_g_argument(JSContext      *context,
                     if (!gjs_array_to_array (context,
                                              value,
                                              length,
+                                             transfer,
                                              param_info,
                                              &arg->v_pointer))
                       wrong = TRUE;
@@ -2369,7 +2503,7 @@ gjs_g_arg_release_internal(JSContext  *context,
             case GI_TYPE_TAG_FILENAME:
                 if (transfer == GI_TRANSFER_CONTAINER)
                     g_free(arg->v_pointer);
-                else if (transfer == GI_TRANSFER_EVERYTHING)
+                else if (transfer != TRANSFER_IN_NOTHING)
                     g_strfreev (arg->v_pointer);
                 break;
 
@@ -2407,7 +2541,10 @@ gjs_g_arg_release_internal(JSContext  *context,
                 break;
 
             default:
-                g_assert_not_reached ();
+                gjs_throw(context,
+                          "Releasing a C array with explicit length, that was nested"
+                          "inside another container. This is not supported (and will leak)");
+                failed = JS_TRUE;
             }
 
             g_base_info_unref((GIBaseInfo*) param_info);
@@ -2440,7 +2577,7 @@ gjs_g_arg_release_internal(JSContext  *context,
             case GI_TYPE_TAG_GHASH:
                 if (transfer == GI_TRANSFER_CONTAINER) {
                     g_array_free((GArray*) arg->v_pointer, TRUE);
-                } else if (transfer == GI_TRANSFER_EVERYTHING) {
+                } else if (transfer != TRANSFER_IN_NOTHING) {
                     GArray *array = arg->v_pointer;
                     guint i;
 
@@ -2449,7 +2586,7 @@ gjs_g_arg_release_internal(JSContext  *context,
 
                         arg.v_pointer = g_array_index (array, gpointer, i);
                         gjs_g_argument_release(context,
-                                               GI_TRANSFER_EVERYTHING,
+                                               transfer,
                                                param_info,
                                                &arg);
                     }
@@ -2597,5 +2734,42 @@ gjs_g_argument_release_in_arg(JSContext  *context,
     return JS_TRUE;
 }
 
+JSBool
+gjs_g_argument_release_in_array (JSContext  *context,
+                                 GITransfer  transfer,
+                                 GITypeInfo *type_info,
+                                 guint       length,
+                                 GArgument  *arg)
+{
+    GITypeInfo *param_type;
+    gpointer *array;
+    GArgument elem;
+    guint i;
+    JSBool ret = JS_TRUE;
+    GITypeTag type_tag;
+
+    if (transfer != GI_TRANSFER_NOTHING)
+        return JS_TRUE;
+
+    gjs_debug_marshal(GJS_DEBUG_GFUNCTION,
+                      "Releasing GArgument array in param");
+
+    array = arg->v_pointer;
 
+    param_type = g_type_info_get_param_type(type_info, 0);
+    type_tag = g_type_info_get_tag(param_type);
 
+    if (type_needs_release(param_type, type_tag)) {
+        for (i = 0; i < length; i++) {
+            elem.v_pointer = array[i];
+            if (!gjs_g_arg_release_internal(context, TRANSFER_IN_NOTHING,
+                                            param_type, type_tag, &elem)) {
+                ret = JS_FALSE;
+                break;
+            }
+        }
+    }
+
+    g_free(array);
+    return ret;
+}
diff --git a/gi/arg.h b/gi/arg.h
index df473e2..47e7f64 100644
--- a/gi/arg.h
+++ b/gi/arg.h
@@ -38,7 +38,8 @@ typedef enum {
     GJS_ARGUMENT_RETURN_VALUE,
     GJS_ARGUMENT_FIELD,
     GJS_ARGUMENT_LIST_ELEMENT,
-    GJS_ARGUMENT_HASH_ELEMENT
+    GJS_ARGUMENT_HASH_ELEMENT,
+    GJS_ARGUMENT_ARRAY_ELEMENT
 } GjsArgumentType;
 
 JSBool gjs_value_to_arg   (JSContext  *context,
@@ -67,6 +68,11 @@ JSBool gjs_g_argument_release    (JSContext  *context,
                                   GITransfer  transfer,
                                   GITypeInfo *type_info,
                                   GArgument  *arg);
+JSBool gjs_g_argument_release_in_array (JSContext  *context,
+                                        GITransfer  transfer,
+                                        GITypeInfo *type_info,
+                                        guint       length,
+                                        GArgument  *arg);
 JSBool gjs_g_argument_release_in_arg (JSContext  *context,
                                       GITransfer  transfer,
                                       GITypeInfo *type_info,
diff --git a/gi/function.c b/gi/function.c
index 436a458..6aa4ba2 100644
--- a/gi/function.c
+++ b/gi/function.c
@@ -707,6 +707,7 @@ release:
         if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
             GArgument *arg;
             GITransfer transfer;
+            gint array_length_pos;
 
             if (direction == GI_DIRECTION_IN) {
                 g_assert_cmpuint(in_args_pos, <, in_args_len);
@@ -722,10 +723,26 @@ release:
                  */
                 transfer = GI_TRANSFER_EVERYTHING;
             }
-            if (!gjs_g_argument_release_in_arg(context,
-                                               transfer,
-                                               &arg_type_info,
-                                               arg)) {
+            if (g_type_info_get_tag(&arg_type_info) == GI_TYPE_TAG_ARRAY &&
+                (array_length_pos = g_type_info_get_array_length(&arg_type_info)) != -1) {
+                /* Assume that the length passed as an argument was the length of the Array
+                   (not necessarily true) */
+
+                if (is_method) /* get_array_length does not consider the instance argument */
+                    array_length_pos++;
+
+                guint length = in_arg_cvalues[array_length_pos].v_uint;
+                if (!gjs_g_argument_release_in_array(context,
+                                                     transfer,
+                                                     &arg_type_info,
+                                                     length,
+                                                     arg)) {
+                    postinvoke_release_failed = TRUE;
+                }
+            } else if (!gjs_g_argument_release_in_arg(context,
+                                                      transfer,
+                                                      &arg_type_info,
+                                                      arg)) {
                 postinvoke_release_failed = TRUE;
             }
         }
diff --git a/test/js/testGIMarshalling.js b/test/js/testGIMarshalling.js
index 467b105..2298119 100644
--- a/test/js/testGIMarshalling.js
+++ b/test/js/testGIMarshalling.js
@@ -18,6 +18,33 @@ function testCArray() {
     assertEquals(42, array[0].long_);
     assertEquals(43, array[1].long_);
     assertEquals(44, array[2].long_);
+
+    GIMarshallingTests.array_string_in(["foo", "bar"], 2);
+
+    array = [];
+    for (var i = 0; i < 3; i++) {
+	array[i] = new GIMarshallingTests.BoxedStruct();
+	array[i].long_ = i + 1;
+    }
+
+    GIMarshallingTests.array_struct_in(array, array.length);
+
+    // Run twice to ensure that copies are correctly made for (transfer full)
+    GIMarshallingTests.array_struct_take_in(array, array.length);
+    GIMarshallingTests.array_struct_take_in(array, array.length);
+
+    GIMarshallingTests.array_uint8_in ("abcd", 4);
+    GIMarshallingTests.array_nested_in ([[11], [21, 22, 23], [31, 32]], 3);
+    GIMarshallingTests.array_enum_in([GIMarshallingTests.Enum.VALUE1,
+				      GIMarshallingTests.Enum.VALUE2,
+				      GIMarshallingTests.Enum.VALUE3], 3);
+
+    array = [-1, 0, 1, 2];
+    GIMarshallingTests.array_in(array, array.length);
+    GIMarshallingTests.array_in_len_before(array.length, array);
+    GIMarshallingTests.array_in_len_zero_terminated(array, array.length);
+    GIMarshallingTests.array_in_guint64_len(array, array.length);
+    GIMarshallingTests.array_in_guint8_len(array, array.length);
 }
 
 function testGArray() {



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