@gjsify/node-gi 0.63.0 → 0.64.1
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 +25 -0
- package/cairo.js +23 -0
- package/gi.js +380 -122
- package/index.d.ts +13 -11
- package/index.js +1 -0
- 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/addon.cc +1 -0
- package/src/cairo.cc +66 -1
- package/src/calls.cc +84 -63
- package/src/class.cc +2 -2
- package/src/common.h +174 -28
- package/src/marshal.cc +93 -111
- package/src/object.cc +79 -10
- package/src/private.cc +33 -0
- package/src/repo.cc +43 -15
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)) {
|
package/src/private.cc
CHANGED
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
|
|
26
26
|
#include "common.h"
|
|
27
27
|
|
|
28
|
+
#ifdef _WIN32
|
|
29
|
+
#include <windows.h>
|
|
30
|
+
#endif
|
|
31
|
+
|
|
28
32
|
namespace nodegi {
|
|
29
33
|
|
|
30
34
|
// ---- GLib.log_set_writer_func ----------------------------------------------
|
|
@@ -557,10 +561,39 @@ Napi::Value Bindtextdomain(const Napi::CallbackInfo& info) {
|
|
|
557
561
|
}
|
|
558
562
|
location = info[1].As<Napi::String>().Utf8Value();
|
|
559
563
|
}
|
|
564
|
+
#ifdef _WIN32
|
|
565
|
+
// bindtextdomain() reads its path in the ANSI code page, so a UTF-8 JS string with
|
|
566
|
+
// a non-ASCII directory (".../Steuererklärung/share/locale") binds a path that
|
|
567
|
+
// does not exist and every lookup silently answers the msgid (measured in a Windows
|
|
568
|
+
// 11 VM: the same catalog under an ASCII path translated). GNU gettext >= 0.21
|
|
569
|
+
// ships wbindtextdomain() for exactly this; both directions go through UTF-16.
|
|
570
|
+
std::wstring wlocation;
|
|
571
|
+
if (!query) {
|
|
572
|
+
int n = MultiByteToWideChar(CP_UTF8, 0, location.c_str(), -1, nullptr, 0);
|
|
573
|
+
if (n <= 0) {
|
|
574
|
+
Napi::TypeError::New(env, "bindtextdomain: location is not valid UTF-8")
|
|
575
|
+
.ThrowAsJavaScriptException();
|
|
576
|
+
return env.Null();
|
|
577
|
+
}
|
|
578
|
+
wlocation.resize(n);
|
|
579
|
+
MultiByteToWideChar(CP_UTF8, 0, location.c_str(), -1, wlocation.data(), n);
|
|
580
|
+
wlocation.resize(n - 1);
|
|
581
|
+
}
|
|
582
|
+
const wchar_t* wresult = wbindtextdomain(domain.c_str(), query ? nullptr : wlocation.c_str());
|
|
583
|
+
if (!query) bind_textdomain_codeset(domain.c_str(), "UTF-8");
|
|
584
|
+
if (wresult == nullptr) return env.Null();
|
|
585
|
+
int len = WideCharToMultiByte(CP_UTF8, 0, wresult, -1, nullptr, 0, nullptr, nullptr);
|
|
586
|
+
if (len <= 0) return env.Null();
|
|
587
|
+
std::string utf8(len, '\0');
|
|
588
|
+
WideCharToMultiByte(CP_UTF8, 0, wresult, -1, utf8.data(), len, nullptr, nullptr);
|
|
589
|
+
utf8.resize(len - 1);
|
|
590
|
+
return Napi::String::New(env, utf8);
|
|
591
|
+
#else
|
|
560
592
|
const char* result = bindtextdomain(domain.c_str(), query ? nullptr : location.c_str());
|
|
561
593
|
if (!query) bind_textdomain_codeset(domain.c_str(), "UTF-8");
|
|
562
594
|
if (result == nullptr) return env.Null();
|
|
563
595
|
return Napi::String::New(env, result);
|
|
596
|
+
#endif
|
|
564
597
|
}
|
|
565
598
|
|
|
566
599
|
// localeCategories() -> { ALL, COLLATE, CTYPE, MESSAGES, MONETARY, NUMERIC, TIME }
|
package/src/repo.cc
CHANGED
|
@@ -13,7 +13,6 @@ void NodeGiEnvDataFinalize(napi_env env, void* data, void* /*hint*/) {
|
|
|
13
13
|
NodeGiEnvData* d = static_cast<NodeGiEnvData*>(data);
|
|
14
14
|
if (d == nullptr) return;
|
|
15
15
|
if (d->errorBuilder != nullptr) napi_delete_reference(env, d->errorBuilder);
|
|
16
|
-
if (d->errorClass != nullptr) napi_delete_reference(env, d->errorClass);
|
|
17
16
|
if (d->templateCallbackResolver != nullptr)
|
|
18
17
|
napi_delete_reference(env, d->templateCallbackResolver);
|
|
19
18
|
if (d->cairoWrappers != nullptr) napi_delete_reference(env, d->cairoWrappers);
|
|
@@ -260,17 +259,52 @@ Napi::Value GetErrorDomain(const Napi::CallbackInfo& info) {
|
|
|
260
259
|
return result;
|
|
261
260
|
}
|
|
262
261
|
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
262
|
+
// errorDomainInfo(quark: number) -> { namespace: string, name: string } | null
|
|
263
|
+
// Which introspected enum registered a GError domain quark, so L1 can resolve a
|
|
264
|
+
// thrown error to its DOMAIN CLASS (Gio.IOErrorEnum) by reading that member.
|
|
265
|
+
// getErrorDomain below answers the same relation the other way round, and L1's
|
|
266
|
+
// quark→class map is only populated by it — which is not enough on its own: an
|
|
267
|
+
// application that never named `Gio.IOErrorEnum` has nothing in the map, and the
|
|
268
|
+
// error it catches would land on the base class, breaking exactly the
|
|
269
|
+
// `catch (e) { if (e instanceof Gio.IOErrorEnum) }` idiom. Null for a quark no
|
|
270
|
+
// typelib registers (a private `GLib.quark_from_string` domain).
|
|
271
|
+
Napi::Value ErrorDomainInfo(const Napi::CallbackInfo& info) {
|
|
272
|
+
Napi::Env env = info.Env();
|
|
273
|
+
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
274
|
+
Napi::TypeError::New(env, "errorDomainInfo(quark: number)").ThrowAsJavaScriptException();
|
|
275
|
+
return env.Null();
|
|
276
|
+
}
|
|
277
|
+
GQuark quark = static_cast<GQuark>(NodeGiToUint32(info[0]));
|
|
278
|
+
if (quark == 0) return env.Null();
|
|
279
|
+
GIRepository* repo = DupDefaultRepository();
|
|
280
|
+
GIEnumInfo* enumInfo = gi_repository_find_by_error_domain(repo, quark);
|
|
281
|
+
Napi::Value result = env.Null();
|
|
282
|
+
if (enumInfo != nullptr) {
|
|
283
|
+
GIBaseInfo* base = reinterpret_cast<GIBaseInfo*>(enumInfo);
|
|
284
|
+
const char* ns = gi_base_info_get_namespace(base);
|
|
285
|
+
const char* name = gi_base_info_get_name(base);
|
|
286
|
+
if (ns != nullptr && name != nullptr) {
|
|
287
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
288
|
+
obj.Set("namespace", Napi::String::New(env, ns));
|
|
289
|
+
obj.Set("name", Napi::String::New(env, name));
|
|
290
|
+
result = obj;
|
|
291
|
+
}
|
|
292
|
+
gi_base_info_unref(base);
|
|
293
|
+
}
|
|
294
|
+
g_object_unref(repo);
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// setErrorBuilder(builder: (handle) => GLib.Error) -> void
|
|
299
|
+
// Register the L1 error factory the engine calls when a GI invoke fails, so a
|
|
300
|
+
// failed sync call throws the error object of its domain's class. Stored per-env
|
|
301
|
+
// (a napi_ref is env-specific). It takes the GError's boxed HANDLE — the L1 error
|
|
302
|
+
// IS that handle, which is why nothing else needs registering here: the GError
|
|
303
|
+
// IN-arg direction reads the same handle back off the object.
|
|
270
304
|
Napi::Value SetErrorBuilder(const Napi::CallbackInfo& info) {
|
|
271
305
|
Napi::Env env = info.Env();
|
|
272
306
|
if (info.Length() < 1 || !info[0].IsFunction()) {
|
|
273
|
-
Napi::TypeError::New(env, "setErrorBuilder(builder: function
|
|
307
|
+
Napi::TypeError::New(env, "setErrorBuilder(builder: function)")
|
|
274
308
|
.ThrowAsJavaScriptException();
|
|
275
309
|
return env.Undefined();
|
|
276
310
|
}
|
|
@@ -281,12 +315,6 @@ Napi::Value SetErrorBuilder(const Napi::CallbackInfo& info) {
|
|
|
281
315
|
d->errorBuilder = nullptr;
|
|
282
316
|
}
|
|
283
317
|
napi_create_reference(env, info[0], 1, &d->errorBuilder);
|
|
284
|
-
if (d->errorClass != nullptr) {
|
|
285
|
-
napi_delete_reference(env, d->errorClass);
|
|
286
|
-
d->errorClass = nullptr;
|
|
287
|
-
}
|
|
288
|
-
if (info.Length() >= 2 && info[1].IsFunction())
|
|
289
|
-
napi_create_reference(env, info[1], 1, &d->errorClass);
|
|
290
318
|
return env.Undefined();
|
|
291
319
|
}
|
|
292
320
|
|