LibJS: Cache entry modules before loading imports
Host-loaded modules are inserted into the VM module registry before their dependencies are walked, but entry modules passed directly to `VM::run()` were not. That made an entry module self-import parse a second Module Record and observe the second copy after evaluation. Register named entry modules in the same registry before dependency loading runs, so self-imports and namespace access go through the original Module Record. Add a test-js helper for running modules directly and cover the self-import pre-evaluation binding state.
This commit is contained in:
parent
410f0fdbb0
commit
bdecc7f558
4 changed files with 54 additions and 0 deletions
|
|
@ -627,6 +627,8 @@ VM::StoredModule* VM::get_stored_module(ImportedModuleReferrer const&, ByteStrin
|
|||
return &(*end_or_module);
|
||||
}
|
||||
|
||||
static ByteString resolve_module_filename(StringView filename, Utf16View const& module_type);
|
||||
|
||||
ThrowCompletionOr<void> VM::link_and_eval_module(SourceTextModule& module)
|
||||
{
|
||||
return link_and_eval_module(static_cast<CyclicModule&>(module));
|
||||
|
|
@ -635,6 +637,19 @@ ThrowCompletionOr<void> VM::link_and_eval_module(SourceTextModule& module)
|
|||
ThrowCompletionOr<void> VM::link_and_eval_module(CyclicModule& module)
|
||||
{
|
||||
auto filename = module.filename();
|
||||
if (!filename.is_empty()) {
|
||||
auto absolute_filename = resolve_module_filename(LexicalPath::absolute_path("."sv, filename), {});
|
||||
if (!get_stored_module(GC::Ref { module }, absolute_filename, {})) {
|
||||
// Register the entry module before loading dependencies so self-imports resolve to this Module Record.
|
||||
m_loaded_modules.empend(
|
||||
GC::Ref { module },
|
||||
move(absolute_filename),
|
||||
String {},
|
||||
make_root(static_cast<Module&>(module)),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
auto& promise_capability = module.load_requested_modules(nullptr);
|
||||
|
||||
if (auto const& promise = as<Promise>(*promise_capability.promise()); promise.state() == Promise::State::Rejected)
|
||||
|
|
|
|||
|
|
@ -259,6 +259,10 @@ describe("loops", () => {
|
|||
expectModulePassed("./loop-self.mjs");
|
||||
});
|
||||
|
||||
test("entry module can import itself before evaluation", () => {
|
||||
evaluateModule("./modules/entry-self-import.mjs");
|
||||
});
|
||||
|
||||
test("import something which imports a cycle", () => {
|
||||
expectModulePassed("./loop-entry.mjs");
|
||||
});
|
||||
|
|
|
|||
23
Tests/LibJS/Runtime/modules/entry-self-import.mjs
Normal file
23
Tests/LibJS/Runtime/modules/entry-self-import.mjs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
if (importedVarValue !== undefined) {
|
||||
throw new Error(`Expected importedVarValue to be undefined before evaluation, got ${importedVarValue}`);
|
||||
}
|
||||
|
||||
try {
|
||||
importedVarValue = 0;
|
||||
throw new Error("Expected assignment to importedVarValue to throw");
|
||||
} catch (error) {
|
||||
if (!(error instanceof TypeError)) throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
namespace.localValue;
|
||||
throw new Error("Expected namespace.localValue to be in the TDZ before evaluation");
|
||||
} catch (error) {
|
||||
if (!(error instanceof ReferenceError)) throw error;
|
||||
}
|
||||
|
||||
import { varValue as importedVarValue } from "./entry-self-import.mjs";
|
||||
import * as namespace from "./entry-self-import.mjs";
|
||||
|
||||
export var varValue = 1;
|
||||
export let localValue = 2;
|
||||
|
|
@ -47,6 +47,18 @@ TESTJS_GLOBAL_FUNCTION(evaluate_source, evaluateSource)
|
|||
return vm.run(script.value());
|
||||
}
|
||||
|
||||
TESTJS_GLOBAL_FUNCTION(evaluate_module, evaluateModule)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
auto path = TRY(vm.argument(0).to_string(vm));
|
||||
auto module = Test::JS::parse_module(path.to_byte_string(), realm);
|
||||
if (module.is_error())
|
||||
return vm.throw_completion<JS::SyntaxError>(module.error().error.to_string());
|
||||
|
||||
return vm.run(module.value());
|
||||
}
|
||||
|
||||
TESTJS_GLOBAL_FUNCTION(run_queued_promise_jobs, runQueuedPromiseJobs)
|
||||
{
|
||||
vm.run_queued_promise_jobs();
|
||||
|
|
|
|||
Loading…
Reference in a new issue