diff --git a/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs b/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs index ad91d275bf1a4..504460c67ac8d 100644 --- a/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs +++ b/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs @@ -136,8 +136,23 @@ impl<'a> From<&BinaryExpression<'a>> for ValueType { | BinaryOperator::ShiftRight | BinaryOperator::BitwiseXOR | BinaryOperator::BitwiseAnd - | BinaryOperator::Exponential - | BinaryOperator::ShiftRightZeroFill => Self::Number, + | BinaryOperator::Exponential => { + let left = Self::from(&e.left); + let right = Self::from(&e.right); + if left.is_bigint() || right.is_bigint() { + Self::BigInt + } else if !(left.is_object() || left.is_undetermined()) + || !(right.is_object() || right.is_undetermined()) + { + // non-object values other than BigInt are converted to number by `ToNumber` + // if either operand is a number, the result is always a number + // because if the other operand is a bigint, an error is thrown + Self::Number + } else { + Self::Undetermined + } + } + BinaryOperator::ShiftRightZeroFill => Self::Number, BinaryOperator::Instanceof | BinaryOperator::In | BinaryOperator::Equality diff --git a/crates/oxc_minifier/src/peephole/minimize_conditions.rs b/crates/oxc_minifier/src/peephole/minimize_conditions.rs index 5c14e1b8da1a9..7d0974d3b3605 100644 --- a/crates/oxc_minifier/src/peephole/minimize_conditions.rs +++ b/crates/oxc_minifier/src/peephole/minimize_conditions.rs @@ -104,8 +104,8 @@ impl<'a> PeepholeOptimizations { // `a instanceof b === true` -> `a instanceof b` // `a instanceof b === false` -> `!(a instanceof b)` // ^^^^^^^^^^^^^^ `ValueType::from(&e.left).is_boolean()` is `true`. - // `x >> y !== 0` -> `x >> y` - // ^^^^^^ ValueType::from(&e.left).is_number()` is `true`. + // `x >> +y !== 0` -> `x >> +y` + // ^^^^^^^ ValueType::from(&e.left).is_number()` is `true`. fn try_minimize_binary( e: &mut BinaryExpression<'a>, ctx: Ctx<'a, '_>, @@ -1519,10 +1519,10 @@ mod test { #[test] fn compress_binary_number() { - test("if(x >> y == 0){}", "!(x >> y)"); - test("if(x >> y === 0){}", "!(x >> y)"); - test("if(x >> y != 0){}", "x >> y"); - test("if(x >> y !== 0){}", "x >> y"); + test("if(x >> +y == 0){}", "!(x >> +y)"); + test("if(x >> +y === 0){}", "!(x >> +y)"); + test("if(x >> +y != 0){}", "x >> +y"); + test("if(x >> +y !== 0){}", "x >> +y"); test("if((-0 != +0) !== false){}", ""); test_same("foo(x >> y == 0)"); diff --git a/crates/oxc_minifier/src/peephole/minimize_expression_in_boolean_context.rs b/crates/oxc_minifier/src/peephole/minimize_expression_in_boolean_context.rs index ddd8dbc4f0681..8eb8ec5508918 100644 --- a/crates/oxc_minifier/src/peephole/minimize_expression_in_boolean_context.rs +++ b/crates/oxc_minifier/src/peephole/minimize_expression_in_boolean_context.rs @@ -136,8 +136,8 @@ mod test { test("!!a ? b : c", "a ? b : c"); test("if (!!!a);", "!a"); // test("Boolean(!!a)", "Boolean()"); - test("if ((a | b) !== 0);", "a | b"); - test("if ((a | b) === 0);", "!(a | b)"); + test("if ((a | +b) !== 0);", "a | +b"); + test("if ((a | +b) === 0);", "!(a | +b)"); test("if (!!a && !!b);", "a && b"); test("if (!!a || !!b);", "a || b"); test("if (anything || (0, false));", "anything"); diff --git a/crates/oxc_minifier/tests/ecmascript/value_type.rs b/crates/oxc_minifier/tests/ecmascript/value_type.rs index 2c28d1a424e6e..d638e5b348efb 100644 --- a/crates/oxc_minifier/tests/ecmascript/value_type.rs +++ b/crates/oxc_minifier/tests/ecmascript/value_type.rs @@ -97,8 +97,16 @@ fn binary_tests() { test("[] + {}", ValueType::Undetermined); test("1 - 0", ValueType::Number); + test("1n - 0n", ValueType::BigInt); + test("1 - 0n", ValueType::BigInt); // throws an error + test("foo - 1", ValueType::Number); + test("foo - 1n", ValueType::BigInt); + test("foo - undefined", ValueType::Number); + test("foo - null", ValueType::Number); + test("foo - ''", ValueType::Number); + test("foo - true", ValueType::Number); + test("foo - bar", ValueType::Undetermined); // number or bigint test("1 * 0", ValueType::Number); - // test("foo * bar", ValueType::Undetermined); // number or bigint test("1 / 0", ValueType::Number); test("1 % 0", ValueType::Number); test("1 << 0", ValueType::Number); @@ -107,10 +115,12 @@ fn binary_tests() { test("1 ^ 0", ValueType::Number); test("1 & 0", ValueType::Number); test("1 ** 0", ValueType::Number); + test("1 >>> 0", ValueType::Number); + test("1n >>> 0n", ValueType::Number); // throws an error // foo * bar can be number, but the result is always a bigint (if no error happened) - // test("foo * bar * 1n", ValueType::BigInt); + test("foo * bar * 1n", ValueType::BigInt); test("foo * bar * 1", ValueType::Number); // unsigned right shift always returns a number test("foo >>> (1n + 0n)", ValueType::Number); diff --git a/crates/oxc_minifier/tests/peephole/esbuild.rs b/crates/oxc_minifier/tests/peephole/esbuild.rs index 0583d4ee36c9c..81d581bd34ddb 100644 --- a/crates/oxc_minifier/tests/peephole/esbuild.rs +++ b/crates/oxc_minifier/tests/peephole/esbuild.rs @@ -415,8 +415,8 @@ fn js_parser_test() { test("a = Boolean(b ? true : false)", "a = !!b;"); test("a = Boolean(b ? false : true)", "a = !b;"); test("a = Boolean(b ? c > 0 : c < 0)", "a = b ? c > 0 : c < 0;"); - test("a = Boolean((b | c) !== 0)", "a = !!(b | c);"); - test("a = Boolean(b ? (c | d) !== 0 : (d | e) !== 0)", "a = !!(b ? c | d : d | e);"); + test("a = Boolean((b | +c) !== 0)", "a = !!(b | +c);"); + test("a = Boolean(b ? (c | +d) !== 0 : (d | +e) !== 0)", "a = !!(b ? c | +d : d | +e);"); test("a = Number(x)", "a = Number(x);"); test("a = Number(0n)", "a = Number(0n);"); test("a = Number(false); var Number", "a = Number(!1);var Number;"); @@ -726,38 +726,38 @@ fn js_parser_test() { test("if (!!a && !!b) throw 0", "if (a && b) throw 0;"); test("if (!!a ? !!b : !!c) throw 0", "if (a ? b : c) throw 0;"); test("if ((a + b) !== 0) throw 0", "if (a + b !== 0) throw 0;"); - test("if ((a | b) !== 0) throw 0", "if (a | b) throw 0;"); - test("if ((a & b) !== 0) throw 0", "if (a & b) throw 0;"); - test("if ((a ^ b) !== 0) throw 0", "if (a ^ b) throw 0;"); - test("if ((a << b) !== 0) throw 0", "if (a << b) throw 0;"); - test("if ((a >> b) !== 0) throw 0", "if (a >> b) throw 0;"); + test("if ((a | +b) !== 0) throw 0", "if (a | +b) throw 0;"); + test("if ((a & +b) !== 0) throw 0", "if (a & +b) throw 0;"); + test("if ((a ^ +b) !== 0) throw 0", "if (a ^ +b) throw 0;"); + test("if ((a << +b) !== 0) throw 0", "if (a << +b) throw 0;"); + test("if ((a >> +b) !== 0) throw 0", "if (a >> +b) throw 0;"); test("if ((a >>> b) !== 0) throw 0", "if (a >>> b) throw 0;"); // test("if (+a !== 0) throw 0", "if (+a != 0) throw 0;"); // test("if (~a !== 0) throw 0", "if (~a) throw 0;"); test("if (0 != (a + b)) throw 0", "if (a + b != 0) throw 0;"); - test("if (0 != (a | b)) throw 0", "if (a | b) throw 0;"); - test("if (0 != (a & b)) throw 0", "if (a & b) throw 0;"); - test("if (0 != (a ^ b)) throw 0", "if (a ^ b) throw 0;"); - test("if (0 != (a << b)) throw 0", "if (a << b) throw 0;"); - test("if (0 != (a >> b)) throw 0", "if (a >> b) throw 0;"); + test("if (0 != (a | +b)) throw 0", "if (a | +b) throw 0;"); + test("if (0 != (a & +b)) throw 0", "if (a & +b) throw 0;"); + test("if (0 != (a ^ +b)) throw 0", "if (a ^ +b) throw 0;"); + test("if (0 != (a << +b)) throw 0", "if (a << +b) throw 0;"); + test("if (0 != (a >> +b)) throw 0", "if (a >> +b) throw 0;"); test("if (0 != (a >>> b)) throw 0", "if (a >>> b) throw 0;"); // test("if (0 != +a) throw 0", "if (+a != 0) throw 0;"); // test("if (0 != ~a) throw 0", "if (~a) throw 0;"); test("if ((a + b) === 0) throw 0", "if (a + b === 0) throw 0;"); - test("if ((a | b) === 0) throw 0", "if (!(a | b)) throw 0;"); - test("if ((a & b) === 0) throw 0", "if (!(a & b)) throw 0;"); - test("if ((a ^ b) === 0) throw 0", "if (!(a ^ b)) throw 0;"); - test("if ((a << b) === 0) throw 0", "if (!(a << b)) throw 0;"); - test("if ((a >> b) === 0) throw 0", "if (!(a >> b)) throw 0;"); + test("if ((a | +b) === 0) throw 0", "if (!(a | +b)) throw 0;"); + test("if ((a & +b) === 0) throw 0", "if (!(a & +b)) throw 0;"); + test("if ((a ^ +b) === 0) throw 0", "if (!(a ^ +b)) throw 0;"); + test("if ((a << +b) === 0) throw 0", "if (!(a << +b)) throw 0;"); + test("if ((a >> +b) === 0) throw 0", "if (!(a >> +b)) throw 0;"); test("if ((a >>> b) === 0) throw 0", "if (!(a >>> b)) throw 0;"); // test("if (+a === 0) throw 0", "if (+a == 0) throw 0;"); // test("if (~a === 0) throw 0", "if (!~a) throw 0;"); test("if (0 == (a + b)) throw 0", "if (a + b == 0) throw 0;"); - test("if (0 == (a | b)) throw 0", "if (!(a | b)) throw 0;"); - test("if (0 == (a & b)) throw 0", "if (!(a & b)) throw 0;"); - test("if (0 == (a ^ b)) throw 0", "if (!(a ^ b)) throw 0;"); - test("if (0 == (a << b)) throw 0", "if (!(a << b)) throw 0;"); - test("if (0 == (a >> b)) throw 0", "if (!(a >> b)) throw 0;"); + test("if (0 == (a | +b)) throw 0", "if (!(a | +b)) throw 0;"); + test("if (0 == (a & +b)) throw 0", "if (!(a & +b)) throw 0;"); + test("if (0 == (a ^ +b)) throw 0", "if (!(a ^ +b)) throw 0;"); + test("if (0 == (a << +b)) throw 0", "if (!(a << +b)) throw 0;"); + test("if (0 == (a >> +b)) throw 0", "if (!(a >> +b)) throw 0;"); test("if (0 == (a >>> b)) throw 0", "if (!(a >>> b)) throw 0;"); // test("if (0 == +a) throw 0", "if (+a == 0) throw 0;"); // test("if (0 == ~a) throw 0", "if (!~a) throw 0;"); diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 909c4cbb63dd8..622cc34e0b8bc 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -5,23 +5,23 @@ Original | minified | minified | gzip | gzip | Fixture 173.90 kB | 59.55 kB | 59.82 kB | 19.18 kB | 19.33 kB | moment.js -287.63 kB | 89.45 kB | 90.07 kB | 30.97 kB | 31.95 kB | jquery.js +287.63 kB | 89.46 kB | 90.07 kB | 30.97 kB | 31.95 kB | jquery.js 342.15 kB | 117.62 kB | 118.14 kB | 43.45 kB | 44.37 kB | vue.js 544.10 kB | 71.40 kB | 72.48 kB | 25.86 kB | 26.20 kB | lodash.js -555.77 kB | 271.20 kB | 270.13 kB | 88.30 kB | 90.80 kB | d3.js +555.77 kB | 271.21 kB | 270.13 kB | 88.30 kB | 90.80 kB | d3.js 1.01 MB | 440.94 kB | 458.89 kB | 122.52 kB | 126.71 kB | bundle.min.js -1.25 MB | 650.34 kB | 646.76 kB | 160.96 kB | 163.73 kB | three.js +1.25 MB | 650.35 kB | 646.76 kB | 160.96 kB | 163.73 kB | three.js -2.14 MB | 717.05 kB | 724.14 kB | 162.02 kB | 181.07 kB | victory.js +2.14 MB | 717.10 kB | 724.14 kB | 162.05 kB | 181.07 kB | victory.js -3.20 MB | 1.01 MB | 1.01 MB | 324.34 kB | 331.56 kB | echarts.js +3.20 MB | 1.01 MB | 1.01 MB | 324.35 kB | 331.56 kB | echarts.js 6.69 MB | 2.28 MB | 2.31 MB | 467.77 kB | 488.28 kB | antd.js -10.95 MB | 3.36 MB | 3.49 MB | 861.80 kB | 915.50 kB | typescript.js +10.95 MB | 3.36 MB | 3.49 MB | 861.81 kB | 915.50 kB | typescript.js