From 2aef14d34d22df41bd6f421633eadc50826217cc Mon Sep 17 00:00:00 2001 From: Levi Date: Fri, 12 Jul 2024 15:15:12 +1200 Subject: [PATCH] feat(es/utils): Support for arrays using `cast_to_number` (#9212) **Description:** This PR allows `ArrayLit`s to be converted to numbers in the `cast_to_number` function. This allows expressions using arrays to be converted to numbers. See some example expressions below that were previously not able to be computed, but are now able to due to this change. ```js +[] // 0 +[[]] // 0 +[1] // 1 +[undefined] // 0 +[null] // 0 +[[1]] // 1 +[,] // 0 +[,,] // NaN ``` Regarding the implementation, arrays are converted to strings, and the string is then parsed as a number. So arrays like `[]` and `[undefined]` return `""` which then return `0` when parsed as a string. This is also why arrays with more than one element can't be parsed because e.g. `[1, 2]` returns `"1,2"`. This procedure follows the ECMAScript specification. https://262.ecma-international.org/6.0/#sec-tonumber https://262.ecma-international.org/6.0/#sec-toprimitive --- .../src/simplify/expr/tests.rs | 21 +++++++++++++++++++ crates/swc_ecma_utils/src/lib.rs | 8 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/swc_ecma_transforms_optimization/src/simplify/expr/tests.rs b/crates/swc_ecma_transforms_optimization/src/simplify/expr/tests.rs index b70c0743a1e9..ba3ad6f30150 100644 --- a/crates/swc_ecma_transforms_optimization/src/simplify/expr/tests.rs +++ b/crates/swc_ecma_transforms_optimization/src/simplify/expr/tests.rs @@ -488,6 +488,27 @@ fn test_unary_ops_4() { fold("a=~~0xffffffff", "a=-1"); } +#[test] +fn test_unary_ops_5() { + // Empty arrays + fold("+[]", "0"); + fold("+[[]]", "0"); + fold("+[[[]]]", "0"); + + // Arrays with one element + fold("+[1]", "1"); + fold("+[[1]]", "1"); + fold("+[undefined]", "0"); + fold("+[null]", "0"); + fold("+[,]", "0"); + + // Arrays with more than one element + fold("+[1, 2]", "NaN"); + fold("+[[1], 2]", "NaN"); + fold("+[,1]", "NaN"); + fold("+[,,]", "NaN"); +} + #[test] fn test_unary_ops_string_compare() { fold_same("a = -1"); diff --git a/crates/swc_ecma_utils/src/lib.rs b/crates/swc_ecma_utils/src/lib.rs index 4b89225f9920..6db978c98cf0 100644 --- a/crates/swc_ecma_utils/src/lib.rs +++ b/crates/swc_ecma_utils/src/lib.rs @@ -890,6 +890,13 @@ pub trait ExprExt { Lit::Str(Str { value, .. }) => return (Pure, num_from_str(value)), _ => return (Pure, Unknown), }, + Expr::Array(..) => { + let Known(s) = self.as_pure_string(ctx) else { + return (Pure, Unknown); + }; + + return (Pure, num_from_str(&s)); + } Expr::Ident(Ident { sym, span, .. }) => match &**sym { "undefined" | "NaN" if span.ctxt == ctx.unresolved_ctxt => f64::NAN, "Infinity" if span.ctxt == ctx.unresolved_ctxt => f64::INFINITY, @@ -1572,7 +1579,6 @@ pub fn num_from_str(s: &str) -> Value { return Unknown; } - // TODO: Check if this is correct let s = s.trim(); if s.is_empty() {