1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-10 09:07:35 +00:00

LibJS: Allow AsyncBlockStart to accept a SafeFunction

This is needed for the implementation of Array.fromAsync
This commit is contained in:
Shannon Booth 2023-07-16 18:04:01 +12:00 committed by Linus Groh
parent 98c4606544
commit 80b48b708f

View file

@ -2,6 +2,7 @@
* Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@serenityos.org> * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@serenityos.org>
* Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org> * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2023, Andreas Kling <kling@serenityos.org> * Copyright (c) 2023, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -773,9 +774,12 @@ void async_function_start(VM& vm, PromiseCapability const& promise_capability, T
// 27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ), https://tc39.es/ecma262/#sec-asyncblockstart // 27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ), https://tc39.es/ecma262/#sec-asyncblockstart
// 12.7.1.1 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ), https://tc39.es/proposal-explicit-resource-management/#sec-asyncblockstart // 12.7.1.1 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ), https://tc39.es/proposal-explicit-resource-management/#sec-asyncblockstart
// 1.2.1.1 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ), https://tc39.es/proposal-array-from-async/#sec-asyncblockstart
template<typename T> template<typename T>
void async_block_start(VM& vm, T const& async_body, PromiseCapability const& promise_capability, ExecutionContext& async_context) void async_block_start(VM& vm, T const& async_body, PromiseCapability const& promise_capability, ExecutionContext& async_context)
{ {
// NOTE: This function is a combination between two proposals, so does not exactly match spec steps of either.
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
// 1. Assert: promiseCapability is a PromiseCapability Record. // 1. Assert: promiseCapability is a PromiseCapability Record.
@ -785,42 +789,60 @@ void async_block_start(VM& vm, T const& async_body, PromiseCapability const& pro
// 3. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context the following steps will be performed: // 3. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context the following steps will be performed:
auto execution_steps = NativeFunction::create(realm, "", [&realm, &async_body, &promise_capability, &async_context](auto& vm) -> ThrowCompletionOr<Value> { auto execution_steps = NativeFunction::create(realm, "", [&realm, &async_body, &promise_capability, &async_context](auto& vm) -> ThrowCompletionOr<Value> {
// a. Let result be the result of evaluating asyncBody.
Completion result; Completion result;
if (auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists()) {
// FIXME: Cache this executable somewhere. // a. If asyncBody is a Parse Node, then
auto maybe_executable = Bytecode::compile(vm, async_body, FunctionKind::Async, "AsyncBlockStart"sv); if constexpr (!IsCallableWithArguments<T, Completion>) {
if (maybe_executable.is_error()) // a. Let result be the result of evaluating asyncBody.
result = maybe_executable.release_error(); if (auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists()) {
else // FIXME: Cache this executable somewhere.
result = bytecode_interpreter->run_and_return_frame(realm, *maybe_executable.value(), nullptr).value; auto maybe_executable = Bytecode::compile(vm, async_body, FunctionKind::Async, "AsyncBlockStart"sv);
} else { if (maybe_executable.is_error())
result = async_body->execute(vm.interpreter()); result = maybe_executable.release_error();
else
result = bytecode_interpreter->run_and_return_frame(realm, *maybe_executable.value(), nullptr).value;
} else {
result = async_body->execute(vm.interpreter());
}
}
// b. Else,
else {
(void)realm;
// i. Assert: asyncBody is an Abstract Closure with no parameters.
static_assert(IsCallableWithArguments<T, Completion>);
// ii. Let result be asyncBody().
result = async_body();
} }
// b. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. // c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done.
// c. Remove asyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. // d. Remove asyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
vm.pop_execution_context(); vm.pop_execution_context();
// d. Let env be asyncContext's LexicalEnvironment. // NOTE: This does not work for Array.fromAsync, likely due to conflicts between that proposal and Explicit Resource Management proposal.
auto env = async_context.lexical_environment; if constexpr (!IsCallableWithArguments<T, Completion>) {
VERIFY(is<DeclarativeEnvironment>(*env)); // e. Let env be asyncContext's LexicalEnvironment.
auto env = async_context.lexical_environment;
// e. Set result to DisposeResources(env, result). // f. Set result to DisposeResources(env, result).
result = dispose_resources(vm, static_cast<DeclarativeEnvironment*>(env.ptr()), result); result = dispose_resources(vm, verify_cast<DeclarativeEnvironment>(env.ptr()), result);
} else {
(void)async_context;
}
// f. If result.[[Type]] is normal, then // g. If result.[[Type]] is normal, then
if (result.type() == Completion::Type::Normal) { if (result.type() == Completion::Type::Normal) {
// i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). // i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »).
MUST(call(vm, *promise_capability.resolve(), js_undefined(), js_undefined())); MUST(call(vm, *promise_capability.resolve(), js_undefined(), js_undefined()));
} }
// g. Else if result.[[Type]] is return, then // h. Else if result.[[Type]] is return, then
else if (result.type() == Completion::Type::Return) { else if (result.type() == Completion::Type::Return) {
// i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). // i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »).
MUST(call(vm, *promise_capability.resolve(), js_undefined(), *result.value())); MUST(call(vm, *promise_capability.resolve(), js_undefined(), *result.value()));
} }
// h. Else, // i. Else,
else { else {
// i. Assert: result.[[Type]] is throw. // i. Assert: result.[[Type]] is throw.
VERIFY(result.type() == Completion::Type::Throw); VERIFY(result.type() == Completion::Type::Throw);
@ -828,7 +850,7 @@ void async_block_start(VM& vm, T const& async_body, PromiseCapability const& pro
// ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »). // ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).
MUST(call(vm, *promise_capability.reject(), js_undefined(), *result.value())); MUST(call(vm, *promise_capability.reject(), js_undefined(), *result.value()));
} }
// i. Return unused. // j. Return unused.
// NOTE: We don't support returning an empty/optional/unused value here. // NOTE: We don't support returning an empty/optional/unused value here.
return js_undefined(); return js_undefined();
}); });
@ -853,6 +875,9 @@ void async_block_start(VM& vm, T const& async_body, PromiseCapability const& pro
template void async_block_start(VM&, NonnullGCPtr<Statement const> const& async_body, PromiseCapability const&, ExecutionContext&); template void async_block_start(VM&, NonnullGCPtr<Statement const> const& async_body, PromiseCapability const&, ExecutionContext&);
template void async_function_start(VM&, PromiseCapability const&, NonnullGCPtr<Statement const> const& async_function_body); template void async_function_start(VM&, PromiseCapability const&, NonnullGCPtr<Statement const> const& async_function_body);
template void async_block_start(VM&, SafeFunction<Completion()> const& async_body, PromiseCapability const&, ExecutionContext&);
template void async_function_start(VM&, PromiseCapability const&, SafeFunction<Completion()> const& async_function_body);
// 10.2.1.4 OrdinaryCallEvaluateBody ( F, argumentsList ), https://tc39.es/ecma262/#sec-ordinarycallevaluatebody // 10.2.1.4 OrdinaryCallEvaluateBody ( F, argumentsList ), https://tc39.es/ecma262/#sec-ordinarycallevaluatebody
// 15.8.4 Runtime Semantics: EvaluateAsyncFunctionBody, https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody // 15.8.4 Runtime Semantics: EvaluateAsyncFunctionBody, https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody
Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()