[gjs/ewlsh/enumerable-interfaces: 9/9] gi: Add enumeration hook for Interface prototypes
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/ewlsh/enumerable-interfaces: 9/9] gi: Add enumeration hook for Interface prototypes
- Date: Sun, 9 Jan 2022 20:50:31 +0000 (UTC)
commit f593ea0d7494ba560186bd85b46aa230ea2ef6d3
Author: Evan Welsh <contact evanwelsh com>
Date: Sun Jan 9 11:41:20 2022 -0800
gi: Add enumeration hook for Interface prototypes
This commit allows utilities in JavaScript to enumerate the methods
available on a given interface.
gi/interface.cpp | 58 +++++++++++++++++++++++++++++-
gi/interface.h | 5 +++
installed-tests/js/testGObjectInterface.js | 32 ++++++++++++++++-
3 files changed, 93 insertions(+), 2 deletions(-)
---
diff --git a/gi/interface.cpp b/gi/interface.cpp
index 925097e8..15ebdec4 100644
--- a/gi/interface.cpp
+++ b/gi/interface.cpp
@@ -8,8 +8,12 @@
#include <girepository.h>
#include <js/Class.h>
+#include <js/Id.h> // for JSID_VOID, PropertyKey, jsid
#include <js/TypeDecls.h>
#include <js/Utility.h> // for UniqueChars
+#include <jsapi.h> // for JS_ReportOutOfMemory
+
+#include <utility> // for forward
#include "gi/function.h"
#include "gi/interface.h"
@@ -31,6 +35,58 @@ InterfacePrototype::~InterfacePrototype(void) {
GJS_DEC_COUNTER(interface);
}
+static bool append_inferface_properties(JSContext* cx,
+ JS::MutableHandleIdVector properties,
+ GIInterfaceInfo* iface_info) {
+ int n_methods = g_interface_info_get_n_methods(iface_info);
+ if (!properties.reserve(properties.length() + n_methods)) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+
+ for (int i = 0; i < n_methods; i++) {
+ GjsAutoFunctionInfo meth_info =
+ g_interface_info_get_method(iface_info, i);
+ GIFunctionInfoFlags flags = g_function_info_get_flags(meth_info);
+
+ if (flags & GI_FUNCTION_IS_METHOD) {
+ const char* name = meth_info.name();
+ jsid id = gjs_intern_string_to_id(cx, name);
+ if (id == JSID_VOID)
+ return false;
+ properties.infallibleAppend(id);
+ }
+ }
+
+ return true;
+}
+
+bool InterfacePrototype::new_enumerate_impl(
+ JSContext* cx, JS::HandleObject obj [[maybe_unused]],
+ JS::MutableHandleIdVector properties,
+ bool only_enumerable [[maybe_unused]]) {
+ unsigned n_interfaces;
+ GType* interfaces = g_type_interfaces(gtype(), &n_interfaces);
+
+ for (unsigned k = 0; k < n_interfaces; k++) {
+ GjsAutoInterfaceInfo iface_info =
+ g_irepository_find_by_gtype(nullptr, interfaces[k]);
+
+ if (!iface_info)
+ continue;
+
+ if (!append_inferface_properties(cx, properties, iface_info))
+ return false;
+ }
+
+ g_free(interfaces);
+
+ if (!info())
+ return true;
+
+ return append_inferface_properties(cx, properties, info());
+}
+
// See GIWrapperBase::resolve().
bool InterfacePrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
JS::HandleId id, bool* resolved) {
@@ -111,7 +167,7 @@ const struct JSClassOps InterfaceBase::class_ops = {
nullptr, // addProperty
nullptr, // deleteProperty
nullptr, // enumerate
- nullptr, // newEnumerate
+ &InterfaceBase::new_enumerate,
&InterfaceBase::resolve,
nullptr, // mayResolve
&InterfaceBase::finalize,
diff --git a/gi/interface.h b/gi/interface.h
index 9cc2853b..a6d14425 100644
--- a/gi/interface.h
+++ b/gi/interface.h
@@ -92,6 +92,11 @@ class InterfacePrototype
bool resolve_impl(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
bool* resolved);
+ GJS_JSAPI_RETURN_CONVENTION
+ bool new_enumerate_impl(JSContext* cx, JS::HandleObject obj,
+ JS::MutableHandleIdVector properties,
+ bool only_enumerable);
+
// JS methods
GJS_JSAPI_RETURN_CONVENTION
diff --git a/installed-tests/js/testGObjectInterface.js b/installed-tests/js/testGObjectInterface.js
index cbeaa4f9..148b6fdd 100644
--- a/installed-tests/js/testGObjectInterface.js
+++ b/installed-tests/js/testGObjectInterface.js
@@ -148,7 +148,7 @@ describe('GObject interface', function () {
},
}, class BadObject extends GObject.Object {});
expect(() => new BadObject().requiredG())
- .toThrowError(GObject.NotImplementedError);
+ .toThrowError(GObject.NotImplementedError);
});
it("doesn't have to have its optional function implemented", function () {
@@ -311,6 +311,36 @@ describe('GObject interface', function () {
originalDup = Gio.File.prototype.dup;
});
+ it('toString is enumerable and defined', function () {
+ expect(Object.getOwnPropertyNames(Gio.File.prototype)).toContain('toString');
+ expect(Gio.File.prototype.toString).toBeDefined();
+ });
+
+ it('method properties are enumerated', function () {
+ const expectedMethods = [
+ 'copy_attributes',
+ 'copy_async',
+ 'create_async',
+ 'create_readwrite_async',
+ 'delete_async',
+ 'enumerate_children',
+ ];
+
+ const methods = Object.getOwnPropertyNames(Gio.File.prototype);
+
+ for (const method of expectedMethods)
+ expect(methods).toContain(method);
+ });
+
+ it('method properties are defined', function () {
+ const methods = Object.getOwnPropertyNames(Gio.File.prototype);
+
+ for (const method of methods) {
+ expect(Gio.File.prototype[method]).toBeDefined();
+ expect(Gio.File.prototype[method]).toBeInstanceOf(Function);
+ }
+ });
+
it('overrides are inherited by implementing classes', function () {
spyOn(Gio.File.prototype, 'dup');
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]