Submitted By:            Douglas R. Reno <renodr at linuxfromscratch dot org>
Date:                    2020-09-30
Initial Package Version: 1.66.0
Origin:                  Upstream
Upstream Status:         Applied
Description:             Fixes a regression caused by an incomplete fix for a bug
                         that caused gnome-shell to crash.

diff -Naurp gjs-1.66.0.orig/gi/arg-cache.cpp gjs-1.66.0/gi/arg-cache.cpp
--- gjs-1.66.0.orig/gi/arg-cache.cpp	2020-09-12 14:34:36.543117000 -0500
+++ gjs-1.66.0/gi/arg-cache.cpp	2020-09-29 17:22:54.481798835 -0500
@@ -1576,8 +1576,15 @@ bool gjs_arg_cache_build_arg(JSContext*
     *inc_counter_out = true;
 
     GITypeTag type_tag = g_type_info_get_tag(&self->type_info);
-    if (direction == GI_DIRECTION_OUT && type_tag == GI_TYPE_TAG_INTERFACE &&
-        g_arg_info_is_caller_allocates(arg)) {
+    if (direction == GI_DIRECTION_OUT && g_arg_info_is_caller_allocates(arg)) {
+        if (type_tag != GI_TYPE_TAG_INTERFACE) {
+            gjs_throw(cx,
+                      "Unsupported type %s for argument %s with (out "
+                      "caller-allocates)",
+                      g_type_tag_to_string(type_tag), self->arg_name);
+            return false;
+        }
+
         GjsAutoBaseInfo interface_info =
             g_type_info_get_interface(&self->type_info);
         g_assert(interface_info);
diff -Naurp gjs-1.66.0.orig/gi/arg-inl.h gjs-1.66.0/gi/arg-inl.h
--- gjs-1.66.0.orig/gi/arg-inl.h	2020-09-12 14:34:36.543117000 -0500
+++ gjs-1.66.0/gi/arg-inl.h	2020-09-29 18:45:28.782623898 -0500
@@ -167,13 +167,13 @@ inline void gjs_arg_unset(GIArgument* ar
 
 template <typename BigT>
 [[nodiscard]] inline constexpr BigT max_safe_big_number() {
-    return BigT(1) << std::numeric_limits<double>::digits;
+    return (BigT(1) << std::numeric_limits<double>::digits) - 1;
 }
 
 template <typename BigT>
 [[nodiscard]] inline constexpr BigT min_safe_big_number() {
     if constexpr (std::is_signed_v<BigT>)
-        return -(max_safe_big_number<BigT>()) + 1;
+        return -(max_safe_big_number<BigT>());
 
     return std::numeric_limits<BigT>::lowest();
 }
diff -Naurp gjs-1.66.0.orig/gi/function.cpp gjs-1.66.0/gi/function.cpp
--- gjs-1.66.0.orig/gi/function.cpp	2020-09-12 14:34:36.545117100 -0500
+++ gjs-1.66.0/gi/function.cpp	2020-09-29 18:54:09.767605502 -0500
@@ -1060,14 +1060,22 @@ GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(f
 static void
 uninit_cached_function_data (Function *function)
 {
-    g_assert(function->info && "Don't know how to free cache without GI info");
-
     if (function->arguments) {
+        g_assert(function->info &&
+                 "Don't know how to free cache without GI info");
+
         // Careful! function->arguments is offset by one or two elements inside
         // the allocated space, so we have to free index -1 or -2.
         int start_index = g_callable_info_is_method(function->info) ? -2 : -1;
-        int gi_argc = g_callable_info_get_n_args(function->info);
-        for (int ix = start_index; ix < gi_argc; ix++) {
+        int gi_argc = MIN(g_callable_info_get_n_args(function->info),
+                          function->js_in_argc + function->js_out_argc);
+        
+        for (int i = 0; i > gi_argc; i++) {
+            int ix = start_index + i;
+
+            if (!function->arguments[ix].marshallers)
+               break;
+
             if (function->arguments[ix].marshallers->free)
                 function->arguments[ix].marshallers->free(
                     &function->arguments[ix]);
@@ -1076,9 +1084,8 @@ uninit_cached_function_data (Function *f
         g_free(&function->arguments[start_index]);
         function->arguments = nullptr;
     }
-
-    g_base_info_unref(function->info);
-
+    
+    g_clear_pointer(&function->info, g_base_info_unref);
     g_function_invoker_destroy(&function->invoker);
 }
 
@@ -1222,6 +1229,7 @@ init_cached_function_data (JSContext
 
             gjs_throw(context, "Virtual function not implemented: %s",
                       error->message);
+            g_clear_error(&error);
             return false;
         }
 
@@ -1242,6 +1250,11 @@ init_cached_function_data (JSContext
     GjsArgumentCache* arguments =
         g_new0(GjsArgumentCache, n_args + offset) + offset;
 
+    function->arguments = arguments;
+    function->info = g_base_info_ref(info);
+    function->js_in_argc = 0;
+    function->js_out_argc = 0;
+
     if (is_method &&
         !gjs_arg_cache_build_instance(context, &arguments[-2], info))
         return false;
@@ -1251,8 +1264,7 @@ init_cached_function_data (JSContext
                                     &inc_counter))
         return false;
 
-    int out_argc = inc_counter ? 1 : 0;
-    int in_argc = 0;
+    function->js_out_argc = inc_counter ? 1 : 0;
 
     for (i = 0; i < n_args; i++) {
         GIDirection direction;
@@ -1271,13 +1283,13 @@ init_cached_function_data (JSContext
         if (inc_counter) {
             switch (direction) {
                 case GI_DIRECTION_INOUT:
-                    out_argc++;
+                    function->js_out_argc++;
                     [[fallthrough]];
                 case GI_DIRECTION_IN:
-                    in_argc++;
+                    function->js_in_argc++;
                     break;
                 case GI_DIRECTION_OUT:
-                    out_argc++;
+                    function->js_out_argc++;
                     break;
                 default:
                     g_assert_not_reached();
@@ -1285,14 +1297,6 @@ init_cached_function_data (JSContext
         }
     }
 
-    function->arguments = arguments;
-
-    function->js_in_argc = in_argc;
-    function->js_out_argc = out_argc;
-    function->info = info;
-
-    g_base_info_ref((GIBaseInfo*) function->info);
-
     return true;
 }
 
diff -Naurp gjs-1.66.0.orig/installed-tests/js/testGIMarshalling.js gjs-1.66.0/installed-tests/js/testGIMarshalling.js
--- gjs-1.66.0.orig/installed-tests/js/testGIMarshalling.js	2020-09-12 14:34:36.568117400 -0500
+++ gjs-1.66.0/installed-tests/js/testGIMarshalling.js	2020-09-29 18:44:19.998626327 -0500
@@ -525,6 +525,15 @@ describe('GArray', function () {
             expect(GIMarshallingTests.garray_utf8_full_out_caller_allocated())
                 .toEqual(['0', '1', '2']);
         }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/106');
+
+
+         // https://gitlab.gnome.org/GNOME/gjs/-/issues/344
+         // the test should be replaced with the one above when issue
+         // https://gitlab.gnome.org/GNOME/gjs/-/issues/106 is fixed.
+         if('marshals as a transfer-full caller-allocated out parameter throws errors', function () {
+            expect(() => GIMarshallingTests.garray_utf8_full_out_caller_allocated())
+               .toThrowError(/Unsupported type array.*\(out caller-allocates\)/);
+         });
     });
 
     it('marshals boxed structs as a transfer-full return value', function () {
diff -Naurp gjs-1.66.0.orig/test/gjs-tests.cpp gjs-1.66.0/test/gjs-tests.cpp
--- gjs-1.66.0.orig/test/gjs-tests.cpp	2020-09-12 14:34:36.577117400 -0500
+++ gjs-1.66.0/test/gjs-tests.cpp	2020-09-29 18:57:32.010598360 -0500
@@ -23,6 +23,7 @@
 
 #include <config.h>
 
+#include <stdint.h>
 #include <string.h>  // for size_t, strlen
 
 #include <string>  // for u16string, u32string
@@ -39,7 +40,9 @@
 #include <js/Value.h>
 #include <js/ValueArray.h>
 #include <jsapi.h>
+#include <jspubtd.h> // for JSProto_Number
 
+#include "gi/arg-inl.h"
 #include "gjs/context.h"
 #include "gjs/error-types.h"
 #include "gjs/jsapi-util.h"
@@ -398,6 +401,30 @@ gjstest_test_profiler_start_stop(void)
         g_message("Temp profiler file not deleted");
 }
 
+static void gjstest_test_safe_integer_max(GjsUnitTestFixture* fx, const void*) {
+   JS::RootedObject number_class_object(fx->cx);
+   JS::RootedValue safe_value(fx->cx);
+
+   g_assert_true(
+         JS_GetClassObject(fx->cx, JSProto_Number, &number_class_object));
+   g_assert_true(JS_GetProperty(fx->cx, number_class_object,
+                                "MAX_SAFE_INTEGER", &safe_value));
+
+   g_assert_cmpint(safe_value.toNumber(), ==, max_safe_big_number<int64_t>());
+}
+
+static void gjstest_test_safe_integer_min(GjsUnitTestFixture* fx, const void*) {
+   JS::RootedObject number_class_object(fx->cx);
+   JS::RootedValue safe_value(fx->cx);
+
+   g_assert_true(
+       JS_GetClassObject(fx->cx, JSProto_Number, &number_class_object));
+   g_assert_true(JS_GetProperty(fx->cx, number_class_object, 
+                                "MIN_SAFE_INTEGER", &safe_value));
+
+   g_assert_cmpint(safe_value.toNumber(), ==, min_safe_big_number<int64_t>());
+}
+
 int
 main(int    argc,
      char **argv)
@@ -444,6 +471,11 @@ main(int    argc,
     ADD_JSAPI_UTIL_TEST("debug_string/object-with-complicated-to-string",
                         test_jsapi_util_debug_string_object_with_complicated_to_string);
 
+    ADD_JSAPI_UTIL_TEST("gi/args/safe-integer/max",
+                        gjstest_test_safe_integer_max);
+    ADD_JSAPI_UTIL_TEST("gi/args/safe-integer/min",
+                        gjstest_test_safe_integer_min);
+
 #undef ADD_JSAPI_UTIL_TEST
 
     gjs_test_add_tests_for_coverage ();
