Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
748f62f
Web streams
jackkleeman Aug 19, 2024
43d03d2
Set up global ReadableStream etc classes
jackkleeman Jan 1, 2025
fe6e226
Avoid Option for readable stream controller
jackkleeman Jan 2, 2025
da836c9
Bundle stored_error into state
jackkleeman Jan 2, 2025
8e370a4
Avoid usage of ctx.eval and ctx.globals
jackkleeman Jan 3, 2025
5ea3137
Use primordials more sparingly
jackkleeman Jan 4, 2025
a3f67a1
Use Function::new less
jackkleeman Jan 4, 2025
6dc019f
Use primordials for the array constructor lookup
jackkleeman Jan 4, 2025
64afdd8
Rename ObjectExt
jackkleeman Jan 4, 2025
cd066bf
Move structured_clone into llrt_utils and use it in tee
jackkleeman Jan 4, 2025
36d5e2b
Assorted cleanup
jackkleeman Jan 6, 2025
513dfed
queuing strategy comments
jackkleeman Jan 7, 2025
f23d14c
Move option helpers into utils
jackkleeman Jan 7, 2025
329c5b0
Move helpers from lib into our own utils
jackkleeman Jan 7, 2025
234c245
Add docs for StreamWebModule
jackkleeman Jan 7, 2025
c70633e
Add stream/web types
jackkleeman Jan 7, 2025
e4f892b
Review comments
jackkleeman Jan 8, 2025
a85e623
Review comments
jackkleeman Jan 8, 2025
363b4ad
Split readablestream out into its own module
jackkleeman Jan 8, 2025
3cde6bf
Move writablestream into its own module
jackkleeman Jan 8, 2025
823c27d
Check signal matches abortsignal earlier
jackkleeman Jan 8, 2025
52e56a5
Use Acquire/Release for atomic store/load
jackkleeman Jan 8, 2025
0b5172e
Avoid use of super
jackkleeman Jan 8, 2025
4ef0ca4
Add DomExceptionName and wpt for DomException
jackkleeman Jan 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ members = [
"modules/llrt_perf_hooks",
"modules/llrt_process",
"modules/llrt_stream",
"modules/llrt_stream_web",
"modules/llrt_timers",
"modules/llrt_url",
"modules/llrt_zlib",
Expand Down
52 changes: 51 additions & 1 deletion libs/llrt_utils/src/bytes.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use rquickjs::{
ArrayBuffer, Coerced, Ctx, Exception, FromJs, IntoJs, Object, Result, TypedArray, Value,
atom::PredefinedAtom,
class::{Trace, Tracer},
function::Constructor,
ArrayBuffer, Coerced, Ctx, Exception, FromJs, IntoJs, JsLifetime, Object, Result, TypedArray,
Value,
};

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

// Requires manual implementation because rquickjs hasn't implemented JsLifetime for f32 or f64
unsafe impl<'js> JsLifetime<'js> for ObjectBytes<'js> {
type Changed<'to> = ObjectBytes<'to>;
}

impl<'js> Trace<'js> for ObjectBytes<'js> {
fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
match self {
ObjectBytes::U8Array(a) => a.trace(tracer),
ObjectBytes::I8Array(a) => a.trace(tracer),
ObjectBytes::U16Array(a) => a.trace(tracer),
ObjectBytes::I16Array(a) => a.trace(tracer),
ObjectBytes::U32Array(a) => a.trace(tracer),
ObjectBytes::I32Array(a) => a.trace(tracer),
ObjectBytes::U64Array(a) => a.trace(tracer),
ObjectBytes::I64Array(a) => a.trace(tracer),
ObjectBytes::F32Array(a) => a.trace(tracer),
ObjectBytes::F64Array(a) => a.trace(tracer),
ObjectBytes::DataView(d) => d.trace(tracer),
ObjectBytes::Vec(v) => v.trace(tracer),
}
}
}

impl<'js> IntoJs<'js> for ObjectBytes<'js> {
fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
match self {
ObjectBytes::U8Array(a) => a.into_js(ctx),
ObjectBytes::I8Array(a) => a.into_js(ctx),
ObjectBytes::U16Array(a) => a.into_js(ctx),
ObjectBytes::I16Array(a) => a.into_js(ctx),
ObjectBytes::U32Array(a) => a.into_js(ctx),
ObjectBytes::I32Array(a) => a.into_js(ctx),
ObjectBytes::U64Array(a) => a.into_js(ctx),
ObjectBytes::I64Array(a) => a.into_js(ctx),
ObjectBytes::F32Array(a) => a.into_js(ctx),
ObjectBytes::F64Array(a) => a.into_js(ctx),
ObjectBytes::DataView(d) => {
let ctor: Constructor = ctx.globals().get(PredefinedAtom::DataView)?;
ctor.construct((d,))
},
ObjectBytes::Vec(v) => v.into_js(ctx),
}
}
}

impl<'js> From<ObjectBytes<'js>> for Vec<u8> {
fn from(value: ObjectBytes<'js>) -> Self {
value.into_bytes()
Expand Down
26 changes: 15 additions & 11 deletions llrt_core/src/utils/clone.rs → libs/llrt_utils/src/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ use std::collections::HashSet;

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use llrt_utils::{
object::ObjectExt,
primordials::{BasePrimordials, Primordial},
};
use rquickjs::{
atom::PredefinedAtom,
function::This,
function::{Constructor, Opt},
Array, Ctx, Function, IntoJs, Null, Object, Result, Type, Value,
function::{Constructor, Opt, This},
Array, ArrayBuffer, Ctx, Function, IntoJs, Null, Object, Result, Type, Value,
};

use super::hash;
use super::{
hash,
object::ObjectExt,
primordials::{BasePrimordials, Primordial},
};

#[derive(Debug)]
enum StackItem<'js> {
Expand Down Expand Up @@ -343,11 +342,17 @@ fn append_transfer_value<'js>(
object_key: Option<String>,
array_index: Option<usize>,
) -> Result<()> {
let value = if let Some(ab) = ArrayBuffer::from_value(value.clone()) {
ab.get::<_, Function>("transfer")?.call((This(ab),))?
} else {
value.clone()
};

tape.push(TapeItem {
parent,
object_key,
array_index,
value: TapeValue::Value(value.clone()),
value: TapeValue::Value(value),
});
Ok(())
}
Expand Down Expand Up @@ -404,12 +409,11 @@ mod tests {
use llrt_test::test_sync_with;
use rquickjs::{function::Opt, Object, Value};

use crate::utils::clone::structured_clone;
use super::structured_clone;

#[tokio::test]
async fn clone() {
test_sync_with(|ctx| {
crate::modules::buffer::init(&ctx)?;
let value: Object = ctx.eval(
r#"
const a = {
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions libs/llrt_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
pub mod bytearray_buffer;
pub mod bytes;
pub mod class;
pub mod clone;
pub mod error;
pub mod error_messages;
#[cfg(feature = "fs")]
pub mod fs;
pub mod hash;
pub mod macros;
pub mod mc_oneshot;
pub mod module;
pub mod object;
pub mod option;
pub mod primordials;
pub mod result;
pub mod reuse_list;
Expand Down
77 changes: 77 additions & 0 deletions libs/llrt_utils/src/option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use rquickjs::{
class::{Trace, Tracer},
Ctx, FromJs, IntoJs, JsLifetime, Result, Type, Value,
};

/// Helper type for treating an undefined value as None, without treating null as None
#[derive(Clone)]
pub struct Undefined<T>(pub Option<T>);

impl<'js, T: FromJs<'js>> FromJs<'js> for Undefined<T> {
fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
if value.type_of() == Type::Undefined {
Ok(Self(None))
} else {
Ok(Self(Some(FromJs::from_js(ctx, value)?)))
}
}
}

impl<'js, T: IntoJs<'js>> IntoJs<'js> for Undefined<T> {
fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
match self.0 {
None => Ok(Value::new_undefined(ctx.clone())),
Some(val) => val.into_js(ctx),
}
}
}

impl<T> Default for Undefined<T> {
fn default() -> Self {
Self(None)
}
}

unsafe impl<'js, T: JsLifetime<'js>> JsLifetime<'js> for Undefined<T> {
type Changed<'to> = Undefined<T::Changed<'to>>;
}

impl<'js, T: Trace<'js>> Trace<'js> for Undefined<T> {
fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
self.0.trace(tracer)
}
}

/// Helper type for converting an None into null instead of undefined.
/// Needed while rquickjs::function::Null has no IntoJs implementation
#[derive(Clone)]
pub struct Null<T>(pub Option<T>);

impl<'js, T: FromJs<'js>> FromJs<'js> for Null<T> {
fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
if value.type_of() == Type::Null {
Ok(Self(None))
} else {
Ok(Self(Some(FromJs::from_js(ctx, value)?)))
}
}
}

impl<'js, T: IntoJs<'js>> IntoJs<'js> for Null<T> {
fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
match self.0 {
None => Ok(Value::new_null(ctx.clone())),
Some(val) => val.into_js(ctx),
}
}
}

unsafe impl<'js, T: JsLifetime<'js>> JsLifetime<'js> for Null<T> {
type Changed<'to> = Null<T::Changed<'to>>;
}

impl<'js, T: Trace<'js>> Trace<'js> for Null<T> {
fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
self.0.trace(tracer)
}
}
36 changes: 28 additions & 8 deletions libs/llrt_utils/src/primordials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@ pub struct BasePrimordials<'js> {
pub constructor_date: Constructor<'js>,
pub constructor_error: Constructor<'js>,
pub constructor_type_error: Constructor<'js>,
pub constructor_range_error: Constructor<'js>,
pub constructor_regexp: Constructor<'js>,
pub constructor_uint8array: Constructor<'js>,
pub constructor_array_buffer: Constructor<'js>,
pub constructor_proxy: Constructor<'js>,
pub constructor_object: Constructor<'js>,
pub constructor_bool: Constructor<'js>,
pub constructor_number: Constructor<'js>,
pub constructor_string: Constructor<'js>,

// Prototypes
pub prototype_object: Object<'js>,
Expand All @@ -28,7 +35,6 @@ pub struct BasePrimordials<'js> {
pub function_array_from: Function<'js>,
pub function_array_buffer_is_view: Function<'js>,
pub function_get_own_property_descriptor: Function<'js>,
pub function_number: Function<'js>,
pub function_parse_int: Function<'js>,
pub function_parse_float: Function<'js>,
pub function_symbol_for: Function<'js>,
Expand Down Expand Up @@ -61,7 +67,7 @@ impl<'js> Primordial<'js> for BasePrimordials<'js> {
fn new(ctx: &Ctx<'js>) -> Result<Self> {
let globals = ctx.globals();

let constructor_object: Object = globals.get(PredefinedAtom::Object)?;
let constructor_object: Constructor = globals.get(PredefinedAtom::Object)?;
let prototype_object: Object = constructor_object.get(PredefinedAtom::Prototype)?;

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

let constructor_uint8array: Constructor = globals.get(PredefinedAtom::Uint8Array)?;
let constructor_arraybuffer: Constructor = globals.get(PredefinedAtom::ArrayBuffer)?;

let constructor_error: Constructor = globals.get(PredefinedAtom::Error)?;
let constructor_type_error: Constructor = ctx.globals().get(PredefinedAtom::TypeError)?;
let constructor_range_error: Constructor = ctx.globals().get(PredefinedAtom::RangeError)?;
let prototype_error: Object = constructor_error.get(PredefinedAtom::Prototype)?;

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

let function_number: Function = globals.get(PredefinedAtom::Number)?;
let function_parse_float: Function = function_number.get("parseFloat")?;
let function_parse_int: Function = function_number.get("parseInt")?;
let constructor_bool: Constructor = globals.get(PredefinedAtom::Boolean)?;

let constructor_number: Constructor = globals.get(PredefinedAtom::Number)?;
let function_parse_float: Function = constructor_number.get("parseFloat")?;
let function_parse_int: Function = constructor_number.get("parseInt")?;

let constructor_string: Constructor = globals.get(PredefinedAtom::String)?;

let symbol_constructor: Function = globals.get(PredefinedAtom::Symbol)?;
let function_symbol_for: Function = symbol_constructor.get(PredefinedAtom::For)?;
let constructor_symbol: Constructor = globals.get(PredefinedAtom::Symbol)?;
let function_symbol_for: Function = constructor_symbol.get(PredefinedAtom::For)?;

let symbol_custom_inspect: Symbol<'js> =
function_symbol_for.call((CUSTOM_INSPECT_SYMBOL_DESCRIPTION,))?;
Expand All @@ -108,7 +122,14 @@ impl<'js> Primordial<'js> for BasePrimordials<'js> {
constructor_proxy,
constructor_error,
constructor_type_error,
constructor_range_error,
constructor_regexp,
constructor_uint8array,
constructor_array_buffer: constructor_arraybuffer,
constructor_object,
constructor_bool,
constructor_number,
constructor_string,
prototype_object,
prototype_date,
prototype_regexp,
Expand All @@ -121,7 +142,6 @@ impl<'js> Primordial<'js> for BasePrimordials<'js> {
function_parse_float,
function_parse_int,
function_symbol_for,
function_number,
symbol_custom_inspect,
})
}
Expand Down
3 changes: 3 additions & 0 deletions llrt_core/src/module_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::modules::{
path::PathModule,
perf_hooks::PerfHooksModule,
process::ProcessModule,
stream_web::StreamWebModule,
tty::TtyModule,
url::UrlModule,
util::UtilModule,
Expand Down Expand Up @@ -95,6 +96,8 @@ impl Default for ModuleBuilder {
.with_global(crate::modules::navigator::init)
.with_global(crate::modules::url::init)
.with_module(UrlModule)
.with_module(StreamWebModule)
.with_global(crate::modules::stream_web::init)
.with_global(crate::modules::http::init)
.with_global(crate::modules::exceptions::init)
.with_module(LlrtHexModule)
Expand Down
Loading