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 f8d31f708331..837d215b875b 100644 --- a/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs +++ b/crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs @@ -633,13 +633,6 @@ impl SimplifyExpr { // Bit shift operations op!("<<") | op!(">>") | op!(">>>") => { - /// Uses a method for treating a double as 32bits that is - /// equivalent to how JavaScript would convert a - /// number before applying a bit operation. - fn js_convert_double_to_bits(d: f64) -> i32 { - ((d.floor() as i64) & 0xffff_ffff) as i32 - } - fn try_fold_shift( ctx: &ExprCtx, op: BinaryOp, @@ -654,38 +647,12 @@ impl SimplifyExpr { (Known(lv), Known(rv)) => (lv, rv), _ => unreachable!(), }; - - // only the lower 5 bits are used when shifting, so don't do anything - // if the shift amount is outside [0,32) - if !(0.0..32.0).contains(&rv) { - return Unknown; - } - - let rv_int = rv as i32; - if rv_int as f64 != rv { - unimplemented!("error reporting: FRACTIONAL_BITWISE_OPERAND") - // report(FRACTIONAL_BITWISE_OPERAND, right.span()); - // return n; - } - - if lv.floor() != lv { - unimplemented!("error reporting: FRACTIONAL_BITWISE_OPERAND") - // report(FRACTIONAL_BITWISE_OPERAND, left.span()); - // return n; - } - - let bits = js_convert_double_to_bits(lv); + let (lv, rv) = (JsNumber::from(lv), JsNumber::from(rv)); Known(match op { - op!("<<") => (bits << rv_int) as f64, - op!(">>") => (bits >> rv_int) as f64, - op!(">>>") => { - let res = bits as u32 >> rv_int as u32; - // JavaScript always treats the result of >>> as unsigned. - // We must force Java to do the same here. - // unimplemented!(">>> (Zerofill rshift)") - res as f64 - } + op!("<<") => *(lv << rv), + op!(">>") => *(lv >> rv), + op!(">>>") => *(lv.unsigned_shr(rv)), _ => unreachable!("Unknown bit operator {:?}", op), }) 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 ba3ad6f30150..dc2bae680324 100644 --- a/crates/swc_ecma_transforms_optimization/src/simplify/expr/tests.rs +++ b/crates/swc_ecma_transforms_optimization/src/simplify/expr/tests.rs @@ -659,6 +659,27 @@ fn test_fold_bitwise_op2() { fold("x = 12 | NaN", "x=12"); } +#[test] +fn test_issue_9256() { + // Returns -2 prior to fix (Number.MAX_VALUE) + fold("1.7976931348623157e+308 << 1", "0"); + + // Isn't changed prior to fix + fold("1.7976931348623157e+308 << 1.7976931348623157e+308", "0"); + fold("1.7976931348623157e+308 >> 1.7976931348623157e+308", "0"); + + // Panics prior to fix (Number.MIN_VALUE) + fold("5e-324 >> 5e-324", "0"); + fold("5e-324 << 5e-324", "0"); + fold("5e-324 << 0", "0"); + fold("0 << 5e-324", "0"); + + // Wasn't broken prior, used to ensure overflows are handled correctly + fold("1 << 31", "-2147483648"); + fold("-8 >> 2", "-2"); + fold("-8 >>> 2", "1073741822"); +} + #[test] #[ignore] fn test_folding_mix_types_early() { @@ -726,9 +747,9 @@ fn test_fold_bit_shifts() { fold("x = 0xffffffff << 0", "x = -1"); fold("x = 0xffffffff << 4", "x = -16"); - fold_same("1 << 32"); - fold_same("1 << -1"); - fold_same("1 >> 32"); + fold("1 << 32", "1"); + fold("1 << -1", "-2147483648"); + fold("1 >> 32", "1"); } #[test] diff --git a/crates/swc_ecma_utils/src/number.rs b/crates/swc_ecma_utils/src/number.rs index 12c9facb8600..e3923d49b23e 100644 --- a/crates/swc_ecma_utils/src/number.rs +++ b/crates/swc_ecma_utils/src/number.rs @@ -36,11 +36,7 @@ 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.0.trunc() as i32 + self.as_uint32() as i32 } // https://tc39.es/ecma262/#sec-touint32 @@ -49,7 +45,8 @@ impl JsNumber { return 0; } - self.0.trunc() as u32 + // pow(2, 32) = 4294967296 + self.0.trunc().rem_euclid(4294967296.0) as u32 } } @@ -211,6 +208,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]