[gjs: 3/5] value: Support converting BigInt to (u)int64 values
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs: 3/5] value: Support converting BigInt to (u)int64 values
- Date: Fri, 4 Feb 2022 06:17:19 +0000 (UTC)
commit 20da419bf09d0d73b4b08d58cbe2da9f22b13677
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date: Thu Oct 8 04:25:26 2020 +0200
value: Support converting BigInt to (u)int64 values
BigInt values are now accepted as parameters to C functions that take
64-bit integers.
For now BigInts are only accepted for 64-bit parameters. We may allow
BigInt for any parameter in the future (once we've checked that we don't
go out-of-range), for now we assume that BigInt values are used only for
functions taking a type that can mostly handle them.
For bounds checks, given that we don't have a bigger value holder than
(u)int64_t, we have to create a new wrapper to avoid taking the fast path
in gjs_arg_set_from_js_value(), and without having to hack
type_has_js_getter().
As per this, now for 64bit integers we use TypeWrapper as the type holder
that has conversion operators to the base type, and for which we can then
add a js_value_to_c_checked() specialization.
See: #271
gi/arg-inl.h | 17 +----
gi/js-value-inl.h | 74 ++++++++++++++++++++-
gi/value.cpp | 26 ++++++--
installed-tests/js/testGIMarshalling.js | 111 +++++++++++++++++++++++++++++---
installed-tests/js/testGObjectClass.js | 72 +++++++++++++++++++++
installed-tests/js/testRegress.js | 78 +++++++++++++++++++++-
test/gjs-tests.cpp | 7 +-
7 files changed, 353 insertions(+), 32 deletions(-)
---
diff --git a/gi/arg-inl.h b/gi/arg-inl.h
index 19de766b9..5fb19e3b1 100644
--- a/gi/arg-inl.h
+++ b/gi/arg-inl.h
@@ -173,19 +173,6 @@ template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
// Implementation to store rounded (u)int64_t numbers into double
-template <typename BigT>
-[[nodiscard]] inline constexpr BigT max_safe_big_number() {
- 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>());
-
- return std::numeric_limits<BigT>::lowest();
-}
-
template <typename BigT>
[[nodiscard]] inline std::enable_if_t<std::is_integral_v<BigT> &&
(std::numeric_limits<BigT>::max() >
@@ -194,8 +181,8 @@ template <typename BigT>
gjs_arg_get_maybe_rounded(GIArgument* arg) {
BigT val = gjs_arg_get<BigT>(arg);
- if (val < min_safe_big_number<BigT>() ||
- val > max_safe_big_number<BigT>()) {
+ if (val < Gjs::min_safe_big_number<BigT>() ||
+ val > Gjs::max_safe_big_number<BigT>()) {
g_warning(
"Value %s cannot be safely stored in a JS Number "
"and may be rounded",
diff --git a/gi/js-value-inl.h b/gi/js-value-inl.h
index 1aa12bda2..ff2358d25 100644
--- a/gi/js-value-inl.h
+++ b/gi/js-value-inl.h
@@ -15,6 +15,7 @@
#include <glib-object.h>
#include <glib.h>
+#include <js/BigInt.h>
#include <js/Conversions.h>
#include <js/RootingAPI.h>
#include <js/TypeDecls.h>
@@ -27,6 +28,17 @@
namespace Gjs {
+template <typename T>
+struct TypeWrapper {
+ constexpr TypeWrapper() : m_value(0) {}
+ explicit constexpr TypeWrapper(T v) : m_value(v) {}
+ constexpr operator T() const { return m_value; }
+ constexpr operator T() { return m_value; }
+
+ private:
+ T m_value;
+};
+
namespace JsValueHolder {
template <typename T1, typename T2>
@@ -81,7 +93,9 @@ constexpr auto get_strict() {
template <typename T>
constexpr auto get_relaxed() {
- if constexpr (type_fits<T, int32_t>())
+ if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>)
+ return TypeWrapper<T>{};
+ else if constexpr (type_fits<T, int32_t>())
return int32_t{};
else if constexpr (type_fits<T, uint16_t>())
return uint32_t{};
@@ -135,12 +149,20 @@ GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
template <>
GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
JSContext* cx, const JS::HandleValue& value, int64_t* out) {
+ if (value.isBigInt()) {
+ *out = JS::ToBigInt64(value.toBigInt());
+ return true;
+ }
return JS::ToInt64(cx, value, out);
}
template <>
GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
JSContext* cx, const JS::HandleValue& value, uint64_t* out) {
+ if (value.isBigInt()) {
+ *out = JS::ToBigUint64(value.toBigInt());
+ return true;
+ }
return JS::ToUint64(cx, value, out);
}
@@ -194,6 +216,19 @@ GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
return true;
}
+template <typename BigT>
+[[nodiscard]] inline constexpr BigT max_safe_big_number() {
+ 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>());
+
+ return std::numeric_limits<BigT>::lowest();
+}
+
template <typename WantedType, typename T>
GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
JSContext* cx, const JS::HandleValue& value, T* out, bool* out_of_range) {
@@ -203,6 +238,27 @@ GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
std::numeric_limits<WantedType>::lowest(),
"Container can't contain wanted type");
+ if constexpr (std::is_same_v<WantedType, uint64_t> ||
+ std::is_same_v<WantedType, int64_t>) {
+ if (out_of_range) {
+ JS::BigInt* bi = nullptr;
+ *out_of_range = false;
+
+ if (value.isBigInt()) {
+ bi = value.toBigInt();
+ } else if (value.isNumber()) {
+ bi = JS::NumberToBigInt(cx, value.toNumber());
+ if (!bi)
+ return false;
+ }
+
+ if (bi) {
+ *out_of_range = Gjs::bigint_is_out_of_range(bi, out);
+ return true;
+ }
+ }
+ }
+
if constexpr (std::is_same_v<WantedType, T>)
return js_value_to_c(cx, value, out);
@@ -230,4 +286,20 @@ GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
}
}
+template <typename WantedType>
+GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
+ JSContext* cx, const JS::HandleValue& value, TypeWrapper<WantedType>* out,
+ bool* out_of_range) {
+ static_assert(std::is_integral_v<WantedType>);
+
+ WantedType wanted_out;
+ if (!js_value_to_c_checked<WantedType>(cx, value, &wanted_out,
+ out_of_range))
+ return false;
+
+ *out = TypeWrapper<WantedType>{wanted_out};
+
+ return true;
+}
+
} // namespace Gjs
diff --git a/gi/value.cpp b/gi/value.cpp
index 96f5cf8d0..4a0e27548 100644
--- a/gi/value.cpp
+++ b/gi/value.cpp
@@ -13,6 +13,7 @@
#include <glib-object.h>
#include <glib.h>
+#include <js/BigInt.h>
#include <js/CharacterEncoding.h>
#include <js/Conversions.h>
#include <js/Exception.h>
@@ -321,6 +322,17 @@ static bool gjs_value_guess_g_type(JSContext* context, JS::Value value,
*gtype_out = G_TYPE_BOOLEAN;
return true;
}
+ if (value.isBigInt()) {
+ // Assume that if the value is negative or within the int64_t limit,
+ // then we're handling a signed integer, otherwise unsigned.
+ int64_t ignored;
+ if (JS::BigIntIsNegative(value.toBigInt()) ||
+ JS::BigIntFits(value.toBigInt(), &ignored))
+ *gtype_out = G_TYPE_INT64;
+ else
+ *gtype_out = G_TYPE_UINT64;
+ return true;
+ }
if (value.isObject()) {
JS::RootedObject obj(context, &value.toObject());
return gjs_gtype_get_actual_gtype(context, obj, gtype_out);
@@ -427,10 +439,13 @@ gjs_value_to_g_value_internal(JSContext *context,
}
} else if (gtype == G_TYPE_INT64) {
gint64 i;
- if (Gjs::js_value_to_c(context, value, &i)) {
+ if (Gjs::js_value_to_c_checked<int64_t>(context, value, &i,
+ &out_of_range) &&
+ !out_of_range) {
g_value_set_int64(gvalue, i);
} else {
- return throw_expect_type(context, value, "64-bit integer");
+ return throw_expect_type(context, value, "64-bit integer", 0,
+ out_of_range);
}
} else if (gtype == G_TYPE_DOUBLE) {
gdouble d;
@@ -457,10 +472,13 @@ gjs_value_to_g_value_internal(JSContext *context,
}
} else if (gtype == G_TYPE_UINT64) {
guint64 i;
- if (Gjs::js_value_to_c(context, value, &i)) {
+ if (Gjs::js_value_to_c_checked<uint64_t>(context, value, &i,
+ &out_of_range) &&
+ !out_of_range) {
g_value_set_uint64(gvalue, i);
} else {
- return throw_expect_type(context, value, "unsigned 64-bit integer");
+ return throw_expect_type(context, value, "unsigned 64-bit integer",
+ 0, out_of_range);
}
} else if (gtype == G_TYPE_BOOLEAN) {
/* JS::ToBoolean() can't fail */
diff --git a/installed-tests/js/testGIMarshalling.js b/installed-tests/js/testGIMarshalling.js
index 7d5249194..72eba0835 100644
--- a/installed-tests/js/testGIMarshalling.js
+++ b/installed-tests/js/testGIMarshalling.js
@@ -135,17 +135,33 @@ const Limits = {
utype: 'size',
},
};
+const BigIntLimits = {
+ int64: {
+ min: -(2n ** 63n),
+ max: 2n ** 63n - 1n,
+ umax: 2n ** 64n - 1n,
+ },
+ long: {},
+ ssize: {
+ utype: 'size',
+ },
+};
+
Object.assign(Limits.short, Limits.int16);
Object.assign(Limits.int, Limits.int32);
// Platform dependent sizes; expand definitions as needed
-if (GLib.SIZEOF_LONG === 8)
+if (GLib.SIZEOF_LONG === 8) {
Object.assign(Limits.long, Limits.int64);
-else
+ Object.assign(BigIntLimits.long, BigIntLimits.int64);
+} else {
Object.assign(Limits.long, Limits.int32);
-if (GLib.SIZEOF_SSIZE_T === 8)
+}
+if (GLib.SIZEOF_SSIZE_T === 8) {
Object.assign(Limits.ssize, Limits.int64);
-else
+ Object.assign(BigIntLimits.ssize, BigIntLimits.int64);
+} else {
Object.assign(Limits.ssize, Limits.int32);
+}
// Functions for dealing with tests that require or return unsafe 64-bit ints,
// until we get BigInts.
@@ -239,6 +255,21 @@ describe('Integer', function () {
});
});
+describe('BigInt', function () {
+ Object.entries(BigIntLimits).forEach(([type, {min, max, umax, utype = `u${type}`}]) => {
+ describe(`${type}-typed`, function () {
+ it('marshals signed value as an in parameter', function () {
+ expect(() => GIMarshallingTests[`${type}_in_max`](max)).not.toThrow();
+ expect(() => GIMarshallingTests[`${type}_in_min`](min)).not.toThrow();
+ });
+
+ it('marshals unsigned value as an in parameter', function () {
+ expect(() => GIMarshallingTests[`${utype}_in`](umax)).not.toThrow();
+ });
+ });
+ });
+});
+
describe('Floating point', function () {
const FloatLimits = {
float: {
@@ -744,9 +775,10 @@ describe('GValue', function () {
},
});
- xit('marshals as an int64 in parameter', function () {
- expect(() => GIMarshallingTests.gvalue_int64_in(Limits.int64.max)).not.toThrow();
- }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/271');
+ it('marshals as an int64 in parameter', function () {
+ expect(() => GIMarshallingTests.gvalue_int64_in(BigIntLimits.int64.max))
+ .not.toThrow();
+ });
it('type objects can be converted from primitive-like types', function () {
expect(() => GIMarshallingTests.gvalue_in_with_type(42, GObject.Int))
@@ -1699,7 +1731,12 @@ describe('Interface', function () {
Implements: [GIMarshallingTests.Interface3],
}, class I3Impl extends GObject.Object {
vfunc_test_variant_array_in(variantArray) {
- this.stuff = variantArray.map(v => v.deepUnpack());
+ this.stuff = variantArray.map(v => {
+ const bit64 = this.bigInt &&
+ (v.is_of_type(new GLib.VariantType('t')) ||
+ v.is_of_type(new GLib.VariantType('x')));
+ return warn64(bit64, () => v.deepUnpack());
+ });
}
});
const i3 = new I3Impl();
@@ -1707,8 +1744,22 @@ describe('Interface', function () {
new GLib.Variant('b', true),
new GLib.Variant('s', 'hello'),
new GLib.Variant('i', 42),
+ new GLib.Variant('t', 43),
+ new GLib.Variant('x', 44),
+ ]);
+ expect(i3.stuff).toEqual([true, 'hello', 42, 43, 44]);
+
+ i3.bigInt = true;
+ i3.test_variant_array_in([
+ new GLib.Variant('x', BigIntLimits.int64.min),
+ new GLib.Variant('x', BigIntLimits.int64.max),
+ new GLib.Variant('t', BigIntLimits.int64.umax),
+ ]);
+ expect(i3.stuff).toEqual([
+ Limits.int64.min,
+ Limits.int64.max,
+ Limits.int64.umax,
]);
- expect(i3.stuff).toEqual([true, 'hello', 42]);
});
});
@@ -1870,6 +1921,16 @@ describe('GObject properties', function () {
expect(obj[`some_${type}`]).toEqual(value2);
});
}
+
+ function testPropertyGetSetBigInt(type, value1, value2) {
+ it(`gets and sets a ${type} property with a bigint`, function () {
+ obj[`some_${type}`] = value1;
+ expect(obj[`some_${type}`]).toEqual(Number(value1));
+ obj[`some_${type}`] = value2;
+ expect(obj[`some_${type}`]).toEqual(Number(value2));
+ });
+ }
+
testPropertyGetSet('boolean', true, false);
testPropertyGetSet('char', 42, 64);
testPropertyGetSet('uchar', 42, 64);
@@ -1878,10 +1939,37 @@ describe('GObject properties', function () {
testPropertyGetSet('long', 42, 64);
testPropertyGetSet('ulong', 42, 64);
testPropertyGetSet('int64', 42, 64);
+ testPropertyGetSet('int64', Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
+ testPropertyGetSetBigInt('int64', BigIntLimits.int64.min, BigIntLimits.int64.max);
testPropertyGetSet('uint64', 42, 64);
+ testPropertyGetSetBigInt('uint64', BigIntLimits.int64.max, BigIntLimits.int64.umax);
testPropertyGetSet('string', 'Gjs', 'is cool!',
'https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/268');
+ it('get and sets out-of-range values throws', function () {
+ expect(() => {
+ obj.some_int64 = Limits.int64.max;
+ }).toThrowError(/out of range/);
+ expect(() => {
+ obj.some_int64 = BigIntLimits.int64.max + 1n;
+ }).toThrowError(/out of range/);
+ expect(() => {
+ obj.some_int64 = BigIntLimits.int64.min - 1n;
+ }).toThrowError(/out of range/);
+ expect(() => {
+ obj.some_int64 = BigIntLimits.int64.umax;
+ }).toThrowError(/out of range/);
+ expect(() => {
+ obj.some_int64 = -BigIntLimits.int64.umax;
+ }).toThrowError(/out of range/);
+ expect(() => {
+ obj.some_uint64 = Limits.int64.min;
+ }).toThrowError(/out of range/);
+ expect(() => {
+ obj.some_uint64 = BigIntLimits.int64.umax + 100n;
+ }).toThrowError(/out of range/);
+ });
+
it('gets and sets a float property', function () {
obj.some_float = Math.E;
expect(obj.some_float).toBeCloseTo(Math.E);
@@ -1901,8 +1989,13 @@ describe('GObject properties', function () {
new GIMarshallingTests.BoxedStruct({long_: 42}));
testPropertyGetSet('boxed_glist', null, null);
testPropertyGetSet('gvalue', 42, 'foo');
+ testPropertyGetSetBigInt('gvalue', BigIntLimits.int64.umax, BigIntLimits.int64.min);
testPropertyGetSet('variant', new GLib.Variant('b', true),
new GLib.Variant('s', 'hello'));
+ testPropertyGetSet('variant', new GLib.Variant('x', BigIntLimits.int64.min),
+ new GLib.Variant('x', BigIntLimits.int64.max));
+ testPropertyGetSet('variant', new GLib.Variant('t', BigIntLimits.int64.max),
+ new GLib.Variant('t', BigIntLimits.int64.umax));
testPropertyGetSet('object', new GObject.Object(),
new GIMarshallingTests.Object({int: 42}));
testPropertyGetSet('flags', GIMarshallingTests.Flags.VALUE2,
diff --git a/installed-tests/js/testGObjectClass.js b/installed-tests/js/testGObjectClass.js
index 41e7a8e9e..ee07b61d8 100644
--- a/installed-tests/js/testGObjectClass.js
+++ b/installed-tests/js/testGObjectClass.js
@@ -153,6 +153,17 @@ const MyCustomInit = GObject.registerClass(class MyCustomInit extends GObject.Ob
}
});
+const BigIntLimits = {
+ int64: {
+ min: -0x7fff_ffff_ffff_ffffn - 1n,
+ max: 0x7fff_ffff_ffff_ffffn,
+ },
+ uint64: {
+ min: 0n,
+ max: 0xffff_ffff_ffff_ffffn,
+ },
+};
+
const NoName = GObject.registerClass(class extends GObject.Object {});
describe('GObject class with decorator', function () {
@@ -365,6 +376,67 @@ describe('GObject class with decorator', function () {
expect(() => new InterfacePropObject({file})).not.toThrow();
});
+ it('can have an int64 property', function () {
+ const PropInt64 = GObject.registerClass({
+ Properties: {
+ 'int64': GObject.ParamSpec.int64('int64', 'int64', 'int64',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ BigIntLimits.int64.min, BigIntLimits.int64.max, 0),
+ },
+ }, class PropInt64 extends GObject.Object {});
+
+ let int64 = BigIntLimits.int64.max - 5n;
+ let obj = new PropInt64({int64});
+ expect(obj.int64).toEqual(Number(int64));
+
+ int64 = BigIntLimits.int64.min + 555n;
+ obj = new PropInt64({int64});
+ expect(obj.int64).toEqual(Number(int64));
+ });
+
+ it('can have a default int64 property', function () {
+ const defaultValue = BigIntLimits.int64.max - 1000n;
+ const PropInt64Init = GObject.registerClass({
+ Properties: {
+ 'int64': GObject.ParamSpec.int64('int64', 'int64', 'int64',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ BigIntLimits.int64.min, BigIntLimits.int64.max,
+ defaultValue),
+ },
+ }, class PropDefaultInt64Init extends GObject.Object {});
+
+ const obj = new PropInt64Init();
+ expect(obj.int64).toEqual(Number(defaultValue));
+ });
+
+ it('can have an uint64 property', function () {
+ const PropUint64 = GObject.registerClass({
+ Properties: {
+ 'uint64': GObject.ParamSpec.uint64('uint64', 'uint64', 'uint64',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ 0, BigIntLimits.uint64.max, 0),
+ },
+ }, class PropUint64 extends GObject.Object {});
+
+ const uint64 = BigIntLimits.uint64.max - 5n;
+ const obj = new PropUint64({uint64});
+ expect(obj.uint64).toEqual(Number(uint64));
+ });
+
+ it('can have a default uint64 property', function () {
+ const defaultValue = BigIntLimits.uint64.max;
+ const PropUint64Init = GObject.registerClass({
+ Properties: {
+ 'uint64': GObject.ParamSpec.uint64('uint64', 'uint64', 'uint64',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ 0n, BigIntLimits.uint64.max, defaultValue),
+ },
+ }, class PropDefaultUint64Init extends GObject.Object {});
+
+ const obj = new PropUint64Init();
+ expect(obj.uint64).toEqual(Number(defaultValue));
+ });
+
it('can override a property from the parent class', function () {
const OverrideObject = GObject.registerClass({
Properties: {
diff --git a/installed-tests/js/testRegress.js b/installed-tests/js/testRegress.js
index 094d41509..91eff3544 100644
--- a/installed-tests/js/testRegress.js
+++ b/installed-tests/js/testRegress.js
@@ -10,6 +10,23 @@ const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GObject = imports.gi.GObject;
+function expectWarn64(callable) {
+ GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_WARNING,
+ '*cannot be safely stored*');
+ const ret = callable();
+ GLib.test_assert_expected_messages_internal('Gjs',
+ 'testRegress.js', 0, 'Ignore message');
+ return ret;
+}
+
+const bit64Types = ['uint64', 'int64'];
+if (GLib.SIZEOF_LONG === 8)
+ bit64Types.push('long', 'ulong');
+if (GLib.SIZEOF_SIZE_T === 8)
+ bit64Types.push('size');
+if (GLib.SIZEOF_SSIZE_T === 8)
+ bit64Types.push('ssize');
+
describe('Life, the Universe and Everything', function () {
it('includes null return value', function () {
expect(Regress.test_return_allow_none()).toBeNull();
@@ -29,12 +46,25 @@ describe('Life, the Universe and Everything', function () {
expect(Regress[method](42)).toBe(42);
expect(Regress[method](-42)).toBe(-42);
expect(Regress[method](undefined)).toBe(0);
+
+ if (bits >= 64) {
+ expect(Regress[method](42n)).toBe(42);
+ expect(Regress[method](-42n)).toBe(-42);
+ } else {
+ expect(() => Regress[method](42n)).toThrow();
+ expect(() => Regress[method](-42n)).toThrow();
+ }
});
it(`includes unsigned ${bits}-bit integers`, function () {
const method = `test_uint${bits}`;
expect(Regress[method](42)).toBe(42);
expect(Regress[method](undefined)).toBe(0);
+
+ if (bits >= 64)
+ expect(Regress[method](42n)).toEqual(42);
+ else
+ expect(() => Regress[method](42n)).toThrow();
});
});
@@ -48,6 +78,14 @@ describe('Life, the Universe and Everything', function () {
expect(Number.isNaN(Regress[method](undefined))).toBeTruthy();
else
expect(Regress[method](undefined)).toBe(0);
+
+ if (bit64Types.includes(type)) {
+ expect(Regress[method](42n)).toBe(42);
+ expect(Regress[method](-42n)).toBe(-42);
+ } else {
+ expect(() => Regress[method](42n)).toThrow();
+ expect(() => Regress[method](-42n)).toThrow();
+ }
});
});
@@ -56,17 +94,55 @@ describe('Life, the Universe and Everything', function () {
const method = `test_${type}`;
expect(Regress[method](42)).toBe(42);
expect(Regress[method](undefined)).toBe(0);
+
+ if (bit64Types.includes(type))
+ expect(Regress[method](42n)).toBe(42);
+ else
+ expect(() => Regress[method](42n)).toThrow();
});
});
describe('No implicit conversion to unsigned', function () {
['uint8', 'uint16', 'uint32', 'uint64', 'uint', 'size'].forEach(type => {
it(`for ${type}`, function () {
- expect(() => Regress[`test_${type}`](-42)).toThrow();
+ expect(() => Regress[`test_${type}`](-42)).toThrowError(/out of range/);
+
+ if (bit64Types.includes(type))
+ expect(() => Regress[`test_${type}`](-42n)).toThrowError(/out of range/);
+ else
+ expect(() => Regress[`test_${type}`](-42n)).toThrow();
});
});
});
+ describe('(u)int64 numeric values', function () {
+ const minInt64 = -(2n ** 63n);
+ const maxInt64 = 2n ** 63n - 1n;
+ const maxUint64 = 2n ** 64n - 1n;
+
+ ['uint64', 'int64', 'long', 'ulong', 'size', 'ssize'].forEach(type => {
+ if (!bit64Types.includes(type))
+ return;
+ const signed = ['int64', 'long', 'ssize'].includes(type);
+ const limits = {
+ min: signed ? minInt64 : 0n,
+ max: signed ? maxInt64 : maxUint64,
+ };
+ const testFunc = Regress[`test_${type}`];
+
+ it(`can use numeric limits for ${type}`, function () {
+ expect(expectWarn64(() => testFunc(limits.max)))
+ .toEqual(Number(limits.max));
+
+ if (signed) {
+ expect(expectWarn64(() => testFunc(limits.min)))
+ .toEqual(Number(limits.min));
+ }
+ });
+ });
+ });
+
+
it('includes wide characters', function () {
expect(Regress.test_unichar('c')).toBe('c');
expect(Regress.test_unichar('')).toBe('');
diff --git a/test/gjs-tests.cpp b/test/gjs-tests.cpp
index aaffc779a..329c081c3 100644
--- a/test/gjs-tests.cpp
+++ b/test/gjs-tests.cpp
@@ -33,6 +33,7 @@
#include <mozilla/Span.h> // for MakeStringSpan
#include "gi/arg-inl.h"
+#include "gi/js-value-inl.h"
#include "gjs/context.h"
#include "gjs/error-types.h"
#include "gjs/jsapi-util.h"
@@ -837,7 +838,8 @@ static void gjstest_test_safe_integer_max(GjsUnitTestFixture* fx, const void*) {
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>());
+ g_assert_cmpint(safe_value.toNumber(), ==,
+ Gjs::max_safe_big_number<int64_t>());
}
static void gjstest_test_safe_integer_min(GjsUnitTestFixture* fx, const void*) {
@@ -849,7 +851,8 @@ static void gjstest_test_safe_integer_min(GjsUnitTestFixture* fx, const void*) {
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>());
+ g_assert_cmpint(safe_value.toNumber(), ==,
+ Gjs::min_safe_big_number<int64_t>());
}
static void gjstest_test_args_set_get_unset() {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]