From 85abd9c4bff9f1f1fd18d5718b793370a2f870c9 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 9 May 2024 11:23:02 -0700 Subject: [PATCH 1/5] Adding TryFromJs implementations for tuples --- .../src/value/conversions/try_from_js.rs | 74 ++++++++++++++++++- .../value/conversions/try_from_js/tuples.rs | 55 ++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 core/engine/src/value/conversions/try_from_js/tuples.rs diff --git a/core/engine/src/value/conversions/try_from_js.rs b/core/engine/src/value/conversions/try_from_js.rs index a4b5a465606..021669d9594 100644 --- a/core/engine/src/value/conversions/try_from_js.rs +++ b/core/engine/src/value/conversions/try_from_js.rs @@ -2,7 +2,9 @@ use num_bigint::BigInt; -use crate::{js_string, Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue}; +use crate::{Context, js_string, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue}; + +mod tuples; /// This trait adds a fallible and efficient conversions from a [`JsValue`] to Rust types. pub trait TryFromJs: Sized { @@ -364,3 +366,73 @@ fn value_into_vec() { ), ]); } + +#[test] +fn value_into_tuple() { + use boa_engine::{run_test_actions, TestAction}; + use indoc::indoc; + + run_test_actions([ + TestAction::assert_with_op(indoc! {r#" [] "#}, |value, context| { + type TestType = (); + TestType::try_from_js(&value, context).unwrap() == () + }), + TestAction::assert_with_op(indoc! {r#" 1 "#}, |value, context| { + type TestType = (); + TestType::try_from_js(&value, context).unwrap() == () + }), + TestAction::assert_with_op(indoc! {r#" [1, 2, 3] "#}, |value, context| { + type TestType = (); + TestType::try_from_js(&value, context).unwrap() == () + }), + TestAction::assert_with_op(indoc! {r#" [42, "hello", true] "#}, |value, context| { + type TestType = (i32, String, bool); + TestType::try_from_js(&value, context).unwrap() == (42, "hello".to_string(), true) + }), + TestAction::assert_with_op(indoc! {r#" [42, "hello", true] "#}, |value, context| { + type TestType = (i32, String, Option, Option); + TestType::try_from_js(&value, context).unwrap() + == (42, "hello".to_string(), Some(true), None) + }), + TestAction::assert_with_op(indoc! {r#" [] "#}, |value, context| { + type TestType = ( + Option, + Option, + Option, + Option, + Option, + Option, + Option, + Option, + Option, + Option, + ); + TestType::try_from_js(&value, context).unwrap() + == (None, None, None, None, None, None, None, None, None, None) + }), + TestAction::assert_with_op(indoc!(r#"[42, "hello", {}]"#), |value, context| { + type TestType = (i32, String, bool); + let Err(value) = TestType::try_from_js(&value, context) else { + return false; + }; + assert!(value.to_string().contains("TypeError")); + true + }), + TestAction::assert_with_op(indoc!(r#"[42, "hello"]"#), |value, context| { + type TestType = (i32, String, bool); + let Err(value) = TestType::try_from_js(&value, context) else { + return false; + }; + assert!(value.to_string().contains("TypeError")); + true + }), + TestAction::assert_with_op(indoc!(r#" null "#), |value, context| { + type TestType = (); + let Err(value) = TestType::try_from_js(&value, context) else { + return false; + }; + assert!(value.to_string().contains("TypeError")); + true + }), + ]); +} diff --git a/core/engine/src/value/conversions/try_from_js/tuples.rs b/core/engine/src/value/conversions/try_from_js/tuples.rs new file mode 100644 index 00000000000..0dac07d10ee --- /dev/null +++ b/core/engine/src/value/conversions/try_from_js/tuples.rs @@ -0,0 +1,55 @@ +//! Implementation of [`TryFromJs`] for tuples. +//! +//! Tuples are converted from a JavaScript array, using similar semantics to TypeScript tuples: +//! - If the tuple is shorter than the array, the extra elements are ignored. +//! - If the tuple is longer than the array, the extra elements are `undefined`. +//! - If the array is empty, all elements are `undefined`. +//! +//! A tuple of size 0 (unit type) is represented as any value except `null` or `undefined`. + +use crate::{Context, JsError, JsNativeError, JsResult}; +use crate::value::JsValue; + +use super::TryFromJs; + +impl TryFromJs for () { + fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { + if value.is_null_or_undefined() { + Err(JsError::from_native( + JsNativeError::typ() + .with_message("Cannot convert null or undefined to unit type") + .into(), + )) + } else { + Ok(()) + } + } +} + +macro_rules! impl_try_from_js_for_tuples { + ($($name:ident),*) => { + impl<$($name: TryFromJs),*> TryFromJs for ($($name,)*) { + fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult { + let vec: Vec = value.try_js_into(context)?; + let mut iter = vec.into_iter(); + + Ok(( + $( + $name::try_from_js(&iter.next().unwrap_or_else(JsValue::undefined), context)?, + )* + )) + } + } + }; +} + +impl_try_from_js_for_tuples!(A); +impl_try_from_js_for_tuples!(A, B); +impl_try_from_js_for_tuples!(A, B, C); +impl_try_from_js_for_tuples!(A, B, C, D); +impl_try_from_js_for_tuples!(A, B, C, D, E); +impl_try_from_js_for_tuples!(A, B, C, D, E, F); +impl_try_from_js_for_tuples!(A, B, C, D, E, F, G); +impl_try_from_js_for_tuples!(A, B, C, D, E, F, G, H); +impl_try_from_js_for_tuples!(A, B, C, D, E, F, G, H, I); +impl_try_from_js_for_tuples!(A, B, C, D, E, F, G, H, I, J); From 71217f44f4b7dddb792b482fd230ace0e2aee1d0 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 9 May 2024 11:38:53 -0700 Subject: [PATCH 2/5] rustfmt and clippies --- core/engine/src/value/conversions/try_from_js.rs | 9 ++++++--- .../engine/src/value/conversions/try_from_js/tuples.rs | 10 ++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/engine/src/value/conversions/try_from_js.rs b/core/engine/src/value/conversions/try_from_js.rs index 021669d9594..07ae30e5173 100644 --- a/core/engine/src/value/conversions/try_from_js.rs +++ b/core/engine/src/value/conversions/try_from_js.rs @@ -375,15 +375,18 @@ fn value_into_tuple() { run_test_actions([ TestAction::assert_with_op(indoc! {r#" [] "#}, |value, context| { type TestType = (); - TestType::try_from_js(&value, context).unwrap() == () + TestType::try_from_js(&value, context).unwrap(); + true }), TestAction::assert_with_op(indoc! {r#" 1 "#}, |value, context| { type TestType = (); - TestType::try_from_js(&value, context).unwrap() == () + TestType::try_from_js(&value, context).unwrap(); + true }), TestAction::assert_with_op(indoc! {r#" [1, 2, 3] "#}, |value, context| { type TestType = (); - TestType::try_from_js(&value, context).unwrap() == () + TestType::try_from_js(&value, context).unwrap(); + true }), TestAction::assert_with_op(indoc! {r#" [42, "hello", true] "#}, |value, context| { type TestType = (i32, String, bool); diff --git a/core/engine/src/value/conversions/try_from_js/tuples.rs b/core/engine/src/value/conversions/try_from_js/tuples.rs index 0dac07d10ee..9fd77e28ec7 100644 --- a/core/engine/src/value/conversions/try_from_js/tuples.rs +++ b/core/engine/src/value/conversions/try_from_js/tuples.rs @@ -1,6 +1,6 @@ //! Implementation of [`TryFromJs`] for tuples. //! -//! Tuples are converted from a JavaScript array, using similar semantics to TypeScript tuples: +//! Tuples are converted from a JavaScript array, using similar semantics to `TypeScript` tuples: //! - If the tuple is shorter than the array, the extra elements are ignored. //! - If the tuple is longer than the array, the extra elements are `undefined`. //! - If the array is empty, all elements are `undefined`. @@ -15,11 +15,9 @@ use super::TryFromJs; impl TryFromJs for () { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { if value.is_null_or_undefined() { - Err(JsError::from_native( - JsNativeError::typ() - .with_message("Cannot convert null or undefined to unit type") - .into(), - )) + Err(JsError::from_native(JsNativeError::typ().with_message( + "Cannot convert null or undefined to unit type", + ))) } else { Ok(()) } From 5508ec6cbc59ee71b9a719fb1cbea5c217a5abe9 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 9 May 2024 12:08:53 -0700 Subject: [PATCH 3/5] rustfmt --- core/engine/src/value/conversions/try_from_js.rs | 2 +- core/engine/src/value/conversions/try_from_js/tuples.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/engine/src/value/conversions/try_from_js.rs b/core/engine/src/value/conversions/try_from_js.rs index 07ae30e5173..8adeaad790e 100644 --- a/core/engine/src/value/conversions/try_from_js.rs +++ b/core/engine/src/value/conversions/try_from_js.rs @@ -2,7 +2,7 @@ use num_bigint::BigInt; -use crate::{Context, js_string, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue}; +use crate::{js_string, Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue}; mod tuples; diff --git a/core/engine/src/value/conversions/try_from_js/tuples.rs b/core/engine/src/value/conversions/try_from_js/tuples.rs index 9fd77e28ec7..835e9903695 100644 --- a/core/engine/src/value/conversions/try_from_js/tuples.rs +++ b/core/engine/src/value/conversions/try_from_js/tuples.rs @@ -7,8 +7,8 @@ //! //! A tuple of size 0 (unit type) is represented as any value except `null` or `undefined`. -use crate::{Context, JsError, JsNativeError, JsResult}; use crate::value::JsValue; +use crate::{Context, JsError, JsNativeError, JsResult}; use super::TryFromJs; From 9cc3325653bb591550930d7058219eb85606a4fe Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 17 Jun 2024 10:30:03 -0700 Subject: [PATCH 4/5] Remove unit type --- .../src/value/conversions/try_from_js.rs | 23 ------------------- .../value/conversions/try_from_js/tuples.rs | 14 +---------- 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/core/engine/src/value/conversions/try_from_js.rs b/core/engine/src/value/conversions/try_from_js.rs index 8adeaad790e..046b84e02f4 100644 --- a/core/engine/src/value/conversions/try_from_js.rs +++ b/core/engine/src/value/conversions/try_from_js.rs @@ -373,21 +373,6 @@ fn value_into_tuple() { use indoc::indoc; run_test_actions([ - TestAction::assert_with_op(indoc! {r#" [] "#}, |value, context| { - type TestType = (); - TestType::try_from_js(&value, context).unwrap(); - true - }), - TestAction::assert_with_op(indoc! {r#" 1 "#}, |value, context| { - type TestType = (); - TestType::try_from_js(&value, context).unwrap(); - true - }), - TestAction::assert_with_op(indoc! {r#" [1, 2, 3] "#}, |value, context| { - type TestType = (); - TestType::try_from_js(&value, context).unwrap(); - true - }), TestAction::assert_with_op(indoc! {r#" [42, "hello", true] "#}, |value, context| { type TestType = (i32, String, bool); TestType::try_from_js(&value, context).unwrap() == (42, "hello".to_string(), true) @@ -429,13 +414,5 @@ fn value_into_tuple() { assert!(value.to_string().contains("TypeError")); true }), - TestAction::assert_with_op(indoc!(r#" null "#), |value, context| { - type TestType = (); - let Err(value) = TestType::try_from_js(&value, context) else { - return false; - }; - assert!(value.to_string().contains("TypeError")); - true - }), ]); } diff --git a/core/engine/src/value/conversions/try_from_js/tuples.rs b/core/engine/src/value/conversions/try_from_js/tuples.rs index 835e9903695..dd6a5bced0e 100644 --- a/core/engine/src/value/conversions/try_from_js/tuples.rs +++ b/core/engine/src/value/conversions/try_from_js/tuples.rs @@ -5,25 +5,13 @@ //! - If the tuple is longer than the array, the extra elements are `undefined`. //! - If the array is empty, all elements are `undefined`. //! -//! A tuple of size 0 (unit type) is represented as any value except `null` or `undefined`. +//! A tuple of size 0 (unit type) does not implement [`TryFromJs`]. use crate::value::JsValue; use crate::{Context, JsError, JsNativeError, JsResult}; use super::TryFromJs; -impl TryFromJs for () { - fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { - if value.is_null_or_undefined() { - Err(JsError::from_native(JsNativeError::typ().with_message( - "Cannot convert null or undefined to unit type", - ))) - } else { - Ok(()) - } - } -} - macro_rules! impl_try_from_js_for_tuples { ($($name:ident),*) => { impl<$($name: TryFromJs),*> TryFromJs for ($($name,)*) { From 0fbbf523ae2745ca72600b200bd27d896afb407a Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 17 Jun 2024 10:31:18 -0700 Subject: [PATCH 5/5] Clippies --- core/engine/src/value/conversions/try_from_js/tuples.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/engine/src/value/conversions/try_from_js/tuples.rs b/core/engine/src/value/conversions/try_from_js/tuples.rs index dd6a5bced0e..babb54a36c6 100644 --- a/core/engine/src/value/conversions/try_from_js/tuples.rs +++ b/core/engine/src/value/conversions/try_from_js/tuples.rs @@ -8,7 +8,7 @@ //! A tuple of size 0 (unit type) does not implement [`TryFromJs`]. use crate::value::JsValue; -use crate::{Context, JsError, JsNativeError, JsResult}; +use crate::{Context, JsResult}; use super::TryFromJs;