diff --git a/examples/spec/streams-transform.js b/examples/spec/streams-transform.js index c00d89b..1125c42 100644 --- a/examples/spec/streams-transform.js +++ b/examples/spec/streams-transform.js @@ -2,6 +2,8 @@ import { test, testDeep, testThrows, summary } from './helpers.js'; console.log('TransformStream / TransformStreamDefaultController Tests\n'); +const READABLE_NO_BACKPRESSURE = { highWaterMark: 1 }; + test('TS typeof', typeof TransformStream, 'function'); test('TS toStringTag', Object.prototype.toString.call(new TransformStream()), '[object TransformStream]'); @@ -101,7 +103,7 @@ async function testTransformError() { const err = new Error('transform boom'); const ts = new TransformStream({ transform() { throw err; } - }); + }, undefined, READABLE_NO_BACKPRESSURE); const writer = ts.writable.getWriter(); const reader = ts.readable.getReader(); try { @@ -123,7 +125,7 @@ async function testFlushError() { const ts = new TransformStream({ transform() {}, flush() { throw err; } - }); + }, undefined, READABLE_NO_BACKPRESSURE); const writer = ts.writable.getWriter(); await writer.write('a'); try { @@ -272,7 +274,7 @@ async function testSubclass() { async function testReadableHWM() { let ctrl; - const ts = new TransformStream({ + new TransformStream({ start(c) { ctrl = c; } }, undefined, { highWaterMark: 9 }); test('readable custom HWM desiredSize', ctrl.desiredSize, 9); diff --git a/src/streams/pipes.c b/src/streams/pipes.c index a6c61c6..80276cb 100644 --- a/src/streams/pipes.c +++ b/src/streams/pipes.c @@ -429,11 +429,12 @@ static ant_value_t js_rs_pipe_to(ant_t *js, ant_value_t *args, int nargs) { } static ant_value_t js_rs_pipe_through(ant_t *js, ant_value_t *args, int nargs) { - if (!is_object_type(js->this_val)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid ReadableStream"); - rs_stream_t *stream = rs_get_stream(js->this_val); + ant_value_t source = js->this_val; + if (!is_object_type(source)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid ReadableStream"); + rs_stream_t *stream = rs_get_stream(source); if (!stream) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid ReadableStream"); - if (is_object_type(rs_stream_reader(js->this_val))) + if (is_object_type(rs_stream_reader(source))) return js_mkerr_typed(js, JS_ERR_TYPE, "ReadableStream is already locked"); if (nargs < 1 || !is_object_type(args[0])) return js_mkerr_typed(js, JS_ERR_TYPE, "pipeThrough requires a transform object"); @@ -441,6 +442,7 @@ static ant_value_t js_rs_pipe_through(ant_t *js, ant_value_t *args, int nargs) { ant_value_t transform = args[0]; ant_value_t writable = js_get(js, transform, "writable"); ant_value_t readable = js_get(js, transform, "readable"); + if (!is_object_type(writable) || !ws_get_stream(writable)) return js_mkerr_typed(js, JS_ERR_TYPE, "pipeThrough transform.writable must be a WritableStream"); if (!is_object_type(readable) || !rs_get_stream(readable)) @@ -450,11 +452,17 @@ static ant_value_t js_rs_pipe_through(ant_t *js, ant_value_t *args, int nargs) { bool prevent_close, prevent_abort, prevent_cancel; ant_value_t signal; - pipes_parse_options(js, nargs > 1 ? args[1] : js_mkundef(), - &prevent_close, &prevent_abort, &prevent_cancel, &signal); - - ant_value_t pipe_promise = readable_stream_pipe_to(js, js->this_val, writable, - prevent_close, prevent_abort, prevent_cancel, signal); + + pipes_parse_options( + js, nargs > 1 ? args[1] : js_mkundef(), + &prevent_close, &prevent_abort, &prevent_cancel, &signal + ); + + ant_value_t pipe_promise = readable_stream_pipe_to( + js, source, writable, + prevent_close, prevent_abort, prevent_cancel, signal + ); + promise_mark_handled(pipe_promise); return readable; } diff --git a/src/streams/readable.c b/src/streams/readable.c index 099f674..52cf358 100644 --- a/src/streams/readable.c +++ b/src/streams/readable.c @@ -510,6 +510,31 @@ void rs_controller_enqueue(ant_t *js, ant_value_t ctrl_obj, ant_value_t chunk) { return; } + double chunk_size = 1; + ant_value_t size_fn = rs_ctrl_size(ctrl_obj); + if (is_callable(size_fn)) { + ant_value_t size_args[1] = { chunk }; + ant_value_t size_result = sv_vm_call(js->vm, js, size_fn, js_mkundef(), size_args, 1, NULL, false); + if (is_err(size_result)) { + ant_value_t thrown = js->thrown_value; + ant_value_t err = is_object_type(thrown) ? thrown : size_result; + js->thrown_exists = false; + js->thrown_value = js_mkundef(); + js->thrown_stack = js_mkundef(); + readable_stream_error(js, stream_obj, err); + return; + } + if (vtype(size_result) == T_NUM) chunk_size = js_getnum(size_result); + else chunk_size = js_to_number(js, size_result); + } + + if (chunk_size < 0 || chunk_size != chunk_size || chunk_size == (double)INFINITY) { + ant_value_t err = js_make_error_silent(js, JS_ERR_RANGE, + "The return value of a queuing strategy's size function must be a finite, non-NaN, non-negative number"); + readable_stream_error(js, stream_obj, err); + return; + } + rs_ctrl_queue_push(js, ctrl_obj, chunk); if (ctrl->queue_sizes_len >= ctrl->queue_sizes_cap) { uint32_t new_cap = ctrl->queue_sizes_cap ? ctrl->queue_sizes_cap * 2 : 4; @@ -518,8 +543,8 @@ void rs_controller_enqueue(ant_t *js, ant_value_t ctrl_obj, ant_value_t chunk) { } if (ctrl->queue_sizes_len < ctrl->queue_sizes_cap) - ctrl->queue_sizes[ctrl->queue_sizes_len++] = 1; - ctrl->queue_total_size += 1; + ctrl->queue_sizes[ctrl->queue_sizes_len++] = chunk_size; + ctrl->queue_total_size += chunk_size; rs_default_controller_call_pull_if_needed(js, ctrl_obj); } @@ -604,8 +629,11 @@ ant_value_t js_rs_reader_ctor(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr_typed(js, JS_ERR_TYPE, "ReadableStream is already locked to a reader"); ant_value_t closed = js_mkpromise(js); + promise_mark_handled(closed); + ant_value_t obj = js_mkobj(js); ant_value_t proto = js_instance_proto_from_new_target(js, g_reader_proto); + if (is_object_type(proto)) js_set_proto_init(obj, proto); js_set_slot(obj, SLOT_ENTRIES, stream_obj); js_set_slot(obj, SLOT_RS_CLOSED, closed); diff --git a/src/streams/transform.c b/src/streams/transform.c index 625aefa..db4ad0b 100644 --- a/src/streams/transform.c +++ b/src/streams/transform.c @@ -8,6 +8,7 @@ #include "descriptors.h" #include "silver/engine.h" +#include "modules/assert.h" #include "modules/symbol.h" #include "streams/transform.h" #include "streams/readable.h" @@ -100,6 +101,10 @@ static inline ant_value_t ts_ctrl_cancel_fn(ant_value_t ctrl_obj) { return js_get_slot(ctrl_obj, SLOT_BUFFER); } +static inline ant_value_t ts_ctrl_transformer(ant_value_t ctrl_obj) { + return js_get_slot(ctrl_obj, SLOT_SETTLED); +} + static inline ant_value_t ts_ctrl_stream(ant_value_t ctrl_obj) { return js_get_slot(ctrl_obj, SLOT_DATA); } @@ -114,6 +119,15 @@ static void ts_ctrl_clear_algorithms(ant_value_t ctrl_obj) { js_set_slot(ctrl_obj, SLOT_BUFFER, js_mkundef()); } +static ant_value_t ts_take_thrown_or(ant_t *js, ant_value_t fallback) { + ant_value_t thrown = js->thrown_exists ? js->thrown_value : js_mkundef(); + ant_value_t err = is_object_type(thrown) ? thrown : fallback; + js->thrown_exists = false; + js->thrown_value = js_mkundef(); + js->thrown_stack = js_mkundef(); + return err; +} + static bool ts_is_thenable(ant_t *js, ant_value_t val) { if (vtype(val) == T_PROMISE) return true; if (!is_object_type(val)) return false; @@ -123,20 +137,20 @@ static bool ts_is_thenable(ant_t *js, ant_value_t val) { static void ts_chain_thenable(ant_t *js, ant_value_t val, ant_value_t res_fn, ant_value_t rej_fn) { if (vtype(val) == T_PROMISE) { - ant_value_t then_fn = js_get(js, val, "then"); - if (is_callable(then_fn)) { - ant_value_t then_args[2] = { res_fn, rej_fn }; - sv_vm_call(js->vm, js, then_fn, val, then_args, 2, NULL, false); - } - } else { - ant_value_t resolved = js_mkpromise(js); - js_resolve_promise(js, resolved, val); - ant_value_t then_fn = js_get(js, resolved, "then"); - if (is_callable(then_fn)) { - ant_value_t then_args[2] = { res_fn, rej_fn }; - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); - } - } + ant_value_t then_fn = js_get(js, val, "then"); + if (is_callable(then_fn)) { + ant_value_t then_args[2] = { res_fn, rej_fn }; + ant_value_t then_result = sv_vm_call(js->vm, js, then_fn, val, then_args, 2, NULL, false); + promise_mark_handled(then_result); + }} else { + ant_value_t resolved = js_mkpromise(js); + js_resolve_promise(js, resolved, val); + ant_value_t then_fn = js_get(js, resolved, "then"); + if (is_callable(then_fn)) { + ant_value_t then_args[2] = { res_fn, rej_fn }; + ant_value_t then_result = sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); + promise_mark_handled(then_result); + }} } static void ts_error(ant_t *js, ant_value_t ts_obj, ant_value_t e) { @@ -213,17 +227,7 @@ static void ts_ctrl_terminate(ant_t *js, ant_value_t ctrl_obj) { ant_value_t writable = ts_writable(ts_obj); ws_stream_t *ws = ws_get_stream(writable); if (ws && ws->state == WS_STATE_WRITABLE) { - bool had_throw = js->thrown_exists; - ant_value_t saved_value = had_throw ? js->thrown_value : js_mkundef(); - ant_value_t saved_stack = had_throw ? js->thrown_stack : js_mkundef(); - - js_mkerr_typed(js, JS_ERR_TYPE, "TransformStream readable side terminated"); - ant_value_t err = js->thrown_value; - - js->thrown_exists = had_throw; - js->thrown_value = saved_value; - js->thrown_stack = saved_stack; - + ant_value_t err = js_make_error_silent(js, JS_ERR_TYPE, "TransformStream readable side terminated"); ts_error_writable_and_unblock_write(js, ts_obj, err); } } @@ -248,14 +252,14 @@ static ant_value_t ts_ctrl_perform_transform(ant_t *js, ant_value_t ctrl_obj, an ant_value_t transform_fn = ts_ctrl_transform_fn(ctrl_obj); ant_value_t ts_obj = ts_ctrl_stream(ctrl_obj); ant_value_t p = js_mkpromise(js); + promise_mark_handled(p); if (is_callable(transform_fn)) { ant_value_t call_args[2] = { chunk, ctrl_obj }; - ant_value_t result = sv_vm_call(js->vm, js, transform_fn, js_mkundef(), call_args, 2, NULL, false); + ant_value_t result = sv_vm_call(js->vm, js, transform_fn, ts_ctrl_transformer(ctrl_obj), call_args, 2, NULL, false); if (is_err(result)) { - ant_value_t thrown = js->thrown_value; - ant_value_t err = is_object_type(thrown) ? thrown : result; + ant_value_t err = ts_take_thrown_or(js, result); ts_error(js, ts_obj, err); js_reject_promise(js, p, err); return p; @@ -286,21 +290,10 @@ static ant_value_t ts_sink_write_bp_resolve(ant_t *js, ant_value_t *args, int na ant_value_t ctrl_obj = js_get_slot(wrapper, SLOT_DATA); ant_value_t chunk = js_get_slot(wrapper, SLOT_ENTRIES); ant_value_t ts_obj = js_get_slot(wrapper, SLOT_CTOR); - ws_stream_t *ws = ws_get_stream(ts_writable(ts_obj)); if (ws && ws->state == WS_STATE_ERRORING) { - bool had_throw = js->thrown_exists; - ant_value_t saved_value = had_throw ? js->thrown_value : js_mkundef(); - ant_value_t saved_stack = had_throw ? js->thrown_stack : js_mkundef(); - - js_mkerr_typed(js, JS_ERR_TYPE, "WritableStream is in erroring state"); - ant_value_t err = js->thrown_value; - - js->thrown_exists = had_throw; - js->thrown_value = saved_value; - js->thrown_stack = saved_stack; - + ant_value_t err = js_make_error_silent(js, JS_ERR_TYPE, "WritableStream is in erroring state"); ant_value_t fp = ts_ctrl_finish_promise(ctrl_obj); if (vtype(fp) == T_PROMISE) js_reject_promise(js, fp, err); return js_mkundef(); @@ -325,6 +318,7 @@ static ant_value_t ts_sink_write(ant_t *js, ant_value_t *args, int nargs) { ant_value_t ctrl_obj = ts_controller(ts_obj); ant_value_t finish_p = js_mkpromise(js); + promise_mark_handled(finish_p); js_set_slot(ctrl_obj, SLOT_RS_PULL, finish_p); if (ts_get_backpressure(ts_obj)) { @@ -360,11 +354,8 @@ static ant_value_t ts_sink_abort(ant_t *js, ant_value_t *args, int nargs) { if (is_callable(cancel_fn)) { ant_value_t cancel_args[1] = { reason }; - ant_value_t result = sv_vm_call(js->vm, js, cancel_fn, js_mkundef(), cancel_args, 1, NULL, false); - if (is_err(result)) { - ant_value_t thrown = js->thrown_value; - reason = is_object_type(thrown) ? thrown : reason; - } + ant_value_t result = sv_vm_call(js->vm, js, cancel_fn, ts_ctrl_transformer(ctrl_obj), cancel_args, 1, NULL, false); + if (is_err(result)) reason = ts_take_thrown_or(js, reason); } ts_error(js, ts_obj, reason); @@ -380,20 +371,9 @@ static ant_value_t ts_sink_close_resolve(ant_t *js, ant_value_t *args, int nargs ant_value_t readable = ts_readable(ts_obj); rs_stream_t *rs = rs_get_stream(readable); - if (rs && rs->state == RS_STATE_READABLE) - js_resolve_promise(js, p, js_mkundef()); + if (rs && rs->state == RS_STATE_READABLE) js_resolve_promise(js, p, js_mkundef()); else { - bool had_throw = js->thrown_exists; - ant_value_t saved_value = had_throw ? js->thrown_value : js_mkundef(); - ant_value_t saved_stack = had_throw ? js->thrown_stack : js_mkundef(); - - js_mkerr_typed(js, JS_ERR_TYPE, "TransformStream readable side is not in a readable state"); - ant_value_t err = js->thrown_value; - - js->thrown_exists = had_throw; - js->thrown_value = saved_value; - js->thrown_stack = saved_stack; - + ant_value_t err = js_make_error_silent(js, JS_ERR_TYPE, "TransformStream readable side is not in a readable state"); js_reject_promise(js, p, err); } @@ -419,14 +399,14 @@ static ant_value_t ts_sink_close(ant_t *js, ant_value_t *args, int nargs) { ts_ctrl_clear_algorithms(ctrl_obj); ant_value_t p = js_mkpromise(js); + promise_mark_handled(p); if (is_callable(flush_fn)) { ant_value_t flush_args[1] = { ctrl_obj }; - ant_value_t result = sv_vm_call(js->vm, js, flush_fn, js_mkundef(), flush_args, 1, NULL, false); + ant_value_t result = sv_vm_call(js->vm, js, flush_fn, ts_ctrl_transformer(ctrl_obj), flush_args, 1, NULL, false); if (is_err(result)) { - ant_value_t thrown = js->thrown_value; - ant_value_t err = is_object_type(thrown) ? thrown : result; + ant_value_t err = ts_take_thrown_or(js, result); ts_error(js, ts_obj, err); js_reject_promise(js, p, err); return p; @@ -487,21 +467,19 @@ static ant_value_t ts_source_cancel(ant_t *js, ant_value_t *args, int nargs) { if (is_callable(cancel_fn)) { ant_value_t cancel_args[1] = { reason }; - ant_value_t result = sv_vm_call(js->vm, js, cancel_fn, js_mkundef(), cancel_args, 1, NULL, false); - - if (is_err(result)) { - ant_value_t thrown = js->thrown_value; - reason = is_object_type(thrown) ? thrown : reason; - } else if (ts_is_thenable(js, result)) { + ant_value_t result = sv_vm_call(js->vm, js, cancel_fn, ts_ctrl_transformer(ctrl_obj), cancel_args, 1, NULL, false); + + if (is_err(result)) reason = ts_take_thrown_or(js, reason); + else if (ts_is_thenable(js, result)) { ant_value_t p = js_mkpromise(js); ant_value_t wrapper = js_mkobj(js); js_set_slot(wrapper, SLOT_DATA, p); js_set_slot(wrapper, SLOT_ENTRIES, ts_obj); - + ant_value_t res_fn = js_heavy_mkfun(js, ts_source_pull_resolve, p); ant_value_t rej_fn = js_heavy_mkfun(js, ts_sink_close_reject, wrapper); ts_chain_thenable(js, result, res_fn, rej_fn); - + ts_error_writable_and_unblock_write(js, ts_obj, reason); return p; } @@ -707,6 +685,7 @@ static ant_value_t js_ts_ctor(ant_t *js, ant_value_t *args, int nargs) { js_set_slot(ctrl_obj, SLOT_ENTRIES, transform_fn); js_set_slot(ctrl_obj, SLOT_CTOR, flush_fn); js_set_slot(ctrl_obj, SLOT_BUFFER, cancel_fn); + js_set_slot(ctrl_obj, SLOT_SETTLED, transformer); js_set_slot(ctrl_obj, SLOT_RS_PULL, js_mkundef()); js_set_slot(ts_obj, SLOT_DEFAULT, ctrl_obj); diff --git a/src/streams/writable.c b/src/streams/writable.c index 4d65fd4..73fb069 100644 --- a/src/streams/writable.c +++ b/src/streams/writable.c @@ -191,14 +191,16 @@ if (vtype(val) == T_PROMISE) { ant_value_t then_fn = js_get(js, val, "then"); if (is_callable(then_fn)) { ant_value_t then_args[2] = { res_fn, rej_fn }; - sv_vm_call(js->vm, js, then_fn, val, then_args, 2, NULL, false); + ant_value_t then_result = sv_vm_call(js->vm, js, then_fn, val, then_args, 2, NULL, false); + promise_mark_handled(then_result); }} else { ant_value_t resolved = js_mkpromise(js); js_resolve_promise(js, resolved, val); ant_value_t then_fn = js_get(js, resolved, "then"); if (is_callable(then_fn)) { ant_value_t then_args[2] = { res_fn, rej_fn }; - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); + ant_value_t then_result = sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); + promise_mark_handled(then_result); } }} @@ -229,20 +231,30 @@ static bool writable_stream_has_operation_in_flight(ant_value_t stream_obj) { return !is_undefined(iw) || !is_undefined(ic); } -static void ws_writer_ensure_ready_promise_rejected(ant_t *js, ant_value_t writer_obj, ant_value_t error) { +static void ws_writer_replace_ready_promise_rejected(ant_t *js, ant_value_t writer_obj, ant_value_t error) { ant_value_t ready = js_mkpromise(js); js_reject_promise(js, ready, error); promise_mark_handled(ready); js_set_slot(writer_obj, SLOT_WS_READY, ready); } -static void ws_writer_ensure_closed_promise_rejected(ant_t *js, ant_value_t writer_obj, ant_value_t error) { +static void ws_writer_replace_closed_promise_rejected(ant_t *js, ant_value_t writer_obj, ant_value_t error) { ant_value_t closed = js_mkpromise(js); js_reject_promise(js, closed, error); promise_mark_handled(closed); js_set_slot(writer_obj, SLOT_RS_CLOSED, closed); } +static void ws_writer_reject_ready_promise(ant_t *js, ant_value_t writer_obj, ant_value_t error) { + ant_value_t ready = ws_writer_ready(writer_obj); + if (!is_undefined(ready)) js_reject_promise(js, ready, error); +} + +static void ws_writer_reject_closed_promise(ant_t *js, ant_value_t writer_obj, ant_value_t error) { + ant_value_t closed = ws_writer_closed(writer_obj); + if (!is_undefined(closed)) js_reject_promise(js, closed, error); +} + static void writable_stream_start_erroring(ant_t *js, ant_value_t stream_obj, ant_value_t reason) { ws_stream_t *stream = ws_get_stream(stream_obj); if (!stream || stream->state != WS_STATE_WRITABLE) return; @@ -262,7 +274,7 @@ static void writable_stream_start_erroring(ant_t *js, ant_value_t stream_obj, an ant_value_t writer_obj = ws_stream_writer(stream_obj); if (is_object_type(writer_obj)) - ws_writer_ensure_ready_promise_rejected(js, writer_obj, reason); + ws_writer_reject_ready_promise(js, writer_obj, reason); if (!writable_stream_has_operation_in_flight(stream_obj) && ctrl && ctrl->started) writable_stream_finish_erroring(js, stream_obj); @@ -277,7 +289,7 @@ static void ws_reject_close_and_closed(ant_t *js, ant_value_t stream_obj) { } ant_value_t writer_obj = ws_stream_writer(stream_obj); if (is_object_type(writer_obj)) - ws_writer_ensure_closed_promise_rejected(js, writer_obj, stored_error); + ws_writer_reject_closed_promise(js, writer_obj, stored_error); } static ant_value_t ws_finish_erroring_abort_resolve(ant_t *js, ant_value_t *args, int nargs) { @@ -384,7 +396,9 @@ static void writable_stream_update_backpressure(ant_t *js, ant_value_t stream_ob if (is_object_type(writer_obj) && stream->backpressure != backpressure) { if (backpressure) { - js_set_slot(writer_obj, SLOT_WS_READY, js_mkpromise(js)); + ant_value_t ready = js_mkpromise(js); + promise_mark_handled(ready); + js_set_slot(writer_obj, SLOT_WS_READY, ready); } else { ant_value_t ready = ws_writer_ready(writer_obj); if (!is_undefined(ready)) js_resolve_promise(js, ready, js_mkundef()); @@ -689,13 +703,14 @@ static ant_value_t ws_abort_resolve(ant_t *js, ant_value_t *args, int nargs) { ant_value_t stored_error = ws_stream_stored_error(stream_obj); ant_value_t cr = ws_stream_close_request(stream_obj); + if (!is_undefined(cr)) { js_reject_promise(js, cr, stored_error); js_set_slot(stream_obj, SLOT_WS_CLOSE, js_mkundef()); } + ant_value_t writer_obj = ws_stream_writer(stream_obj); - if (is_object_type(writer_obj)) - ws_writer_ensure_closed_promise_rejected(js, writer_obj, stored_error); + if (is_object_type(writer_obj)) ws_writer_reject_closed_promise(js, writer_obj, stored_error); return js_mkundef(); } @@ -713,13 +728,15 @@ static ant_value_t ws_abort_reject(ant_t *js, ant_value_t *args, int nargs) { ant_value_t stored_error = ws_stream_stored_error(stream_obj); ant_value_t cr = ws_stream_close_request(stream_obj); + if (!is_undefined(cr)) { js_reject_promise(js, cr, stored_error); js_set_slot(stream_obj, SLOT_WS_CLOSE, js_mkundef()); } + ant_value_t writer_obj = ws_stream_writer(stream_obj); if (is_object_type(writer_obj)) - ws_writer_ensure_closed_promise_rejected(js, writer_obj, stored_error); + ws_writer_reject_closed_promise(js, writer_obj, stored_error); return js_mkundef(); } @@ -920,14 +937,20 @@ static ant_value_t js_ws_writer_close(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t js_ws_writer_release_lock(ant_t *js, ant_value_t *args, int nargs) { ant_value_t stream_obj = ws_writer_stream(js->this_val); if (!is_object_type(stream_obj)) return js_mkundef(); - ant_value_t release_err = js_make_error_silent(js, JS_ERR_TYPE, "Writer was released"); - ws_writer_ensure_ready_promise_rejected(js, js->this_val, release_err); - ws_writer_ensure_closed_promise_rejected(js, js->this_val, release_err); + ws_writer_reject_ready_promise(js, js->this_val, release_err); + ws_writer_reject_closed_promise(js, js->this_val, release_err); + + promise_mark_handled(ws_writer_ready(js->this_val)); + promise_mark_handled(ws_writer_closed(js->this_val)); + + ws_writer_replace_ready_promise_rejected(js, js->this_val, release_err); + ws_writer_replace_closed_promise_rejected(js, js->this_val, release_err); js_set_slot(stream_obj, SLOT_CTOR, js_mkundef()); js_set_slot(js->this_val, SLOT_ENTRIES, js_mkundef()); + return js_mkundef(); } @@ -964,6 +987,9 @@ ant_value_t js_ws_writer_ctor(ant_t *js, ant_value_t *args, int nargs) { ant_value_t closed = js_mkpromise(js); ant_value_t ready = js_mkpromise(js); + + promise_mark_handled(closed); + promise_mark_handled(ready); js_set_slot(obj, SLOT_ENTRIES, stream_obj); js_set_slot(obj, SLOT_RS_CLOSED, closed);