mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 20:28:58 +00:00
feat(minifier): support subtraction assignment. (#6214)
Due to the potential for string concatenation when using the `+=` operator, we should only handle the scenario when using the `-=` operator.
This commit is contained in:
parent
ea28ee9eda
commit
585ccdad8c
3 changed files with 83 additions and 15 deletions
|
|
@ -1,3 +1,14 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{GetSpan, Span, SPAN};
|
||||
use oxc_syntax::{
|
||||
number::{NumberBase, ToJsInt32},
|
||||
operator::{BinaryOperator, LogicalOperator, UnaryOperator},
|
||||
};
|
||||
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||
|
||||
use crate::{
|
||||
node_util::{
|
||||
is_exact_int64, IsLiteralValue, MayHaveSideEffects, NodeUtil, NumberValue, ValueType,
|
||||
|
|
@ -6,16 +17,6 @@ use crate::{
|
|||
ty::Ty,
|
||||
CompressorPass,
|
||||
};
|
||||
use num_bigint::BigInt;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{GetSpan, Span, SPAN};
|
||||
use oxc_syntax::number::ToJsInt32;
|
||||
use oxc_syntax::{
|
||||
number::NumberBase,
|
||||
operator::{BinaryOperator, LogicalOperator, UnaryOperator},
|
||||
};
|
||||
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// Constant Folding
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use oxc_ast::ast::*;
|
||||
use oxc_span::{GetSpan, SPAN};
|
||||
use oxc_syntax::number::ToJsInt32;
|
||||
use oxc_syntax::{
|
||||
number::NumberBase,
|
||||
operator::{BinaryOperator, UnaryOperator},
|
||||
|
|
@ -77,6 +78,12 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
|
|||
}
|
||||
|
||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if let Expression::AssignmentExpression(assignment_expr) = expr {
|
||||
if let Some(new_expr) = self.try_compress_assignment_expression(assignment_expr, ctx) {
|
||||
*expr = new_expr;
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
if !self.compress_undefined(expr, ctx) {
|
||||
self.compress_boolean(expr, ctx);
|
||||
}
|
||||
|
|
@ -260,6 +267,45 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
|
|||
self.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn try_compress_assignment_expression(
|
||||
&mut self,
|
||||
expr: &mut AssignmentExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let target = expr.left.as_simple_assignment_target_mut()?;
|
||||
if matches!(expr.operator, AssignmentOperator::Subtraction) {
|
||||
match &expr.right {
|
||||
Expression::NumericLiteral(num) if num.value.to_js_int_32() == 1 => {
|
||||
// The `_` will not be placed to the target code.
|
||||
let target = std::mem::replace(
|
||||
target,
|
||||
ctx.ast.simple_assignment_target_identifier_reference(SPAN, "_"),
|
||||
);
|
||||
Some(ctx.ast.expression_update(SPAN, UpdateOperator::Decrement, true, target))
|
||||
}
|
||||
Expression::UnaryExpression(un)
|
||||
if matches!(un.operator, UnaryOperator::UnaryNegation) =>
|
||||
{
|
||||
if let Expression::NumericLiteral(num) = &un.argument {
|
||||
(num.value.to_js_int_32() == 1).then(|| {
|
||||
// The `_` will not be placed to the target code.
|
||||
let target = std::mem::replace(
|
||||
target,
|
||||
ctx.ast.simple_assignment_target_identifier_reference(SPAN, "_"),
|
||||
);
|
||||
ctx.ast.expression_update(SPAN, UpdateOperator::Increment, true, target)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.java>
|
||||
|
|
@ -302,4 +348,25 @@ mod test {
|
|||
// shadowd
|
||||
test_same("(function(undefined) { let x = typeof undefined; })()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn fold_true_false_comparison() {
|
||||
test("x == true", "x == 1");
|
||||
test("x == false", "x == 0");
|
||||
test("x != true", "x != 1");
|
||||
test("x < true", "x < 1");
|
||||
test("x <= true", "x <= 1");
|
||||
test("x > true", "x > 1");
|
||||
test("x >= true", "x >= 1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_subtraction_assignment() {
|
||||
test("x -= 1", "--x");
|
||||
test("x -= -1", "++x");
|
||||
test_same("x -= 2");
|
||||
test_same("x += 1"); // The string concatenation may be triggered, so we don't fold this.
|
||||
test_same("x += -1");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ Original | Minified | esbuild | Gzip | esbuild
|
|||
|
||||
72.14 kB | 24.47 kB | 23.70 kB | 8.65 kB | 8.54 kB | react.development.js
|
||||
|
||||
173.90 kB | 61.70 kB | 59.82 kB | 19.54 kB | 19.33 kB | moment.js
|
||||
173.90 kB | 61.69 kB | 59.82 kB | 19.54 kB | 19.33 kB | moment.js
|
||||
|
||||
287.63 kB | 92.83 kB | 90.07 kB | 32.29 kB | 31.95 kB | jquery.js
|
||||
|
||||
|
|
@ -14,13 +14,13 @@ Original | Minified | esbuild | Gzip | esbuild
|
|||
|
||||
1.01 MB | 470.11 kB | 458.89 kB | 126.97 kB | 126.71 kB | bundle.min.js
|
||||
|
||||
1.25 MB | 671 kB | 646.76 kB | 164.72 kB | 163.73 kB | three.js
|
||||
1.25 MB | 671.00 kB | 646.76 kB | 164.72 kB | 163.73 kB | three.js
|
||||
|
||||
2.14 MB | 756.69 kB | 724.14 kB | 182.87 kB | 181.07 kB | victory.js
|
||||
|
||||
3.20 MB | 1.05 MB | 1.01 MB | 334.11 kB | 331.56 kB | echarts.js
|
||||
3.20 MB | 1.05 MB | 1.01 MB | 334.10 kB | 331.56 kB | echarts.js
|
||||
|
||||
6.69 MB | 2.44 MB | 2.31 MB | 498.90 kB | 488.28 kB | antd.js
|
||||
6.69 MB | 2.44 MB | 2.31 MB | 498.93 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.59 MB | 3.49 MB | 913.94 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.59 MB | 3.49 MB | 913.96 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue