[gjs: 1/2] arg-cache: Always use an unsigned int mask for flags
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs: 1/2] arg-cache: Always use an unsigned int mask for flags
- Date: Sat, 22 Aug 2020 22:48:09 +0000 (UTC)
commit 1716c8667677f57f90be13aca7b7f6d90e0a5ece
Author: Simon McVittie <smcv debian org>
Date: Sat Aug 22 23:25:41 2020 +0100
arg-cache: Always use an unsigned int mask for flags
When retrieving flags from the GIArgumentInfo in
set_return_ffi_arg_from_giargument(), we use the v_int member of the
union (arguably we ought to use the v_uint member for flags, but we
don't), so to avoid undefined behaviour from type-punning we need to
store flags via that same member.
In particular, even if we somehow convince ourselves that a flags type
can have values outside the 32-bit range, it would be incorrect to
store them in the v_int64 or v_uint64 member, because when we read
them back out it'll be via the v_int member. That's formally undefined
behaviour, but in practice will access the first 32 bits of the 64-bit
integer, which is the same as (real_value & 0xffff'ffff) on
little-endian CPUs, but gives a meaningless result on big-endian.
Sure enough, after 1f3984c9 the unit tests start failing on s390x,
which is 64-bit big-endian.
GLib's GFlagsClass.mask and GFlagsValue.value are of type guint
(equivalent to unsigned int), so it can't possibly represent flags
outside the range of guint as a GType anyway. To be consistent with
that (and have reasonable unsigned arithmetic), go via an unsigned
integer.
The corresponding fix for enum types was done as a side-effect of
8b2927b0 "arg-cache: Save space in enum bounds".
Fixes: 1f3984c9 "arg-cache: extend to handle interface types too"
Resolves: https://gitlab.gnome.org/GNOME/gjs/-/issues/341
Signed-off-by: Simon McVittie <smcv debian org>
gi/arg-cache.cpp | 18 +++++++++++-------
gi/arg-cache.h | 2 +-
2 files changed, 12 insertions(+), 8 deletions(-)
---
diff --git a/gi/arg-cache.cpp b/gi/arg-cache.cpp
index ab8c0f84..e7ed2a65 100644
--- a/gi/arg-cache.cpp
+++ b/gi/arg-cache.cpp
@@ -572,11 +572,10 @@ static bool gjs_marshal_flags_in_in(JSContext* cx, GjsArgumentCache* self,
return false;
}
- if (self->contents.flags_mask <= G_MAXUINT32)
- gjs_arg_set<unsigned, GI_TYPE_TAG_INTERFACE>(arg, number);
- else
- gjs_arg_set(arg, uint64_t(number));
-
+ // We cast to unsigned because that's what makes sense, but then we
+ // put it in the v_int slot because that's what we use to unmarshal
+ // flags types at the moment.
+ gjs_arg_set<int, GI_TYPE_TAG_INTERFACE>(arg, static_cast<unsigned>(number));
return true;
}
@@ -1292,8 +1291,13 @@ static void gjs_arg_cache_build_flags_mask(GjsArgumentCache* self,
int n = g_enum_info_get_n_values(enum_info);
for (int i = 0; i < n; i++) {
GjsAutoValueInfo value_info = g_enum_info_get_value(enum_info, i);
- uint64_t value = uint64_t(g_value_info_get_value(value_info));
- mask |= value;
+ int64_t value = g_value_info_get_value(value_info);
+ // From the docs for g_value_info_get_value(): "This will always be
+ // representable as a 32-bit signed or unsigned value. The use of
+ // gint64 as the return type is to allow both."
+ // We stuff both into an unsigned, int-sized field, matching the
+ // internal representation of flags in GLib (which uses guint).
+ mask |= static_cast<unsigned>(value);
}
self->contents.flags_mask = mask;
diff --git a/gi/arg-cache.h b/gi/arg-cache.h
index a7223920..5cf93145 100644
--- a/gi/arg-cache.h
+++ b/gi/arg-cache.h
@@ -93,7 +93,7 @@ struct GjsArgumentCache {
uint32_t enum_min;
uint32_t enum_max;
} enum_type;
- uint64_t flags_mask;
+ unsigned flags_mask;
// string / filename
bool string_is_filename : 1;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]