diff --git a/core/engine/src/builtins/array_buffer/tests.rs b/core/engine/src/builtins/array_buffer/tests.rs index 604bf6140fe..f1af0936fc8 100644 --- a/core/engine/src/builtins/array_buffer/tests.rs +++ b/core/engine/src/builtins/array_buffer/tests.rs @@ -1,4 +1,4 @@ -use crate::Context; +use crate::{run_test_actions, Context, TestAction}; #[test] fn create_byte_data_block() { @@ -19,3 +19,122 @@ fn create_shared_byte_data_block() { // Rainy day assert!(super::shared::create_shared_byte_data_block(u64::MAX, context).is_err()); } + +#[test] +fn get_values() { + run_test_actions([ + TestAction::run( + r#" + var buffer = new ArrayBuffer(12); + var sample = new DataView(buffer, 0); + + sample.setUint8(0, 127); + sample.setUint8(1, 255); + sample.setUint8(2, 255); + sample.setUint8(3, 255); + sample.setUint8(4, 128); + sample.setUint8(5, 0); + sample.setUint8(6, 0); + sample.setUint8(7, 0); + sample.setUint8(8, 1); + sample.setUint8(9, 0); + sample.setUint8(10, 0); + sample.setUint8(11, 0); + "#, + ), + TestAction::assert("sample.getUint32(0, false) == 2147483647"), + TestAction::assert("sample.getUint32(1, false) == 4294967168"), + TestAction::assert("sample.getUint32(2, false) == 4294934528"), + TestAction::assert("sample.getUint32(3, false) == 4286578688"), + TestAction::assert("sample.getUint32(4, false) == 2147483648"), + TestAction::assert("sample.getUint32(5, false) == 1"), + TestAction::assert("sample.getUint32(6, false) == 256"), + TestAction::assert("sample.getUint32(7, false) == 65536"), + TestAction::assert("sample.getUint32(8, false) == 16777216"), + TestAction::assert("sample.getUint32(0, true) == 4294967167"), + TestAction::assert("sample.getUint32(1, true) == 2164260863"), + TestAction::assert("sample.getUint32(2, true) == 8454143"), + TestAction::assert("sample.getUint32(3, true) == 33023"), + TestAction::assert("sample.getUint32(4, true) == 128"), + TestAction::assert("sample.getUint32(5, true) == 16777216"), + TestAction::assert("sample.getUint32(6, true) == 65536"), + TestAction::assert("sample.getUint32(7, true) == 256"), + TestAction::assert("sample.getUint32(8, true) == 1"), + ]); +} + +#[test] +fn sort() { + run_test_actions([ + TestAction::run( + r#" + function cmp(a, b) { + return a.length === b.length && a.every((v, i) => v === b[i]); + } + + var TypedArrayCtor = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + ]; + + var descending = TypedArrayCtor.map((ctor) => new ctor([4, 3, 2, 1]).sort()); + var mixed = TypedArrayCtor.map((ctor) => new ctor([3, 4, 1, 2]).sort()); + var repeating = TypedArrayCtor.map((ctor) => new ctor([0, 1, 1, 2, 3, 3, 4]).sort()); + "#, + ), + // Descending + TestAction::assert("cmp(descending[0], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[1], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[2], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[3], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[4], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[5], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[6], [1, 2, 3, 4])"), + TestAction::assert("cmp(descending[7], [1, 2, 3, 4])"), + // Mixed + TestAction::assert("cmp(mixed[0], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[1], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[2], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[3], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[4], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[5], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[6], [1, 2, 3, 4])"), + TestAction::assert("cmp(mixed[7], [1, 2, 3, 4])"), + // Repeating + TestAction::assert("cmp(repeating[0], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[1], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[2], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[3], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[4], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[5], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[6], [0, 1, 1, 2, 3, 3, 4])"), + TestAction::assert("cmp(repeating[7], [0, 1, 1, 2, 3, 3, 4])"), + ]); +} + +#[test] +fn sort_negative_zero() { + run_test_actions([ + TestAction::run( + r#" + function cmp(a, b) { + return a.length === b.length && a.every((v, i) => v === b[i]); + } + + var TypedArrayCtor = [Float32Array, Float64Array]; + var negativeZero = TypedArrayCtor.map((ctor) => new ctor([1, 0, -0, 2]).sort()); + var infinities = TypedArrayCtor.map((ctor) => new ctor([3, 4, Infinity, -Infinity, 1, 2]).sort()); + "#, + ), + TestAction::assert("cmp(negativeZero[0], [-0, 0, 1, 2])"), + TestAction::assert("cmp(negativeZero[1], [-0, 0, 1, 2])"), + TestAction::assert("cmp(infinities[0], [-Infinity, 1, 2, 3, 4, Infinity])"), + TestAction::assert("cmp(infinities[1], [-Infinity, 1, 2, 3, 4, Infinity])"), + ]); +} diff --git a/core/engine/src/builtins/temporal/duration/tests.rs b/core/engine/src/builtins/temporal/duration/tests.rs index 4deadf08195..fb050899ee1 100644 --- a/core/engine/src/builtins/temporal/duration/tests.rs +++ b/core/engine/src/builtins/temporal/duration/tests.rs @@ -25,3 +25,48 @@ fn duration_abs() { TestAction::assert_eq("abs.milliseconds", 0), ]); } + +#[test] +fn basic() { + run_test_actions([ + TestAction::run( + r#" + var dur = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 0); + "#, + ), + TestAction::assert_eq("dur.years", 5), + TestAction::assert_eq("dur.months", 5), + TestAction::assert_eq("dur.weeks", 5), + TestAction::assert_eq("dur.days", 5), + TestAction::assert_eq("dur.hours", 5), + TestAction::assert_eq("dur.minutes", 5), + TestAction::assert_eq("dur.seconds", 5), + TestAction::assert_eq("dur.milliseconds", 5), + TestAction::assert_eq("dur.microseconds", 5), + TestAction::assert_eq("dur.nanoseconds", 0), + // Negative + TestAction::run("dur = new Temporal.Duration(-5, -5, -5, -5, -5, -5, -5, -5, -5, 0)"), + TestAction::assert_eq("dur.years", -5), + TestAction::assert_eq("dur.months", -5), + TestAction::assert_eq("dur.weeks", -5), + TestAction::assert_eq("dur.days", -5), + TestAction::assert_eq("dur.hours", -5), + TestAction::assert_eq("dur.minutes", -5), + TestAction::assert_eq("dur.seconds", -5), + TestAction::assert_eq("dur.milliseconds", -5), + TestAction::assert_eq("dur.microseconds", -5), + TestAction::assert_eq("dur.nanoseconds", 0), + // Negative Zero + TestAction::run("dur = new Temporal.Duration(-0, -0, -0, -0, -0, -0, -0, -0, -0, 0)"), + TestAction::assert_eq("dur.years", 0), + TestAction::assert_eq("dur.months", 0), + TestAction::assert_eq("dur.weeks", 0), + TestAction::assert_eq("dur.days", 0), + TestAction::assert_eq("dur.hours", 0), + TestAction::assert_eq("dur.minutes", 0), + TestAction::assert_eq("dur.seconds", 0), + TestAction::assert_eq("dur.milliseconds", 0), + TestAction::assert_eq("dur.microseconds", 0), + TestAction::assert_eq("dur.nanoseconds", 0), + ]); +} diff --git a/core/engine/src/value/conversions/mod.rs b/core/engine/src/value/conversions/mod.rs index 7be6fbcc62f..daee6456064 100644 --- a/core/engine/src/value/conversions/mod.rs +++ b/core/engine/src/value/conversions/mod.rs @@ -49,25 +49,22 @@ impl From for JsValue { } } -macro_rules! impl_from_float { - ( $( $type_:ty ),* ) => { - $( - impl From<$type_> for JsValue { - #[inline] - #[allow(trivial_numeric_casts)] - #[allow(clippy::cast_lossless)] - fn from(value: $type_) -> Self { - let _timer = Profiler::global().start_event(concat!("From<", stringify!($type_), ">"), "value"); +impl From for JsValue { + #[inline] + fn from(value: f32) -> Self { + let _timer = Profiler::global().start_event("From", "value"); - if value != -0.0 && value.fract() == 0.0 && value <= i32::MAX as $type_ && value >= i32::MIN as $type_ { - Self::from_inner(InnerValue::Integer32(value as i32)) - } else { - Self::from_inner(InnerValue::Float64(f64::from(value))) - } - } - } - )* - }; + JsValue::from(f64::from(value)) + } +} + +impl From for JsValue { + #[inline] + fn from(value: f64) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + + Self::from_inner(InnerValue::Float64(value)) + } } macro_rules! impl_from_integer { @@ -90,7 +87,6 @@ macro_rules! impl_from_integer { }; } -impl_from_float!(f32, f64); impl_from_integer!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize); impl From for JsValue { diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 14d1a890963..0d55e3fc781 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -349,11 +349,16 @@ impl JsValue { /// [spec]: https://tc39.es/ecma262/#sec-isintegralnumber #[inline] #[must_use] + #[allow(clippy::float_cmp)] pub const fn as_i32(&self) -> Option { - if let InnerValue::Integer32(i) = self.inner { - Some(i) - } else { - None + match self.inner { + InnerValue::Integer32(integer) => Some(integer), + // If it can fit in a i32 and the truncated version is + // equal to the original then it is an integer. + InnerValue::Float64(rational) if rational == ((rational as i32) as f64) => { + Some(rational as i32) + } + _ => None, } } @@ -578,8 +583,8 @@ impl JsValue { InnerValue::Undefined => Ok(js_string!("undefined")), InnerValue::Boolean(true) => Ok(js_string!("true")), InnerValue::Boolean(false) => Ok(js_string!("false")), - InnerValue::Float64(rational) => Ok(Number::to_js_string_radix(rational, 10)), - InnerValue::Integer32(integer) => Ok(integer.to_string().into()), + InnerValue::Float64(rational) => Ok(JsString::from(rational)), + InnerValue::Integer32(integer) => Ok(JsString::from(integer)), InnerValue::String(ref string) => Ok(string.clone()), InnerValue::Symbol(_) => Err(JsNativeError::typ() .with_message("can't convert symbol to string") diff --git a/core/engine/src/value/tests.rs b/core/engine/src/value/tests.rs index 7f00f68708b..6a898377717 100644 --- a/core/engine/src/value/tests.rs +++ b/core/engine/src/value/tests.rs @@ -708,6 +708,18 @@ fn to_bigint() { })]); } +#[test] +fn pad_end() { + run_test_actions([ + TestAction::assert_eq("'abc'.padEnd(10, false)", js_string!("abcfalsefa")), + TestAction::assert_eq("'abc'.padEnd(10, true)", js_string!("abctruetru")), + TestAction::assert_eq("'abc'.padEnd(10, null)", js_string!("abcnullnul")), + TestAction::assert_eq("'abc'.padEnd(10, 0)", js_string!("abc0000000")), + TestAction::assert_eq("'abc'.padEnd(10, -0)", js_string!("abc0000000")), + TestAction::assert_eq("'abc'.padEnd(10, NaN)", js_string!("abcNaNNaNN")), + ]); +} + /// Test cyclic conversions that previously caused stack overflows /// Relevant mitigation for these are in `JsObject::ordinary_to_primitive` and /// `JsObject::to_json`