[gjs/wip/gcampax/70-arg-cache: 1144/1149] function: introduce a new system for better caching of marshalling info



commit ec1d04d78ce64254bc78f37b507edef61ae5348a
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Fri Apr 19 20:27:07 2013 +0200

    function: introduce a new system for better caching of marshalling info
    
    The marshalling metadata, as stored in the typelib, is not a format
    friendly to gjs, and requires to have multiple switches and a complex
    logic in the hot path of function calls.
    Instead, extend the current caching mechanism to allow arbitrary
    data to be attached to arguments, and to have it deal with JS arguments,
    rather than a mixture of C and GI arguments.
    
    (Philip Chimento: rebased and fixed coding style and bugs.)

 gi/arg-cache.cpp                        | 179 ++++++++++++++++++++++++++
 gi/arg-cache.h                          |  72 +++++++++++
 gi/function.cpp                         | 214 ++++++++++++++++++--------------
 installed-tests/js/testGIMarshalling.js |   8 --
 meson.build                             |   1 +
 5 files changed, 372 insertions(+), 102 deletions(-)
---
diff --git a/gi/arg-cache.cpp b/gi/arg-cache.cpp
new file mode 100644
index 00000000..5f686a11
--- /dev/null
+++ b/gi/arg-cache.cpp
@@ -0,0 +1,179 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2013 Giovanni Campagna <scampa giovanni gmail com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <girepository.h>
+#include <glib.h>
+
+#include "gi/arg-cache.h"
+#include "gi/arg.h"
+#include "gi/function.h"
+#include "gjs/jsapi-util.h"
+
+bool gjs_arg_cache_build_return(GjsArgumentCache* self,
+                                GjsParamType* param_types, GICallableInfo* info,
+                                bool& inc_counter) {
+    GITypeInfo return_type;
+    g_callable_info_load_return_type(info, &return_type);
+
+    if (g_type_info_get_tag(&return_type) != GI_TYPE_TAG_VOID) {
+        inc_counter = true;
+        self->param_type = PARAM_NORMAL;
+    } else {
+        inc_counter = false;
+        self->param_type = PARAM_SKIPPED;
+    }
+
+    int length_pos = g_type_info_get_array_length(&return_type);
+    if (length_pos >= 0)
+        param_types[length_pos] = PARAM_SKIPPED;
+
+    return true;
+}
+
+bool gjs_arg_cache_build_in_arg(GjsArgumentCache* self,
+                                GjsParamType* param_types, int gi_index,
+                                GIArgInfo* arg_info, bool& inc_counter,
+                                const char** message_out) {
+    GITypeInfo type_info;
+    g_arg_info_load_type(arg_info, &type_info);
+
+    self->param_type = PARAM_NORMAL;
+    inc_counter = true;
+
+    GITypeTag type_tag = g_type_info_get_tag(&type_info);
+    if (type_tag == GI_TYPE_TAG_INTERFACE) {
+        GjsAutoBaseInfo interface_info = g_type_info_get_interface(&type_info);
+        if (interface_info.type() == GI_INFO_TYPE_CALLBACK) {
+            if (strcmp(interface_info.name(), "DestroyNotify") == 0 &&
+                strcmp(interface_info.ns(), "GLib") == 0) {
+                // We don't know (yet) what to do with GDestroyNotify appearing
+                // before a callback. If the callback comes later in the
+                // argument list, then PARAM_UNKNOWN will be overwritten with
+                // PARAM_SKIPPED. If no callback follows, then this is probably
+                // an unsupported function, so the value will remain
+                // PARAM_UNKNOWN.
+                self->param_type = PARAM_UNKNOWN;
+                inc_counter = false;
+            } else {
+                self->param_type = PARAM_CALLBACK;
+
+                int destroy_pos = g_arg_info_get_destroy(arg_info);
+                int closure_pos = g_arg_info_get_closure(arg_info);
+
+                if (destroy_pos >= 0)
+                    param_types[destroy_pos] = PARAM_SKIPPED;
+
+                if (closure_pos >= 0)
+                    param_types[closure_pos] = PARAM_SKIPPED;
+
+                if (destroy_pos >= 0 && closure_pos < 0) {
+                    *message_out =
+                        "GDestroyNotify without user_data is not supported";
+                    return false;
+                }
+            }
+        }
+    } else if (type_tag == GI_TYPE_TAG_ARRAY) {
+        if (g_type_info_get_array_type(&type_info) == GI_ARRAY_TYPE_C) {
+            int length_pos = g_type_info_get_array_length(&type_info);
+
+            if (length_pos >= 0) {
+                self->param_type = PARAM_ARRAY;
+                if (param_types[length_pos] != PARAM_SKIPPED) {
+                    param_types[length_pos] = PARAM_SKIPPED;
+                    if (length_pos < gi_index) {
+                        // we already collected length_pos, remove it
+                        inc_counter = false;
+                    }
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+bool gjs_arg_cache_build_out_arg(GjsArgumentCache* self,
+                                 GjsParamType* param_types, int gi_index,
+                                 GIArgInfo* arg_info, bool& inc_counter) {
+    GITypeInfo type_info;
+    g_arg_info_load_type(arg_info, &type_info);
+
+    self->param_type = PARAM_NORMAL;
+    inc_counter = true;
+
+    GITypeTag type_tag = g_type_info_get_tag(&type_info);
+    if (type_tag == GI_TYPE_TAG_ARRAY) {
+        if (g_type_info_get_array_type(&type_info) == GI_ARRAY_TYPE_C) {
+            int length_pos = g_type_info_get_array_length(&type_info);
+
+            if (length_pos >= 0) {
+                param_types[length_pos] = PARAM_SKIPPED;
+                self->param_type = PARAM_ARRAY;
+
+                if (length_pos < gi_index) {
+                    /* we already collected length_pos, remove it */
+                    inc_counter = false;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+bool gjs_arg_cache_build_inout_arg(GjsArgumentCache* in_self,
+                                   GjsArgumentCache* out_self,
+                                   GjsParamType* param_types, int gi_index,
+                                   GIArgInfo* arg_info, bool& inc_counter) {
+    GITypeInfo type_info;
+    g_arg_info_load_type(arg_info, &type_info);
+
+    in_self->param_type = PARAM_NORMAL;
+    out_self->param_type = PARAM_NORMAL;
+    inc_counter = true;
+
+    GITypeTag type_tag = g_type_info_get_tag(&type_info);
+    if (type_tag == GI_TYPE_TAG_ARRAY) {
+        if (g_type_info_get_array_type(&type_info) == GI_ARRAY_TYPE_C) {
+            int length_pos = g_type_info_get_array_length(&type_info);
+
+            if (length_pos >= 0) {
+                param_types[length_pos] = PARAM_SKIPPED;
+                in_self->param_type = PARAM_ARRAY;
+                out_self->param_type = PARAM_ARRAY;
+
+                if (length_pos < gi_index) {
+                    // we already collected length_pos, remove it
+                    inc_counter = false;
+                }
+            }
+        }
+    }
+
+    return true;
+}
diff --git a/gi/arg-cache.h b/gi/arg-cache.h
new file mode 100644
index 00000000..8a033857
--- /dev/null
+++ b/gi/arg-cache.h
@@ -0,0 +1,72 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2013 Giovanni Campagna <scampa giovanni gmail com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef GI_ARG_CACHE_H_
+#define GI_ARG_CACHE_H_
+
+#include <config.h>
+
+#include <girepository.h>
+#include <glib.h>
+
+#include <js/TypeDecls.h>
+
+#include "gi/function.h"
+#include "gjs/macros.h"
+
+typedef struct _GjsArgumentCache {
+    bool (*marshal)(struct _GjsArgumentCache*, GIArgument*, JS::HandleValue);
+    bool (*release)(struct _GjsArgumentCache*, GIArgument*);
+    bool (*free)(struct _GjsArgumentCache*);
+
+    // For compatibility
+    GjsParamType param_type;
+
+    union {
+        int dummy;
+    } contents;
+} GjsArgumentCache;
+
+GJS_JSAPI_RETURN_CONVENTION
+bool gjs_arg_cache_build_in_arg(GjsArgumentCache* self,
+                                GjsParamType* param_types, int gi_index,
+                                GIArgInfo* arg, bool& inc_counter,
+                                const char** message_out);
+
+GJS_JSAPI_RETURN_CONVENTION
+bool gjs_arg_cache_build_out_arg(GjsArgumentCache* self,
+                                 GjsParamType* param_types, int gi_index,
+                                 GIArgInfo* arg, bool& inc_counter);
+
+GJS_JSAPI_RETURN_CONVENTION
+bool gjs_arg_cache_build_inout_arg(GjsArgumentCache* in_self,
+                                   GjsArgumentCache* out_self,
+                                   GjsParamType* param_types, int gi_index,
+                                   GIArgInfo* arg, bool& inc_counter);
+
+GJS_JSAPI_RETURN_CONVENTION
+bool gjs_arg_cache_build_return(GjsArgumentCache* self,
+                                GjsParamType* param_types, GICallableInfo* info,
+                                bool& inc_counter);
+
+#endif  // GI_ARG_CACHE_H_
diff --git a/gi/function.cpp b/gi/function.cpp
index 34e9a7ae..1dc15684 100644
--- a/gi/function.cpp
+++ b/gi/function.cpp
@@ -49,6 +49,7 @@
 #include <jspubtd.h>      // for JSProto_TypeError, JSTYPE_FUNCTION
 #include <mozilla/Maybe.h>
 
+#include "gi/arg-cache.h"
 #include "gi/arg.h"
 #include "gi/boxed.h"
 #include "gi/closure.h"
@@ -75,8 +76,10 @@ typedef struct {
     GIFunctionInfo *info;
 
     GjsParamType *param_types;
+    GjsArgumentCache* in_arguments;
+    GjsArgumentCache* out_arguments;
 
-    guint8 expected_js_argc;
+    uint8_t js_in_argc;
     guint8 js_out_argc;
     GIFunctionInvoker invoker;
 } Function;
@@ -849,25 +852,24 @@ gjs_invoke_c_function(JSContext                             *context,
     /* @c_argc is the number of arguments that the underlying C
      * function takes. @gi_argc is the number of arguments the
      * GICallableInfo describes (which does not include "this" or
-     * GError**). @function->expected_js_argc is the number of
+     * GError**). @function->js_in_argc is the number of
      * arguments we expect the JS function to take (which does not
      * include PARAM_SKIPPED args).
      *
      * @args.length() is the number of arguments that were actually passed.
      */
-    if (args.length() > function->expected_js_argc) {
+    if (args.length() > function->js_in_argc) {
         GjsAutoChar name = format_function_name(function, is_method);
-        if (!JS::WarnUTF8(context,
-                          "Too many arguments to %s: expected %d, "
-                          "got %" G_GSIZE_FORMAT,
-                          name.get(), function->expected_js_argc,
-                          args.length()))
+        if (!JS::WarnUTF8(
+                context,
+                "Too many arguments to %s: expected %d, got %" G_GSIZE_FORMAT,
+                name.get(), function->js_in_argc, args.length()))
             return false;
-    } else if (args.length() < function->expected_js_argc) {
+    } else if (args.length() < function->js_in_argc) {
         GjsAutoChar name = format_function_name(function, is_method);
-        gjs_throw(context, "Too few arguments to %s: "
-                  "expected %d, got %" G_GSIZE_FORMAT,
-                  name.get(), function->expected_js_argc, args.length());
+        gjs_throw(context,
+                  "Too few arguments to %s: expected %d, got %" G_GSIZE_FORMAT,
+                  name.get(), function->js_in_argc, args.length());
         return false;
     }
 
@@ -1627,6 +1629,20 @@ static JSFunctionSpec gjs_function_proto_funcs[] = {
 
 static JSFunctionSpec *gjs_function_static_funcs = nullptr;
 
+GJS_JSAPI_RETURN_CONVENTION
+static bool throw_not_introspectable_argument(JSContext* cx,
+                                              GICallableInfo* function,
+                                              GIArgInfo* arg,
+                                              const char* message) {
+    gjs_throw(cx,
+              "Function %s.%s cannot be called: argument '%s' is not "
+              "introspectable. %s",
+              g_base_info_get_namespace(function),
+              g_base_info_get_name(function), g_base_info_get_name(arg),
+              message ? message : "");
+    return false;
+}
+
 GJS_JSAPI_RETURN_CONVENTION
 static bool
 init_cached_function_data (JSContext      *context,
@@ -1635,9 +1651,7 @@ init_cached_function_data (JSContext      *context,
                            GICallableInfo *info)
 {
     guint8 i, n_args;
-    int array_length_pos;
     GError *error = NULL;
-    GITypeInfo return_type;
     GIInfoType info_type;
 
     info_type = g_base_info_get_type((GIBaseInfo *)info);
@@ -1667,110 +1681,122 @@ init_cached_function_data (JSContext      *context,
         }
     }
 
-    g_callable_info_load_return_type((GICallableInfo*)info, &return_type);
-    if (g_type_info_get_tag(&return_type) != GI_TYPE_TAG_VOID)
-        function->js_out_argc += 1;
-
     n_args = g_callable_info_get_n_args((GICallableInfo*) info);
+
+    // We preallocate structures conservatively, then we copy in dynamic memory
+    // only those we need.
+    // Ideally, there would be a 1:1 mapping between GjsArgumentCache and JS
+    // arguments, but we need to handle the case of a length argument happening
+    // before its corresponding array, so we allow for "holes"
+    // (GjsArgumentCache whose param_type is PARAM_SKIPPED), and in_args maps
+    // to GI arguments, while out_args maps to GI arguments and the return
+    // value. To ease handling, out_args is actually one inside the array (so
+    // out_args[-1] is the return value).
+    // In any case, there is a 1:1 mapping between GI arguments and
+    // function->param_types.
+    GjsArgumentCache* in_args = g_newa(GjsArgumentCache, n_args);
+    GjsArgumentCache* out_args = g_newa(GjsArgumentCache, n_args + 1) + 1;
+    memset(in_args, 0, sizeof(GjsArgumentCache) * n_args);
+    memset(out_args - 1, 0, sizeof(GjsArgumentCache) * (n_args + 1));
     function->param_types = g_new0(GjsParamType, n_args);
 
-    array_length_pos = g_type_info_get_array_length(&return_type);
-    if (array_length_pos >= 0 && array_length_pos < n_args)
-        function->param_types[array_length_pos] = PARAM_SKIPPED;
+    bool inc_counter;
+    if (!gjs_arg_cache_build_return(&out_args[-1], function->param_types, info,
+                                    inc_counter)) {
+        g_free(function->param_types);
+        gjs_throw(context,
+                  "Function %s.%s cannot be called: the return value is not "
+                  "introspectable.",
+                  g_base_info_get_namespace(info), g_base_info_get_name(info));
+        return false;
+    }
+    int out_argc = inc_counter ? 1 : 0;
+    int in_argc = 0;
 
     for (i = 0; i < n_args; i++) {
         GIDirection direction;
         GIArgInfo arg_info;
-        GITypeInfo type_info;
-        int destroy = -1;
-        int closure = -1;
-        GITypeTag type_tag;
 
         if (function->param_types[i] == PARAM_SKIPPED)
             continue;
 
         g_callable_info_load_arg((GICallableInfo*) info, i, &arg_info);
-        g_arg_info_load_type(&arg_info, &type_info);
-
         direction = g_arg_info_get_direction(&arg_info);
-        type_tag = g_type_info_get_tag(&type_info);
 
-        if (type_tag == GI_TYPE_TAG_INTERFACE) {
-            GIBaseInfo* interface_info;
-            GIInfoType interface_type;
+        if (direction == GI_DIRECTION_IN) {
+            const char* message = nullptr;
+            if (!gjs_arg_cache_build_in_arg(&in_args[i], function->param_types,
+                                            i, &arg_info, inc_counter,
+                                            &message)) {
+                g_free(function->param_types);
+                return throw_not_introspectable_argument(context, info,
+                                                         &arg_info, message);
+            }
 
-            interface_info = g_type_info_get_interface(&type_info);
-            interface_type = g_base_info_get_type(interface_info);
-            if (interface_type == GI_INFO_TYPE_CALLBACK) {
-                if (strcmp(g_base_info_get_name(interface_info), "DestroyNotify") == 0 &&
-                    strcmp(g_base_info_get_namespace(interface_info), "GLib") == 0) {
-                    // We don't know (yet) what to do with GDestroyNotify
-                    // appearing before a callback. If the callback comes later
-                    // in the argument list, then PARAM_UNKNOWN will be
-                    // overwritten with PARAM_SKIPPED. If no callback follows,
-                    // then this is probably an unsupported function, so the
-                    // value will remain PARAM_UNKNOWN.
-                    function->param_types[i] = PARAM_UNKNOWN;
-                } else {
-                    function->param_types[i] = PARAM_CALLBACK;
-                    function->expected_js_argc += 1;
+            function->param_types[i] = in_args[i].param_type;
+            if (inc_counter)
+                in_argc++;
+        } else if (direction == GI_DIRECTION_INOUT) {
+            if (!gjs_arg_cache_build_inout_arg(&in_args[i], &out_args[i],
+                                               function->param_types, i,
+                                               &arg_info, inc_counter)) {
+                g_free(function->param_types);
+                return throw_not_introspectable_argument(context, info,
+                                                         &arg_info, nullptr);
+            }
 
-                    destroy = g_arg_info_get_destroy(&arg_info);
-                    closure = g_arg_info_get_closure(&arg_info);
+            function->param_types[i] = in_args[i].param_type;
+            if (inc_counter) {
+                in_argc++;
+                out_argc++;
+            }
+        } else { /* GI_DIRECTION_OUT */
+            if (out_args[i].param_type == PARAM_SKIPPED)
+                continue;
 
-                    if (destroy >= 0 && destroy < n_args)
-                        function->param_types[destroy] = PARAM_SKIPPED;
+            if (!gjs_arg_cache_build_out_arg(&out_args[i],
+                                             function->param_types, i,
+                                             &arg_info, inc_counter)) {
+                g_free(function->param_types);
+                return throw_not_introspectable_argument(context, info,
+                                                         &arg_info, nullptr);
+            }
 
-                    if (closure >= 0 && closure < n_args)
-                        function->param_types[closure] = PARAM_SKIPPED;
+            function->param_types[i] = out_args[i].param_type;
+            if (inc_counter)
+                out_argc++;
+        }
+    }
 
-                    if (destroy >= 0 && closure < 0) {
-                        gjs_throw(context, "Function %s.%s has a GDestroyNotify but no user_data, not 
supported",
-                                  g_base_info_get_namespace( (GIBaseInfo*) info),
-                                  g_base_info_get_name( (GIBaseInfo*) info));
-                        g_base_info_unref(interface_info);
-                        g_free(function->param_types);
-                        return false;
-                    }
-                }
-            }
-            g_base_info_unref(interface_info);
-        } else if (type_tag == GI_TYPE_TAG_ARRAY) {
-            if (g_type_info_get_array_type(&type_info) == GI_ARRAY_TYPE_C) {
-                array_length_pos = g_type_info_get_array_length(&type_info);
+    function->in_arguments = g_new(GjsArgumentCache, in_argc);
+    function->out_arguments = g_new(GjsArgumentCache, out_argc);
 
-                if (array_length_pos >= 0 && array_length_pos < n_args) {
-                    GIArgInfo length_arg_info;
+    function->js_in_argc = function->js_out_argc = 0;
+    if (out_args[-1].param_type != PARAM_SKIPPED) {
+        function->out_arguments[0] = out_args[-1];
+        function->js_out_argc = 1;
+    }
 
-                    g_callable_info_load_arg((GICallableInfo*) info, array_length_pos, &length_arg_info);
-                    if (g_arg_info_get_direction(&length_arg_info) != direction) {
-                        gjs_throw(context, "Function %s.%s has an array with different-direction length arg, 
not supported",
-                                  g_base_info_get_namespace( (GIBaseInfo*) info),
-                                  g_base_info_get_name( (GIBaseInfo*) info));
-                        g_free(function->param_types);
-                        return false;
-                    }
+    for (i = 0; i < n_args; i++) {
+        if (function->param_types[i] == PARAM_SKIPPED ||
+            function->param_types[i] == PARAM_UNKNOWN)
+            continue;
 
-                    function->param_types[array_length_pos] = PARAM_SKIPPED;
-                    function->param_types[i] = PARAM_ARRAY;
+        GIArgInfo arg_info;
+        g_callable_info_load_arg(info, i, &arg_info);
+        GIDirection direction = g_arg_info_get_direction(&arg_info);
 
-                    if (array_length_pos < i) {
-                        /* we already collected array_length_pos, remove it */
-                        if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT)
-                            function->expected_js_argc -= 1;
-                        if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT)
-                            function->js_out_argc -= 1;
-                    }
-                }
-            }
+        if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
+            g_assert(function->js_in_argc < in_argc &&
+                     "Mismatch in in-argument count");
+            function->in_arguments[function->js_in_argc] = in_args[i];
+            function->js_in_argc++;
         }
-
-        if (function->param_types[i] == PARAM_NORMAL ||
-            function->param_types[i] == PARAM_ARRAY) {
-            if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT)
-                function->expected_js_argc += 1;
-            if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT)
-                function->js_out_argc += 1;
+        if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
+            g_assert(function->js_out_argc < out_argc &&
+                     "Mismatch in out-argument count");
+            function->out_arguments[function->js_out_argc] = out_args[i];
+            function->js_out_argc++;
         }
     }
 
diff --git a/installed-tests/js/testGIMarshalling.js b/installed-tests/js/testGIMarshalling.js
index 7247078c..b92dd84a 100644
--- a/installed-tests/js/testGIMarshalling.js
+++ b/installed-tests/js/testGIMarshalling.js
@@ -422,15 +422,7 @@ describe('C array with length', function () {
     it('marshals two arrays with the same length parameter', function () {
         const keys = ['one', 'two', 'three'];
         const values = [1, 2, 3];
-
-        // Intercept message; see https://gitlab.gnome.org/GNOME/gjs/issues/267
-        GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_MESSAGE,
-            '*Too many arguments*');
-
         expect(() => GIMarshallingTests.multi_array_key_value_in(keys, values)).not.toThrow();
-
-        GLib.test_assert_expected_messages_internal('Gjs',
-            'testGIMarshalling.js', 0, 'Ignore message');
     });
 
     // Run twice to ensure that copies are correctly made for (transfer full)
diff --git a/meson.build b/meson.build
index 9fb16fa9..3a136ee9 100644
--- a/meson.build
+++ b/meson.build
@@ -351,6 +351,7 @@ gjs_public_headers = [
 
 libgjs_sources = [
     'gi/arg.cpp', 'gi/arg.h',
+    'gi/arg-cache.cpp', 'gi/arg-cache.h',
     'gi/boxed.cpp', 'gi/boxed.h',
     'gi/closure.cpp', 'gi/closure.h',
     'gi/enumeration.cpp', 'gi/enumeration.h',


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