LibWeb: Use ThrowCompletion AO to create throw completions

Corresponds to 4989b3457f
This commit is contained in:
Sam Atkins 2025-07-08 12:43:02 +01:00 committed by Tim Ledbetter
commit da8a29376f
Notes: github-actions[bot] 2025-07-08 16:10:44 +00:00
2 changed files with 41 additions and 36 deletions

View file

@ -104,9 +104,9 @@ void initialize_main_thread_vm(AgentType type)
// This avoids doing an exhaustive garbage collection on process exit. // This avoids doing an exhaustive garbage collection on process exit.
s_main_thread_vm->ref(); s_main_thread_vm->ref();
// 8.1.5.1 HostEnsureCanAddPrivateElement(O), https://html.spec.whatwg.org/multipage/webappapis.html#the-hostensurecanaddprivateelement-implementation // 8.1.6.1 HostEnsureCanAddPrivateElement(O), https://html.spec.whatwg.org/multipage/webappapis.html#the-hostensurecanaddprivateelement-implementation
s_main_thread_vm->host_ensure_can_add_private_element = [](JS::Object const& object) -> JS::ThrowCompletionOr<void> { s_main_thread_vm->host_ensure_can_add_private_element = [](JS::Object const& object) -> JS::ThrowCompletionOr<void> {
// 1. If O is a WindowProxy object, or implements Location, then return Completion { [[Type]]: throw, [[Value]]: a new TypeError }. // 1. If O is a WindowProxy object, or implements Location, then return ThrowCompletion(a new TypeError).
if (is<HTML::WindowProxy>(object) || is<HTML::Location>(object)) if (is<HTML::WindowProxy>(object) || is<HTML::Location>(object))
return s_main_thread_vm->throw_completion<JS::TypeError>("Cannot add private elements to window or location object"sv); return s_main_thread_vm->throw_completion<JS::TypeError>("Cannot add private elements to window or location object"sv);
@ -418,13 +418,11 @@ void initialize_main_thread_vm(AgentType type)
// 2. If moduleMapRealm's global object implements WorkletGlobalScope or ServiceWorkerGlobalScope and loadState is undefined, then: // 2. If moduleMapRealm's global object implements WorkletGlobalScope or ServiceWorkerGlobalScope and loadState is undefined, then:
if ((is<HTML::WorkletGlobalScope>(module_map_realm->global_object()) || is<ServiceWorker::ServiceWorkerGlobalScope>(module_map_realm->global_object())) && !load_state) { if ((is<HTML::WorkletGlobalScope>(module_map_realm->global_object()) || is<ServiceWorker::ServiceWorkerGlobalScope>(module_map_realm->global_object())) && !load_state) {
// 1. Let completion be Completion Record { [[Type]]: throw, [[Value]]: a new TypeError, [[Target]]: empty }. // 1. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, ThrowCompletion(a new TypeError)).
auto completion = JS::throw_completion(JS::TypeError::create(*module_map_realm, "Dynamic Import not available for Worklets or ServiceWorkers"_string)); auto completion = JS::throw_completion(JS::TypeError::create(*module_map_realm, "Dynamic Import not available for Worklets or ServiceWorkers"_string));
// 2. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, completion).
JS::finish_loading_imported_module(referrer, module_request, payload, completion); JS::finish_loading_imported_module(referrer, module_request, payload, completion);
// 3. Return. // 2. Return.
return; return;
} }
@ -462,13 +460,15 @@ void initialize_main_thread_vm(AgentType type)
if (attribute.key == "type"sv) if (attribute.key == "type"sv)
continue; continue;
// 1. Let completion be Completion Record { [[Type]]: throw, [[Value]]: a new SyntaxError exception, [[Target]]: empty }. // 1. Let error be a new SyntaxError exception.
auto completion = JS::throw_completion(JS::SyntaxError::create(*module_map_realm, "Module request attributes must only contain a type attribute"_string)); auto error = JS::SyntaxError::create(*module_map_realm, "Module request attributes must only contain a type attribute"_string);
// 2. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, completion). // FIXME: 2. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to error.
JS::finish_loading_imported_module(referrer, module_request, payload, completion);
// 3. Return. // 3. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, ThrowCompletion(error)).
JS::finish_loading_imported_module(referrer, module_request, payload, JS::throw_completion(error));
// 4. Return.
return; return;
} }
} }
@ -479,10 +479,10 @@ void initialize_main_thread_vm(AgentType type)
// 3. If the previous step threw an exception, then: // 3. If the previous step threw an exception, then:
if (maybe_exception.is_exception()) { if (maybe_exception.is_exception()) {
// 1. Let completion be Completion Record { [[Type]]: throw, [[Value]]: resolutionError, [[Target]]: empty }. // FIXME: 1. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to resolutionError.
auto completion = exception_to_throw_completion(main_thread_vm(), maybe_exception.exception());
// 2. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, completion). // 2. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, ThrowCompletion(resolutionError)).
auto completion = exception_to_throw_completion(main_thread_vm(), maybe_exception.exception());
JS::finish_loading_imported_module(referrer, module_request, payload, completion); JS::finish_loading_imported_module(referrer, module_request, payload, completion);
// 3. Return. // 3. Return.
@ -494,13 +494,15 @@ void initialize_main_thread_vm(AgentType type)
// 5. If the result of running the module type allowed steps given moduleType and moduleMapRealm is false, then: // 5. If the result of running the module type allowed steps given moduleType and moduleMapRealm is false, then:
if (!HTML::module_type_allowed(*module_map_realm, module_type)) { if (!HTML::module_type_allowed(*module_map_realm, module_type)) {
// 1. Let completion be Completion Record { [[Type]]: throw, [[Value]]: a new TypeError exception, [[Target]]: empty }. // 1. Let error be a new TypeError exception.
auto completion = JS::throw_completion(JS::SyntaxError::create(*module_map_realm, MUST(String::formatted("Module type '{}' is not supported", module_type)))); auto error = JS::TypeError::create(*module_map_realm, MUST(String::formatted("Module type '{}' is not supported", module_type)));
// 2. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, completion). // FIXME: 2. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to error.
JS::finish_loading_imported_module(referrer, module_request, payload, completion);
// 3. Return // 3. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, ThrowCompletion(error)).
JS::finish_loading_imported_module(referrer, module_request, payload, JS::throw_completion(error));
// 4. Return
return; return;
} }
@ -519,10 +521,10 @@ void initialize_main_thread_vm(AgentType type)
// 9. If the previous step threw an exception, then: // 9. If the previous step threw an exception, then:
if (url.is_exception()) { if (url.is_exception()) {
// 1. Let completion be Completion Record { [[Type]]: throw, [[Value]]: resolutionError, [[Target]]: empty }. // FIXME: 1. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to resolutionError.
auto completion = exception_to_throw_completion(main_thread_vm(), url.exception());
// 2. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, completion). // 2. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, ThrowCompletion(resolutionError)).
auto completion = exception_to_throw_completion(main_thread_vm(), url.exception());
HTML::TemporaryExecutionContext context { *module_map_realm }; HTML::TemporaryExecutionContext context { *module_map_realm };
JS::finish_loading_imported_module(referrer, module_request, payload, completion); JS::finish_loading_imported_module(referrer, module_request, payload, completion);
@ -568,7 +570,7 @@ void initialize_main_thread_vm(AgentType type)
GC::Ptr<JS::Module> module = nullptr; GC::Ptr<JS::Module> module = nullptr;
auto completion = [&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Module>> { auto completion = [&]() -> JS::ThrowCompletionOr<GC::Ref<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 ThrowCompletion(a new TypeError).
if (!module_script) { if (!module_script) {
return JS::throw_completion(JS::TypeError::create(realm, ByteString::formatted("Loading imported module '{}' failed.", module_request.module_specifier))); return JS::throw_completion(JS::TypeError::create(realm, ByteString::formatted("Loading imported module '{}' failed.", module_request.module_specifier)));
} }
@ -577,7 +579,7 @@ void initialize_main_thread_vm(AgentType type)
// 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 ThrowCompletion(parseError).
auto completion = 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.
@ -590,7 +592,7 @@ void initialize_main_thread_vm(AgentType type)
return completion; return completion;
} }
// 4. Otherwise, set completion to Completion Record { [[Type]]: normal, [[Value]]: moduleScript's record, [[Target]]: empty }. // 4. Otherwise, set completion to NormalCompletion(moduleScript's record).
else { else {
module = static_cast<HTML::JavaScriptModuleScript&>(*module_script).record(); module = static_cast<HTML::JavaScriptModuleScript&>(*module_script).record();
return JS::ThrowCompletionOr<GC::Ref<JS::Module>>(*module); return JS::ThrowCompletionOr<GC::Ref<JS::Module>>(*module);

View file

@ -84,19 +84,22 @@ JS::Completion ClassicScript::run(RethrowErrors rethrow_errors, GC::Ptr<JS::Envi
if (can_run_script(realm) == RunScriptDecision::DoNotRun) if (can_run_script(realm) == RunScriptDecision::DoNotRun)
return JS::normal_completion(JS::js_undefined()); return JS::normal_completion(JS::js_undefined());
// 3. Prepare to run script given realm. // FIXME: 3. Record classic script execution start time given script.
// 4. Prepare to run script given realm.
prepare_to_run_script(realm); prepare_to_run_script(realm);
// 4. Let evaluationStatus be null. // 5. Let evaluationStatus be null.
JS::Completion evaluation_status; JS::Completion evaluation_status;
// 5. If script's error to rethrow is not null, then set evaluationStatus to Completion { [[Type]]: throw, [[Value]]: script's error to rethrow, [[Target]]: empty }. // 6. If script's error to rethrow is not null, then set evaluationStatus to ThrowCompletion(script's error to rethrow).
if (!error_to_rethrow().is_null()) { if (!error_to_rethrow().is_null()) {
evaluation_status = JS::Completion { JS::Completion::Type::Throw, error_to_rethrow() }; evaluation_status = JS::throw_completion(error_to_rethrow());
} else { }
// 7. Otherwise, set evaluationStatus to ScriptEvaluation(script's record).
else {
auto timer = Core::ElapsedTimer::start_new(); auto timer = Core::ElapsedTimer::start_new();
// 6. Otherwise, set evaluationStatus to ScriptEvaluation(script's record).
evaluation_status = vm().bytecode_interpreter().run(*m_script_record, lexical_environment_override); evaluation_status = vm().bytecode_interpreter().run(*m_script_record, lexical_environment_override);
// FIXME: If ScriptEvaluation does not complete because the user agent has aborted the running script, leave evaluationStatus as null. // FIXME: If ScriptEvaluation does not complete because the user agent has aborted the running script, leave evaluationStatus as null.
@ -104,7 +107,7 @@ JS::Completion ClassicScript::run(RethrowErrors rethrow_errors, GC::Ptr<JS::Envi
dbgln_if(HTML_SCRIPT_DEBUG, "ClassicScript: Finished running script {}, Duration: {}ms", filename(), timer.elapsed_milliseconds()); dbgln_if(HTML_SCRIPT_DEBUG, "ClassicScript: Finished running script {}, Duration: {}ms", filename(), timer.elapsed_milliseconds());
} }
// 7. If evaluationStatus is an abrupt completion, then: // 8. If evaluationStatus is an abrupt completion, then:
if (evaluation_status.is_abrupt()) { if (evaluation_status.is_abrupt()) {
// 1. If rethrow errors is true and script's muted errors is false, then: // 1. If rethrow errors is true and script's muted errors is false, then:
if (rethrow_errors == RethrowErrors::Yes && m_muted_errors == MutedErrors::No) { if (rethrow_errors == RethrowErrors::Yes && m_muted_errors == MutedErrors::No) {
@ -138,15 +141,15 @@ JS::Completion ClassicScript::run(RethrowErrors rethrow_errors, GC::Ptr<JS::Envi
return evaluation_status; return evaluation_status;
} }
// 8. Clean up after running script with realm. // 9. Clean up after running script with realm.
clean_up_after_running_script(realm); clean_up_after_running_script(realm);
// 9. If evaluationStatus is a normal completion, then return evaluationStatus. // 10. If evaluationStatus is a normal completion, then return evaluationStatus.
VERIFY(!evaluation_status.is_abrupt()); VERIFY(!evaluation_status.is_abrupt());
return evaluation_status; return evaluation_status;
// FIXME: 10. If we've reached this point, evaluationStatus was left as null because the script was aborted prematurely during evaluation. // FIXME: 11. If we've reached this point, evaluationStatus was left as null because the script was aborted prematurely during evaluation.
// Return Completion { [[Type]]: throw, [[Value]]: a new "QuotaExceededError" DOMException, [[Target]]: empty }. // Return ThrowCompletion(a new "QuotaExceededError" DOMException).
} }
ClassicScript::ClassicScript(URL::URL base_url, ByteString filename, JS::Realm& realm) ClassicScript::ClassicScript(URL::URL base_url, ByteString filename, JS::Realm& realm)