[gjs/esm/static-imports: 63/63] Add additional library bindings.
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/esm/static-imports: 63/63] Add additional library bindings.
- Date: Mon, 23 Nov 2020 21:04:44 +0000 (UTC)
commit 48fb503a0daf088b82e1aa8a6a47e54ba0b019c0
Author: Evan Welsh <noreply evanwelsh com>
Date: Sun Nov 8 13:03:04 2020 -0600
Add additional library bindings.
js.gresource.xml | 5 ++
lib/modules/esm.js | 9 ++-
lib/modules/gi.js | 4 +-
modules/core/_text.js | 13 ++++
modules/core/overrides/GLib.js | 5 ++
modules/esm/cairo.js | 7 +++
modules/esm/format.js | 5 ++
modules/esm/gettext.js | 16 +++++
modules/esm/gi.js | 66 ++++++++++++++++++++
modules/esm/signals.js | 118 +++++++++++++++++++++++++++++++++++
modules/esm/system.js | 2 +
modules/script/_bootstrap/default.js | 13 ++++
12 files changed, 256 insertions(+), 7 deletions(-)
---
diff --git a/js.gresource.xml b/js.gresource.xml
index c3a29d94..bbd0106d 100644
--- a/js.gresource.xml
+++ b/js.gresource.xml
@@ -9,8 +9,12 @@
<file>lib/entry.js</file>
<!-- ESM-based modules -->
+ <file>modules/esm/cairo.js</file>
<file>modules/esm/gi.js</file>
<file>modules/esm/system.js</file>
+ <file>modules/esm/format.js</file>
+ <file>modules/esm/signals.js</file>
+ <file>modules/esm/gettext.js</file>
<!-- Script-based Modules -->
<file>modules/script/_bootstrap/debugger.js</file>
@@ -44,5 +48,6 @@
<file>modules/core/_format.js</file>
<file>modules/core/_gettext.js</file>
<file>modules/core/_signals.js</file>
+ <file>modules/core/_text.js</file>
</gresource>
</gresources>
diff --git a/lib/modules/esm.js b/lib/modules/esm.js
index b2e4e3f6..7b7d2676 100644
--- a/lib/modules/esm.js
+++ b/lib/modules/esm.js
@@ -176,10 +176,9 @@ moduleLoader.registerScheme('gi', {
load(uri) {
const version = uri.query.version ?? getGIVersionMap(uri.host);
- if (!version)
- throw new ImportError(`No version specified for ${uri.host}.`);
-
- giVersionMap.set(uri.host, version);
+ if (version) {
+ giVersionMap.set(uri.host, version);
+ }
return [generateModule(uri.host, version), true];
},
@@ -209,7 +208,7 @@ setModuleLoadHook(moduleGlobalThis, (id, uri) => {
const registry = getRegistry(moduleGlobalThis);
- registry.set(uri, compiled);
+ registry.set(id, compiled);
return compiled;
});
diff --git a/lib/modules/gi.js b/lib/modules/gi.js
index fb0076dc..d38bd347 100644
--- a/lib/modules/gi.js
+++ b/lib/modules/gi.js
@@ -5,7 +5,7 @@
* Creates a module source text to expose a GI namespace via a default export.
*
* @param {string} namespace the GI namespace to import
- * @param {string} version the version string of the namespace
+ * @param {string} [version] the version string of the namespace
*
* @returns {string} the generated module source text
*/
@@ -13,7 +13,7 @@ export function generateModule(namespace, version) {
const source = `
import $$gi from 'gi';
- const $$ns = $$gi.require('${namespace}', '${version}');
+ const $$ns = $$gi.require${version ? `('${namespace}', '${version}')` : `('${namespace}')`};
export default $$ns;
`;
diff --git a/modules/core/_text.js b/modules/core/_text.js
new file mode 100644
index 00000000..60d081d3
--- /dev/null
+++ b/modules/core/_text.js
@@ -0,0 +1,13 @@
+const ByteArray = imports.byteArray;
+
+var TextDecoder = class TextDecoder {
+ decode(bytes) {
+ return ByteArray.toString(bytes);
+ }
+}
+
+var TextEncoder = class TextEncoder {
+ encode(string) {
+ return ByteArray.fromString(string);
+ }
+}
\ No newline at end of file
diff --git a/modules/core/overrides/GLib.js b/modules/core/overrides/GLib.js
index 20ae7e04..8d17f790 100644
--- a/modules/core/overrides/GLib.js
+++ b/modules/core/overrides/GLib.js
@@ -166,6 +166,11 @@ function _packVariant(signature, value) {
}
}
+/**
+ * @param {*} variant
+ * @param {boolean} deep
+ * @param {boolean} [recursive]
+ */
function _unpackVariant(variant, deep, recursive = false) {
switch (String.fromCharCode(variant.classify())) {
case 'b':
diff --git a/modules/esm/cairo.js b/modules/esm/cairo.js
new file mode 100644
index 00000000..642f2ccc
--- /dev/null
+++ b/modules/esm/cairo.js
@@ -0,0 +1,7 @@
+const cairo = import.meta.importSync('cairoNative');
+
+export default Object.assign(
+ {},
+ imports._cairo,
+ cairo
+);
\ No newline at end of file
diff --git a/modules/esm/format.js b/modules/esm/format.js
new file mode 100644
index 00000000..1d5dc4c9
--- /dev/null
+++ b/modules/esm/format.js
@@ -0,0 +1,5 @@
+export const vprintf = imports._format.vprintf;
+
+export function format(...args) {
+ return vprintf(this, args);
+}
\ No newline at end of file
diff --git a/modules/esm/gettext.js b/modules/esm/gettext.js
new file mode 100644
index 00000000..29392d05
--- /dev/null
+++ b/modules/esm/gettext.js
@@ -0,0 +1,16 @@
+export let setlocale = imports._gettext.setlocale;
+
+export let textdomain = imports._gettext.textdomain;
+export let bindtextdomain = imports._gettext.bindtextdomain;
+
+export let gettext = imports._gettext.gettext;
+export let dgettext = imports._gettext.dgettext;
+export let dcgettext = imports._gettext.dcgettext;
+
+export let ngettext = imports._gettext.ngettext;
+export let dngettext= imports._gettext.dngettext;
+
+export let pgettext = imports._gettext.pgettext;
+export let dpgettext = imports._gettext.dpgettext;
+
+export let domain = imports._gettext.domain;
diff --git a/modules/esm/gi.js b/modules/esm/gi.js
index f9cbc374..8ee1ef14 100644
--- a/modules/esm/gi.js
+++ b/modules/esm/gi.js
@@ -1,5 +1,7 @@
const gi = import.meta.importSync('gi');
+import System from 'system';
+
const Gi = {
require(name, version = null) {
if (version !== null)
@@ -11,6 +13,70 @@ const Gi = {
return gi[name];
},
+ requireSymbol(lib, ver, symbol) {
+ if (!checkSymbol(lib, ver, symbol)) {
+ if (symbol)
+ printerr(`Unsatisfied dependency: No ${symbol} in ${lib}`);
+ else
+ printerr(`Unsatisfied dependency: ${lib}`);
+ System.exit(1);
+ }
+ },
+
+ /**
+ * Check whether an external GI typelib can be imported
+ * and provides @symbol.
+ *
+ * Symbols may refer to
+ * - global functions ('main_quit')
+ * - classes ('Window')
+ * - class / instance methods ('IconTheme.get_default' / 'IconTheme.has_icon')
+ * - GObject properties ('Window.default_height')
+ *
+ * @param {string} lib an external dependency to import
+ * @param {string} [ver] version of the dependency
+ * @param {string} [symbol] symbol to check for
+ * @returns {boolean} true if `lib` can be imported and provides `symbol`, false
+ * otherwise
+ */
+ checkSymbol(lib, ver, symbol) {
+ let Lib = null;
+
+ if (ver)
+ gi.versions[lib] = ver;
+
+ try {
+ Lib = gi[lib];
+ } catch (e) {
+ return false;
+ }
+
+ if (!symbol)
+ return true; // Done
+
+ let [klass, sym] = symbol.split('.');
+ if (klass === symbol) // global symbol
+ return typeof Lib[symbol] !== 'undefined';
+
+ let obj = Lib[klass];
+ if (typeof obj === 'undefined')
+ return false;
+
+ if (typeof obj[sym] !== 'undefined' ||
+ obj.prototype && typeof obj.prototype[sym] !== 'undefined')
+ return true; // class- or object method
+
+ // GObject property
+ let pspec = null;
+ if (GObject.type_is_a(obj.$gtype, GObject.TYPE_INTERFACE)) {
+ let iface = GObject.type_default_interface_ref(obj.$gtype);
+ pspec = GObject.Object.interface_find_property(iface, sym);
+ } else if (GObject.type_is_a(obj.$gtype, GObject.TYPE_OBJECT)) {
+ pspec = GObject.Object.find_property.call(obj.$gtype, sym);
+ }
+
+ return pspec !== null;
+ }
};
Object.freeze(Gi);
diff --git a/modules/esm/signals.js b/modules/esm/signals.js
new file mode 100644
index 00000000..d997eda7
--- /dev/null
+++ b/modules/esm/signals.js
@@ -0,0 +1,118 @@
+
+export class EventEmitter {
+ constructor() {
+ this._events = new Map();
+ this._nextConnectionId = 1n;
+ }
+
+ _connectListener(eventName, listener) {
+ const listeners = this._events.get(eventName);
+ if (listeners) {
+ listeners.push(listener);
+ } else {
+ this._events.set(eventName, [listener]);
+ }
+ }
+
+ connect(name, callback) {
+ // be paranoid about callback arg since we'd start to throw from emit()
+ // if it was messed up
+ if (typeof callback !== 'function')
+ throw new Error('When connecting signal must give a callback that is a function');
+
+ // we instantiate the "signal machinery" only on-demand if anything
+ // gets connected.
+
+ let id = this._nextConnectionId;
+ this._nextConnectionId += 1n;
+
+ // this makes it O(n) in total connections to emit, but I think
+ // it's right to optimize for low memory and reentrancy-safety
+ // rather than speed
+
+ this._connectListener(name, {
+ id,
+ name,
+ callback,
+ 'disconnected': false,
+ });
+
+ return id;
+ }
+
+ disconnect(id) {
+ this._events.forEach(_events => {
+ for (let i = 0; i < _events.length; ++i) {
+ let connection = _events[i];
+ if (connection.id === id) {
+ if (connection.disconnected)
+ throw new Error(`Signal handler id ${id} already disconnected`);
+
+ // set a flag to deal with removal during emission
+ connection.disconnected = true;
+ _events.splice(i, 1);
+
+ return;
+ }
+ }
+ });
+ }
+
+ signalHandlerIsConnected(id) {
+ this._events.forEach(_events => {
+ for (let i = 0; i < _events.length; ++i) {
+ const connection = this._events[i];
+ if (connection.id === id)
+ return !connection.disconnected;
+ }
+ });
+
+ return false;
+ }
+
+ disconnectAll() {
+ this._events.forEach(_events => {
+ while (_events.length > 0)
+ this.disconnect.call(this, _events[0].id);
+ });
+ }
+
+ emit(name, ...args) {
+ // may not be any signal handlers at all, if not then return
+ if (!('_events' in this))
+ return;
+
+ // To deal with re-entrancy (removal/addition while
+ // emitting), we copy out a list of what was connected
+ // at emission start; and just before invoking each
+ // handler we check its disconnected flag.
+ let handlers = [...(this._events.get(name) || [])];
+
+ // create arg array which is emitter + everything passed in except
+ // signal name. Would be more convenient not to pass emitter to
+ // the callback, but trying to be 100% consistent with GObject
+ // which does pass it in. Also if we pass in the emitter here,
+ // people don't create closures with the emitter in them,
+ // which would be a cycle.
+ let argArray = [this, ...args];
+
+ for (let i = 0; i < handlers.length; ++i) {
+ let connection = handlers[i];
+ if (!connection.disconnected) {
+ try {
+ // since we pass "null" for this, the global object will be used.
+ let ret = connection.callback.apply(null, argArray);
+
+ // if the callback returns true, we don't call the next
+ // signal handlers
+ if (ret === true)
+ break;
+ } catch (e) {
+ // just log any exceptions so that callbacks can't disrupt
+ // signal emission
+ logError(e, `Exception in callback for signal: ${name}`);
+ }
+ }
+ }
+ }
+}
diff --git a/modules/esm/system.js b/modules/esm/system.js
index cecaa4d7..52c47f8e 100644
--- a/modules/esm/system.js
+++ b/modules/esm/system.js
@@ -15,3 +15,5 @@ export let exit = system.exit;
export let version = system.version;
export let programInvocationName = system.programInvocationName;
+
+export let clearDateCaches = system.clearDateCaches;
\ No newline at end of file
diff --git a/modules/script/_bootstrap/default.js b/modules/script/_bootstrap/default.js
index eb042d49..e0c1090c 100644
--- a/modules/script/_bootstrap/default.js
+++ b/modules/script/_bootstrap/default.js
@@ -4,8 +4,21 @@
'use strict';
const {print, printerr, log, logError} = imports._print;
+ const {TextDecoder, TextEncoder} = imports._text;
Object.defineProperties(exports, {
+ TextDecoder: {
+ configurable: false,
+ enumerable: true,
+ writable: false,
+ value: TextDecoder,
+ },
+ TextEncoder: {
+ configurable: false,
+ enumerable: true,
+ writable: false,
+ value: TextEncoder,
+ },
print: {
configurable: false,
enumerable: true,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]