[gjs/esm/dynamic-imports: 12/13] WIP - add dynamic import support to scripts




commit 40859bd2caa5704687c9c91ab3b078ab64ba9099
Author: Evan Welsh <contact evanwelsh com>
Date:   Fri Feb 5 21:51:41 2021 -0800

    WIP - add dynamic import support to scripts
    
    To allow import() from scripts we must set script privates
    with a object similar to the ModulePrivate class.

 gjs/context.cpp                     |  7 +++++++
 gjs/module.cpp                      | 25 +++++++++++++++++++++++--
 gjs/module.h                        |  3 +++
 installed-tests/js/testESModules.js | 19 +++++++++----------
 modules/internal/loader.js          |  4 ++++
 5 files changed, 46 insertions(+), 12 deletions(-)
---
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 1a85f6c6..626923dc 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -1248,6 +1248,13 @@ bool GjsContextPrivate::eval_with_scope(JS::HandleObject scope_object,
 
     JS::CompileOptions options(m_cx);
     options.setFileAndLine(filename, 1);
+    GjsAutoUnref<GFile> file = g_file_new_for_commandline_arg(filename);
+    GjsAutoChar uri = g_file_get_uri(file);
+    JS::RootedObject priv(m_cx, gjs_script_module_build_private(m_cx, uri));
+    // TODO: This could fail.
+    if (priv) {
+        options.setPrivateValue(JS::ObjectValue(*priv));
+    }
 
     if (!JS::Evaluate(m_cx, scope_chain, options, buf, retval))
         return false;
diff --git a/gjs/module.cpp b/gjs/module.cpp
index 2d4a7e06..7362a078 100644
--- a/gjs/module.cpp
+++ b/gjs/module.cpp
@@ -91,7 +91,7 @@ class GjsScriptModule {
     GJS_JSAPI_RETURN_CONVENTION
     bool evaluate_import(JSContext* cx, JS::HandleObject module,
                          const char* script, ssize_t script_len,
-                         const char* filename) {
+                         const char* filename, const char* uri) {
         std::u16string utf16_string =
             gjs_utf8_script_to_utf16(script, script_len);
         // COMPAT: This could use JS::SourceText<mozilla::Utf8Unit> directly,
@@ -111,6 +111,9 @@ class GjsScriptModule {
         JS::CompileOptions options(cx);
         options.setFileAndLine(filename, 1);
 
+        JS::RootedObject priv(cx, build_script_private(cx, uri));
+        options.setPrivateValue(JS::ObjectValue(*priv));
+
         JS::RootedValue ignored_retval(cx);
         if (!JS::Evaluate(cx, scope_chain, options, buf, &ignored_retval))
             return false;
@@ -142,7 +145,8 @@ class GjsScriptModule {
         g_assert(script);
 
         GjsAutoChar full_path = g_file_get_parse_name(file);
-        return evaluate_import(cx, module, script, script_len, full_path);
+        GjsAutoChar uri = g_file_get_uri(file);
+        return evaluate_import(cx, module, script, script_len, full_path, uri);
     }
 
     /* JSClass operations */
@@ -212,6 +216,19 @@ class GjsScriptModule {
     };
 
  public:
+    /* Creates a JS object to pass to JS::CompileOptions as a script's private.
+     */
+    [[nodiscard]] static JSObject* build_script_private(
+        JSContext* cx, const char* script_uri) {
+        JS::RootedObject priv(cx, JS_NewPlainObject(cx));
+        auto atoms = GjsContextPrivate::atoms(cx);
+        JS::RootedValue val(cx);
+        if (!gjs_string_from_utf8(cx, script_uri, &val) ||
+            !JS_SetPropertyById(cx, priv, atoms.uri(), val))
+            return nullptr;
+        return priv;
+    }
+
     /* Carries out the import operation */
     GJS_JSAPI_RETURN_CONVENTION
     static JSObject *
@@ -231,6 +248,10 @@ class GjsScriptModule {
     }
 };
 
+JSObject* gjs_script_module_build_private(JSContext* cx, const char* uri) {
+    return GjsScriptModule::build_script_private(cx, uri);
+}
+
 /**
  * gjs_module_import:
  * @cx: the JS context
diff --git a/gjs/module.h b/gjs/module.h
index d303530f..dbe088ec 100644
--- a/gjs/module.h
+++ b/gjs/module.h
@@ -21,6 +21,9 @@ gjs_module_import(JSContext       *cx,
                   const char      *name,
                   GFile           *file);
 
+GJS_JSAPI_RETURN_CONVENTION
+JSObject* gjs_script_module_build_private(JSContext* cx, const char* uri);
+
 GJS_JSAPI_RETURN_CONVENTION
 JSObject* gjs_get_native_registry(JSObject* global);
 
diff --git a/installed-tests/js/testESModules.js b/installed-tests/js/testESModules.js
index 6b5afa40..bdc1a89f 100644
--- a/installed-tests/js/testESModules.js
+++ b/installed-tests/js/testESModules.js
@@ -80,21 +80,20 @@ describe('Builtin ES modules', function () {
 
 describe('Dynamic imports', function () {
     let module;
-    beforeEach(function (done) {
-        import('resource:///org/gjs/jsunit/modules/say.js')
-            .then(m => (module = m))
-            .catch(err => {
-                logError(err);
-                fail();
-            })
-            .finally(done);
+    beforeEach(async function () {
+        try {
+            module = await import('resource:///org/gjs/jsunit/modules/say.js')
+        } catch(err) {
+            logError(err);
+            fail();
+        }
     });
 
-    it('default import', function () {
+    it('default import', async function () {
         expect(module.default()).toEqual('default export');
     });
 
-    it('named import', function () {
+    it('named import', async function () {
         expect(module.say('hi')).toEqual('<( hi )');
     });
 });
diff --git a/modules/internal/loader.js b/modules/internal/loader.js
index 464a1034..6067af15 100644
--- a/modules/internal/loader.js
+++ b/modules/internal/loader.js
@@ -77,6 +77,10 @@ class ModuleLoader extends InternalModuleLoader {
     }
 
     moduleDynamicImportHook(module, specifier, promise) {
+        if (!module || !module.uri) {
+            throw new ImportError('Cannot resolve relative imports from an unknown file.');
+        }
+
         this.resolveModuleAsync(specifier, module.uri).then((m) => {
             initAndEval(m);
     


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