diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index 60d5eadd18b17..5fb999ad3ccb2 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -258,9 +258,30 @@ impl<'a, 'b> PeepholeFoldConstants { | BinaryOperator::Remainder | BinaryOperator::Multiplication | BinaryOperator::Exponential - | BinaryOperator::Instanceof => { - ctx.eval_binary_expression(e).map(|v| ctx.value_to_expr(e.span, v)) + | BinaryOperator::Instanceof => match (&e.left, &e.right) { + (Expression::NumericLiteral(left), Expression::NumericLiteral(right)) => { + // Do not fold any division unless rhs is 0. + if e.operator == BinaryOperator::Division + && right.value != 0.0 + && !right.value.is_nan() + && !right.value.is_infinite() + { + return None; + } + let value = ctx.eval_binary_expression(e)?; + let ConstantValue::Number(num) = value else { return None }; + (num.is_nan() + || num.is_infinite() + || (num.abs() <= f64::powf(2.0, 53.0) + && Self::approximate_printed_int_char_count(num) + <= Self::approximate_printed_int_char_count(left.value) + + Self::approximate_printed_int_char_count(right.value) + + e.operator.as_str().len())) + .then_some(value) + } + _ => ctx.eval_binary_expression(e), } + .map(|v| ctx.value_to_expr(e.span, v)), BinaryOperator::Addition => Self::try_fold_add(e, ctx), BinaryOperator::BitwiseAnd | BinaryOperator::BitwiseOR | BinaryOperator::BitwiseXOR => { if let Some(v) = ctx.eval_binary_expression(e) { @@ -273,6 +294,23 @@ impl<'a, 'b> PeepholeFoldConstants { } } + // https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L1128 + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + #[must_use] + fn approximate_printed_int_char_count(value: f64) -> usize { + let mut count = if value.is_infinite() { + "Infinity".len() + } else if value.is_nan() { + "NaN".len() + } else { + 1 + 0.max(value.abs().log10().floor() as usize) + }; + if value.is_sign_negative() { + count += 1; + } + count + } + // Simplified version of `tryFoldAdd` from closure compiler. fn try_fold_add(e: &mut BinaryExpression<'a>, ctx: Ctx<'a, 'b>) -> Option> { if let Some(v) = ctx.eval_binary_expression(e) { @@ -1340,8 +1378,8 @@ mod test { #[test] fn test_fold_bitwise_op_additional() { test("x = null & 1", "x = 0"); - test("x = (2 ** 31 - 1) | 1", "x = 2147483647"); - test("x = (2 ** 31) | 1", "x = -2147483647"); + test_same("x = (2 ** 31 - 1) | 1"); + test_same("x = (2 ** 31) | 1"); // https://github.com/oxc-project/oxc/issues/7944 test_same("(x - 1) & 1"); @@ -1374,9 +1412,9 @@ mod test { test("x = 10 >>> 1", "x=5"); test("x = 10 >>> 2", "x=2"); test("x = 10 >>> 5", "x=0"); - test("x = -1 >>> 1", "x=2147483647"); // 0x7fffffff - test("x = -1 >>> 0", "x=4294967295"); // 0xffffffff - test("x = -2 >>> 0", "x=4294967294"); // 0xfffffffe + test_same("x = -1 >>> 1"); + test_same("x = -1 >>> 0"); + test_same("x = -2 >>> 0"); test("x = 0x90000000 >>> 28", "x=9"); test("x = 0xffffffff << 0", "x=-1"); @@ -1446,7 +1484,7 @@ mod test { #[test] fn test_fold_arithmetic() { test("x = 10 + 20", "x = 30"); - test("x = 2 / 4", "x = 0.5"); + test_same("x = 2 / 4"); test("x = 2.25 * 3", "x = 6.75"); test_same("z = x * y"); test_same("x = y * 5"); @@ -1458,8 +1496,7 @@ mod test { test("x = 2 ** 3", "x = 8"); test("x = 2 ** -3", "x = 0.125"); - // FIXME - // test_same("x = 2 ** 55"); // backs off folding because 2 ** 55 is too large + test_same("x = 2 ** 55"); // test_same("x = 3 ** -1"); // backs off because 3**-1 is shorter than 0.3333333333333333 test("x = 0 / 0", "x = NaN"); diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index db227b91e5e5c..fcba39bf20243 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -11,17 +11,17 @@ Original | minified | minified | gzip | gzip | Fixture 544.10 kB | 71.97 kB | 72.48 kB | 26.19 kB | 26.20 kB | lodash.js -555.77 kB | 273.85 kB | 270.13 kB | 91.17 kB | 90.80 kB | d3.js +555.77 kB | 273.64 kB | 270.13 kB | 91.12 kB | 90.80 kB | d3.js 1.01 MB | 460.99 kB | 458.89 kB | 126.91 kB | 126.71 kB | bundle.min.js -1.25 MB | 653.54 kB | 646.76 kB | 164.04 kB | 163.73 kB | three.js +1.25 MB | 653.41 kB | 646.76 kB | 164.04 kB | 163.73 kB | three.js -2.14 MB | 727.17 kB | 724.14 kB | 180.34 kB | 181.07 kB | victory.js +2.14 MB | 726.97 kB | 724.14 kB | 180.29 kB | 181.07 kB | victory.js -3.20 MB | 1.01 MB | 1.01 MB | 332.27 kB | 331.56 kB | echarts.js +3.20 MB | 1.01 MB | 1.01 MB | 332.24 kB | 331.56 kB | echarts.js -6.69 MB | 2.32 MB | 2.31 MB | 493.10 kB | 488.28 kB | antd.js +6.69 MB | 2.32 MB | 2.31 MB | 493.09 kB | 488.28 kB | antd.js 10.95 MB | 3.51 MB | 3.49 MB | 910.40 kB | 915.50 kB | typescript.js