1use crate::host::{invoke, with_host, JsObj};
26use fusevm::Value;
27use indexmap::IndexMap;
28use std::io::{Read, Write};
29
30pub const CLASSES: &[&str] = &[
35 "ReadableStream",
36 "ReadableStreamDefaultReader",
37 "ReadableStreamBYOBReader",
38 "ReadableStreamDefaultController",
39 "ReadableByteStreamController",
40 "ReadableStreamBYOBRequest",
41 "WritableStream",
42 "WritableStreamDefaultWriter",
43 "WritableStreamDefaultController",
44 "TransformStream",
45 "TransformStreamDefaultController",
46 "ByteLengthQueuingStrategy",
47 "CountQueuingStrategy",
48 "TextEncoderStream",
49 "TextDecoderStream",
50 "CompressionStream",
51 "DecompressionStream",
52];
53
54pub const METHODS: &[&str] = &[];
56
57pub const READABLE_STREAM_TAG: &str = "ReadableStream";
59pub const RS_DEFAULT_READER_TAG: &str = "ReadableStreamDefaultReader";
60pub const RS_BYOB_READER_TAG: &str = "ReadableStreamBYOBReader";
61pub const RS_DEFAULT_CONTROLLER_TAG: &str = "ReadableStreamDefaultController";
62pub const RS_BYTE_CONTROLLER_TAG: &str = "ReadableByteStreamController";
63pub const RS_BYOB_REQUEST_TAG: &str = "ReadableStreamBYOBRequest";
64pub const WRITABLE_STREAM_TAG: &str = "WritableStream";
65pub const WS_DEFAULT_WRITER_TAG: &str = "WritableStreamDefaultWriter";
66pub const WS_DEFAULT_CONTROLLER_TAG: &str = "WritableStreamDefaultController";
67pub const TRANSFORM_STREAM_TAG: &str = "TransformStream";
68pub const TS_DEFAULT_CONTROLLER_TAG: &str = "TransformStreamDefaultController";
69pub const BYTE_LENGTH_STRATEGY_TAG: &str = "ByteLengthQueuingStrategy";
70pub const COUNT_STRATEGY_TAG: &str = "CountQueuingStrategy";
71pub const TEXT_ENCODER_STREAM_TAG: &str = "TextEncoderStream";
72pub const TEXT_DECODER_STREAM_TAG: &str = "TextDecoderStream";
73pub const COMPRESSION_STREAM_TAG: &str = "CompressionStream";
74pub const DECOMPRESSION_STREAM_TAG: &str = "DecompressionStream";
75
76pub const READABLE_STREAM_METHODS: &[&str] =
78 &["getReader", "cancel", "tee", "pipeTo", "pipeThrough"];
79pub const RS_DEFAULT_READER_METHODS: &[&str] = &["read", "releaseLock", "cancel"];
80pub const RS_BYOB_READER_METHODS: &[&str] = &["read", "releaseLock", "cancel"];
81pub const RS_DEFAULT_CONTROLLER_METHODS: &[&str] = &["enqueue", "close", "error"];
82pub const RS_BYTE_CONTROLLER_METHODS: &[&str] = &["enqueue", "close", "error"];
83pub const RS_BYOB_REQUEST_METHODS: &[&str] = &["respond", "respondWithNewView"];
84pub const WRITABLE_STREAM_METHODS: &[&str] = &["getWriter", "abort", "close"];
85pub const WS_DEFAULT_WRITER_METHODS: &[&str] = &["write", "close", "abort", "releaseLock"];
86pub const WS_DEFAULT_CONTROLLER_METHODS: &[&str] = &["error"];
87pub const TS_DEFAULT_CONTROLLER_METHODS: &[&str] = &["enqueue", "terminate", "error"];
88pub const STRATEGY_METHODS: &[&str] = &["size"];
89
90pub fn is_class(name: &str) -> bool {
92 CLASSES.contains(&name)
93}
94
95pub fn constant(name: &str) -> Option<Value> {
97 if is_class(name) {
98 Some(with_host(|h| h.alloc(JsObj::Builtin(name.to_string()))))
99 } else {
100 None
101 }
102}
103
104pub fn methods_for(tag: &str) -> &'static [&'static str] {
106 match tag {
107 READABLE_STREAM_TAG => READABLE_STREAM_METHODS,
108 RS_DEFAULT_READER_TAG => RS_DEFAULT_READER_METHODS,
109 RS_BYOB_READER_TAG => RS_BYOB_READER_METHODS,
110 RS_DEFAULT_CONTROLLER_TAG => RS_DEFAULT_CONTROLLER_METHODS,
111 RS_BYTE_CONTROLLER_TAG => RS_BYTE_CONTROLLER_METHODS,
112 RS_BYOB_REQUEST_TAG => RS_BYOB_REQUEST_METHODS,
113 WRITABLE_STREAM_TAG => WRITABLE_STREAM_METHODS,
114 WS_DEFAULT_WRITER_TAG => WS_DEFAULT_WRITER_METHODS,
115 WS_DEFAULT_CONTROLLER_TAG => WS_DEFAULT_CONTROLLER_METHODS,
116 TS_DEFAULT_CONTROLLER_TAG => TS_DEFAULT_CONTROLLER_METHODS,
117 BYTE_LENGTH_STRATEGY_TAG | COUNT_STRATEGY_TAG => STRATEGY_METHODS,
118 _ => &[],
119 }
120}
121
122fn get_prop(recv: &Value, key: &str) -> Option<Value> {
125 with_host(|h| match h.get(recv) {
126 Some(JsObj::Object(p)) => p.get(key).cloned(),
127 _ => None,
128 })
129}
130
131fn set_prop(recv: &Value, key: &str, val: Value) {
132 with_host(|h| {
133 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
134 p.insert(key.to_string(), val);
135 }
136 });
137}
138
139fn remove_prop(recv: &Value, key: &str) {
140 with_host(|h| {
141 if let Some(JsObj::Object(p)) = h.get_mut(recv) {
142 p.shift_remove(key);
143 }
144 });
145}
146
147fn get_str(recv: &Value, key: &str) -> Option<String> {
148 get_prop(recv, key).map(|v| with_host(|h| h.str_of(&v)))
149}
150
151fn state_of(stream: &Value) -> String {
152 get_str(stream, "@@state").unwrap_or_else(|| "readable".into())
153}
154
155fn is_locked(stream: &Value) -> bool {
156 get_prop(stream, "locked")
157 .map(|v| with_host(|h| h.truthy(&v)))
158 .unwrap_or(false)
159}
160
161fn is_callable_val(v: &Value) -> bool {
162 with_host(|h| crate::host::is_callable(h, v))
163}
164
165fn opt_cb(obj: &Value, key: &str) -> Option<Value> {
167 get_prop(obj, key).filter(is_callable_val)
168}
169
170fn iter_result(value: Value, done: bool) -> Value {
172 with_host(|h| {
173 let mut m = IndexMap::new();
174 m.insert("value".into(), value);
175 m.insert("done".into(), Value::Bool(done));
176 h.new_object(m)
177 })
178}
179
180fn synth(msg: &str) -> Value {
181 with_host(|h| crate::builtins::synth_error(h, msg))
182}
183
184fn new_pending() -> Value {
185 with_host(|h| h.new_promise())
186}
187
188fn settle(p: &Value, val: Value) {
189 if let Some(id) = with_host(|h| h.promise_id(p)) {
190 crate::host::resolve_promise_val(id, val);
191 }
192}
193
194fn settle_reject(p: &Value, err: Value) {
195 if let Some(id) = with_host(|h| h.promise_id(p)) {
196 crate::host::reject_promise_val(id, err);
197 }
198}
199
200fn resolved(v: Value) -> Value {
201 crate::host::promise_of(&v)
202}
203
204fn rejected(err: Value) -> Value {
205 let p = new_pending();
206 settle_reject(&p, err);
207 p
208}
209
210fn chunk_bytes(v: &Value) -> Vec<u8> {
213 let via_field = with_host(|h| match h.get(v) {
214 Some(JsObj::Object(p)) => {
215 let field = if p.contains_key("@@bytes") {
216 Some("@@bytes")
217 } else if p.contains_key("@@buffer") {
218 return Some(
221 crate::stdlib::typedarray::elems_with_host(h, v)
222 .iter()
223 .map(|x| h.to_number(x) as u8)
224 .collect::<Vec<u8>>(),
225 );
226 } else {
227 None
228 };
229 field.and_then(|f| match p.get(f).and_then(|a| h.get(a)) {
230 Some(JsObj::Array(items)) => Some(
231 items
232 .iter()
233 .map(|x| h.to_number(x) as u8)
234 .collect::<Vec<u8>>(),
235 ),
236 _ => None,
237 })
238 }
239 _ => None,
240 });
241 via_field.unwrap_or_else(|| with_host(|h| h.str_of(v)).into_bytes())
242}
243
244fn chunk_byte_length(v: &Value) -> f64 {
246 if let Some(bl) = get_prop(v, "byteLength") {
247 return with_host(|h| h.to_number(&bl));
248 }
249 chunk_bytes(v).len() as f64
250}
251
252fn arr_push(recv: &Value, key: &str, val: Value) {
255 with_host(|h| {
256 let arr = match h.get(recv) {
257 Some(JsObj::Object(p)) => p.get(key).cloned(),
258 _ => None,
259 };
260 if let Some(a) = arr {
261 if let Some(JsObj::Array(items)) = h.get_mut(&a) {
262 items.push(val);
263 }
264 }
265 });
266}
267
268fn arr_shift(recv: &Value, key: &str) -> Option<Value> {
269 with_host(|h| {
270 let arr = match h.get(recv) {
271 Some(JsObj::Object(p)) => p.get(key).cloned(),
272 _ => None,
273 }?;
274 match h.get_mut(&arr) {
275 Some(JsObj::Array(items)) if !items.is_empty() => Some(items.remove(0)),
276 _ => None,
277 }
278 })
279}
280
281fn arr_len(recv: &Value, key: &str) -> usize {
282 with_host(|h| match h.get(recv) {
283 Some(JsObj::Object(p)) => match p.get(key).and_then(|a| h.get(a)) {
284 Some(JsObj::Array(items)) => items.len(),
285 _ => 0,
286 },
287 _ => 0,
288 })
289}
290
291fn arr_get(recv: &Value, key: &str, i: usize) -> Option<Value> {
292 with_host(|h| match h.get(recv) {
293 Some(JsObj::Object(p)) => match p.get(key).and_then(|a| h.get(a)) {
294 Some(JsObj::Array(items)) => items.get(i).cloned(),
295 _ => None,
296 },
297 _ => None,
298 })
299}
300
301fn arr_drain(recv: &Value, key: &str) -> Vec<Value> {
302 with_host(|h| {
303 let arr = match h.get(recv) {
304 Some(JsObj::Object(p)) => p.get(key).cloned(),
305 _ => None,
306 };
307 match arr.as_ref().and_then(|a| h.get_mut(a)) {
308 Some(JsObj::Array(items)) => std::mem::take(items),
309 _ => Vec::new(),
310 }
311 })
312}
313
314fn new_readable_shell() -> Value {
318 with_host(|h| {
319 let queue = h.new_array(Vec::new());
320 let waiters = h.new_array(Vec::new());
321 let mut m = IndexMap::new();
322 m.insert("@@native".into(), h.new_str(READABLE_STREAM_TAG));
323 m.insert("@@state".into(), h.new_str("readable"));
324 m.insert("@@queue".into(), queue);
325 m.insert("@@waiters".into(), waiters);
326 m.insert("locked".into(), Value::Bool(false));
327 h.new_object(m)
328 })
329}
330
331fn new_readable_controller(stream: &Value, byte: bool) -> Value {
333 let tag = if byte {
334 RS_BYTE_CONTROLLER_TAG
335 } else {
336 RS_DEFAULT_CONTROLLER_TAG
337 };
338 let ctrl = with_host(|h| {
339 let mut m = IndexMap::new();
340 m.insert("@@native".into(), h.new_str(tag));
341 m.insert("@@stream".into(), stream.clone());
342 m.insert("desiredSize".into(), Value::Float(1.0));
343 if byte {
344 m.insert("byobRequest".into(), h.null());
345 }
346 h.new_object(m)
347 });
348 set_prop(stream, "@@controller", ctrl.clone());
349 ctrl
350}
351
352fn stream_enqueue(stream: &Value, chunk: Value) {
355 if state_of(stream) != "readable" {
356 return;
357 }
358 if let Some(waiter) = arr_shift(stream, "@@waiters") {
359 let r = iter_result(chunk, false);
360 settle(&waiter, r);
361 } else {
362 arr_push(stream, "@@queue", chunk);
363 }
364}
365
366fn stream_close(stream: &Value) {
368 if state_of(stream) != "readable" {
369 return;
370 }
371 set_prop(stream, "@@state", with_host(|h| h.new_str("closed")));
372 for waiter in arr_drain(stream, "@@waiters") {
373 let r = iter_result(Value::Undef, true);
374 settle(&waiter, r);
375 }
376 if let Some(reader) = get_prop(stream, "@@reader") {
378 if let Some(cp) = get_prop(&reader, "closed") {
379 settle(&cp, Value::Undef);
380 }
381 }
382}
383
384fn stream_error(stream: &Value, err: Value) {
386 if state_of(stream) != "readable" {
387 return;
388 }
389 set_prop(stream, "@@state", with_host(|h| h.new_str("errored")));
390 set_prop(stream, "@@stored_error", err.clone());
391 for waiter in arr_drain(stream, "@@waiters") {
392 settle_reject(&waiter, err.clone());
393 }
394 if let Some(reader) = get_prop(stream, "@@reader") {
395 if let Some(cp) = get_prop(&reader, "closed") {
396 settle_reject(&cp, err.clone());
397 }
398 }
399}
400
401fn try_immediate_read(stream: &Value) -> Option<Value> {
403 match state_of(stream).as_str() {
404 "errored" => {
405 let e = get_prop(stream, "@@stored_error")
406 .unwrap_or_else(|| synth("TypeError: stream errored"));
407 Some(rejected(e))
408 }
409 _ if arr_len(stream, "@@queue") > 0 => {
410 let chunk = arr_shift(stream, "@@queue").unwrap_or(Value::Undef);
411 Some(resolved(iter_result(chunk, false)))
412 }
413 "closed" => Some(resolved(iter_result(Value::Undef, true))),
414 _ => None,
415 }
416}
417
418fn drive_pull(stream: &Value) {
421 if let Some(kind) = get_str(stream, "@@native_pull") {
422 if kind == "tee" {
423 tee_pull(stream);
424 }
425 return;
426 }
427 if let Some(pull) = opt_cb(stream, "@@pull") {
428 let ctrl = get_prop(stream, "@@controller").unwrap_or(Value::Undef);
429 if let Err(msg) = invoke(&pull, vec![ctrl], None) {
432 stream_error(stream, synth(&msg));
433 }
434 }
435}
436
437fn stream_read(stream: &Value) -> Value {
440 if let Some(p) = try_immediate_read(stream) {
441 return p;
442 }
443 drive_pull(stream);
444 if let Some(p) = try_immediate_read(stream) {
445 return p;
446 }
447 let p = new_pending();
449 arr_push(stream, "@@waiters", p.clone());
450 p
451}
452
453fn stream_cancel(stream: &Value, reason: Value) -> Value {
455 if state_of(stream) == "readable" {
456 let _ = arr_drain(stream, "@@queue");
458 if let Some(cancel) = opt_cb(stream, "@@cancel") {
459 if let Err(msg) = invoke(&cancel, vec![reason], None) {
460 return rejected(synth(&msg));
461 }
462 }
463 stream_close(stream);
464 }
465 resolved(Value::Undef)
466}
467
468pub fn construct_readable(args: &[Value]) -> Result<Value, String> {
472 let source = args.first().cloned().unwrap_or(Value::Undef);
473 let byte = get_str(&source, "type").as_deref() == Some("bytes");
474
475 let stream = new_readable_shell();
476 if byte {
477 set_prop(&stream, "@@type", with_host(|h| h.new_str("bytes")));
478 }
479 if let Some(pull) = opt_cb(&source, "pull") {
480 set_prop(&stream, "@@pull", pull);
481 }
482 if let Some(cancel) = opt_cb(&source, "cancel") {
483 set_prop(&stream, "@@cancel", cancel);
484 }
485 let ctrl = new_readable_controller(&stream, byte);
486
487 if let Some(start) = opt_cb(&source, "start") {
488 if let Err(msg) = invoke(&start, vec![ctrl], None) {
489 stream_error(&stream, synth(&msg));
490 }
491 }
492 Ok(stream)
493}
494
495fn get_reader(stream: &Value, args: &[Value]) -> Result<Value, String> {
497 if is_locked(stream) {
498 return Err(crate::host::type_error("ReadableStream is locked"));
499 }
500 let byob = args.first().and_then(|o| get_str(o, "mode")).as_deref() == Some("byob");
501 let tag = if byob {
502 RS_BYOB_READER_TAG
503 } else {
504 RS_DEFAULT_READER_TAG
505 };
506
507 let closed = new_pending();
509 match state_of(stream).as_str() {
510 "closed" => settle(&closed, Value::Undef),
511 "errored" => settle_reject(
512 &closed,
513 get_prop(stream, "@@stored_error")
514 .unwrap_or_else(|| synth("TypeError: stream errored")),
515 ),
516 _ => {}
517 }
518
519 let reader = with_host(|h| {
520 let mut m = IndexMap::new();
521 m.insert("@@native".into(), h.new_str(tag));
522 m.insert("@@stream".into(), stream.clone());
523 m.insert("closed".into(), closed);
524 h.new_object(m)
525 });
526 set_prop(stream, "locked", Value::Bool(true));
527 set_prop(stream, "@@reader", reader.clone());
528 Ok(reader)
529}
530
531fn reader_release(reader: &Value) {
532 if let Some(stream) = get_prop(reader, "@@stream") {
533 set_prop(&stream, "locked", Value::Bool(false));
534 remove_prop(&stream, "@@reader");
535 }
536 remove_prop(reader, "@@stream");
537}
538
539fn ws_accept_chunk(ws: &Value, chunk: Value) -> Result<(), String> {
544 if let Some(kind) = get_str(ws, "@@xform_kind") {
545 let readable = get_prop(ws, "@@readable");
546 match kind.as_str() {
547 "textencode" => {
548 if let Some(r) = &readable {
549 let bytes = with_host(|h| h.str_of(&chunk)).into_bytes();
550 let buf = super::buffer::from_bytes(&bytes);
551 stream_enqueue(r, buf);
552 }
553 }
554 "textdecode" => {
555 if let Some(r) = &readable {
556 let bytes = chunk_bytes(&chunk);
557 let s = with_host(|h| h.new_str(String::from_utf8_lossy(&bytes).into_owned()));
558 stream_enqueue(r, s);
559 }
560 }
561 k if k.starts_with("compress:") || k.starts_with("decompress:") => {
562 let mut bytes = chunk_bytes(&chunk);
563 arr_push_bytes(ws, &mut bytes);
564 }
565 "js" => {
566 let ctrl = get_prop(ws, "@@tcontroller").unwrap_or(Value::Undef);
567 if let Some(transform) = opt_cb(ws, "@@transform") {
568 invoke(&transform, vec![chunk, ctrl], None)?;
569 } else if let Some(r) = &readable {
570 stream_enqueue(r, chunk);
572 }
573 }
574 _ => {}
575 }
576 return Ok(());
577 }
578 if let Some(write) = opt_cb(ws, "@@write") {
579 let ctrl = get_prop(ws, "@@controller").unwrap_or(Value::Undef);
580 invoke(&write, vec![chunk, ctrl], None)?;
581 }
582 Ok(())
583}
584
585fn arr_push_bytes(ws: &Value, bytes: &mut Vec<u8>) {
587 with_host(|h| {
588 let accum = match h.get(ws) {
589 Some(JsObj::Object(p)) => p.get("@@accum").cloned(),
590 _ => None,
591 };
592 if let Some(a) = accum {
593 if let Some(JsObj::Array(items)) = h.get_mut(&a) {
594 items.extend(bytes.drain(..).map(|b| Value::Float(b as f64)));
595 }
596 }
597 });
598}
599
600fn ws_accum_bytes(ws: &Value) -> Vec<u8> {
601 with_host(|h| match h.get(ws) {
602 Some(JsObj::Object(p)) => match p.get("@@accum").and_then(|a| h.get(a)) {
603 Some(JsObj::Array(items)) => items.iter().map(|x| h.to_number(x) as u8).collect(),
604 _ => Vec::new(),
605 },
606 _ => Vec::new(),
607 })
608}
609
610fn ws_finish(ws: &Value) -> Result<(), String> {
613 if let Some(kind) = get_str(ws, "@@xform_kind") {
614 let readable = get_prop(ws, "@@readable");
615 match kind.as_str() {
616 "js" => {
617 let ctrl = get_prop(ws, "@@tcontroller").unwrap_or(Value::Undef);
618 if let Some(flush) = opt_cb(ws, "@@flush") {
619 invoke(&flush, vec![ctrl], None)?;
620 }
621 if let Some(r) = &readable {
622 stream_close(r);
623 }
624 }
625 k if k.starts_with("compress:") || k.starts_with("decompress:") => {
626 let data = ws_accum_bytes(ws);
627 let out = run_codec(&kind, &data)?;
628 if let Some(r) = &readable {
629 stream_enqueue(r, super::buffer::from_bytes(&out));
630 stream_close(r);
631 }
632 }
633 _ => {
634 if let Some(r) = &readable {
635 stream_close(r);
636 }
637 }
638 }
639 } else if let Some(close) = opt_cb(ws, "@@close") {
640 invoke(&close, vec![], None)?;
641 }
642 set_prop(ws, "@@state", with_host(|h| h.new_str("closed")));
643 if let Some(writer) = get_prop(ws, "@@writer") {
644 if let Some(cp) = get_prop(&writer, "closed") {
645 settle(&cp, Value::Undef);
646 }
647 }
648 Ok(())
649}
650
651fn ws_abort(ws: &Value, reason: Value) -> Result<(), String> {
653 if get_str(ws, "@@xform_kind").is_some() {
654 if let Some(r) = get_prop(ws, "@@readable") {
655 stream_error(&r, reason.clone());
656 }
657 } else if let Some(abort) = opt_cb(ws, "@@abort") {
658 invoke(&abort, vec![reason.clone()], None)?;
659 }
660 set_prop(ws, "@@state", with_host(|h| h.new_str("errored")));
661 set_prop(ws, "@@stored_error", reason.clone());
662 if let Some(writer) = get_prop(ws, "@@writer") {
663 if let Some(cp) = get_prop(&writer, "closed") {
664 settle_reject(&cp, reason);
665 }
666 }
667 Ok(())
668}
669
670pub fn construct_writable(args: &[Value]) -> Result<Value, String> {
672 let sink = args.first().cloned().unwrap_or(Value::Undef);
673 let ws = with_host(|h| {
674 let mut m = IndexMap::new();
675 m.insert("@@native".into(), h.new_str(WRITABLE_STREAM_TAG));
676 m.insert("@@state".into(), h.new_str("writable"));
677 m.insert("locked".into(), Value::Bool(false));
678 h.new_object(m)
679 });
680 for key in ["write", "close", "abort"] {
681 if let Some(cb) = opt_cb(&sink, key) {
682 set_prop(&ws, &format!("@@{key}"), cb);
683 }
684 }
685 let ctrl = with_host(|h| {
686 let mut m = IndexMap::new();
687 m.insert("@@native".into(), h.new_str(WS_DEFAULT_CONTROLLER_TAG));
688 m.insert("@@stream".into(), ws.clone());
689 h.new_object(m)
690 });
691 set_prop(&ws, "@@controller", ctrl.clone());
692 if let Some(start) = opt_cb(&sink, "start") {
693 if let Err(msg) = invoke(&start, vec![ctrl], None) {
694 let _ = ws_abort(&ws, synth(&msg));
695 }
696 }
697 Ok(ws)
698}
699
700fn get_writer(ws: &Value) -> Result<Value, String> {
702 if is_locked(ws) {
703 return Err(crate::host::type_error("WritableStream is locked"));
704 }
705 let ready = resolved(Value::Undef);
706 let closed = new_pending();
707 if state_of(ws) == "closed" {
708 settle(&closed, Value::Undef);
709 } else if state_of(ws) == "errored" {
710 settle_reject(
711 &closed,
712 get_prop(ws, "@@stored_error").unwrap_or(Value::Undef),
713 );
714 }
715 let writer = with_host(|h| {
716 let mut m = IndexMap::new();
717 m.insert("@@native".into(), h.new_str(WS_DEFAULT_WRITER_TAG));
718 m.insert("@@stream".into(), ws.clone());
719 m.insert("ready".into(), ready);
720 m.insert("closed".into(), closed);
721 m.insert("desiredSize".into(), Value::Float(1.0));
722 h.new_object(m)
723 });
724 set_prop(ws, "locked", Value::Bool(true));
725 set_prop(ws, "@@writer", writer.clone());
726 Ok(writer)
727}
728
729fn writer_release(writer: &Value) {
730 if let Some(ws) = get_prop(writer, "@@stream") {
731 set_prop(&ws, "locked", Value::Bool(false));
732 remove_prop(&ws, "@@writer");
733 }
734 remove_prop(writer, "@@stream");
735}
736
737fn build_transform_pair(kind: &str) -> (Value, Value) {
742 let readable = new_readable_shell();
743 let ws = with_host(|h| {
744 let accum = h.new_array(Vec::new());
745 let mut m = IndexMap::new();
746 m.insert("@@native".into(), h.new_str(WRITABLE_STREAM_TAG));
747 m.insert("@@state".into(), h.new_str("writable"));
748 m.insert("locked".into(), Value::Bool(false));
749 m.insert("@@xform_kind".into(), h.new_str(kind));
750 m.insert("@@accum".into(), accum);
751 h.new_object(m)
752 });
753 set_prop(&ws, "@@readable", readable.clone());
754 (readable, ws)
755}
756
757pub fn construct_transform(args: &[Value]) -> Result<Value, String> {
759 let transformer = args.first().cloned().unwrap_or(Value::Undef);
760 let (readable, writable) = build_transform_pair("js");
761
762 let tctrl = with_host(|h| {
764 let mut m = IndexMap::new();
765 m.insert("@@native".into(), h.new_str(TS_DEFAULT_CONTROLLER_TAG));
766 m.insert("@@readable".into(), readable.clone());
767 m.insert("@@writable".into(), writable.clone());
768 m.insert("desiredSize".into(), Value::Float(1.0));
769 h.new_object(m)
770 });
771 set_prop(&writable, "@@tcontroller", tctrl.clone());
772 if let Some(t) = opt_cb(&transformer, "transform") {
773 set_prop(&writable, "@@transform", t);
774 }
775 if let Some(f) = opt_cb(&transformer, "flush") {
776 set_prop(&writable, "@@flush", f);
777 }
778
779 let ts = with_host(|h| {
780 let mut m = IndexMap::new();
781 m.insert("@@native".into(), h.new_str(TRANSFORM_STREAM_TAG));
782 m.insert("readable".into(), readable);
783 m.insert("writable".into(), writable);
784 h.new_object(m)
785 });
786
787 if let Some(start) = opt_cb(&transformer, "start") {
788 invoke(&start, vec![tctrl], None)?;
789 }
790 Ok(ts)
791}
792
793fn build_codec_stream(tag: &str, kind: &str, extra: Vec<(&str, Value)>) -> Value {
796 let (readable, writable) = build_transform_pair(kind);
797 with_host(|h| {
798 let mut m = IndexMap::new();
799 m.insert("@@native".into(), h.new_str(tag));
800 m.insert("readable".into(), readable);
801 m.insert("writable".into(), writable);
802 for (k, v) in extra {
803 m.insert(k.to_string(), v);
804 }
805 h.new_object(m)
806 })
807}
808
809pub fn construct_text_encoder_stream() -> Result<Value, String> {
810 let enc = with_host(|h| h.new_str("utf-8"));
811 Ok(build_codec_stream(
812 TEXT_ENCODER_STREAM_TAG,
813 "textencode",
814 vec![("encoding", enc)],
815 ))
816}
817
818pub fn construct_text_decoder_stream(args: &[Value]) -> Result<Value, String> {
819 let label = args
820 .first()
821 .map(|v| with_host(|h| h.str_of(v)))
822 .filter(|s| !s.is_empty() && s.as_str() != "undefined")
823 .unwrap_or_else(|| "utf-8".into());
824 let extra = with_host(|h| {
825 vec![
826 ("encoding", h.new_str(label.to_lowercase())),
827 ("fatal", Value::Bool(false)),
828 ("ignoreBOM", Value::Bool(false)),
829 ]
830 });
831 Ok(build_codec_stream(
832 TEXT_DECODER_STREAM_TAG,
833 "textdecode",
834 extra,
835 ))
836}
837
838pub fn construct_compression(args: &[Value], decompress: bool) -> Result<Value, String> {
840 let format = super::arg_str(args, 0);
841 if !matches!(format.as_str(), "gzip" | "deflate" | "deflate-raw") {
842 return Err(crate::host::type_error(&format!(
843 "Unsupported compression format: '{format}'"
844 )));
845 }
846 let (tag, prefix) = if decompress {
847 (DECOMPRESSION_STREAM_TAG, "decompress")
848 } else {
849 (COMPRESSION_STREAM_TAG, "compress")
850 };
851 Ok(build_codec_stream(
852 tag,
853 &format!("{prefix}:{format}"),
854 Vec::new(),
855 ))
856}
857
858fn run_codec(kind: &str, data: &[u8]) -> Result<Vec<u8>, String> {
860 use flate2::read::{DeflateDecoder, GzDecoder, ZlibDecoder};
861 use flate2::write::{DeflateEncoder, GzEncoder, ZlibEncoder};
862 use flate2::Compression;
863 let io = |e: std::io::Error| format!("Error: {e}");
864 match kind {
865 "compress:gzip" => {
866 let mut e = GzEncoder::new(Vec::new(), Compression::default());
867 e.write_all(data).map_err(io)?;
868 e.finish().map_err(io)
869 }
870 "compress:deflate" => {
871 let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
872 e.write_all(data).map_err(io)?;
873 e.finish().map_err(io)
874 }
875 "compress:deflate-raw" => {
876 let mut e = DeflateEncoder::new(Vec::new(), Compression::default());
877 e.write_all(data).map_err(io)?;
878 e.finish().map_err(io)
879 }
880 "decompress:gzip" => {
881 let mut out = Vec::new();
882 GzDecoder::new(data).read_to_end(&mut out).map_err(io)?;
883 Ok(out)
884 }
885 "decompress:deflate" => {
886 let mut out = Vec::new();
887 ZlibDecoder::new(data).read_to_end(&mut out).map_err(io)?;
888 Ok(out)
889 }
890 "decompress:deflate-raw" => {
891 let mut out = Vec::new();
892 DeflateDecoder::new(data)
893 .read_to_end(&mut out)
894 .map_err(io)?;
895 Ok(out)
896 }
897 _ => Err(crate::host::type_error("unknown codec")),
898 }
899}
900
901pub fn construct_strategy(tag: &str, args: &[Value]) -> Result<Value, String> {
904 let hwm = args
905 .first()
906 .and_then(|o| get_prop(o, "highWaterMark"))
907 .map(|v| with_host(|h| h.to_number(&v)))
908 .unwrap_or(f64::NAN);
909 Ok(with_host(|h| {
910 let mut m = IndexMap::new();
911 m.insert("@@native".into(), h.new_str(tag));
912 m.insert("highWaterMark".into(), Value::Float(hwm));
913 h.new_object(m)
914 }))
915}
916
917enum Pulled {
921 Chunk(Value),
922 Done,
923 Errored(Value),
924}
925
926fn pull_one(stream: &Value) -> Pulled {
928 if state_of(stream) == "errored" {
929 return Pulled::Errored(get_prop(stream, "@@stored_error").unwrap_or(Value::Undef));
930 }
931 if let Some(c) = arr_shift(stream, "@@queue") {
932 return Pulled::Chunk(c);
933 }
934 if state_of(stream) == "closed" {
935 return Pulled::Done;
936 }
937 drive_pull(stream);
938 if state_of(stream) == "errored" {
939 return Pulled::Errored(get_prop(stream, "@@stored_error").unwrap_or(Value::Undef));
940 }
941 if let Some(c) = arr_shift(stream, "@@queue") {
942 return Pulled::Chunk(c);
943 }
944 Pulled::Done
946}
947
948fn byob_read(reader: &Value, args: &[Value]) -> Value {
950 let Some(stream) = get_prop(reader, "@@stream") else {
951 return rejected(synth("TypeError: reader has no associated stream"));
952 };
953 let view = args.first().cloned().unwrap_or(Value::Undef);
954 match pull_one(&stream) {
955 Pulled::Chunk(c) => {
956 let bytes = chunk_bytes(&c);
957 resolved(iter_result(super::buffer::from_bytes(&bytes), false))
960 }
961 Pulled::Done => resolved(iter_result(view, true)),
962 Pulled::Errored(e) => rejected(e),
963 }
964}
965
966fn tee_pull(branch: &Value) {
971 let Some(shared) = get_prop(branch, "@@tee_shared") else {
972 return;
973 };
974 let idx = get_prop(branch, "@@tee_index")
975 .map(|v| with_host(|h| h.to_number(&v)) as usize)
976 .unwrap_or(0);
977
978 if let Some(chunk) = arr_get(&shared, "@@buf", idx) {
979 set_prop(branch, "@@tee_index", Value::Float((idx + 1) as f64));
980 stream_enqueue(branch, chunk);
981 return;
982 }
983 let done = get_prop(&shared, "@@done")
984 .map(|v| with_host(|h| h.truthy(&v)))
985 .unwrap_or(false);
986 if done {
987 stream_close(branch);
988 return;
989 }
990 let Some(source) = get_prop(&shared, "@@source") else {
991 stream_close(branch);
992 return;
993 };
994 match pull_one(&source) {
995 Pulled::Chunk(c) => {
996 arr_push(&shared, "@@buf", c.clone());
997 set_prop(branch, "@@tee_index", Value::Float((idx + 1) as f64));
998 stream_enqueue(branch, c);
999 }
1000 Pulled::Done => {
1001 set_prop(&shared, "@@done", Value::Bool(true));
1002 stream_close(branch);
1003 }
1004 Pulled::Errored(e) => stream_error(branch, e),
1005 }
1006}
1007
1008fn tee(stream: &Value) -> Value {
1010 set_prop(stream, "locked", Value::Bool(true));
1011 let shared = with_host(|h| {
1012 let buf = h.new_array(Vec::new());
1013 let mut m = IndexMap::new();
1014 m.insert("@@source".into(), stream.clone());
1015 m.insert("@@buf".into(), buf);
1016 m.insert("@@done".into(), Value::Bool(false));
1017 h.new_object(m)
1018 });
1019 let make_branch = || {
1020 let b = new_readable_shell();
1021 new_readable_controller(&b, false);
1022 set_prop(&b, "@@native_pull", with_host(|h| h.new_str("tee")));
1023 set_prop(&b, "@@tee_shared", shared.clone());
1024 set_prop(&b, "@@tee_index", Value::Float(0.0));
1025 b
1026 };
1027 let b1 = make_branch();
1028 let b2 = make_branch();
1029 with_host(|h| h.new_array(vec![b1, b2]))
1030}
1031
1032fn pipe_to(stream: &Value, dest: &Value) -> Value {
1037 set_prop(stream, "locked", Value::Bool(true));
1038 set_prop(dest, "locked", Value::Bool(true));
1039 loop {
1040 match pull_one(stream) {
1041 Pulled::Chunk(c) => {
1042 if let Err(msg) = ws_accept_chunk(dest, c) {
1043 return rejected(synth(&msg));
1044 }
1045 }
1046 Pulled::Done => break,
1047 Pulled::Errored(e) => {
1048 let _ = ws_abort(dest, e.clone());
1049 return rejected(e);
1050 }
1051 }
1052 }
1053 if let Err(msg) = ws_finish(dest) {
1054 return rejected(synth(&msg));
1055 }
1056 resolved(Value::Undef)
1057}
1058
1059fn pipe_through(stream: &Value, args: &[Value]) -> Result<Value, String> {
1062 let transform = args.first().cloned().unwrap_or(Value::Undef);
1063 let writable = get_prop(&transform, "writable").ok_or_else(|| {
1064 crate::host::type_error("pipeThrough argument must have a writable and readable")
1065 })?;
1066 let readable = get_prop(&transform, "readable").ok_or_else(|| {
1067 crate::host::type_error("pipeThrough argument must have a writable and readable")
1068 })?;
1069 let _ = pipe_to(stream, &writable);
1070 Ok(readable)
1071}
1072
1073pub fn construct(name: &str, args: &[Value]) -> Option<Result<Value, String>> {
1077 Some(match name {
1078 "ReadableStream" => construct_readable(args),
1079 "WritableStream" => construct_writable(args),
1080 "TransformStream" => construct_transform(args),
1081 "TextEncoderStream" => construct_text_encoder_stream(),
1082 "TextDecoderStream" => construct_text_decoder_stream(args),
1083 "CompressionStream" => construct_compression(args, false),
1084 "DecompressionStream" => construct_compression(args, true),
1085 "ByteLengthQueuingStrategy" => construct_strategy(BYTE_LENGTH_STRATEGY_TAG, args),
1086 "CountQueuingStrategy" => construct_strategy(COUNT_STRATEGY_TAG, args),
1087 "ReadableStreamDefaultReader" => Ok(bare(RS_DEFAULT_READER_TAG)),
1091 "ReadableStreamBYOBReader" => Ok(bare(RS_BYOB_READER_TAG)),
1092 "ReadableStreamDefaultController" => Ok(bare(RS_DEFAULT_CONTROLLER_TAG)),
1093 "ReadableByteStreamController" => Ok(bare(RS_BYTE_CONTROLLER_TAG)),
1094 "ReadableStreamBYOBRequest" => Ok(bare(RS_BYOB_REQUEST_TAG)),
1095 "WritableStreamDefaultWriter" => Ok(bare(WS_DEFAULT_WRITER_TAG)),
1096 "WritableStreamDefaultController" => Ok(bare(WS_DEFAULT_CONTROLLER_TAG)),
1097 "TransformStreamDefaultController" => Ok(bare(TS_DEFAULT_CONTROLLER_TAG)),
1098 _ => return None,
1099 })
1100}
1101
1102fn bare(tag: &str) -> Value {
1103 with_host(|h| {
1104 let mut m = IndexMap::new();
1105 m.insert("@@native".into(), h.new_str(tag));
1106 h.new_object(m)
1107 })
1108}
1109
1110pub fn instance_call(
1114 tag: &str,
1115 recv: &Value,
1116 method: &str,
1117 args: Vec<Value>,
1118) -> Result<Value, String> {
1119 match tag {
1120 READABLE_STREAM_TAG => match method {
1121 "getReader" => get_reader(recv, &args),
1122 "cancel" => Ok(stream_cancel(
1123 recv,
1124 args.first().cloned().unwrap_or(Value::Undef),
1125 )),
1126 "tee" => Ok(tee(recv)),
1127 "pipeTo" => Ok(pipe_to(
1128 recv,
1129 &args.first().cloned().unwrap_or(Value::Undef),
1130 )),
1131 "pipeThrough" => pipe_through(recv, &args),
1132 _ => unknown(tag, method),
1133 },
1134 RS_DEFAULT_READER_TAG => match method {
1135 "read" => match get_prop(recv, "@@stream") {
1136 Some(stream) => Ok(stream_read(&stream)),
1137 None => Ok(rejected(synth(
1138 "TypeError: reader has no associated stream",
1139 ))),
1140 },
1141 "releaseLock" => {
1142 reader_release(recv);
1143 Ok(Value::Undef)
1144 }
1145 "cancel" => match get_prop(recv, "@@stream") {
1146 Some(stream) => Ok(stream_cancel(
1147 &stream,
1148 args.first().cloned().unwrap_or(Value::Undef),
1149 )),
1150 None => Ok(resolved(Value::Undef)),
1151 },
1152 _ => unknown(tag, method),
1153 },
1154 RS_BYOB_READER_TAG => match method {
1155 "read" => Ok(byob_read(recv, &args)),
1156 "releaseLock" => {
1157 reader_release(recv);
1158 Ok(Value::Undef)
1159 }
1160 "cancel" => match get_prop(recv, "@@stream") {
1161 Some(stream) => Ok(stream_cancel(
1162 &stream,
1163 args.first().cloned().unwrap_or(Value::Undef),
1164 )),
1165 None => Ok(resolved(Value::Undef)),
1166 },
1167 _ => unknown(tag, method),
1168 },
1169 RS_DEFAULT_CONTROLLER_TAG | RS_BYTE_CONTROLLER_TAG => {
1170 let stream = get_prop(recv, "@@stream").unwrap_or(Value::Undef);
1171 match method {
1172 "enqueue" => {
1173 stream_enqueue(&stream, args.first().cloned().unwrap_or(Value::Undef));
1174 Ok(Value::Undef)
1175 }
1176 "close" => {
1177 stream_close(&stream);
1178 Ok(Value::Undef)
1179 }
1180 "error" => {
1181 stream_error(&stream, args.first().cloned().unwrap_or(Value::Undef));
1182 Ok(Value::Undef)
1183 }
1184 _ => unknown(tag, method),
1185 }
1186 }
1187 RS_BYOB_REQUEST_TAG => match method {
1188 "respond" | "respondWithNewView" => Ok(Value::Undef),
1191 _ => unknown(tag, method),
1192 },
1193 WRITABLE_STREAM_TAG => match method {
1194 "getWriter" => get_writer(recv),
1195 "close" => match ws_finish(recv) {
1196 Ok(()) => Ok(resolved(Value::Undef)),
1197 Err(msg) => Ok(rejected(synth(&msg))),
1198 },
1199 "abort" => match ws_abort(recv, args.first().cloned().unwrap_or(Value::Undef)) {
1200 Ok(()) => Ok(resolved(Value::Undef)),
1201 Err(msg) => Ok(rejected(synth(&msg))),
1202 },
1203 _ => unknown(tag, method),
1204 },
1205 WS_DEFAULT_WRITER_TAG => {
1206 let ws = get_prop(recv, "@@stream").unwrap_or(Value::Undef);
1207 match method {
1208 "write" => {
1209 if state_of(&ws) == "errored" {
1210 return Ok(rejected(
1211 get_prop(&ws, "@@stored_error").unwrap_or(Value::Undef),
1212 ));
1213 }
1214 match ws_accept_chunk(&ws, args.first().cloned().unwrap_or(Value::Undef)) {
1215 Ok(()) => Ok(resolved(Value::Undef)),
1216 Err(msg) => Ok(rejected(synth(&msg))),
1217 }
1218 }
1219 "close" => match ws_finish(&ws) {
1220 Ok(()) => Ok(resolved(Value::Undef)),
1221 Err(msg) => Ok(rejected(synth(&msg))),
1222 },
1223 "abort" => match ws_abort(&ws, args.first().cloned().unwrap_or(Value::Undef)) {
1224 Ok(()) => Ok(resolved(Value::Undef)),
1225 Err(msg) => Ok(rejected(synth(&msg))),
1226 },
1227 "releaseLock" => {
1228 writer_release(recv);
1229 Ok(Value::Undef)
1230 }
1231 _ => unknown(tag, method),
1232 }
1233 }
1234 WS_DEFAULT_CONTROLLER_TAG => match method {
1235 "error" => {
1236 let ws = get_prop(recv, "@@stream").unwrap_or(Value::Undef);
1237 let _ = ws_abort(&ws, args.first().cloned().unwrap_or(Value::Undef));
1238 Ok(Value::Undef)
1239 }
1240 _ => unknown(tag, method),
1241 },
1242 TS_DEFAULT_CONTROLLER_TAG => {
1243 let readable = get_prop(recv, "@@readable").unwrap_or(Value::Undef);
1244 match method {
1245 "enqueue" => {
1246 stream_enqueue(&readable, args.first().cloned().unwrap_or(Value::Undef));
1247 Ok(Value::Undef)
1248 }
1249 "terminate" => {
1250 stream_close(&readable);
1251 Ok(Value::Undef)
1252 }
1253 "error" => {
1254 let e = args.first().cloned().unwrap_or(Value::Undef);
1255 stream_error(&readable, e.clone());
1256 if let Some(ws) = get_prop(recv, "@@writable") {
1257 let _ = ws_abort(&ws, e);
1258 }
1259 Ok(Value::Undef)
1260 }
1261 _ => unknown(tag, method),
1262 }
1263 }
1264 BYTE_LENGTH_STRATEGY_TAG => match method {
1265 "size" => Ok(Value::Float(chunk_byte_length(
1266 &args.first().cloned().unwrap_or(Value::Undef),
1267 ))),
1268 _ => unknown(tag, method),
1269 },
1270 COUNT_STRATEGY_TAG => match method {
1271 "size" => Ok(Value::Float(1.0)),
1272 _ => unknown(tag, method),
1273 },
1274 _ => unknown(tag, method),
1275 }
1276}
1277
1278fn unknown(tag: &str, method: &str) -> Result<Value, String> {
1279 Err(crate::host::type_error(&format!(
1280 "{tag}.{method} is not a function"
1281 )))
1282}