@gjsify/node-gi 0.64.0 → 0.64.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cairo.d.ts +63 -0
- package/cairo.js +118 -17
- package/gettext.js +29 -7
- package/gi.js +204 -87
- package/host.nativescript.js +6 -0
- package/host.node.js +6 -0
- package/index.js +192 -76
- package/overrides/gio-settings.js +2 -24
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/node_gi.node +0 -0
- package/prebuilds/darwin-x64/node_gi.node +0 -0
- package/prebuilds/linux-arm64/node_gi.node +0 -0
- package/prebuilds/linux-x64/node_gi.node +0 -0
- package/prebuilds/win32-arm64/node_gi.node +0 -0
- package/prebuilds/win32-x64/node_gi.node +0 -0
- package/src/cairo.cc +162 -1
- package/src/calls.cc +59 -26
- package/src/class.cc +15 -0
- package/src/common.h +177 -1
- package/src/marshal.cc +77 -26
- package/src/object.cc +79 -10
- package/src/private.cc +33 -0
package/src/calls.cc
CHANGED
|
@@ -1242,6 +1242,57 @@ static GIFunctionInfo* ResolveInstanceMethod(GIRepository* repo, GType gtype, GI
|
|
|
1242
1242
|
return func;
|
|
1243
1243
|
}
|
|
1244
1244
|
|
|
1245
|
+
// Resolved instance methods, keyed by the instance's concrete GType and the name
|
|
1246
|
+
// as JS spelled it. The resolution above is the expensive half of a GI call —
|
|
1247
|
+
// dup the default repository, walk the GType parents for the nearest
|
|
1248
|
+
// GIObjectInfo, then search own + interface + inherited method tables twice (the
|
|
1249
|
+
// literal name, then the snake_case alias) — and it repeats IDENTICALLY on every
|
|
1250
|
+
// call, because a typelib's method tables do not change at runtime. MEASURED on
|
|
1251
|
+
// this machine (gjs 1.88.1 / GLib 2.88.3): `set_property` on a registerClass
|
|
1252
|
+
// subclass cost 8224 ns through callMethod against 2815 ns for the same
|
|
1253
|
+
// g_object_set_property through setProperty — 5.4 µs of pure re-resolution per
|
|
1254
|
+
// call, which is most of what a property write on the real-GObject engine spends.
|
|
1255
|
+
//
|
|
1256
|
+
// Only a HIT is cached. A miss must stay uncached: namespaces load lazily, so a
|
|
1257
|
+
// name that resolves to nothing now can resolve after the typelib that declares
|
|
1258
|
+
// it is required — caching the miss would make load order change behaviour.
|
|
1259
|
+
//
|
|
1260
|
+
// The cache OWNS the ref it holds (GIFunctionInfos live as long as the process
|
|
1261
|
+
// anyway, and the entry count is bounded by the methods a program actually
|
|
1262
|
+
// calls); callers get a borrowed pointer and must not unref it. Written only
|
|
1263
|
+
// from the JS thread, like every other entry point here.
|
|
1264
|
+
static std::map<std::pair<GType, std::string>, GIFunctionInfo*> g_instanceMethodCache;
|
|
1265
|
+
|
|
1266
|
+
// The resolution of ResolveInstanceMethod, memoized. Returns a BORROWED pointer
|
|
1267
|
+
// (never unref) or nullptr.
|
|
1268
|
+
static GIFunctionInfo* ResolveInstanceMethodCached(GType gtype, const std::string& method) {
|
|
1269
|
+
const std::pair<GType, std::string> key(gtype, method);
|
|
1270
|
+
auto it = g_instanceMethodCache.find(key);
|
|
1271
|
+
if (it != g_instanceMethodCache.end()) return it->second;
|
|
1272
|
+
|
|
1273
|
+
GIRepository* repo = DupDefaultRepository();
|
|
1274
|
+
GIObjectInfo* objInfo = FindNearestObjectInfo(repo, gtype);
|
|
1275
|
+
GIFunctionInfo* func = nullptr;
|
|
1276
|
+
if (objInfo != nullptr) {
|
|
1277
|
+
func = ResolveInstanceMethod(repo, gtype, objInfo, method);
|
|
1278
|
+
gi_base_info_unref(objInfo);
|
|
1279
|
+
}
|
|
1280
|
+
g_object_unref(repo);
|
|
1281
|
+
if (func != nullptr) g_instanceMethodCache.emplace(key, func);
|
|
1282
|
+
return func;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
// Whether `gtype` has introspection info at all — the distinction callMethod
|
|
1286
|
+
// reports as "no introspection info for X" before it even looks for a method.
|
|
1287
|
+
static bool HasObjectInfo(GType gtype) {
|
|
1288
|
+
GIRepository* repo = DupDefaultRepository();
|
|
1289
|
+
GIObjectInfo* objInfo = FindNearestObjectInfo(repo, gtype);
|
|
1290
|
+
const bool found = objInfo != nullptr;
|
|
1291
|
+
if (objInfo != nullptr) gi_base_info_unref(objInfo);
|
|
1292
|
+
g_object_unref(repo);
|
|
1293
|
+
return found;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1245
1296
|
// callMethod(handle, methodName, args?: unknown[]) -> unknown
|
|
1246
1297
|
// Resolve an instance method by walking the instance's GIObjectInfo (own +
|
|
1247
1298
|
// implemented-interface methods at each level, then up the parent chain), then
|
|
@@ -1262,20 +1313,14 @@ Napi::Value CallMethod(const Napi::CallbackInfo& info) {
|
|
|
1262
1313
|
: Napi::Array::New(env, 0);
|
|
1263
1314
|
|
|
1264
1315
|
GType gtype = G_OBJECT_TYPE(obj);
|
|
1265
|
-
|
|
1316
|
+
GIFunctionInfo* func = ResolveInstanceMethodCached(gtype, method);
|
|
1266
1317
|
|
|
1267
|
-
|
|
1268
|
-
if (objInfo == nullptr) {
|
|
1269
|
-
g_object_unref(repo);
|
|
1318
|
+
if (func == nullptr && !HasObjectInfo(gtype)) {
|
|
1270
1319
|
Napi::Error::New(env, std::string("no introspection info for ") + G_OBJECT_TYPE_NAME(obj))
|
|
1271
1320
|
.ThrowAsJavaScriptException();
|
|
1272
1321
|
return env.Null();
|
|
1273
1322
|
}
|
|
1274
1323
|
|
|
1275
|
-
GIFunctionInfo* func = ResolveInstanceMethod(repo, gtype, objInfo, method);
|
|
1276
|
-
gi_base_info_unref(objInfo);
|
|
1277
|
-
g_object_unref(repo);
|
|
1278
|
-
|
|
1279
1324
|
if (func == nullptr) {
|
|
1280
1325
|
// On a terminating env the method-name read (info[1].Utf8Value) degrades to
|
|
1281
1326
|
// "" (a swallowed napi failure), so resolution misses and we land here — but
|
|
@@ -1290,15 +1335,13 @@ Napi::Value CallMethod(const Napi::CallbackInfo& info) {
|
|
|
1290
1335
|
return env.Null();
|
|
1291
1336
|
}
|
|
1292
1337
|
if (!gi_callable_info_is_method(reinterpret_cast<GICallableInfo*>(func))) {
|
|
1293
|
-
gi_base_info_unref(func);
|
|
1294
1338
|
Napi::TypeError::New(env, method + " is not an instance method").ThrowAsJavaScriptException();
|
|
1295
1339
|
return env.Null();
|
|
1296
1340
|
}
|
|
1297
1341
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
return result;
|
|
1342
|
+
// The cache owns `func`; this is a borrowed pointer.
|
|
1343
|
+
return InvokeFunctionInfo(env, func, obj, args,
|
|
1344
|
+
std::string(G_OBJECT_TYPE_NAME(obj)) + "." + method);
|
|
1302
1345
|
}
|
|
1303
1346
|
|
|
1304
1347
|
// hasMethod(handle, methodName) -> boolean
|
|
@@ -1321,20 +1364,10 @@ Napi::Value HasMethod(const Napi::CallbackInfo& info) {
|
|
|
1321
1364
|
if (obj == nullptr) return env.Null();
|
|
1322
1365
|
std::string method = info[1].As<Napi::String>().Utf8Value();
|
|
1323
1366
|
|
|
1324
|
-
|
|
1325
|
-
GIRepository* repo = DupDefaultRepository();
|
|
1326
|
-
GIObjectInfo* objInfo = FindNearestObjectInfo(repo, gtype);
|
|
1327
|
-
if (objInfo == nullptr) {
|
|
1328
|
-
g_object_unref(repo);
|
|
1329
|
-
return Napi::Boolean::New(env, false);
|
|
1330
|
-
}
|
|
1331
|
-
GIFunctionInfo* func = ResolveInstanceMethod(repo, gtype, objInfo, method);
|
|
1332
|
-
gi_base_info_unref(objInfo);
|
|
1333
|
-
g_object_unref(repo);
|
|
1367
|
+
GIFunctionInfo* func = ResolveInstanceMethodCached(G_OBJECT_TYPE(obj), method);
|
|
1334
1368
|
if (func == nullptr) return Napi::Boolean::New(env, false);
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
return Napi::Boolean::New(env, invocable);
|
|
1369
|
+
return Napi::Boolean::New(env,
|
|
1370
|
+
gi_callable_info_is_method(reinterpret_cast<GICallableInfo*>(func)));
|
|
1338
1371
|
}
|
|
1339
1372
|
|
|
1340
1373
|
// hasClassMethod(namespace, typeName, methodName) -> boolean
|
package/src/class.cc
CHANGED
|
@@ -44,6 +44,8 @@ static GType UnwrapGType(Napi::Env env, Napi::Value v) {
|
|
|
44
44
|
static GType TypeNameToGType(const std::string& t) {
|
|
45
45
|
if (t == "string" || t == "utf8") return G_TYPE_STRING;
|
|
46
46
|
if (t == "boolean" || t == "bool") return G_TYPE_BOOLEAN;
|
|
47
|
+
if (t == "char") return G_TYPE_CHAR;
|
|
48
|
+
if (t == "uchar") return G_TYPE_UCHAR;
|
|
47
49
|
if (t == "int") return G_TYPE_INT;
|
|
48
50
|
if (t == "uint") return G_TYPE_UINT;
|
|
49
51
|
if (t == "int64") return G_TYPE_INT64;
|
|
@@ -95,6 +97,19 @@ static GParamSpec* BuildParamSpec(Napi::Env env, Napi::Object spec, std::string*
|
|
|
95
97
|
: (def.IsNumber() ? NodeGiToBool(def) : FALSE);
|
|
96
98
|
return g_param_spec_boolean(nm, nm, nm, d, flags);
|
|
97
99
|
}
|
|
100
|
+
if (type == "char") {
|
|
101
|
+
return g_param_spec_char(nm, nm, nm, hasMin ? static_cast<gint8>(mn) : G_MININT8,
|
|
102
|
+
hasMax ? static_cast<gint8>(mx) : G_MAXINT8,
|
|
103
|
+
def.IsNumber() ? static_cast<gint8>(def.As<Napi::Number>().Int32Value()) : 0,
|
|
104
|
+
flags);
|
|
105
|
+
}
|
|
106
|
+
if (type == "uchar") {
|
|
107
|
+
return g_param_spec_uchar(nm, nm, nm, hasMin ? static_cast<guint8>(mn) : 0,
|
|
108
|
+
hasMax ? static_cast<guint8>(mx) : G_MAXUINT8,
|
|
109
|
+
def.IsNumber() ? static_cast<guint8>(def.As<Napi::Number>().Uint32Value())
|
|
110
|
+
: 0,
|
|
111
|
+
flags);
|
|
112
|
+
}
|
|
98
113
|
if (type == "int") {
|
|
99
114
|
return g_param_spec_int(nm, nm, nm, hasMin ? static_cast<gint>(mn) : G_MININT,
|
|
100
115
|
hasMax ? static_cast<gint>(mx) : G_MAXINT,
|
package/src/common.h
CHANGED
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
#endif
|
|
36
36
|
|
|
37
37
|
#ifndef _WIN32
|
|
38
|
+
#include <cmath>
|
|
38
39
|
#include <dlfcn.h> // dlopen/dlsym the GtkWidgetClass template API (no GTK link)
|
|
39
40
|
#endif
|
|
40
41
|
#include <napi.h>
|
|
@@ -280,6 +281,180 @@ inline uint64_t JsValueToUint64(Napi::Value v) {
|
|
|
280
281
|
return n.IsEmpty() ? 0 : static_cast<uint64_t>(n.Int64Value());
|
|
281
282
|
}
|
|
282
283
|
|
|
284
|
+
// IN integers, GJS-exact (refs/gjs/gi/js-value-inl.h, arg-inl.h): an integer tag
|
|
285
|
+
// REFUSES an out-of-range value instead of wrapping it. The rule is not uniform and is
|
|
286
|
+
// copied as is, never made stricter: 8/16-bit run ToInt32 first (so it wraps mod 2^32)
|
|
287
|
+
// and then compare against the tag range; int32 is not checked at all; uint32 and the
|
|
288
|
+
// 64-bit tags refuse what falls outside their range. undefined and a non-finite Number
|
|
289
|
+
// become 0. `shown` receives the text GJS prints for the offending value.
|
|
290
|
+
// kPending: a BigInt/Symbol hit ToNumber, the TypeError is already pending — the caller
|
|
291
|
+
// must stop without throwing a second error.
|
|
292
|
+
enum class NodeGiIntResult { kOk, kOutOfRange, kPending };
|
|
293
|
+
inline NodeGiIntResult NodeGiJsToInteger(Napi::Value v, GITypeTag tag, GIArgument* out,
|
|
294
|
+
std::string* shown) {
|
|
295
|
+
const bool is64 = tag == GI_TYPE_TAG_INT64 || tag == GI_TYPE_TAG_UINT64;
|
|
296
|
+
if (is64 && v.IsBigInt()) {
|
|
297
|
+
bool lossless = false;
|
|
298
|
+
if (tag == GI_TYPE_TAG_INT64) {
|
|
299
|
+
out->v_int64 = v.As<Napi::BigInt>().Int64Value(&lossless);
|
|
300
|
+
} else {
|
|
301
|
+
out->v_uint64 = v.As<Napi::BigInt>().Uint64Value(&lossless);
|
|
302
|
+
}
|
|
303
|
+
if (!lossless && shown != nullptr) *shown = "0";
|
|
304
|
+
return lossless ? NodeGiIntResult::kOk : NodeGiIntResult::kOutOfRange;
|
|
305
|
+
}
|
|
306
|
+
Napi::Number n = v.ToNumber();
|
|
307
|
+
if (n.IsEmpty()) return NodeGiIntResult::kPending;
|
|
308
|
+
double d = n.DoubleValue();
|
|
309
|
+
switch (tag) {
|
|
310
|
+
case GI_TYPE_TAG_INT8:
|
|
311
|
+
case GI_TYPE_TAG_UINT8:
|
|
312
|
+
case GI_TYPE_TAG_INT16:
|
|
313
|
+
case GI_TYPE_TAG_UINT16: {
|
|
314
|
+
int32_t i = n.Int32Value();
|
|
315
|
+
int32_t lo = tag == GI_TYPE_TAG_INT8 ? INT8_MIN : tag == GI_TYPE_TAG_INT16 ? INT16_MIN : 0;
|
|
316
|
+
int32_t hi = tag == GI_TYPE_TAG_INT8 ? INT8_MAX : tag == GI_TYPE_TAG_UINT8 ? UINT8_MAX
|
|
317
|
+
: tag == GI_TYPE_TAG_INT16 ? INT16_MAX : UINT16_MAX;
|
|
318
|
+
if (shown != nullptr) *shown = std::to_string(i);
|
|
319
|
+
if (i < lo || i > hi) return NodeGiIntResult::kOutOfRange;
|
|
320
|
+
if (tag == GI_TYPE_TAG_INT8) out->v_int8 = static_cast<int8_t>(i);
|
|
321
|
+
else if (tag == GI_TYPE_TAG_UINT8) out->v_uint8 = static_cast<uint8_t>(i);
|
|
322
|
+
else if (tag == GI_TYPE_TAG_INT16) out->v_int16 = static_cast<int16_t>(i);
|
|
323
|
+
else out->v_uint16 = static_cast<uint16_t>(i);
|
|
324
|
+
return NodeGiIntResult::kOk;
|
|
325
|
+
}
|
|
326
|
+
case GI_TYPE_TAG_INT32:
|
|
327
|
+
out->v_int32 = n.Int32Value();
|
|
328
|
+
return NodeGiIntResult::kOk;
|
|
329
|
+
case GI_TYPE_TAG_UINT32: {
|
|
330
|
+
bool zero = v.IsUndefined() || (v.IsNumber() && !std::isfinite(d));
|
|
331
|
+
if (zero) {
|
|
332
|
+
out->v_uint32 = 0;
|
|
333
|
+
return NodeGiIntResult::kOk;
|
|
334
|
+
}
|
|
335
|
+
if (shown != nullptr) *shown = std::to_string(d);
|
|
336
|
+
if (std::isnan(d) || d < 0 || d > static_cast<double>(UINT32_MAX))
|
|
337
|
+
return NodeGiIntResult::kOutOfRange;
|
|
338
|
+
out->v_uint32 = static_cast<uint32_t>(d);
|
|
339
|
+
return NodeGiIntResult::kOk;
|
|
340
|
+
}
|
|
341
|
+
case GI_TYPE_TAG_INT64: {
|
|
342
|
+
double t = std::isfinite(d) ? std::trunc(d) : 0;
|
|
343
|
+
if (t < -9223372036854775808.0 || t >= 9223372036854775808.0) {
|
|
344
|
+
if (shown != nullptr)
|
|
345
|
+
*shown = std::to_string(t < 0 ? INT64_MIN : INT64_MAX);
|
|
346
|
+
return NodeGiIntResult::kOutOfRange;
|
|
347
|
+
}
|
|
348
|
+
out->v_int64 = static_cast<int64_t>(t);
|
|
349
|
+
return NodeGiIntResult::kOk;
|
|
350
|
+
}
|
|
351
|
+
case GI_TYPE_TAG_UINT64: {
|
|
352
|
+
double t = std::isfinite(d) ? std::trunc(d) : 0;
|
|
353
|
+
if (t < 0 || t >= 18446744073709551616.0) {
|
|
354
|
+
if (shown != nullptr)
|
|
355
|
+
*shown = std::to_string(t < 0 ? uint64_t{0} : UINT64_MAX);
|
|
356
|
+
return NodeGiIntResult::kOutOfRange;
|
|
357
|
+
}
|
|
358
|
+
out->v_uint64 = static_cast<uint64_t>(t);
|
|
359
|
+
return NodeGiIntResult::kOk;
|
|
360
|
+
}
|
|
361
|
+
default:
|
|
362
|
+
return NodeGiIntResult::kOk;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
enum class NodeGiIntContext { kArgument, kField, kReturnValue, kListElement, kHashElement };
|
|
367
|
+
inline const char* NodeGiIntTagName(GITypeTag tag) {
|
|
368
|
+
switch (tag) {
|
|
369
|
+
case GI_TYPE_TAG_INT8: return "int8";
|
|
370
|
+
case GI_TYPE_TAG_UINT8: return "uint8";
|
|
371
|
+
case GI_TYPE_TAG_INT16: return "int16";
|
|
372
|
+
case GI_TYPE_TAG_UINT16: return "uint16";
|
|
373
|
+
case GI_TYPE_TAG_INT32: return "int32";
|
|
374
|
+
case GI_TYPE_TAG_UINT32: return "uint32";
|
|
375
|
+
case GI_TYPE_TAG_INT64: return "int64";
|
|
376
|
+
default: return "uint64";
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// GJS's three texts (arg-cache.cpp / arg.cpp), plain Error.
|
|
380
|
+
inline void NodeGiThrowIntOutOfRange(Napi::Env env, GITypeTag tag, NodeGiIntContext ctx,
|
|
381
|
+
const char* name, const std::string& shown) {
|
|
382
|
+
std::string t = NodeGiIntTagName(tag);
|
|
383
|
+
std::string msg;
|
|
384
|
+
if (ctx == NodeGiIntContext::kArgument) {
|
|
385
|
+
msg = std::string("Argument ") + (name != nullptr ? name : "") + ": value is out of range for " + t;
|
|
386
|
+
} else {
|
|
387
|
+
msg = "value " + shown + " is out of range for " +
|
|
388
|
+
(ctx == NodeGiIntContext::kField
|
|
389
|
+
? std::string("Field '") + (name ? name : "") + "'"
|
|
390
|
+
: ctx == NodeGiIntContext::kListElement ? std::string("List element")
|
|
391
|
+
: ctx == NodeGiIntContext::kHashElement ? std::string("Hash element")
|
|
392
|
+
: std::string("Return value")) +
|
|
393
|
+
" (type " + t + ")";
|
|
394
|
+
}
|
|
395
|
+
Napi::Error::New(env, msg).ThrowAsJavaScriptException();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// The text gjs_debug_value (refs/gjs/gi/../jsapi-util-string.cpp) prints for the offending
|
|
399
|
+
// value in a list/hash element error: a Number goes through C++ ostream (%g, 6 digits), a
|
|
400
|
+
// BigInt is shown as its signed 64-bit value with a "(modulo 2^64)" suffix.
|
|
401
|
+
inline std::string NodeGiDebugValue(Napi::Value v) {
|
|
402
|
+
if (v.IsBigInt()) {
|
|
403
|
+
bool lossless = false;
|
|
404
|
+
return std::to_string(v.As<Napi::BigInt>().Int64Value(&lossless)) + "n (modulo 2^64)";
|
|
405
|
+
}
|
|
406
|
+
Napi::Number n = v.ToNumber();
|
|
407
|
+
if (n.IsEmpty()) return std::string();
|
|
408
|
+
char buf[G_ASCII_DTOSTR_BUF_SIZE];
|
|
409
|
+
g_ascii_formatd(buf, sizeof(buf), "%g", n.DoubleValue()); // locale-independent, like ostream
|
|
410
|
+
return buf;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// IN array elements and GValue int/uint, GJS-exact: these WRAP instead of refusing.
|
|
414
|
+
// An array element goes through js_value_to_c_strict (refs/gjs/gi/arg.cpp:543), which
|
|
415
|
+
// is ToInt32/ToUint32 truncated to the tag for 8/16/32 bits and ToInt64/ToUint64 —
|
|
416
|
+
// modulo 2^64 — for 64 bits; a BigInt takes ToBigInt64/ToBigUint64 (also modulo 2^64)
|
|
417
|
+
// on a 64-bit tag and is a TypeError on a narrower one. kPending: that TypeError is
|
|
418
|
+
// already pending, the caller must stop without throwing a second error.
|
|
419
|
+
inline uint64_t NodeGiWrapToUint64(double d) {
|
|
420
|
+
if (!std::isfinite(d)) return 0;
|
|
421
|
+
double m = std::fmod(std::trunc(d), 18446744073709551616.0);
|
|
422
|
+
return m < 0 ? uint64_t{0} - static_cast<uint64_t>(-m) : static_cast<uint64_t>(m);
|
|
423
|
+
}
|
|
424
|
+
inline NodeGiIntResult NodeGiJsToIntegerWrap(Napi::Value v, GITypeTag tag, GIArgument* out) {
|
|
425
|
+
const bool is64 = tag == GI_TYPE_TAG_INT64 || tag == GI_TYPE_TAG_UINT64;
|
|
426
|
+
if (is64 && v.IsBigInt()) {
|
|
427
|
+
bool lossless = false;
|
|
428
|
+
out->v_uint64 = v.As<Napi::BigInt>().Uint64Value(&lossless);
|
|
429
|
+
return NodeGiIntResult::kOk;
|
|
430
|
+
}
|
|
431
|
+
Napi::Number n = v.ToNumber();
|
|
432
|
+
if (n.IsEmpty()) return NodeGiIntResult::kPending;
|
|
433
|
+
switch (tag) {
|
|
434
|
+
case GI_TYPE_TAG_INT8: out->v_int8 = static_cast<int8_t>(n.Int32Value()); break;
|
|
435
|
+
case GI_TYPE_TAG_UINT8: out->v_uint8 = static_cast<uint8_t>(n.Uint32Value()); break;
|
|
436
|
+
case GI_TYPE_TAG_INT16: out->v_int16 = static_cast<int16_t>(n.Int32Value()); break;
|
|
437
|
+
case GI_TYPE_TAG_UINT16: out->v_uint16 = static_cast<uint16_t>(n.Uint32Value()); break;
|
|
438
|
+
case GI_TYPE_TAG_INT32: out->v_int32 = n.Int32Value(); break;
|
|
439
|
+
case GI_TYPE_TAG_UINT32: out->v_uint32 = n.Uint32Value(); break;
|
|
440
|
+
case GI_TYPE_TAG_INT64:
|
|
441
|
+
case GI_TYPE_TAG_UINT64: out->v_uint64 = NodeGiWrapToUint64(n.DoubleValue()); break;
|
|
442
|
+
default: break;
|
|
443
|
+
}
|
|
444
|
+
return NodeGiIntResult::kOk;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// GValue char/uchar/int64/uint64: refused out of range with gjs_value_to_g_value's own
|
|
448
|
+
// text (refs/gjs/gi/value.cpp:602-690, throw_expect_type), a plain Error. `what` is the
|
|
449
|
+
// "expected" noun gjs prints; the offending value is its JS ToString.
|
|
450
|
+
inline void NodeGiThrowGValueOutOfRange(Napi::Env env, Napi::Value v, const char* what) {
|
|
451
|
+
Napi::String s = v.ToString();
|
|
452
|
+
std::string shown = s.IsEmpty() ? std::string() : s.Utf8Value();
|
|
453
|
+
Napi::Error::New(env, std::string("Wrong type ") + (v.IsBigInt() ? "bigint" : "number") + "; " +
|
|
454
|
+
what + " expected. But it's out of range: " + shown)
|
|
455
|
+
.ThrowAsJavaScriptException();
|
|
456
|
+
}
|
|
457
|
+
|
|
283
458
|
// OUT (WarnIfUnsafe{Int,Uint}64): GJS ALWAYS returns a JS Number for a 64-bit int
|
|
284
459
|
// (never a BigInt) and emits this g_warning when the value falls outside the range
|
|
285
460
|
// a double represents exactly (|v| > 2^53 - 1 == Number.MAX_SAFE_INTEGER), because
|
|
@@ -391,7 +566,8 @@ bool JsToGIArgument(Napi::Env env, Napi::Value v, GITypeInfo* type, GIArgument*
|
|
|
391
566
|
CreatedClosures* closures = nullptr,
|
|
392
567
|
CreatedBytes* bytes = nullptr,
|
|
393
568
|
CreatedValues* values = nullptr,
|
|
394
|
-
const char* argName = nullptr
|
|
569
|
+
const char* argName = nullptr,
|
|
570
|
+
NodeGiIntContext intContext = NodeGiIntContext::kArgument);
|
|
395
571
|
|
|
396
572
|
// ---- IN container building -----------------------------------------
|
|
397
573
|
//
|
package/src/marshal.cc
CHANGED
|
@@ -516,7 +516,8 @@ bool JsToGIArgument(Napi::Env env, Napi::Value v, GITypeInfo* type, GIArgument*
|
|
|
516
516
|
CreatedClosures* closures,
|
|
517
517
|
CreatedBytes* bytes,
|
|
518
518
|
CreatedValues* values,
|
|
519
|
-
const char* argName
|
|
519
|
+
const char* argName,
|
|
520
|
+
NodeGiIntContext intContext) {
|
|
520
521
|
if (v.IsEmpty()) {
|
|
521
522
|
// Residue of a swallowed napi failure (a fallible Get()/coercion upstream
|
|
522
523
|
// failed on a terminating env, or a throwing getter left the exception
|
|
@@ -536,16 +537,24 @@ bool JsToGIArgument(Napi::Env env, Napi::Value v, GITypeInfo* type, GIArgument*
|
|
|
536
537
|
GITypeTag tag = gi_type_info_get_tag(type);
|
|
537
538
|
switch (tag) {
|
|
538
539
|
case GI_TYPE_TAG_BOOLEAN: out->v_boolean = NodeGiToBool(v); return true;
|
|
539
|
-
case GI_TYPE_TAG_INT8:
|
|
540
|
-
case GI_TYPE_TAG_UINT8:
|
|
541
|
-
case GI_TYPE_TAG_INT16:
|
|
542
|
-
case GI_TYPE_TAG_UINT16:
|
|
543
|
-
case GI_TYPE_TAG_INT32:
|
|
544
|
-
case GI_TYPE_TAG_UINT32:
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
540
|
+
case GI_TYPE_TAG_INT8:
|
|
541
|
+
case GI_TYPE_TAG_UINT8:
|
|
542
|
+
case GI_TYPE_TAG_INT16:
|
|
543
|
+
case GI_TYPE_TAG_UINT16:
|
|
544
|
+
case GI_TYPE_TAG_INT32:
|
|
545
|
+
case GI_TYPE_TAG_UINT32:
|
|
546
|
+
case GI_TYPE_TAG_INT64:
|
|
547
|
+
case GI_TYPE_TAG_UINT64: {
|
|
548
|
+
std::string shown;
|
|
549
|
+
switch (NodeGiJsToInteger(v, tag, out, &shown)) {
|
|
550
|
+
case NodeGiIntResult::kOk: return true;
|
|
551
|
+
case NodeGiIntResult::kPending: return false;
|
|
552
|
+
case NodeGiIntResult::kOutOfRange:
|
|
553
|
+
NodeGiThrowIntOutOfRange(env, tag, intContext, argName, shown);
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
549
558
|
case GI_TYPE_TAG_FLOAT: out->v_float = static_cast<float>(NodeGiToDouble(v)); return true;
|
|
550
559
|
case GI_TYPE_TAG_DOUBLE: out->v_double = NodeGiToDouble(v); return true;
|
|
551
560
|
case GI_TYPE_TAG_UTF8:
|
|
@@ -1051,6 +1060,8 @@ static bool IsSupportedElementType(GITypeInfo* elem, std::string* why, bool byVa
|
|
|
1051
1060
|
case GI_TYPE_TAG_UTF8:
|
|
1052
1061
|
case GI_TYPE_TAG_FILENAME:
|
|
1053
1062
|
return true;
|
|
1063
|
+
// A gunichar is a 4-byte cell; only a C array writes one (no list/hash node).
|
|
1064
|
+
case GI_TYPE_TAG_UNICHAR: return cArrayCell;
|
|
1054
1065
|
case GI_TYPE_TAG_INTERFACE: {
|
|
1055
1066
|
GIBaseInfo* iface = gi_type_info_get_interface(elem);
|
|
1056
1067
|
bool ok = iface != nullptr && (GI_IS_OBJECT_INFO(iface) || GI_IS_INTERFACE_INFO(iface));
|
|
@@ -1169,7 +1180,8 @@ size_t CElementSize(GITypeInfo* elem) {
|
|
|
1169
1180
|
case GI_TYPE_TAG_INT16:
|
|
1170
1181
|
case GI_TYPE_TAG_UINT16: return 2;
|
|
1171
1182
|
case GI_TYPE_TAG_INT32:
|
|
1172
|
-
case GI_TYPE_TAG_UINT32:
|
|
1183
|
+
case GI_TYPE_TAG_UINT32:
|
|
1184
|
+
case GI_TYPE_TAG_UNICHAR: return 4;
|
|
1173
1185
|
case GI_TYPE_TAG_INT64:
|
|
1174
1186
|
case GI_TYPE_TAG_UINT64: return 8;
|
|
1175
1187
|
case GI_TYPE_TAG_FLOAT: return sizeof(gfloat);
|
|
@@ -1592,21 +1604,40 @@ Napi::Value ReadOutOrReturn(Napi::Env env, GICallableInfo* callable, GITypeInfo*
|
|
|
1592
1604
|
// the element, so it gets a ref (objects) or an independent copy (boxed) instead.
|
|
1593
1605
|
// Throws + returns false on an unsupported element.
|
|
1594
1606
|
static bool ElementToGIArgument(Napi::Env env, GITypeInfo* elem, Napi::Value v, GIArgument* a,
|
|
1595
|
-
GITransfer elemTransfer
|
|
1607
|
+
GITransfer elemTransfer, NodeGiIntContext intContext = NodeGiIntContext::kListElement,
|
|
1608
|
+
bool wrapIntegers = false) {
|
|
1596
1609
|
memset(a, 0, sizeof(*a));
|
|
1597
1610
|
switch (gi_type_info_get_tag(elem)) {
|
|
1598
1611
|
// Scalar coercions via the terminate-safe helpers (common.h): a swallowed
|
|
1599
1612
|
// napi failure mid worker.terminate() must degrade to a zero, not cascade
|
|
1600
1613
|
// into Error::New(nullptr)'s fatal sites.
|
|
1601
1614
|
case GI_TYPE_TAG_BOOLEAN: a->v_boolean = NodeGiToBool(v); return true;
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
case
|
|
1605
|
-
case
|
|
1606
|
-
case
|
|
1607
|
-
case
|
|
1608
|
-
case
|
|
1609
|
-
case
|
|
1615
|
+
// Integers WRAP in an array (arg.cpp gjs_array_to_auto_array, NodeGiJsToIntegerWrap in
|
|
1616
|
+
// common.h) and are REFUSED out of range in a GList/GHashTable node.
|
|
1617
|
+
case GI_TYPE_TAG_INT8:
|
|
1618
|
+
case GI_TYPE_TAG_UINT8:
|
|
1619
|
+
case GI_TYPE_TAG_INT16:
|
|
1620
|
+
case GI_TYPE_TAG_UINT16:
|
|
1621
|
+
case GI_TYPE_TAG_INT32:
|
|
1622
|
+
case GI_TYPE_TAG_UINT32:
|
|
1623
|
+
case GI_TYPE_TAG_INT64:
|
|
1624
|
+
case GI_TYPE_TAG_UINT64:
|
|
1625
|
+
if (wrapIntegers)
|
|
1626
|
+
return NodeGiJsToIntegerWrap(v, gi_type_info_get_tag(elem), a) == NodeGiIntResult::kOk;
|
|
1627
|
+
// A list/hash node is checked, not wrapped (arg.cpp gjs_value_to_basic_gi_argument
|
|
1628
|
+
// -> gjs_arg_set_from_js_value): out of range is a plain Error.
|
|
1629
|
+
switch (NodeGiJsToInteger(v, gi_type_info_get_tag(elem), a, nullptr)) {
|
|
1630
|
+
case NodeGiIntResult::kPending: return false;
|
|
1631
|
+
case NodeGiIntResult::kOutOfRange:
|
|
1632
|
+
NodeGiThrowIntOutOfRange(env, gi_type_info_get_tag(elem), intContext, nullptr,
|
|
1633
|
+
NodeGiDebugValue(v));
|
|
1634
|
+
return false;
|
|
1635
|
+
case NodeGiIntResult::kOk: return true;
|
|
1636
|
+
}
|
|
1637
|
+
return true;
|
|
1638
|
+
// gjs_array_to_auto_array<char32_t> wraps like uint32 (arg.cpp js_value_to_c_strict).
|
|
1639
|
+
case GI_TYPE_TAG_UNICHAR:
|
|
1640
|
+
return NodeGiJsToIntegerWrap(v, GI_TYPE_TAG_UINT32, a) == NodeGiIntResult::kOk;
|
|
1610
1641
|
case GI_TYPE_TAG_FLOAT: a->v_float = static_cast<gfloat>(NodeGiToDouble(v)); return true;
|
|
1611
1642
|
case GI_TYPE_TAG_DOUBLE: a->v_double = NodeGiToDouble(v); return true;
|
|
1612
1643
|
case GI_TYPE_TAG_GTYPE: {
|
|
@@ -1814,7 +1845,10 @@ bool JsToAnswerArgument(Napi::Env env, Napi::Value v, GITypeInfo* ti, GITransfer
|
|
|
1814
1845
|
}
|
|
1815
1846
|
std::string held;
|
|
1816
1847
|
return JsToGIArgument(env, v, ti, out, &held, transfer, nullptr, nullptr, nullptr, nullptr,
|
|
1817
|
-
argName
|
|
1848
|
+
argName,
|
|
1849
|
+
argName != nullptr && strcmp(argName, "return value") == 0
|
|
1850
|
+
? NodeGiIntContext::kReturnValue
|
|
1851
|
+
: NodeGiIntContext::kArgument);
|
|
1818
1852
|
}
|
|
1819
1853
|
|
|
1820
1854
|
} // namespace
|
|
@@ -2240,6 +2274,20 @@ static bool JsToCArray(Napi::Env env, Napi::Value v, GITypeInfo* type, GITransfe
|
|
|
2240
2274
|
return true;
|
|
2241
2275
|
}
|
|
2242
2276
|
|
|
2277
|
+
// A JS string as a gunichar C array → its code points (arg.cpp:584, gjs_string_to_ucs4).
|
|
2278
|
+
if (at == GI_ARRAY_TYPE_C && etag == GI_TYPE_TAG_UNICHAR && v.IsString()) {
|
|
2279
|
+
std::string s = v.As<Napi::String>().Utf8Value();
|
|
2280
|
+
glong n = 0;
|
|
2281
|
+
gunichar* ucs = g_utf8_to_ucs4_fast(s.c_str(), static_cast<glong>(s.size()), &n);
|
|
2282
|
+
void* buf = g_malloc0(elemSize * (n + (zt ? 1 : 0)));
|
|
2283
|
+
if (n > 0) memcpy(buf, ucs, elemSize * n);
|
|
2284
|
+
g_free(ucs);
|
|
2285
|
+
*outPtr = buf;
|
|
2286
|
+
*outCount = static_cast<long>(n);
|
|
2287
|
+
gi_base_info_unref(elem);
|
|
2288
|
+
return true;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2243
2291
|
if (!v.IsArray()) {
|
|
2244
2292
|
gi_base_info_unref(elem);
|
|
2245
2293
|
Napi::TypeError::New(env, "expected an array for the array argument")
|
|
@@ -2273,7 +2321,8 @@ static bool JsToCArray(Napi::Env env, Napi::Value v, GITypeInfo* type, GITransfe
|
|
|
2273
2321
|
continue;
|
|
2274
2322
|
}
|
|
2275
2323
|
GIArgument a;
|
|
2276
|
-
ok = ElementToGIArgument(env, elem, arr.Get(static_cast<uint32_t>(i)), &a, elemTransfer
|
|
2324
|
+
ok = ElementToGIArgument(env, elem, arr.Get(static_cast<uint32_t>(i)), &a, elemTransfer,
|
|
2325
|
+
NodeGiIntContext::kListElement, /*wrapIntegers=*/true);
|
|
2277
2326
|
if (!ok) break;
|
|
2278
2327
|
// Copy the element's storage bytes (LE: the low elemSize bytes of the union
|
|
2279
2328
|
// alias the active field — v_string/v_pointer for pointers, the scalar
|
|
@@ -2420,7 +2469,8 @@ static bool JsToGHashIn(Napi::Env env, Napi::Value v, GITypeInfo* type, GITransf
|
|
|
2420
2469
|
} else {
|
|
2421
2470
|
GIArgument ka;
|
|
2422
2471
|
Napi::Value keyNum = Napi::Number::New(env, static_cast<double>(g_ascii_strtoll(ks.c_str(), nullptr, 10)));
|
|
2423
|
-
ok = ElementToGIArgument(env, kt, keyNum, &ka, GI_TRANSFER_NOTHING
|
|
2472
|
+
ok = ElementToGIArgument(env, kt, keyNum, &ka, GI_TRANSFER_NOTHING,
|
|
2473
|
+
NodeGiIntContext::kHashElement, /*wrapIntegers=*/true);
|
|
2424
2474
|
if (!ok) break;
|
|
2425
2475
|
kp = gi_type_info_hash_pointer_from_argument(kt, &ka);
|
|
2426
2476
|
}
|
|
@@ -2431,7 +2481,7 @@ static bool JsToGHashIn(Napi::Env env, Napi::Value v, GITypeInfo* type, GITransf
|
|
|
2431
2481
|
Napi::Value val = obj.Get(key);
|
|
2432
2482
|
gpointer vp = nullptr;
|
|
2433
2483
|
GIArgument va;
|
|
2434
|
-
ok = ElementToGIArgument(env, vt, val, &va, elemTransfer);
|
|
2484
|
+
ok = ElementToGIArgument(env, vt, val, &va, elemTransfer, NodeGiIntContext::kHashElement);
|
|
2435
2485
|
if (!ok) {
|
|
2436
2486
|
g_free(kp);
|
|
2437
2487
|
break;
|
|
@@ -2973,7 +3023,8 @@ Napi::Value SetBoxedField(const Napi::CallbackInfo& info) {
|
|
|
2973
3023
|
GIArgument arg;
|
|
2974
3024
|
memset(&arg, 0, sizeof(arg));
|
|
2975
3025
|
std::string heldString;
|
|
2976
|
-
if (JsToGIArgument(env, value, ftype, &arg, &heldString, GI_TRANSFER_NOTHING, nullptr
|
|
3026
|
+
if (JsToGIArgument(env, value, ftype, &arg, &heldString, GI_TRANSFER_NOTHING, nullptr,
|
|
3027
|
+
nullptr, nullptr, nullptr, name.c_str(), NodeGiIntContext::kField)) {
|
|
2977
3028
|
if (!gi_field_info_set_field(field, bh->ptr, &arg)) {
|
|
2978
3029
|
Napi::Error::New(env, std::string("Writing field ") + typeName + "." + name + " is not supported")
|
|
2979
3030
|
.ThrowAsJavaScriptException();
|
package/src/object.cc
CHANGED
|
@@ -444,17 +444,53 @@ bool JsToGValue(Napi::Env env, Napi::Value js, GValue* v) {
|
|
|
444
444
|
// napi failure mid worker.terminate() must degrade to a zero, not cascade
|
|
445
445
|
// into Error::New(nullptr)'s fatal sites (the terminate-mid-`newObject` path).
|
|
446
446
|
case G_TYPE_BOOLEAN: g_value_set_boolean(v, NodeGiToBool(js)); return true;
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
447
|
+
// char/uchar/int64/uint64 refuse an out-of-range value and int/uint wrap, as gjs's
|
|
448
|
+
// gjs_value_to_g_value does (value.cpp:602-690) — the same rule the constructor
|
|
449
|
+
// properties and signal arguments reach. A BigInt on a 32-bit tag is ToNumber's
|
|
450
|
+
// TypeError, left pending.
|
|
451
|
+
case G_TYPE_CHAR:
|
|
452
|
+
case G_TYPE_UCHAR:
|
|
453
|
+
case G_TYPE_INT64:
|
|
454
|
+
case G_TYPE_UINT64: {
|
|
455
|
+
GITypeTag tag = ft == G_TYPE_CHAR ? GI_TYPE_TAG_INT8
|
|
456
|
+
: ft == G_TYPE_UCHAR ? GI_TYPE_TAG_UINT8
|
|
457
|
+
: ft == G_TYPE_INT64 ? GI_TYPE_TAG_INT64
|
|
458
|
+
: GI_TYPE_TAG_UINT64;
|
|
459
|
+
GIArgument a;
|
|
460
|
+
memset(&a, 0, sizeof(a));
|
|
461
|
+
switch (NodeGiJsToInteger(js, tag, &a, nullptr)) {
|
|
462
|
+
case NodeGiIntResult::kPending: return false;
|
|
463
|
+
case NodeGiIntResult::kOutOfRange:
|
|
464
|
+
NodeGiThrowGValueOutOfRange(env, js,
|
|
465
|
+
ft == G_TYPE_CHAR ? "char"
|
|
466
|
+
: ft == G_TYPE_UCHAR ? "unsigned char"
|
|
467
|
+
: ft == G_TYPE_INT64 ? "64-bit integer"
|
|
468
|
+
: "unsigned 64-bit integer");
|
|
469
|
+
return false;
|
|
470
|
+
case NodeGiIntResult::kOk: break;
|
|
471
|
+
}
|
|
472
|
+
if (ft == G_TYPE_CHAR) g_value_set_schar(v, a.v_int8);
|
|
473
|
+
else if (ft == G_TYPE_UCHAR) g_value_set_uchar(v, a.v_uint8);
|
|
474
|
+
else if (ft == G_TYPE_INT64) g_value_set_int64(v, a.v_int64);
|
|
475
|
+
else g_value_set_uint64(v, a.v_uint64);
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
case G_TYPE_INT:
|
|
479
|
+
case G_TYPE_UINT: {
|
|
480
|
+
GIArgument a;
|
|
481
|
+
memset(&a, 0, sizeof(a));
|
|
482
|
+
if (NodeGiJsToIntegerWrap(js, ft == G_TYPE_INT ? GI_TYPE_TAG_INT32 : GI_TYPE_TAG_UINT32,
|
|
483
|
+
&a) == NodeGiIntResult::kPending)
|
|
484
|
+
return false;
|
|
485
|
+
if (ft == G_TYPE_INT) g_value_set_int(v, a.v_int32);
|
|
486
|
+
else g_value_set_uint(v, a.v_uint32);
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
// glong/gulong are 64-bit on LP64: accept a BigInt losslessly, else a truncated
|
|
490
|
+
// Number — never let a BigInt reach ToNumber(). Shared with the GI scalar marshaller
|
|
491
|
+
// via JsValueTo{Int,Uint}64 (common.h). gjs has no G_TYPE_LONG case here at all.
|
|
454
492
|
case G_TYPE_LONG: g_value_set_long(v, static_cast<glong>(JsValueToInt64(js))); return true;
|
|
455
493
|
case G_TYPE_ULONG: g_value_set_ulong(v, static_cast<gulong>(JsValueToUint64(js))); return true;
|
|
456
|
-
case G_TYPE_INT64: g_value_set_int64(v, JsValueToInt64(js)); return true;
|
|
457
|
-
case G_TYPE_UINT64: g_value_set_uint64(v, JsValueToUint64(js)); return true;
|
|
458
494
|
case G_TYPE_FLOAT: g_value_set_float(v, static_cast<float>(NodeGiToDouble(js))); return true;
|
|
459
495
|
case G_TYPE_DOUBLE: g_value_set_double(v, NodeGiToDouble(js)); return true;
|
|
460
496
|
case G_TYPE_ENUM: g_value_set_enum(v, NodeGiToInt32(js)); return true;
|
|
@@ -611,7 +647,16 @@ Napi::Value ConstructGObject(Napi::Env env, GType gtype, Napi::Object props,
|
|
|
611
647
|
}
|
|
612
648
|
g_value_init(&values[i], pspec->value_type);
|
|
613
649
|
initialised = i + 1;
|
|
614
|
-
|
|
650
|
+
Napi::Value propValue = props.Get(nameStorage[i]);
|
|
651
|
+
if (!propValue.IsEmpty() && propValue.IsUndefined()) {
|
|
652
|
+
// gjs refuses `undefined` for every property type (object.cpp:2090).
|
|
653
|
+
Napi::Error::New(env, "Invalid value 'undefined' for property " + std::string(pspec->name) +
|
|
654
|
+
" in object initializer.")
|
|
655
|
+
.ThrowAsJavaScriptException();
|
|
656
|
+
ok = false;
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
if (!JsToGValue(env, propValue, &values[i])) {
|
|
615
660
|
ok = false;
|
|
616
661
|
break;
|
|
617
662
|
}
|
|
@@ -734,6 +779,30 @@ Napi::Value SetProperty(const Napi::CallbackInfo& info) {
|
|
|
734
779
|
Napi::TypeError::New(env, "no such property '" + name + "'").ThrowAsJavaScriptException();
|
|
735
780
|
return env.Undefined();
|
|
736
781
|
}
|
|
782
|
+
// gjs refuses an out-of-range value for these property types on assignment
|
|
783
|
+
// (object.cpp, with its own text); JsToGValue below now refuses them too, with the
|
|
784
|
+
// gjs_value_to_g_value text.
|
|
785
|
+
GITypeTag intTag = GI_TYPE_TAG_VOID;
|
|
786
|
+
switch (G_TYPE_FUNDAMENTAL(pspec->value_type)) {
|
|
787
|
+
case G_TYPE_CHAR: intTag = GI_TYPE_TAG_INT8; break;
|
|
788
|
+
case G_TYPE_UCHAR: intTag = GI_TYPE_TAG_UINT8; break;
|
|
789
|
+
case G_TYPE_UINT: intTag = GI_TYPE_TAG_UINT32; break;
|
|
790
|
+
case G_TYPE_INT64: intTag = GI_TYPE_TAG_INT64; break;
|
|
791
|
+
case G_TYPE_UINT64: intTag = GI_TYPE_TAG_UINT64; break;
|
|
792
|
+
default: break;
|
|
793
|
+
}
|
|
794
|
+
if (intTag != GI_TYPE_TAG_VOID) {
|
|
795
|
+
GIArgument probe;
|
|
796
|
+
std::string shown;
|
|
797
|
+
NodeGiIntResult r = NodeGiJsToInteger(info[2], intTag, &probe, &shown);
|
|
798
|
+
if (r == NodeGiIntResult::kPending) return env.Undefined();
|
|
799
|
+
if (r == NodeGiIntResult::kOutOfRange) {
|
|
800
|
+
Napi::Error::New(env, "value " + shown + " is out of range for " + pspec->name +
|
|
801
|
+
" (type " + NodeGiIntTagName(intTag) + ")")
|
|
802
|
+
.ThrowAsJavaScriptException();
|
|
803
|
+
return env.Undefined();
|
|
804
|
+
}
|
|
805
|
+
}
|
|
737
806
|
GValue v = G_VALUE_INIT;
|
|
738
807
|
g_value_init(&v, pspec->value_type);
|
|
739
808
|
if (JsToGValue(env, info[2], &v)) {
|