node-gtk 4.0.1 → 4.1.0
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/lib/bootstrap.js +92 -1
- package/lib/module.js +6 -9
- package/lib/native.js +13 -3
- package/package.json +24 -7
- package/scripts/ci.sh +2 -5
- package/src/boxed.cc +13 -3
- package/src/callback.cc +12 -0
- package/src/function.cc +1 -4
- package/src/fundamental.cc +26 -0
- package/src/gi.cc +97 -0
- package/src/gi.h +15 -0
- package/src/gobject.cc +21 -5
- package/src/value.cc +8 -0
- package/src/value.h +10 -0
package/lib/bootstrap.js
CHANGED
|
@@ -2,9 +2,21 @@
|
|
|
2
2
|
* bootstrap.js
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const camelCaseRaw = require('lodash.camelcase')
|
|
6
6
|
const internal = require('./native.js')
|
|
7
7
|
|
|
8
|
+
// Method/field/property names repeat heavily across classes (get_name,
|
|
9
|
+
// set_visible, ...): memoize the case conversion.
|
|
10
|
+
const camelCaseCache = new Map()
|
|
11
|
+
function camelCase(name) {
|
|
12
|
+
let result = camelCaseCache.get(name)
|
|
13
|
+
if (result === undefined) {
|
|
14
|
+
result = camelCaseRaw(name)
|
|
15
|
+
camelCaseCache.set(name, result)
|
|
16
|
+
}
|
|
17
|
+
return result
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
// The bootstrap from C here contains functions and methods for each object,
|
|
9
21
|
// namespaced with underscores. See gi.cc for more information.
|
|
10
22
|
const GI = internal.Bootstrap();
|
|
@@ -15,6 +27,7 @@ module.exports = {
|
|
|
15
27
|
GI,
|
|
16
28
|
makeInfo,
|
|
17
29
|
getInfoName,
|
|
30
|
+
defineLazyInfos,
|
|
18
31
|
}
|
|
19
32
|
|
|
20
33
|
// The GIRepository API is fairly poor, and contains methods on classes,
|
|
@@ -341,6 +354,84 @@ function applyInterfaceMethods(constructor, interfaceRefs) {
|
|
|
341
354
|
|
|
342
355
|
internal.SetInterfaceMethodsApplier(applyInterfaceMethods)
|
|
343
356
|
|
|
357
|
+
/*
|
|
358
|
+
* Lazy type materialization. Namespaces expose thousands of top-level infos
|
|
359
|
+
* (6,600+ for Gtk-4.0 with its dependencies) but an app touches a few dozen:
|
|
360
|
+
* giRequire() defines one lazy accessor per info instead of building every
|
|
361
|
+
* class eagerly, and the first access runs makeInfo() and replaces the
|
|
362
|
+
* accessor with the result. Types reached from C before JS ever names them (a
|
|
363
|
+
* method return value, a signal argument) are materialized through the C++
|
|
364
|
+
* template-creation hook (SetTypeMaterializer below), so their prototypes are
|
|
365
|
+
* decorated before any wrapper is handed out.
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
const materializing = new Set()
|
|
369
|
+
|
|
370
|
+
function defineLazyInfos(module, ns) {
|
|
371
|
+
// One native call per namespace: a flat [name, infoType, index, ...] array
|
|
372
|
+
// of the infos makeInfo() can handle (enumerating via GI.* costs ~3 FFI
|
|
373
|
+
// round-trips per info, 48ms for Gtk-4.0 + dependencies).
|
|
374
|
+
const entries = internal.GetInfoEntries(ns)
|
|
375
|
+
|
|
376
|
+
for (let i = 0; i < entries.length; i += 3) {
|
|
377
|
+
const type = entries[i + 1]
|
|
378
|
+
const name = type === GI.InfoType.FUNCTION ? camelCase(entries[i]) : entries[i]
|
|
379
|
+
defineLazyInfo(module, ns, name, entries[i + 2])
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function defineLazyInfo(module, ns, name, index) {
|
|
384
|
+
const key = ns + '.' + name
|
|
385
|
+
|
|
386
|
+
Object.defineProperty(module, name, {
|
|
387
|
+
configurable: true,
|
|
388
|
+
enumerable: true,
|
|
389
|
+
get() {
|
|
390
|
+
/* Re-entered while materializing: the C++ hook fires for the class
|
|
391
|
+
* makeInfo() is currently building, and MakeBoxedClass() consults the
|
|
392
|
+
* module cache for the G_TYPE_NONE struct it is building. Report
|
|
393
|
+
* undefined; the outer call installs the final value. */
|
|
394
|
+
if (materializing.has(key))
|
|
395
|
+
return undefined
|
|
396
|
+
materializing.add(key)
|
|
397
|
+
try {
|
|
398
|
+
const repo = GI.Repository_get_default()
|
|
399
|
+
const info = GI.Repository_get_info.call(repo, ns, index)
|
|
400
|
+
const value = makeInfo(info)
|
|
401
|
+
Object.defineProperty(module, name, {
|
|
402
|
+
configurable: true, enumerable: true, writable: true, value,
|
|
403
|
+
})
|
|
404
|
+
return value
|
|
405
|
+
} finally {
|
|
406
|
+
materializing.delete(key)
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
/* Plain assignments must keep working (getInterface() and the overrides
|
|
410
|
+
* write straight into the module). */
|
|
411
|
+
set(value) {
|
|
412
|
+
Object.defineProperty(module, name, {
|
|
413
|
+
configurable: true, enumerable: true, writable: true, value,
|
|
414
|
+
})
|
|
415
|
+
},
|
|
416
|
+
})
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/*
|
|
420
|
+
* Called from C++ when a class template is first created for an
|
|
421
|
+
* introspectable type (GetClassTemplate/GetBoxedFunction/
|
|
422
|
+
* GetFundamentalTemplate), so that a type reached from C first has its
|
|
423
|
+
* prototype decorated. Triggering the accessor is enough; the accessor makes
|
|
424
|
+
* this idempotent and re-entrancy-safe.
|
|
425
|
+
*/
|
|
426
|
+
function materializeType(ns, name) {
|
|
427
|
+
const module = moduleCache[ns]
|
|
428
|
+
if (module === undefined)
|
|
429
|
+
return
|
|
430
|
+
void module[name]
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
internal.SetTypeMaterializer(materializeType)
|
|
434
|
+
|
|
344
435
|
function makeBoxed(info) {
|
|
345
436
|
if (getType(info) == GI.InfoType.UNION) {
|
|
346
437
|
return makeUnion(info)
|
package/lib/module.js
CHANGED
|
@@ -7,7 +7,7 @@ const util = require('util')
|
|
|
7
7
|
const readdir = util.promisify(fs.readdir)
|
|
8
8
|
|
|
9
9
|
const internal = require('./native.js')
|
|
10
|
-
const { GI,
|
|
10
|
+
const { GI, defineLazyInfos } = require('./bootstrap.js')
|
|
11
11
|
|
|
12
12
|
const moduleCache = internal.GetModuleCache();
|
|
13
13
|
|
|
@@ -37,14 +37,11 @@ function giRequire(ns, version) {
|
|
|
37
37
|
|
|
38
38
|
loadDependencies(ns, version)
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (item !== undefined)
|
|
46
|
-
module[getInfoName(info)] = item
|
|
47
|
-
}
|
|
40
|
+
// Top-level infos materialize on first access (see defineLazyInfos in
|
|
41
|
+
// bootstrap.js): building every class of a namespace and its dependencies
|
|
42
|
+
// eagerly used to cost ~160ms for Gtk-4.0, for the few dozen classes a
|
|
43
|
+
// typical app touches.
|
|
44
|
+
defineLazyInfos(module, ns)
|
|
48
45
|
|
|
49
46
|
// Apply overrides, if present
|
|
50
47
|
let override
|
package/lib/native.js
CHANGED
|
@@ -2,12 +2,22 @@
|
|
|
2
2
|
* native.js
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const binary = require('@mapbox/node-pre-gyp')
|
|
6
5
|
const path = require('path')
|
|
7
6
|
const fs = require('fs')
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
// Resolve the compiled addon directly rather than through node-pre-gyp:
|
|
9
|
+
// requiring node-pre-gyp costs ~16ms of startup for what amounts to
|
|
10
|
+
// evaluating the module_path template of package.json (`binary.find`). Keep
|
|
11
|
+
// it as a fallback for any layout the shorthand doesn't cover (e.g. a
|
|
12
|
+
// non-node runtime with a different ABI naming scheme).
|
|
13
|
+
const bindingName = `node-v${process.versions.modules}-${process.platform}-${process.arch}`
|
|
14
|
+
let bindingPath = path.join(__dirname, 'binding', bindingName, 'node_gtk.node')
|
|
15
|
+
|
|
16
|
+
if (!fs.existsSync(bindingPath)) {
|
|
17
|
+
const binary = require('@mapbox/node-pre-gyp')
|
|
18
|
+
const packagePath = path.resolve(path.join(__dirname, '../package.json'))
|
|
19
|
+
bindingPath = binary.find(packagePath)
|
|
20
|
+
}
|
|
11
21
|
|
|
12
22
|
// On Windows, the prebuilt binary ships with its whole GTK runtime bundled in
|
|
13
23
|
// the same directory as the .node (DLLs, GObject-Introspection typelibs, and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-gtk",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "GNOME Gtk+ bindings for NodeJS",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"homepage": "https://github.com/romgrk/node-gtk#readme",
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@mapbox/node-pre-gyp": "^
|
|
54
|
+
"@mapbox/node-pre-gyp": "^2.0.3",
|
|
55
55
|
"lodash.camelcase": "4.3.0",
|
|
56
56
|
"lodash.isequal": "4.5.0",
|
|
57
57
|
"lodash.snakecase": "^4.1.1",
|
|
@@ -62,11 +62,26 @@
|
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"assert": "^1.5.0",
|
|
65
|
-
"aws-sdk": "^2.
|
|
65
|
+
"aws-sdk": "^2.1692.0",
|
|
66
66
|
"chalk": "^2.4.2",
|
|
67
|
-
"mocha": "^7.
|
|
68
|
-
"nid-parser": "0.0.5"
|
|
69
|
-
|
|
67
|
+
"mocha": "^11.7.0",
|
|
68
|
+
"nid-parser": "0.0.5"
|
|
69
|
+
},
|
|
70
|
+
"overrides": {
|
|
71
|
+
"nid-parser": {
|
|
72
|
+
"js-yaml": "^3.15.0",
|
|
73
|
+
"extend": "^3.0.2"
|
|
74
|
+
},
|
|
75
|
+
"optimist": {
|
|
76
|
+
"minimist": "^1.2.8"
|
|
77
|
+
},
|
|
78
|
+
"aws-sdk": {
|
|
79
|
+
"uuid": "^11.1.1"
|
|
80
|
+
},
|
|
81
|
+
"mocha": {
|
|
82
|
+
"diff": "^8.0.3",
|
|
83
|
+
"serialize-javascript": "^7.0.5"
|
|
84
|
+
}
|
|
70
85
|
},
|
|
71
86
|
"files": [
|
|
72
87
|
"/bin",
|
|
@@ -74,7 +89,9 @@
|
|
|
74
89
|
"/src",
|
|
75
90
|
"/scripts",
|
|
76
91
|
"/tools",
|
|
77
|
-
"binding.gyp"
|
|
92
|
+
"binding.gyp",
|
|
93
|
+
"!lib/binding",
|
|
94
|
+
"!**/*.node"
|
|
78
95
|
],
|
|
79
96
|
"binary": {
|
|
80
97
|
"module_name": "node_gtk",
|
package/scripts/ci.sh
CHANGED
|
@@ -46,12 +46,9 @@ function npm_test() {
|
|
|
46
46
|
# fixture build does not run automatically; do it here. Best-effort:
|
|
47
47
|
# marshalling tests skip if fixtures cannot be produced on macOS.
|
|
48
48
|
npm run build:test-fixtures || true;
|
|
49
|
-
npx mocha
|
|
50
|
-
--skip=callback \
|
|
51
|
-
tests/__run__.js
|
|
49
|
+
NODE_GTK_TEST_SKIP=callback npx mocha tests/__run__.js
|
|
52
50
|
else
|
|
53
|
-
xvfb-run -a npm test
|
|
54
|
-
--skip=callback;
|
|
51
|
+
NODE_GTK_TEST_SKIP=callback xvfb-run -a npm test;
|
|
55
52
|
fi;
|
|
56
53
|
}
|
|
57
54
|
|
package/src/boxed.cc
CHANGED
|
@@ -409,6 +409,14 @@ Local<Function> GetBoxedFunction(GIBaseInfo *info, GType gtype) {
|
|
|
409
409
|
|
|
410
410
|
g_type_set_qdata(gtype, GNodeJS::function_quark(), persistent);
|
|
411
411
|
|
|
412
|
+
/* Modules hold lazy accessors: a registered boxed type reached from C
|
|
413
|
+
* first (method return value, signal argument) must be materialized so
|
|
414
|
+
* makeBoxed() in JS attaches its methods/fields before a wrapper is
|
|
415
|
+
* handed out. Fired after the qdata above so the re-entrant
|
|
416
|
+
* MakeBoxedClass() from JS finds this same function. Idempotent and
|
|
417
|
+
* re-entrancy-safe on the JS side. */
|
|
418
|
+
GNodeJS::MaterializeType(info);
|
|
419
|
+
|
|
412
420
|
return fn;
|
|
413
421
|
}
|
|
414
422
|
|
|
@@ -430,10 +438,12 @@ Local<Function> MakeBoxedClass(GIBaseInfo *info) {
|
|
|
430
438
|
if (Nan::HasOwnProperty(moduleCache, ns).FromMaybe(false)) {
|
|
431
439
|
auto module = TO_OBJECT (Nan::Get(moduleCache, ns).ToLocalChecked());
|
|
432
440
|
|
|
433
|
-
|
|
434
|
-
|
|
441
|
+
/* The Get can run a lazy accessor. It returns the materialized
|
|
442
|
+
* class, or undefined when re-entered from the accessor's own
|
|
443
|
+
* makeBoxed() call — fall through and build the class then. */
|
|
444
|
+
auto constructor = Nan::Get(module, name).ToLocalChecked();
|
|
445
|
+
if (constructor->IsFunction())
|
|
435
446
|
return Local<Function>::Cast (constructor);
|
|
436
|
-
}
|
|
437
447
|
}
|
|
438
448
|
}
|
|
439
449
|
|
package/src/callback.cc
CHANGED
|
@@ -220,6 +220,18 @@ void Callback::Execute (GIArgument *result, GIArgument **args, Callback *callbac
|
|
|
220
220
|
Throw::InvalidReturnValue (&return_type_info, jsReturnValue);
|
|
221
221
|
goto out;
|
|
222
222
|
}
|
|
223
|
+
|
|
224
|
+
// When the callback's return value is transfer-full, the C caller takes
|
|
225
|
+
// ownership of it (e.g. GtkTreeListModelCreateModelFunc → GListModel).
|
|
226
|
+
// V8ToGIArgument only unwrapped the pointer, leaving node-gtk holding
|
|
227
|
+
// just its toggle ref; without the owning reference the caller "owns"
|
|
228
|
+
// that toggle ref, so no toggle-up fires, the wrapper stays weak, and
|
|
229
|
+
// once GC collects it the object is finalized while the caller still
|
|
230
|
+
// uses it — a use-after-free (e.g. gtk_tree_list_model_finalize
|
|
231
|
+
// disconnecting its items-changed handler from a freed child model).
|
|
232
|
+
// This is the return counterpart of the transfer-full IN argument.
|
|
233
|
+
if (g_callable_info_get_caller_owns(callback->info) == GI_TRANSFER_EVERYTHING)
|
|
234
|
+
TakeOwnershipForTransferFull(&return_type_info, result, -1);
|
|
223
235
|
}
|
|
224
236
|
|
|
225
237
|
// TODO: assess transferness of arguments & check if we need to free them
|
package/src/function.cc
CHANGED
|
@@ -527,10 +527,7 @@ Local<Value> FunctionCall (
|
|
|
527
527
|
// GskRenderNode): add the reference the callee will own (#468).
|
|
528
528
|
else if (direction == GI_DIRECTION_IN
|
|
529
529
|
&& g_arg_info_get_ownership_transfer(&arg_info) == GI_TRANSFER_EVERYTHING) {
|
|
530
|
-
|
|
531
|
-
RefObjectForTransferFullIn(&type_info, &callable_arg_values[i]);
|
|
532
|
-
RefFundamentalForTransferFullIn(&type_info, &callable_arg_values[i]);
|
|
533
|
-
RefVariantForTransferFullIn(&type_info, &callable_arg_values[i]);
|
|
530
|
+
TakeOwnershipForTransferFull(&type_info, &callable_arg_values[i], param.length);
|
|
534
531
|
}
|
|
535
532
|
}
|
|
536
533
|
|
package/src/fundamental.cc
CHANGED
|
@@ -239,6 +239,16 @@ static Local<FunctionTemplate> GetFundamentalTemplate (GIObjectInfo *info, GType
|
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
/* The parent recursion above runs JS (each ancestor's template fires the
|
|
243
|
+
* type materializer below), so JS may have re-entered here and created
|
|
244
|
+
* this very template already. Keep that one: its function is the one JS
|
|
245
|
+
* has started decorating. */
|
|
246
|
+
data = g_type_get_qdata (gtype, GNodeJS::template_quark ());
|
|
247
|
+
if (data) {
|
|
248
|
+
auto *persistent = (Persistent<FunctionTemplate> *) data;
|
|
249
|
+
return New<FunctionTemplate> (*persistent);
|
|
250
|
+
}
|
|
251
|
+
|
|
242
252
|
auto *persistentTpl = new Persistent<FunctionTemplate> (tpl);
|
|
243
253
|
auto *persistentFn = new Persistent<Function> (Nan::GetFunction (tpl).ToLocalChecked ());
|
|
244
254
|
persistentTpl->SetWeak (
|
|
@@ -247,6 +257,11 @@ static Local<FunctionTemplate> GetFundamentalTemplate (GIObjectInfo *info, GType
|
|
|
247
257
|
g_type_set_qdata (gtype, GNodeJS::template_quark (), persistentTpl);
|
|
248
258
|
g_type_set_qdata (gtype, GNodeJS::function_quark (), persistentFn);
|
|
249
259
|
|
|
260
|
+
/* Modules hold lazy accessors: a fundamental type reached from C first
|
|
261
|
+
* must be materialized so makeObject() in JS attaches its methods before
|
|
262
|
+
* a wrapper is handed out (see gi.h). */
|
|
263
|
+
GNodeJS::MaterializeType (info);
|
|
264
|
+
|
|
250
265
|
return tpl;
|
|
251
266
|
}
|
|
252
267
|
|
|
@@ -405,6 +420,17 @@ static Local<FunctionTemplate> GetVariantTemplate () {
|
|
|
405
420
|
g_type_set_qdata (gtype, GNodeJS::template_quark (), persistentTpl);
|
|
406
421
|
g_type_set_qdata (gtype, GNodeJS::function_quark (), persistentFn);
|
|
407
422
|
|
|
423
|
+
/* Modules hold lazy accessors: a variant reached from C first (e.g. a
|
|
424
|
+
* signal argument, #465) must have GLib.Variant materialized so
|
|
425
|
+
* makeBoxed() in JS attaches its methods before a wrapper is handed out. */
|
|
426
|
+
if (g_irepository_is_registered (NULL, "GLib", NULL)) {
|
|
427
|
+
GIBaseInfo *variant_info = g_irepository_find_by_name (NULL, "GLib", "Variant");
|
|
428
|
+
if (variant_info) {
|
|
429
|
+
GNodeJS::MaterializeType (variant_info);
|
|
430
|
+
g_base_info_unref (variant_info);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
408
434
|
return tpl;
|
|
409
435
|
}
|
|
410
436
|
|
package/src/gi.cc
CHANGED
|
@@ -35,6 +35,32 @@ namespace GNodeJS {
|
|
|
35
35
|
Local<Object> GetModuleCache() {
|
|
36
36
|
return Nan::New<Object>(GNodeJS::moduleCache);
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
static Nan::Persistent<v8::Function> typeMaterializer;
|
|
40
|
+
|
|
41
|
+
void SetTypeMaterializerInternal(Local<v8::Function> fn) {
|
|
42
|
+
typeMaterializer.Reset(fn);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void MaterializeType(GIBaseInfo *info) {
|
|
46
|
+
if (typeMaterializer.IsEmpty() || info == NULL)
|
|
47
|
+
return;
|
|
48
|
+
|
|
49
|
+
Local<v8::Function> fn = Nan::New<v8::Function>(typeMaterializer);
|
|
50
|
+
Local<Value> argv[] = {
|
|
51
|
+
UTF8(g_base_info_get_namespace(info)),
|
|
52
|
+
UTF8(g_base_info_get_name(info)),
|
|
53
|
+
};
|
|
54
|
+
Nan::TryCatch tryCatch;
|
|
55
|
+
Nan::Call(fn, Nan::GetCurrentContext()->Global(), 2, argv);
|
|
56
|
+
if (tryCatch.HasCaught()) {
|
|
57
|
+
Nan::Utf8String message(tryCatch.Exception());
|
|
58
|
+
g_warning("node-gtk: could not materialize type %s.%s: %s",
|
|
59
|
+
g_base_info_get_namespace(info),
|
|
60
|
+
g_base_info_get_name(info),
|
|
61
|
+
*message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
38
64
|
}
|
|
39
65
|
|
|
40
66
|
|
|
@@ -121,6 +147,71 @@ NAN_METHOD(Bootstrap) {
|
|
|
121
147
|
info.GetReturnValue().Set(module_obj);
|
|
122
148
|
}
|
|
123
149
|
|
|
150
|
+
/*
|
|
151
|
+
* Enumerate a loaded namespace's top-level infos in one native call, so that
|
|
152
|
+
* lib/module.js can define lazy accessors without paying ~3 FFI round-trips
|
|
153
|
+
* per info (48ms for Gtk-4.0 + dependencies, vs ~1ms here). Returns a flat
|
|
154
|
+
* array of [name, infoType, index, ...] triplets. Infos that makeInfo() cannot
|
|
155
|
+
* handle (callbacks, gtype structs, etc.) are filtered out here, mirroring the
|
|
156
|
+
* `item !== undefined` check the eager loop used to do.
|
|
157
|
+
*/
|
|
158
|
+
NAN_METHOD(GetInfoEntries) {
|
|
159
|
+
Nan::Utf8String ns(info[0]);
|
|
160
|
+
GIRepository *repo = g_irepository_get_default();
|
|
161
|
+
|
|
162
|
+
int n = g_irepository_get_n_infos(repo, *ns);
|
|
163
|
+
Local<Array> result = Nan::New<Array>();
|
|
164
|
+
uint32_t position = 0;
|
|
165
|
+
|
|
166
|
+
for (int i = 0; i < n; i++) {
|
|
167
|
+
BaseInfo baseInfo(g_irepository_get_info(repo, *ns, i));
|
|
168
|
+
GIInfoType type = baseInfo.type();
|
|
169
|
+
|
|
170
|
+
switch (type) {
|
|
171
|
+
case GI_INFO_TYPE_FUNCTION:
|
|
172
|
+
case GI_INFO_TYPE_BOXED:
|
|
173
|
+
case GI_INFO_TYPE_ENUM:
|
|
174
|
+
case GI_INFO_TYPE_FLAGS:
|
|
175
|
+
case GI_INFO_TYPE_OBJECT:
|
|
176
|
+
case GI_INFO_TYPE_INTERFACE:
|
|
177
|
+
case GI_INFO_TYPE_CONSTANT:
|
|
178
|
+
case GI_INFO_TYPE_UNION:
|
|
179
|
+
break;
|
|
180
|
+
case GI_INFO_TYPE_STRUCT:
|
|
181
|
+
if (g_struct_info_is_gtype_struct(baseInfo.info()))
|
|
182
|
+
continue;
|
|
183
|
+
break;
|
|
184
|
+
default:
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Register the GType with the GObject runtime NOW, even though the JS
|
|
189
|
+
* class stays lazy. The eager loop did this as a side effect
|
|
190
|
+
* (MakeObjectClass/MakeBoxedClass call get_g_type() for every class)
|
|
191
|
+
* and code depends on it: by-name lookups such as
|
|
192
|
+
* GObject.typeFromName('GFileInfo') or type names in GtkBuilder XML
|
|
193
|
+
* must resolve without JS ever touching the class. Registration is a
|
|
194
|
+
* few µs per type; class initialization stays lazy either way. */
|
|
195
|
+
switch (type) {
|
|
196
|
+
case GI_INFO_TYPE_STRUCT:
|
|
197
|
+
case GI_INFO_TYPE_BOXED:
|
|
198
|
+
case GI_INFO_TYPE_UNION:
|
|
199
|
+
case GI_INFO_TYPE_OBJECT:
|
|
200
|
+
case GI_INFO_TYPE_INTERFACE:
|
|
201
|
+
g_registered_type_info_get_g_type((GIRegisteredTypeInfo *) baseInfo.info());
|
|
202
|
+
break;
|
|
203
|
+
default:
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
Nan::Set(result, position++, UTF8(baseInfo.name()));
|
|
208
|
+
Nan::Set(result, position++, Nan::New<v8::Int32>(type));
|
|
209
|
+
Nan::Set(result, position++, Nan::New<v8::Int32>(i));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
info.GetReturnValue().Set(result);
|
|
213
|
+
}
|
|
214
|
+
|
|
124
215
|
NAN_METHOD(GetConstantValue) {
|
|
125
216
|
GIBaseInfo *gi_info = (GIBaseInfo *) GNodeJS::PointerFromWrapper (info[0]);
|
|
126
217
|
GITypeInfo *type_info = g_constant_info_get_type(gi_info);
|
|
@@ -418,6 +509,10 @@ NAN_METHOD(SetInterfaceMethodsApplier) {
|
|
|
418
509
|
GNodeJS::SetInterfaceMethodsApplier(info);
|
|
419
510
|
}
|
|
420
511
|
|
|
512
|
+
NAN_METHOD(SetTypeMaterializer) {
|
|
513
|
+
GNodeJS::SetTypeMaterializerInternal(info[0].As<v8::Function>());
|
|
514
|
+
}
|
|
515
|
+
|
|
421
516
|
NAN_METHOD(RegisterClass) {
|
|
422
517
|
GNodeJS::ObjectClass::RegisterClass(info);
|
|
423
518
|
}
|
|
@@ -438,6 +533,8 @@ void InitModule(Local<Object> exports, Local<Value> module, void *priv) {
|
|
|
438
533
|
Nan::Export(exports, "GetBaseClass", GetBaseClass);
|
|
439
534
|
Nan::Export(exports, "GetTypeSize", GetTypeSize);
|
|
440
535
|
Nan::Export(exports, "GetConstantValue", GetConstantValue);
|
|
536
|
+
Nan::Export(exports, "GetInfoEntries", GetInfoEntries);
|
|
537
|
+
Nan::Export(exports, "SetTypeMaterializer", SetTypeMaterializer);
|
|
441
538
|
Nan::Export(exports, "MakeBoxedClass", MakeBoxedClass);
|
|
442
539
|
Nan::Export(exports, "MakeObjectClass", MakeObjectClass);
|
|
443
540
|
Nan::Export(exports, "MakeFunction", MakeFunction);
|
package/src/gi.h
CHANGED
|
@@ -26,6 +26,21 @@ extern Nan::Persistent<Object> moduleCache;
|
|
|
26
26
|
|
|
27
27
|
Local<Object> GetModuleCache();
|
|
28
28
|
|
|
29
|
+
/*
|
|
30
|
+
* Lazy type materialization: modules are populated with lazy accessors
|
|
31
|
+
* (lib/module.js), and prototypes only get their methods/properties when a
|
|
32
|
+
* class is first touched. Types can also be reached from C first (a method
|
|
33
|
+
* return value, a signal argument): the template-creation paths in
|
|
34
|
+
* gobject.cc/boxed.cc/fundamental.cc call MaterializeType so the JS side
|
|
35
|
+
* decorates the prototype before any wrapper is handed out. The callback is
|
|
36
|
+
* installed from bootstrap.js via SetTypeMaterializer and is idempotent and
|
|
37
|
+
* re-entrancy-safe on the JS side.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
void MaterializeType(GIBaseInfo *info);
|
|
41
|
+
|
|
42
|
+
void SetTypeMaterializerInternal(Local<v8::Function> fn);
|
|
43
|
+
|
|
29
44
|
|
|
30
45
|
|
|
31
46
|
/*
|
package/src/gobject.cc
CHANGED
|
@@ -878,6 +878,17 @@ static MaybeLocal<FunctionTemplate> GetClassTemplate(GType gtype) {
|
|
|
878
878
|
if (maybeTpl.IsEmpty())
|
|
879
879
|
return MaybeLocal<FunctionTemplate> ();
|
|
880
880
|
|
|
881
|
+
/* NewClassTemplate() runs JS while building the parent chain (each
|
|
882
|
+
* ancestor's template fires the type materializer below), so JS may have
|
|
883
|
+
* re-entered here and created this very template already. Keep that one:
|
|
884
|
+
* its function is the one JS has started decorating. */
|
|
885
|
+
data = g_type_get_qdata (gtype, GNodeJS::template_quark());
|
|
886
|
+
if (data) {
|
|
887
|
+
auto *persistent = (Nan::Persistent<FunctionTemplate> *) data;
|
|
888
|
+
auto existingTpl = New<FunctionTemplate> (*persistent);
|
|
889
|
+
return existingTpl;
|
|
890
|
+
}
|
|
891
|
+
|
|
881
892
|
auto tpl = maybeTpl.ToLocalChecked();
|
|
882
893
|
auto fn = Nan::GetFunction (tpl).ToLocalChecked();
|
|
883
894
|
auto persistentTpl = new Nan::Persistent<FunctionTemplate>(tpl);
|
|
@@ -892,14 +903,19 @@ static MaybeLocal<FunctionTemplate> GetClassTemplate(GType gtype) {
|
|
|
892
903
|
g_type_set_qdata(gtype, GNodeJS::template_quark(), persistentTpl);
|
|
893
904
|
g_type_set_qdata(gtype, GNodeJS::function_quark(), persistentFn);
|
|
894
905
|
|
|
895
|
-
// Introspectable object types have their
|
|
896
|
-
// makeObject() in JS.
|
|
897
|
-
//
|
|
906
|
+
// Introspectable object types have their methods/properties installed by
|
|
907
|
+
// makeObject() in JS. Modules hold lazy accessors, so a type reached from
|
|
908
|
+
// C first (method return value, signal argument) must be materialized here
|
|
909
|
+
// or its wrappers would expose a bare prototype. Private concrete types
|
|
910
|
+
// are invisible to makeObject(), so mix in their interface methods
|
|
911
|
+
// instead (issue #441).
|
|
898
912
|
GIBaseInfo *own_info = g_irepository_find_by_gtype(NULL, gtype);
|
|
899
|
-
if (own_info == NULL)
|
|
913
|
+
if (own_info == NULL) {
|
|
900
914
|
ApplyInterfaceMethods(fn, gtype);
|
|
901
|
-
else
|
|
915
|
+
} else {
|
|
916
|
+
GNodeJS::MaterializeType(own_info);
|
|
902
917
|
g_base_info_unref(own_info);
|
|
918
|
+
}
|
|
903
919
|
|
|
904
920
|
return MaybeLocal<FunctionTemplate> (tpl);
|
|
905
921
|
}
|
package/src/value.cc
CHANGED
|
@@ -1702,6 +1702,14 @@ void RefObjectForTransferFullIn (GITypeInfo *type_info, GIArgument *arg) {
|
|
|
1702
1702
|
g_base_info_unref(iface);
|
|
1703
1703
|
}
|
|
1704
1704
|
|
|
1705
|
+
// Documented on the declaration in value.h.
|
|
1706
|
+
void TakeOwnershipForTransferFull (GITypeInfo *type_info, GIArgument *arg, long length) {
|
|
1707
|
+
CopyBoxedForTransferFullIn(type_info, arg, length);
|
|
1708
|
+
RefObjectForTransferFullIn(type_info, arg);
|
|
1709
|
+
RefFundamentalForTransferFullIn(type_info, arg);
|
|
1710
|
+
RefVariantForTransferFullIn(type_info, arg);
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1705
1713
|
|
|
1706
1714
|
/*
|
|
1707
1715
|
* GValue conversion functions
|
package/src/value.h
CHANGED
|
@@ -52,6 +52,16 @@ void CopyBoxedForTransferFullIn (GITypeInfo *type_info, GIArgument *arg,
|
|
|
52
52
|
// under the callee once the wrapper is GC'd. See #439.
|
|
53
53
|
void RefObjectForTransferFullIn (GITypeInfo *type_info, GIArgument *arg);
|
|
54
54
|
|
|
55
|
+
// Hand a transfer-full value its own reference for its new owner. A value whose
|
|
56
|
+
// ownership transfers full — a transfer-full IN argument, or a transfer-full
|
|
57
|
+
// callable return — is exactly one of boxed / GObject / fundamental / GVariant,
|
|
58
|
+
// and the four per-kind helpers (CopyBoxedForTransferFullIn / RefObjectFor... /
|
|
59
|
+
// RefFundamentalFor... / RefVariantFor...) each no-op for the other kinds. Runs
|
|
60
|
+
// them all so the single call covers every kind. `length` is the element count
|
|
61
|
+
// for a transfer-full C array of boxed pointers (-1 when not such an array).
|
|
62
|
+
// See #439/#409/#468.
|
|
63
|
+
void TakeOwnershipForTransferFull (GITypeInfo *type_info, GIArgument *arg, long length);
|
|
64
|
+
|
|
55
65
|
bool CanConvertV8ToGIArgument (GITypeInfo *type_info, Local<Value> value, bool may_be_null);
|
|
56
66
|
|
|
57
67
|
bool V8ToGValue(GValue *gvalue, Local<Value> value, ResourceOwnership ownership = kNone);
|