소스 검색

LibWeb: Port Stream algorithms from JS::SafeFunction to JS::HeapFunction

Shannon Booth 1 년 전
부모
커밋
5f484d200a

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

@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <LibJS/Heap/HeapFunction.h>
 #include <LibJS/Runtime/PromiseCapability.h>
 #include <LibWeb/Bindings/MainThreadVM.h>
 #include <LibWeb/Fetch/BodyInit.h>
@@ -55,14 +56,22 @@ JS::NonnullGCPtr<Body> Body::clone(JS::Realm& realm) const
     // FIXME: 2. Set body’s stream to out1.
     JS::GCPtr<Streams::ReadableStream> out2;
 
-    Streams::StartAlgorithm start_algorithm = []() { return JS::js_undefined(); };
-    Streams::PullAlgorithm pull_algorithm = [&realm]() { return WebIDL::create_resolved_promise(realm, JS::js_undefined()); };
-    Streams::CancelAlgorithm cancel_algorithm = [&realm](auto) { return WebIDL::create_resolved_promise(realm, JS::js_undefined()); };
+    auto start_algorithm = JS::create_heap_function(realm.heap(), []() -> WebIDL::ExceptionOr<JS::Value> {
+        return JS::js_undefined();
+    });
+
+    auto pull_algorithm = JS::create_heap_function(realm.heap(), [&realm]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        return WebIDL::create_resolved_promise(realm, JS::js_undefined());
+    });
+
+    auto cancel_algorithm = JS::create_heap_function(realm.heap(), [&realm](JS::Value) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        return WebIDL::create_resolved_promise(realm, JS::js_undefined());
+    });
 
     if (m_stream->controller()->has<JS::NonnullGCPtr<Streams::ReadableStreamDefaultController>>()) {
-        out2 = Streams::create_readable_stream(realm, move(start_algorithm), move(pull_algorithm), move(cancel_algorithm)).release_value_but_fixme_should_propagate_errors();
+        out2 = Streams::create_readable_stream(realm, start_algorithm, pull_algorithm, cancel_algorithm).release_value_but_fixme_should_propagate_errors();
     } else {
-        out2 = Streams::create_readable_byte_stream(realm, move(start_algorithm), move(pull_algorithm), move(cancel_algorithm)).release_value_but_fixme_should_propagate_errors();
+        out2 = Streams::create_readable_byte_stream(realm, start_algorithm, pull_algorithm, cancel_algorithm).release_value_but_fixme_should_propagate_errors();
     }
 
     // 3. Return a body whose stream is out2 and other members are copied from body.

+ 121 - 119
Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp

@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
  * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
- * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
+ * Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
  * Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
@@ -238,17 +238,16 @@ bool readable_stream_has_default_reader(ReadableStream const& stream)
 }
 
 // https://streams.spec.whatwg.org/#make-size-algorithm-from-size-function
-SizeAlgorithm extract_size_algorithm(QueuingStrategy const& strategy)
+JS::NonnullGCPtr<SizeAlgorithm> extract_size_algorithm(JS::VM& vm, QueuingStrategy const& strategy)
 {
     // 1. If strategy["size"] does not exist, return an algorithm that returns 1.
     if (!strategy.size)
-        return [](auto const&) { return JS::normal_completion(JS::Value(1)); };
+        return JS::create_heap_function(vm.heap(), [](JS::Value) { return JS::normal_completion(JS::Value(1)); });
 
     // 2. Return an algorithm that performs the following steps, taking a chunk argument:
-    return [strategy](auto const& chunk) {
-        // 1. Return the result of invoking strategy["size"] with argument list « chunk ».
-        return WebIDL::invoke_callback(*strategy.size, JS::js_undefined(), chunk);
-    };
+    return JS::create_heap_function(vm.heap(), [size = strategy.size](JS::Value chunk) {
+        return WebIDL::invoke_callback(*size, JS::js_undefined(), chunk);
+    });
 }
 
 // https://streams.spec.whatwg.org/#validate-and-normalize-high-water-mark
@@ -935,7 +934,7 @@ WebIDL::ExceptionOr<void> readable_stream_default_controller_enqueue(ReadableStr
     // 4. Otherwise,
     else {
         // 1. Let result be the result of performing controller.[[strategySizeAlgorithm]], passing in chunk, and interpreting the result as a completion record.
-        auto result = (*controller.strategy_size_algorithm())(chunk);
+        auto result = controller.strategy_size_algorithm()->function()(chunk);
 
         // 2. If result is an abrupt completion,
         if (result.is_abrupt()) {
@@ -995,7 +994,7 @@ WebIDL::ExceptionOr<void> readable_stream_default_controller_can_pull_if_needed(
     controller.set_pulling(true);
 
     // 6. Let pullPromise be the result of performing controller.[[pullAlgorithm]].
-    auto pull_promise = TRY((*controller.pull_algorithm())());
+    auto pull_promise = TRY(controller.pull_algorithm()->function()());
 
     // 7. Upon fulfillment of pullPromise,
     WebIDL::upon_fulfillment(*pull_promise, [&](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
@@ -1316,7 +1315,7 @@ bool readable_stream_default_controller_can_close_or_enqueue(ReadableStreamDefau
 }
 
 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller
-WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStream& stream, ReadableStreamDefaultController& controller, StartAlgorithm&& start_algorithm, PullAlgorithm&& pull_algorithm, CancelAlgorithm&& cancel_algorithm, double high_water_mark, SizeAlgorithm&& size_algorithm)
+WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStream& stream, ReadableStreamDefaultController& controller, JS::NonnullGCPtr<StartAlgorithm> start_algorithm, JS::NonnullGCPtr<PullAlgorithm> pull_algorithm, JS::NonnullGCPtr<CancelAlgorithm> cancel_algorithm, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> size_algorithm)
 {
     auto& realm = stream.realm();
 
@@ -1336,20 +1335,20 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStre
     controller.set_pulling(false);
 
     // 5. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm and controller.[[strategyHWM]] to highWaterMark.
-    controller.set_strategy_size_algorithm(move(size_algorithm));
+    controller.set_strategy_size_algorithm(size_algorithm);
     controller.set_strategy_hwm(high_water_mark);
 
     // 6. Set controller.[[pullAlgorithm]] to pullAlgorithm.
-    controller.set_pull_algorithm(move(pull_algorithm));
+    controller.set_pull_algorithm(pull_algorithm);
 
     // 7. Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
-    controller.set_cancel_algorithm(move(cancel_algorithm));
+    controller.set_cancel_algorithm(cancel_algorithm);
 
     // 8. Set stream.[[controller]] to controller.
     stream.set_controller(ReadableStreamController { controller });
 
     // 9. Let startResult be the result of performing startAlgorithm. (This might throw an exception.)
-    auto start_result = TRY(start_algorithm());
+    auto start_result = TRY(start_algorithm->function()());
 
     // 10. Let startPromise be a promise resolved with startResult.
     auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
@@ -1383,7 +1382,7 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStre
 }
 
 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller-from-underlying-source
-WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underlying_source(ReadableStream& stream, JS::Value underlying_source_value, UnderlyingSource underlying_source, double high_water_mark, SizeAlgorithm&& size_algorithm)
+WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underlying_source(ReadableStream& stream, JS::Value underlying_source_value, UnderlyingSource underlying_source, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> size_algorithm)
 {
     auto& realm = stream.realm();
 
@@ -1391,46 +1390,48 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underly
     auto controller = stream.heap().allocate<ReadableStreamDefaultController>(realm, realm);
 
     // 2. Let startAlgorithm be an algorithm that returns undefined.
-    StartAlgorithm start_algorithm = [] { return JS::js_undefined(); };
+    auto start_algorithm = JS::create_heap_function(realm.heap(), []() -> WebIDL::ExceptionOr<JS::Value> {
+        return JS::js_undefined();
+    });
 
     // 3. Let pullAlgorithm be an algorithm that returns a promise resolved with undefined.
-    PullAlgorithm pull_algorithm = [&realm]() {
+    auto pull_algorithm = JS::create_heap_function(realm.heap(), [&realm]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 4. Let cancelAlgorithm be an algorithm that returns a promise resolved with undefined.
-    CancelAlgorithm cancel_algorithm = [&realm](auto const&) {
+    auto cancel_algorithm = JS::create_heap_function(realm.heap(), [&realm](JS::Value) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 5. If underlyingSourceDict["start"] exists, then set startAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource.
     if (underlying_source.start) {
-        start_algorithm = [controller, underlying_source_value, callback = underlying_source.start]() -> WebIDL::ExceptionOr<JS::Value> {
+        start_algorithm = JS::create_heap_function(realm.heap(), [controller, underlying_source_value, callback = underlying_source.start]() -> WebIDL::ExceptionOr<JS::Value> {
             // Note: callback does not return a promise, so invoke_callback may return an abrupt completion
             return TRY(WebIDL::invoke_callback(*callback, underlying_source_value, controller)).release_value();
-        };
+        });
     }
 
     // 6. If underlyingSourceDict["pull"] exists, then set pullAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource.
     if (underlying_source.pull) {
-        pull_algorithm = [&realm, controller, underlying_source_value, callback = underlying_source.pull]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        pull_algorithm = JS::create_heap_function(realm.heap(), [&realm, controller, underlying_source_value, callback = underlying_source.pull]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
             // Note: callback return a promise, so invoke_callback will never return an abrupt completion
             auto result = MUST_OR_THROW_OOM(WebIDL::invoke_callback(*callback, underlying_source_value, controller)).release_value();
             return WebIDL::create_resolved_promise(realm, result);
-        };
+        });
     }
 
     // 7. If underlyingSourceDict["cancel"] exists, then set cancelAlgorithm to an algorithm which takes an argument reason and returns the result of invoking underlyingSourceDict["cancel"] with argument list « reason » and callback this value underlyingSource.
     if (underlying_source.cancel) {
-        cancel_algorithm = [&realm, underlying_source_value, callback = underlying_source.cancel](auto const& reason) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        cancel_algorithm = JS::create_heap_function(realm.heap(), [&realm, underlying_source_value, callback = underlying_source.cancel](JS::Value reason) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
             // Note: callback return a promise, so invoke_callback will never return an abrupt completion
             auto result = MUST_OR_THROW_OOM(WebIDL::invoke_callback(*callback, underlying_source_value, reason)).release_value();
             return WebIDL::create_resolved_promise(realm, result);
-        };
+        });
     }
 
     // 8. Perform ? SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm).
-    return set_up_readable_stream_default_controller(stream, controller, move(start_algorithm), move(pull_algorithm), move(cancel_algorithm), high_water_mark, move(size_algorithm));
+    return set_up_readable_stream_default_controller(stream, controller, start_algorithm, pull_algorithm, cancel_algorithm, high_water_mark, size_algorithm);
 }
 
 // https://streams.spec.whatwg.org/#readable-byte-stream-controller-call-pull-if-needed
@@ -1459,7 +1460,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_call_pull_if_needed(Re
     controller.set_pulling(true);
 
     // 6. Let pullPromise be the result of performing controller.[[pullAlgorithm]].
-    auto pull_promise = TRY((*controller.pull_algorithm())());
+    auto pull_promise = TRY(controller.pull_algorithm()->function()());
 
     // 7. Upon fulfillment of pullPromise,
     WebIDL::upon_fulfillment(*pull_promise, [&](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
@@ -1712,15 +1713,15 @@ bool readable_byte_stream_controller_should_call_pull(ReadableByteStreamControll
 }
 
 // https://streams.spec.whatwg.org/#create-readable-stream
-WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_stream(JS::Realm& realm, StartAlgorithm&& start_algorithm, PullAlgorithm&& pull_algorithm, CancelAlgorithm&& cancel_algorithm, Optional<double> high_water_mark, Optional<SizeAlgorithm>&& size_algorithm)
+WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_stream(JS::Realm& realm, JS::NonnullGCPtr<StartAlgorithm> start_algorithm, JS::NonnullGCPtr<PullAlgorithm> pull_algorithm, JS::NonnullGCPtr<CancelAlgorithm> cancel_algorithm, Optional<double> high_water_mark, JS::GCPtr<SizeAlgorithm> size_algorithm)
 {
     // 1. If highWaterMark was not passed, set it to 1.
     if (!high_water_mark.has_value())
         high_water_mark = 1.0;
 
     // 2. If sizeAlgorithm was not passed, set it to an algorithm that returns 1.
-    if (!size_algorithm.has_value())
-        size_algorithm = [](auto const&) { return JS::normal_completion(JS::Value(1)); };
+    if (!size_algorithm)
+        size_algorithm = JS::create_heap_function(realm.heap(), [](JS::Value) { return JS::normal_completion(JS::Value(1)); });
 
     // 3. Assert: ! IsNonNegativeNumber(highWaterMark) is true.
     VERIFY(is_non_negative_number(JS::Value { *high_water_mark }));
@@ -1735,14 +1736,14 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_stream(JS:
     auto controller = realm.heap().allocate<ReadableStreamDefaultController>(realm, realm);
 
     // 7. Perform ? SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm).
-    TRY(set_up_readable_stream_default_controller(*stream, *controller, move(start_algorithm), move(pull_algorithm), move(cancel_algorithm), *high_water_mark, move(*size_algorithm)));
+    TRY(set_up_readable_stream_default_controller(*stream, *controller, start_algorithm, pull_algorithm, cancel_algorithm, *high_water_mark, *size_algorithm));
 
     // 8. Return stream.
     return stream;
 }
 
 // https://streams.spec.whatwg.org/#abstract-opdef-createreadablebytestream
-WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_byte_stream(JS::Realm& realm, StartAlgorithm&& start_algorithm, PullAlgorithm&& pull_algorithm, CancelAlgorithm&& cancel_algorithm)
+WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_byte_stream(JS::Realm& realm, JS::NonnullGCPtr<StartAlgorithm> start_algorithm, JS::NonnullGCPtr<PullAlgorithm> pull_algorithm, JS::NonnullGCPtr<CancelAlgorithm> cancel_algorithm)
 {
     // 1. Let stream be a new ReadableStream.
     auto stream = realm.heap().allocate<ReadableStream>(realm, realm);
@@ -1754,14 +1755,14 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_byte_strea
     auto controller = realm.heap().allocate<ReadableByteStreamController>(realm, realm);
 
     // 4. Perform ? SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, 0, undefined).
-    TRY(set_up_readable_byte_stream_controller(stream, controller, move(start_algorithm), move(pull_algorithm), move(cancel_algorithm), 0, JS::js_undefined()));
+    TRY(set_up_readable_byte_stream_controller(stream, controller, start_algorithm, pull_algorithm, cancel_algorithm, 0, JS::js_undefined()));
 
     // 5. Return stream.
     return stream;
 }
 
 // https://streams.spec.whatwg.org/#create-writable-stream
-WebIDL::ExceptionOr<JS::NonnullGCPtr<WritableStream>> create_writable_stream(JS::Realm& realm, StartAlgorithm&& start_algorithm, WriteAlgorithm&& write_algorithm, CloseAlgorithm&& close_algorithm, AbortAlgorithm&& abort_algorithm, double high_water_mark, SizeAlgorithm&& size_algorithm)
+WebIDL::ExceptionOr<JS::NonnullGCPtr<WritableStream>> create_writable_stream(JS::Realm& realm, JS::NonnullGCPtr<StartAlgorithm> start_algorithm, JS::NonnullGCPtr<WriteAlgorithm> write_algorithm, JS::NonnullGCPtr<CloseAlgorithm> close_algorithm, JS::NonnullGCPtr<AbortAlgorithm> abort_algorithm, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> size_algorithm)
 {
     // 1. Assert: ! IsNonNegativeNumber(highWaterMark) is true.
     VERIFY(is_non_negative_number(JS::Value { high_water_mark }));
@@ -1921,7 +1922,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_writer(WritableStreamDe
 }
 
 // https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller
-WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller(ReadableStream& stream, ReadableByteStreamController& controller, StartAlgorithm&& start_algorithm, PullAlgorithm&& pull_algorithm, CancelAlgorithm&& cancel_algorithm, double high_water_mark, JS::Value auto_allocate_chunk_size)
+WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller(ReadableStream& stream, ReadableByteStreamController& controller, JS::NonnullGCPtr<StartAlgorithm> start_algorithm, JS::NonnullGCPtr<PullAlgorithm> pull_algorithm, JS::NonnullGCPtr<CancelAlgorithm> cancel_algorithm, double high_water_mark, JS::Value auto_allocate_chunk_size)
 {
     auto& realm = stream.realm();
 
@@ -1958,10 +1959,10 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller(ReadableStream&
     controller.set_strategy_hwm(high_water_mark);
 
     // 9. Set controller.[[pullAlgorithm]] to pullAlgorithm.
-    controller.set_pull_algorithm(move(pull_algorithm));
+    controller.set_pull_algorithm(pull_algorithm);
 
     // 10. Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
-    controller.set_cancel_algorithm(move(cancel_algorithm));
+    controller.set_cancel_algorithm(cancel_algorithm);
 
     // 11. Set controller.[[autoAllocateChunkSize]] to autoAllocateChunkSize.
     if (auto_allocate_chunk_size.is_integral_number())
@@ -1974,7 +1975,7 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller(ReadableStream&
     stream.set_controller(ReadableStreamController { controller });
 
     // 14. Let startResult be the result of performing startAlgorithm.
-    auto start_result = TRY(start_algorithm());
+    auto start_result = TRY(start_algorithm->function()());
 
     // 15. Let startPromise be a promise resolved with startResult.
     auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
@@ -2338,19 +2339,19 @@ PullIntoDescriptor readable_byte_stream_controller_shift_pending_pull_into(Reada
 }
 
 // https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support
-WebIDL::ExceptionOr<void> set_up_readable_stream_controller_with_byte_reading_support(ReadableStream& stream, Optional<PullAlgorithm>&& pull_algorithm, Optional<CancelAlgorithm>&& cancel_algorithm, double high_water_mark)
+WebIDL::ExceptionOr<void> set_up_readable_stream_controller_with_byte_reading_support(ReadableStream& stream, JS::GCPtr<PullAlgorithm> pull_algorithm, JS::GCPtr<CancelAlgorithm> cancel_algorithm, double high_water_mark)
 {
     auto& realm = stream.realm();
 
     // 1. Let startAlgorithm be an algorithm that returns undefined.
-    StartAlgorithm start_algorithm = [] { return JS::js_undefined(); };
+    auto start_algorithm = JS::create_heap_function(realm.heap(), []() -> WebIDL::ExceptionOr<JS::Value> { return JS::js_undefined(); });
 
     // 2. Let pullAlgorithmWrapper be an algorithm that runs these steps:
-    PullAlgorithm pull_algorithm_wrapper = [&realm, pull_algorithm = move(pull_algorithm)]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+    auto pull_algorithm_wrapper = JS::create_heap_function(realm.heap(), [&realm, pull_algorithm]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         // 1. Let result be the result of running pullAlgorithm, if pullAlgorithm was given, or null otherwise. If this throws an exception e, return a promise rejected with e.
         JS::GCPtr<JS::PromiseCapability> result = nullptr;
-        if (pull_algorithm.has_value())
-            result = TRY(pull_algorithm.value()());
+        if (pull_algorithm)
+            result = TRY(pull_algorithm->function()());
 
         // 2. If result is a Promise, then return result.
         if (result != nullptr)
@@ -2358,14 +2359,14 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_controller_with_byte_reading_su
 
         // 3. Return a promise resolved with undefined.
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 3. Let cancelAlgorithmWrapper be an algorithm that runs these steps:
-    CancelAlgorithm cancel_algorithm_wrapper = [&realm, cancel_algorithm = move(cancel_algorithm)](auto const& c) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+    auto cancel_algorithm_wrapper = JS::create_heap_function(realm.heap(), [&realm, cancel_algorithm](JS::Value c) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         // 1. Let result be the result of running cancelAlgorithm, if cancelAlgorithm was given, or null otherwise. If this throws an exception e, return a promise rejected with e.
         JS::GCPtr<JS::PromiseCapability> result = nullptr;
-        if (cancel_algorithm.has_value())
-            result = TRY(cancel_algorithm.value()(c));
+        if (cancel_algorithm)
+            result = TRY(cancel_algorithm->function()(c));
 
         // 2. If result is a Promise, then return result.
         if (result != nullptr)
@@ -2373,14 +2374,14 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_controller_with_byte_reading_su
 
         // 3. Return a promise resolved with undefined.
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 4. Perform ! InitializeReadableStream(stream).
     // 5. Let controller be a new ReadableByteStreamController.
     auto controller = stream.heap().allocate<ReadableByteStreamController>(realm, realm);
 
     // 6. Perform ! SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithmWrapper, cancelAlgorithmWrapper, highWaterMark, undefined).
-    TRY(set_up_readable_byte_stream_controller(stream, controller, move(start_algorithm), move(pull_algorithm_wrapper), move(cancel_algorithm_wrapper), high_water_mark, JS::js_undefined()));
+    TRY(set_up_readable_byte_stream_controller(stream, controller, start_algorithm, pull_algorithm_wrapper, cancel_algorithm_wrapper, high_water_mark, JS::js_undefined()));
 
     return {};
 }
@@ -3046,7 +3047,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_default_w
 }
 
 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller
-WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStream& stream, WritableStreamDefaultController& controller, StartAlgorithm&& start_algorithm, WriteAlgorithm&& write_algorithm, CloseAlgorithm&& close_algorithm, AbortAlgorithm&& abort_algorithm, double high_water_mark, SizeAlgorithm&& size_algorithm)
+WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStream& stream, WritableStreamDefaultController& controller, JS::NonnullGCPtr<StartAlgorithm> start_algorithm, JS::NonnullGCPtr<WriteAlgorithm> write_algorithm, JS::NonnullGCPtr<CloseAlgorithm> close_algorithm, JS::NonnullGCPtr<AbortAlgorithm> abort_algorithm, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> size_algorithm)
 {
     auto& realm = stream.realm();
 
@@ -3071,19 +3072,19 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStre
     controller.set_started(false);
 
     // 8. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm.
-    controller.set_strategy_size_algorithm(move(size_algorithm));
+    controller.set_strategy_size_algorithm(size_algorithm);
 
     // 9. Set controller.[[strategyHWM]] to highWaterMark.
     controller.set_strategy_hwm(high_water_mark);
 
     // 10. Set controller.[[writeAlgorithm]] to writeAlgorithm.
-    controller.set_write_algorithm(move(write_algorithm));
+    controller.set_write_algorithm(write_algorithm);
 
     // 11. Set controller.[[closeAlgorithm]] to closeAlgorithm.
-    controller.set_close_algorithm(move(close_algorithm));
+    controller.set_close_algorithm(close_algorithm);
 
     // 12. Set controller.[[abortAlgorithm]] to abortAlgorithm.
-    controller.set_abort_algorithm(move(abort_algorithm));
+    controller.set_abort_algorithm(abort_algorithm);
 
     // 13. Let backpressure be ! WritableStreamDefaultControllerGetBackpressure(controller).
     auto backpressure = writable_stream_default_controller_get_backpressure(controller);
@@ -3092,7 +3093,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStre
     writable_stream_update_backpressure(stream, backpressure);
 
     // 15. Let startResult be the result of performing startAlgorithm. (This may throw an exception.)
-    auto start_result = TRY(start_algorithm());
+    auto start_result = TRY(start_algorithm->function()());
 
     // 16. Let startPromise be a promise resolved with startResult.
     auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
@@ -3131,7 +3132,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStre
 }
 
 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink
-WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underlying_sink(WritableStream& stream, JS::Value underlying_sink_value, UnderlyingSink& underlying_sink, double high_water_mark, SizeAlgorithm&& size_algorithm)
+WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underlying_sink(WritableStream& stream, JS::Value underlying_sink_value, UnderlyingSink& underlying_sink, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> size_algorithm)
 {
     auto& realm = stream.realm();
 
@@ -3139,60 +3140,60 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underly
     auto controller = realm.heap().allocate<WritableStreamDefaultController>(realm, realm);
 
     // 2. Let startAlgorithm be an algorithm that returns undefined.
-    StartAlgorithm start_algorithm = [] { return JS::js_undefined(); };
+    auto start_algorithm = JS::create_heap_function(realm.heap(), []() -> WebIDL::ExceptionOr<JS::Value> { return JS::js_undefined(); });
 
     // 3. Let writeAlgorithm be an algorithm that returns a promise resolved with undefined.
-    WriteAlgorithm write_algorithm = [&realm](auto const&) {
+    auto write_algorithm = JS::create_heap_function(realm.heap(), [&realm](JS::Value) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 4. Let closeAlgorithm be an algorithm that returns a promise resolved with undefined.
-    CloseAlgorithm close_algorithm = [&realm] {
+    auto close_algorithm = JS::create_heap_function(realm.heap(), [&realm]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 5. Let abortAlgorithm be an algorithm that returns a promise resolved with undefined.
-    AbortAlgorithm abort_algorithm = [&realm](auto const&) {
+    auto abort_algorithm = JS::create_heap_function(realm.heap(), [&realm](JS::Value) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 6. If underlyingSinkDict["start"] exists, then set startAlgorithm to an algorithm which returns the result of invoking underlyingSinkDict["start"] with argument list « controller » and callback this value underlyingSink.
     if (underlying_sink.start) {
-        start_algorithm = [controller, underlying_sink_value, callback = underlying_sink.start]() -> WebIDL::ExceptionOr<JS::Value> {
+        start_algorithm = JS::create_heap_function(realm.heap(), [controller, underlying_sink_value, callback = underlying_sink.start]() -> WebIDL::ExceptionOr<JS::Value> {
             // Note: callback does not return a promise, so invoke_callback may return an abrupt completion
             return TRY(WebIDL::invoke_callback(*callback, underlying_sink_value, controller)).release_value();
-        };
+        });
     }
 
     // 7. If underlyingSinkDict["write"] exists, then set writeAlgorithm to an algorithm which takes an argument chunk and returns the result of invoking underlyingSinkDict["write"] with argument list « chunk, controller » and callback this value underlyingSink.
     if (underlying_sink.write) {
-        write_algorithm = [&realm, controller, underlying_sink_value, callback = underlying_sink.write](JS::Value chunk) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        write_algorithm = JS::create_heap_function(realm.heap(), [&realm, controller, underlying_sink_value, callback = underlying_sink.write](JS::Value chunk) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
             // Note: callback return a promise, so invoke_callback will never return an abrupt completion
             auto result = MUST_OR_THROW_OOM(WebIDL::invoke_callback(*callback, underlying_sink_value, chunk, controller)).release_value();
             return WebIDL::create_resolved_promise(realm, result);
-        };
+        });
     }
 
     // 8. If underlyingSinkDict["close"] exists, then set closeAlgorithm to an algorithm which returns the result of invoking underlyingSinkDict["close"] with argument list «» and callback this value underlyingSink.
     if (underlying_sink.close) {
-        close_algorithm = [&realm, underlying_sink_value, callback = underlying_sink.close]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        close_algorithm = JS::create_heap_function(realm.heap(), [&realm, underlying_sink_value, callback = underlying_sink.close]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
             // Note: callback return a promise, so invoke_callback will never return an abrupt completion
             auto result = MUST_OR_THROW_OOM(WebIDL::invoke_callback(*callback, underlying_sink_value)).release_value();
             return WebIDL::create_resolved_promise(realm, result);
-        };
+        });
     }
 
     // 9. If underlyingSinkDict["abort"] exists, then set abortAlgorithm to an algorithm which takes an argument reason and returns the result of invoking underlyingSinkDict["abort"] with argument list « reason » and callback this value underlyingSink.
     if (underlying_sink.abort) {
-        abort_algorithm = [&realm, underlying_sink_value, callback = underlying_sink.abort](JS::Value reason) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        abort_algorithm = JS::create_heap_function(realm.heap(), [&realm, underlying_sink_value, callback = underlying_sink.abort](JS::Value reason) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
             // Note: callback return a promise, so invoke_callback will never return an abrupt completion
             auto result = MUST_OR_THROW_OOM(WebIDL::invoke_callback(*callback, underlying_sink_value, reason)).release_value();
             return WebIDL::create_resolved_promise(realm, result);
-        };
+        });
     }
 
     // 10. Perform ? SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm).
-    TRY(set_up_writable_stream_default_controller(stream, controller, move(start_algorithm), move(write_algorithm), move(close_algorithm), move(abort_algorithm), high_water_mark, move(size_algorithm)));
+    TRY(set_up_writable_stream_default_controller(stream, controller, start_algorithm, write_algorithm, close_algorithm, abort_algorithm, high_water_mark, size_algorithm));
 
     return {};
 }
@@ -3316,7 +3317,7 @@ bool writable_stream_default_controller_get_backpressure(WritableStreamDefaultCo
 WebIDL::ExceptionOr<JS::Value> writable_stream_default_controller_get_chunk_size(WritableStreamDefaultController& controller, JS::Value chunk)
 {
     // 1. Let returnValue be the result of performing controller.[[strategySizeAlgorithm]], passing in chunk, and interpreting the result as a completion record.
-    auto return_value = (*controller.strategy_size_algorithm())(chunk);
+    auto return_value = controller.strategy_size_algorithm()->function()(chunk);
 
     // 2. If returnValue is an abrupt completion,
     if (return_value.is_abrupt()) {
@@ -3354,7 +3355,7 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_process_close(Writa
     VERIFY(controller.queue().is_empty());
 
     // 5. Let sinkClosePromise be the result of performing controller.[[closeAlgorithm]].
-    auto sink_close_promise = TRY((*controller.close_algorithm())());
+    auto sink_close_promise = TRY(controller.close_algorithm()->function()());
 
     // 6. Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
     writable_stream_default_controller_clear_algorithms(controller);
@@ -3388,7 +3389,7 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_process_write(Writa
     writable_stream_mark_first_write_request_in_flight(*stream);
 
     // 3. Let sinkWritePromise be the result of performing controller.[[writeAlgorithm]], passing in chunk.
-    auto sink_write_promise = TRY((*controller.write_algorithm())(chunk));
+    auto sink_write_promise = TRY(controller.write_algorithm()->function()(chunk));
 
     // 4. Upon fulfillment of sinkWritePromise,
     WebIDL::upon_fulfillment(*sink_write_promise, [&, stream = stream](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
@@ -3472,56 +3473,57 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_write(WritableStrea
 }
 
 // https://streams.spec.whatwg.org/#initialize-transform-stream
-WebIDL::ExceptionOr<void> initialize_transform_stream(TransformStream& stream, JS::NonnullGCPtr<JS::PromiseCapability> start_promise, double writable_high_water_mark, SizeAlgorithm&& writable_size_algorithm, double readable_high_water_mark, SizeAlgorithm&& readable_size_algorithm)
+WebIDL::ExceptionOr<void> initialize_transform_stream(TransformStream& stream, JS::NonnullGCPtr<JS::PromiseCapability> start_promise, double writable_high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> writable_size_algorithm, double readable_high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> readable_size_algorithm)
 {
     auto& realm = stream.realm();
 
     // 1. Let startAlgorithm be an algorithm that returns startPromise.
-    StartAlgorithm writable_start_algorithm = [start_promise] {
+    auto writable_start_algorithm = JS::create_heap_function(realm.heap(), [start_promise]() -> WebIDL::ExceptionOr<JS::Value> {
         return start_promise->promise();
-    };
-    StartAlgorithm readable_start_algorithm = [start_promise] {
+    });
+
+    auto readable_start_algorithm = JS::create_heap_function(realm.heap(), [start_promise]() -> WebIDL::ExceptionOr<JS::Value> {
         return start_promise->promise();
-    };
+    });
 
     // 2. Let writeAlgorithm be the following steps, taking a chunk argument:
-    WriteAlgorithm write_algorithm = [&stream](JS::Value chunk) {
+    auto write_algorithm = JS::create_heap_function(realm.heap(), [&stream](JS::Value chunk) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         // 1. Return ! TransformStreamDefaultSinkWriteAlgorithm(stream, chunk).
         return transform_stream_default_sink_write_algorithm(stream, chunk);
-    };
+    });
 
     // 3. Let abortAlgorithm be the following steps, taking a reason argument:
-    AbortAlgorithm abort_algorithm = [&stream](JS::Value reason) {
+    auto abort_algorithm = JS::create_heap_function(realm.heap(), [&stream](JS::Value reason) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         // 1. Return ! TransformStreamDefaultSinkAbortAlgorithm(stream, reason).
         return transform_stream_default_sink_abort_algorithm(stream, reason);
-    };
+    });
 
     // 4. Let closeAlgorithm be the following steps:
-    CloseAlgorithm close_algorithm = [&stream] {
+    auto close_algorithm = JS::create_heap_function(realm.heap(), [&stream]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         // 1. Return ! TransformStreamDefaultSinkCloseAlgorithm(stream).
         return transform_stream_default_sink_close_algorithm(stream);
-    };
+    });
 
     // 5. Set stream.[[writable]] to ! CreateWritableStream(startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark, writableSizeAlgorithm).
-    stream.set_writable(TRY(create_writable_stream(realm, move(writable_start_algorithm), move(write_algorithm), move(close_algorithm), move(abort_algorithm), writable_high_water_mark, move(writable_size_algorithm))));
+    stream.set_writable(TRY(create_writable_stream(realm, writable_start_algorithm, write_algorithm, close_algorithm, abort_algorithm, writable_high_water_mark, writable_size_algorithm)));
 
     // 6. Let pullAlgorithm be the following steps:
-    PullAlgorithm pull_algorithm = [&stream] {
+    auto pull_algorithm = JS::create_heap_function(realm.heap(), [&stream]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         // 1. Return ! TransformStreamDefaultSourcePullAlgorithm(stream).
         return transform_stream_default_source_pull_algorithm(stream);
-    };
+    });
 
     // 7. Let cancelAlgorithm be the following steps, taking a reason argument:
-    CancelAlgorithm cancel_algorithm = [&stream, &realm](JS::Value reason) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+    auto cancel_algorithm = JS::create_heap_function(realm.heap(), [&stream, &realm](JS::Value reason) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         // 1. Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, reason).
         TRY(transform_stream_error_writable_and_unblock_write(stream, reason));
 
         // 2. Return a promise resolved with undefined.
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 8. Set stream.[[readable]] to ! CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, readableHighWaterMark, readableSizeAlgorithm).
-    stream.set_readable(TRY(create_readable_stream(realm, move(readable_start_algorithm), move(pull_algorithm), move(cancel_algorithm), readable_high_water_mark, move(readable_size_algorithm))));
+    stream.set_readable(TRY(create_readable_stream(realm, readable_start_algorithm, pull_algorithm, cancel_algorithm, readable_high_water_mark, readable_size_algorithm)));
 
     // 9. Set stream.[[backpressure]] and stream.[[backpressureChangePromise]] to undefined.
     stream.set_backpressure({});
@@ -3537,7 +3539,7 @@ WebIDL::ExceptionOr<void> initialize_transform_stream(TransformStream& stream, J
 }
 
 // https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller
-void set_up_transform_stream_default_controller(TransformStream& stream, TransformStreamDefaultController& controller, TransformAlgorithm&& transform_algorithm, FlushAlgorithm&& flush_algorithm)
+void set_up_transform_stream_default_controller(TransformStream& stream, TransformStreamDefaultController& controller, JS::NonnullGCPtr<TransformAlgorithm> transform_algorithm, JS::NonnullGCPtr<FlushAlgorithm> flush_algorithm)
 {
     // 1. Assert: stream implements TransformStream.
     // 2. Assert: stream.[[controller]] is undefined.
@@ -3550,10 +3552,10 @@ void set_up_transform_stream_default_controller(TransformStream& stream, Transfo
     stream.set_controller(controller);
 
     // 5. Set controller.[[transformAlgorithm]] to transformAlgorithm.
-    controller.set_transform_algorithm(move(transform_algorithm));
+    controller.set_transform_algorithm(transform_algorithm);
 
     // 6. Set controller.[[flushAlgorithm]] to flushAlgorithm.
-    controller.set_flush_algorithm(move(flush_algorithm));
+    controller.set_flush_algorithm(flush_algorithm);
 }
 
 // https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer
@@ -3566,7 +3568,7 @@ WebIDL::ExceptionOr<void> set_up_transform_stream_default_controller_from_transf
     auto controller = realm.heap().allocate<TransformStreamDefaultController>(realm, realm);
 
     // 2. Let transformAlgorithm be the following steps, taking a chunk argument:
-    TransformAlgorithm transform_algorithm = [controller, &realm, &vm](JS::Value chunk) {
+    auto transform_algorithm = JS::create_heap_function(realm.heap(), [controller, &realm, &vm](JS::Value chunk) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         // 1. Let result be TransformStreamDefaultControllerEnqueue(controller, chunk).
         auto result = transform_stream_default_controller_enqueue(*controller, chunk);
 
@@ -3578,41 +3580,41 @@ WebIDL::ExceptionOr<void> set_up_transform_stream_default_controller_from_transf
 
         // 3. Otherwise, return a promise resolved with undefined.
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 3. Let flushAlgorithm be an algorithm which returns a promise resolved with undefined.
-    FlushAlgorithm flush_algorithm = [&realm] {
+    auto flush_algorithm = JS::create_heap_function(realm.heap(), [&realm]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 4. If transformerDict["transform"] exists, set transformAlgorithm to an algorithm which takes an argument chunk
     //    and returns the result of invoking transformerDict["transform"] with argument list « chunk, controller » and
     //    callback this value transformer.
     if (transformer_dict.transform) {
-        transform_algorithm = [controller, &realm, transformer, callback = transformer_dict.transform](JS::Value chunk) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        transform_algorithm = JS::create_heap_function(realm.heap(), [controller, &realm, transformer, callback = transformer_dict.transform](JS::Value chunk) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
             // Note: callback does not return a promise, so invoke_callback may return an abrupt completion
             auto result = WebIDL::invoke_callback(*callback, transformer, chunk, controller);
             if (result.is_error())
                 return WebIDL::create_rejected_promise(realm, *result.release_value());
 
             return WebIDL::create_resolved_promise(realm, *result.release_value());
-        };
+        });
     }
     // 5. If transformerDict["flush"] exists, set flushAlgorithm to an algorithm which returns the result of invoking
     //    transformerDict["flush"] with argument list « controller » and callback this value transformer.
     if (transformer_dict.flush) {
-        flush_algorithm = [&realm, transformer, callback = transformer_dict.flush, controller]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        flush_algorithm = JS::create_heap_function(realm.heap(), [&realm, transformer, callback = transformer_dict.flush, controller]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
             // Note: callback does not return a promise, so invoke_callback may return an abrupt completion
             auto result = WebIDL::invoke_callback(*callback, transformer, controller);
             if (result.is_error()) {
                 return WebIDL::create_rejected_promise(realm, *result.release_value());
             }
             return WebIDL::create_resolved_promise(realm, *result.release_value());
-        };
+        });
     }
 
     // 6. Perform ! SetUpTransformStreamDefaultController(stream, controller, transformAlgorithm, flushAlgorithm).
-    set_up_transform_stream_default_controller(stream, *controller, move(transform_algorithm), move(flush_algorithm));
+    set_up_transform_stream_default_controller(stream, *controller, transform_algorithm, flush_algorithm);
 
     return {};
 }
@@ -3712,7 +3714,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> transform_stream_default_
     auto& realm = controller.realm();
 
     // 1. Let transformPromise be the result of performing controller.[[transformAlgorithm]], passing chunk.
-    auto transform_promise = TRY((*controller.transform_algorithm())(chunk));
+    auto transform_promise = TRY(controller.transform_algorithm()->function()(chunk));
 
     // 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,
@@ -3752,7 +3754,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> transform_stream_default_
     auto controller = stream.controller();
 
     // 3. Let flushPromise be the result of performing controller.[[flushAlgorithm]].
-    auto flush_promise = TRY((*controller->flush_algorithm())());
+    auto flush_promise = TRY(controller->flush_algorithm()->function()());
 
     // 4. Perform ! TransformStreamDefaultControllerClearAlgorithms(controller).
     transform_stream_default_controller_clear_algorithms(*controller);
@@ -3976,42 +3978,42 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller_from_underlying
     auto controller = stream.heap().allocate<ReadableByteStreamController>(realm, realm);
 
     // 2. Let startAlgorithm be an algorithm that returns undefined.
-    StartAlgorithm start_algorithm = [] { return JS::js_undefined(); };
+    auto start_algorithm = JS::create_heap_function(realm.heap(), []() -> WebIDL::ExceptionOr<JS::Value> { return JS::js_undefined(); });
 
     // 3. Let pullAlgorithm be an algorithm that returns a promise resolved with undefined.
-    PullAlgorithm pull_algorithm = [&realm]() {
+    auto pull_algorithm = JS::create_heap_function(realm.heap(), [&realm]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 4. Let cancelAlgorithm be an algorithm that returns a promise resolved with undefined.
-    CancelAlgorithm cancel_algorithm = [&realm](auto const&) {
+    auto cancel_algorithm = JS::create_heap_function(realm.heap(), [&realm](JS::Value) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
         return WebIDL::create_resolved_promise(realm, JS::js_undefined());
-    };
+    });
 
     // 5. If underlyingSourceDict["start"] exists, then set startAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource.
     if (underlying_source_dict.start) {
-        start_algorithm = [controller, underlying_source, callback = underlying_source_dict.start]() -> WebIDL::ExceptionOr<JS::Value> {
+        start_algorithm = JS::create_heap_function(realm.heap(), [controller, underlying_source, callback = underlying_source_dict.start]() -> WebIDL::ExceptionOr<JS::Value> {
             // Note: callback does not return a promise, so invoke_callback may return an abrupt completion
             return TRY(WebIDL::invoke_callback(*callback, underlying_source, controller)).release_value();
-        };
+        });
     }
 
     // 6. If underlyingSourceDict["pull"] exists, then set pullAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource.
     if (underlying_source_dict.pull) {
-        pull_algorithm = [&realm, controller, underlying_source, callback = underlying_source_dict.pull]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        pull_algorithm = JS::create_heap_function(realm.heap(), [&realm, controller, underlying_source, callback = underlying_source_dict.pull]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
             // Note: callback return a promise, so invoke_callback will never return an abrupt completion
             auto result = MUST_OR_THROW_OOM(WebIDL::invoke_callback(*callback, underlying_source, controller)).release_value();
             return WebIDL::create_resolved_promise(realm, result);
-        };
+        });
     }
 
     // 7. If underlyingSourceDict["cancel"] exists, then set cancelAlgorithm to an algorithm which takes an argument reason and returns the result of invoking underlyingSourceDict["cancel"] with argument list « reason » and callback this value underlyingSource.
     if (underlying_source_dict.cancel) {
-        cancel_algorithm = [&realm, underlying_source, callback = underlying_source_dict.cancel](auto const& reason) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
+        cancel_algorithm = JS::create_heap_function(realm.heap(), [&realm, underlying_source, callback = underlying_source_dict.cancel](JS::Value reason) -> WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> {
             // Note: callback return a promise, so invoke_callback will never return an abrupt completion
             auto result = MUST_OR_THROW_OOM(WebIDL::invoke_callback(*callback, underlying_source, reason)).release_value();
             return WebIDL::create_resolved_promise(realm, result);
-        };
+        });
     }
 
     // 8. Let autoAllocateChunkSize be underlyingSourceDict["autoAllocateChunkSize"], if it exists, or undefined otherwise.
@@ -4024,7 +4026,7 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller_from_underlying
         return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot use an auto allocate chunk size of 0"sv };
 
     // 10. Perform ? SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, autoAllocateChunkSize).
-    return set_up_readable_byte_stream_controller(stream, controller, move(start_algorithm), move(pull_algorithm), move(cancel_algorithm), high_water_mark, auto_allocate_chunk_size);
+    return set_up_readable_byte_stream_controller(stream, controller, start_algorithm, pull_algorithm, cancel_algorithm, high_water_mark, auto_allocate_chunk_size);
 }
 
 }

+ 22 - 22
Userland/Libraries/LibWeb/Streams/AbstractOperations.h

@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
  * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
- * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
+ * Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
  * Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
@@ -18,21 +18,21 @@
 
 namespace Web::Streams {
 
-using SizeAlgorithm = JS::SafeFunction<JS::Completion(JS::Value)>;
-using PullAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>()>;
-using CancelAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>(JS::Value)>;
-using StartAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::Value>()>;
-using AbortAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>(JS::Value)>;
-using CloseAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>()>;
-using WriteAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>(JS::Value)>;
-using FlushAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>()>;
-using TransformAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>(JS::Value)>;
+using SizeAlgorithm = JS::HeapFunction<JS::Completion(JS::Value)>;
+using PullAlgorithm = JS::HeapFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>()>;
+using CancelAlgorithm = JS::HeapFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>(JS::Value)>;
+using StartAlgorithm = JS::HeapFunction<WebIDL::ExceptionOr<JS::Value>()>;
+using AbortAlgorithm = JS::HeapFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>(JS::Value)>;
+using CloseAlgorithm = JS::HeapFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>()>;
+using WriteAlgorithm = JS::HeapFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>(JS::Value)>;
+using FlushAlgorithm = JS::HeapFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>()>;
+using TransformAlgorithm = JS::HeapFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>>(JS::Value)>;
 
 WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStreamDefaultReader>> acquire_readable_stream_default_reader(ReadableStream&);
 WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStreamBYOBReader>> acquire_readable_stream_byob_reader(ReadableStream&);
 bool is_readable_stream_locked(ReadableStream const&);
 
-SizeAlgorithm extract_size_algorithm(QueuingStrategy const&);
+JS::NonnullGCPtr<SizeAlgorithm> extract_size_algorithm(JS::VM&, QueuingStrategy const&);
 WebIDL::ExceptionOr<double> extract_high_water_mark(QueuingStrategy const&, double default_hwm);
 
 void readable_stream_close(ReadableStream&);
@@ -73,10 +73,10 @@ void readable_stream_default_controller_clear_algorithms(ReadableStreamDefaultCo
 void readable_stream_default_controller_error(ReadableStreamDefaultController&, JS::Value error);
 Optional<double> readable_stream_default_controller_get_desired_size(ReadableStreamDefaultController&);
 bool readable_stream_default_controller_can_close_or_enqueue(ReadableStreamDefaultController&);
-WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStream&, ReadableStreamDefaultController&, StartAlgorithm&&, PullAlgorithm&&, CancelAlgorithm&&, double high_water_mark, SizeAlgorithm&&);
-WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underlying_source(ReadableStream&, JS::Value underlying_source_value, UnderlyingSource, double high_water_mark, SizeAlgorithm&&);
-WebIDL::ExceptionOr<void> set_up_readable_stream_controller_with_byte_reading_support(ReadableStream&, Optional<PullAlgorithm>&& = {}, Optional<CancelAlgorithm>&& = {}, double high_water_mark = 0);
-WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller(ReadableStream&, ReadableByteStreamController&, StartAlgorithm&&, PullAlgorithm&&, CancelAlgorithm&&, double high_water_mark, JS::Value auto_allocate_chunk_size);
+WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStream&, ReadableStreamDefaultController&, JS::NonnullGCPtr<StartAlgorithm>, JS::NonnullGCPtr<PullAlgorithm>, JS::NonnullGCPtr<CancelAlgorithm>, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm>);
+WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underlying_source(ReadableStream&, JS::Value underlying_source_value, UnderlyingSource, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm>);
+WebIDL::ExceptionOr<void> set_up_readable_stream_controller_with_byte_reading_support(ReadableStream&, JS::GCPtr<PullAlgorithm> = {}, JS::GCPtr<CancelAlgorithm> = {}, double high_water_mark = 0);
+WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller(ReadableStream&, ReadableByteStreamController&, JS::NonnullGCPtr<StartAlgorithm>, JS::NonnullGCPtr<PullAlgorithm>, JS::NonnullGCPtr<CancelAlgorithm>, double high_water_mark, JS::Value auto_allocate_chunk_size);
 WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller_from_underlying_source(ReadableStream&, JS::Value underlying_source, UnderlyingSource const& underlying_source_dict, double high_water_mark);
 JS::GCPtr<ReadableStreamBYOBRequest> readable_byte_stream_controller_get_byob_request(JS::NonnullGCPtr<ReadableByteStreamController>);
 
@@ -108,9 +108,9 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_handle_queue_drain(Rea
 void readable_byte_stream_controller_invalidate_byob_request(ReadableByteStreamController&);
 bool readable_byte_stream_controller_should_call_pull(ReadableByteStreamController const&);
 
-WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_stream(JS::Realm& realm, StartAlgorithm&& start_algorithm, PullAlgorithm&& pull_algorithm, CancelAlgorithm&& cancel_algorithm, Optional<double> high_water_mark = {}, Optional<SizeAlgorithm>&& size_algorithm = {});
-WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_byte_stream(JS::Realm& realm, StartAlgorithm&& start_algorithm, PullAlgorithm&& pull_algorithm, CancelAlgorithm&& cancel_algorithm);
-WebIDL::ExceptionOr<JS::NonnullGCPtr<WritableStream>> create_writable_stream(JS::Realm& realm, StartAlgorithm&& start_algorithm, WriteAlgorithm&& write_algorithm, CloseAlgorithm&& close_algorithm, AbortAlgorithm&& abort_algorithm, double high_water_mark, SizeAlgorithm&& size_algorithm);
+WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_stream(JS::Realm& realm, JS::NonnullGCPtr<StartAlgorithm> start_algorithm, JS::NonnullGCPtr<PullAlgorithm> pull_algorithm, JS::NonnullGCPtr<CancelAlgorithm> cancel_algorithm, Optional<double> high_water_mark = {}, JS::GCPtr<SizeAlgorithm> size_algorithm = {});
+WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> create_readable_byte_stream(JS::Realm& realm, JS::NonnullGCPtr<StartAlgorithm> start_algorithm, JS::NonnullGCPtr<PullAlgorithm> pull_algorithm, JS::NonnullGCPtr<CancelAlgorithm> cancel_algorithm);
+WebIDL::ExceptionOr<JS::NonnullGCPtr<WritableStream>> create_writable_stream(JS::Realm& realm, JS::NonnullGCPtr<StartAlgorithm> start_algorithm, JS::NonnullGCPtr<WriteAlgorithm> write_algorithm, JS::NonnullGCPtr<CloseAlgorithm> close_algorithm, JS::NonnullGCPtr<AbortAlgorithm> abort_algorithm, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> size_algorithm);
 void initialize_readable_stream(ReadableStream&);
 void initialize_writable_stream(WritableStream&);
 
@@ -143,8 +143,8 @@ Optional<double> writable_stream_default_writer_get_desired_size(WritableStreamD
 WebIDL::ExceptionOr<void> writable_stream_default_writer_release(WritableStreamDefaultWriter&);
 WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_default_writer_write(WritableStreamDefaultWriter&, JS::Value chunk);
 
-WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStream&, WritableStreamDefaultController&, StartAlgorithm&&, WriteAlgorithm&&, CloseAlgorithm&&, AbortAlgorithm&&, double high_water_mark, SizeAlgorithm&&);
-WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underlying_sink(WritableStream&, JS::Value underlying_sink_value, UnderlyingSink&, double high_water_mark, SizeAlgorithm&& size_algorithm);
+WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStream&, WritableStreamDefaultController&, JS::NonnullGCPtr<StartAlgorithm>, JS::NonnullGCPtr<WriteAlgorithm>, JS::NonnullGCPtr<CloseAlgorithm>, JS::NonnullGCPtr<AbortAlgorithm>, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm>);
+WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underlying_sink(WritableStream&, JS::Value underlying_sink_value, UnderlyingSink&, double high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> size_algorithm);
 WebIDL::ExceptionOr<void> writable_stream_default_controller_advance_queue_if_needed(WritableStreamDefaultController&);
 void writable_stream_default_controller_clear_algorithms(WritableStreamDefaultController&);
 WebIDL::ExceptionOr<void> writable_stream_default_controller_close(WritableStreamDefaultController&);
@@ -157,8 +157,8 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_process_close(Writa
 WebIDL::ExceptionOr<void> writable_stream_default_controller_process_write(WritableStreamDefaultController&, JS::Value chunk);
 WebIDL::ExceptionOr<void> writable_stream_default_controller_write(WritableStreamDefaultController&, JS::Value chunk, JS::Value chunk_size);
 
-WebIDL::ExceptionOr<void> initialize_transform_stream(TransformStream&, JS::NonnullGCPtr<JS::PromiseCapability> start_promise, double writable_high_water_mark, SizeAlgorithm&& writable_size_algorithm, double readable_high_water_mark, SizeAlgorithm&& readable_size_algorithm);
-void set_up_transform_stream_default_controller(TransformStream&, TransformStreamDefaultController&, TransformAlgorithm&&, FlushAlgorithm&&);
+WebIDL::ExceptionOr<void> initialize_transform_stream(TransformStream&, JS::NonnullGCPtr<JS::PromiseCapability> start_promise, double writable_high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> writable_size_algorithm, double readable_high_water_mark, JS::NonnullGCPtr<SizeAlgorithm> readable_size_algorithm);
+void set_up_transform_stream_default_controller(TransformStream&, TransformStreamDefaultController&, JS::NonnullGCPtr<TransformAlgorithm>, JS::NonnullGCPtr<FlushAlgorithm>);
 WebIDL::ExceptionOr<void> set_up_transform_stream_default_controller_from_transformer(TransformStream&, JS::Value transformer, Transformer&);
 void transform_stream_default_controller_clear_algorithms(TransformStreamDefaultController&);
 WebIDL::ExceptionOr<void> transform_stream_default_controller_enqueue(TransformStreamDefaultController&, JS::Value chunk);

+ 4 - 2
Userland/Libraries/LibWeb/Streams/ReadableByteStreamController.cpp

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
- * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
+ * Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -98,7 +98,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> ReadableByteStreamControl
     reset_queue(*this);
 
     // 3. Let result be the result of performing this.[[cancelAlgorithm]], passing in reason.
-    auto result = (*m_cancel_algorithm)(reason);
+    auto result = m_cancel_algorithm->function()(reason);
 
     // 4. Perform ! ReadableByteStreamControllerClearAlgorithms(this).
     readable_byte_stream_controller_clear_algorithms(*this);
@@ -201,6 +201,8 @@ void ReadableByteStreamController::visit_edges(Cell::Visitor& visitor)
     for (auto const& item : m_queue)
         visitor.visit(item.buffer);
     visitor.visit(m_stream);
+    visitor.visit(m_cancel_algorithm);
+    visitor.visit(m_pull_algorithm);
 }
 
 }

+ 6 - 6
Userland/Libraries/LibWeb/Streams/ReadableByteStreamController.h

@@ -94,8 +94,8 @@ public:
     Optional<u64> const& auto_allocate_chunk_size() { return m_auto_allocate_chunk_size; }
     void set_auto_allocate_chunk_size(Optional<u64> value) { m_auto_allocate_chunk_size = value; }
 
-    auto& cancel_algorithm() { return m_cancel_algorithm; }
-    void set_cancel_algorithm(Optional<CancelAlgorithm> value) { m_cancel_algorithm = move(value); }
+    JS::GCPtr<CancelAlgorithm> cancel_algorithm() { return m_cancel_algorithm; }
+    void set_cancel_algorithm(JS::GCPtr<CancelAlgorithm> value) { m_cancel_algorithm = value; }
 
     bool close_requested() const { return m_close_requested; }
     void set_close_requested(bool value) { m_close_requested = value; }
@@ -103,8 +103,8 @@ public:
     bool pull_again() const { return m_pull_again; }
     void set_pull_again(bool value) { m_pull_again = value; }
 
-    auto& pull_algorithm() { return m_pull_algorithm; }
-    void set_pull_algorithm(Optional<PullAlgorithm> value) { m_pull_algorithm = move(value); }
+    JS::GCPtr<PullAlgorithm> pull_algorithm() { return m_pull_algorithm; }
+    void set_pull_algorithm(JS::GCPtr<PullAlgorithm> value) { m_pull_algorithm = value; }
 
     bool pulling() const { return m_pulling; }
     void set_pulling(bool value) { m_pulling = value; }
@@ -148,7 +148,7 @@ private:
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-cancelalgorithm
     // A promise-returning algorithm, taking one argument (the cancel reason), which communicates a requested cancelation to the underlying source
-    Optional<CancelAlgorithm> m_cancel_algorithm;
+    JS::GCPtr<CancelAlgorithm> m_cancel_algorithm;
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-closerequested
     // A boolean flag indicating whether the stream has been closed by its underlying source, but still has chunks in its internal queue that have not yet been read
@@ -160,7 +160,7 @@ private:
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-pullalgorithm
     // A promise-returning algorithm that pulls data from the underlying source
-    Optional<PullAlgorithm> m_pull_algorithm;
+    JS::GCPtr<PullAlgorithm> m_pull_algorithm;
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-pulling
     // A boolean flag set to true while the underlying source's pull algorithm is executing and the returned promise has not yet fulfilled, used to prevent reentrant calls

+ 3 - 3
Userland/Libraries/LibWeb/Streams/ReadableStream.cpp

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
- * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
+ * Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -53,13 +53,13 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> ReadableStream::construct_
         VERIFY(!underlying_source_dict.type.has_value());
 
         // 2. Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy).
-        auto size_algorithm = extract_size_algorithm(strategy);
+        auto size_algorithm = extract_size_algorithm(vm, strategy);
 
         // 3. Let highWaterMark be ? ExtractHighWaterMark(strategy, 1).
         auto high_water_mark = TRY(extract_high_water_mark(strategy, 1));
 
         // 4. Perform ? SetUpReadableStreamDefaultControllerFromUnderlyingSource(this, underlyingSource, underlyingSourceDict, highWaterMark, sizeAlgorithm).
-        TRY(set_up_readable_stream_default_controller_from_underlying_source(*readable_stream, underlying_source, underlying_source_dict, high_water_mark, move(size_algorithm)));
+        TRY(set_up_readable_stream_default_controller_from_underlying_source(*readable_stream, underlying_source, underlying_source_dict, high_water_mark, size_algorithm));
     }
 
     return readable_stream;

+ 4 - 1
Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultController.cpp

@@ -71,7 +71,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> ReadableStreamDefaultCont
     reset_queue(*this);
 
     // 2. Let result be the result of performing this.[[cancelAlgorithm]], passing reason.
-    auto result = (*cancel_algorithm())(reason);
+    auto result = cancel_algorithm()->function()(reason);
 
     // 3. Perform ! ReadableStreamDefaultControllerClearAlgorithms(this).
     readable_stream_default_controller_clear_algorithms(*this);
@@ -138,6 +138,9 @@ void ReadableStreamDefaultController::visit_edges(Cell::Visitor& visitor)
     for (auto const& item : m_queue)
         visitor.visit(item.value);
     visitor.visit(m_stream);
+    visitor.visit(m_cancel_algorithm);
+    visitor.visit(m_pull_algorithm);
+    visitor.visit(m_strategy_size_algorithm);
 }
 
 }

+ 9 - 9
Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultController.h

@@ -32,8 +32,8 @@ public:
     WebIDL::ExceptionOr<void> enqueue(JS::Value chunk);
     void error(JS::Value error);
 
-    auto& cancel_algorithm() { return m_cancel_algorithm; }
-    void set_cancel_algorithm(Optional<CancelAlgorithm> value) { m_cancel_algorithm = move(value); }
+    JS::GCPtr<CancelAlgorithm> cancel_algorithm() { return m_cancel_algorithm; }
+    void set_cancel_algorithm(JS::GCPtr<CancelAlgorithm> value) { m_cancel_algorithm = value; }
 
     bool close_requested() const { return m_close_requested; }
     void set_close_requested(bool value) { m_close_requested = value; }
@@ -41,8 +41,8 @@ public:
     bool pull_again() const { return m_pull_again; }
     void set_pull_again(bool value) { m_pull_again = value; }
 
-    auto& pull_algorithm() { return m_pull_algorithm; }
-    void set_pull_algorithm(Optional<PullAlgorithm> value) { m_pull_algorithm = move(value); }
+    JS::GCPtr<PullAlgorithm> pull_algorithm() { return m_pull_algorithm; }
+    void set_pull_algorithm(JS::GCPtr<PullAlgorithm> value) { m_pull_algorithm = value; }
 
     bool pulling() const { return m_pulling; }
     void set_pulling(bool value) { m_pulling = value; }
@@ -58,8 +58,8 @@ public:
     double strategy_hwm() const { return m_strategy_hwm; }
     void set_strategy_hwm(double value) { m_strategy_hwm = value; }
 
-    auto& strategy_size_algorithm() { return m_strategy_size_algorithm; }
-    void set_strategy_size_algorithm(Optional<SizeAlgorithm> value) { m_strategy_size_algorithm = move(value); }
+    JS::GCPtr<SizeAlgorithm> strategy_size_algorithm() { return m_strategy_size_algorithm; }
+    void set_strategy_size_algorithm(JS::GCPtr<SizeAlgorithm> value) { m_strategy_size_algorithm = value; }
 
     JS::GCPtr<ReadableStream> stream() { return m_stream; }
     void set_stream(JS::GCPtr<ReadableStream> value) { m_stream = value; }
@@ -75,7 +75,7 @@ private:
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-cancelalgorithm
     // A promise-returning algorithm, taking one argument (the cancel reason), which communicates a requested cancelation to the underlying source
-    Optional<CancelAlgorithm> m_cancel_algorithm;
+    JS::GCPtr<CancelAlgorithm> m_cancel_algorithm;
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-closerequested
     // A boolean flag indicating whether the stream has been closed by its underlying source, but still has chunks in its internal queue that have not yet been read
@@ -87,7 +87,7 @@ private:
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-pullalgorithm
     // A promise-returning algorithm that pulls data from the underlying source
-    Optional<PullAlgorithm> m_pull_algorithm;
+    JS::GCPtr<PullAlgorithm> m_pull_algorithm;
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-pulling
     // A boolean flag set to true while the underlying source's pull algorithm is executing and the returned promise has not yet fulfilled, used to prevent reentrant calls
@@ -111,7 +111,7 @@ private:
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-strategysizealgorithm
     // An algorithm to calculate the size of enqueued chunks, as part of the stream’s queuing strategy
-    Optional<SizeAlgorithm> m_strategy_size_algorithm;
+    JS::GCPtr<SizeAlgorithm> m_strategy_size_algorithm;
 
     // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-stream
     // The ReadableStream instance controlled

+ 2 - 2
Userland/Libraries/LibWeb/Streams/TransformStream.cpp

@@ -42,13 +42,13 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<TransformStream>> TransformStream::construc
     auto readable_high_water_mark = TRY(extract_high_water_mark(readable_strategy, 0));
 
     // 6. Let readableSizeAlgorithm be ! ExtractSizeAlgorithm(readableStrategy).
-    auto readable_size_algorithm = extract_size_algorithm(readable_strategy);
+    auto readable_size_algorithm = extract_size_algorithm(vm, readable_strategy);
 
     // 7. Let writableHighWaterMark be ? ExtractHighWaterMark(writableStrategy, 1).
     auto writable_high_water_mark = TRY(extract_high_water_mark(writable_strategy, 1));
 
     // 8. Let writableSizeAlgorithm be ! ExtractSizeAlgorithm(writableStrategy).
-    auto writable_size_algorithm = extract_size_algorithm(writable_strategy);
+    auto writable_size_algorithm = extract_size_algorithm(vm, writable_strategy);
 
     // 9. Let startPromise be a new promise.
     auto start_promise = WebIDL::create_promise(realm);

+ 2 - 0
Userland/Libraries/LibWeb/Streams/TransformStreamDefaultController.cpp

@@ -29,6 +29,8 @@ void TransformStreamDefaultController::visit_edges(Cell::Visitor& visitor)
 {
     Base::visit_edges(visitor);
     visitor.visit(m_stream);
+    visitor.visit(m_flush_algorithm);
+    visitor.visit(m_transform_algorithm);
 }
 
 // https://streams.spec.whatwg.org/#ts-default-controller-desired-size

+ 6 - 6
Userland/Libraries/LibWeb/Streams/TransformStreamDefaultController.h

@@ -24,11 +24,11 @@ public:
     WebIDL::ExceptionOr<void> error(Optional<JS::Value> reason = {});
     WebIDL::ExceptionOr<void> terminate();
 
-    auto& flush_algorithm() { return m_flush_algorithm; }
-    void set_flush_algorithm(Optional<FlushAlgorithm>&& value) { m_flush_algorithm = move(value); }
+    JS::GCPtr<FlushAlgorithm> flush_algorithm() { return m_flush_algorithm; }
+    void set_flush_algorithm(JS::GCPtr<FlushAlgorithm>&& value) { m_flush_algorithm = move(value); }
 
-    auto& transform_algorithm() { return m_transform_algorithm; }
-    void set_transform_algorithm(Optional<TransformAlgorithm>&& value) { m_transform_algorithm = move(value); }
+    JS::GCPtr<TransformAlgorithm> transform_algorithm() { return m_transform_algorithm; }
+    void set_transform_algorithm(JS::GCPtr<TransformAlgorithm>&& value) { m_transform_algorithm = move(value); }
 
     JS::GCPtr<TransformStream> stream() { return m_stream; }
     void set_stream(JS::GCPtr<TransformStream> stream) { m_stream = stream; }
@@ -39,10 +39,10 @@ private:
     virtual void visit_edges(Cell::Visitor&) override;
 
     // https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-flushalgorithm
-    Optional<FlushAlgorithm> m_flush_algorithm;
+    JS::GCPtr<FlushAlgorithm> m_flush_algorithm;
 
     // https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-transformalgorithm
-    Optional<TransformAlgorithm> m_transform_algorithm;
+    JS::GCPtr<TransformAlgorithm> m_transform_algorithm;
 
     // https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-stream
     JS::GCPtr<TransformStream> m_stream;

+ 1 - 1
Userland/Libraries/LibWeb/Streams/WritableStream.cpp

@@ -39,7 +39,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WritableStream>> WritableStream::construct_
     // Note: This AO configures slot values which are already specified in the class's field initializers.
 
     // 5. Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy).
-    auto size_algorithm = extract_size_algorithm(strategy);
+    auto size_algorithm = extract_size_algorithm(vm, strategy);
 
     // 6. Let highWaterMark be ? ExtractHighWaterMark(strategy, 1).
     auto high_water_mark = TRY(extract_high_water_mark(strategy, 1));

+ 5 - 1
Userland/Libraries/LibWeb/Streams/WritableStreamDefaultController.cpp

@@ -19,6 +19,10 @@ void WritableStreamDefaultController::visit_edges(Visitor& visitor)
     for (auto& value : m_queue)
         visitor.visit(value.value);
     visitor.visit(m_stream);
+    visitor.visit(m_abort_algorithm);
+    visitor.visit(m_close_algorithm);
+    visitor.visit(m_strategy_size_algorithm);
+    visitor.visit(m_write_algorithm);
 }
 
 // https://streams.spec.whatwg.org/#ws-default-controller-error
@@ -39,7 +43,7 @@ WebIDL::ExceptionOr<void> WritableStreamDefaultController::error(JS::Value error
 WebIDL::ExceptionOr<JS::GCPtr<WebIDL::Promise>> WritableStreamDefaultController::abort_steps(JS::Value reason)
 {
     // 1. Let result be the result of performing this.[[abortAlgorithm]], passing reason.
-    auto result = TRY((*m_abort_algorithm)(reason));
+    auto result = TRY(m_abort_algorithm->function()(reason));
 
     // 2. Perform ! WritableStreamDefaultControllerClearAlgorithms(this).
     writable_stream_default_controller_clear_algorithms(*this);

+ 12 - 12
Userland/Libraries/LibWeb/Streams/WritableStreamDefaultController.h

@@ -24,11 +24,11 @@ public:
     JS::NonnullGCPtr<DOM::AbortSignal> signal() { return *m_signal; }
     void set_signal(JS::NonnullGCPtr<DOM::AbortSignal> value) { m_signal = value; }
 
-    auto& abort_algorithm() { return m_abort_algorithm; }
-    void set_abort_algorithm(Optional<AbortAlgorithm>&& value) { m_abort_algorithm = move(value); }
+    JS::GCPtr<AbortAlgorithm> abort_algorithm() { return m_abort_algorithm; }
+    void set_abort_algorithm(JS::GCPtr<AbortAlgorithm> value) { m_abort_algorithm = value; }
 
-    auto& close_algorithm() { return m_close_algorithm; }
-    void set_close_algorithm(Optional<CloseAlgorithm>&& value) { m_close_algorithm = move(value); }
+    JS::GCPtr<CloseAlgorithm> close_algorithm() { return m_close_algorithm; }
+    void set_close_algorithm(JS::GCPtr<CloseAlgorithm> value) { m_close_algorithm = value; }
 
     SinglyLinkedList<ValueWithSize>& queue() { return m_queue; }
 
@@ -41,14 +41,14 @@ public:
     size_t strategy_hwm() const { return m_strategy_hwm; }
     void set_strategy_hwm(size_t value) { m_strategy_hwm = value; }
 
-    auto& strategy_size_algorithm() { return m_strategy_size_algorithm; }
-    void set_strategy_size_algorithm(Optional<SizeAlgorithm>&& value) { m_strategy_size_algorithm = move(value); }
+    JS::GCPtr<SizeAlgorithm> strategy_size_algorithm() { return m_strategy_size_algorithm; }
+    void set_strategy_size_algorithm(JS::GCPtr<SizeAlgorithm> value) { m_strategy_size_algorithm = value; }
 
     JS::NonnullGCPtr<WritableStream> stream() { return *m_stream; }
     void set_stream(JS::NonnullGCPtr<WritableStream> value) { m_stream = value; }
 
-    auto& write_algorithm() { return m_write_algorithm; }
-    void set_write_algorithm(Optional<WriteAlgorithm>&& value) { m_write_algorithm = move(value); }
+    JS::GCPtr<WriteAlgorithm> write_algorithm() { return m_write_algorithm; }
+    void set_write_algorithm(JS::GCPtr<WriteAlgorithm> value) { m_write_algorithm = value; }
 
     WebIDL::ExceptionOr<JS::GCPtr<WebIDL::Promise>> abort_steps(JS::Value reason);
     void error_steps();
@@ -60,11 +60,11 @@ private:
 
     // https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-abortalgorithm
     // A promise-returning algorithm, taking one argument (the abort reason), which communicates a requested abort to the underlying sink
-    Optional<AbortAlgorithm> m_abort_algorithm;
+    JS::GCPtr<AbortAlgorithm> m_abort_algorithm;
 
     // https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-closealgorithm
     // A promise-returning algorithm which communicates a requested close to the underlying sink
-    Optional<CloseAlgorithm> m_close_algorithm;
+    JS::GCPtr<CloseAlgorithm> m_close_algorithm;
 
     // https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-queue
     // A list representing the stream’s internal queue of chunks
@@ -88,7 +88,7 @@ private:
 
     // https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-strategysizealgorithm
     // An algorithm to calculate the size of enqueued chunks, as part of the stream’s queuing strategy
-    Optional<SizeAlgorithm> m_strategy_size_algorithm;
+    JS::GCPtr<SizeAlgorithm> m_strategy_size_algorithm;
 
     // https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-stream
     // The WritableStream instance controlled
@@ -96,7 +96,7 @@ private:
 
     // https://streams.spec.whatwg.org/#writablestreamdefaultcontroller-writealgorithm
     // A promise-returning algorithm, taking one argument (the chunk to write), which writes data to the underlying sink
-    Optional<WriteAlgorithm> m_write_algorithm;
+    JS::GCPtr<WriteAlgorithm> m_write_algorithm;
 };
 
 }