1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 09:04:59 +00:00

LibJS: Make PromiseCapability GC-allocated

A struct with three raw pointers to other GC'd types is a pretty big
liability, let's just turn this into a Cell itself.
This comes with the additional benefit of being able to capture it in
a lambda effortlessly, without having to create handles for individual
members.
This commit is contained in:
Linus Groh 2022-10-02 12:11:30 +01:00
parent 0585029c30
commit fc9d587e39
29 changed files with 243 additions and 217 deletions

View file

@ -3361,10 +3361,10 @@ Completion ImportCall::execute(Interpreter& interpreter) const
if (!options_value.is_object()) {
auto* error = TypeError::create(realm, String::formatted(ErrorType::NotAnObject.message(), "ImportOptions"));
// i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
MUST(call(vm, *promise_capability.reject, js_undefined(), error));
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
// ii. Return promiseCapability.[[Promise]].
return Value { promise_capability.promise };
return Value { promise_capability->promise() };
}
// b. Let assertionsObj be Get(options, "assert").
@ -3377,10 +3377,10 @@ Completion ImportCall::execute(Interpreter& interpreter) const
if (!assertion_object.is_object()) {
auto* error = TypeError::create(realm, String::formatted(ErrorType::NotAnObject.message(), "ImportOptionsAssertions"));
// 1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
MUST(call(vm, *promise_capability.reject, js_undefined(), error));
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
// 2. Return promiseCapability.[[Promise]].
return Value { promise_capability.promise };
return Value { promise_capability->promise() };
}
// ii. Let keys be EnumerableOwnPropertyNames(assertionsObj, key).
@ -3402,10 +3402,10 @@ Completion ImportCall::execute(Interpreter& interpreter) const
if (!value.is_string()) {
auto* error = TypeError::create(realm, String::formatted(ErrorType::NotAString.message(), "Import Assertion option value"));
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
MUST(call(vm, *promise_capability.reject, js_undefined(), error));
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
// b. Return promiseCapability.[[Promise]].
return Value { promise_capability.promise };
return Value { promise_capability->promise() };
}
// 4. If supportedAssertions contains key, then
@ -3426,7 +3426,7 @@ Completion ImportCall::execute(Interpreter& interpreter) const
interpreter.vm().host_import_module_dynamically(referencing_script_or_module, move(request), promise_capability);
// 13. Return promiseCapability.[[Promise]].
return Value { promise_capability.promise };
return Value { promise_capability->promise() };
}
// 13.2.3.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-literals-runtime-semantics-evaluation

View file

@ -197,10 +197,9 @@ ThrowCompletionOr<Promise*> CyclicModule::evaluate(VM& vm)
}
// 4. If module.[[TopLevelCapability]] is not empty, then
if (m_top_level_capability.has_value()) {
if (m_top_level_capability != nullptr) {
// a. Return module.[[TopLevelCapability]].[[Promise]].
VERIFY(is<Promise>(*m_top_level_capability->promise));
return static_cast<Promise*>(m_top_level_capability->promise);
return verify_cast<Promise>(m_top_level_capability->promise().ptr());
}
// 5. Let stack be a new empty List.
@ -243,7 +242,7 @@ ThrowCompletionOr<Promise*> CyclicModule::evaluate(VM& vm)
VERIFY(m_evaluation_error.is_error() && same_value(*m_evaluation_error.throw_completion().value(), *result.throw_completion().value()));
// d. Perform ! Call(capability.[[Reject]], undefined, « result.[[Value]] »).
MUST(call(vm, m_top_level_capability->reject, js_undefined(), *result.throw_completion().value()));
MUST(call(vm, *m_top_level_capability->reject(), js_undefined(), *result.throw_completion().value()));
}
// 10. Else,
else {
@ -257,7 +256,7 @@ ThrowCompletionOr<Promise*> CyclicModule::evaluate(VM& vm)
// i. Assert: module.[[Status]] is evaluated.
VERIFY(m_status == ModuleStatus::Evaluated);
// ii. Perform ! Call(capability.[[Resolve]], undefined, « undefined »).
MUST(call(vm, m_top_level_capability->resolve, js_undefined(), js_undefined()));
MUST(call(vm, *m_top_level_capability->resolve(), js_undefined(), js_undefined()));
}
// d. Assert: stack is empty.
@ -265,8 +264,7 @@ ThrowCompletionOr<Promise*> CyclicModule::evaluate(VM& vm)
}
// 11. Return capability.[[Promise]].
VERIFY(is<Promise>(*m_top_level_capability->promise));
return static_cast<Promise*>(m_top_level_capability->promise);
return verify_cast<Promise>(m_top_level_capability->promise().ptr());
}
// 16.2.1.5.2.1 InnerModuleEvaluation ( module, stack, index ), https://tc39.es/ecma262/#sec-innermoduleevaluation
@ -432,7 +430,7 @@ ThrowCompletionOr<void> CyclicModule::initialize_environment(VM&)
VERIFY_NOT_REACHED();
}
ThrowCompletionOr<void> CyclicModule::execute_module(VM&, Optional<PromiseCapability>)
ThrowCompletionOr<void> CyclicModule::execute_module(VM&, GCPtr<PromiseCapability>)
{
// Note: In ecma262 this is never called on a cyclic module only on SourceTextModules.
// So this check is to make sure we don't accidentally call this.
@ -479,10 +477,8 @@ void CyclicModule::execute_async_module(VM& vm)
// 7. Let onRejected be CreateBuiltinFunction(rejectedClosure, 0, "", « »).
auto* on_rejected = NativeFunction::create(realm, move(rejected_closure), 0, "");
VERIFY(is<Promise>(*capability.promise));
// 8. Perform PerformPromiseThen(capability.[[Promise]], onFulfilled, onRejected).
static_cast<Promise*>(capability.promise)->perform_then(on_fulfilled, on_rejected, {});
verify_cast<Promise>(capability->promise().ptr())->perform_then(on_fulfilled, on_rejected, {});
// 9. Perform ! module.ExecuteModule(capability).
MUST(execute_module(vm, capability));
@ -555,12 +551,12 @@ void CyclicModule::async_module_execution_fulfilled(VM& vm)
m_status = ModuleStatus::Evaluated;
// 7. If module.[[TopLevelCapability]] is not empty, then
if (m_top_level_capability.has_value()) {
if (m_top_level_capability != nullptr) {
// a. Assert: module.[[CycleRoot]] is module.
VERIFY(m_cycle_root == this);
// b. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]], undefined, « undefined »).
MUST(call(vm, m_top_level_capability->resolve, js_undefined(), js_undefined()));
MUST(call(vm, *m_top_level_capability->resolve(), js_undefined(), js_undefined()));
}
// 8. Let execList be a new empty List.
@ -603,12 +599,12 @@ void CyclicModule::async_module_execution_fulfilled(VM& vm)
module->m_status = ModuleStatus::Evaluated;
// 2. If m.[[TopLevelCapability]] is not empty, then
if (module->m_top_level_capability.has_value()) {
if (module->m_top_level_capability != nullptr) {
// a. Assert: m.[[CycleRoot]] is m.
VERIFY(module->m_cycle_root == module);
// b. Perform ! Call(m.[[TopLevelCapability]].[[Resolve]], undefined, « undefined »).
MUST(call(vm, module->m_top_level_capability->resolve, js_undefined(), js_undefined()));
MUST(call(vm, *module->m_top_level_capability->resolve(), js_undefined(), js_undefined()));
}
}
}
@ -652,12 +648,12 @@ void CyclicModule::async_module_execution_rejected(VM& vm, Value error)
}
// 8. If module.[[TopLevelCapability]] is not empty, then
if (m_top_level_capability.has_value()) {
if (m_top_level_capability != nullptr) {
// a. Assert: module.[[CycleRoot]] is module.
VERIFY(m_cycle_root == this);
// b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]], undefined, « error »).
MUST(call(vm, m_top_level_capability->reject, js_undefined(), error));
MUST(call(vm, *m_top_level_capability->reject(), js_undefined(), error));
}
// 9. Return unused.

View file

@ -42,24 +42,24 @@ protected:
virtual ThrowCompletionOr<u32> inner_module_evaluation(VM& vm, Vector<Module*>& stack, u32 index) override;
virtual ThrowCompletionOr<void> initialize_environment(VM& vm);
virtual ThrowCompletionOr<void> execute_module(VM& vm, Optional<PromiseCapability> capability = {});
virtual ThrowCompletionOr<void> execute_module(VM& vm, GCPtr<PromiseCapability> capability = {});
void execute_async_module(VM& vm);
void gather_available_ancestors(Vector<CyclicModule*>& exec_list);
void async_module_execution_fulfilled(VM& vm);
void async_module_execution_rejected(VM& vm, Value error);
ModuleStatus m_status { ModuleStatus::Unlinked }; // [[Status]]
ThrowCompletionOr<void> m_evaluation_error; // [[EvaluationError]]
Optional<u32> m_dfs_index; // [[DFSIndex]]
Optional<u32> m_dfs_ancestor_index; // [[DFSAncestorIndex]]
Vector<ModuleRequest> m_requested_modules; // [[RequestedModules]]
CyclicModule* m_cycle_root { nullptr }; // [[CycleRoot]]
bool m_has_top_level_await { false }; // [[HasTLA]]
bool m_async_evaluation { false }; // [[AsyncEvaluation]]
Optional<PromiseCapability> m_top_level_capability; // [[TopLevelCapability]]
Vector<CyclicModule*> m_async_parent_modules; // [[AsyncParentModules]]
Optional<u32> m_pending_async_dependencies; // [[PendingAsyncDependencies]]
ModuleStatus m_status { ModuleStatus::Unlinked }; // [[Status]]
ThrowCompletionOr<void> m_evaluation_error; // [[EvaluationError]]
Optional<u32> m_dfs_index; // [[DFSIndex]]
Optional<u32> m_dfs_ancestor_index; // [[DFSAncestorIndex]]
Vector<ModuleRequest> m_requested_modules; // [[RequestedModules]]
CyclicModule* m_cycle_root { nullptr }; // [[CycleRoot]]
bool m_has_top_level_await { false }; // [[HasTLA]]
bool m_async_evaluation { false }; // [[AsyncEvaluation]]
GCPtr<PromiseCapability> m_top_level_capability; // [[TopLevelCapability]]
Vector<CyclicModule*> m_async_parent_modules; // [[AsyncParentModules]]
Optional<u32> m_pending_async_dependencies; // [[PendingAsyncDependencies]]
};
}

View file

@ -176,6 +176,7 @@ class Module;
class NativeFunction;
class ObjectEnvironment;
class PrimitiveString;
class PromiseCapability;
class PromiseReaction;
class PropertyAttributes;
class PropertyDescriptor;
@ -199,7 +200,6 @@ enum class DeclarationKind;
struct AlreadyResolved;
struct JobCallback;
struct ModuleRequest;
struct PromiseCapability;
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
class ProxyObject;

View file

@ -38,15 +38,15 @@ static Object* async_from_sync_iterator_continuation(VM& vm, Object& result, Pro
// 1. NOTE: Because promiseCapability is derived from the intrinsic %Promise%, the calls to promiseCapability.[[Reject]] entailed by the use IfAbruptRejectPromise below are guaranteed not to throw.
// 2. Let done be Completion(IteratorComplete(result)).
// 3. IfAbruptRejectPromise(done, promiseCapability).
auto done = TRY_OR_MUST_REJECT(vm, promise_capability, iterator_complete(vm, result));
auto done = TRY_OR_MUST_REJECT(vm, &promise_capability, iterator_complete(vm, result));
// 4. Let value be Completion(IteratorValue(result)).
// 5. IfAbruptRejectPromise(value, promiseCapability).
auto value = TRY_OR_MUST_REJECT(vm, promise_capability, iterator_value(vm, result));
auto value = TRY_OR_MUST_REJECT(vm, &promise_capability, iterator_value(vm, result));
// 6. Let valueWrapper be PromiseResolve(%Promise%, value).
// 7. IfAbruptRejectPromise(valueWrapper, promiseCapability).
auto value_wrapper = TRY_OR_MUST_REJECT(vm, promise_capability, promise_resolve(vm, *realm.intrinsics().promise_constructor(), value));
auto value_wrapper = TRY_OR_MUST_REJECT(vm, &promise_capability, promise_resolve(vm, *realm.intrinsics().promise_constructor(), value));
// 8. Let unwrap be a new Abstract Closure with parameters (value) that captures done and performs the following steps when called:
auto unwrap = [done](VM& vm) -> ThrowCompletionOr<Value> {
@ -59,10 +59,10 @@ static Object* async_from_sync_iterator_continuation(VM& vm, Object& result, Pro
auto* on_fulfilled = NativeFunction::create(realm, move(unwrap), 1, "");
// 11. Perform PerformPromiseThen(valueWrapper, onFulfilled, undefined, promiseCapability).
verify_cast<Promise>(value_wrapper)->perform_then(move(on_fulfilled), js_undefined(), promise_capability);
verify_cast<Promise>(value_wrapper)->perform_then(move(on_fulfilled), js_undefined(), &promise_capability);
// 12. Return promiseCapability.[[Promise]].
return promise_capability.promise;
return promise_capability.promise();
}
// 27.1.4.2.1 %AsyncFromSyncIteratorPrototype%.next ( [ value ] ), https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.next
@ -118,10 +118,10 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::return_)
auto* iter_result = create_iterator_result_object(vm, vm.argument(0), true);
// b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
MUST(call(vm, *promise_capability.resolve, js_undefined(), iter_result));
MUST(call(vm, *promise_capability->resolve(), js_undefined(), iter_result));
// c. Return promiseCapability.[[Promise]].
return promise_capability.promise;
return promise_capability->promise();
}
// 8. If value is present, then
@ -137,9 +137,9 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::return_)
if (!result.is_object()) {
auto* error = TypeError::create(realm, String::formatted(ErrorType::NotAnObject.message(), "SyncIteratorReturnResult"));
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
MUST(call(vm, *promise_capability.reject, js_undefined(), error));
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
// b. Return promiseCapability.[[Promise]].
return promise_capability.promise;
return promise_capability->promise();
}
// 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability).
@ -168,9 +168,9 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::throw_)
// 7. If throw is undefined, then
if (throw_method == nullptr) {
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
MUST(call(vm, *promise_capability.reject, js_undefined(), vm.argument(0)));
MUST(call(vm, *promise_capability->reject(), js_undefined(), vm.argument(0)));
// b. Return promiseCapability.[[Promise]].
return promise_capability.promise;
return promise_capability->promise();
}
// 8. If value is present, then
// a. Let result be Completion(Call(throw, syncIterator, « value »)).
@ -185,10 +185,10 @@ JS_DEFINE_NATIVE_FUNCTION(AsyncFromSyncIteratorPrototype::throw_)
if (!result.is_object()) {
auto* error = TypeError::create(realm, String::formatted(ErrorType::NotAnObject.message(), "SyncIteratorThrowResult"));
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
MUST(call(vm, *promise_capability.reject, js_undefined(), error));
MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
// b. Return promiseCapability.[[Promise]].
return promise_capability.promise;
return promise_capability->promise();
}
// 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability).

View file

@ -59,7 +59,8 @@ ThrowCompletionOr<Value> AsyncFunctionDriverWrapper::react_to_async_task_complet
if (TRY(result.get(vm, vm.names.done)).to_boolean())
return promise;
return promise->perform_then(m_on_fulfillment, m_on_rejection, PromiseCapability { promise, m_on_fulfillment, m_on_rejection });
auto promise_capability = PromiseCapability::create(vm, promise, m_on_fulfillment, m_on_rejection);
return promise->perform_then(m_on_fulfillment, m_on_rejection, promise_capability);
}
void AsyncFunctionDriverWrapper::visit_edges(Cell::Visitor& visitor)

View file

@ -21,9 +21,7 @@ void AsyncGenerator::visit_edges(Cell::Visitor& visitor)
for (auto const& request : m_async_generator_queue) {
if (request.completion.value().has_value())
visitor.visit(*request.completion.value());
visitor.visit(request.capability.promise);
visitor.visit(request.capability.reject);
visitor.visit(request.capability.resolve);
visitor.visit(request.capability);
}
}

View file

@ -13,8 +13,8 @@ namespace JS {
// 27.6.3.1 AsyncGeneratorRequest Records, https://tc39.es/ecma262/#sec-asyncgeneratorrequest-records
struct AsyncGeneratorRequest {
Completion completion; // [[Completion]]
PromiseCapability capability; // [[Capability]]
Completion completion; // [[Completion]]
NonnullGCPtr<PromiseCapability> capability; // [[Capability]]
};
}

View file

@ -743,12 +743,12 @@ void async_block_start(VM& vm, NonnullRefPtr<Statement> const& async_body, Promi
// d. If result.[[Type]] is normal, then
if (result.type() == Completion::Type::Normal) {
// 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()));
}
// e. Else if result.[[Type]] is return, then
else if (result.type() == Completion::Type::Return) {
// 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()));
}
// f. Else,
else {
@ -756,7 +756,7 @@ void async_block_start(VM& vm, NonnullRefPtr<Statement> const& async_body, Promi
VERIFY(result.type() == Completion::Type::Throw);
// 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()));
}
// g. Return unused.
// NOTE: We don't support returning an empty/optional/unused value here.
@ -877,7 +877,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
// 3. If declResult is an abrupt completion, then
if (declaration_result.is_throw_completion()) {
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « declResult.[[Value]] »).
MUST(call(vm, promise_capability.reject, js_undefined(), *declaration_result.throw_completion().value()));
MUST(call(vm, *promise_capability->reject(), js_undefined(), *declaration_result.throw_completion().value()));
}
// 4. Else,
else {
@ -886,7 +886,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
}
// 5. Return Completion Record { [[Type]]: return, [[Value]]: promiseCapability.[[Promise]], [[Target]]: empty }.
return Completion { Completion::Type::Return, promise_capability.promise, {} };
return Completion { Completion::Type::Return, promise_capability->promise(), {} };
}
}
VERIFY_NOT_REACHED();

View file

@ -36,10 +36,10 @@ ThrowCompletionOr<Object*> promise_resolve(VM& vm, Object& constructor, Value va
auto promise_capability = TRY(new_promise_capability(vm, &constructor));
// 3. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »).
(void)TRY(call(vm, *promise_capability.resolve, js_undefined(), value));
(void)TRY(call(vm, *promise_capability->resolve(), js_undefined(), value));
// 4. Return promiseCapability.[[Promise]].
return promise_capability.promise;
return promise_capability->promise().ptr();
}
Promise* Promise::create(Realm& realm)
@ -288,7 +288,7 @@ void Promise::trigger_reactions() const
}
// 27.2.5.4.1 PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] ), https://tc39.es/ecma262/#sec-performpromisethen
Value Promise::perform_then(Value on_fulfilled, Value on_rejected, Optional<PromiseCapability> result_capability)
Value Promise::perform_then(Value on_fulfilled, Value on_rejected, GCPtr<PromiseCapability> result_capability)
{
auto& vm = this->vm();
@ -377,7 +377,7 @@ Value Promise::perform_then(Value on_fulfilled, Value on_rejected, Optional<Prom
m_is_handled = true;
// 13. If resultCapability is undefined, then
if (!result_capability.has_value()) {
if (result_capability == nullptr) {
// a. Return undefined.
dbgln_if(PROMISE_DEBUG, "[Promise @ {} / perform_then()]: No result PromiseCapability, returning undefined", this);
return js_undefined();
@ -385,9 +385,8 @@ Value Promise::perform_then(Value on_fulfilled, Value on_rejected, Optional<Prom
// 14. Else,
// a. Return resultCapability.[[Promise]].
auto* promise = result_capability.value().promise;
dbgln_if(PROMISE_DEBUG, "[Promise @ {} / perform_then()]: Returning Promise @ {} from result PromiseCapability @ {}", this, promise, &result_capability.value());
return promise;
dbgln_if(PROMISE_DEBUG, "[Promise @ {} / perform_then()]: Returning Promise @ {} from result PromiseCapability @ {}", this, result_capability->promise().ptr(), result_capability.ptr());
return result_capability->promise();
}
void Promise::visit_edges(Cell::Visitor& visitor)

View file

@ -42,7 +42,7 @@ public:
void fulfill(Value value);
void reject(Value reason);
Value perform_then(Value on_fulfilled, Value on_rejected, Optional<PromiseCapability> result_capability);
Value perform_then(Value on_fulfilled, Value on_rejected, GCPtr<PromiseCapability> result_capability);
bool is_handled() const { return m_is_handled; }
void set_is_handled() { m_is_handled = true; }

View file

@ -11,8 +11,27 @@
namespace JS {
NonnullGCPtr<PromiseCapability> PromiseCapability::create(VM& vm, GCPtr<Object> promise, GCPtr<FunctionObject> resolve, GCPtr<FunctionObject> reject)
{
return NonnullGCPtr { *vm.heap().allocate_without_realm<PromiseCapability>(promise, resolve, reject) };
}
PromiseCapability::PromiseCapability(GCPtr<Object> promise, GCPtr<FunctionObject> resolve, GCPtr<FunctionObject> reject)
: m_promise(promise)
, m_resolve(resolve)
, m_reject(reject)
{
}
void PromiseCapability::visit_edges(Cell::Visitor& visitor)
{
visitor.visit(m_promise);
visitor.visit(m_resolve);
visitor.visit(m_reject);
}
// 27.2.1.5 NewPromiseCapability ( C ), https://tc39.es/ecma262/#sec-newpromisecapability
ThrowCompletionOr<PromiseCapability> new_promise_capability(VM& vm, Value constructor)
ThrowCompletionOr<NonnullGCPtr<PromiseCapability>> new_promise_capability(VM& vm, Value constructor)
{
auto& realm = *vm.current_realm();
@ -70,11 +89,11 @@ ThrowCompletionOr<PromiseCapability> new_promise_capability(VM& vm, Value constr
// 9. Set promiseCapability.[[Promise]] to promise.
// 10. Return promiseCapability.
return PromiseCapability {
return PromiseCapability::create(
vm,
promise,
&promise_capability_functions.resolve.as_function(),
&promise_capability_functions.reject.as_function(),
};
&promise_capability_functions.reject.as_function());
}
}

View file

@ -13,27 +13,48 @@
namespace JS {
// 27.2.1.1 PromiseCapability Records, https://tc39.es/ecma262/#sec-promisecapability-records
struct PromiseCapability {
Object* promise { nullptr };
FunctionObject* resolve { nullptr };
FunctionObject* reject { nullptr };
class PromiseCapability final : public Cell {
JS_CELL(PromiseCapability, Cell);
public:
static NonnullGCPtr<PromiseCapability> create(VM& vm, GCPtr<Object> promise, GCPtr<FunctionObject> resolve, GCPtr<FunctionObject> reject);
virtual ~PromiseCapability() = default;
[[nodiscard]] GCPtr<Object> promise() const { return m_promise; }
void set_promise(NonnullGCPtr<Object> promise) { m_promise = promise; }
[[nodiscard]] GCPtr<FunctionObject> resolve() const { return m_resolve; }
void set_resolve(NonnullGCPtr<FunctionObject> resolve) { m_resolve = resolve; }
[[nodiscard]] GCPtr<FunctionObject> reject() const { return m_reject; }
void set_reject(NonnullGCPtr<FunctionObject> reject) { m_reject = reject; }
private:
PromiseCapability(GCPtr<Object>, GCPtr<FunctionObject>, GCPtr<FunctionObject>);
virtual void visit_edges(Visitor&) override;
GCPtr<Object> m_promise;
GCPtr<FunctionObject> m_resolve;
GCPtr<FunctionObject> m_reject;
};
// 27.2.1.1.1 IfAbruptRejectPromise ( value, capability ), https://tc39.es/ecma262/#sec-ifabruptrejectpromise
#define __TRY_OR_REJECT(vm, capability, expression, CALL_CHECK) \
({ \
auto _temporary_try_or_reject_result = (expression); \
/* 1. If value is an abrupt completion, then */ \
if (_temporary_try_or_reject_result.is_error()) { \
/* a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). */ \
CALL_CHECK(JS::call(vm, *capability.reject, js_undefined(), *_temporary_try_or_reject_result.release_error().value())); \
\
/* b. Return capability.[[Promise]]. */ \
return capability.promise; \
} \
\
/* 2. Else if value is a Completion Record, set value to value.[[Value]]. */ \
_temporary_try_or_reject_result.release_value(); \
#define __TRY_OR_REJECT(vm, capability, expression, CALL_CHECK) \
({ \
auto _temporary_try_or_reject_result = (expression); \
/* 1. If value is an abrupt completion, then */ \
if (_temporary_try_or_reject_result.is_error()) { \
/* a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). */ \
CALL_CHECK(JS::call(vm, *(capability)->reject(), js_undefined(), *_temporary_try_or_reject_result.release_error().value())); \
\
/* b. Return capability.[[Promise]]. */ \
return (capability)->promise(); \
} \
\
/* 2. Else if value is a Completion Record, set value to value.[[Value]]. */ \
_temporary_try_or_reject_result.release_value(); \
})
#define TRY_OR_REJECT(vm, capability, expression) \
@ -43,23 +64,23 @@ struct PromiseCapability {
__TRY_OR_REJECT(vm, capability, expression, MUST)
// 27.2.1.1.1 IfAbruptRejectPromise ( value, capability ), https://tc39.es/ecma262/#sec-ifabruptrejectpromise
#define TRY_OR_REJECT_WITH_VALUE(vm, capability, expression) \
({ \
auto _temporary_try_or_reject_result = (expression); \
/* 1. If value is an abrupt completion, then */ \
if (_temporary_try_or_reject_result.is_error()) { \
/* a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). */ \
TRY(JS::call(vm, *capability.reject, js_undefined(), *_temporary_try_or_reject_result.release_error().value())); \
\
/* b. Return capability.[[Promise]]. */ \
return Value { capability.promise }; \
} \
\
/* 2. Else if value is a Completion Record, set value to value.[[Value]]. */ \
_temporary_try_or_reject_result.release_value(); \
#define TRY_OR_REJECT_WITH_VALUE(vm, capability, expression) \
({ \
auto _temporary_try_or_reject_result = (expression); \
/* 1. If value is an abrupt completion, then */ \
if (_temporary_try_or_reject_result.is_error()) { \
/* a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). */ \
TRY(JS::call(vm, *(capability)->reject(), js_undefined(), *_temporary_try_or_reject_result.release_error().value())); \
\
/* b. Return capability.[[Promise]]. */ \
return Value { (capability)->promise() }; \
} \
\
/* 2. Else if value is a Completion Record, set value to value.[[Value]]. */ \
_temporary_try_or_reject_result.release_value(); \
})
// 27.2.1.5 NewPromiseCapability ( C ), https://tc39.es/ecma262/#sec-newpromisecapability
ThrowCompletionOr<PromiseCapability> new_promise_capability(VM& vm, Value constructor);
ThrowCompletionOr<NonnullGCPtr<PromiseCapability>> new_promise_capability(VM& vm, Value constructor);
}

View file

@ -39,7 +39,7 @@ static ThrowCompletionOr<Value> get_promise_resolve(VM& vm, Value constructor)
using EndOfElementsCallback = Function<ThrowCompletionOr<Value>(PromiseValueList&)>;
using InvokeElementFunctionCallback = Function<ThrowCompletionOr<Value>(PromiseValueList&, RemainingElements&, Value, size_t)>;
static ThrowCompletionOr<Value> perform_promise_common(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability result_capability, Value promise_resolve, EndOfElementsCallback end_of_list, InvokeElementFunctionCallback invoke_element_function)
static ThrowCompletionOr<Value> perform_promise_common(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability const& result_capability, Value promise_resolve, EndOfElementsCallback end_of_list, InvokeElementFunctionCallback invoke_element_function)
{
VERIFY(constructor.is_constructor());
VERIFY(promise_resolve.is_function());
@ -79,7 +79,7 @@ static ThrowCompletionOr<Value> perform_promise_common(VM& vm, Iterator& iterato
}
// iv. Return resultCapability.[[Promise]].
return result_capability.promise;
return result_capability.promise();
}
// e. Let nextValue be Completion(IteratorValue(next)).
@ -113,7 +113,7 @@ static ThrowCompletionOr<Value> perform_promise_common(VM& vm, Iterator& iterato
}
// 27.2.4.1.2 PerformPromiseAll ( iteratorRecord, constructor, resultCapability, promiseResolve ), https://tc39.es/ecma262/#sec-performpromiseall
static ThrowCompletionOr<Value> perform_promise_all(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability result_capability, Value promise_resolve)
static ThrowCompletionOr<Value> perform_promise_all(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability const& result_capability, Value promise_resolve)
{
auto& realm = *vm.current_realm();
@ -124,10 +124,10 @@ static ThrowCompletionOr<Value> perform_promise_all(VM& vm, Iterator& iterator_r
auto* values_array = Array::create_from(realm, values.values());
// 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
TRY(call(vm, *result_capability.resolve, js_undefined(), values_array));
TRY(call(vm, *result_capability.resolve(), js_undefined(), values_array));
// iv. Return resultCapability.[[Promise]].
return Value(result_capability.promise);
return result_capability.promise();
},
[&](PromiseValueList& values, RemainingElements& remaining_elements_count, Value next_promise, size_t index) {
// j. Let steps be the algorithm steps defined in Promise.all Resolve Element Functions.
@ -142,12 +142,12 @@ static ThrowCompletionOr<Value> perform_promise_all(VM& vm, Iterator& iterator_r
on_fulfilled->define_direct_property(vm.names.name, js_string(vm, String::empty()), Attribute::Configurable);
// s. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »).
return next_promise.invoke(vm, vm.names.then, on_fulfilled, result_capability.reject);
return next_promise.invoke(vm, vm.names.then, on_fulfilled, result_capability.reject());
});
}
// 27.2.4.2.1 PerformPromiseAllSettled ( iteratorRecord, constructor, resultCapability, promiseResolve ), https://tc39.es/ecma262/#sec-performpromiseallsettled
static ThrowCompletionOr<Value> perform_promise_all_settled(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability result_capability, Value promise_resolve)
static ThrowCompletionOr<Value> perform_promise_all_settled(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability const& result_capability, Value promise_resolve)
{
auto& realm = *vm.current_realm();
@ -156,9 +156,9 @@ static ThrowCompletionOr<Value> perform_promise_all_settled(VM& vm, Iterator& it
[&](PromiseValueList& values) -> ThrowCompletionOr<Value> {
auto* values_array = Array::create_from(realm, values.values());
TRY(call(vm, *result_capability.resolve, js_undefined(), values_array));
TRY(call(vm, *result_capability.resolve(), js_undefined(), values_array));
return Value(result_capability.promise);
return result_capability.promise();
},
[&](PromiseValueList& values, RemainingElements& remaining_elements_count, Value next_promise, size_t index) {
// j. Let stepsFulfilled be the algorithm steps defined in Promise.allSettled Resolve Element Functions.
@ -190,7 +190,7 @@ static ThrowCompletionOr<Value> perform_promise_all_settled(VM& vm, Iterator& it
}
// 27.2.4.3.1 PerformPromiseAny ( iteratorRecord, constructor, resultCapability, promiseResolve ), https://tc39.es/ecma262/#sec-performpromiseany
static ThrowCompletionOr<Value> perform_promise_any(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability result_capability, Value promise_resolve)
static ThrowCompletionOr<Value> perform_promise_any(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability const& result_capability, Value promise_resolve)
{
auto& realm = *vm.current_realm();
@ -220,22 +220,22 @@ static ThrowCompletionOr<Value> perform_promise_any(VM& vm, Iterator& iterator_r
on_rejected->define_direct_property(vm.names.name, js_string(vm, String::empty()), Attribute::Configurable);
// s. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], onRejected »).
return next_promise.invoke(vm, vm.names.then, result_capability.resolve, on_rejected);
return next_promise.invoke(vm, vm.names.then, result_capability.resolve(), on_rejected);
});
}
// 27.2.4.5.1 PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve ), https://tc39.es/ecma262/#sec-performpromiserace
static ThrowCompletionOr<Value> perform_promise_race(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability result_capability, Value promise_resolve)
static ThrowCompletionOr<Value> perform_promise_race(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability const& result_capability, Value promise_resolve)
{
return perform_promise_common(
vm, iterator_record, constructor, result_capability, promise_resolve,
[&](PromiseValueList&) -> ThrowCompletionOr<Value> {
// ii. Return resultCapability.[[Promise]].
return Value(result_capability.promise);
return result_capability.promise();
},
[&](PromiseValueList&, RemainingElements&, Value next_promise, size_t) {
// i. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »).
return next_promise.invoke(vm, vm.names.then, result_capability.resolve, result_capability.reject);
return next_promise.invoke(vm, vm.names.then, result_capability.resolve(), result_capability.reject());
});
}
@ -456,10 +456,10 @@ JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::reject)
auto promise_capability = TRY(new_promise_capability(vm, constructor));
// 3. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »).
[[maybe_unused]] auto result = TRY(JS::call(vm, *promise_capability.reject, js_undefined(), reason));
[[maybe_unused]] auto result = TRY(JS::call(vm, *promise_capability->reject(), js_undefined(), reason));
// 4. Return promiseCapability.[[Promise]].
return promise_capability.promise;
return promise_capability->promise();
}
// 27.2.4.7 Promise.resolve ( x ), https://tc39.es/ecma262/#sec-promise.resolve

View file

@ -20,7 +20,7 @@ namespace JS {
static ThrowCompletionOr<Value> run_reaction_job(VM& vm, PromiseReaction& reaction, Value argument)
{
// a. Let promiseCapability be reaction.[[Capability]].
auto& promise_capability = reaction.capability();
auto promise_capability = reaction.capability();
// b. Let type be reaction.[[Type]].
auto type = reaction.type();
@ -58,7 +58,7 @@ static ThrowCompletionOr<Value> run_reaction_job(VM& vm, PromiseReaction& reacti
}
// f. If promiseCapability is undefined, then
if (!promise_capability.has_value()) {
if (promise_capability == nullptr) {
// i. Assert: handlerResult is not an abrupt completion.
VERIFY(!handler_result.is_abrupt());
@ -74,15 +74,15 @@ static ThrowCompletionOr<Value> run_reaction_job(VM& vm, PromiseReaction& reacti
// h. If handlerResult is an abrupt completion, then
if (handler_result.is_abrupt()) {
// i. Return ? Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »).
auto* reject_function = promise_capability.value().reject;
dbgln_if(PROMISE_DEBUG, "run_reaction_job: Calling PromiseCapability's reject function @ {}", reject_function);
auto reject_function = promise_capability->reject();
dbgln_if(PROMISE_DEBUG, "run_reaction_job: Calling PromiseCapability's reject function @ {}", reject_function.ptr());
return call(vm, *reject_function, js_undefined(), *handler_result.value());
}
// i. Else,
else {
// i. Return ? Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »).
auto* resolve_function = promise_capability.value().resolve;
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob]: Calling PromiseCapability's resolve function @ {}", resolve_function);
auto resolve_function = promise_capability->resolve();
dbgln_if(PROMISE_DEBUG, "[PromiseReactionJob]: Calling PromiseCapability's resolve function @ {}", resolve_function.ptr());
return call(vm, *resolve_function, js_undefined(), *handler_result.value());
}
}
@ -165,9 +165,8 @@ PromiseJob create_promise_resolve_thenable_job(VM& vm, Promise& promise_to_resol
VERIFY(then_realm);
// 1. Let job be a new Job Abstract Closure with no parameters that captures promiseToResolve, thenable, and then and performs the following steps when called:
// See PromiseResolveThenableJob::call() for "the following steps".
// NOTE: This is done out of order, since `then` is moved into the lambda and `then` would be invalid if it was done at the start.
auto job = [&vm, promise_to_resolve = make_handle(&promise_to_resolve), thenable = make_handle(thenable), then = move(then)]() mutable {
// See run_resolve_thenable_job() for "the following steps".
auto job = [&vm, promise_to_resolve = make_handle(promise_to_resolve), thenable = make_handle(thenable), then = move(then)]() mutable {
return run_resolve_thenable_job(vm, *promise_to_resolve.cell(), thenable.value(), then);
};

View file

@ -10,14 +10,14 @@
namespace JS {
PromiseReaction* PromiseReaction::create(VM& vm, Type type, Optional<PromiseCapability> capability, Optional<JobCallback> handler)
PromiseReaction* PromiseReaction::create(VM& vm, Type type, GCPtr<PromiseCapability> capability, Optional<JobCallback> handler)
{
return vm.heap().allocate_without_realm<PromiseReaction>(type, move(capability), move(handler));
return vm.heap().allocate_without_realm<PromiseReaction>(type, capability, move(handler));
}
PromiseReaction::PromiseReaction(Type type, Optional<PromiseCapability> capability, Optional<JobCallback> handler)
PromiseReaction::PromiseReaction(Type type, GCPtr<PromiseCapability> capability, Optional<JobCallback> handler)
: m_type(type)
, m_capability(move(capability))
, m_capability(capability)
, m_handler(move(handler))
{
}
@ -25,12 +25,7 @@ PromiseReaction::PromiseReaction(Type type, Optional<PromiseCapability> capabili
void PromiseReaction::visit_edges(Cell::Visitor& visitor)
{
Cell::visit_edges(visitor);
if (m_capability.has_value()) {
auto& capability = m_capability.value();
visitor.visit(capability.promise);
visitor.visit(capability.resolve);
visitor.visit(capability.reject);
}
visitor.visit(m_capability);
}
}

View file

@ -23,23 +23,23 @@ public:
Reject,
};
static PromiseReaction* create(VM& vm, Type type, Optional<PromiseCapability> capability, Optional<JobCallback> handler);
static PromiseReaction* create(VM& vm, Type type, GCPtr<PromiseCapability> capability, Optional<JobCallback> handler);
virtual ~PromiseReaction() = default;
Type type() const { return m_type; }
Optional<PromiseCapability> const& capability() const { return m_capability; }
GCPtr<PromiseCapability> capability() const { return m_capability; }
Optional<JobCallback>& handler() { return m_handler; }
Optional<JobCallback> const& handler() const { return m_handler; }
private:
PromiseReaction(Type type, Optional<PromiseCapability> capability, Optional<JobCallback> handler);
PromiseReaction(Type type, GCPtr<PromiseCapability> capability, Optional<JobCallback> handler);
virtual void visit_edges(Visitor&) override;
Type m_type;
Optional<PromiseCapability> m_capability;
GCPtr<PromiseCapability> m_capability;
Optional<JobCallback> m_handler;
};

View file

@ -20,11 +20,11 @@ void PromiseValueList::visit_edges(Visitor& visitor)
visitor.visit(val);
}
PromiseResolvingElementFunction::PromiseResolvingElementFunction(size_t index, PromiseValueList& values, PromiseCapability capability, RemainingElements& remaining_elements, Object& prototype)
PromiseResolvingElementFunction::PromiseResolvingElementFunction(size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability> capability, RemainingElements& remaining_elements, Object& prototype)
: NativeFunction(prototype)
, m_index(index)
, m_values(values)
, m_capability(move(capability))
, m_capability(capability)
, m_remaining_elements(remaining_elements)
{
}
@ -49,19 +49,17 @@ void PromiseResolvingElementFunction::visit_edges(Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(&m_values);
visitor.visit(m_capability.promise);
visitor.visit(m_capability.resolve);
visitor.visit(m_capability.reject);
visitor.visit(m_capability);
visitor.visit(&m_remaining_elements);
}
PromiseAllResolveElementFunction* PromiseAllResolveElementFunction::create(Realm& realm, size_t index, PromiseValueList& values, PromiseCapability capability, RemainingElements& remaining_elements)
PromiseAllResolveElementFunction* PromiseAllResolveElementFunction::create(Realm& realm, size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability> capability, RemainingElements& remaining_elements)
{
return realm.heap().allocate<PromiseAllResolveElementFunction>(realm, index, values, capability, remaining_elements, *realm.intrinsics().function_prototype());
}
PromiseAllResolveElementFunction::PromiseAllResolveElementFunction(size_t index, PromiseValueList& values, PromiseCapability capability, RemainingElements& remaining_elements, Object& prototype)
: PromiseResolvingElementFunction(index, values, move(capability), remaining_elements, prototype)
PromiseAllResolveElementFunction::PromiseAllResolveElementFunction(size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability> capability, RemainingElements& remaining_elements, Object& prototype)
: PromiseResolvingElementFunction(index, values, capability, remaining_elements, prototype)
{
}
@ -80,20 +78,20 @@ ThrowCompletionOr<Value> PromiseAllResolveElementFunction::resolve_element()
auto* values_array = Array::create_from(realm, m_values.values());
// b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »).
return JS::call(vm, *m_capability.resolve, js_undefined(), values_array);
return JS::call(vm, *m_capability->resolve(), js_undefined(), values_array);
}
// 11. Return undefined.
return js_undefined();
}
PromiseAllSettledResolveElementFunction* PromiseAllSettledResolveElementFunction::create(Realm& realm, size_t index, PromiseValueList& values, PromiseCapability capability, RemainingElements& remaining_elements)
PromiseAllSettledResolveElementFunction* PromiseAllSettledResolveElementFunction::create(Realm& realm, size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability> capability, RemainingElements& remaining_elements)
{
return realm.heap().allocate<PromiseAllSettledResolveElementFunction>(realm, index, values, capability, remaining_elements, *realm.intrinsics().function_prototype());
}
PromiseAllSettledResolveElementFunction::PromiseAllSettledResolveElementFunction(size_t index, PromiseValueList& values, PromiseCapability capability, RemainingElements& remaining_elements, Object& prototype)
: PromiseResolvingElementFunction(index, values, move(capability), remaining_elements, prototype)
PromiseAllSettledResolveElementFunction::PromiseAllSettledResolveElementFunction(size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability> capability, RemainingElements& remaining_elements, Object& prototype)
: PromiseResolvingElementFunction(index, values, capability, remaining_elements, prototype)
{
}
@ -121,20 +119,20 @@ ThrowCompletionOr<Value> PromiseAllSettledResolveElementFunction::resolve_elemen
auto* values_array = Array::create_from(realm, m_values.values());
// b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »).
return JS::call(vm, *m_capability.resolve, js_undefined(), values_array);
return JS::call(vm, *m_capability->resolve(), js_undefined(), values_array);
}
// 15. Return undefined.
return js_undefined();
}
PromiseAllSettledRejectElementFunction* PromiseAllSettledRejectElementFunction::create(Realm& realm, size_t index, PromiseValueList& values, PromiseCapability capability, RemainingElements& remaining_elements)
PromiseAllSettledRejectElementFunction* PromiseAllSettledRejectElementFunction::create(Realm& realm, size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability> capability, RemainingElements& remaining_elements)
{
return realm.heap().allocate<PromiseAllSettledRejectElementFunction>(realm, index, values, capability, remaining_elements, *realm.intrinsics().function_prototype());
}
PromiseAllSettledRejectElementFunction::PromiseAllSettledRejectElementFunction(size_t index, PromiseValueList& values, PromiseCapability capability, RemainingElements& remaining_elements, Object& prototype)
: PromiseResolvingElementFunction(index, values, move(capability), remaining_elements, prototype)
PromiseAllSettledRejectElementFunction::PromiseAllSettledRejectElementFunction(size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability> capability, RemainingElements& remaining_elements, Object& prototype)
: PromiseResolvingElementFunction(index, values, capability, remaining_elements, prototype)
{
}
@ -162,20 +160,20 @@ ThrowCompletionOr<Value> PromiseAllSettledRejectElementFunction::resolve_element
auto* values_array = Array::create_from(realm, m_values.values());
// b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »).
return JS::call(vm, *m_capability.resolve, js_undefined(), values_array);
return JS::call(vm, *m_capability->resolve(), js_undefined(), values_array);
}
// 15. Return undefined.
return js_undefined();
}
PromiseAnyRejectElementFunction* PromiseAnyRejectElementFunction::create(Realm& realm, size_t index, PromiseValueList& errors, PromiseCapability capability, RemainingElements& remaining_elements)
PromiseAnyRejectElementFunction* PromiseAnyRejectElementFunction::create(Realm& realm, size_t index, PromiseValueList& errors, NonnullGCPtr<PromiseCapability> capability, RemainingElements& remaining_elements)
{
return realm.heap().allocate<PromiseAnyRejectElementFunction>(realm, index, errors, capability, remaining_elements, *realm.intrinsics().function_prototype());
}
PromiseAnyRejectElementFunction::PromiseAnyRejectElementFunction(size_t index, PromiseValueList& errors, PromiseCapability capability, RemainingElements& remaining_elements, Object& prototype)
: PromiseResolvingElementFunction(index, errors, move(capability), remaining_elements, prototype)
PromiseAnyRejectElementFunction::PromiseAnyRejectElementFunction(size_t index, PromiseValueList& errors, NonnullGCPtr<PromiseCapability> capability, RemainingElements& remaining_elements, Object& prototype)
: PromiseResolvingElementFunction(index, errors, capability, remaining_elements, prototype)
{
}
@ -198,7 +196,7 @@ ThrowCompletionOr<Value> PromiseAnyRejectElementFunction::resolve_element()
MUST(error->define_property_or_throw(vm.names.errors, { .value = errors_array, .writable = true, .enumerable = false, .configurable = true }));
// c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »).
return JS::call(vm, *m_capability.reject, js_undefined(), error);
return JS::call(vm, *m_capability->reject(), js_undefined(), error);
}
return js_undefined();

View file

@ -51,13 +51,13 @@ public:
virtual ThrowCompletionOr<Value> call() override;
protected:
explicit PromiseResolvingElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype);
explicit PromiseResolvingElementFunction(size_t, PromiseValueList&, NonnullGCPtr<PromiseCapability>, RemainingElements&, Object& prototype);
virtual ThrowCompletionOr<Value> resolve_element() = 0;
size_t m_index { 0 };
PromiseValueList& m_values;
PromiseCapability m_capability;
NonnullGCPtr<PromiseCapability> m_capability;
RemainingElements& m_remaining_elements;
private:
@ -71,12 +71,12 @@ class PromiseAllResolveElementFunction final : public PromiseResolvingElementFun
JS_OBJECT(PromiseResolvingFunction, NativeFunction);
public:
static PromiseAllResolveElementFunction* create(Realm&, size_t, PromiseValueList&, PromiseCapability, RemainingElements&);
static PromiseAllResolveElementFunction* create(Realm&, size_t, PromiseValueList&, NonnullGCPtr<PromiseCapability>, RemainingElements&);
virtual ~PromiseAllResolveElementFunction() override = default;
private:
explicit PromiseAllResolveElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype);
explicit PromiseAllResolveElementFunction(size_t, PromiseValueList&, NonnullGCPtr<PromiseCapability>, RemainingElements&, Object& prototype);
virtual ThrowCompletionOr<Value> resolve_element() override;
};
@ -86,12 +86,12 @@ class PromiseAllSettledResolveElementFunction final : public PromiseResolvingEle
JS_OBJECT(PromiseResolvingFunction, NativeFunction);
public:
static PromiseAllSettledResolveElementFunction* create(Realm&, size_t, PromiseValueList&, PromiseCapability, RemainingElements&);
static PromiseAllSettledResolveElementFunction* create(Realm&, size_t, PromiseValueList&, NonnullGCPtr<PromiseCapability>, RemainingElements&);
virtual ~PromiseAllSettledResolveElementFunction() override = default;
private:
explicit PromiseAllSettledResolveElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype);
explicit PromiseAllSettledResolveElementFunction(size_t, PromiseValueList&, NonnullGCPtr<PromiseCapability>, RemainingElements&, Object& prototype);
virtual ThrowCompletionOr<Value> resolve_element() override;
};
@ -101,12 +101,12 @@ class PromiseAllSettledRejectElementFunction final : public PromiseResolvingElem
JS_OBJECT(PromiseResolvingFunction, PromiseResolvingElementFunction);
public:
static PromiseAllSettledRejectElementFunction* create(Realm&, size_t, PromiseValueList&, PromiseCapability, RemainingElements&);
static PromiseAllSettledRejectElementFunction* create(Realm&, size_t, PromiseValueList&, NonnullGCPtr<PromiseCapability>, RemainingElements&);
virtual ~PromiseAllSettledRejectElementFunction() override = default;
private:
explicit PromiseAllSettledRejectElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype);
explicit PromiseAllSettledRejectElementFunction(size_t, PromiseValueList&, NonnullGCPtr<PromiseCapability>, RemainingElements&, Object& prototype);
virtual ThrowCompletionOr<Value> resolve_element() override;
};
@ -116,12 +116,12 @@ class PromiseAnyRejectElementFunction final : public PromiseResolvingElementFunc
JS_OBJECT(PromiseResolvingFunction, PromiseResolvingElementFunction);
public:
static PromiseAnyRejectElementFunction* create(Realm&, size_t, PromiseValueList&, PromiseCapability, RemainingElements&);
static PromiseAnyRejectElementFunction* create(Realm&, size_t, PromiseValueList&, NonnullGCPtr<PromiseCapability>, RemainingElements&);
virtual ~PromiseAnyRejectElementFunction() override = default;
private:
explicit PromiseAnyRejectElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype);
explicit PromiseAnyRejectElementFunction(size_t, PromiseValueList&, NonnullGCPtr<PromiseCapability>, RemainingElements&, Object& prototype);
virtual ThrowCompletionOr<Value> resolve_element() override;
};

View file

@ -276,7 +276,7 @@ ThrowCompletionOr<Value> shadow_realm_import_value(VM& vm, String specifier_stri
});
// 13. Return PerformPromiseThen(innerCapability.[[Promise]], onFulfilled, callerRealm.[[Intrinsics]].[[%ThrowTypeError%]], promiseCapability).
return verify_cast<Promise>(inner_capability.promise)->perform_then(on_fulfilled, throw_type_error, promise_capability);
return verify_cast<Promise>(inner_capability->promise().ptr())->perform_then(on_fulfilled, throw_type_error, promise_capability);
}
// 3.1.5 GetWrappedValue ( callerRealm: a Realm Record, value: unknown, ), https://tc39.es/proposal-shadowrealm/#sec-getwrappedvalue

View file

@ -70,7 +70,7 @@ VM::VM(OwnPtr<CustomData> custom_data)
return resolve_imported_module(move(referencing_script_or_module), specifier);
};
host_import_module_dynamically = [&](ScriptOrModule, ModuleRequest const&, PromiseCapability promise_capability) {
host_import_module_dynamically = [&](ScriptOrModule, ModuleRequest const&, PromiseCapability const& promise_capability) {
// By default, we throw on dynamic imports this is to prevent arbitrary file access by scripts.
VERIFY(current_realm());
auto& realm = *current_realm();
@ -85,11 +85,11 @@ VM::VM(OwnPtr<CustomData> custom_data)
NativeFunction::create(realm, "", [](auto&) -> ThrowCompletionOr<Value> {
VERIFY_NOT_REACHED();
}),
NativeFunction::create(realm, "", [reject = make_handle(promise_capability.reject)](auto& vm) -> ThrowCompletionOr<Value> {
NativeFunction::create(realm, "", [&promise_capability](auto& vm) -> ThrowCompletionOr<Value> {
auto error = vm.argument(0);
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « error »).
MUST(call(vm, reject.cell(), js_undefined(), error));
MUST(call(vm, *promise_capability.reject(), js_undefined(), error));
// b. Return undefined.
return js_undefined();
@ -97,7 +97,7 @@ VM::VM(OwnPtr<CustomData> custom_data)
{});
};
host_finish_dynamic_import = [&](ScriptOrModule referencing_script_or_module, ModuleRequest const& specifier, PromiseCapability promise_capability, Promise* promise) {
host_finish_dynamic_import = [&](ScriptOrModule referencing_script_or_module, ModuleRequest const& specifier, PromiseCapability const& promise_capability, Promise* promise) {
return finish_dynamic_import(move(referencing_script_or_module), specifier, promise_capability, promise);
};
@ -146,7 +146,7 @@ VM::VM(OwnPtr<CustomData> custom_data)
void VM::enable_default_host_import_module_dynamically_hook()
{
host_import_module_dynamically = [&](ScriptOrModule referencing_script_or_module, ModuleRequest const& specifier, PromiseCapability promise_capability) {
host_import_module_dynamically = [&](ScriptOrModule referencing_script_or_module, ModuleRequest const& specifier, PromiseCapability const& promise_capability) {
return import_module_dynamically(move(referencing_script_or_module), specifier, promise_capability);
};
}
@ -976,7 +976,7 @@ ThrowCompletionOr<NonnullGCPtr<Module>> VM::resolve_imported_module(ScriptOrModu
}
// 16.2.1.8 HostImportModuleDynamically ( referencingScriptOrModule, specifier, promiseCapability ), https://tc39.es/ecma262/#sec-hostimportmoduledynamically
void VM::import_module_dynamically(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability promise_capability)
void VM::import_module_dynamically(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability const& promise_capability)
{
auto& realm = *current_realm();
@ -995,7 +995,7 @@ void VM::import_module_dynamically(ScriptOrModule referencing_script_or_module,
auto* promise = Promise::create(realm);
ScopeGuard finish_dynamic_import = [&] {
host_finish_dynamic_import(referencing_script_or_module, move(module_request), promise_capability, promise);
host_finish_dynamic_import(referencing_script_or_module, module_request, promise_capability, promise);
};
// Generally within ECMA262 we always get a referencing_script_or_moulde. However, ShadowRealm gives an explicit null.
@ -1034,14 +1034,14 @@ void VM::import_module_dynamically(ScriptOrModule referencing_script_or_module,
}
// 16.2.1.9 FinishDynamicImport ( referencingScriptOrModule, specifier, promiseCapability, innerPromise ), https://tc39.es/ecma262/#sec-finishdynamicimport
void VM::finish_dynamic_import(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability promise_capability, Promise* inner_promise)
void VM::finish_dynamic_import(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability const& promise_capability, Promise* inner_promise)
{
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] finish_dynamic_import on {}", module_request.module_specifier);
auto& realm = *current_realm();
// 1. Let fulfilledClosure be a new Abstract Closure with parameters (result) that captures referencingScriptOrModule, specifier, and promiseCapability and performs the following steps when called:
auto fulfilled_closure = [referencing_script_or_module, module_request = move(module_request), resolve_function = make_handle(promise_capability.resolve), reject_function = make_handle(promise_capability.reject)](VM& vm) -> ThrowCompletionOr<Value> {
auto fulfilled_closure = [referencing_script_or_module = move(referencing_script_or_module), module_request = move(module_request), &promise_capability](VM& vm) -> ThrowCompletionOr<Value> {
auto result = vm.argument(0);
// a. Assert: result is undefined.
VERIFY(result.is_undefined());
@ -1057,12 +1057,12 @@ void VM::finish_dynamic_import(ScriptOrModule referencing_script_or_module, Modu
// e. If namespace is an abrupt completion, then
if (namespace_.is_throw_completion()) {
// i. Perform ! Call(promiseCapability.[[Reject]], undefined, « namespace.[[Value]] »).
MUST(call(vm, reject_function.cell(), js_undefined(), *namespace_.throw_completion().value()));
MUST(call(vm, *promise_capability.reject(), js_undefined(), *namespace_.throw_completion().value()));
}
// f. Else,
else {
// i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace.[[Value]] »).
MUST(call(vm, resolve_function.cell(), js_undefined(), namespace_.release_value()));
MUST(call(vm, *promise_capability.resolve(), js_undefined(), namespace_.release_value()));
}
// g. Return unused.
// NOTE: We don't support returning an empty/optional/unused value here.
@ -1073,10 +1073,10 @@ void VM::finish_dynamic_import(ScriptOrModule referencing_script_or_module, Modu
auto* on_fulfilled = NativeFunction::create(realm, move(fulfilled_closure), 0, "");
// 3. Let rejectedClosure be a new Abstract Closure with parameters (error) that captures promiseCapability and performs the following steps when called:
auto rejected_closure = [rejected_function = make_handle(promise_capability.reject)](VM& vm) -> ThrowCompletionOr<Value> {
auto rejected_closure = [&promise_capability](VM& vm) -> ThrowCompletionOr<Value> {
auto error = vm.argument(0);
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « error »).
MUST(call(vm, rejected_function.cell(), js_undefined(), error));
MUST(call(vm, *promise_capability.reject(), js_undefined(), error));
// b. Return unused.
// NOTE: We don't support returning an empty/optional/unused value here.

View file

@ -223,8 +223,8 @@ public:
ScriptOrModule get_active_script_or_module() const;
Function<ThrowCompletionOr<NonnullGCPtr<Module>>(ScriptOrModule, ModuleRequest const&)> host_resolve_imported_module;
Function<void(ScriptOrModule, ModuleRequest, PromiseCapability)> host_import_module_dynamically;
Function<void(ScriptOrModule, ModuleRequest const&, PromiseCapability, Promise*)> host_finish_dynamic_import;
Function<void(ScriptOrModule, ModuleRequest, PromiseCapability const&)> host_import_module_dynamically;
Function<void(ScriptOrModule, ModuleRequest const&, PromiseCapability const&, Promise*)> host_finish_dynamic_import;
Function<HashMap<PropertyKey, Value>(SourceTextModule const&)> host_get_import_meta_properties;
Function<void(Object*, SourceTextModule const&)> host_finalize_import_meta;
@ -250,8 +250,8 @@ private:
ThrowCompletionOr<NonnullGCPtr<Module>> resolve_imported_module(ScriptOrModule referencing_script_or_module, ModuleRequest const& module_request);
ThrowCompletionOr<void> link_and_eval_module(Module& module);
void import_module_dynamically(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability promise_capability);
void finish_dynamic_import(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability promise_capability, Promise* inner_promise);
void import_module_dynamically(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability const& promise_capability);
void finish_dynamic_import(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability const& promise_capability, Promise* inner_promise);
HashMap<String, PrimitiveString*> m_string_cache;

View file

@ -646,9 +646,9 @@ ThrowCompletionOr<ResolvedBinding> SourceTextModule::resolve_export(VM& vm, FlyS
}
// 16.2.1.6.5 ExecuteModule ( [ capability ] ), https://tc39.es/ecma262/#sec-source-text-module-record-execute-module
ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, Optional<PromiseCapability> capability)
ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GCPtr<PromiseCapability> capability)
{
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] SourceTextModule::execute_module({}, capability has value: {})", filename(), capability.has_value());
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] SourceTextModule::execute_module({}, PromiseCapability @ {})", filename(), capability.ptr());
// 1. Let moduleContext be a new ECMAScript code execution context.
ExecutionContext module_context { vm.heap() };
@ -679,7 +679,7 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, Optional<Promis
// 9. If module.[[HasTLA]] is false, then
if (!m_has_top_level_await) {
// a. Assert: capability is not present.
VERIFY(!capability.has_value());
VERIFY(capability == nullptr);
// b. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
TRY(vm.push_execution_context(module_context, {}));
@ -701,10 +701,10 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, Optional<Promis
// 10. Else,
else {
// a. Assert: capability is a PromiseCapability Record.
VERIFY(capability.has_value());
VERIFY(capability != nullptr);
// b. Perform AsyncBlockStart(capability, module.[[ECMAScriptCode]], moduleContext).
async_block_start(vm, m_ecmascript_code, capability.value(), module_context);
async_block_start(vm, m_ecmascript_code, *capability, module_context);
}
// 11. Return unused.

View file

@ -34,7 +34,7 @@ public:
protected:
virtual ThrowCompletionOr<void> initialize_environment(VM& vm) override;
virtual ThrowCompletionOr<void> execute_module(VM& vm, Optional<PromiseCapability> capability) override;
virtual ThrowCompletionOr<void> execute_module(VM& vm, GCPtr<PromiseCapability> capability) override;
private:
SourceTextModule(Realm&, StringView filename, bool has_top_level_await, NonnullRefPtr<Program> body, Vector<ModuleRequest> requested_modules,

View file

@ -147,7 +147,7 @@ JS::NonnullGCPtr<JS::Promise> consume_body(JS::Realm& realm, BodyMixin const& ob
// 1. If object is unusable, then return a promise rejected with a TypeError.
if (object.is_unusable()) {
auto promise_capability = WebIDL::create_rejected_promise(realm, JS::TypeError::create(realm, "Body is unusable"sv));
return static_cast<JS::Promise&>(*promise_capability.promise);
return verify_cast<JS::Promise>(*promise_capability->promise().ptr());
}
// 2. Let promise be a promise resolved with an empty byte sequence.

View file

@ -40,7 +40,7 @@ WebIDL::ExceptionOr<Body> Body::clone() const
}
// https://fetch.spec.whatwg.org/#fully-reading-body-as-promise
JS::PromiseCapability Body::fully_read_as_promise() const
JS::NonnullGCPtr<JS::PromiseCapability> Body::fully_read_as_promise() const
{
auto& vm = Bindings::main_thread_vm();
auto& realm = *vm.current_realm();

View file

@ -31,7 +31,7 @@ public:
[[nodiscard]] WebIDL::ExceptionOr<Body> clone() const;
[[nodiscard]] JS::PromiseCapability fully_read_as_promise() const;
[[nodiscard]] JS::NonnullGCPtr<JS::PromiseCapability> fully_read_as_promise() const;
private:
// https://fetch.spec.whatwg.org/#concept-body-stream

View file

@ -15,7 +15,7 @@
namespace Web::WebIDL {
// https://webidl.spec.whatwg.org/#a-new-promise
JS::PromiseCapability create_promise(JS::Realm& realm)
JS::NonnullGCPtr<JS::PromiseCapability> create_promise(JS::Realm& realm)
{
auto& vm = realm.vm();
@ -28,7 +28,7 @@ JS::PromiseCapability create_promise(JS::Realm& realm)
}
// https://webidl.spec.whatwg.org/#a-promise-resolved-with
JS::PromiseCapability create_resolved_promise(JS::Realm& realm, JS::Value value)
JS::NonnullGCPtr<JS::PromiseCapability> create_resolved_promise(JS::Realm& realm, JS::Value value)
{
auto& vm = realm.vm();
@ -42,14 +42,14 @@ JS::PromiseCapability create_resolved_promise(JS::Realm& realm, JS::Value value)
auto promise_capability = MUST(JS::new_promise_capability(vm, constructor));
// 4. Perform ! Call(promiseCapability.[[Resolve]], undefined, « value »).
MUST(JS::call(vm, promise_capability.resolve, JS::js_undefined(), value));
MUST(JS::call(vm, *promise_capability->resolve(), JS::js_undefined(), value));
// 5. Return promiseCapability.
return promise_capability;
}
// https://webidl.spec.whatwg.org/#a-promise-rejected-with
JS::PromiseCapability create_rejected_promise(JS::Realm& realm, JS::Value reason)
JS::NonnullGCPtr<JS::PromiseCapability> create_rejected_promise(JS::Realm& realm, JS::Value reason)
{
auto& vm = realm.vm();
@ -61,7 +61,7 @@ JS::PromiseCapability create_rejected_promise(JS::Realm& realm, JS::Value reason
auto promise_capability = MUST(JS::new_promise_capability(vm, constructor));
// 3. Perform ! Call(promiseCapability.[[Reject]], undefined, « r »).
MUST(JS::call(vm, promise_capability.reject, JS::js_undefined(), reason));
MUST(JS::call(vm, *promise_capability->reject(), JS::js_undefined(), reason));
// 4. Return promiseCapability.
return promise_capability;
@ -76,20 +76,20 @@ void resolve_promise(JS::VM& vm, JS::PromiseCapability const& promise_capability
// 2. Let value be the result of converting x to an ECMAScript value.
// 3. Perform ! Call(p.[[Resolve]], undefined, « value »).
MUST(JS::call(vm, promise_capability.resolve, JS::js_undefined(), value));
MUST(JS::call(vm, *promise_capability.resolve(), JS::js_undefined(), value));
}
// https://webidl.spec.whatwg.org/#reject
void reject_promise(JS::VM& vm, JS::PromiseCapability const& promise_capability, JS::Value reason)
{
// 1. Perform ! Call(p.[[Reject]], undefined, « r »).
MUST(JS::call(vm, promise_capability.reject, JS::js_undefined(), reason));
MUST(JS::call(vm, *promise_capability.reject(), JS::js_undefined(), reason));
}
// https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled
JS::NonnullGCPtr<JS::Promise> react_to_promise(JS::PromiseCapability const& promise_capability, Optional<ReactionSteps> on_fulfilled_callback, Optional<ReactionSteps> on_rejected_callback)
{
auto& realm = promise_capability.promise->shape().realm();
auto& realm = promise_capability.promise()->shape().realm();
auto& vm = realm.vm();
// 1. Let onFulfilledSteps be the following steps given argument V:
@ -117,7 +117,7 @@ JS::NonnullGCPtr<JS::Promise> react_to_promise(JS::PromiseCapability const& prom
// 2. If there is a set of steps to be run if the promise was rejected, then let result be the result of performing them, given reason. Otherwise, let result be a promise rejected with reason.
auto result = on_rejected_callback.has_value()
? TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return (*on_rejected_callback)(reason); }))
: WebIDL::create_rejected_promise(realm, reason).promise;
: WebIDL::create_rejected_promise(realm, reason)->promise();
// 3. Return result, converted to an ECMAScript value.
return result;
@ -134,7 +134,7 @@ JS::NonnullGCPtr<JS::Promise> react_to_promise(JS::PromiseCapability const& prom
auto new_capability = MUST(JS::new_promise_capability(vm, constructor));
// 7. Return PerformPromiseThen(promise.[[Promise]], onFulfilled, onRejected, newCapability).
auto* promise = static_cast<JS::Promise*>(promise_capability.promise);
auto* promise = verify_cast<JS::Promise>(promise_capability.promise().ptr());
auto value = promise->perform_then(on_fulfilled, on_rejected, new_capability);
return verify_cast<JS::Promise>(value.as_object());
}
@ -172,7 +172,7 @@ JS::NonnullGCPtr<JS::Promise> upon_rejection(JS::PromiseCapability const& promis
void mark_promise_as_handled(JS::PromiseCapability const& promise_capability)
{
// To mark as handled a Promise<T> promise, set promise.[[Promise]].[[PromiseIsHandled]] to true.
auto* promise = static_cast<JS::Promise*>(promise_capability.promise);
auto* promise = verify_cast<JS::Promise>(promise_capability.promise().ptr());
promise->set_is_handled();
}

View file

@ -14,9 +14,9 @@ namespace Web::WebIDL {
using ReactionSteps = JS::SafeFunction<WebIDL::ExceptionOr<JS::Value>(JS::Value)>;
JS::PromiseCapability create_promise(JS::Realm&);
JS::PromiseCapability create_resolved_promise(JS::Realm&, JS::Value);
JS::PromiseCapability create_rejected_promise(JS::Realm&, JS::Value);
JS::NonnullGCPtr<JS::PromiseCapability> create_promise(JS::Realm&);
JS::NonnullGCPtr<JS::PromiseCapability> create_resolved_promise(JS::Realm&, JS::Value);
JS::NonnullGCPtr<JS::PromiseCapability> create_rejected_promise(JS::Realm&, JS::Value);
void resolve_promise(JS::VM&, JS::PromiseCapability const&, JS::Value = JS::js_undefined());
void reject_promise(JS::VM&, JS::PromiseCapability const&, JS::Value);
JS::NonnullGCPtr<JS::Promise> react_to_promise(JS::PromiseCapability const&, Optional<ReactionSteps> on_fulfilled_callback, Optional<ReactionSteps> on_rejected_callback);