فهرست منبع

LibWeb: Use JS::HeapFunction for WebIDL promise reaction steps

Switching away from SafeFunction immediately backfired here, as we're
dealing with two layers of captures, not one.

Let's do the correct fix, which is to use HeapFunction. This makes the
API and its behavior explicit, and keeps captures alive as long as the
HeapFunction is alive.

Fixes #23819.
Andreas Kling 1 سال پیش
والد
کامیت
ffac32d20e

+ 4 - 4
Userland/Libraries/LibWeb/FileAPI/Blob.cpp

@@ -375,7 +375,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> Blob::text()
     auto promise = TRY(reader->read_all_bytes_deprecated());
     auto promise = TRY(reader->read_all_bytes_deprecated());
 
 
     // 4. Return the result of transforming promise by a fulfillment handler that returns the result of running UTF-8 decode on its first argument.
     // 4. Return the result of transforming promise by a fulfillment handler that returns the result of running UTF-8 decode on its first argument.
-    return WebIDL::upon_fulfillment(*promise, [&](auto const& first_argument) -> WebIDL::ExceptionOr<JS::Value> {
+    return WebIDL::upon_fulfillment(*promise, JS::create_heap_function(heap(), [&vm](JS::Value first_argument) -> WebIDL::ExceptionOr<JS::Value> {
         auto const& object = first_argument.as_object();
         auto const& object = first_argument.as_object();
         VERIFY(is<JS::ArrayBuffer>(object));
         VERIFY(is<JS::ArrayBuffer>(object));
         auto const& buffer = static_cast<const JS::ArrayBuffer&>(object).buffer();
         auto const& buffer = static_cast<const JS::ArrayBuffer&>(object).buffer();
@@ -383,7 +383,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> Blob::text()
         auto decoder = TextCodec::decoder_for("UTF-8"sv);
         auto decoder = TextCodec::decoder_for("UTF-8"sv);
         auto utf8_text = TRY_OR_THROW_OOM(vm, TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, buffer));
         auto utf8_text = TRY_OR_THROW_OOM(vm, TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, buffer));
         return JS::PrimitiveString::create(vm, move(utf8_text));
         return JS::PrimitiveString::create(vm, move(utf8_text));
-    });
+    }));
 }
 }
 
 
 // https://w3c.github.io/FileAPI/#dom-blob-arraybuffer
 // https://w3c.github.io/FileAPI/#dom-blob-arraybuffer
@@ -404,13 +404,13 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> Blob::array_buffer()
     auto promise = TRY(reader->read_all_bytes_deprecated());
     auto promise = TRY(reader->read_all_bytes_deprecated());
 
 
     // 4. Return the result of transforming promise by a fulfillment handler that returns a new ArrayBuffer whose contents are its first argument.
     // 4. Return the result of transforming promise by a fulfillment handler that returns a new ArrayBuffer whose contents are its first argument.
-    return WebIDL::upon_fulfillment(*promise, [&](auto const& first_argument) -> WebIDL::ExceptionOr<JS::Value> {
+    return WebIDL::upon_fulfillment(*promise, JS::create_heap_function(heap(), [&realm](JS::Value first_argument) -> WebIDL::ExceptionOr<JS::Value> {
         auto const& object = first_argument.as_object();
         auto const& object = first_argument.as_object();
         VERIFY(is<JS::ArrayBuffer>(object));
         VERIFY(is<JS::ArrayBuffer>(object));
         auto const& buffer = static_cast<const JS::ArrayBuffer&>(object).buffer();
         auto const& buffer = static_cast<const JS::ArrayBuffer&>(object).buffer();
 
 
         return JS::ArrayBuffer::create(realm, buffer);
         return JS::ArrayBuffer::create(realm, buffer);
-    });
+    }));
 }
 }
 
 
 }
 }

+ 4 - 4
Userland/Libraries/LibWeb/HTML/Scripting/Fetching.cpp

@@ -828,7 +828,7 @@ void fetch_descendants_of_and_link_a_module_script(JS::Realm& realm,
     auto& loading_promise = record->load_requested_modules(state);
     auto& loading_promise = record->load_requested_modules(state);
 
 
     // 6. Upon fulfillment of loadingPromise, run the following steps:
     // 6. Upon fulfillment of loadingPromise, run the following steps:
-    WebIDL::upon_fulfillment(loading_promise, [&realm, record, &module_script, on_complete](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_fulfillment(loading_promise, JS::create_heap_function(realm.heap(), [&realm, record, &module_script, on_complete](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Perform record.Link().
         // 1. Perform record.Link().
         auto linking_result = record->link(realm.vm());
         auto linking_result = record->link(realm.vm());
 
 
@@ -840,10 +840,10 @@ void fetch_descendants_of_and_link_a_module_script(JS::Realm& realm,
         on_complete->function()(module_script);
         on_complete->function()(module_script);
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 7. Upon rejection of loadingPromise, run the following steps:
     // 7. Upon rejection of loadingPromise, run the following steps:
-    WebIDL::upon_rejection(loading_promise, [state, &module_script, on_complete](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(loading_promise, JS::create_heap_function(realm.heap(), [state, &module_script, on_complete](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. If state.[[ParseError]] is not null, set moduleScript's error to rethrow to state.[[ParseError]] and run
         // 1. If state.[[ParseError]] is not null, set moduleScript's error to rethrow to state.[[ParseError]] and run
         //    onComplete given moduleScript.
         //    onComplete given moduleScript.
         if (!state->parse_error.is_null()) {
         if (!state->parse_error.is_null()) {
@@ -857,7 +857,7 @@ void fetch_descendants_of_and_link_a_module_script(JS::Realm& realm,
         }
         }
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     fetch_client.clean_up_after_running_callback();
     fetch_client.clean_up_after_running_callback();
     realm.vm().pop_execution_context();
     realm.vm().pop_execution_context();

+ 45 - 45
Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp

@@ -127,7 +127,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> readable_stream_cancel(Re
 
 
     // 8. Return the result of reacting to sourceCancelPromise with a fulfillment step that returns undefined.
     // 8. Return the result of reacting to sourceCancelPromise with a fulfillment step that returns undefined.
     auto react_result = WebIDL::react_to_promise(*source_cancel_promise,
     auto react_result = WebIDL::react_to_promise(*source_cancel_promise,
-        [](auto const&) -> WebIDL::ExceptionOr<JS::Value> { return JS::js_undefined(); },
+        JS::create_heap_function(stream.heap(), [](JS::Value) -> WebIDL::ExceptionOr<JS::Value> { return JS::js_undefined(); }),
         {});
         {});
 
 
     return WebIDL::create_resolved_promise(realm, react_result);
     return WebIDL::create_resolved_promise(realm, react_result);
@@ -535,7 +535,7 @@ WebIDL::ExceptionOr<ReadableStreamPair> readable_stream_default_tee(JS::Realm& r
     params->branch2 = MUST(create_readable_stream(realm, start_algorithm, pull_algorithm, cancel2_algorithm));
     params->branch2 = MUST(create_readable_stream(realm, start_algorithm, pull_algorithm, cancel2_algorithm));
 
 
     // 19. Upon rejection of reader.[[closedPromise]] with reason r,
     // 19. Upon rejection of reader.[[closedPromise]] with reason r,
-    WebIDL::upon_rejection(*reader->closed_promise_capability(), [&realm, params, cancel_promise](auto reason) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(*reader->closed_promise_capability(), JS::create_heap_function(realm.heap(), [&realm, params, cancel_promise](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
         auto controller1 = params->branch1->controller()->get<JS::NonnullGCPtr<ReadableStreamDefaultController>>();
         auto controller1 = params->branch1->controller()->get<JS::NonnullGCPtr<ReadableStreamDefaultController>>();
         auto controller2 = params->branch2->controller()->get<JS::NonnullGCPtr<ReadableStreamDefaultController>>();
         auto controller2 = params->branch2->controller()->get<JS::NonnullGCPtr<ReadableStreamDefaultController>>();
 
 
@@ -551,7 +551,7 @@ WebIDL::ExceptionOr<ReadableStreamPair> readable_stream_default_tee(JS::Realm& r
         }
         }
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 20. Return « branch1, branch2 ».
     // 20. Return « branch1, branch2 ».
     return ReadableStreamPair { *params->branch1, *params->branch2 };
     return ReadableStreamPair { *params->branch1, *params->branch2 };
@@ -964,7 +964,7 @@ WebIDL::ExceptionOr<ReadableStreamPair> readable_byte_stream_tee(JS::Realm& real
         // 1. Upon rejection of thisReader.[[closedPromise]] with reason r,
         // 1. Upon rejection of thisReader.[[closedPromise]] with reason r,
         auto closed_promise = this_reader.visit([](auto const& underlying_reader) { return underlying_reader->closed_promise_capability(); });
         auto closed_promise = this_reader.visit([](auto const& underlying_reader) { return underlying_reader->closed_promise_capability(); });
 
 
-        WebIDL::upon_rejection(*closed_promise, [&realm, this_reader, params, cancel_promise](auto reason) -> WebIDL::ExceptionOr<JS::Value> {
+        WebIDL::upon_rejection(*closed_promise, JS::create_heap_function(realm.heap(), [&realm, this_reader, params, cancel_promise](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
             auto controller1 = params->branch1->controller()->get<JS::NonnullGCPtr<ReadableByteStreamController>>();
             auto controller1 = params->branch1->controller()->get<JS::NonnullGCPtr<ReadableByteStreamController>>();
             auto controller2 = params->branch2->controller()->get<JS::NonnullGCPtr<ReadableByteStreamController>>();
             auto controller2 = params->branch2->controller()->get<JS::NonnullGCPtr<ReadableByteStreamController>>();
 
 
@@ -985,7 +985,7 @@ WebIDL::ExceptionOr<ReadableStreamPair> readable_byte_stream_tee(JS::Realm& real
             }
             }
 
 
             return JS::js_undefined();
             return JS::js_undefined();
-        });
+        }));
     });
     });
 
 
     // 15. Let pullWithDefaultReader be the following steps:
     // 15. Let pullWithDefaultReader be the following steps:
@@ -1938,7 +1938,7 @@ WebIDL::ExceptionOr<void> readable_stream_default_controller_can_pull_if_needed(
     auto pull_promise = TRY(controller.pull_algorithm()->function()());
     auto pull_promise = TRY(controller.pull_algorithm()->function()());
 
 
     // 7. Upon fulfillment of pullPromise,
     // 7. Upon fulfillment of pullPromise,
-    WebIDL::upon_fulfillment(*pull_promise, [&](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_fulfillment(*pull_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Set controller.[[pulling]] to false.
         // 1. Set controller.[[pulling]] to false.
         controller.set_pulling(false);
         controller.set_pulling(false);
 
 
@@ -1952,15 +1952,15 @@ WebIDL::ExceptionOr<void> readable_stream_default_controller_can_pull_if_needed(
         }
         }
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 8. Upon rejection of pullPromise with reason e,
     // 8. Upon rejection of pullPromise with reason e,
-    WebIDL::upon_rejection(*pull_promise, [&](auto const& e) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(*pull_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value e) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Perform ! ReadableStreamDefaultControllerError(controller, e).
         // 1. Perform ! ReadableStreamDefaultControllerError(controller, e).
         readable_stream_default_controller_error(controller, e);
         readable_stream_default_controller_error(controller, e);
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     return {};
     return {};
 }
 }
@@ -2345,7 +2345,7 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStre
     auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
     auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
 
 
     // 11. Upon fulfillment of startPromise,
     // 11. Upon fulfillment of startPromise,
-    WebIDL::upon_fulfillment(start_promise, [&](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_fulfillment(start_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Set controller.[[started]] to true.
         // 1. Set controller.[[started]] to true.
         controller.set_started(true);
         controller.set_started(true);
 
 
@@ -2359,15 +2359,15 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStre
         TRY(readable_stream_default_controller_can_pull_if_needed(controller));
         TRY(readable_stream_default_controller_can_pull_if_needed(controller));
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 12. Upon rejection of startPromise with reason r,
     // 12. Upon rejection of startPromise with reason r,
-    WebIDL::upon_rejection(start_promise, [&](auto const& r) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(start_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value r) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Perform ! ReadableStreamDefaultControllerError(controller, r).
         // 1. Perform ! ReadableStreamDefaultControllerError(controller, r).
         readable_stream_default_controller_error(controller, r);
         readable_stream_default_controller_error(controller, r);
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     return {};
     return {};
 }
 }
@@ -2454,7 +2454,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_call_pull_if_needed(Re
     auto pull_promise = TRY(controller.pull_algorithm()->function()());
     auto pull_promise = TRY(controller.pull_algorithm()->function()());
 
 
     // 7. Upon fulfillment of pullPromise,
     // 7. Upon fulfillment of pullPromise,
-    WebIDL::upon_fulfillment(*pull_promise, [&](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_fulfillment(*pull_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Set controller.[[pulling]] to false.
         // 1. Set controller.[[pulling]] to false.
         controller.set_pulling(false);
         controller.set_pulling(false);
 
 
@@ -2468,15 +2468,15 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_call_pull_if_needed(Re
         }
         }
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 8. Upon rejection of pullPromise with reason e,
     // 8. Upon rejection of pullPromise with reason e,
-    WebIDL::upon_rejection(*pull_promise, [&](auto const& error) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(*pull_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value error) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Perform ! ReadableByteStreamControllerError(controller, e).
         // 1. Perform ! ReadableByteStreamControllerError(controller, e).
         readable_byte_stream_controller_error(controller, error);
         readable_byte_stream_controller_error(controller, error);
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     return {};
     return {};
 }
 }
@@ -2967,7 +2967,7 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller(ReadableStream&
     auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
     auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
 
 
     // 16. Upon fulfillment of startPromise,
     // 16. Upon fulfillment of startPromise,
-    WebIDL::upon_fulfillment(start_promise, [&](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_fulfillment(start_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Set controller.[[started]] to true.
         // 1. Set controller.[[started]] to true.
         controller.set_started(true);
         controller.set_started(true);
 
 
@@ -2981,15 +2981,15 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller(ReadableStream&
         TRY(readable_byte_stream_controller_call_pull_if_needed(controller));
         TRY(readable_byte_stream_controller_call_pull_if_needed(controller));
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 17. Upon rejection of startPromise with reason r,
     // 17. Upon rejection of startPromise with reason r,
-    WebIDL::upon_rejection(start_promise, [&](auto const& r) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(start_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value r) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Perform ! ReadableByteStreamControllerError(controller, r).
         // 1. Perform ! ReadableByteStreamControllerError(controller, r).
         readable_byte_stream_controller_error(controller, r);
         readable_byte_stream_controller_error(controller, r);
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     return {};
     return {};
 }
 }
@@ -3577,7 +3577,7 @@ WebIDL::ExceptionOr<void> writable_stream_finish_erroring(WritableStream& stream
     auto promise = TRY(stream.controller()->abort_steps(abort_request.reason));
     auto promise = TRY(stream.controller()->abort_steps(abort_request.reason));
 
 
     // 13. Upon fulfillment of promise,
     // 13. Upon fulfillment of promise,
-    WebIDL::upon_fulfillment(*promise, [&, abort_promise = abort_request.promise](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_fulfillment(*promise, JS::create_heap_function(realm.heap(), [&realm, &stream, abort_promise = abort_request.promise](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Resolve abortRequest’s promise with undefined.
         // 1. Resolve abortRequest’s promise with undefined.
         WebIDL::resolve_promise(realm, abort_promise, JS::js_undefined());
         WebIDL::resolve_promise(realm, abort_promise, JS::js_undefined());
 
 
@@ -3585,10 +3585,10 @@ WebIDL::ExceptionOr<void> writable_stream_finish_erroring(WritableStream& stream
         writable_stream_reject_close_and_closed_promise_if_needed(stream);
         writable_stream_reject_close_and_closed_promise_if_needed(stream);
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 14. Upon rejection of promise with reason reason,
     // 14. Upon rejection of promise with reason reason,
-    WebIDL::upon_rejection(*promise, [&, abort_promise = abort_request.promise](auto const& reason) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(*promise, JS::create_heap_function(realm.heap(), [&realm, &stream, abort_promise = abort_request.promise](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Reject abortRequest’s promise with reason.
         // 1. Reject abortRequest’s promise with reason.
         WebIDL::reject_promise(realm, abort_promise, reason);
         WebIDL::reject_promise(realm, abort_promise, reason);
 
 
@@ -3596,7 +3596,7 @@ WebIDL::ExceptionOr<void> writable_stream_finish_erroring(WritableStream& stream
         writable_stream_reject_close_and_closed_promise_if_needed(stream);
         writable_stream_reject_close_and_closed_promise_if_needed(stream);
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     return {};
     return {};
 }
 }
@@ -4085,7 +4085,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStre
     auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
     auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
 
 
     // 17. Upon fulfillment of startPromise,
     // 17. Upon fulfillment of startPromise,
-    WebIDL::upon_fulfillment(*start_promise, [&](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_fulfillment(*start_promise, JS::create_heap_function(realm.heap(), [&controller, &stream](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Assert: stream.[[state]] is "writable" or "erroring".
         // 1. Assert: stream.[[state]] is "writable" or "erroring".
         auto state = stream.state();
         auto state = stream.state();
         VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
         VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
@@ -4097,10 +4097,10 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStre
         TRY(writable_stream_default_controller_advance_queue_if_needed(controller));
         TRY(writable_stream_default_controller_advance_queue_if_needed(controller));
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 18. Upon rejection of startPromise with reason r,
     // 18. Upon rejection of startPromise with reason r,
-    WebIDL::upon_rejection(*start_promise, [&](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(*start_promise, JS::create_heap_function(realm.heap(), [&stream, &controller](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Assert: stream.[[state]] is "writable" or "erroring".
         // 1. Assert: stream.[[state]] is "writable" or "erroring".
         auto state = stream.state();
         auto state = stream.state();
         VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
         VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
@@ -4112,7 +4112,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStre
         TRY(writable_stream_deal_with_rejection(stream, reason));
         TRY(writable_stream_deal_with_rejection(stream, reason));
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     return {};
     return {};
 }
 }
@@ -4342,20 +4342,20 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_process_close(Writa
     writable_stream_default_controller_clear_algorithms(controller);
     writable_stream_default_controller_clear_algorithms(controller);
 
 
     // 7. Upon fulfillment of sinkClosePromise,
     // 7. Upon fulfillment of sinkClosePromise,
-    WebIDL::upon_fulfillment(*sink_close_promise, [&, stream = stream](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_fulfillment(*sink_close_promise, JS::create_heap_function(controller.heap(), [stream](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Perform ! WritableStreamFinishInFlightClose(stream).
         // 1. Perform ! WritableStreamFinishInFlightClose(stream).
         writable_stream_finish_in_flight_close(*stream);
         writable_stream_finish_in_flight_close(*stream);
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 8. Upon rejection of sinkClosePromise with reason reason,
     // 8. Upon rejection of sinkClosePromise with reason reason,
-    WebIDL::upon_rejection(*sink_close_promise, [&, stream = stream](auto const& reason) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(*sink_close_promise, JS::create_heap_function(controller.heap(), [stream = stream](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Perform ! WritableStreamFinishInFlightCloseWithError(stream, reason).
         // 1. Perform ! WritableStreamFinishInFlightCloseWithError(stream, reason).
         TRY(writable_stream_finish_in_flight_close_with_error(*stream, reason));
         TRY(writable_stream_finish_in_flight_close_with_error(*stream, reason));
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     return {};
     return {};
 }
 }
@@ -4373,7 +4373,7 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_process_write(Writa
     auto sink_write_promise = TRY(controller.write_algorithm()->function()(chunk));
     auto sink_write_promise = TRY(controller.write_algorithm()->function()(chunk));
 
 
     // 4. Upon fulfillment of sinkWritePromise,
     // 4. Upon fulfillment of sinkWritePromise,
-    WebIDL::upon_fulfillment(*sink_write_promise, [&, stream = stream](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_fulfillment(*sink_write_promise, JS::create_heap_function(controller.heap(), [&controller, stream](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. Perform ! WritableStreamFinishInFlightWrite(stream).
         // 1. Perform ! WritableStreamFinishInFlightWrite(stream).
         writable_stream_finish_in_flight_write(*stream);
         writable_stream_finish_in_flight_write(*stream);
 
 
@@ -4399,10 +4399,10 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_process_write(Writa
         TRY(writable_stream_default_controller_advance_queue_if_needed(controller));
         TRY(writable_stream_default_controller_advance_queue_if_needed(controller));
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     // 5. Upon rejection of sinkWritePromise with reason,
     // 5. Upon rejection of sinkWritePromise with reason,
-    WebIDL::upon_rejection(*sink_write_promise, [&, stream = stream](auto const& reason) -> WebIDL::ExceptionOr<JS::Value> {
+    WebIDL::upon_rejection(*sink_write_promise, JS::create_heap_function(controller.heap(), [&controller, stream](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
         // 1. If stream.[[state]] is "writable", perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
         // 1. If stream.[[state]] is "writable", perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
         if (stream->state() == WritableStream::State::Writable)
         if (stream->state() == WritableStream::State::Writable)
             writable_stream_default_controller_clear_algorithms(controller);
             writable_stream_default_controller_clear_algorithms(controller);
@@ -4411,7 +4411,7 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_process_write(Writa
         TRY(writable_stream_finish_in_flight_write_with_error(*stream, reason));
         TRY(writable_stream_finish_in_flight_write_with_error(*stream, reason));
 
 
         return JS::js_undefined();
         return JS::js_undefined();
-    });
+    }));
 
 
     return {};
     return {};
 }
 }
@@ -4700,13 +4700,13 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> transform_stream_default_
     // 2. Return the result of reacting to transformPromise with the following rejection steps given the argument r:
     // 2. Return the result of reacting to transformPromise with the following rejection steps given the argument r:
     auto react_result = WebIDL::react_to_promise(*transform_promise,
     auto react_result = WebIDL::react_to_promise(*transform_promise,
         {},
         {},
-        [&](auto const& reason) -> WebIDL::ExceptionOr<JS::Value> {
+        JS::create_heap_function(realm.heap(), [&controller](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
             // 1. Perform ! TransformStreamError(controller.[[stream]], r).
             // 1. Perform ! TransformStreamError(controller.[[stream]], r).
             TRY(transform_stream_error(*controller.stream(), reason));
             TRY(transform_stream_error(*controller.stream(), reason));
 
 
             // 2. Throw r.
             // 2. Throw r.
             return JS::throw_completion(reason);
             return JS::throw_completion(reason);
-        });
+        }));
 
 
     return WebIDL::create_resolved_promise(realm, react_result);
     return WebIDL::create_resolved_promise(realm, react_result);
 }
 }
@@ -4744,7 +4744,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> transform_stream_default_
     auto react_result = WebIDL::react_to_promise(
     auto react_result = WebIDL::react_to_promise(
         *flush_promise,
         *flush_promise,
         // 1. If flushPromise was fulfilled, then:
         // 1. If flushPromise was fulfilled, then:
-        [readable](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+        JS::create_heap_function(realm.heap(), [readable](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
             // 1. If readable.[[state]] is "errored", throw readable.[[storedError]].
             // 1. If readable.[[state]] is "errored", throw readable.[[storedError]].
             if (readable->state() == ReadableStream::State::Errored)
             if (readable->state() == ReadableStream::State::Errored)
                 return JS::throw_completion(readable->stored_error());
                 return JS::throw_completion(readable->stored_error());
@@ -4754,15 +4754,15 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> transform_stream_default_
             readable_stream_default_controller_close(readable->controller().value().get<JS::NonnullGCPtr<ReadableStreamDefaultController>>());
             readable_stream_default_controller_close(readable->controller().value().get<JS::NonnullGCPtr<ReadableStreamDefaultController>>());
 
 
             return JS::js_undefined();
             return JS::js_undefined();
-        },
+        }),
         // 2. If flushPromise was rejected with reason r, then:
         // 2. If flushPromise was rejected with reason r, then:
-        [&stream, readable](auto const& reason) -> WebIDL::ExceptionOr<JS::Value> {
+        JS::create_heap_function(realm.heap(), [&stream, readable](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
             // 1. Perform ! TransformStreamError(stream, r).
             // 1. Perform ! TransformStreamError(stream, r).
             TRY(transform_stream_error(stream, reason));
             TRY(transform_stream_error(stream, reason));
 
 
             // 2. Throw readable.[[storedError]].
             // 2. Throw readable.[[storedError]].
             return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, readable->stored_error().as_string().utf8_string() };
             return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, readable->stored_error().as_string().utf8_string() };
-        });
+        }));
 
 
     return WebIDL::create_resolved_promise(realm, react_result);
     return WebIDL::create_resolved_promise(realm, react_result);
 }
 }
@@ -4788,7 +4788,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> transform_stream_default_
 
 
         // 3. Return the result of reacting to backpressureChangePromise with the following fulfillment steps:
         // 3. Return the result of reacting to backpressureChangePromise with the following fulfillment steps:
         auto react_result = WebIDL::react_to_promise(*backpressure_change_promise,
         auto react_result = WebIDL::react_to_promise(*backpressure_change_promise,
-            [&stream, controller, chunk](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
+            JS::create_heap_function(realm.heap(), [&stream, controller, chunk](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
                 // 1. Let writable be stream.[[writable]].
                 // 1. Let writable be stream.[[writable]].
                 auto writable = stream.writable();
                 auto writable = stream.writable();
 
 
@@ -4804,7 +4804,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> transform_stream_default_
 
 
                 // 5. Return ! TransformStreamDefaultControllerPerformTransform(controller, chunk).
                 // 5. Return ! TransformStreamDefaultControllerPerformTransform(controller, chunk).
                 return TRY(transform_stream_default_controller_perform_transform(*controller, chunk))->promise();
                 return TRY(transform_stream_default_controller_perform_transform(*controller, chunk))->promise();
-            },
+            }),
             {});
             {});
 
 
         return WebIDL::create_resolved_promise(realm, react_result);
         return WebIDL::create_resolved_promise(realm, react_result);

+ 11 - 19
Userland/Libraries/LibWeb/WebIDL/Promise.cpp

@@ -93,7 +93,7 @@ void reject_promise(JS::Realm& realm, Promise const& promise, JS::Value reason)
 }
 }
 
 
 // https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled
 // https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled
-JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const& promise, Optional<ReactionSteps> on_fulfilled_callback, Optional<ReactionSteps> on_rejected_callback)
+JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const& promise, JS::GCPtr<ReactionSteps> on_fulfilled_callback, JS::GCPtr<ReactionSteps> on_rejected_callback)
 {
 {
     auto& realm = promise.promise()->shape().realm();
     auto& realm = promise.promise()->shape().realm();
     auto& vm = realm.vm();
     auto& vm = realm.vm();
@@ -104,8 +104,8 @@ JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const& promise, Optional<
         auto value = vm.argument(0);
         auto value = vm.argument(0);
 
 
         // 2. If there is a set of steps to be run if the promise was fulfilled, then let result be the result of performing them, given value if T is not undefined. Otherwise, let result be value.
         // 2. If there is a set of steps to be run if the promise was fulfilled, then let result be the result of performing them, given value if T is not undefined. Otherwise, let result be value.
-        auto result = on_fulfilled_callback.has_value()
-            ? TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return (*on_fulfilled_callback)(value); }))
+        auto result = on_fulfilled_callback
+            ? TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return on_fulfilled_callback->function()(value); }))
             : value;
             : value;
 
 
         // 3. Return result, converted to an ECMAScript value.
         // 3. Return result, converted to an ECMAScript value.
@@ -121,8 +121,8 @@ JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const& promise, Optional<
         auto reason = vm.argument(0);
         auto reason = vm.argument(0);
 
 
         // 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.
         // 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); }))
+        auto result = on_rejected_callback
+            ? TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return on_rejected_callback->function()(reason); }))
             : WebIDL::create_rejected_promise(realm, reason)->promise();
             : WebIDL::create_rejected_promise(realm, reason)->promise();
 
 
         // 3. Return result, converted to an ECMAScript value.
         // 3. Return result, converted to an ECMAScript value.
@@ -146,32 +146,24 @@ JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const& promise, Optional<
 }
 }
 
 
 // https://webidl.spec.whatwg.org/#upon-fulfillment
 // https://webidl.spec.whatwg.org/#upon-fulfillment
-JS::NonnullGCPtr<JS::Promise> upon_fulfillment(Promise const& promise, ReactionSteps steps)
+JS::NonnullGCPtr<JS::Promise> upon_fulfillment(Promise const& promise, JS::NonnullGCPtr<ReactionSteps> steps)
 {
 {
     // 1. Return the result of reacting to promise:
     // 1. Return the result of reacting to promise:
     return react_to_promise(promise,
     return react_to_promise(promise,
         // - If promise was fulfilled with value v, then:
         // - If promise was fulfilled with value v, then:
-        [steps = move(steps)](auto value) {
-            // 1. Perform steps with v.
-            // NOTE: The `return` is not immediately obvious, but `steps` may be something like
-            // "Return the result of ...", which we also need to do _here_.
-            return steps(value);
-        },
+        // 1. Perform steps with v.
+        steps,
         {});
         {});
 }
 }
 
 
 // https://webidl.spec.whatwg.org/#upon-rejection
 // https://webidl.spec.whatwg.org/#upon-rejection
-JS::NonnullGCPtr<JS::Promise> upon_rejection(Promise const& promise, ReactionSteps steps)
+JS::NonnullGCPtr<JS::Promise> upon_rejection(Promise const& promise, JS::NonnullGCPtr<ReactionSteps> steps)
 {
 {
     // 1. Return the result of reacting to promise:
     // 1. Return the result of reacting to promise:
     return react_to_promise(promise, {},
     return react_to_promise(promise, {},
         // - If promise was rejected with reason r, then:
         // - If promise was rejected with reason r, then:
-        [steps = move(steps)](auto reason) {
-            // 1. Perform steps with r.
-            // NOTE: The `return` is not immediately obvious, but `steps` may be something like
-            // "Return the result of ...", which we also need to do _here_.
-            return steps(reason);
-        });
+        // 1. Perform steps with r.
+        steps);
 }
 }
 
 
 // https://webidl.spec.whatwg.org/#mark-a-promise-as-handled
 // https://webidl.spec.whatwg.org/#mark-a-promise-as-handled

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

@@ -17,7 +17,7 @@
 namespace Web::WebIDL {
 namespace Web::WebIDL {
 
 
 // NOTE: This is Function, not SafeFunction, because they get stored in a NativeFunction anyway, which will protect captures.
 // NOTE: This is Function, not SafeFunction, because they get stored in a NativeFunction anyway, which will protect captures.
-using ReactionSteps = Function<WebIDL::ExceptionOr<JS::Value>(JS::Value)>;
+using ReactionSteps = JS::HeapFunction<WebIDL::ExceptionOr<JS::Value>(JS::Value)>;
 
 
 // https://webidl.spec.whatwg.org/#es-promise
 // https://webidl.spec.whatwg.org/#es-promise
 using Promise = JS::PromiseCapability;
 using Promise = JS::PromiseCapability;
@@ -27,9 +27,9 @@ JS::NonnullGCPtr<Promise> create_resolved_promise(JS::Realm&, JS::Value);
 JS::NonnullGCPtr<Promise> create_rejected_promise(JS::Realm&, JS::Value);
 JS::NonnullGCPtr<Promise> create_rejected_promise(JS::Realm&, JS::Value);
 void resolve_promise(JS::Realm&, Promise const&, JS::Value = JS::js_undefined());
 void resolve_promise(JS::Realm&, Promise const&, JS::Value = JS::js_undefined());
 void reject_promise(JS::Realm&, Promise const&, JS::Value);
 void reject_promise(JS::Realm&, Promise const&, JS::Value);
-JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const&, Optional<ReactionSteps> on_fulfilled_callback, Optional<ReactionSteps> on_rejected_callback);
-JS::NonnullGCPtr<JS::Promise> upon_fulfillment(Promise const&, ReactionSteps);
-JS::NonnullGCPtr<JS::Promise> upon_rejection(Promise const&, ReactionSteps);
+JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const&, JS::GCPtr<ReactionSteps> on_fulfilled_callback, JS::GCPtr<ReactionSteps> on_rejected_callback);
+JS::NonnullGCPtr<JS::Promise> upon_fulfillment(Promise const&, JS::NonnullGCPtr<ReactionSteps>);
+JS::NonnullGCPtr<JS::Promise> upon_rejection(Promise const&, JS::NonnullGCPtr<ReactionSteps>);
 void mark_promise_as_handled(Promise const&);
 void mark_promise_as_handled(Promise const&);
 void wait_for_all(JS::Realm&, Vector<JS::NonnullGCPtr<Promise>> const& promises, Function<void(Vector<JS::Value> const&)> success_steps, Function<void(JS::Value)> failure_steps);
 void wait_for_all(JS::Realm&, Vector<JS::NonnullGCPtr<Promise>> const& promises, Function<void(Vector<JS::Value> const&)> success_steps, Function<void(JS::Value)> failure_steps);