From 2268e8345421d0e9ab73df1085e6a0bb35d57842 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Tue, 26 Dec 2023 02:12:02 +0200 Subject: [PATCH] hydrate objects constructed from GtkBuilder This runs JS constructors on objects that were not constructed from JavaScript. This is effectively objects constructed from GtkBuilder. This is done by tracking all objects currently being constructed in a variable named `ConstructContext`. If an object's `TypeInfo`'s `instance_init` method (see https://docs.gtk.org/gobject/struct.TypeInfo.html) while it's gType is not the currently being constructed type (i.e. the last element of `ConstructContext`), it is assumed that the object is being constructed from GtkBuilder and the object's constructor is called, but instead of creating a new `GObject`, the constructed object's `gi:ref` (pointer) is passed instead, and the javascript "construction" continues. --- src/types/object.js | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/types/object.js b/src/types/object.js index 895d469..c1199d6 100644 --- a/src/types/object.js +++ b/src/types/object.js @@ -98,8 +98,22 @@ function defineClassStructMethods(target, info) { } } -/** @type number | null */ -export let _HydratingObject = null; +/** @type bigint | null */ +let HydratingObject = null; + +/** + * @param {bigint | null} object + */ +export function _setHydratingObject(object) { + HydratingObject = object; +} + +/** + * An array of GTypes being currently constructed. This is to catch JS objects + * whose instance_init is called. + * @type bigint[] + */ +export const ConstructContext = []; export function createObject(info, gType) { const ParentClass = getParentClass(info) ?? Object; @@ -109,22 +123,31 @@ export function createObject(info, gType) { super(props); if (gType == GType.OBJECT) { - if (_HydratingObject === null) { - const gType = Reflect.getOwnMetadata("gi:gtype", this.constructor); + const klass = this.constructor; + + if (HydratingObject === null) { + const gType = Reflect.getOwnMetadata("gi:gtype", klass); if (!gType) { throw new Error("Tried to construct an object without a GType"); } + ConstructContext.push(gType); + Reflect.defineMetadata("gi:ref", g.object.new(gType, null), this); Object.entries(props).forEach(([key, value]) => { this[key] = value; }); + + ConstructContext.pop(); } else { - Reflect.defineMetadata("gi:ref", _HydratingObject, this); + Reflect.defineMetadata("gi:ref", HydratingObject, this); - _HydratingObject = null; + HydratingObject = null; } + + const init_fn = Reflect.getMetadata("gi:instance_init", klass); + if (init_fn) init_fn.call(this); } } };