Parcourir la source

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.
Linus Groh il y a 2 ans
Parent
commit
fc9d587e39
29 fichiers modifiés avec 243 ajouts et 217 suppressions
  1. 7 7
      Userland/Libraries/LibJS/AST.cpp
  2. 13 17
      Userland/Libraries/LibJS/CyclicModule.cpp
  3. 12 12
      Userland/Libraries/LibJS/CyclicModule.h
  4. 1 1
      Userland/Libraries/LibJS/Forward.h
  5. 13 13
      Userland/Libraries/LibJS/Runtime/AsyncFromSyncIteratorPrototype.cpp
  6. 2 1
      Userland/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp
  7. 1 3
      Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp
  8. 2 2
      Userland/Libraries/LibJS/Runtime/AsyncGeneratorRequest.h
  9. 5 5
      Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp
  10. 6 7
      Userland/Libraries/LibJS/Runtime/Promise.cpp
  11. 1 1
      Userland/Libraries/LibJS/Runtime/Promise.h
  12. 23 4
      Userland/Libraries/LibJS/Runtime/PromiseCapability.cpp
  13. 54 33
      Userland/Libraries/LibJS/Runtime/PromiseCapability.h
  14. 16 16
      Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp
  15. 8 9
      Userland/Libraries/LibJS/Runtime/PromiseJobs.cpp
  16. 5 10
      Userland/Libraries/LibJS/Runtime/PromiseReaction.cpp
  17. 4 4
      Userland/Libraries/LibJS/Runtime/PromiseReaction.h
  18. 19 21
      Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.cpp
  19. 10 10
      Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.h
  20. 1 1
      Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp
  21. 13 13
      Userland/Libraries/LibJS/Runtime/VM.cpp
  22. 4 4
      Userland/Libraries/LibJS/Runtime/VM.h
  23. 5 5
      Userland/Libraries/LibJS/SourceTextModule.cpp
  24. 1 1
      Userland/Libraries/LibJS/SourceTextModule.h
  25. 1 1
      Userland/Libraries/LibWeb/Fetch/Body.cpp
  26. 1 1
      Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp
  27. 1 1
      Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.h
  28. 11 11
      Userland/Libraries/LibWeb/WebIDL/Promise.cpp
  29. 3 3
      Userland/Libraries/LibWeb/WebIDL/Promise.h

+ 7 - 7
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

+ 13 - 17
Userland/Libraries/LibJS/CyclicModule.cpp

@@ -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.

+ 12 - 12
Userland/Libraries/LibJS/CyclicModule.h

@@ -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]]
 };
 
 }

+ 1 - 1
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;

+ 13 - 13
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<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).

+ 2 - 1
Userland/Libraries/LibJS/Runtime/AsyncFunctionDriverWrapper.cpp

@@ -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)

+ 1 - 3
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);
     }
 }
 

+ 2 - 2
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<PromiseCapability> capability; // [[Capability]]
 };
 
 }

+ 5 - 5
Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp

@@ -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();

+ 6 - 7
Userland/Libraries/LibJS/Runtime/Promise.cpp

@@ -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)

+ 1 - 1
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<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; }

+ 23 - 4
Userland/Libraries/LibJS/Runtime/PromiseCapability.cpp

@@ -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());
 }
 
 }

+ 54 - 33
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<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);
 
 }

+ 16 - 16
Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp

@@ -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

+ 8 - 9
Userland/Libraries/LibJS/Runtime/PromiseJobs.cpp

@@ -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);
     };
 

+ 5 - 10
Userland/Libraries/LibJS/Runtime/PromiseReaction.cpp

@@ -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);
 }
 
 }

+ 4 - 4
Userland/Libraries/LibJS/Runtime/PromiseReaction.h

@@ -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;
 };
 

+ 19 - 21
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<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();

+ 10 - 10
Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.h

@@ -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;
 };

+ 1 - 1
Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp

@@ -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

+ 13 - 13
Userland/Libraries/LibJS/Runtime/VM.cpp

@@ -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.

+ 4 - 4
Userland/Libraries/LibJS/Runtime/VM.h

@@ -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;
 

+ 5 - 5
Userland/Libraries/LibJS/SourceTextModule.cpp

@@ -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.

+ 1 - 1
Userland/Libraries/LibJS/SourceTextModule.h

@@ -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,

+ 1 - 1
Userland/Libraries/LibWeb/Fetch/Body.cpp

@@ -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.

+ 1 - 1
Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp

@@ -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();

+ 1 - 1
Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.h

@@ -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

+ 11 - 11
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<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();
 }
 

+ 3 - 3
Userland/Libraries/LibWeb/WebIDL/Promise.h

@@ -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);