Skip to content

Commit baeb9e2

Browse files
levi-nzmagic-akari
andauthoredJul 16, 2024··
fix(es/minifier): Fix panic in bitwise logic and incorrect values (#9258)
**Description:** This PR fixes all issues listed in #9256 as well as the following: `1.7976931348623157e+308 << 1.7976931348623157e+308` - swc currently does not transform this and leaves it as-is Anything involving low floating-point numbers, like `5e-324`, such as `5e-324 >> 5e-324`, `5e-324 >> 0` etc - swc currently panics; https://play.swc.rs/?version=1.6.7&code=H4sIAAAAAAAAA0vTME3VNTYyUbCzUzDQBACkm%2Ft7DgAAAA%3D%3D&config=H4sIAAAAAAAAA32UO3LjMAyG%2B5zCozrFjostcoDtcgYOTYIyvXxoCNCxJuO7L0TJj40hdRI%2B%2FAAJgPh%2B2%2B26E5ruY%2FfNn%2Fwz6IJQ7v9swTGRvrClAxM1muIH6t5v9IQTcjogNNN1Jh3p0gM1Fe5%2F7feLogs5I9wUiy365N34nNPkOBRAfLKxlUPWCInwf%2F3CSv6aAJX6bD%2FkHECnDaI0Kp8IeihSYJND0AOCOusiRJlOqovHLKWYYCWwaih5EHmynnxOnPOVWtBWmWxBQL6AIX8GSca5WJaQryfcp2ELh9r3rc8%2F1HDWoWoScsKltYRPK0Q9Zo%2BkXE1SCWe4UoMZLsX9qfROFaBa0qvulH1a6clfAK5A0IhJR5DiNg%2FH87SmdptKnxyPLI0C5%2FmWbpmg56Iq751Q2akyUMhL3Sxgq4GpskY6zoJXyofeggLneFaE0PjlyRylpDQOkJ0AuL%2FaSVM1A3V%2FhSt8ehAb%2BA%2FfkuQBWzyipuM6xTEecthIEIGO2W44cCsor%2BPCW%2BIyrPOaLPBogBVdKjbwugT4AVBWoe3Ll9ng58ERVR%2Fy4bEmFofrfQ9HnfrHe59X8dvi0MVsa4PLkp%2F6O6%2Fm393D6baF7wfvPH7elC3p9R%2BoYzQdMAYAAA%3D%3D This PR also fixes `shiftCount` being incorrect and incorrect conversion between `f64` and `i32`/`u32`. A new API in swc_ecma_utils has been added, `to_js_int32` and `to_js_uint32` that allows external callers to convert `f64`s to `i32` and `u32` so they can perform bitwise operations themselves if they're trying to replicate JavaScript behaviour. I also believe the updated bit shifting logic should be accessible externally via swc_ecma_utils, but unsure about this. **Related issue:** - Closes #9256 --------- Co-authored-by: magic-akari <akari.ccino@gmail.com>
1 parent 5a218f7 commit baeb9e2

File tree

3 files changed

+32
-46
lines changed

3 files changed

+32
-46
lines changed
 

‎crates/swc_ecma_transforms_optimization/src/simplify/expr/mod.rs

+4-37
Original file line numberDiff line numberDiff line change
@@ -633,13 +633,6 @@ impl SimplifyExpr {
633633

634634
// Bit shift operations
635635
op!("<<") | op!(">>") | op!(">>>") => {
636-
/// Uses a method for treating a double as 32bits that is
637-
/// equivalent to how JavaScript would convert a
638-
/// number before applying a bit operation.
639-
fn js_convert_double_to_bits(d: f64) -> i32 {
640-
((d.floor() as i64) & 0xffff_ffff) as i32
641-
}
642-
643636
fn try_fold_shift(
644637
ctx: &ExprCtx,
645638
op: BinaryOp,
@@ -654,38 +647,12 @@ impl SimplifyExpr {
654647
(Known(lv), Known(rv)) => (lv, rv),
655648
_ => unreachable!(),
656649
};
657-
658-
// only the lower 5 bits are used when shifting, so don't do anything
659-
// if the shift amount is outside [0,32)
660-
if !(0.0..32.0).contains(&rv) {
661-
return Unknown;
662-
}
663-
664-
let rv_int = rv as i32;
665-
if rv_int as f64 != rv {
666-
unimplemented!("error reporting: FRACTIONAL_BITWISE_OPERAND")
667-
// report(FRACTIONAL_BITWISE_OPERAND, right.span());
668-
// return n;
669-
}
670-
671-
if lv.floor() != lv {
672-
unimplemented!("error reporting: FRACTIONAL_BITWISE_OPERAND")
673-
// report(FRACTIONAL_BITWISE_OPERAND, left.span());
674-
// return n;
675-
}
676-
677-
let bits = js_convert_double_to_bits(lv);
650+
let (lv, rv) = (JsNumber::from(lv), JsNumber::from(rv));
678651

679652
Known(match op {
680-
op!("<<") => (bits << rv_int) as f64,
681-
op!(">>") => (bits >> rv_int) as f64,
682-
op!(">>>") => {
683-
let res = bits as u32 >> rv_int as u32;
684-
// JavaScript always treats the result of >>> as unsigned.
685-
// We must force Java to do the same here.
686-
// unimplemented!(">>> (Zerofill rshift)")
687-
res as f64
688-
}
653+
op!("<<") => *(lv << rv),
654+
op!(">>") => *(lv >> rv),
655+
op!(">>>") => *(lv.unsigned_shr(rv)),
689656

690657
_ => unreachable!("Unknown bit operator {:?}", op),
691658
})

‎crates/swc_ecma_transforms_optimization/src/simplify/expr/tests.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,27 @@ fn test_fold_bitwise_op2() {
659659
fold("x = 12 | NaN", "x=12");
660660
}
661661

662+
#[test]
663+
fn test_issue_9256() {
664+
// Returns -2 prior to fix (Number.MAX_VALUE)
665+
fold("1.7976931348623157e+308 << 1", "0");
666+
667+
// Isn't changed prior to fix
668+
fold("1.7976931348623157e+308 << 1.7976931348623157e+308", "0");
669+
fold("1.7976931348623157e+308 >> 1.7976931348623157e+308", "0");
670+
671+
// Panics prior to fix (Number.MIN_VALUE)
672+
fold("5e-324 >> 5e-324", "0");
673+
fold("5e-324 << 5e-324", "0");
674+
fold("5e-324 << 0", "0");
675+
fold("0 << 5e-324", "0");
676+
677+
// Wasn't broken prior, used to ensure overflows are handled correctly
678+
fold("1 << 31", "-2147483648");
679+
fold("-8 >> 2", "-2");
680+
fold("-8 >>> 2", "1073741822");
681+
}
682+
662683
#[test]
663684
#[ignore]
664685
fn test_folding_mix_types_early() {
@@ -726,9 +747,9 @@ fn test_fold_bit_shifts() {
726747

727748
fold("x = 0xffffffff << 0", "x = -1");
728749
fold("x = 0xffffffff << 4", "x = -16");
729-
fold_same("1 << 32");
730-
fold_same("1 << -1");
731-
fold_same("1 >> 32");
750+
fold("1 << 32", "1");
751+
fold("1 << -1", "-2147483648");
752+
fold("1 >> 32", "1");
732753
}
733754

734755
#[test]

‎crates/swc_ecma_utils/src/number.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@ impl std::ops::Deref for JsNumber {
3636
impl JsNumber {
3737
// https://tc39.es/ecma262/#sec-toint32
3838
fn as_int32(&self) -> i32 {
39-
if !self.0.is_finite() {
40-
return 0;
41-
}
42-
43-
self.0.trunc() as i32
39+
self.as_uint32() as i32
4440
}
4541

4642
// https://tc39.es/ecma262/#sec-touint32
@@ -49,7 +45,8 @@ impl JsNumber {
4945
return 0;
5046
}
5147

52-
self.0.trunc() as u32
48+
// pow(2, 32) = 4294967296
49+
self.0.trunc().rem_euclid(4294967296.0) as u32
5350
}
5451
}
5552

@@ -211,6 +208,7 @@ mod test_js_number {
211208
assert_eq!(JsNumber(-0.0).as_uint32(), 0);
212209
assert_eq!(JsNumber(f64::INFINITY).as_uint32(), 0);
213210
assert_eq!(JsNumber(f64::NEG_INFINITY).as_uint32(), 0);
211+
assert_eq!(JsNumber(-8.0).as_uint32(), 4294967288);
214212
}
215213

216214
#[test]

0 commit comments

Comments
 (0)
Please sign in to comment.