From 2d69a009fb451c1adf9bd6750fe06a350eb2dca7 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 6 Dec 2023 11:57:47 +0100 Subject: [PATCH] LibWeb: Make more JS modules actually run First, we had a logic typo where we were checking parse errors for non-empty instead of non-null. Fixing this caused more modules to actually start executing. As usual, this tripped on some "empty backup incumbent settings object stack" bugs, so this patch also pushes a module execution context in two places where it makes sense. Co-Authored-By: networkException --- .../LibWeb/Bindings/MainThreadVM.cpp | 26 ++++++++++++++----- .../LibWeb/HTML/Scripting/ModuleScript.cpp | 9 +++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp b/Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp index 3ae9a8a3c7..eb1b75fe56 100644 --- a/Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp +++ b/Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp @@ -480,18 +480,22 @@ ErrorOr initialize_main_thread_vm() // 1. Let completion be null. // NOTE: Our JS::Completion does not support non JS::Value types for its [[Value]], a such we // use JS::ThrowCompletionOr here. + + auto& vm = realm.vm(); + JS::GCPtr module = nullptr; + auto completion = [&]() -> JS::ThrowCompletionOr> { // 2. If moduleScript is null, then set completion to Completion Record { [[Type]]: throw, [[Value]]: a new TypeError, [[Target]]: empty }. if (!module_script) { return JS::throw_completion(JS::TypeError::create(realm, DeprecatedString::formatted("Loading imported module '{}' failed.", module_request.module_specifier))); } // 3. Otherwise, if moduleScript's parse error is not null, then: - else if (!module_script->parse_error().is_empty()) { + else if (!module_script->parse_error().is_null()) { // 1. Let parseError be moduleScript's parse error. auto parse_error = module_script->parse_error(); // 2. Set completion to Completion Record { [[Type]]: throw, [[Value]]: parseError, [[Target]]: empty }. - return JS::throw_completion(parse_error); + auto completion = JS::throw_completion(parse_error); // 3. If loadState is not undefined and loadState.[[ParseError]] is null, set loadState.[[ParseError]] to parseError. if (load_state) { @@ -500,18 +504,28 @@ ErrorOr initialize_main_thread_vm() load_state_as_fetch_context.parse_error = parse_error; } } + + return completion; } // 4. Otherwise, set completion to Completion Record { [[Type]]: normal, [[Value]]: result's record, [[Target]]: empty }. else { - auto* record = static_cast(*module_script).record(); - - return JS::ThrowCompletionOr>(*record); + module = static_cast(*module_script).record(); + return JS::ThrowCompletionOr>(*module); } }(); // 5. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, completion). - HTML::TemporaryExecutionContext context { host_defined_environment_settings_object(realm) }; + // NON-STANDARD: To ensure that LibJS can find the module on the stack, we push a new execution context. + + auto module_execution_context = JS::ExecutionContext::create(realm.heap()); + module_execution_context->realm = realm; + if (module) + module_execution_context->script_or_module = JS::NonnullGCPtr { *module }; + vm.push_execution_context(*module_execution_context); + JS::finish_loading_imported_module(referrer, module_request, payload, completion); + + vm.pop_execution_context(); }); // 13. Fetch a single imported module script given url, fetchClient, destination, fetchOptions, settingsObject, fetchReferrer, diff --git a/Userland/Libraries/LibWeb/HTML/Scripting/ModuleScript.cpp b/Userland/Libraries/LibWeb/HTML/Scripting/ModuleScript.cpp index 1ba8368640..8bc2cc9cd8 100644 --- a/Userland/Libraries/LibWeb/HTML/Scripting/ModuleScript.cpp +++ b/Userland/Libraries/LibWeb/HTML/Scripting/ModuleScript.cpp @@ -147,6 +147,12 @@ JS::Promise* JavaScriptModuleScript::run(PreventErrorReporting) auto record = m_record; VERIFY(record); + // NON-STANDARD: To ensure that LibJS can find the module on the stack, we push a new execution context. + auto module_execution_context = JS::ExecutionContext::create(heap()); + module_execution_context->realm = &settings.realm(); + module_execution_context->script_or_module = JS::NonnullGCPtr { *record }; + vm().push_execution_context(*module_execution_context); + // 2. Set evaluationPromise to record.Evaluate(). auto elevation_promise_or_error = record->evaluate(vm()); @@ -161,6 +167,9 @@ JS::Promise* JavaScriptModuleScript::run(PreventErrorReporting) } else { evaluation_promise = elevation_promise_or_error.value(); } + + // NON-STANDARD: Pop the execution context mentioned above. + vm().pop_execution_context(); } // FIXME: 7. If preventErrorReporting is false, then upon rejection of evaluationPromise with reason, report the exception given by reason for script.