Pārlūkot izejas kodu

LibJS: Use HostImportModuleDynamically in ShadowRealmImportValue

Now that module loading is implemented this just works :^).

Since ShadowRealm explicitly passed a null ScriptOrModule we attempt to
get the top most ScriptOrModule in HostImportModuleDynamically.
This won't work in general as the web specifies other behavior but for
LibJS there must always be an active script to call
HostImportModuleDynamically.
davidot 3 gadi atpakaļ
vecāks
revīzija
986ad3ccf0

+ 4 - 26
Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp

@@ -8,6 +8,7 @@
 #include <LibJS/Parser.h>
 #include <LibJS/Runtime/AbstractOperations.h>
 #include <LibJS/Runtime/DeclarativeEnvironment.h>
+#include <LibJS/Runtime/ModuleNamespaceObject.h>
 #include <LibJS/Runtime/NativeFunction.h>
 #include <LibJS/Runtime/PromiseConstructor.h>
 #include <LibJS/Runtime/PromiseReaction.h>
@@ -164,32 +165,7 @@ ThrowCompletionOr<Value> shadow_realm_import_value(GlobalObject& global_object,
     TRY(vm.push_execution_context(eval_context, eval_realm.global_object()));
 
     // 10. Perform ! HostImportModuleDynamically(null, specifierString, innerCapability).
-    // FIXME: We don't have this yet. We generally have very little support for modules and imports.
-    //        So, in the meantime we just do the "Failure path" step, and pretend to call FinishDynamicImport
-    //        with the rejected promise. This should be easy to complete once those missing module AOs are added.
-
-    // HostImportModuleDynamically: At some future time, the host environment must perform
-    // FinishDynamicImport(referencingScriptOrModule, specifier, promiseCapability, promise),
-    // where promise is a Promise rejected with an error representing the cause of failure.
-    auto* promise = Promise::create(global_object);
-    promise->reject(Error::create(global_object, String::formatted("Import of '{}' from '{}' failed", export_name_string, specifier_string)));
-
-    // FinishDynamicImport, 5. Perform ! PerformPromiseThen(innerPromise, onFulfilled, onRejected).
-    promise->perform_then(
-        NativeFunction::create(global_object, "", [](auto&, auto&) -> ThrowCompletionOr<Value> {
-            // Not called because we hardcoded a rejection above.
-            TODO();
-        }),
-        NativeFunction::create(global_object, "", [reject = make_handle(inner_capability.reject)](auto& vm, auto& global_object) -> ThrowCompletionOr<Value> {
-            auto error = vm.argument(0);
-
-            // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « error »).
-            MUST(call(global_object, reject.cell(), js_undefined(), error));
-
-            // b. Return undefined.
-            return js_undefined();
-        }),
-        {});
+    vm.host_import_module_dynamically(Empty {}, ModuleRequest { move(specifier_string) }, inner_capability);
 
     // 11. Suspend evalContext and remove it from the execution context stack.
     // NOTE: We don't support this concept yet.
@@ -208,7 +184,9 @@ ThrowCompletionOr<Value> shadow_realm_import_value(GlobalObject& global_object,
         "",
         [string = move(export_name_string)](auto& vm, auto& global_object) -> ThrowCompletionOr<Value> {
             // 1. Assert: exports is a module namespace exotic object.
+            VERIFY(vm.argument(0).is_object());
             auto& exports = vm.argument(0).as_object();
+            VERIFY(is<ModuleNamespaceObject>(exports));
 
             // 2. Let f be the active function object.
             auto* function = vm.running_execution_context().function;

+ 49 - 3
Userland/Libraries/LibJS/Tests/builtins/ShadowRealm/ShadowRealm.prototype.importValue.js

@@ -3,10 +3,10 @@ describe("normal behavior", () => {
         expect(ShadowRealm.prototype.importValue).toHaveLength(2);
     });
 
-    test("basic functionality", () => {
+    test("fails if module cannot be loaded", () => {
         // NOTE: The actual import is currently not implemented and always pretends to fail for now.
         const shadowRealm = new ShadowRealm();
-        const promise = shadowRealm.importValue("./myModule.js", "foo");
+        const promise = shadowRealm.importValue("./file_should_not_exist.js", "foo");
         let error;
         promise.catch(value => {
             error = value;
@@ -14,7 +14,53 @@ describe("normal behavior", () => {
         expect(promise).toBeInstanceOf(Promise);
         runQueuedPromiseJobs();
         expect(error).toBeInstanceOf(TypeError);
-        expect(error.message).toBe("Import of 'foo' from './myModule.js' failed");
+        expect(error.message).toBe("Cannot find/open module: './file_should_not_exist.js'");
+    });
+
+    test("basic functionality", () => {
+        const shadowRealm = new ShadowRealm();
+        const promise = shadowRealm.importValue("./external-module.mjs", "foo");
+        expect(promise).toBeInstanceOf(Promise);
+        let error = null;
+        let passed = false;
+        promise
+            .then(value => {
+                expect(value).toBe("Well hello shadows");
+                expect(typeof value).toBe("string");
+
+                expect(value).not.toHaveProperty("default", null);
+                expect(value).not.toHaveProperty("bar", null);
+                passed = true;
+            })
+            .catch(value => {
+                error = value;
+            });
+        runQueuedPromiseJobs();
+        expect(error).toBeNull();
+        expect(passed).toBeTrue();
+    });
+
+    test("value from async module", () => {
+        const shadowRealm = new ShadowRealm();
+        const promise = shadowRealm.importValue("./async-module.mjs", "foo");
+        expect(promise).toBeInstanceOf(Promise);
+        let error = null;
+        let passed = false;
+        promise
+            .then(value => {
+                expect(value).toBe("Well hello async shadows");
+                expect(typeof value).toBe("string");
+
+                expect(value).not.toHaveProperty("default", null);
+                expect(value).not.toHaveProperty("bar", null);
+                passed = true;
+            })
+            .catch(value => {
+                error = value;
+            });
+        runQueuedPromiseJobs();
+        expect(error).toBeNull();
+        expect(passed).toBeTrue();
     });
 });
 

+ 11 - 0
Userland/Libraries/LibJS/Tests/builtins/ShadowRealm/async-module.mjs

@@ -0,0 +1,11 @@
+await Promise.resolve(0);
+
+export const foo = "Well hello async shadows";
+
+await 1;
+
+export default "Default export";
+
+await Promise.resolve(2);
+
+export const bar = "'bar' export";

+ 5 - 0
Userland/Libraries/LibJS/Tests/builtins/ShadowRealm/external-module.mjs

@@ -0,0 +1,5 @@
+export const foo = "Well hello shadows";
+
+export default "Default export";
+
+export const bar = "'bar' export";