Skip to content

Commit 3ff3dcd

Browse files
authored
Add streams/web module (#761)
Web streams
1 parent ea4d05b commit 3ff3dcd

File tree

112 files changed

+33338
-64
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+33338
-64
lines changed

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ members = [
1818
"modules/llrt_perf_hooks",
1919
"modules/llrt_process",
2020
"modules/llrt_stream",
21+
"modules/llrt_stream_web",
2122
"modules/llrt_timers",
2223
"modules/llrt_url",
2324
"modules/llrt_zlib",

libs/llrt_utils/src/bytes.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33
use rquickjs::{
4-
ArrayBuffer, Coerced, Ctx, Exception, FromJs, IntoJs, Object, Result, TypedArray, Value,
4+
atom::PredefinedAtom,
5+
class::{Trace, Tracer},
6+
function::Constructor,
7+
ArrayBuffer, Coerced, Ctx, Exception, FromJs, IntoJs, JsLifetime, Object, Result, TypedArray,
8+
Value,
59
};
610

711
use crate::error_messages::ERROR_MSG_ARRAY_BUFFER_DETACHED;
@@ -22,6 +26,52 @@ pub enum ObjectBytes<'js> {
2226
Vec(Vec<u8>),
2327
}
2428

29+
// Requires manual implementation because rquickjs hasn't implemented JsLifetime for f32 or f64
30+
unsafe impl<'js> JsLifetime<'js> for ObjectBytes<'js> {
31+
type Changed<'to> = ObjectBytes<'to>;
32+
}
33+
34+
impl<'js> Trace<'js> for ObjectBytes<'js> {
35+
fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
36+
match self {
37+
ObjectBytes::U8Array(a) => a.trace(tracer),
38+
ObjectBytes::I8Array(a) => a.trace(tracer),
39+
ObjectBytes::U16Array(a) => a.trace(tracer),
40+
ObjectBytes::I16Array(a) => a.trace(tracer),
41+
ObjectBytes::U32Array(a) => a.trace(tracer),
42+
ObjectBytes::I32Array(a) => a.trace(tracer),
43+
ObjectBytes::U64Array(a) => a.trace(tracer),
44+
ObjectBytes::I64Array(a) => a.trace(tracer),
45+
ObjectBytes::F32Array(a) => a.trace(tracer),
46+
ObjectBytes::F64Array(a) => a.trace(tracer),
47+
ObjectBytes::DataView(d) => d.trace(tracer),
48+
ObjectBytes::Vec(v) => v.trace(tracer),
49+
}
50+
}
51+
}
52+
53+
impl<'js> IntoJs<'js> for ObjectBytes<'js> {
54+
fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
55+
match self {
56+
ObjectBytes::U8Array(a) => a.into_js(ctx),
57+
ObjectBytes::I8Array(a) => a.into_js(ctx),
58+
ObjectBytes::U16Array(a) => a.into_js(ctx),
59+
ObjectBytes::I16Array(a) => a.into_js(ctx),
60+
ObjectBytes::U32Array(a) => a.into_js(ctx),
61+
ObjectBytes::I32Array(a) => a.into_js(ctx),
62+
ObjectBytes::U64Array(a) => a.into_js(ctx),
63+
ObjectBytes::I64Array(a) => a.into_js(ctx),
64+
ObjectBytes::F32Array(a) => a.into_js(ctx),
65+
ObjectBytes::F64Array(a) => a.into_js(ctx),
66+
ObjectBytes::DataView(d) => {
67+
let ctor: Constructor = ctx.globals().get(PredefinedAtom::DataView)?;
68+
ctor.construct((d,))
69+
},
70+
ObjectBytes::Vec(v) => v.into_js(ctx),
71+
}
72+
}
73+
}
74+
2575
impl<'js> From<ObjectBytes<'js>> for Vec<u8> {
2676
fn from(value: ObjectBytes<'js>) -> Self {
2777
value.into_bytes()

llrt_core/src/utils/clone.rs renamed to libs/llrt_utils/src/clone.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@ use std::collections::HashSet;
22

33
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
44
// SPDX-License-Identifier: Apache-2.0
5-
use llrt_utils::{
6-
object::ObjectExt,
7-
primordials::{BasePrimordials, Primordial},
8-
};
95
use rquickjs::{
106
atom::PredefinedAtom,
11-
function::This,
12-
function::{Constructor, Opt},
13-
Array, Ctx, Function, IntoJs, Null, Object, Result, Type, Value,
7+
function::{Constructor, Opt, This},
8+
Array, ArrayBuffer, Ctx, Function, IntoJs, Null, Object, Result, Type, Value,
149
};
1510

16-
use super::hash;
11+
use super::{
12+
hash,
13+
object::ObjectExt,
14+
primordials::{BasePrimordials, Primordial},
15+
};
1716

1817
#[derive(Debug)]
1918
enum StackItem<'js> {
@@ -343,11 +342,17 @@ fn append_transfer_value<'js>(
343342
object_key: Option<String>,
344343
array_index: Option<usize>,
345344
) -> Result<()> {
345+
let value = if let Some(ab) = ArrayBuffer::from_value(value.clone()) {
346+
ab.get::<_, Function>("transfer")?.call((This(ab),))?
347+
} else {
348+
value.clone()
349+
};
350+
346351
tape.push(TapeItem {
347352
parent,
348353
object_key,
349354
array_index,
350-
value: TapeValue::Value(value.clone()),
355+
value: TapeValue::Value(value),
351356
});
352357
Ok(())
353358
}
@@ -404,12 +409,11 @@ mod tests {
404409
use llrt_test::test_sync_with;
405410
use rquickjs::{function::Opt, Object, Value};
406411

407-
use crate::utils::clone::structured_clone;
412+
use super::structured_clone;
408413

409414
#[tokio::test]
410415
async fn clone() {
411416
test_sync_with(|ctx| {
412-
crate::modules::buffer::init(&ctx)?;
413417
let value: Object = ctx.eval(
414418
r#"
415419
const a = {
File renamed without changes.

libs/llrt_utils/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44
pub mod bytearray_buffer;
55
pub mod bytes;
66
pub mod class;
7+
pub mod clone;
78
pub mod error;
89
pub mod error_messages;
910
#[cfg(feature = "fs")]
1011
pub mod fs;
12+
pub mod hash;
1113
pub mod macros;
1214
pub mod mc_oneshot;
1315
pub mod module;
1416
pub mod object;
17+
pub mod option;
1518
pub mod primordials;
1619
pub mod result;
1720
pub mod reuse_list;

libs/llrt_utils/src/option.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use rquickjs::{
2+
class::{Trace, Tracer},
3+
function::{FromParam, ParamRequirement, ParamsAccessor},
4+
Ctx, FromJs, IntoJs, JsLifetime, Result, Type, Value,
5+
};
6+
7+
/// Helper type for treating an undefined value as None, without treating null as None
8+
#[derive(Clone)]
9+
pub struct Undefined<T>(pub Option<T>);
10+
11+
impl<'js, T: FromJs<'js>> FromJs<'js> for Undefined<T> {
12+
fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
13+
if value.type_of() == Type::Undefined {
14+
Ok(Self(None))
15+
} else {
16+
Ok(Self(Some(FromJs::from_js(ctx, value)?)))
17+
}
18+
}
19+
}
20+
21+
impl<'js, T: IntoJs<'js>> IntoJs<'js> for Undefined<T> {
22+
fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
23+
match self.0 {
24+
None => Ok(Value::new_undefined(ctx.clone())),
25+
Some(val) => val.into_js(ctx),
26+
}
27+
}
28+
}
29+
30+
impl<T> Default for Undefined<T> {
31+
fn default() -> Self {
32+
Self(None)
33+
}
34+
}
35+
36+
unsafe impl<'js, T: JsLifetime<'js>> JsLifetime<'js> for Undefined<T> {
37+
type Changed<'to> = Undefined<T::Changed<'to>>;
38+
}
39+
40+
impl<'js, T: Trace<'js>> Trace<'js> for Undefined<T> {
41+
fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
42+
self.0.trace(tracer)
43+
}
44+
}
45+
46+
/// Helper type for converting an None into null instead of undefined.
47+
/// Needed while rquickjs::function::Null has no IntoJs implementation
48+
#[derive(Clone)]
49+
pub struct Null<T>(pub Option<T>);
50+
51+
impl<'js, T: FromJs<'js>> FromJs<'js> for Null<T> {
52+
fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
53+
if value.type_of() == Type::Null {
54+
Ok(Self(None))
55+
} else {
56+
Ok(Self(Some(FromJs::from_js(ctx, value)?)))
57+
}
58+
}
59+
}
60+
61+
impl<'js, T: IntoJs<'js>> IntoJs<'js> for Null<T> {
62+
fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
63+
match self.0 {
64+
None => Ok(Value::new_null(ctx.clone())),
65+
Some(val) => val.into_js(ctx),
66+
}
67+
}
68+
}
69+
70+
unsafe impl<'js, T: JsLifetime<'js>> JsLifetime<'js> for Null<T> {
71+
type Changed<'to> = Null<T::Changed<'to>>;
72+
}
73+
74+
impl<'js, T: Trace<'js>> Trace<'js> for Null<T> {
75+
fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
76+
self.0.trace(tracer)
77+
}
78+
}
79+
80+
/// Helper type for accepting no value, or null, but considering undefined as a value
81+
pub struct NullableOpt<T>(pub Option<T>);
82+
83+
impl<'js, T: FromJs<'js>> FromParam<'js> for NullableOpt<T> {
84+
fn param_requirement() -> ParamRequirement {
85+
ParamRequirement::optional()
86+
}
87+
88+
fn from_param<'a>(params: &mut ParamsAccessor<'a, 'js>) -> Result<Self> {
89+
if !params.is_empty() {
90+
let arg = params.arg();
91+
if arg.is_null() {
92+
Ok(NullableOpt(None))
93+
} else {
94+
let ctx = params.ctx().clone();
95+
Ok(NullableOpt(Some(T::from_js(&ctx, arg)?)))
96+
}
97+
} else {
98+
Ok(NullableOpt(None))
99+
}
100+
}
101+
}

libs/llrt_utils/src/primordials.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,15 @@ pub struct BasePrimordials<'js> {
1313
pub constructor_date: Constructor<'js>,
1414
pub constructor_error: Constructor<'js>,
1515
pub constructor_type_error: Constructor<'js>,
16+
pub constructor_range_error: Constructor<'js>,
1617
pub constructor_regexp: Constructor<'js>,
18+
pub constructor_uint8array: Constructor<'js>,
19+
pub constructor_array_buffer: Constructor<'js>,
1720
pub constructor_proxy: Constructor<'js>,
21+
pub constructor_object: Constructor<'js>,
22+
pub constructor_bool: Constructor<'js>,
23+
pub constructor_number: Constructor<'js>,
24+
pub constructor_string: Constructor<'js>,
1825

1926
// Prototypes
2027
pub prototype_object: Object<'js>,
@@ -28,7 +35,6 @@ pub struct BasePrimordials<'js> {
2835
pub function_array_from: Function<'js>,
2936
pub function_array_buffer_is_view: Function<'js>,
3037
pub function_get_own_property_descriptor: Function<'js>,
31-
pub function_number: Function<'js>,
3238
pub function_parse_int: Function<'js>,
3339
pub function_parse_float: Function<'js>,
3440
pub function_symbol_for: Function<'js>,
@@ -61,7 +67,7 @@ impl<'js> Primordial<'js> for BasePrimordials<'js> {
6167
fn new(ctx: &Ctx<'js>) -> Result<Self> {
6268
let globals = ctx.globals();
6369

64-
let constructor_object: Object = globals.get(PredefinedAtom::Object)?;
70+
let constructor_object: Constructor = globals.get(PredefinedAtom::Object)?;
6571
let prototype_object: Object = constructor_object.get(PredefinedAtom::Prototype)?;
6672

6773
let constructor_proxy: Constructor = globals.get(PredefinedAtom::Proxy)?;
@@ -81,8 +87,12 @@ impl<'js> Primordial<'js> for BasePrimordials<'js> {
8187
let constructor_regexp: Constructor = globals.get(PredefinedAtom::RegExp)?;
8288
let prototype_regexp: Object = constructor_regexp.get(PredefinedAtom::Prototype)?;
8389

90+
let constructor_uint8array: Constructor = globals.get(PredefinedAtom::Uint8Array)?;
91+
let constructor_arraybuffer: Constructor = globals.get(PredefinedAtom::ArrayBuffer)?;
92+
8493
let constructor_error: Constructor = globals.get(PredefinedAtom::Error)?;
8594
let constructor_type_error: Constructor = ctx.globals().get(PredefinedAtom::TypeError)?;
95+
let constructor_range_error: Constructor = ctx.globals().get(PredefinedAtom::RangeError)?;
8696
let prototype_error: Object = constructor_error.get(PredefinedAtom::Prototype)?;
8797

8898
let constructor_array: Object = globals.get(PredefinedAtom::Array)?;
@@ -91,12 +101,16 @@ impl<'js> Primordial<'js> for BasePrimordials<'js> {
91101
let constructor_array_buffer: Object = globals.get(PredefinedAtom::ArrayBuffer)?;
92102
let function_array_buffer_is_view: Function = constructor_array_buffer.get("isView")?;
93103

94-
let function_number: Function = globals.get(PredefinedAtom::Number)?;
95-
let function_parse_float: Function = function_number.get("parseFloat")?;
96-
let function_parse_int: Function = function_number.get("parseInt")?;
104+
let constructor_bool: Constructor = globals.get(PredefinedAtom::Boolean)?;
105+
106+
let constructor_number: Constructor = globals.get(PredefinedAtom::Number)?;
107+
let function_parse_float: Function = constructor_number.get("parseFloat")?;
108+
let function_parse_int: Function = constructor_number.get("parseInt")?;
109+
110+
let constructor_string: Constructor = globals.get(PredefinedAtom::String)?;
97111

98-
let symbol_constructor: Function = globals.get(PredefinedAtom::Symbol)?;
99-
let function_symbol_for: Function = symbol_constructor.get(PredefinedAtom::For)?;
112+
let constructor_symbol: Constructor = globals.get(PredefinedAtom::Symbol)?;
113+
let function_symbol_for: Function = constructor_symbol.get(PredefinedAtom::For)?;
100114

101115
let symbol_custom_inspect: Symbol<'js> =
102116
function_symbol_for.call((CUSTOM_INSPECT_SYMBOL_DESCRIPTION,))?;
@@ -108,7 +122,14 @@ impl<'js> Primordial<'js> for BasePrimordials<'js> {
108122
constructor_proxy,
109123
constructor_error,
110124
constructor_type_error,
125+
constructor_range_error,
111126
constructor_regexp,
127+
constructor_uint8array,
128+
constructor_array_buffer: constructor_arraybuffer,
129+
constructor_object,
130+
constructor_bool,
131+
constructor_number,
132+
constructor_string,
112133
prototype_object,
113134
prototype_date,
114135
prototype_regexp,
@@ -121,7 +142,6 @@ impl<'js> Primordial<'js> for BasePrimordials<'js> {
121142
function_parse_float,
122143
function_parse_int,
123144
function_symbol_for,
124-
function_number,
125145
symbol_custom_inspect,
126146
})
127147
}

llrt_core/src/module_builder.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::modules::{
1515
path::PathModule,
1616
perf_hooks::PerfHooksModule,
1717
process::ProcessModule,
18+
stream_web::StreamWebModule,
1819
tty::TtyModule,
1920
url::UrlModule,
2021
util::UtilModule,
@@ -95,6 +96,8 @@ impl Default for ModuleBuilder {
9596
.with_global(crate::modules::navigator::init)
9697
.with_global(crate::modules::url::init)
9798
.with_module(UrlModule)
99+
.with_module(StreamWebModule)
100+
.with_global(crate::modules::stream_web::init)
98101
.with_global(crate::modules::http::init)
99102
.with_global(crate::modules::exceptions::init)
100103
.with_module(LlrtHexModule)

0 commit comments

Comments
 (0)