From 5abc82793a218f8c96eae660ff11e58aa3898d39 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Mon, 15 Jul 2024 22:41:44 +0800 Subject: [PATCH 1/5] fix(es/minifier): Fix exponentiate operator --- .../src/compress/util/mod.rs | 15 +- .../src/simplify/expr/mod.rs | 9 +- .../src/transform.rs | 4 +- .../src/ts_enum.rs | 47 +++--- crates/swc_ecma_utils/src/number.rs | 155 ++++++++++++++++++ 5 files changed, 193 insertions(+), 37 deletions(-) diff --git a/crates/swc_ecma_minifier/src/compress/util/mod.rs b/crates/swc_ecma_minifier/src/compress/util/mod.rs index 5cc58bc72f5d..be656e3836a7 100644 --- a/crates/swc_ecma_minifier/src/compress/util/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/util/mod.rs @@ -4,7 +4,7 @@ use swc_common::{util::take::Take, DUMMY_SP}; use swc_ecma_ast::*; #[cfg(feature = "debug")] use swc_ecma_transforms_base::fixer::fixer; -use swc_ecma_utils::{ExprCtx, ExprExt, IdentUsageFinder, Value}; +use swc_ecma_utils::{number::JsNumber, ExprCtx, ExprExt, IdentUsageFinder, Value}; #[cfg(feature = "debug")] use swc_ecma_visit::{as_folder, FoldWith}; use swc_ecma_visit::{ @@ -512,16 +512,11 @@ pub(crate) fn eval_as_number(expr_ctx: &ExprCtx, e: &Expr) -> Option { if args.len() != 2 { return None; } - let base = eval_as_number(expr_ctx, &args[0].expr)?; - let exponent = eval_as_number(expr_ctx, &args[1].expr)?; + let base: JsNumber = eval_as_number(expr_ctx, &args[0].expr)?.into(); + let exponent: JsNumber = + eval_as_number(expr_ctx, &args[1].expr)?.into(); - // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-exponentiate - // https://github.com/rust-lang/rust/issues/60468 - if exponent.is_nan() { - return Some(f64::NAN); - } - - return Some(base.powf(exponent)); + return Some(base.pow(exponent).into()); } _ => {} diff --git a/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs b/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs index b20047df3828..f8d31f708331 100644 --- a/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs +++ b/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs @@ -13,8 +13,8 @@ use swc_ecma_transforms_base::{ perf::{cpu_count, Parallel, ParallelExt}, }; use swc_ecma_utils::{ - is_literal, prop_name_eq, to_int32, BoolType, ExprCtx, ExprExt, NullType, NumberType, - ObjectType, StringType, SymbolType, UndefinedType, Value, + is_literal, number::JsNumber, prop_name_eq, to_int32, BoolType, ExprCtx, ExprExt, NullType, + NumberType, ObjectType, StringType, SymbolType, UndefinedType, Value, }; use swc_ecma_visit::{as_folder, standard_only_visit_mut, VisitMut, VisitMutWith}; use Value::{Known, Unknown}; @@ -1024,7 +1024,10 @@ impl SimplifyExpr { } if let (Known(lv), Known(rv)) = (lv, rv) { - return try_replace!(lv.powf(rv)); + let lv: JsNumber = lv.into(); + let rv: JsNumber = rv.into(); + let result: f64 = lv.pow(rv).into(); + return try_replace!(result); } return Unknown; diff --git a/crates/swc_ecma_transforms_typescript/src/transform.rs b/crates/swc_ecma_transforms_typescript/src/transform.rs index 5a67f30a24cc..bfe4ce4de3e3 100644 --- a/crates/swc_ecma_transforms_typescript/src/transform.rs +++ b/crates/swc_ecma_transforms_typescript/src/transform.rs @@ -621,14 +621,14 @@ impl Transform { enum_id: &Id, default_init: &TsEnumRecordValue, record: &TsEnumRecord, - top_level_mark: Mark, + unresolved_mark: Mark, ) -> TsEnumRecordValue { member .init .map(|expr| { EnumValueComputer { enum_id, - top_level_mark, + unresolved_mark, record, } .compute(expr) diff --git a/crates/swc_ecma_transforms_typescript/src/ts_enum.rs b/crates/swc_ecma_transforms_typescript/src/ts_enum.rs index 1e26af7ec5c6..2026cf01c2d3 100644 --- a/crates/swc_ecma_transforms_typescript/src/ts_enum.rs +++ b/crates/swc_ecma_transforms_typescript/src/ts_enum.rs @@ -3,7 +3,10 @@ use std::mem; use swc_atoms::JsWord; use swc_common::{collections::AHashMap, Mark, DUMMY_SP}; use swc_ecma_ast::*; -use swc_ecma_utils::{number::ToJsString, ExprFactory}; +use swc_ecma_utils::{ + number::{JsNumber, ToJsString}, + ExprFactory, +}; use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -17,7 +20,7 @@ pub(crate) type TsEnumRecord = AHashMap; #[derive(Debug, Clone)] pub(crate) enum TsEnumRecordValue { String(JsWord), - Number(f64), + Number(JsNumber), Opaque(Box), Void, } @@ -25,7 +28,7 @@ pub(crate) enum TsEnumRecordValue { impl TsEnumRecordValue { pub fn inc(&self) -> Self { match self { - Self::Number(num) => Self::Number(num + 1.0), + Self::Number(num) => Self::Number((**num + 1.0).into()), _ => Self::Void, } } @@ -50,13 +53,13 @@ impl From for Expr { fn from(value: TsEnumRecordValue) -> Self { match value { TsEnumRecordValue::String(string) => Lit::Str(string.into()).into(), - TsEnumRecordValue::Number(num) if f64::is_nan(num) => Ident { + TsEnumRecordValue::Number(num) if num.is_nan() => Ident { span: DUMMY_SP, sym: "NaN".into(), ..Default::default() } .into(), - TsEnumRecordValue::Number(num) if f64::is_infinite(num) => { + TsEnumRecordValue::Number(num) if num.is_infinite() => { let value: Expr = Ident { span: DUMMY_SP, sym: "Infinity".into(), @@ -64,7 +67,7 @@ impl From for Expr { } .into(); - if f64::is_sign_negative(num) { + if num.is_sign_negative() { UnaryExpr { span: DUMMY_SP, op: op!(unary, "-"), @@ -77,7 +80,7 @@ impl From for Expr { } TsEnumRecordValue::Number(num) => Lit::Num(Number { span: DUMMY_SP, - value: num, + value: *num, raw: None, }) .into(), @@ -89,13 +92,13 @@ impl From for Expr { impl From for TsEnumRecordValue { fn from(value: f64) -> Self { - Self::Number(value) + Self::Number(value.into()) } } pub(crate) struct EnumValueComputer<'a> { pub enum_id: &'a Id, - pub top_level_mark: Mark, + pub unresolved_mark: Mark, pub record: &'a TsEnumRecord, } @@ -110,16 +113,16 @@ impl<'a> EnumValueComputer<'a> { fn compute_rec(&self, expr: Box) -> TsEnumRecordValue { match *expr { Expr::Lit(Lit::Str(s)) => TsEnumRecordValue::String(s.value), - Expr::Lit(Lit::Num(n)) => TsEnumRecordValue::Number(n.value), + Expr::Lit(Lit::Num(n)) => TsEnumRecordValue::Number(n.value.into()), Expr::Ident(Ident { ctxt, sym, .. }) - if &*sym == "NaN" && ctxt.has_mark(self.top_level_mark) => + if &*sym == "NaN" && ctxt.has_mark(self.unresolved_mark) => { - TsEnumRecordValue::Number(f64::NAN) + TsEnumRecordValue::Number(f64::NAN.into()) } Expr::Ident(Ident { ctxt, sym, .. }) - if &*sym == "Infinity" && ctxt.has_mark(self.top_level_mark) => + if &*sym == "Infinity" && ctxt.has_mark(self.unresolved_mark) => { - TsEnumRecordValue::Number(f64::INFINITY) + TsEnumRecordValue::Number(f64::INFINITY.into()) } Expr::Ident(ref ident) => self .record @@ -160,7 +163,7 @@ impl<'a> EnumValueComputer<'a> { match expr.op { op!(unary, "+") => TsEnumRecordValue::Number(num), op!(unary, "-") => TsEnumRecordValue::Number(-num), - op!("~") => TsEnumRecordValue::Number(!(num as i32) as f64), + op!("~") => TsEnumRecordValue::Number(!num), _ => unreachable!(), } } @@ -195,13 +198,13 @@ impl<'a> EnumValueComputer<'a> { op!("*") => left * right, op!("/") => left / right, op!("%") => left % right, - op!("**") => left.powf(right), - op!("<<") => (left.trunc() as i32).wrapping_shl(right.trunc() as u32) as f64, - op!(">>") => (left.trunc() as i32).wrapping_shr(right.trunc() as u32) as f64, - op!(">>>") => (left.trunc() as u32).wrapping_shr(right.trunc() as u32) as f64, - op!("|") => ((left.trunc() as i32) | (right.trunc() as i32)) as f64, - op!("&") => ((left.trunc() as i32) & (right.trunc() as i32)) as f64, - op!("^") => ((left.trunc() as i32) ^ (right.trunc() as i32)) as f64, + op!("**") => left.pow(right), + op!("<<") => left << right, + op!(">>") => left >> right, + op!(">>>") => left.unsigned_shr(right), + op!("|") => left | right, + op!("&") => left & right, + op!("^") => left ^ right, _ => unreachable!(), }; diff --git a/crates/swc_ecma_utils/src/number.rs b/crates/swc_ecma_utils/src/number.rs index 54d0ae892f38..fc725ed0a8b5 100644 --- a/crates/swc_ecma_utils/src/number.rs +++ b/crates/swc_ecma_utils/src/number.rs @@ -9,3 +9,158 @@ impl ToJsString for f64 { buffer.format(*self).to_string() } } + +#[derive(Debug, Clone, Copy)] +pub struct JsNumber(f64); + +impl From for JsNumber { + fn from(v: f64) -> Self { + JsNumber(v) + } +} + +impl From for f64 { + fn from(v: JsNumber) -> Self { + v.0 + } +} + +impl std::ops::Deref for JsNumber { + type Target = f64; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +// JsNumber + JsNumber +impl std::ops::Add for JsNumber { + type Output = JsNumber; + + fn add(self, rhs: JsNumber) -> Self::Output { + JsNumber(self.0 + rhs.0) + } +} + +// JsNumber - JsNumber +impl std::ops::Sub for JsNumber { + type Output = JsNumber; + + fn sub(self, rhs: JsNumber) -> Self::Output { + JsNumber(self.0 - rhs.0) + } +} + +// JsNumber * JsNumber +impl std::ops::Mul for JsNumber { + type Output = JsNumber; + + fn mul(self, rhs: JsNumber) -> Self::Output { + JsNumber(self.0 * rhs.0) + } +} + +// JsNumber / JsNumber +impl std::ops::Div for JsNumber { + type Output = JsNumber; + + fn div(self, rhs: JsNumber) -> Self::Output { + JsNumber(self.0 / rhs.0) + } +} + +// JsNumber % JsNumber +impl std::ops::Rem for JsNumber { + type Output = JsNumber; + + fn rem(self, rhs: JsNumber) -> Self::Output { + JsNumber(self.0 % rhs.0) + } +} + +// JsNumber ** JsNumber +impl JsNumber { + pub fn pow(self, rhs: JsNumber) -> JsNumber { + // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-exponentiate + // https://github.com/rust-lang/rust/issues/60468 + if rhs.0.is_nan() { + return JsNumber(f64::NAN); + } + + if f64::abs(self.0) == 1f64 && rhs.0.is_infinite() { + return JsNumber(f64::NAN); + } + + JsNumber(self.0.powf(rhs.0)) + } +} + +// JsNumber << JsNumber +impl std::ops::Shl for JsNumber { + type Output = JsNumber; + + fn shl(self, rhs: JsNumber) -> Self::Output { + JsNumber((self.0.trunc() as i32).wrapping_shl(rhs.0.trunc() as u32) as f64) + } +} + +// JsNumber >> JsNumber +impl std::ops::Shr for JsNumber { + type Output = JsNumber; + + fn shr(self, rhs: JsNumber) -> Self::Output { + JsNumber((self.0.trunc() as i32).wrapping_shr(rhs.0.trunc() as u32) as f64) + } +} + +// JsNumber >>> JsNumber +impl JsNumber { + pub fn unsigned_shr(self, rhs: JsNumber) -> JsNumber { + JsNumber((self.0 as u32).wrapping_shr(rhs.0.trunc() as u32) as f64) + } +} + +// JsNumber | JsNumber +impl std::ops::BitOr for JsNumber { + type Output = JsNumber; + + fn bitor(self, rhs: JsNumber) -> Self::Output { + JsNumber((self.0.trunc() as i32 | rhs.0.trunc() as i32) as f64) + } +} + +// JsNumber & JsNumber +impl std::ops::BitAnd for JsNumber { + type Output = JsNumber; + + fn bitand(self, rhs: JsNumber) -> Self::Output { + JsNumber((self.0.trunc() as i32 & rhs.0.trunc() as i32) as f64) + } +} + +// JsNumber ^ JsNumber +impl std::ops::BitXor for JsNumber { + type Output = JsNumber; + + fn bitxor(self, rhs: JsNumber) -> Self::Output { + JsNumber((self.0.trunc() as i32 ^ rhs.0.trunc() as i32) as f64) + } +} + +// - JsNumber +impl std::ops::Neg for JsNumber { + type Output = JsNumber; + + fn neg(self) -> Self::Output { + JsNumber(-self.0) + } +} + +// ~ JsNumber +impl std::ops::Not for JsNumber { + type Output = JsNumber; + + fn not(self) -> Self::Output { + JsNumber(!(self.0.trunc() as i32) as f64) + } +} From a52a9322b449d6ab08ac3002c074b9d9cf4d78ec Mon Sep 17 00:00:00 2001 From: magic-akari Date: Mon, 15 Jul 2024 22:42:02 +0800 Subject: [PATCH 2/5] chore: update test cases --- .../terser/compress/evaluate/pow_nan/input.js | 1 + .../compress/evaluate/pow_nan/output.js | 1 + .../evaluate/pow_nan/output.mangleOnly.js | 1 + .../evaluate/pow_spec/expected.stdout | 82 ++++++++++++++++++ .../compress/evaluate/pow_spec/input.js | 84 +++++++++++++++++++ .../compress/evaluate/pow_spec/output.js | 82 ++++++++++++++++++ .../evaluate/pow_spec/output.mangleOnly.js | 82 ++++++++++++++++++ 7 files changed, 333 insertions(+) diff --git a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/input.js b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/input.js index 3aaaee6dea32..8b119d63a82e 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/input.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/input.js @@ -1 +1,2 @@ console.log(Math.pow(1, NaN)); +console.log(1 ** NaN); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/output.js b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/output.js index 89e3dd4ea485..5da7fad836f1 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/output.js @@ -1 +1,2 @@ console.log(NaN); +console.log(NaN); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/output.mangleOnly.js b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/output.mangleOnly.js index 3aaaee6dea32..8b119d63a82e 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/output.mangleOnly.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_nan/output.mangleOnly.js @@ -1 +1,2 @@ console.log(Math.pow(1, NaN)); +console.log(1 ** NaN); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/expected.stdout b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/expected.stdout index e50f2f14c9f3..0758f75c8939 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/expected.stdout +++ b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/expected.stdout @@ -79,3 +79,85 @@ NaN Infinity -Infinity 1 +---- +NaN +NaN +NaN +NaN +1 +NaN +NaN +NaN +1 +NaN +Infinity +Infinity +Infinity +1 +Infinity +0 +0 +1 +NaN +Infinity +4 +2 +1 +1.4142135623730951 +0 +0.5 +1 +NaN +NaN +1 +1 +1 +1 +NaN +1 +1 +NaN +0 +0.25 +0.5 +1 +0.7071067811865476 +Infinity +2 +1 +NaN +0 +0 +0 +1 +0 +Infinity +Infinity +1 +NaN +Infinity +Infinity +-Infinity +1 +Infinity +0 +-0 +1 +NaN +NaN +1 +-1 +1 +NaN +NaN +-1 +1 +NaN +0 +0 +-0 +1 +0 +Infinity +-Infinity +1 diff --git a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/input.js b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/input.js index 562d5f9d153c..87f94f53b3dd 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/input.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/input.js @@ -79,3 +79,87 @@ console.log(Math.pow(-0, 0.5)); console.log(Math.pow(-0, -Infinity)); console.log(Math.pow(-0, -1)); console.log(Math.pow(-0, -0)); + +console.log("----"); + +console.log(NaN ** NaN); +console.log(NaN ** Infinity); +console.log(NaN ** 2); +console.log(NaN ** 1); +console.log(NaN ** 0); +console.log(NaN ** 0.5); +console.log(NaN ** -Infinity); +console.log(NaN ** -1); +console.log(NaN ** -0); +console.log(Infinity ** NaN); +console.log(Infinity ** Infinity); +console.log(Infinity ** 2); +console.log(Infinity ** 1); +console.log(Infinity ** 0); +console.log(Infinity ** 0.5); +console.log(Infinity ** -Infinity); +console.log(Infinity ** -1); +console.log(Infinity ** -0); +console.log(2 ** NaN); +console.log(2 ** Infinity); +console.log(2 ** 2); +console.log(2 ** 1); +console.log(2 ** 0); +console.log(2 ** 0.5); +console.log(2 ** -Infinity); +console.log(2 ** -1); +console.log(2 ** -0); +console.log(1 ** NaN); +console.log(1 ** Infinity); +console.log(1 ** 2); +console.log(1 ** 1); +console.log(1 ** 0); +console.log(1 ** 0.5); +console.log(1 ** -Infinity); +console.log(1 ** -1); +console.log(1 ** -0); +console.log(0.5 ** NaN); +console.log(0.5 ** Infinity); +console.log(0.5 ** 2); +console.log(0.5 ** 1); +console.log(0.5 ** 0); +console.log(0.5 ** 0.5); +console.log(0.5 ** -Infinity); +console.log(0.5 ** -1); +console.log(0.5 ** -0); +console.log(0 ** NaN); +console.log(0 ** Infinity); +console.log(0 ** 2); +console.log(0 ** 1); +console.log(0 ** 0); +console.log(0 ** 0.5); +console.log(0 ** -Infinity); +console.log(0 ** -1); +console.log(0 ** -0); +console.log((-Infinity) ** NaN); +console.log((-Infinity) ** Infinity); +console.log((-Infinity) ** 2); +console.log((-Infinity) ** 1); +console.log((-Infinity) ** 0); +console.log((-Infinity) ** 0.5); +console.log((-Infinity) ** -Infinity); +console.log((-Infinity) ** -1); +console.log((-Infinity) ** -0); +console.log((-1) ** NaN); +console.log((-1) ** Infinity); +console.log((-1) ** 2); +console.log((-1) ** 1); +console.log((-1) ** 0); +console.log((-1) ** 0.5); +console.log((-1) ** -Infinity); +console.log((-1) ** -1); +console.log((-1) ** -0); +console.log((-0) ** NaN); +console.log((-0) ** Infinity); +console.log((-0) ** 2); +console.log((-0) ** 1); +console.log((-0) ** 0); +console.log((-0) ** 0.5); +console.log((-0) ** -Infinity); +console.log((-0) ** -1); +console.log((-0) ** -0); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.js b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.js index 3d1bf52a0c95..d33b69748151 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.js @@ -79,3 +79,85 @@ console.log(0); console.log(Math.pow(-0, -1 / 0)); console.log(-Infinity); console.log(1); +console.log("----"); +console.log(NaN); +console.log(NaN); +console.log(NaN); +console.log(NaN); +console.log(1); +console.log(NaN); +console.log(NaN); +console.log(NaN); +console.log(1); +console.log(NaN); +console.log(Infinity); +console.log(Infinity); +console.log(Infinity); +console.log(1); +console.log(Infinity); +console.log(0); +console.log(0); +console.log(1); +console.log(NaN); +console.log(Infinity); +console.log(4); +console.log(2); +console.log(1); +console.log(1.4142135623730951); +console.log(0); +console.log(0.5); +console.log(1); +console.log(NaN); +console.log(NaN); +console.log(1); +console.log(1); +console.log(1); +console.log(1); +console.log(NaN); +console.log(1); +console.log(1); +console.log(NaN); +console.log(0); +console.log(0.25); +console.log(0.5); +console.log(1); +console.log(0.7071067811865476); +console.log(Infinity); +console.log(2); +console.log(1); +console.log(NaN); +console.log(0); +console.log(0); +console.log(0); +console.log(1); +console.log(0); +console.log(Infinity); +console.log(Infinity); +console.log(1); +console.log(NaN); +console.log(Infinity); +console.log(Infinity); +console.log(-Infinity); +console.log(1); +console.log(Infinity); +console.log(0); +console.log(-0); +console.log(1); +console.log(NaN); +console.log(NaN); +console.log(1); +console.log(-1); +console.log(1); +console.log(NaN); +console.log(NaN); +console.log(-1); +console.log(1); +console.log(NaN); +console.log(0); +console.log(0); +console.log(-0); +console.log(1); +console.log(0); +console.log(Infinity); +console.log(-Infinity); +console.log(1); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.mangleOnly.js b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.mangleOnly.js index 562d5f9d153c..0cc18baa41ea 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.mangleOnly.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/evaluate/pow_spec/output.mangleOnly.js @@ -79,3 +79,85 @@ console.log(Math.pow(-0, 0.5)); console.log(Math.pow(-0, -Infinity)); console.log(Math.pow(-0, -1)); console.log(Math.pow(-0, -0)); +console.log("----"); +console.log(NaN ** NaN); +console.log(NaN ** Infinity); +console.log(NaN ** 2); +console.log(NaN ** 1); +console.log(NaN ** 0); +console.log(NaN ** 0.5); +console.log(NaN ** -Infinity); +console.log(NaN ** -1); +console.log(NaN ** -0); +console.log(Infinity ** NaN); +console.log(Infinity ** Infinity); +console.log(Infinity ** 2); +console.log(Infinity ** 1); +console.log(Infinity ** 0); +console.log(Infinity ** 0.5); +console.log(Infinity ** -Infinity); +console.log(Infinity ** -1); +console.log(Infinity ** -0); +console.log(2 ** NaN); +console.log(2 ** Infinity); +console.log(2 ** 2); +console.log(2 ** 1); +console.log(2 ** 0); +console.log(2 ** 0.5); +console.log(2 ** -Infinity); +console.log(2 ** -1); +console.log(2 ** -0); +console.log(1 ** NaN); +console.log(1 ** Infinity); +console.log(1 ** 2); +console.log(1 ** 1); +console.log(1 ** 0); +console.log(1 ** 0.5); +console.log(1 ** -Infinity); +console.log(1 ** -1); +console.log(1 ** -0); +console.log(0.5 ** NaN); +console.log(0.5 ** Infinity); +console.log(0.5 ** 2); +console.log(0.5 ** 1); +console.log(0.5 ** 0); +console.log(0.5 ** 0.5); +console.log(0.5 ** -Infinity); +console.log(0.5 ** -1); +console.log(0.5 ** -0); +console.log(0 ** NaN); +console.log(0 ** Infinity); +console.log(0 ** 2); +console.log(0 ** 1); +console.log(0 ** 0); +console.log(0 ** 0.5); +console.log(0 ** -Infinity); +console.log(0 ** -1); +console.log(0 ** -0); +console.log((-Infinity) ** NaN); +console.log((-Infinity) ** Infinity); +console.log((-Infinity) ** 2); +console.log((-Infinity) ** 1); +console.log((-Infinity) ** 0); +console.log((-Infinity) ** 0.5); +console.log((-Infinity) ** -Infinity); +console.log((-Infinity) ** -1); +console.log((-Infinity) ** -0); +console.log((-1) ** NaN); +console.log((-1) ** Infinity); +console.log((-1) ** 2); +console.log((-1) ** 1); +console.log((-1) ** 0); +console.log((-1) ** 0.5); +console.log((-1) ** -Infinity); +console.log((-1) ** -1); +console.log((-1) ** -0); +console.log((-0) ** NaN); +console.log((-0) ** Infinity); +console.log((-0) ** 2); +console.log((-0) ** 1); +console.log((-0) ** 0); +console.log((-0) ** 0.5); +console.log((-0) ** -Infinity); +console.log((-0) ** -1); +console.log((-0) ** -0); From 7822b3ffa58fba057aefb7add12ae14078fbdbff Mon Sep 17 00:00:00 2001 From: magic-akari Date: Mon, 15 Jul 2024 22:48:16 +0800 Subject: [PATCH 3/5] fix --- crates/swc_ecma_utils/src/number.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/swc_ecma_utils/src/number.rs b/crates/swc_ecma_utils/src/number.rs index fc725ed0a8b5..4716f04cb997 100644 --- a/crates/swc_ecma_utils/src/number.rs +++ b/crates/swc_ecma_utils/src/number.rs @@ -87,7 +87,7 @@ impl JsNumber { return JsNumber(f64::NAN); } - if f64::abs(self.0) == 1f64 && rhs.0.is_infinite() { + if self.0.abs() == 1f64 && rhs.0.is_infinite() { return JsNumber(f64::NAN); } @@ -116,7 +116,7 @@ impl std::ops::Shr for JsNumber { // JsNumber >>> JsNumber impl JsNumber { pub fn unsigned_shr(self, rhs: JsNumber) -> JsNumber { - JsNumber((self.0 as u32).wrapping_shr(rhs.0.trunc() as u32) as f64) + JsNumber((self.0.trunc() as u32).wrapping_shr(rhs.0.trunc() as u32) as f64) } } From adbeb7bfd680fbf724fa7d1888b383143e5d5021 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Tue, 16 Jul 2024 10:58:34 +0800 Subject: [PATCH 4/5] chore: Add as_int32 and as_uint32 methods to JsNumber --- crates/swc_ecma_utils/src/number.rs | 86 ++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/crates/swc_ecma_utils/src/number.rs b/crates/swc_ecma_utils/src/number.rs index 4716f04cb997..a9ca7444b9fb 100644 --- a/crates/swc_ecma_utils/src/number.rs +++ b/crates/swc_ecma_utils/src/number.rs @@ -10,7 +10,7 @@ impl ToJsString for f64 { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct JsNumber(f64); impl From for JsNumber { @@ -33,6 +33,26 @@ impl std::ops::Deref for JsNumber { } } +impl JsNumber { + // https://tc39.es/ecma262/#sec-toint32 + fn as_int32(&self) -> i32 { + if !self.0.is_finite() { + return 0; + } + + self.as_int32() + } + + // https://tc39.es/ecma262/#sec-touint32 + fn as_uint32(&self) -> u32 { + if !self.0.is_finite() { + return 0; + } + + self.0.trunc() as u32 + } +} + // JsNumber + JsNumber impl std::ops::Add for JsNumber { type Output = JsNumber; @@ -96,54 +116,60 @@ impl JsNumber { } // JsNumber << JsNumber +// https://tc39.es/ecma262/#sec-numeric-types-number-leftShift impl std::ops::Shl for JsNumber { type Output = JsNumber; fn shl(self, rhs: JsNumber) -> Self::Output { - JsNumber((self.0.trunc() as i32).wrapping_shl(rhs.0.trunc() as u32) as f64) + JsNumber(self.as_int32().wrapping_shl(rhs.as_uint32()) as f64) } } // JsNumber >> JsNumber +// https://tc39.es/ecma262/#sec-numeric-types-number-signedRightShift impl std::ops::Shr for JsNumber { type Output = JsNumber; fn shr(self, rhs: JsNumber) -> Self::Output { - JsNumber((self.0.trunc() as i32).wrapping_shr(rhs.0.trunc() as u32) as f64) + JsNumber((self.as_int32()).wrapping_shr(rhs.as_uint32()) as f64) } } // JsNumber >>> JsNumber +// https://tc39.es/ecma262/#sec-numeric-types-number-unsignedRightShift impl JsNumber { pub fn unsigned_shr(self, rhs: JsNumber) -> JsNumber { - JsNumber((self.0.trunc() as u32).wrapping_shr(rhs.0.trunc() as u32) as f64) + JsNumber((self.as_uint32()).wrapping_shr(rhs.as_uint32()) as f64) } } // JsNumber | JsNumber +// https://tc39.es/ecma262/#sec-numberbitwiseop impl std::ops::BitOr for JsNumber { type Output = JsNumber; fn bitor(self, rhs: JsNumber) -> Self::Output { - JsNumber((self.0.trunc() as i32 | rhs.0.trunc() as i32) as f64) + JsNumber((self.as_int32() | rhs.as_int32()) as f64) } } // JsNumber & JsNumber +// https://tc39.es/ecma262/#sec-numberbitwiseop impl std::ops::BitAnd for JsNumber { type Output = JsNumber; fn bitand(self, rhs: JsNumber) -> Self::Output { - JsNumber((self.0.trunc() as i32 & rhs.0.trunc() as i32) as f64) + JsNumber((self.as_int32() & rhs.as_int32()) as f64) } } // JsNumber ^ JsNumber +// https://tc39.es/ecma262/#sec-numberbitwiseop impl std::ops::BitXor for JsNumber { type Output = JsNumber; fn bitxor(self, rhs: JsNumber) -> Self::Output { - JsNumber((self.0.trunc() as i32 ^ rhs.0.trunc() as i32) as f64) + JsNumber((self.as_int32() ^ rhs.as_int32()) as f64) } } @@ -161,6 +187,50 @@ impl std::ops::Not for JsNumber { type Output = JsNumber; fn not(self) -> Self::Output { - JsNumber(!(self.0.trunc() as i32) as f64) + JsNumber(!(self.as_int32()) as f64) + } +} + +#[cfg(test)] +mod test_js_number { + use super::*; + + #[test] + fn test_as_int32() { + assert_eq!(JsNumber(f64::NAN).as_int32(), 0); + assert_eq!(JsNumber(0.0).as_int32(), 0); + assert_eq!(JsNumber(-0.0).as_int32(), 0); + assert_eq!(JsNumber(f64::INFINITY).as_int32(), 0); + assert_eq!(JsNumber(f64::NEG_INFINITY).as_int32(), 0); + } + + #[test] + fn test_as_uint32() { + assert_eq!(JsNumber(f64::NAN).as_uint32(), 0); + assert_eq!(JsNumber(0.0).as_uint32(), 0); + assert_eq!(JsNumber(-0.0).as_uint32(), 0); + assert_eq!(JsNumber(f64::INFINITY).as_uint32(), 0); + assert_eq!(JsNumber(f64::NEG_INFINITY).as_uint32(), 0); + } + + #[test] + fn test_add() { + assert_eq!(JsNumber(1.0) + JsNumber(2.0), JsNumber(3.0)); + + assert!((JsNumber(1.0) + JsNumber(f64::NAN)).is_nan()); + assert!((JsNumber(f64::NAN) + JsNumber(1.0)).is_nan()); + assert!((JsNumber(f64::NAN) + JsNumber(f64::NAN)).is_nan()); + assert!((JsNumber(f64::INFINITY) + JsNumber(f64::NEG_INFINITY)).is_nan()); + assert!((JsNumber(f64::NEG_INFINITY) + JsNumber(f64::INFINITY)).is_nan()); + + assert_eq!( + JsNumber(f64::INFINITY) + JsNumber(1.0), + JsNumber(f64::INFINITY) + ); + assert_eq!( + JsNumber(f64::NEG_INFINITY) + JsNumber(1.0), + JsNumber(f64::NEG_INFINITY) + ); + assert_eq!(JsNumber(-0.0) + JsNumber(0.0), JsNumber(-0.0)); } } From 6db8424b971b8d9264687036f1ade1b513fb445b Mon Sep 17 00:00:00 2001 From: magic-akari Date: Tue, 16 Jul 2024 11:04:42 +0800 Subject: [PATCH 5/5] fix --- crates/swc_ecma_utils/src/number.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/swc_ecma_utils/src/number.rs b/crates/swc_ecma_utils/src/number.rs index a9ca7444b9fb..12c9facb8600 100644 --- a/crates/swc_ecma_utils/src/number.rs +++ b/crates/swc_ecma_utils/src/number.rs @@ -40,7 +40,7 @@ impl JsNumber { return 0; } - self.as_int32() + self.0.trunc() as i32 } // https://tc39.es/ecma262/#sec-touint32