From fc9d587e3958538fc2fd027c32dbcc0fb251e3f5 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sun, 2 Oct 2022 12:11:30 +0100 Subject: [PATCH] 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. --- Userland/Libraries/LibJS/AST.cpp | 14 +-- Userland/Libraries/LibJS/CyclicModule.cpp | 30 +++---- Userland/Libraries/LibJS/CyclicModule.h | 24 ++--- Userland/Libraries/LibJS/Forward.h | 2 +- .../AsyncFromSyncIteratorPrototype.cpp | 26 +++--- .../Runtime/AsyncFunctionDriverWrapper.cpp | 3 +- .../LibJS/Runtime/AsyncGenerator.cpp | 4 +- .../LibJS/Runtime/AsyncGeneratorRequest.h | 4 +- .../Runtime/ECMAScriptFunctionObject.cpp | 10 +-- Userland/Libraries/LibJS/Runtime/Promise.cpp | 13 ++- Userland/Libraries/LibJS/Runtime/Promise.h | 2 +- .../LibJS/Runtime/PromiseCapability.cpp | 27 +++++- .../LibJS/Runtime/PromiseCapability.h | 87 ++++++++++++------- .../LibJS/Runtime/PromiseConstructor.cpp | 32 +++---- .../Libraries/LibJS/Runtime/PromiseJobs.cpp | 17 ++-- .../LibJS/Runtime/PromiseReaction.cpp | 15 ++-- .../Libraries/LibJS/Runtime/PromiseReaction.h | 8 +- .../PromiseResolvingElementFunctions.cpp | 40 ++++----- .../PromiseResolvingElementFunctions.h | 20 ++--- .../Libraries/LibJS/Runtime/ShadowRealm.cpp | 2 +- Userland/Libraries/LibJS/Runtime/VM.cpp | 26 +++--- Userland/Libraries/LibJS/Runtime/VM.h | 8 +- Userland/Libraries/LibJS/SourceTextModule.cpp | 10 +-- Userland/Libraries/LibJS/SourceTextModule.h | 2 +- Userland/Libraries/LibWeb/Fetch/Body.cpp | 2 +- .../Fetch/Infrastructure/HTTP/Bodies.cpp | 2 +- .../LibWeb/Fetch/Infrastructure/HTTP/Bodies.h | 2 +- Userland/Libraries/LibWeb/WebIDL/Promise.cpp | 22 ++--- Userland/Libraries/LibWeb/WebIDL/Promise.h | 6 +- 29 files changed, 243 insertions(+), 217 deletions(-) diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 3bd1a28376..cda5af2b97 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -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 diff --git a/Userland/Libraries/LibJS/CyclicModule.cpp b/Userland/Libraries/LibJS/CyclicModule.cpp index b98d9c84bb..658889ab90 100644 --- a/Userland/Libraries/LibJS/CyclicModule.cpp +++ b/Userland/Libraries/LibJS/CyclicModule.cpp @@ -197,10 +197,9 @@ ThrowCompletionOr 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(*m_top_level_capability->promise)); - return static_cast(m_top_level_capability->promise); + return verify_cast(m_top_level_capability->promise().ptr()); } // 5. Let stack be a new empty List. @@ -243,7 +242,7 @@ ThrowCompletionOr 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 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 CyclicModule::evaluate(VM& vm) } // 11. Return capability.[[Promise]]. - VERIFY(is(*m_top_level_capability->promise)); - return static_cast(m_top_level_capability->promise); + return verify_cast(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 CyclicModule::initialize_environment(VM&) VERIFY_NOT_REACHED(); } -ThrowCompletionOr CyclicModule::execute_module(VM&, Optional) +ThrowCompletionOr CyclicModule::execute_module(VM&, GCPtr) { // 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(*capability.promise)); - // 8. Perform PerformPromiseThen(capability.[[Promise]], onFulfilled, onRejected). - static_cast(capability.promise)->perform_then(on_fulfilled, on_rejected, {}); + verify_cast(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. diff --git a/Userland/Libraries/LibJS/CyclicModule.h b/Userland/Libraries/LibJS/CyclicModule.h index 00facbefcb..b610a678fc 100644 --- a/Userland/Libraries/LibJS/CyclicModule.h +++ b/Userland/Libraries/LibJS/CyclicModule.h @@ -42,24 +42,24 @@ protected: virtual ThrowCompletionOr inner_module_evaluation(VM& vm, Vector& stack, u32 index) override; virtual ThrowCompletionOr initialize_environment(VM& vm); - virtual ThrowCompletionOr execute_module(VM& vm, Optional capability = {}); + virtual ThrowCompletionOr execute_module(VM& vm, GCPtr capability = {}); void execute_async_module(VM& vm); void gather_available_ancestors(Vector& 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 m_evaluation_error; // [[EvaluationError]] - Optional m_dfs_index; // [[DFSIndex]] - Optional m_dfs_ancestor_index; // [[DFSAncestorIndex]] - Vector 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 m_top_level_capability; // [[TopLevelCapability]] - Vector m_async_parent_modules; // [[AsyncParentModules]] - Optional m_pending_async_dependencies; // [[PendingAsyncDependencies]] + ModuleStatus m_status { ModuleStatus::Unlinked }; // [[Status]] + ThrowCompletionOr m_evaluation_error; // [[EvaluationError]] + Optional m_dfs_index; // [[DFSIndex]] + Optional m_dfs_ancestor_index; // [[DFSAncestorIndex]] + Vector 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 m_top_level_capability; // [[TopLevelCapability]] + Vector m_async_parent_modules; // [[AsyncParentModules]] + Optional m_pending_async_dependencies; // [[PendingAsyncDependencies]] }; } diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index 7f1735dc42..9e2a6fda05 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -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; diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp index 1b3aa73bc4..e8899b7c63 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp @@ -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 { @@ -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(value_wrapper)->perform_then(move(on_fulfilled), js_undefined(), promise_capability); + verify_cast(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). diff --git a/Userland/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp b/Userland/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp index d6977d61a9..4226026f88 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp +++ b/Userland/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp @@ -59,7 +59,8 @@ ThrowCompletionOr 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) diff --git a/Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp b/Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp index db83d52a25..f3eb5a3be5 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp +++ b/Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp @@ -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); } } diff --git a/Userland/Libraries/LibJS/Runtime/AsyncGeneratorRequest.h b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorRequest.h index ddfd38857b..7be045fab0 100644 --- a/Userland/Libraries/LibJS/Runtime/AsyncGeneratorRequest.h +++ b/Userland/Libraries/LibJS/Runtime/AsyncGeneratorRequest.h @@ -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 capability; // [[Capability]] }; } diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index 1341da4f49..5fd32e9cae 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -743,12 +743,12 @@ void async_block_start(VM& vm, NonnullRefPtr 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 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(); diff --git a/Userland/Libraries/LibJS/Runtime/Promise.cpp b/Userland/Libraries/LibJS/Runtime/Promise.cpp index 40637f2151..5d688babdf 100644 --- a/Userland/Libraries/LibJS/Runtime/Promise.cpp +++ b/Userland/Libraries/LibJS/Runtime/Promise.cpp @@ -36,10 +36,10 @@ ThrowCompletionOr 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 result_capability) +Value Promise::perform_then(Value on_fulfilled, Value on_rejected, GCPtr result_capability) { auto& vm = this->vm(); @@ -377,7 +377,7 @@ Value Promise::perform_then(Value on_fulfilled, Value on_rejected, Optionalpromise().ptr(), result_capability.ptr()); + return result_capability->promise(); } void Promise::visit_edges(Cell::Visitor& visitor) diff --git a/Userland/Libraries/LibJS/Runtime/Promise.h b/Userland/Libraries/LibJS/Runtime/Promise.h index 82692393dc..790e3482e4 100644 --- a/Userland/Libraries/LibJS/Runtime/Promise.h +++ b/Userland/Libraries/LibJS/Runtime/Promise.h @@ -42,7 +42,7 @@ public: void fulfill(Value value); void reject(Value reason); - Value perform_then(Value on_fulfilled, Value on_rejected, Optional result_capability); + Value perform_then(Value on_fulfilled, Value on_rejected, GCPtr result_capability); bool is_handled() const { return m_is_handled; } void set_is_handled() { m_is_handled = true; } diff --git a/Userland/Libraries/LibJS/Runtime/PromiseCapability.cpp b/Userland/Libraries/LibJS/Runtime/PromiseCapability.cpp index b75cf4093c..5c56801c0a 100644 --- a/Userland/Libraries/LibJS/Runtime/PromiseCapability.cpp +++ b/Userland/Libraries/LibJS/Runtime/PromiseCapability.cpp @@ -11,8 +11,27 @@ namespace JS { +NonnullGCPtr PromiseCapability::create(VM& vm, GCPtr promise, GCPtr resolve, GCPtr reject) +{ + return NonnullGCPtr { *vm.heap().allocate_without_realm(promise, resolve, reject) }; +} + +PromiseCapability::PromiseCapability(GCPtr promise, GCPtr resolve, GCPtr 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 new_promise_capability(VM& vm, Value constructor) +ThrowCompletionOr> new_promise_capability(VM& vm, Value constructor) { auto& realm = *vm.current_realm(); @@ -70,11 +89,11 @@ ThrowCompletionOr 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()); } } diff --git a/Userland/Libraries/LibJS/Runtime/PromiseCapability.h b/Userland/Libraries/LibJS/Runtime/PromiseCapability.h index 12d88b542f..c2cee43deb 100644 --- a/Userland/Libraries/LibJS/Runtime/PromiseCapability.h +++ b/Userland/Libraries/LibJS/Runtime/PromiseCapability.h @@ -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 create(VM& vm, GCPtr promise, GCPtr resolve, GCPtr reject); + + virtual ~PromiseCapability() = default; + + [[nodiscard]] GCPtr promise() const { return m_promise; } + void set_promise(NonnullGCPtr promise) { m_promise = promise; } + + [[nodiscard]] GCPtr resolve() const { return m_resolve; } + void set_resolve(NonnullGCPtr resolve) { m_resolve = resolve; } + + [[nodiscard]] GCPtr reject() const { return m_reject; } + void set_reject(NonnullGCPtr reject) { m_reject = reject; } + +private: + PromiseCapability(GCPtr, GCPtr, GCPtr); + + virtual void visit_edges(Visitor&) override; + + GCPtr m_promise; + GCPtr m_resolve; + GCPtr 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 new_promise_capability(VM& vm, Value constructor); +ThrowCompletionOr> new_promise_capability(VM& vm, Value constructor); } diff --git a/Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp b/Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp index 2aead988f0..65fed311f1 100644 --- a/Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp @@ -39,7 +39,7 @@ static ThrowCompletionOr get_promise_resolve(VM& vm, Value constructor) using EndOfElementsCallback = Function(PromiseValueList&)>; using InvokeElementFunctionCallback = Function(PromiseValueList&, RemainingElements&, Value, size_t)>; -static ThrowCompletionOr 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 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 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 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 perform_promise_all(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability result_capability, Value promise_resolve) +static ThrowCompletionOr 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 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 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 perform_promise_all_settled(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability result_capability, Value promise_resolve) +static ThrowCompletionOr 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 perform_promise_all_settled(VM& vm, Iterator& it [&](PromiseValueList& values) -> ThrowCompletionOr { 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 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 perform_promise_any(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability result_capability, Value promise_resolve) +static ThrowCompletionOr 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 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 perform_promise_race(VM& vm, Iterator& iterator_record, Value constructor, PromiseCapability result_capability, Value promise_resolve) +static ThrowCompletionOr 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 { // 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 diff --git a/Userland/Libraries/LibJS/Runtime/PromiseJobs.cpp b/Userland/Libraries/LibJS/Runtime/PromiseJobs.cpp index a31ed65060..1eb425d476 100644 --- a/Userland/Libraries/LibJS/Runtime/PromiseJobs.cpp +++ b/Userland/Libraries/LibJS/Runtime/PromiseJobs.cpp @@ -20,7 +20,7 @@ namespace JS { static ThrowCompletionOr 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 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 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); }; diff --git a/Userland/Libraries/LibJS/Runtime/PromiseReaction.cpp b/Userland/Libraries/LibJS/Runtime/PromiseReaction.cpp index a6f2e392e0..abe81da6f2 100644 --- a/Userland/Libraries/LibJS/Runtime/PromiseReaction.cpp +++ b/Userland/Libraries/LibJS/Runtime/PromiseReaction.cpp @@ -10,14 +10,14 @@ namespace JS { -PromiseReaction* PromiseReaction::create(VM& vm, Type type, Optional capability, Optional handler) +PromiseReaction* PromiseReaction::create(VM& vm, Type type, GCPtr capability, Optional handler) { - return vm.heap().allocate_without_realm(type, move(capability), move(handler)); + return vm.heap().allocate_without_realm(type, capability, move(handler)); } -PromiseReaction::PromiseReaction(Type type, Optional capability, Optional handler) +PromiseReaction::PromiseReaction(Type type, GCPtr capability, Optional handler) : m_type(type) - , m_capability(move(capability)) + , m_capability(capability) , m_handler(move(handler)) { } @@ -25,12 +25,7 @@ PromiseReaction::PromiseReaction(Type type, Optional 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); } } diff --git a/Userland/Libraries/LibJS/Runtime/PromiseReaction.h b/Userland/Libraries/LibJS/Runtime/PromiseReaction.h index 58d6dd63af..ef913c80c7 100644 --- a/Userland/Libraries/LibJS/Runtime/PromiseReaction.h +++ b/Userland/Libraries/LibJS/Runtime/PromiseReaction.h @@ -23,23 +23,23 @@ public: Reject, }; - static PromiseReaction* create(VM& vm, Type type, Optional capability, Optional handler); + static PromiseReaction* create(VM& vm, Type type, GCPtr capability, Optional handler); virtual ~PromiseReaction() = default; Type type() const { return m_type; } - Optional const& capability() const { return m_capability; } + GCPtr capability() const { return m_capability; } Optional& handler() { return m_handler; } Optional const& handler() const { return m_handler; } private: - PromiseReaction(Type type, Optional capability, Optional handler); + PromiseReaction(Type type, GCPtr capability, Optional handler); virtual void visit_edges(Visitor&) override; Type m_type; - Optional m_capability; + GCPtr m_capability; Optional m_handler; }; diff --git a/Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.cpp b/Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.cpp index a967474cd3..35824d5d37 100644 --- a/Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.cpp +++ b/Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.cpp @@ -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 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 capability, RemainingElements& remaining_elements) { return realm.heap().allocate(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 capability, RemainingElements& remaining_elements, Object& prototype) + : PromiseResolvingElementFunction(index, values, capability, remaining_elements, prototype) { } @@ -80,20 +78,20 @@ ThrowCompletionOr 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 capability, RemainingElements& remaining_elements) { return realm.heap().allocate(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 capability, RemainingElements& remaining_elements, Object& prototype) + : PromiseResolvingElementFunction(index, values, capability, remaining_elements, prototype) { } @@ -121,20 +119,20 @@ ThrowCompletionOr 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 capability, RemainingElements& remaining_elements) { return realm.heap().allocate(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 capability, RemainingElements& remaining_elements, Object& prototype) + : PromiseResolvingElementFunction(index, values, capability, remaining_elements, prototype) { } @@ -162,20 +160,20 @@ ThrowCompletionOr 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 capability, RemainingElements& remaining_elements) { return realm.heap().allocate(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 capability, RemainingElements& remaining_elements, Object& prototype) + : PromiseResolvingElementFunction(index, errors, capability, remaining_elements, prototype) { } @@ -198,7 +196,7 @@ ThrowCompletionOr 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(); diff --git a/Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.h b/Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.h index 62cb018009..bbfeff46ad 100644 --- a/Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.h +++ b/Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.h @@ -51,13 +51,13 @@ public: virtual ThrowCompletionOr call() override; protected: - explicit PromiseResolvingElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype); + explicit PromiseResolvingElementFunction(size_t, PromiseValueList&, NonnullGCPtr, RemainingElements&, Object& prototype); virtual ThrowCompletionOr resolve_element() = 0; size_t m_index { 0 }; PromiseValueList& m_values; - PromiseCapability m_capability; + NonnullGCPtr 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, RemainingElements&); virtual ~PromiseAllResolveElementFunction() override = default; private: - explicit PromiseAllResolveElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype); + explicit PromiseAllResolveElementFunction(size_t, PromiseValueList&, NonnullGCPtr, RemainingElements&, Object& prototype); virtual ThrowCompletionOr 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, RemainingElements&); virtual ~PromiseAllSettledResolveElementFunction() override = default; private: - explicit PromiseAllSettledResolveElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype); + explicit PromiseAllSettledResolveElementFunction(size_t, PromiseValueList&, NonnullGCPtr, RemainingElements&, Object& prototype); virtual ThrowCompletionOr 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, RemainingElements&); virtual ~PromiseAllSettledRejectElementFunction() override = default; private: - explicit PromiseAllSettledRejectElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype); + explicit PromiseAllSettledRejectElementFunction(size_t, PromiseValueList&, NonnullGCPtr, RemainingElements&, Object& prototype); virtual ThrowCompletionOr 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, RemainingElements&); virtual ~PromiseAnyRejectElementFunction() override = default; private: - explicit PromiseAnyRejectElementFunction(size_t, PromiseValueList&, PromiseCapability, RemainingElements&, Object& prototype); + explicit PromiseAnyRejectElementFunction(size_t, PromiseValueList&, NonnullGCPtr, RemainingElements&, Object& prototype); virtual ThrowCompletionOr resolve_element() override; }; diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp index 74b4cad7f4..cf85dd83b1 100644 --- a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp @@ -276,7 +276,7 @@ ThrowCompletionOr shadow_realm_import_value(VM& vm, String specifier_stri }); // 13. Return PerformPromiseThen(innerCapability.[[Promise]], onFulfilled, callerRealm.[[Intrinsics]].[[%ThrowTypeError%]], promiseCapability). - return verify_cast(inner_capability.promise)->perform_then(on_fulfilled, throw_type_error, promise_capability); + return verify_cast(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 diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index 1b15a1571d..40a17de1c1 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -70,7 +70,7 @@ VM::VM(OwnPtr 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 custom_data) NativeFunction::create(realm, "", [](auto&) -> ThrowCompletionOr { VERIFY_NOT_REACHED(); }), - NativeFunction::create(realm, "", [reject = make_handle(promise_capability.reject)](auto& vm) -> ThrowCompletionOr { + NativeFunction::create(realm, "", [&promise_capability](auto& vm) -> ThrowCompletionOr { 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 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 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> 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 { + auto fulfilled_closure = [referencing_script_or_module = move(referencing_script_or_module), module_request = move(module_request), &promise_capability](VM& vm) -> ThrowCompletionOr { 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 { + auto rejected_closure = [&promise_capability](VM& vm) -> ThrowCompletionOr { 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. diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h index 5d037e8013..63e5a2e52c 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.h +++ b/Userland/Libraries/LibJS/Runtime/VM.h @@ -223,8 +223,8 @@ public: ScriptOrModule get_active_script_or_module() const; Function>(ScriptOrModule, ModuleRequest const&)> host_resolve_imported_module; - Function host_import_module_dynamically; - Function host_finish_dynamic_import; + Function host_import_module_dynamically; + Function host_finish_dynamic_import; Function(SourceTextModule const&)> host_get_import_meta_properties; Function host_finalize_import_meta; @@ -250,8 +250,8 @@ private: ThrowCompletionOr> resolve_imported_module(ScriptOrModule referencing_script_or_module, ModuleRequest const& module_request); ThrowCompletionOr 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 m_string_cache; diff --git a/Userland/Libraries/LibJS/SourceTextModule.cpp b/Userland/Libraries/LibJS/SourceTextModule.cpp index c8c75b5bc0..3f2f1e9359 100644 --- a/Userland/Libraries/LibJS/SourceTextModule.cpp +++ b/Userland/Libraries/LibJS/SourceTextModule.cpp @@ -646,9 +646,9 @@ ThrowCompletionOr 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 SourceTextModule::execute_module(VM& vm, Optional capability) +ThrowCompletionOr SourceTextModule::execute_module(VM& vm, GCPtr 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 SourceTextModule::execute_module(VM& vm, Optional SourceTextModule::execute_module(VM& vm, Optional initialize_environment(VM& vm) override; - virtual ThrowCompletionOr execute_module(VM& vm, Optional capability) override; + virtual ThrowCompletionOr execute_module(VM& vm, GCPtr capability) override; private: SourceTextModule(Realm&, StringView filename, bool has_top_level_await, NonnullRefPtr body, Vector requested_modules, diff --git a/Userland/Libraries/LibWeb/Fetch/Body.cpp b/Userland/Libraries/LibWeb/Fetch/Body.cpp index 7cc9dbc285..03151faec4 100644 --- a/Userland/Libraries/LibWeb/Fetch/Body.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Body.cpp @@ -147,7 +147,7 @@ JS::NonnullGCPtr 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(*promise_capability.promise); + return verify_cast(*promise_capability->promise().ptr()); } // 2. Let promise be a promise resolved with an empty byte sequence. diff --git a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp index 7b08e08058..d4b5ded3ec 100644 --- a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp @@ -40,7 +40,7 @@ WebIDL::ExceptionOr Body::clone() const } // https://fetch.spec.whatwg.org/#fully-reading-body-as-promise -JS::PromiseCapability Body::fully_read_as_promise() const +JS::NonnullGCPtr Body::fully_read_as_promise() const { auto& vm = Bindings::main_thread_vm(); auto& realm = *vm.current_realm(); diff --git a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.h b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.h index 2d7a3221f2..3dbe155259 100644 --- a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.h +++ b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.h @@ -31,7 +31,7 @@ public: [[nodiscard]] WebIDL::ExceptionOr clone() const; - [[nodiscard]] JS::PromiseCapability fully_read_as_promise() const; + [[nodiscard]] JS::NonnullGCPtr fully_read_as_promise() const; private: // https://fetch.spec.whatwg.org/#concept-body-stream diff --git a/Userland/Libraries/LibWeb/WebIDL/Promise.cpp b/Userland/Libraries/LibWeb/WebIDL/Promise.cpp index ad726919e2..be8f9bfce0 100644 --- a/Userland/Libraries/LibWeb/WebIDL/Promise.cpp +++ b/Userland/Libraries/LibWeb/WebIDL/Promise.cpp @@ -15,7 +15,7 @@ namespace Web::WebIDL { // https://webidl.spec.whatwg.org/#a-new-promise -JS::PromiseCapability create_promise(JS::Realm& realm) +JS::NonnullGCPtr 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 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 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 react_to_promise(JS::PromiseCapability const& promise_capability, Optional on_fulfilled_callback, Optional 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 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 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(promise_capability.promise); + auto* promise = verify_cast(promise_capability.promise().ptr()); auto value = promise->perform_then(on_fulfilled, on_rejected, new_capability); return verify_cast(value.as_object()); } @@ -172,7 +172,7 @@ JS::NonnullGCPtr upon_rejection(JS::PromiseCapability const& promis void mark_promise_as_handled(JS::PromiseCapability const& promise_capability) { // To mark as handled a Promise promise, set promise.[[Promise]].[[PromiseIsHandled]] to true. - auto* promise = static_cast(promise_capability.promise); + auto* promise = verify_cast(promise_capability.promise().ptr()); promise->set_is_handled(); } diff --git a/Userland/Libraries/LibWeb/WebIDL/Promise.h b/Userland/Libraries/LibWeb/WebIDL/Promise.h index 43a9336a45..0e221dc518 100644 --- a/Userland/Libraries/LibWeb/WebIDL/Promise.h +++ b/Userland/Libraries/LibWeb/WebIDL/Promise.h @@ -14,9 +14,9 @@ namespace Web::WebIDL { using ReactionSteps = JS::SafeFunction(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 create_promise(JS::Realm&); +JS::NonnullGCPtr create_resolved_promise(JS::Realm&, JS::Value); +JS::NonnullGCPtr 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 react_to_promise(JS::PromiseCapability const&, Optional on_fulfilled_callback, Optional on_rejected_callback);