[gjs/ewlsh/fix-closures: 1/2] Support GObject.Closure boxed type and invoke()
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/ewlsh/fix-closures: 1/2] Support GObject.Closure boxed type and invoke()
- Date: Sat, 15 Jan 2022 20:09:39 +0000 (UTC)
commit fe3a9841c61a0ff7ba8c86fe5b1139a35a2818bd
Author: Evan Welsh <contact evanwelsh com>
Date: Sat Jan 15 12:04:43 2022 -0800
Support GObject.Closure boxed type and invoke()
gi/arg-cache.cpp | 14 ++++
gi/arg.cpp | 29 ++++---
gi/boxed.cpp | 10 +--
gi/private.cpp | 114 ++++++++++++++++++++++++++++
installed-tests/js/.eslintrc.yml | 3 +
installed-tests/js/dataUtils.js | 115 ++++++++++++++++++++++++++++
installed-tests/js/meson.build | 3 +-
installed-tests/js/testGIMarshalling.js | 14 ++--
installed-tests/js/testGObject.js | 2 +-
installed-tests/js/testGObjectClosure.js | 56 ++++++++++++++
installed-tests/js/testGObjectValue.js | 125 +++----------------------------
modules/core/overrides/GObject.js | 32 ++++++++
12 files changed, 379 insertions(+), 138 deletions(-)
---
diff --git a/gi/arg-cache.cpp b/gi/arg-cache.cpp
index 2fda580c..4079bf46 100644
--- a/gi/arg-cache.cpp
+++ b/gi/arg-cache.cpp
@@ -1166,6 +1166,20 @@ bool GClosureInTransferNone::in(JSContext* cx, GjsFunctionCallState* state,
if (value.isNull())
return NullableIn::in(cx, state, arg, value);
+ if (value.isObject()) {
+ JS::RootedObject obj(cx, &value.toObject());
+ GType gtype;
+
+ if (!gjs_gtype_get_actual_gtype(cx, obj, >ype))
+ return false;
+
+ if (gtype == G_TYPE_CLOSURE) {
+ gjs_arg_set(arg, BoxedBase::to_c_ptr<Gjs::Closure>(cx, obj));
+ state->ignore_release.insert(arg);
+ return true;
+ }
+ }
+
if (!(JS_TypeOfValue(cx, value) == JSTYPE_FUNCTION))
return report_typeof_mismatch(cx, m_arg_name, value,
ExpectedType::FUNCTION);
diff --git a/gi/arg.cpp b/gi/arg.cpp
index 99446bf7..9d49e6b6 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -1346,16 +1346,27 @@ static bool value_to_interface_gi_argument(
} else if (g_type_is_a(gtype, G_TYPE_BOXED)) {
if (g_type_is_a(gtype, G_TYPE_CLOSURE)) {
- GClosure* closure = Gjs::Closure::create_marshaled(
- cx, JS_GetObjectFunction(obj), "boxed");
- // GI doesn't know about floating GClosure references. We
- // guess that if this is a return value going from JS::Value
- // to GArgument, it's intended to be passed to a C API that
- // will consume the floating reference.
- if (arg_type != GJS_ARGUMENT_RETURN_VALUE) {
- g_closure_ref(closure);
- g_closure_sink(closure);
+ if (JS_ObjectIsFunction(obj)) {
+ GClosure* closure = Gjs::Closure::create_marshaled(
+ cx, JS_GetObjectFunction(obj), "boxed");
+ // GI doesn't know about floating GClosure references.
+ // We guess that if this is a return value going from
+ // JS::Value to GArgument, it's intended to be passed to
+ // a C API that will consume the floating reference.
+ if (arg_type != GJS_ARGUMENT_RETURN_VALUE) {
+ g_closure_ref(closure);
+ g_closure_sink(closure);
+ }
+
+ gjs_arg_set(arg, closure);
+ return true;
}
+
+ Gjs::Closure* closure =
+ BoxedBase::to_c_ptr<Gjs::Closure>(cx, obj);
+ if (!closure)
+ return false;
+
gjs_arg_set(arg, closure);
return true;
}
diff --git a/gi/boxed.cpp b/gi/boxed.cpp
index 92af1366..f5e9efde 100644
--- a/gi/boxed.cpp
+++ b/gi/boxed.cpp
@@ -323,18 +323,18 @@ bool BoxedInstance::constructor_impl(JSContext* context, JS::HandleObject obj,
}
}
- if (gtype() == G_TYPE_VARIANT) {
+ if (gtype() == G_TYPE_VARIANT || gtype() == G_TYPE_CLOSURE) {
/* Short-circuit construction for GVariants by calling into the JS packing
function */
const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
if (!boxed_invoke_constructor(context, obj, atoms.new_internal(), args))
return false;
- // The return value of GLib.Variant.new_internal() gets its own
- // BoxedInstance, and the one we're setting up in this constructor is
- // discarded.
+ // The return values of GLib.Variant.new_internal() and
+ // GObject.Closure.new_internal() gets their own BoxedInstance,
+ // and the one we're setting up in this constructor is discarded.
debug_lifecycle(
- "Boxed construction delegated to GVariant constructor, "
+ "Boxed construction delegated to JavaScript constructor, "
"boxed object discarded");
return true;
diff --git a/gi/private.cpp b/gi/private.cpp
index 4a07afb1..d1f68258 100644
--- a/gi/private.cpp
+++ b/gi/private.cpp
@@ -18,6 +18,7 @@
#include <js/Utility.h> // for UniqueChars
#include <jsapi.h> // for JS_GetElement
+#include "gi/boxed.h"
#include "gi/gobject.h"
#include "gi/gtype.h"
#include "gi/interface.h"
@@ -238,6 +239,117 @@ static inline void gjs_add_interface(GType instance_type,
&interface_vtable);
}
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_value_from_closure(JSContext* cx, Gjs::Closure* closure,
+ JS::MutableHandleValue value) {
+ GjsAutoStructInfo info =
+ g_irepository_find_by_gtype(nullptr, G_TYPE_CLOSURE);
+ g_assert(info);
+
+ JS::RootedObject boxed(cx, BoxedInstance::new_for_c_struct(
+ cx, info, closure, BoxedInstance::NoCopy()));
+ if (!boxed)
+ return false;
+
+ value.setObject(*boxed);
+ return true;
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_create_closure(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ JS::RootedObject callable(cx);
+
+ if (!gjs_parse_call_args(cx, "create_closure", args, "o", "callable",
+ &callable))
+ return false;
+
+ if (!JS_ObjectIsFunction(callable)) {
+ gjs_throw(cx, "create_closure() expects a callable function");
+ return false;
+ }
+
+ JS::RootedFunction func(cx, JS_GetObjectFunction(callable));
+
+ Gjs::Closure* closure =
+ Gjs::Closure::create_marshaled(cx, func, "custom callback");
+ if (closure == nullptr)
+ return false;
+
+ return gjs_value_from_closure(cx, closure, args.rval());
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_invoke_closure(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ JS::RootedObject closure(cx);
+ JS::RootedObject this_object(cx);
+ JS::RootedObject params(cx);
+ JS::RootedObject return_type(cx);
+
+ if (!gjs_parse_call_args(cx, "invoke_closure", args, "o?oo?o", "closure",
+ &closure, "this_object", &this_object, "params",
+ ¶ms, "return_type", &return_type))
+ return false;
+
+ GType gtype;
+
+ if (!gjs_gtype_get_actual_gtype(cx, closure, >ype))
+ return false;
+
+ if (gtype != G_TYPE_CLOSURE) {
+ gjs_throw(cx, "Expected closure.");
+ return false;
+ }
+
+ Gjs::Closure* gjs_closure = BoxedBase::to_c_ptr<Gjs::Closure>(cx, closure);
+ if (closure == nullptr)
+ return false;
+
+ bool isArray;
+ if (!JS::IsArrayObject(cx, params, &isArray))
+ return false;
+ if (!isArray) {
+ gjs_throw(cx, "No array.");
+ return false;
+ }
+
+ uint32_t length;
+ if (!JS::GetArrayLength(cx, params, &length))
+ return false;
+ JS::RootedValue elem(cx);
+ AutoGValueVector param_values;
+ param_values.reserve(length);
+ for (uint32_t i = 0; i < length; i++) {
+ if (!JS_GetElement(cx, params, i, &elem))
+ return false;
+ Gjs::AutoGValue& value = param_values.emplace_back();
+ if (!gjs_value_to_g_value(cx, elem, &value))
+ return false;
+ }
+
+ if (return_type) {
+ GValue return_value = {0};
+ GType return_gtype;
+ if (!gjs_gtype_get_actual_gtype(cx, return_type, &return_gtype))
+ return false;
+ if (return_gtype == G_TYPE_INVALID) {
+ gjs_throw(cx, "Invalid gtype.");
+ return false;
+ }
+ g_value_init(&return_value, return_gtype);
+ g_closure_invoke(gjs_closure, &return_value, param_values.size(),
+ param_values.data(), nullptr);
+ return gjs_value_from_g_value(cx, args.rval(), &return_value);
+ }
+ g_closure_invoke(gjs_closure, nullptr, param_values.size(),
+ param_values.data(), nullptr);
+ args.rval().setNull();
+ return true;
+}
+
GJS_JSAPI_RETURN_CONVENTION
static bool gjs_register_type(JSContext* cx, unsigned argc, JS::Value* vp) {
JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
@@ -409,6 +521,8 @@ static JSFunctionSpec private_module_funcs[] = {
GJS_MODULE_PROP_FLAGS),
JS_FN("register_type", gjs_register_type, 4, GJS_MODULE_PROP_FLAGS),
JS_FN("signal_new", gjs_signal_new, 6, GJS_MODULE_PROP_FLAGS),
+ JS_FN("create_closure", gjs_create_closure, 1, GJS_MODULE_PROP_FLAGS),
+ JS_FN("invoke_closure", gjs_invoke_closure, 3, GJS_MODULE_PROP_FLAGS),
JS_FS_END,
};
diff --git a/installed-tests/js/.eslintrc.yml b/installed-tests/js/.eslintrc.yml
index bbf09ab2..74eefc1b 100644
--- a/installed-tests/js/.eslintrc.yml
+++ b/installed-tests/js/.eslintrc.yml
@@ -32,12 +32,15 @@ globals:
overrides:
- files:
- matchers.js
+ - dataUtils.js
- testAsync.js
- testCairoModule.js
- testConsole.js
- testESModules.js
- testEncoding.js
- testGLibLogWriter.js
+ - testGObjectClosure.js
+ - testGObjectValue.js
- modules/importmeta.js
- modules/exports.js
- modules/say.js
diff --git a/installed-tests/js/dataUtils.js b/installed-tests/js/dataUtils.js
new file mode 100644
index 00000000..f6ad6be5
--- /dev/null
+++ b/installed-tests/js/dataUtils.js
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2020 Marco Trevisan <marco trevisan canonical com>
+
+const {GLib, GObject, GIMarshallingTests, Regress} = imports.gi;
+
+export const SIGNED_TYPES = ['schar', 'int', 'int64', 'long'];
+export const UNSIGNED_TYPES = ['char', 'uchar', 'uint', 'uint64', 'ulong'];
+export const FLOATING_TYPES = ['double', 'float'];
+export const NUMERIC_TYPES = [...SIGNED_TYPES, ...UNSIGNED_TYPES, ...FLOATING_TYPES];
+export const SPECIFIC_TYPES = ['gtype', 'boolean', 'string', 'param', 'variant', 'boxed', 'gvalue'];
+export const INSTANCED_TYPES = ['object', 'instance'];
+
+export const ALL_TYPES = [...NUMERIC_TYPES, ...SPECIFIC_TYPES, ...INSTANCED_TYPES];
+
+export function getDefaultContentByType(type, createNestedObject = true) {
+ if (SIGNED_TYPES.includes(type))
+ return -((Math.random() * 100 | 0) + 1);
+ if (UNSIGNED_TYPES.includes(type))
+ return -getDefaultContentByType('int') + 2;
+ if (FLOATING_TYPES.includes(type))
+ return getDefaultContentByType('uint') + 0.5;
+ if (type === 'string')
+ return `Hello GValue! ${getDefaultContentByType('uint')}`;
+ if (type === 'boolean')
+ return !!(getDefaultContentByType('int') % 2);
+ if (type === 'gtype')
+ return getGType(ALL_TYPES[Math.random() * ALL_TYPES.length | 0]);
+
+ if (type === 'boxed' || type === 'boxed-struct') {
+ return new GIMarshallingTests.BoxedStruct({
+ long_: getDefaultContentByType('long'),
+ // string_: getDefaultContentByType('string'), not supported
+ });
+ }
+ if (type === 'object') {
+ const props = ALL_TYPES.filter(e =>
+ (e !== 'object' || createNestedObject) &&
+ e !== 'boxed' &&
+ e !== 'gtype' &&
+ e !== 'instance' &&
+ e !== 'param' &&
+ // Include string when gobject-introspection!268 is merged
+ e !== 'string' &&
+ e !== 'schar').concat([
+ 'boxed-struct',
+ ]).reduce((ac, a) => ({
+ ...ac, [`some-${a}`]: getDefaultContentByType(a, false),
+ }), {});
+
+ return new GIMarshallingTests.PropertiesObject(props);
+ }
+ if (type === 'param') {
+ return GObject.ParamSpec.string('test-param', '', getDefaultContentByType('string'),
+ GObject.ParamFlags.READABLE, '');
+ }
+ if (type === 'variant') {
+ return new GLib.Variant('a{sv}', {
+ pasta: new GLib.Variant('s', 'Carbonara (con guanciale)'),
+ pizza: new GLib.Variant('s', 'Verace'),
+ randomString: new GLib.Variant('s', getDefaultContentByType('string')),
+ });
+ }
+ if (type === 'gvalue') {
+ const value = new GObject.Value();
+ const valueType = NUMERIC_TYPES[Math.random() * NUMERIC_TYPES.length | 0];
+ value.init(getGType(valueType));
+ setContent(value, valueType, getDefaultContentByType(valueType));
+ return value;
+ }
+ if (type === 'instance')
+ return new Regress.TestFundamentalSubObject(getDefaultContentByType('string'));
+
+
+ throw new Error(`No default content set for type ${type}`);
+}
+
+export function getGType(type) {
+ if (type === 'schar')
+ return GObject.TYPE_CHAR;
+
+ if (type === 'boxed' || type === 'gvalue' || type === 'instance')
+ return getDefaultContentByType(type).constructor.$gtype;
+
+ return GObject[`TYPE_${type.toUpperCase()}`];
+}
+
+export function getContent(gvalue, type) {
+ if (type === 'gvalue')
+ type = 'boxed';
+
+ if (type === 'instance') {
+ pending('https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/268');
+ return GIMarshallingTests.gvalue_round_trip(gvalue);
+ }
+
+ return gvalue[`get_${type}`]();
+}
+
+export function setContent(gvalue, type, content) {
+ if (type === 'gvalue')
+ type = 'boxed';
+
+ if (type === 'instance')
+ pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/402');
+
+ return gvalue[`set_${type}`](content);
+}
+
+export function skipUnsupported(type) {
+ if (type === 'boxed')
+ pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/402');
+
+ if (type === 'gvalue')
+ pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/272');
+}
diff --git a/installed-tests/js/meson.build b/installed-tests/js/meson.build
index 2f007351..63990caa 100644
--- a/installed-tests/js/meson.build
+++ b/installed-tests/js/meson.build
@@ -124,7 +124,6 @@ jasmine_tests = [
'GObject',
'GObjectClass',
'GObjectInterface',
- 'GObjectValue',
'GTypeClass',
'Importer',
'Introspection',
@@ -236,6 +235,8 @@ modules_tests = [
'ESModules',
'Encoding',
'GLibLogWriter',
+ 'GObjectClosure',
+ 'GObjectValue',
]
if build_cairo
modules_tests += 'CairoModule'
diff --git a/installed-tests/js/testGIMarshalling.js b/installed-tests/js/testGIMarshalling.js
index 7d524919..06ee19e5 100644
--- a/installed-tests/js/testGIMarshalling.js
+++ b/installed-tests/js/testGIMarshalling.js
@@ -79,9 +79,6 @@ function testTransferMarshalling(root, value, inoutValue, options = {}) {
in: {
omit: true, // this case is not in the test suite
},
- inout: {
- skip: 'https://gitlab.gnome.org/GNOME/gobject-introspection/issues/192',
- },
};
Object.assign(fullOptions, options.full);
testSimpleMarshalling(`${root}_full`, value, inoutValue, fullOptions);
@@ -896,12 +893,11 @@ describe('Callback', function () {
describe('GClosure', function () {
testInParameter('gclosure', () => 42);
- xit('marshals a GClosure as a return value', function () {
- // Currently a GObject.Closure instance is returned, upon which it's
- // not possible to call invoke() because that method takes a bare
- // pointer as an argument.
- expect(GIMarshallingTests.gclosure_return()()).toEqual(42);
- }).pend('https://gitlab.gnome.org/GNOME/gjs/issues/80');
+ it('marshals a GClosure as a return value', function () {
+ const closure = GIMarshallingTests.gclosure_return();
+ expect(closure instanceof GObject.Closure).toBeTruthy();
+ expect(closure.invoke(GObject.TYPE_INT, [])).toEqual(42);
+ });
});
it('marshals a return value', function () {
diff --git a/installed-tests/js/testGObject.js b/installed-tests/js/testGObject.js
index d6aafbbc..b41e6a26 100644
--- a/installed-tests/js/testGObject.js
+++ b/installed-tests/js/testGObject.js
@@ -62,7 +62,7 @@ describe('GObject overrides', function () {
});
describe('GObject should', function () {
- const types = ['gpointer', 'GBoxed', 'GParam', 'GInterface', 'GObject', 'GVariant'];
+ const types = ['gpointer', 'GBoxed', 'GParam', 'GInterface', 'GObject', 'GVariant', 'GClosure'];
types.forEach(type => {
it(`be able to create a GType object for ${type}`, function () {
diff --git a/installed-tests/js/testGObjectClosure.js b/installed-tests/js/testGObjectClosure.js
new file mode 100644
index 00000000..4cd3e6c2
--- /dev/null
+++ b/installed-tests/js/testGObjectClosure.js
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2022 Evan Welsh <contact evanwelsh com>
+
+import GObject from 'gi://GObject';
+
+import {
+ ALL_TYPES,
+ getDefaultContentByType,
+ getGType,
+ skipUnsupported
+} from './dataUtils.js';
+
+describe('GObject closure (GClosure)', function () {
+ let spyFn;
+ let closure;
+ beforeEach(function () {
+ spyFn = jasmine.createSpy();
+ closure = new GObject.Closure(spyFn);
+ });
+
+ it('is an instanceof GObject.Closure', function () {
+ expect(closure instanceof GObject.Closure).toBeTruthy();
+ });
+
+ ALL_TYPES.forEach(type => {
+ const gtype = getGType(type);
+
+ it(`can return ${type}`, function () {
+ let randomContent = getDefaultContentByType(type);
+
+ skipUnsupported(type);
+ spyFn.and.returnValue(randomContent);
+ expect(closure.invoke(gtype, [])).toEqual(randomContent);
+ });
+ });
+
+ it('can be invalidated', function () {
+ spyFn.and.returnValue(13);
+ expect(closure.invoke(GObject.TYPE_INT, [])).toBe(13);
+ closure.invalidate();
+ expect(closure.invoke(null, [])).toBe(null);
+ });
+
+ it('can be called with parameters', function () {
+ const plusClosure = new GObject.Closure((a, b) => {
+ return a + b;
+ });
+
+ expect(plusClosure.invoke(GObject.TYPE_INT, [5, 6])).toBe(11);
+ expect(plusClosure.invoke(GObject.TYPE_STRING, ['hello', ', world'])).toBe('hello, world');
+ });
+
+ afterEach(function () {
+ closure = null;
+ });
+});
diff --git a/installed-tests/js/testGObjectValue.js b/installed-tests/js/testGObjectValue.js
index 461c1ba2..ab7484ef 100644
--- a/installed-tests/js/testGObjectValue.js
+++ b/installed-tests/js/testGObjectValue.js
@@ -1,15 +1,18 @@
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2020 Marco Trevisan <marco trevisan canonical com>
-const {GLib, GObject, GIMarshallingTests, Regress} = imports.gi;
-
-const SIGNED_TYPES = ['schar', 'int', 'int64', 'long'];
-const UNSIGNED_TYPES = ['char', 'uchar', 'uint', 'uint64', 'ulong'];
-const FLOATING_TYPES = ['double', 'float'];
-const NUMERIC_TYPES = [...SIGNED_TYPES, ...UNSIGNED_TYPES, ...FLOATING_TYPES];
-const SPECIFIC_TYPES = ['gtype', 'boolean', 'string', 'param', 'variant', 'boxed', 'gvalue'];
-const INSTANCED_TYPES = ['object', 'instance'];
-const ALL_TYPES = [...NUMERIC_TYPES, ...SPECIFIC_TYPES, ...INSTANCED_TYPES];
+import {
+ ALL_TYPES,
+ FLOATING_TYPES,
+ getContent,
+ getDefaultContentByType,
+ getGType,
+ INSTANCED_TYPES,
+ setContent,
+ skipUnsupported
+} from './dataUtils.js';
+
+const {GObject, GIMarshallingTests} = imports.gi;
describe('GObject value (GValue)', function () {
let v;
@@ -17,110 +20,6 @@ describe('GObject value (GValue)', function () {
v = new GObject.Value();
});
- function getDefaultContentByType(type) {
- if (SIGNED_TYPES.includes(type))
- return -((Math.random() * 100 | 0) + 1);
- if (UNSIGNED_TYPES.includes(type))
- return -getDefaultContentByType('int') + 2;
- if (FLOATING_TYPES.includes(type))
- return getDefaultContentByType('uint') + 0.5;
- if (type === 'string')
- return `Hello GValue! ${getDefaultContentByType('uint')}`;
- if (type === 'boolean')
- return !!(getDefaultContentByType('int') % 2);
- if (type === 'gtype')
- return getGType(ALL_TYPES[Math.random() * ALL_TYPES.length | 0]);
-
- if (type === 'boxed' || type === 'boxed-struct') {
- return new GIMarshallingTests.BoxedStruct({
- long_: getDefaultContentByType('long'),
- // string_: getDefaultContentByType('string'), not supported
- });
- }
- if (type === 'object') {
- const wasCreatingObject = this.creatingObject;
- this.creatingObject = true;
- const props = ALL_TYPES.filter(e =>
- (e !== 'object' || !wasCreatingObject) &&
- e !== 'boxed' &&
- e !== 'gtype' &&
- e !== 'instance' &&
- e !== 'param' &&
- // Include string when gobject-introspection!268 is merged
- e !== 'string' &&
- e !== 'schar').concat([
- 'boxed-struct',
- ]).reduce((ac, a) => ({
- ...ac, [`some-${a}`]: getDefaultContentByType(a),
- }), {});
- delete this.creatingObject;
- return new GIMarshallingTests.PropertiesObject(props);
- }
- if (type === 'param') {
- return GObject.ParamSpec.string('test-param', '', getDefaultContentByType('string'),
- GObject.ParamFlags.READABLE, '');
- }
- if (type === 'variant') {
- return new GLib.Variant('a{sv}', {
- pasta: new GLib.Variant('s', 'Carbonara (con guanciale)'),
- pizza: new GLib.Variant('s', 'Verace'),
- randomString: new GLib.Variant('s', getDefaultContentByType('string')),
- });
- }
- if (type === 'gvalue') {
- const value = new GObject.Value();
- const valueType = NUMERIC_TYPES[Math.random() * NUMERIC_TYPES.length | 0];
- value.init(getGType(valueType));
- setContent(value, valueType, getDefaultContentByType(valueType));
- return value;
- }
- if (type === 'instance')
- return new Regress.TestFundamentalSubObject(getDefaultContentByType('string'));
-
-
- throw new Error(`No default content set for type ${type}`);
- }
-
- function getGType(type) {
- if (type === 'schar')
- return GObject.TYPE_CHAR;
-
- if (type === 'boxed' || type === 'gvalue' || type === 'instance')
- return getDefaultContentByType(type).constructor.$gtype;
-
- return GObject[`TYPE_${type.toUpperCase()}`];
- }
-
- function getContent(gvalue, type) {
- if (type === 'gvalue')
- type = 'boxed';
-
- if (type === 'instance') {
- pending('https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/268');
- return GIMarshallingTests.gvalue_round_trip(gvalue);
- }
-
- return gvalue[`get_${type}`]();
- }
-
- function setContent(gvalue, type, content) {
- if (type === 'gvalue')
- type = 'boxed';
-
- if (type === 'instance')
- pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/402');
-
- return gvalue[`set_${type}`](content);
- }
-
- function skipUnsupported(type) {
- if (type === 'boxed')
- pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/402');
-
- if (type === 'gvalue')
- pending('https://gitlab.gnome.org/GNOME/gjs/-/issues/272');
- }
-
ALL_TYPES.forEach(type => {
const gtype = getGType(type);
it(`initializes ${type}`, function () {
diff --git a/modules/core/overrides/GObject.js b/modules/core/overrides/GObject.js
index 6bfaf144..d672921b 100644
--- a/modules/core/overrides/GObject.js
+++ b/modules/core/overrides/GObject.js
@@ -753,4 +753,36 @@ function _init() {
throw new Error('GObject.signal_handlers_disconnect_by_data() is not \
introspectable. Use GObject.signal_handlers_disconnect_by_func() instead.');
};
+
+ GObject.Closure._new_internal = function (callable) {
+ 'sensitive';
+
+ return Gi.create_closure(callable);
+ };
+
+ GObject.Closure.new_simple = function () {
+ throw new Error('GObject.Closure.new_simple() is not introspectable. \
+Use new GObject.Closure() instead.');
+ };
+
+ GObject.Closure.new_object = function () {
+ throw new Error('GObject.Closure.new_object() is not introspectable. \
+Use new GObject.Closure() instead.');
+ };
+
+ const invoke_closure = Gi.invoke_closure;
+
+ /**
+ * @param {GType | null} [return_type] the GType of the return value or null if the closure returns void
+ * @param {any[]} [parameters] a list of values to pass to the closure
+ * @returns {any}
+ */
+ GObject.Closure.prototype.invoke = function (return_type = null, parameters = []) {
+ 'hide source';
+
+ if (return_type === null)
+ return invoke_closure(this, null, parameters, return_type);
+
+ return invoke_closure(this, null, parameters, return_type);
+ };
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]