diff --git a/Meta/Lagom/Wasm/js_repl.cpp b/Meta/Lagom/Wasm/js_repl.cpp index 2ce45247616..ed5c78a7e9a 100644 --- a/Meta/Lagom/Wasm/js_repl.cpp +++ b/Meta/Lagom/Wasm/js_repl.cpp @@ -305,7 +305,7 @@ extern "C" int initialize_repl(char const* time_zone) setenv("TZ", time_zone, 1); g_vm = MUST(JS::VM::create()); - g_vm->enable_default_host_import_module_dynamically_hook(); + g_vm->set_dynamic_imports_allowed(true); // NOTE: These will print out both warnings when using something like Promise.reject().catch(...) - // which is, as far as I can tell, correct - a promise is created, rejected without handler, and a diff --git a/Tests/LibJS/test262-runner.cpp b/Tests/LibJS/test262-runner.cpp index a3856448e7d..ef87d86fad2 100644 --- a/Tests/LibJS/test262-runner.cpp +++ b/Tests/LibJS/test262-runner.cpp @@ -214,7 +214,7 @@ static Result run_test(StringView source, StringView filepath, } auto vm = MUST(JS::VM::create()); - vm->enable_default_host_import_module_dynamically_hook(); + vm->set_dynamic_imports_allowed(true); JS::GCPtr realm; JS::GCPtr global_object; diff --git a/Userland/Applications/Spreadsheet/Workbook.cpp b/Userland/Applications/Spreadsheet/Workbook.cpp index 31bbce44da0..1706afd72ae 100644 --- a/Userland/Applications/Spreadsheet/Workbook.cpp +++ b/Userland/Applications/Spreadsheet/Workbook.cpp @@ -38,7 +38,7 @@ Workbook::Workbook(Vector>&& sheets, GUI::Window& parent_wi m_main_execution_context->realm = &realm; m_main_execution_context->is_strict_mode = true; m_vm->push_execution_context(*m_main_execution_context); - m_vm->enable_default_host_import_module_dynamically_hook(); + m_vm->set_dynamic_imports_allowed(true); } bool Workbook::set_filename(DeprecatedString const& filename) diff --git a/Userland/Libraries/LibJS/CyclicModule.cpp b/Userland/Libraries/LibJS/CyclicModule.cpp index d21a2af46de..e1f0b16639b 100644 --- a/Userland/Libraries/LibJS/CyclicModule.cpp +++ b/Userland/Libraries/LibJS/CyclicModule.cpp @@ -45,13 +45,13 @@ void GraphLoadingState::visit_edges(Cell::Visitor& visitor) } // 16.2.1.5.1 LoadRequestedModules ( [ hostDefined ] ), https://tc39.es/ecma262/#sec-LoadRequestedModules -PromiseCapability& CyclicModule::load_requested_modules(JS::Realm& realm, GCPtr host_defined) +PromiseCapability& CyclicModule::load_requested_modules(GCPtr host_defined) { // 1. If hostDefined is not present, let hostDefined be EMPTY. // NOTE: The empty state is handled by hostDefined being an optional without value. // 2. Let pc be ! NewPromiseCapability(%Promise%). - auto promise_capability = MUST(new_promise_capability(realm.vm(), realm.intrinsics().promise_constructor())); + auto promise_capability = MUST(new_promise_capability(vm(), vm().current_realm()->intrinsics().promise_constructor())); // 3. Let state be the GraphLoadingState Record { [[IsLoading]]: true, [[PendingModulesCount]]: 1, [[Visited]]: « », [[PromiseCapability]]: pc, [[HostDefined]]: hostDefined }. auto state = heap().allocate_without_realm(promise_capability, true, 1, HashTable {}, move(host_defined)); @@ -101,7 +101,7 @@ void CyclicModule::inner_module_loading(JS::GraphLoadingState& state) // ii. Else, if (!found_record_in_loaded_modules) { // 1. Perform HostLoadImportedModule(module, required, state.[[HostDefined]], state). - vm().host_load_imported_module(realm(), NonnullGCPtr(*this), required, state.host_defined, state); + vm().host_load_imported_module(NonnullGCPtr { *this }, required, state.host_defined, NonnullGCPtr { state }); // 2. NOTE: HostLoadImportedModule will call FinishLoadingImportedModule, which re-enters the graph loading process through ContinueModuleLoading. } @@ -138,7 +138,7 @@ void CyclicModule::inner_module_loading(JS::GraphLoadingState& state) } // 16.2.1.5.1.2 ContinueModuleLoading ( state, moduleCompletion ), https://tc39.es/ecma262/#sec-ContinueModuleLoading -void continue_module_loading(Realm& realm, GraphLoadingState& state, ThrowCompletionOr const& module_completion) +void continue_module_loading(GraphLoadingState& state, ThrowCompletionOr> const& module_completion) { // 1. If state.[[IsLoading]] is false, return UNUSED. if (state.is_loading) @@ -146,10 +146,10 @@ void continue_module_loading(Realm& realm, GraphLoadingState& state, ThrowComple // 2. If moduleCompletion is a normal completion, then if (!module_completion.is_error()) { - auto* module = const_cast(module_completion.value()); + auto module = module_completion.value(); // a. Perform InnerModuleLoading(state, moduleCompletion.[[Value]]). - static_cast(module)->inner_module_loading(state); + verify_cast(*module).inner_module_loading(state); } // 3. Else, else { @@ -159,7 +159,7 @@ void continue_module_loading(Realm& realm, GraphLoadingState& state, ThrowComple auto value = module_completion.throw_completion().value(); // b. Perform ! Call(state.[[PromiseCapability]].[[Reject]], undefined, « moduleCompletion.[[Value]] »). - MUST(call(realm.vm(), *state.promise_capability->reject(), js_undefined(), *value)); + MUST(call(state.vm(), *state.promise_capability->reject(), js_undefined(), *value)); } // 4. Return UNUSED. @@ -318,14 +318,14 @@ ThrowCompletionOr CyclicModule::inner_module_linking(VM& vm, Vector CyclicModule::evaluate(VM& vm) { dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] evaluate[{}](vm)", this); // 1. Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent. // FIXME: Verify this somehow - // 2. Assert: module.[[Status]] is linked, evaluating-async, or evaluated. + // 2. Assert: module.[[Status]] is one of linked, evaluating-async, or evaluated. VERIFY(m_status == ModuleStatus::Linked || m_status == ModuleStatus::EvaluatingAsync || m_status == ModuleStatus::Evaluated); // NOTE: The spec does not catch the case where evaluate is called twice on a script which failed @@ -335,7 +335,7 @@ ThrowCompletionOr CyclicModule::evaluate(VM& vm) if (m_top_level_capability != nullptr) return verify_cast(m_top_level_capability->promise().ptr()); - // 3. If module.[[Status]] is evaluating-async or evaluated, set module to module.[[CycleRoot]]. + // 3. If module.[[Status]] is either evaluating-async or evaluated, set module to module.[[CycleRoot]]. if (m_status == ModuleStatus::EvaluatingAsync || m_status == ModuleStatus::Evaluated) { // Note: This will continue this function with module.[[CycleRoot]] VERIFY(m_cycle_root); @@ -396,7 +396,7 @@ ThrowCompletionOr CyclicModule::evaluate(VM& vm) } // 10. Else, else { - // a. Assert: module.[[Status]] is evaluating-async or evaluated. + // a. Assert: module.[[Status]] is either evaluating-async or evaluated. VERIFY(m_status == ModuleStatus::EvaluatingAsync || m_status == ModuleStatus::Evaluated); // b. Assert: module.[[EvaluationError]] is empty. VERIFY(!m_evaluation_error.is_error()); @@ -830,4 +830,90 @@ NonnullGCPtr CyclicModule::get_imported_module(ModuleRequest const& requ VERIFY_NOT_REACHED(); } +// 13.3.10.1.1 ContinueDynamicImport ( promiseCapability, moduleCompletion ), https://tc39.es/ecma262/#sec-ContinueDynamicImport +void continue_dynamic_import(NonnullGCPtr promise_capability, ThrowCompletionOr> const& module_completion) +{ + auto& vm = promise_capability->vm(); + + // 1. If moduleCompletion is an abrupt completion, then + if (module_completion.is_throw_completion()) { + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « moduleCompletion.[[Value]] »). + MUST(call(vm, *promise_capability->reject(), js_undefined(), *module_completion.throw_completion().value())); + + // b. Return unused. + return; + } + + // 2. Let module be moduleCompletion.[[Value]]. + auto& module = *module_completion.value(); + + // 3. Let loadPromise be module.LoadRequestedModules(). + auto& load_promise = verify_cast(module).load_requested_modules({}); + + // 4. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures promiseCapability and performs the + // following steps when called: + auto reject_closure = [promise_capability](VM& vm) -> ThrowCompletionOr { + auto reason = vm.argument(0); + + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « reason »). + MUST(call(vm, *promise_capability->reject(), js_undefined(), reason)); + + // b. Return unused. + return js_undefined(); + }; + + // 5. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »). + auto on_rejected = NativeFunction::create(*vm.current_realm(), move(reject_closure), 1, ""); + + // 6. Let linkAndEvaluateClosure be a new Abstract Closure with no parameters that captures module, promiseCapability, + // and onRejected and performs the following steps when called: + auto link_and_evaluate_closure = [&module, promise_capability, on_rejected](VM& vm) -> ThrowCompletionOr { + // a. Let link be Completion(module.Link()). + auto link = module.link(vm); + + // b. If link is an abrupt completion, then + if (link.is_throw_completion()) { + // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « link.[[Value]] »). + MUST(call(vm, *promise_capability->reject(), js_undefined(), *link.throw_completion().value())); + + // ii. Return unused. + return js_undefined(); + } + + // c. Let evaluatePromise be module.Evaluate(). + auto evaluate_promise = module.evaluate(vm); + + // d. Let fulfilledClosure be a new Abstract Closure with no parameters that captures module and + // promiseCapability and performs the following steps when called: + auto fulfilled_closure = [&module, promise_capability](VM& vm) -> ThrowCompletionOr { + // i. Let namespace be GetModuleNamespace(module). + auto namespace_ = module.get_module_namespace(vm); + + // ii. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace »). + MUST(call(vm, *promise_capability->resolve(), js_undefined(), namespace_.value())); + + // iii. Return unused. + return js_undefined(); + }; + + // e. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 0, "", « »). + auto on_fulfilled = NativeFunction::create(*vm.current_realm(), move(fulfilled_closure), 0, ""); + + // f. Perform PerformPromiseThen(evaluatePromise, onFulfilled, onRejected). + evaluate_promise.value()->perform_then(on_fulfilled, on_rejected, {}); + + // g. Return unused. + return js_undefined(); + }; + + // 7. Let linkAndEvaluate be CreateBuiltinFunction(linkAndEvaluateClosure, 0, "", « »). + auto link_and_evaluate = NativeFunction::create(*vm.current_realm(), move(link_and_evaluate_closure), 0, ""); + + // 8. Perform PerformPromiseThen(loadPromise, linkAndEvaluate, onRejected). + // FIXME: This is likely a spec bug, see load_requested_modules. + verify_cast(*load_promise.promise()).perform_then(link_and_evaluate, on_rejected, {}); + + // 9. Return unused. +} + } diff --git a/Userland/Libraries/LibJS/CyclicModule.h b/Userland/Libraries/LibJS/CyclicModule.h index 259eeb773fc..ab14e9f5a26 100644 --- a/Userland/Libraries/LibJS/CyclicModule.h +++ b/Userland/Libraries/LibJS/CyclicModule.h @@ -65,7 +65,7 @@ public: virtual ThrowCompletionOr link(VM& vm) override final; virtual ThrowCompletionOr evaluate(VM& vm) override final; - virtual PromiseCapability& load_requested_modules(Realm&, GCPtr); + virtual PromiseCapability& load_requested_modules(GCPtr); virtual void inner_module_loading(GraphLoadingState& state); Vector const& requested_modules() const { return m_requested_modules; } @@ -103,6 +103,7 @@ protected: Optional m_pending_async_dependencies; // [[PendingAsyncDependencies]] }; -void continue_module_loading(Realm&, GraphLoadingState& state, ThrowCompletionOr const&); +void continue_module_loading(GraphLoadingState&, ThrowCompletionOr> const&); +void continue_dynamic_import(NonnullGCPtr, ThrowCompletionOr> const& module_completion); } diff --git a/Userland/Libraries/LibJS/Module.cpp b/Userland/Libraries/LibJS/Module.cpp index 4161cbd2343..7509f8096c2 100644 --- a/Userland/Libraries/LibJS/Module.cpp +++ b/Userland/Libraries/LibJS/Module.cpp @@ -67,46 +67,52 @@ ThrowCompletionOr Module::inner_module_evaluation(VM& vm, Vector&, } // 16.2.1.9 FinishLoadingImportedModule ( referrer, specifier, payload, result ), https://tc39.es/ecma262/#sec-FinishLoadingImportedModule -// FIXME: We currently implement an outdated version of https://tc39.es/proposal-import-attributes, as such it is not possible to -// use the exact steps from https://tc39.es/proposal-import-attributes/#sec-HostLoadImportedModule here. -// FIXME: Support Realm for referrer. -void finish_loading_imported_module(Realm& realm, Variant, JS::NonnullGCPtr> referrer, ModuleRequest const& module_request, GraphLoadingState& payload, ThrowCompletionOr const& result) +void finish_loading_imported_module(ImportedModuleReferrer referrer, ModuleRequest const& module_request, ImportedModulePayload payload, ThrowCompletionOr> const& result) { // 1. If result is a normal completion, then if (!result.is_error()) { - auto loaded_modules = referrer.visit( - [](JS::NonnullGCPtr script) -> Vector { return script->loaded_modules(); }, - [](JS::NonnullGCPtr module) -> Vector { return module->loaded_modules(); }); + // NOTE: Only Script and CyclicModule referrers have the [[LoadedModules]] internal slot. + if (referrer.has>() || referrer.has>()) { + auto loaded_modules = referrer.visit( + [](JS::NonnullGCPtr script) -> Vector { return script->loaded_modules(); }, + [](JS::NonnullGCPtr module) -> Vector { return module->loaded_modules(); }, + [](auto&) { + VERIFY_NOT_REACHED(); + return Vector {}; + }); - bool found_record = false; + bool found_record = false; - // a.a. If referrer.[[LoadedModules]] contains a Record whose [[Specifier]] is specifier, then - for (auto const& record : loaded_modules) { - if (record.specifier == module_request.module_specifier) { - // i. Assert: That Record's [[Module]] is result.[[Value]]. - VERIFY(record.module == result.value()); - - found_record = true; + // a. If referrer.[[LoadedModules]] contains a Record whose [[Specifier]] is specifier, then + for (auto const& record : loaded_modules) { + if (record.specifier == module_request.module_specifier) { + // i. Assert: That Record's [[Module]] is result.[[Value]]. + VERIFY(record.module == result.value()); + found_record = true; + } } - } - // b. Else, - if (!found_record) { - auto* module = const_cast(result.value()); + // b. Else, + if (!found_record) { + auto module = result.value(); - // i. Append the Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]]. - loaded_modules.append(ModuleWithSpecifier { - .specifier = module_request.module_specifier, - .module = NonnullGCPtr(*module) }); + // i. Append the Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]]. + loaded_modules.append(ModuleWithSpecifier { + .specifier = module_request.module_specifier, + .module = NonnullGCPtr(*module) }); + } } } - // FIXME: 2. If payload is a GraphLoadingState Record, then - // a. Perform ContinueModuleLoading(payload, result) - continue_module_loading(realm, payload, result); - - // FIXME: Else, - // FIXME: a. Perform ContinueDynamicImport(payload, result). + if (payload.has>()) { + // a. Perform ContinueModuleLoading(payload, result) + continue_module_loading(payload.get>(), result); + } + // Else, + else { + // a. Perform ContinueDynamicImport(payload, result). + continue_dynamic_import(payload.get>(), result); + } // 4. Return unused. } diff --git a/Userland/Libraries/LibJS/Module.h b/Userland/Libraries/LibJS/Module.h index 671e25771cb..521e43c285a 100644 --- a/Userland/Libraries/LibJS/Module.h +++ b/Userland/Libraries/LibJS/Module.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -113,6 +114,6 @@ private: class CyclicModule; struct GraphLoadingState; -void finish_loading_imported_module(Realm&, Variant, NonnullGCPtr>, ModuleRequest const&, GraphLoadingState&, ThrowCompletionOr const&); +void finish_loading_imported_module(ImportedModuleReferrer, ModuleRequest const&, ImportedModulePayload, ThrowCompletionOr> const&); } diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp index 1f9ef67f1f8..65d840a1529 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -1590,7 +1590,7 @@ ThrowCompletionOr perform_import_call(VM& vm, Value specifier, Value opti ModuleRequest request { specifier_string, attributes }; // 13. Perform HostLoadImportedModule(referrer, moduleRequest, empty, promiseCapability). - MUST_OR_THROW_OOM(vm.host_import_module_dynamically(referrer, move(request), promise_capability)); + vm.host_load_imported_module(referrer, move(request), nullptr, promise_capability); // 13. Return promiseCapability.[[Promise]]. return Value { promise_capability->promise() }; diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp index 7e2f55adc70..68ed68a1a61 100644 --- a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp @@ -234,8 +234,9 @@ ThrowCompletionOr shadow_realm_import_value(VM& vm, DeprecatedString spec TRY(vm.push_execution_context(eval_context, {})); // 6. Let referrer be the Realm component of evalContext. - // 7. Perform HostImportModuleDynamically(referrer, specifierString, innerCapability). - MUST_OR_THROW_OOM(vm.host_import_module_dynamically(NonnullGCPtr { *eval_context.realm }, ModuleRequest { move(specifier_string) }, inner_capability)); + auto referrer = JS::NonnullGCPtr { *eval_context.realm }; + // 7. Perform HostLoadImportedModule(referrer, specifierString, empty, innerCapability). + vm.host_load_imported_module(referrer, ModuleRequest { specifier_string }, nullptr, inner_capability); // 7. Suspend evalContext and remove it from the execution context stack. // NOTE: We don't support this concept yet. diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index 74b5777b74a..88f4233637a 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -94,41 +94,8 @@ VM::VM(OwnPtr custom_data, ErrorMessages error_messages) return make_job_callback(function_object); }; - host_resolve_imported_module = [&](ImportedModuleReferrer referrer, ModuleRequest const& specifier) { - return resolve_imported_module(referrer, specifier); - }; - - host_import_module_dynamically = [&](ImportedModuleReferrer, ModuleRequest const&, PromiseCapability const& promise_capability) -> ThrowCompletionOr { - // By default, we throw on dynamic imports this is to prevent arbitrary file access by scripts. - VERIFY(current_realm()); - auto& realm = *current_realm(); - auto promise = Promise::create(realm); - - // If you are here because you want to enable dynamic module importing make sure it won't be a security problem - // by checking the default implementation of HostImportModuleDynamically and creating your own hook or calling - // vm.enable_default_host_import_module_dynamically_hook(). - promise->reject(Error::create(realm, ErrorType::DynamicImportNotAllowed.message())); - - promise->perform_then( - NativeFunction::create(realm, "", [](auto&) -> ThrowCompletionOr { - VERIFY_NOT_REACHED(); - }), - NativeFunction::create(realm, "", [&promise_capability](auto& vm) -> ThrowCompletionOr { - auto error = vm.argument(0); - - // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « error »). - MUST(call(vm, *promise_capability.reject(), js_undefined(), error)); - - // b. Return undefined. - return js_undefined(); - }), - {}); - - return {}; - }; - - host_finish_dynamic_import = [&](ImportedModuleReferrer referrer, ModuleRequest const& specifier, PromiseCapability const& promise_capability, Promise* promise) { - return finish_dynamic_import(referrer, specifier, promise_capability, promise); + host_load_imported_module = [this](ImportedModuleReferrer referrer, ModuleRequest const& module_request, GCPtr load_state, ImportedModulePayload payload) -> void { + return load_imported_module(referrer, module_request, load_state, move(payload)); }; host_get_import_meta_properties = [&](SourceTextModule const&) -> HashMap { @@ -181,13 +148,6 @@ String const& VM::error_message(ErrorMessage type) const return message; } -void VM::enable_default_host_import_module_dynamically_hook() -{ - host_import_module_dynamically = [&](ImportedModuleReferrer referrer, ModuleRequest const& specifier, PromiseCapability const& promise_capability) { - return import_module_dynamically(referrer, specifier, promise_capability); - }; -} - Bytecode::Interpreter& VM::bytecode_interpreter() { return *m_bytecode_interpreter; @@ -865,22 +825,32 @@ static DeprecatedString resolve_module_filename(StringView filename, StringView return filename; } -// 16.2.1.7 HostResolveImportedModule ( referencingScriptOrModule, specifier ), https://tc39.es/ecma262/#sec-hostresolveimportedmodule -ThrowCompletionOr> VM::resolve_imported_module(ImportedModuleReferrer referrer, ModuleRequest const& module_request) +// 16.2.1.8 HostLoadImportedModule ( referrer, specifier, hostDefined, payload ), https://tc39.es/ecma262/#sec-HostLoadImportedModule +void VM::load_imported_module(ImportedModuleReferrer referrer, ModuleRequest const& module_request, GCPtr, ImportedModulePayload payload) { - // An implementation of HostResolveImportedModule must conform to the following requirements: - // - If it completes normally, the [[Value]] slot of the completion must contain an instance of a concrete subclass of Module Record. - // - If a Module Record corresponding to the pair referencingScriptOrModule, moduleRequest does not exist or cannot be created, an exception must be thrown. - // - Each time this operation is called with a specific referencingScriptOrModule, moduleRequest.[[Specifier]], moduleRequest.[[Assertions]] triple - // as arguments it must return the same Module Record instance if it completes normally. - // * It is recommended but not required that implementations additionally conform to the following stronger constraint: - // each time this operation is called with a specific referencingScriptOrModule, moduleRequest.[[Specifier]] pair as arguments it must return the same Module Record instance if it completes normally. - // - moduleRequest.[[Assertions]] must not influence the interpretation of the module or the module specifier; - // instead, it may be used to determine whether the algorithm completes normally or with an abrupt completion. + // An implementation of HostLoadImportedModule must conform to the following requirements: + // + // - The host environment must perform FinishLoadingImportedModule(referrer, specifier, payload, result), + // where result is either a normal completion containing the loaded Module Record or a throw completion, + // either synchronously or asynchronously. + // - If this operation is called multiple times with the same (referrer, specifier) pair and it performs + // FinishLoadingImportedModule(referrer, specifier, payload, result) where result is a normal completion, + // then it must perform FinishLoadingImportedModule(referrer, specifier, payload, result) with the same result each time. + // - The operation must treat payload as an opaque value to be passed through to FinishLoadingImportedModule. + // + // The actual process performed is host-defined, but typically consists of performing whatever I/O operations are necessary to + // load the appropriate Module Record. Multiple different (referrer, specifier) pairs may map to the same Module Record instance. + // The actual mapping semantics is host-defined but typically a normalization process is applied to specifier as part of the + // mapping process. A typical normalization process would include actions such as expansion of relative and abbreviated path specifiers. - // Multiple different referencingScriptOrModule, moduleRequest.[[Specifier]] pairs may map to the same Module Record instance. - // The actual mapping semantic is host-defined but typically a normalization process is applied to specifier as part of the mapping process. - // A typical normalization process would include actions such as alphabetic case folding and expansion of relative and abbreviated path specifiers. + // Here we check, against the spec, if payload is a promise capability, meaning that this was called for a dynamic import + if (payload.has>() && !m_dynamic_imports_allowed) { + // If you are here because you want to enable dynamic module importing make sure it won't be a security problem + // by checking the default implementation of HostImportModuleDynamically and creating your own hook or calling + // vm.allow_dynamic_imports(). + finish_loading_imported_module(referrer, module_request, payload, throw_completion(ErrorType::DynamicImportNotAllowed, module_request.module_specifier)); + return; + } DeprecatedString module_type; for (auto& attribute : module_request.attributes) { @@ -894,13 +864,18 @@ ThrowCompletionOr> VM::resolve_imported_module(ImportedModu StringView const base_filename = referrer.visit( [&](NonnullGCPtr const&) { - return "."sv; + // Generally within ECMA262 we always get a referencing_script_or_module. However, ShadowRealm gives an explicit null. + // To get around this is we attempt to get the active script_or_module otherwise we might start loading "random" files from the working directory. + return get_active_script_or_module().visit( + [](Empty) { + return "."sv; + }, + [](auto const& script_or_module) { + return script_or_module->filename(); + }); }, - [&](NonnullGCPtr