From 87321668e0a1fe50f00b9fbd436183625ba8131c Mon Sep 17 00:00:00 2001 From: Alba Mendez Date: Tue, 20 Apr 2021 02:58:00 +0200 Subject: [PATCH 1/3] BigInt-related API fixes and non-breaking changes --- src/value.cc | 64 ++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/src/value.cc b/src/value.cc index 826bb05..2556f88 100644 --- a/src/value.cc +++ b/src/value.cc @@ -64,9 +64,9 @@ Local GIArgumentToV8(GITypeInfo *type_info, GIArgument *arg, long length, /* For 64-bit integer types, use a float. When JS and V8 adopt * bigger sized integer types, start using those instead. */ case GI_TYPE_TAG_INT64: - return New (arg->v_int64); + return New (arg->v_int64); // FIXME: change case GI_TYPE_TAG_UINT64: - return New (arg->v_uint64); + return New (arg->v_uint64); // FIXME: change case GI_TYPE_TAG_GTYPE: /* c++: gulong */ return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), arg->v_ulong); @@ -697,7 +697,7 @@ bool V8ToGIArgument(GITypeInfo *type_info, GIArgument *arg, Local value, arg->v_int = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_INT64: - arg->v_int64 = Nan::To (value).ToChecked(); + arg->v_int64 = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Int64Value(); break; case GI_TYPE_TAG_UINT8: arg->v_uint8 = Nan::To (value).ToChecked(); @@ -709,7 +709,7 @@ bool V8ToGIArgument(GITypeInfo *type_info, GIArgument *arg, Local value, arg->v_uint = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_UINT64: - arg->v_uint64 = Nan::To (value).ToChecked(); + arg->v_uint64 = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value(); break; case GI_TYPE_TAG_FLOAT: arg->v_float = Nan::To (value).ToChecked(); @@ -718,10 +718,7 @@ bool V8ToGIArgument(GITypeInfo *type_info, GIArgument *arg, Local value, arg->v_double = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_GTYPE: - if (value->IsBigInt()) - arg->v_ulong = value.As()->Uint64Value(); - else - arg->v_ulong = Nan::To (value).ToChecked(); + arg->v_ulong = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value(); break; case GI_TYPE_TAG_UTF8: @@ -835,7 +832,7 @@ bool V8ToOutGIArgument(GITypeInfo *type_info, GIArgument *arg, Local valu *(gint*)arg->v_pointer = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_INT64: - *(gint64*)arg->v_pointer = Nan::To (value).ToChecked(); + *(gint64*)arg->v_pointer = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Int64Value(); break; case GI_TYPE_TAG_UINT8: *(guint8*)arg->v_pointer = Nan::To (value).ToChecked(); @@ -847,7 +844,7 @@ bool V8ToOutGIArgument(GITypeInfo *type_info, GIArgument *arg, Local valu *(guint*)arg->v_pointer = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_UINT64: - *(guint64*)arg->v_pointer = Nan::To (value).ToChecked(); + *(guint64*)arg->v_pointer = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value(); break; case GI_TYPE_TAG_FLOAT: *(gfloat*)arg->v_pointer = Nan::To (value).ToChecked(); @@ -856,10 +853,7 @@ bool V8ToOutGIArgument(GITypeInfo *type_info, GIArgument *arg, Local valu *(gdouble*)arg->v_pointer = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_GTYPE: - if (value->IsBigInt()) - *(gulong*)arg->v_pointer = value.As()->Uint64Value(); - else - *(gulong*)arg->v_pointer = Nan::To (value).ToChecked(); + *(gulong*)arg->v_pointer = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value(); break; case GI_TYPE_TAG_UTF8: @@ -918,12 +912,14 @@ bool CanConvertV8ToGIArgument(GITypeInfo *type_info, Local value, bool ma case GI_TYPE_TAG_UINT16: case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_UINT64: + return value->IsNumber () || value->IsBigInt (); + case GI_TYPE_TAG_FLOAT: case GI_TYPE_TAG_DOUBLE: return value->IsNumber (); case GI_TYPE_TAG_GTYPE: - return value->IsNumber () || value->IsBigInt (); + return value->IsBigInt (); case GI_TYPE_TAG_UTF8: return true; @@ -1300,27 +1296,27 @@ bool CanConvertV8ToGValue(GValue *gvalue, Local value) { if (G_VALUE_HOLDS_BOOLEAN (gvalue)) { return value->IsBoolean() || value->IsNumber(); } else if (G_VALUE_HOLDS_CHAR (gvalue)) { - return value->IsNumber(); + return value->IsNumber() || value->IsBigInt(); } else if (G_VALUE_HOLDS_UCHAR (gvalue)) { - return value->IsNumber(); + return value->IsNumber() || value->IsBigInt(); } else if (G_VALUE_HOLDS_INT (gvalue)) { - return value->IsNumber(); + return value->IsNumber() || value->IsBigInt(); } else if (G_VALUE_HOLDS_UINT (gvalue)) { - return value->IsNumber(); - } else if (G_VALUE_HOLDS_LONG (gvalue)) { - return value->IsNumber(); - } else if (G_VALUE_HOLDS_ULONG (gvalue)) { - return value->IsNumber(); + return value->IsNumber() || value->IsBigInt(); } else if (G_VALUE_HOLDS_INT64 (gvalue)) { - return value->IsNumber(); + return value->IsNumber() || value->IsBigInt(); } else if (G_VALUE_HOLDS_UINT64 (gvalue)) { - return value->IsNumber(); + return value->IsNumber() || value->IsBigInt(); + } else if (G_VALUE_HOLDS_LONG (gvalue)) { + return value->IsNumber() || value->IsBigInt(); + } else if (G_VALUE_HOLDS_ULONG (gvalue)) { + return value->IsNumber() || value->IsBigInt(); } else if (G_VALUE_HOLDS_FLOAT (gvalue)) { return value->IsNumber(); } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) { return value->IsNumber(); } else if (G_VALUE_HOLDS_GTYPE (gvalue)) { - return value->IsNumber(); + return value->IsBigInt(); } else if (G_VALUE_HOLDS_ENUM (gvalue)) { return value->IsNumber(); } else if (G_VALUE_HOLDS_FLAGS (gvalue)) { @@ -1372,19 +1368,19 @@ bool V8ToGValue(GValue *gvalue, Local value, bool mustCopy) { } else if (G_VALUE_HOLDS_UINT (gvalue)) { g_value_set_uint (gvalue, Nan::To (value).ToChecked()); } else if (G_VALUE_HOLDS_LONG (gvalue)) { - g_value_set_long (gvalue, Nan::To (value).ToChecked()); + g_value_set_long (gvalue, value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Int64Value()); } else if (G_VALUE_HOLDS_ULONG (gvalue)) { - g_value_set_ulong (gvalue, Nan::To (value).ToChecked()); + g_value_set_ulong (gvalue, value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value()); } else if (G_VALUE_HOLDS_INT64 (gvalue)) { - g_value_set_int64 (gvalue, Nan::To (value).ToChecked()); + g_value_set_int64 (gvalue, value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Int64Value()); } else if (G_VALUE_HOLDS_UINT64 (gvalue)) { - g_value_set_uint64 (gvalue, Nan::To (value).ToChecked()); + g_value_set_uint64 (gvalue, value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value()); } else if (G_VALUE_HOLDS_FLOAT (gvalue)) { g_value_set_float (gvalue, Nan::To (value).ToChecked()); } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) { g_value_set_double (gvalue, Nan::To (value).ToChecked()); } else if (G_VALUE_HOLDS_GTYPE (gvalue)) { - g_value_set_gtype (gvalue, Nan::To (value).ToChecked()); + g_value_set_gtype (gvalue, (GType) value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value()); } else if (G_VALUE_HOLDS_ENUM (gvalue)) { g_value_set_enum (gvalue, Nan::To (value).ToChecked()); } else if (G_VALUE_HOLDS_FLAGS (gvalue)) { @@ -1444,15 +1440,15 @@ Local GValueToV8(const GValue *gvalue, bool mustCopy) { } else if (G_VALUE_HOLDS_ULONG (gvalue)) { return New(g_value_get_ulong (gvalue)); } else if (G_VALUE_HOLDS_INT64 (gvalue)) { - return New(g_value_get_int64 (gvalue)); + return New(g_value_get_int64 (gvalue)); // FIXME: change } else if (G_VALUE_HOLDS_UINT64 (gvalue)) { - return New(g_value_get_uint64 (gvalue)); + return New(g_value_get_uint64 (gvalue)); // FIXME: change } else if (G_VALUE_HOLDS_FLOAT (gvalue)) { return New(g_value_get_float (gvalue)); } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) { return New(g_value_get_double (gvalue)); } else if (G_VALUE_HOLDS_GTYPE (gvalue)) { - return New(g_value_get_gtype (gvalue)); + return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_gtype(gvalue)); } else if (G_VALUE_HOLDS_ENUM (gvalue)) { return New(g_value_get_enum (gvalue)); } else if (G_VALUE_HOLDS_FLAGS (gvalue)) { From 741732ccc21f974674294472d283b4401e6903f1 Mon Sep 17 00:00:00 2001 From: Alba Mendez Date: Wed, 21 Apr 2021 03:00:34 +0200 Subject: [PATCH 2/3] [BREAKING] convert [u]long & [u]int64 to BigInt side-effects: - g_value_info_get_value() returns an int64, but enums are still 32 bit, so we need to cast to Number --- lib/bootstrap.js | 2 +- lib/inspect.js | 2 +- src/value.cc | 12 ++++++------ tests/object__non-introspected.js | 2 +- tests/union__fields.js | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/bootstrap.js b/lib/bootstrap.js index b5cb518..27145fe 100644 --- a/lib/bootstrap.js +++ b/lib/bootstrap.js @@ -220,7 +220,7 @@ function makeEnum(info) { for (let i = 0; i < nValues; i++) { const valueInfo = GI.enum_info_get_value(info, i); const valueName = getName(valueInfo).toUpperCase() - const value = GI.value_info_get_value(valueInfo) + const value = Number(GI.value_info_get_value(valueInfo)) Object.defineProperty(object, valueName, { configurable: true, enumerable: true, diff --git a/lib/inspect.js b/lib/inspect.js index 2fd0b7c..818eb03 100644 --- a/lib/inspect.js +++ b/lib/inspect.js @@ -203,7 +203,7 @@ function ConstantInfo(info, parent) { function ValueInfo (info, parent) { BaseInfo.call(this, info) - this.value = GI.value_info_get_value(info) + this.value = Number(GI.value_info_get_value(info)) } function PropInfo(info, parent) { diff --git a/src/value.cc b/src/value.cc index 2556f88..0d0b793 100644 --- a/src/value.cc +++ b/src/value.cc @@ -64,9 +64,9 @@ Local GIArgumentToV8(GITypeInfo *type_info, GIArgument *arg, long length, /* For 64-bit integer types, use a float. When JS and V8 adopt * bigger sized integer types, start using those instead. */ case GI_TYPE_TAG_INT64: - return New (arg->v_int64); // FIXME: change + return v8::BigInt::New(Isolate::GetCurrent(), arg->v_int64); case GI_TYPE_TAG_UINT64: - return New (arg->v_uint64); // FIXME: change + return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), arg->v_uint64); case GI_TYPE_TAG_GTYPE: /* c++: gulong */ return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), arg->v_ulong); @@ -1436,13 +1436,13 @@ Local GValueToV8(const GValue *gvalue, bool mustCopy) { } else if (G_VALUE_HOLDS_UINT (gvalue)) { return New(g_value_get_uint (gvalue)); } else if (G_VALUE_HOLDS_LONG (gvalue)) { - return New(g_value_get_long (gvalue)); + return v8::BigInt::New(Isolate::GetCurrent(), g_value_get_long (gvalue)); } else if (G_VALUE_HOLDS_ULONG (gvalue)) { - return New(g_value_get_ulong (gvalue)); + return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_ulong (gvalue)); } else if (G_VALUE_HOLDS_INT64 (gvalue)) { - return New(g_value_get_int64 (gvalue)); // FIXME: change + return v8::BigInt::New(Isolate::GetCurrent(), g_value_get_int64 (gvalue)); } else if (G_VALUE_HOLDS_UINT64 (gvalue)) { - return New(g_value_get_uint64 (gvalue)); // FIXME: change + return v8::BigInt::NewFromUnsigned(Isolate::GetCurrent(), g_value_get_uint64 (gvalue)); } else if (G_VALUE_HOLDS_FLOAT (gvalue)) { return New(g_value_get_float (gvalue)); } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) { diff --git a/tests/object__non-introspected.js b/tests/object__non-introspected.js index 8e50f6c..8cb6f6c 100644 --- a/tests/object__non-introspected.js +++ b/tests/object__non-introspected.js @@ -38,7 +38,7 @@ describe('create non-introspected objected', () => { it('has non-introspected properties', () => { expect(src.pattern, 0) - expect(src.timestampOffset, 0) + expect(src.timestampOffset, BigInt(0)) expect(src.isLive, false) }) diff --git a/tests/union__fields.js b/tests/union__fields.js index 886270b..e74487a 100644 --- a/tests/union__fields.js +++ b/tests/union__fields.js @@ -18,7 +18,7 @@ const tk = new GLib.TokenValue() tk.vInt = Number.MAX_SAFE_INTEGER const result = tk.vInt console.log('Result:', result) - common.assert(result === Number.MAX_SAFE_INTEGER) + common.assert(result === BigInt(Number.MAX_SAFE_INTEGER)) } /* From 515d6a8452eb44fb7ad8304f94d76748a8c8e5ae Mon Sep 17 00:00:00 2001 From: Alba Mendez Date: Wed, 21 Apr 2021 04:14:55 +0200 Subject: [PATCH 3/3] fix conversions turns out ToBigInt() and ToNumber() doesn't perform conversion but only 'implicit' conversion which is not useful. create helpers to extract the numbers from V8 and use them everywhere (except in enum, flags, unichar) --- src/value.cc | 71 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/src/value.cc b/src/value.cc index 0d0b793..5afc5d3 100644 --- a/src/value.cc +++ b/src/value.cc @@ -20,6 +20,7 @@ using v8::Array; using v8::TypedArray; using v8::Integer; +using v8::BigInt; using v8::Local; using v8::Number; using v8::Object; @@ -35,6 +36,22 @@ static void HashPointerToGIArgument (GIArgument *arg, GITypeInfo *type_info); static bool IsUint8Array (GITypeInfo *type_info); +static int64_t V8ToInt64 (Local value) { + if (value->IsBigInt()) + return value.As()->Int64Value(); + return Nan::To(value).ToChecked(); +} + +static int64_t V8ToUint64Strict (Local value) { + return Local::Cast(value)->Uint64Value(); +} + +static int64_t V8ToInt32 (Local value) { + if (value->IsBigInt()) + return value.As()->Int64Value(); + return Nan::To(value).ToChecked(); +} + Local GIArgumentToV8(GITypeInfo *type_info, GIArgument *arg, long length, bool mustCopy) { GITypeTag type_tag = g_type_info_get_tag (type_info); @@ -688,28 +705,28 @@ bool V8ToGIArgument(GITypeInfo *type_info, GIArgument *arg, Local value, arg->v_boolean = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_INT8: - arg->v_int8 = Nan::To (value).ToChecked(); + arg->v_int8 = V8ToInt32(value); break; case GI_TYPE_TAG_INT16: - arg->v_int16 = Nan::To (value).ToChecked(); + arg->v_int16 = V8ToInt32(value); break; case GI_TYPE_TAG_INT32: - arg->v_int = Nan::To (value).ToChecked(); + arg->v_int = V8ToInt32(value); break; case GI_TYPE_TAG_INT64: - arg->v_int64 = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Int64Value(); + arg->v_int64 = V8ToInt64(value); break; case GI_TYPE_TAG_UINT8: - arg->v_uint8 = Nan::To (value).ToChecked(); + arg->v_uint8 = V8ToInt32(value); break; case GI_TYPE_TAG_UINT16: - arg->v_uint16 = Nan::To (value).ToChecked(); + arg->v_uint16 = V8ToInt32(value); break; case GI_TYPE_TAG_UINT32: - arg->v_uint = Nan::To (value).ToChecked(); + arg->v_uint = V8ToInt32(value); break; case GI_TYPE_TAG_UINT64: - arg->v_uint64 = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value(); + arg->v_uint64 = V8ToInt64(value); break; case GI_TYPE_TAG_FLOAT: arg->v_float = Nan::To (value).ToChecked(); @@ -718,7 +735,7 @@ bool V8ToGIArgument(GITypeInfo *type_info, GIArgument *arg, Local value, arg->v_double = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_GTYPE: - arg->v_ulong = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value(); + arg->v_ulong = V8ToUint64Strict(value); break; case GI_TYPE_TAG_UTF8: @@ -823,28 +840,28 @@ bool V8ToOutGIArgument(GITypeInfo *type_info, GIArgument *arg, Local valu *(gboolean*)arg->v_pointer = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_INT8: - *(gint8*)arg->v_pointer = Nan::To (value).ToChecked(); + *(gint8*)arg->v_pointer = V8ToInt32(value); break; case GI_TYPE_TAG_INT16: - *(gint16*)arg->v_pointer = Nan::To (value).ToChecked(); + *(gint16*)arg->v_pointer = V8ToInt32(value); break; case GI_TYPE_TAG_INT32: - *(gint*)arg->v_pointer = Nan::To (value).ToChecked(); + *(gint*)arg->v_pointer = V8ToInt32(value); break; case GI_TYPE_TAG_INT64: - *(gint64*)arg->v_pointer = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Int64Value(); + *(gint64*)arg->v_pointer = V8ToInt64(value); break; case GI_TYPE_TAG_UINT8: - *(guint8*)arg->v_pointer = Nan::To (value).ToChecked(); + *(guint8*)arg->v_pointer = V8ToInt32(value); break; case GI_TYPE_TAG_UINT16: - *(guint16*)arg->v_pointer = Nan::To (value).ToChecked(); + *(guint16*)arg->v_pointer = V8ToInt32(value); break; case GI_TYPE_TAG_UINT32: - *(guint*)arg->v_pointer = Nan::To (value).ToChecked(); + *(guint*)arg->v_pointer = V8ToInt32(value); break; case GI_TYPE_TAG_UINT64: - *(guint64*)arg->v_pointer = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value(); + *(guint64*)arg->v_pointer = V8ToInt64(value); break; case GI_TYPE_TAG_FLOAT: *(gfloat*)arg->v_pointer = Nan::To (value).ToChecked(); @@ -853,7 +870,7 @@ bool V8ToOutGIArgument(GITypeInfo *type_info, GIArgument *arg, Local valu *(gdouble*)arg->v_pointer = Nan::To (value).ToChecked(); break; case GI_TYPE_TAG_GTYPE: - *(gulong*)arg->v_pointer = value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value(); + *(gulong*)arg->v_pointer = V8ToUint64Strict(value); break; case GI_TYPE_TAG_UTF8: @@ -1360,27 +1377,27 @@ bool V8ToGValue(GValue *gvalue, Local value, bool mustCopy) { if (G_VALUE_HOLDS_BOOLEAN (gvalue)) { g_value_set_boolean (gvalue, Nan::To (value).ToChecked()); } else if (G_VALUE_HOLDS_CHAR (gvalue)) { - g_value_set_schar (gvalue, Nan::To (value).ToChecked()); + g_value_set_schar (gvalue, V8ToInt32(value)); } else if (G_VALUE_HOLDS_UCHAR (gvalue)) { - g_value_set_uchar (gvalue, Nan::To (value).ToChecked()); + g_value_set_uchar (gvalue, V8ToInt32(value)); } else if (G_VALUE_HOLDS_INT (gvalue)) { - g_value_set_int (gvalue, Nan::To (value).ToChecked()); + g_value_set_int (gvalue, V8ToInt32(value)); } else if (G_VALUE_HOLDS_UINT (gvalue)) { - g_value_set_uint (gvalue, Nan::To (value).ToChecked()); + g_value_set_uint (gvalue, V8ToInt32(value)); } else if (G_VALUE_HOLDS_LONG (gvalue)) { - g_value_set_long (gvalue, value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Int64Value()); + g_value_set_long (gvalue, V8ToInt64(value)); } else if (G_VALUE_HOLDS_ULONG (gvalue)) { - g_value_set_ulong (gvalue, value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value()); + g_value_set_ulong (gvalue, V8ToInt64(value)); } else if (G_VALUE_HOLDS_INT64 (gvalue)) { - g_value_set_int64 (gvalue, value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Int64Value()); + g_value_set_int64 (gvalue, V8ToInt64(value)); } else if (G_VALUE_HOLDS_UINT64 (gvalue)) { - g_value_set_uint64 (gvalue, value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value()); + g_value_set_uint64 (gvalue, V8ToInt64(value)); } else if (G_VALUE_HOLDS_FLOAT (gvalue)) { g_value_set_float (gvalue, Nan::To (value).ToChecked()); } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) { g_value_set_double (gvalue, Nan::To (value).ToChecked()); } else if (G_VALUE_HOLDS_GTYPE (gvalue)) { - g_value_set_gtype (gvalue, (GType) value->ToBigInt(Nan::GetCurrentContext()).ToLocalChecked()->Uint64Value()); + g_value_set_gtype (gvalue, (GType) V8ToUint64Strict(value)); } else if (G_VALUE_HOLDS_ENUM (gvalue)) { g_value_set_enum (gvalue, Nan::To (value).ToChecked()); } else if (G_VALUE_HOLDS_FLAGS (gvalue)) {