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 b4ad4bf8209ec..241530db1850f 100644 --- a/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs +++ b/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs @@ -19,10 +19,6 @@ use swc_ecma_utils::{ StringType, SymbolType, UndefinedType, Value, is_literal, number::JsNumber, prop_name_eq, to_int32, BoolType, ExprCtx, ExprExt, NullType, NumberType, ObjectType, StringType, SymbolType, UndefinedType, Value, - is_literal, - number::{ToJsInt32, ToJsUint32}, - 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}; @@ -655,29 +651,12 @@ impl SimplifyExpr { (Known(lv), Known(rv)) => (lv, rv), _ => unreachable!(), }; + let (lv, rv) = (JsNumber::from(lv), JsNumber::from(rv)); Known(match op { - op!("<<") => { - // https://262.ecma-international.org/5.1/#sec-11.7.1 - let lnum = lv.to_js_int32(); - let rnum = rv.to_js_uint32(); - - (lnum << (rnum & 0x1f)) as f64 - } - op!(">>") => { - // https://262.ecma-international.org/5.1/#sec-11.7.2 - let lnum = lv.to_js_int32(); - let rnum = rv.to_js_uint32(); - - (lnum >> (rnum & 0x1f)) as f64 - } - op!(">>>") => { - // https://262.ecma-international.org/5.1/#sec-11.7.3 - let lnum = lv.to_js_uint32(); - let rnum = rv.to_js_uint32(); - - (lnum >> (rnum & 0x1f)) as f64 - } + op!("<<") => *(lv << rv), + op!(">>") => *(lv >> rv), + op!(">>>") => *(lv.unsigned_shr(rv)), _ => unreachable!("Unknown bit operator {:?}", op), }) diff --git a/crates/swc_ecma_utils/src/number.rs b/crates/swc_ecma_utils/src/number.rs index 949ac198736ce..f0a80753b65b0 100644 --- a/crates/swc_ecma_utils/src/number.rs +++ b/crates/swc_ecma_utils/src/number.rs @@ -95,20 +95,40 @@ impl std::ops::Deref for JsNumber { impl JsNumber { // https://tc39.es/ecma262/#sec-toint32 fn as_int32(&self) -> i32 { - if !self.0.is_finite() { + if !self.0.is_finite() || self.0 == 0.0 { return 0; } - self.0.trunc() as i32 + const TWO_32: f64 = u32::MAX as f64 + 1.0; // 2^32 + const TWO_31: f64 = i32::MAX as f64 + 1.0; // 2^31 + + let pos_int = self.signum() * self.abs().floor(); + let int32_bit = pos_int % TWO_32; + + (if int32_bit >= TWO_31 { + int32_bit - TWO_32 + } else { + int32_bit + }) as i32 } // https://tc39.es/ecma262/#sec-touint32 fn as_uint32(&self) -> u32 { - if !self.0.is_finite() { + if !self.0.is_finite() || self.0 == 0.0 { return 0; } - self.0.trunc() as u32 + const TWO_32: f64 = u32::MAX as f64 + 1.0; // 2^32 + + let pos_int = self.signum() * self.abs().floor(); + let result = pos_int % TWO_32; + // Extra step: since `x as u32` doesn't overflow, we have to add if result is + // negative + (if result < 0.0 { + result + TWO_32 + } else { + result + }) as u32 } } @@ -349,6 +369,7 @@ mod test_js_number { 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); + assert_eq!(JsNumber(-8.0).as_uint32(), 4294967288); } #[test]