[gjs/esm/static-imports] Add additional library bindings.



commit 7205046c0df32885594eac674f5d4bb99b7783f8
Author: Evan Welsh <noreply evanwelsh com>
Date:   Sun Nov 8 13:03:04 2020 -0600

    Add additional library bindings.

 js.gresource.xml               |   3 +
 lib/modules/esm.js             |   9 ++-
 lib/modules/gi.js              |   4 +-
 modules/core/overrides/GLib.js |   5 ++
 modules/esm/format.js          |   2 +
 modules/esm/gettext.js         |  16 +++++
 modules/esm/signals.js         | 153 +++++++++++++++++++++++++++++++++++++++++
 modules/esm/system.js          |   2 +
 8 files changed, 187 insertions(+), 7 deletions(-)
---
diff --git a/js.gresource.xml b/js.gresource.xml
index c3a29d94..410f17ea 100644
--- a/js.gresource.xml
+++ b/js.gresource.xml
@@ -11,6 +11,9 @@
     <!-- ESM-based modules -->
     <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>
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..b44d9f65 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/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/format.js b/modules/esm/format.js
new file mode 100644
index 00000000..8f4c47eb
--- /dev/null
+++ b/modules/esm/format.js
@@ -0,0 +1,2 @@
+export const format = imports._format.format;
+export const vprintf = imports._format.vprintf;
\ 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/signals.js b/modules/esm/signals.js
new file mode 100644
index 00000000..16946132
--- /dev/null
+++ b/modules/esm/signals.js
@@ -0,0 +1,153 @@
+const _signals = imports._signals;
+
+function _addSignalMethod(proto, functionName, func) {
+    if (proto[functionName] && proto[functionName] !== func)
+        log(`WARNING: addSignalMethods is replacing existing ${proto} ${functionName} method`);
+
+    proto[functionName] = func;
+}
+
+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.
+        if (!('_events' in this)) {
+            this._events = [];
+            this._nextConnectionId = 1n;
+        }
+
+        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._events.push({
+            id,
+            name,
+            callback,
+            'disconnected': false,
+        });
+
+        this._connectListener(name, callback);
+
+        return id;
+    }
+
+    disconnect(id) {
+        if ('_events' in this) {
+            let i;
+            let length = this._events.length;
+            for (i = 0; i < length; ++i) {
+                let connection = this._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;
+                    this._events.splice(i, 1);
+
+                    return;
+                }
+            }
+        }
+        throw new Error(`No signal connection ${id} found`);
+    }
+
+    signalHandlerIsConnected(id) {
+        if (!('_events' in this))
+            return false;
+
+        const { length } = this._events;
+        for (let i = 0; i < length; ++i) {
+            const connection = this._events[i];
+            if (connection.id === id)
+                return !connection.disconnected;
+        }
+
+        return false;
+    }
+
+    disconnectAll() {
+        if ('_events' in this) {
+            while (this._events.length > 0)
+                _disconnect.call(this, 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 = [];
+        let i;
+        let length = this._events.length;
+        for (i = 0; i < length; ++i) {
+            let connection = this._events[i];
+            if (connection.name === name)
+                handlers.push(connection);
+        }
+
+        // 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];
+
+        length = handlers.length;
+        for (i = 0; i < 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}`);
+                }
+            }
+        }
+    }
+}
+
+export function addSignalMethods(proto) {
+    _addSignalMethod(proto, 'connect', _connect);
+    _addSignalMethod(proto, 'disconnect', _disconnect);
+    _addSignalMethod(proto, 'emit', _emit);
+    _addSignalMethod(proto, 'signalHandlerIsConnected', _signalHandlerIsConnected);
+    // this one is not in GObject, but useful
+    _addSignalMethod(proto, 'disconnectAll', _disconnectAll);
+}
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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]