[gjs/ewlsh/enumerable-interfaces] 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] gi: Add enumeration hook for Interface prototypes
- Date: Sun, 9 Jan 2022 06:27:06 +0000 (UTC)
commit 8f82b39344475bba9c15eeb495fb46fdb4aa2ebd
Author: Evan Welsh <contact evanwelsh com>
Date: Sat Jan 8 22:26:45 2022 -0800
gi: Add enumeration hook for Interface prototypes
gi/interface.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++-
gi/interface.h | 5 +++
modules/script/_legacy.js | 104 +++++++++++++++++++++++-----------------------
3 files changed, 155 insertions(+), 54 deletions(-)
---
diff --git a/gi/interface.cpp b/gi/interface.cpp
index 925097e8..c66a6199 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,100 @@ InterfacePrototype::~InterfacePrototype(void) {
GJS_DEC_COUNTER(interface);
}
+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;
+ }
+
+ int n_methods = g_interface_info_get_n_methods(iface_info);
+ int n_properties = g_interface_info_get_n_properties(iface_info);
+ if (!properties.reserve(properties.length() + n_methods +
+ n_properties)) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+
+ // Methods
+ 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);
+ }
+ }
+
+ // Properties
+ for (int i = 0; i < n_properties; i++) {
+ GjsAutoPropertyInfo prop_info =
+ g_interface_info_get_property(iface_info, i);
+
+ GjsAutoChar js_name = gjs_hyphen_to_underscore(prop_info.name());
+
+ jsid id = gjs_intern_string_to_id(cx, js_name);
+ if (id == JSID_VOID)
+ return false;
+ properties.infallibleAppend(id);
+ }
+ }
+
+ g_free(interfaces);
+
+ if (info()) {
+ int n_methods = g_interface_info_get_n_methods(info());
+ int n_properties = g_interface_info_get_n_properties(info());
+ if (!properties.reserve(properties.length() + n_methods +
+ n_properties)) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+
+ // Methods
+ for (int i = 0; i < n_methods; i++) {
+ GjsAutoFunctionInfo meth_info =
+ g_interface_info_get_method(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);
+ }
+ }
+
+ // Properties
+ for (int i = 0; i < n_properties; i++) {
+ GjsAutoPropertyInfo prop_info =
+ g_interface_info_get_property(info(), i);
+
+ GjsAutoChar js_name = gjs_hyphen_to_underscore(prop_info.name());
+ jsid id = gjs_intern_string_to_id(cx, js_name);
+ if (id == JSID_VOID)
+ return false;
+ properties.infallibleAppend(id);
+ }
+ }
+
+ return true;
+}
+
// See GIWrapperBase::resolve().
bool InterfacePrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
JS::HandleId id, bool* resolved) {
@@ -111,7 +209,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 490b7093..9630b478 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/modules/script/_legacy.js b/modules/script/_legacy.js
index 135d2517..3353ef27 100644
--- a/modules/script/_legacy.js
+++ b/modules/script/_legacy.js
@@ -192,27 +192,29 @@ Class.prototype._copyPropertyDescriptor = function (params, propertyObj, key) {
Class.prototype._init = function (params) {
let className = params.Name;
- let propertyObj = { };
+ let propertyObj = {};
let interfaces = params.Implements || [];
interfaces.forEach(iface => {
Object.getOwnPropertyNames(iface.prototype)
- .filter(name => !name.startsWith('__') && name !== 'constructor')
- .filter(name => !(name in this.prototype))
- .forEach(name => {
- let descriptor = Object.getOwnPropertyDescriptor(iface.prototype,
- name);
- // writable and enumerable are inherited, see note above
- descriptor.configurable = false;
- propertyObj[name] = descriptor;
- });
+ .filter(name => !name.startsWith('__') && name !== 'constructor')
+ .filter(name => !(name in this.prototype))
+ .forEach(name => {
+ let descriptor = Object.getOwnPropertyDescriptor(iface.prototype,
+ name);
+ if (descriptor) {
+ // writable and enumerable are inherited, see note above
+ descriptor.configurable = false;
+ propertyObj[name] = descriptor;
+ }
+ });
});
Object.getOwnPropertyNames(params)
- .filter(name =>
- ['Name', 'Extends', 'Abstract', 'Implements'].indexOf(name) === -1)
- .concat(Object.getOwnPropertySymbols(params))
- .forEach(this._copyPropertyDescriptor.bind(this, params, propertyObj));
+ .filter(name =>
+ ['Name', 'Extends', 'Abstract', 'Implements'].indexOf(name) === -1)
+ .concat(Object.getOwnPropertySymbols(params))
+ .forEach(this._copyPropertyDescriptor.bind(this, params, propertyObj));
Object.defineProperties(this.prototype, propertyObj);
Object.defineProperties(this.prototype, {
@@ -251,18 +253,18 @@ function _getMetaInterface(params) {
}
return null;
})
- .reduce((best, candidate) => {
- // This function reduces to the "most derived" meta interface in the list.
- if (best === null)
- return candidate;
- if (candidate === null)
- return best;
- for (let sup = candidate; sup; sup = sup.__super__) {
- if (sup === best)
+ .reduce((best, candidate) => {
+ // This function reduces to the "most derived" meta interface in the list.
+ if (best === null)
return candidate;
- }
- return best;
- }, null);
+ if (candidate === null)
+ return best;
+ for (let sup = candidate; sup; sup = sup.__super__) {
+ if (sup === best)
+ return candidate;
+ }
+ return best;
+ }, null);
// If we reach this point and we don't know the meta-interface, then it's
// most likely because there were only pure-C interfaces listed in Requires
@@ -347,20 +349,16 @@ Interface.prototype._check = function (proto) {
// but is not preferred because it will be the C name. The last option
// is just so that we print something if there is garbage in Requires.
required.prototype.__name__ || required.name || required);
- if (unfulfilledReqs.length > 0) {
- throw new Error(`The following interfaces must be implemented before ${
- this.prototype.__name__}: ${unfulfilledReqs.join(', ')}`);
- }
+ if (unfulfilledReqs.length > 0)
+ throw new Error(`The following interfaces must be implemented before ${this.prototype.__name__}:
${unfulfilledReqs.join(', ')}`);
+
// Check that this interface's required methods are implemented
let unimplementedFns = Object.getOwnPropertyNames(this.prototype)
- .filter(p => this.prototype[p] === Interface.UNIMPLEMENTED)
- .filter(p => !(p in proto) || proto[p] === Interface.UNIMPLEMENTED);
- if (unimplementedFns.length > 0) {
- throw new Error(`The following members of ${
- this.prototype.__name__} are not implemented yet: ${
- unimplementedFns.join(', ')}`);
- }
+ .filter(p => this.prototype[p] === Interface.UNIMPLEMENTED)
+ .filter(p => !(p in proto) || proto[p] === Interface.UNIMPLEMENTED);
+ if (unimplementedFns.length > 0)
+ throw new Error(`The following members of ${this.prototype.__name__} are not implemented yet:
${unimplementedFns.join(', ')}`);
};
Interface.prototype.toString = function () {
@@ -372,25 +370,25 @@ Interface.prototype._init = function (params) {
let propertyObj = {};
Object.getOwnPropertyNames(params)
- .filter(name => ['Name', 'Requires'].indexOf(name) === -1)
- .forEach(name => {
- let descriptor = Object.getOwnPropertyDescriptor(params, name);
-
- // Create wrappers on the interface object so that generics work (e.g.
- // SomeInterface.some_function(this, blah) instead of
- // SomeInterface.prototype.some_function.call(this, blah)
- if (typeof descriptor.value === 'function') {
- let interfaceProto = this.prototype; // capture in closure
- this[name] = function (thisObj, ...args) {
- return interfaceProto[name].call(thisObj, ...args);
- };
- }
+ .filter(name => ['Name', 'Requires'].indexOf(name) === -1)
+ .forEach(name => {
+ let descriptor = Object.getOwnPropertyDescriptor(params, name);
+
+ // Create wrappers on the interface object so that generics work (e.g.
+ // SomeInterface.some_function(this, blah) instead of
+ // SomeInterface.prototype.some_function.call(this, blah)
+ if (typeof descriptor.value === 'function') {
+ let interfaceProto = this.prototype; // capture in closure
+ this[name] = function (thisObj, ...args) {
+ return interfaceProto[name].call(thisObj, ...args);
+ };
+ }
- // writable and enumerable are inherited, see note in Class._init()
- descriptor.configurable = false;
+ // writable and enumerable are inherited, see note in Class._init()
+ descriptor.configurable = false;
- propertyObj[name] = descriptor;
- });
+ propertyObj[name] = descriptor;
+ });
Object.defineProperties(this.prototype, propertyObj);
Object.defineProperties(this.prototype, {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]