GList of int not correctly demarshalled on 64-bit big-endian
Debian is in the process of updating to gjs 1.64.0 and mozjs 68.5.0.
On Debian s390x and the unofficial ppc64 port (which are our two 64-bit big-endian architectures), we get this test failure:
10/53 gjs:JS / GIMarshalling ERROR 0.38 s
--- command ---
00:14:30 GI_TYPELIB_PATH='/<<PKGBUILDDIR>>/obj-powerpc64-linux-gnu:/<<PKGBUILDDIR>>/obj-powerpc64-linux-gnu/installed-tests/js' GSETTINGS_SCHEMA_DIR='/<<PKGBUILDDIR>>/obj-powerpc64-linux-gnu/installed-tests/js' ENABLE_GTK='yes' NO_AT_BRIDGE='1' TOP_BUILDDIR='/<<PKGBUILDDIR>>/obj-powerpc64-linux-gnu' G_DEBUG='fatal-warnings,fatal-criticals' G_FILENAME_ENCODING='latin1' GJS_USE_UNINSTALLED_FILES='1' LSAN_OPTIONS='exitcode=23,suppressions=/<<PKGBUILDDIR>>/installed-tests/extra/lsan.supp' LD_LIBRARY_PATH='/<<PKGBUILDDIR>>/obj-powerpc64-linux-gnu:/<<PKGBUILDDIR>>/obj-powerpc64-linux-gnu/installed-tests/js' GJS_PATH='' /<<PKGBUILDDIR>>/obj-powerpc64-linux-gnu/installed-tests/js/minijasmine /<<PKGBUILDDIR>>/obj-powerpc64-linux-gnu/../installed-tests/js/testGIMarshalling.js
...
ok 190 GStrv marshals as an inout parameter
not ok 191 GList of ints with transfer none marshals as a return value
# Message: Expected [ -1, 0, 0, 0 ] to equal [ -1, 0, 1, 2 ].
# Stack:
# testReturnValue/<@/<<PKGBUILDDIR>>/obj-powerpc64-linux-gnu/../installed-tests/js/testGIMarshalling.js:23:48
Bail out! ERROR:/usr/share/gobject-introspection-1.0/tests/gimarshallingtests.c:2671:gi_marshalling_tests_glist_int_none_in: assertion failed (GPOINTER_TO_INT (g_list_nth_data (list, 0)) == -1): (0 == -1)
I think this is because gjs_array_from_g_list()
assigns list->data
to arg.v_pointer
, and then gjs_value_from_g_argument
reads from a member of the arg
union that is not necessarily arg.v_pointer
(type-punning).
On ILP32 architectures, arg.v_pointer
and arg.v_int
are actually the same bytes (I'm not sure whether ISO C guarantees this, but in any reasonable compiler it's true), so this works.
Similarly, on little-endian architectures, the least-significant sizeof(int)
bytes of arg.v_pointer
are the same bytes as arg.v_int
, which happens to produce the right answer for this test. However, on big-endian, the most-significant sizeof(int)
bytes of arg.v_pointer
are the same bytes as arg.v_int
, so writing to arg.v_pointer
and reading from arg.v_int
converts small negative numbers to -1 and small non-negative numbers to 0 (you're basically seeing the number you wanted >> 32
).
I think the expected result is that depending on the element type, gjs_array_from_g_list()
should use:
- boolean, int16, int32:
GPOINTER_TO_INT (list->data)
- uint8, uint16, uint32, gunichar:
GPOINTER_TO_UINT (list->data)
- int64: I'm not sure, maybe
(gint64) GPOINTER_TO_SIZE (list->data)
? - int64: I'm not sure, maybe
(guint64) GPOINTER_TO_SIZE (list->data)
? - float, double: I'm not sure, perhaps this is just not allowed?
- gtype:
GPOINTER_TO_SIZE (list->data)
- interface:
GPOINTER_TO_INT (list->data)
for enums?GPOINTER_TO_UINT (list->data)
for flags?list->data
otherwise - filename, utf8, other pointer types:
list->data
as is currently done
Perhaps an internal function to transform a pointer to the correct slot in a GArgument according to its type would be useful? PyGI has _pygi_hash_pointer_to_arg()
which seems to be basically this.