1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-28 07:25:07 +00:00

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 <networkexception@serenityos.org>
This commit is contained in:
Andreas Kling 2023-12-06 11:57:47 +01:00
parent a2c3db8367
commit 2d69a009fb
2 changed files with 29 additions and 6 deletions

View file

@ -480,18 +480,22 @@ ErrorOr<void> initialize_main_thread_vm()
// 1. Let completion be null. // 1. Let completion be null.
// NOTE: Our JS::Completion does not support non JS::Value types for its [[Value]], a such we // NOTE: Our JS::Completion does not support non JS::Value types for its [[Value]], a such we
// use JS::ThrowCompletionOr here. // use JS::ThrowCompletionOr here.
auto& vm = realm.vm();
JS::GCPtr<JS::Module> module = nullptr;
auto completion = [&]() -> JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Module>> { auto completion = [&]() -> JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Module>> {
// 2. If moduleScript is null, then set completion to Completion Record { [[Type]]: throw, [[Value]]: a new TypeError, [[Target]]: empty }. // 2. If moduleScript is null, then set completion to Completion Record { [[Type]]: throw, [[Value]]: a new TypeError, [[Target]]: empty }.
if (!module_script) { if (!module_script) {
return JS::throw_completion(JS::TypeError::create(realm, DeprecatedString::formatted("Loading imported module '{}' failed.", module_request.module_specifier))); 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: // 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. // 1. Let parseError be moduleScript's parse error.
auto parse_error = module_script->parse_error(); auto parse_error = module_script->parse_error();
// 2. Set completion to Completion Record { [[Type]]: throw, [[Value]]: parseError, [[Target]]: empty }. // 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. // 3. If loadState is not undefined and loadState.[[ParseError]] is null, set loadState.[[ParseError]] to parseError.
if (load_state) { if (load_state) {
@ -500,18 +504,28 @@ ErrorOr<void> initialize_main_thread_vm()
load_state_as_fetch_context.parse_error = parse_error; 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 }. // 4. Otherwise, set completion to Completion Record { [[Type]]: normal, [[Value]]: result's record, [[Target]]: empty }.
else { else {
auto* record = static_cast<HTML::JavaScriptModuleScript&>(*module_script).record(); module = static_cast<HTML::JavaScriptModuleScript&>(*module_script).record();
return JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Module>>(*module);
return JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Module>>(*record);
} }
}(); }();
// 5. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, completion). // 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); 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, // 13. Fetch a single imported module script given url, fetchClient, destination, fetchOptions, settingsObject, fetchReferrer,

View file

@ -147,6 +147,12 @@ JS::Promise* JavaScriptModuleScript::run(PreventErrorReporting)
auto record = m_record; auto record = m_record;
VERIFY(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<JS::Module> { *record };
vm().push_execution_context(*module_execution_context);
// 2. Set evaluationPromise to record.Evaluate(). // 2. Set evaluationPromise to record.Evaluate().
auto elevation_promise_or_error = record->evaluate(vm()); auto elevation_promise_or_error = record->evaluate(vm());
@ -161,6 +167,9 @@ JS::Promise* JavaScriptModuleScript::run(PreventErrorReporting)
} else { } else {
evaluation_promise = elevation_promise_or_error.value(); 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. // FIXME: 7. If preventErrorReporting is false, then upon rejection of evaluationPromise with reason, report the exception given by reason for script.