mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
perf(minifier): only run optimizations on local changes (#8644)
Previously all code are ran in a fixed point loop when ast changes. This PR changes running code when a function changes its enclosing ast only.
This commit is contained in:
parent
b75d4919ee
commit
5b3c412e26
14 changed files with 205 additions and 70 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1858,6 +1858,7 @@ dependencies = [
|
|||
"oxc_syntax",
|
||||
"oxc_traverse",
|
||||
"pico-args",
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ oxc_syntax = { workspace = true }
|
|||
oxc_traverse = { workspace = true }
|
||||
|
||||
cow-utils = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
oxc_parser = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
|
||||
*stmts = new_stmts;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
if self.changed {
|
||||
if self.is_current_function_changed() {
|
||||
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
|
||||
}
|
||||
}
|
||||
|
|
@ -147,7 +147,7 @@ impl<'a> PeepholeOptimizations {
|
|||
if let Statement::ExpressionStatement(expr_stmt) = ctx.ast.move_statement(&mut stmts[i]) {
|
||||
if let Statement::ForStatement(for_stmt) = &mut stmts[i + 1] {
|
||||
for_stmt.init = Some(ForStatementInit::from(expr_stmt.unbox().expression));
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -163,11 +163,11 @@ impl<'a> PeepholeOptimizations {
|
|||
match for_stmt.init.as_mut() {
|
||||
Some(ForStatementInit::VariableDeclaration(for_var)) => {
|
||||
for_var.declarations.splice(0..0, var.unbox().declarations);
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
None => {
|
||||
for_stmt.init = Some(ForStatementInit::VariableDeclaration(var));
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
|
|
@ -209,7 +209,7 @@ impl<'a> PeepholeOptimizations {
|
|||
_ => unreachable!(),
|
||||
};
|
||||
*left = ForStatementLeft::VariableDeclaration(var);
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ impl<'a> PeepholeOptimizations {
|
|||
*expr = MemberExpression::StaticMemberExpression(
|
||||
ctx.ast.alloc_static_member_expression(e.span, object, property, e.optional),
|
||||
);
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
return;
|
||||
}
|
||||
let v = s.value.as_str();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
_ => None,
|
||||
} {
|
||||
*expr = folded_expr;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -706,7 +706,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
true
|
||||
});
|
||||
if e.properties.len() != len {
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,16 +21,20 @@ impl<'a> PeepholeOptimizations {
|
|||
stmts: &mut oxc_allocator::Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
self.try_replace_if(stmts, ctx);
|
||||
let changed = self.changed;
|
||||
while self.changed {
|
||||
self.changed = false;
|
||||
self.try_replace_if(stmts, ctx);
|
||||
let mut changed = false;
|
||||
let mut changed2 = false;
|
||||
Self::try_replace_if(stmts, &mut changed2, ctx);
|
||||
while changed2 {
|
||||
changed2 = false;
|
||||
Self::try_replace_if(stmts, &mut changed2, ctx);
|
||||
if stmts.iter().any(|stmt| matches!(stmt, Statement::EmptyStatement(_))) {
|
||||
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
|
||||
}
|
||||
changed = changed2;
|
||||
}
|
||||
if changed {
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
self.changed = self.changed || changed;
|
||||
}
|
||||
|
||||
pub fn minimize_conditions_exit_statement(
|
||||
|
|
@ -64,7 +68,7 @@ impl<'a> PeepholeOptimizations {
|
|||
_ => None,
|
||||
} {
|
||||
*stmt = folded_stmt;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +91,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
if changed {
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
@ -99,7 +103,7 @@ impl<'a> PeepholeOptimizations {
|
|||
_ => None,
|
||||
} {
|
||||
*expr = folded_expr;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -236,7 +240,11 @@ impl<'a> PeepholeOptimizations {
|
|||
None
|
||||
}
|
||||
|
||||
fn try_replace_if(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
fn try_replace_if(
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
changed: &mut bool,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
for i in 0..stmts.len() {
|
||||
let Statement::IfStatement(if_stmt) = &stmts[i] else {
|
||||
continue;
|
||||
|
|
@ -268,7 +276,7 @@ impl<'a> PeepholeOptimizations {
|
|||
alternate,
|
||||
);
|
||||
stmts[i] = ctx.ast.statement_return(if_stmt.span, Some(argument));
|
||||
self.changed = true;
|
||||
*changed = true;
|
||||
break;
|
||||
} else if else_branch.is_some() && Self::statement_must_exit_parent(then_branch) {
|
||||
let Statement::IfStatement(if_stmt) = &mut stmts[i] else {
|
||||
|
|
@ -276,7 +284,7 @@ impl<'a> PeepholeOptimizations {
|
|||
};
|
||||
let else_branch = if_stmt.alternate.take().unwrap();
|
||||
stmts.insert(i + 1, else_branch);
|
||||
self.changed = true;
|
||||
*changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ impl<'a> PeepholeOptimizations {
|
|||
if let Some(last) = stmts.last() {
|
||||
if matches!(last, Statement::ReturnStatement(ret) if ret.argument.is_none()) {
|
||||
stmts.pop();
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_syntax::es_target::ESTarget;
|
||||
use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx};
|
||||
|
||||
mod collapse_variable_declarations;
|
||||
mod convert_to_dotted_properties;
|
||||
mod fold_constants;
|
||||
|
|
@ -14,19 +9,43 @@ mod replace_known_methods;
|
|||
mod statement_fusion;
|
||||
mod substitute_alternate_syntax;
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_syntax::{es_target::ESTarget, scope::ScopeId};
|
||||
use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx};
|
||||
|
||||
pub use normalize::{Normalize, NormalizeOptions};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
pub struct PeepholeOptimizations {
|
||||
target: ESTarget,
|
||||
changed: bool,
|
||||
|
||||
/// `in_fixed_loop`: Do not compress syntaxes that are hard to analyze inside the fixed loop.
|
||||
/// Opposite of `late` in Closure Compiler.
|
||||
in_fixed_loop: bool,
|
||||
|
||||
/// Walk the ast in a fixed point loop until no changes are made.
|
||||
/// `prev_function_changed`, `functions_changed` and `current_function` track changes
|
||||
/// in top level and each function. No minification code are run if the function is not changed
|
||||
/// in the previous walk.
|
||||
iteration: u8,
|
||||
prev_functions_changed: FxHashSet<ScopeId>,
|
||||
functions_changed: FxHashSet<ScopeId>,
|
||||
/// Track the current function as a stack.
|
||||
current_function_stack:
|
||||
std::vec::Vec<(ScopeId, /* prev changed */ bool, /* current changed */ bool)>,
|
||||
}
|
||||
|
||||
impl<'a> PeepholeOptimizations {
|
||||
pub fn new(target: ESTarget, in_fixed_loop: bool) -> Self {
|
||||
Self { target, changed: false, in_fixed_loop }
|
||||
Self {
|
||||
target,
|
||||
in_fixed_loop,
|
||||
iteration: 0,
|
||||
prev_functions_changed: FxHashSet::default(),
|
||||
functions_changed: FxHashSet::default(),
|
||||
current_function_stack: std::vec::Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
|
|
@ -34,24 +53,84 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
|
||||
pub fn run_in_loop(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
self.changed = false;
|
||||
self.build(program, ctx);
|
||||
if !self.changed {
|
||||
if self.functions_changed.is_empty() {
|
||||
break;
|
||||
}
|
||||
if i > 10 {
|
||||
self.prev_functions_changed.clear();
|
||||
std::mem::swap(&mut self.prev_functions_changed, &mut self.functions_changed);
|
||||
if self.iteration > 10 {
|
||||
debug_assert!(false, "Ran loop more than 10 times.");
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
self.iteration += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_current_function_as_changed(&mut self) {
|
||||
if let Some((_scope_id, _prev_changed, current_changed)) =
|
||||
self.current_function_stack.last_mut()
|
||||
{
|
||||
*current_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_current_function_changed(&self) -> bool {
|
||||
if let Some((_, _, current_changed)) = self.current_function_stack.last() {
|
||||
return *current_changed;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_prev_function_changed(&self) -> bool {
|
||||
if !self.in_fixed_loop || self.iteration == 0 {
|
||||
return true;
|
||||
}
|
||||
if let Some((_, prev_changed, _)) = self.current_function_stack.last() {
|
||||
return *prev_changed;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn enter_program_or_function(&mut self, scope_id: ScopeId) {
|
||||
self.current_function_stack.push((
|
||||
scope_id,
|
||||
self.prev_functions_changed.contains(&scope_id),
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
fn exit_program_or_function(&mut self) {
|
||||
if let Some((scope_id, _, changed)) = self.current_function_stack.pop() {
|
||||
if changed {
|
||||
self.functions_changed.insert(scope_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for PeepholeOptimizations {
|
||||
fn enter_program(&mut self, program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
self.enter_program_or_function(program.scope_id());
|
||||
}
|
||||
|
||||
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
self.exit_program_or_function();
|
||||
}
|
||||
|
||||
fn enter_function(&mut self, func: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
self.enter_program_or_function(func.scope_id());
|
||||
}
|
||||
|
||||
fn exit_function(&mut self, _: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
self.exit_program_or_function();
|
||||
}
|
||||
|
||||
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.statement_fusion_exit_statements(stmts, ctx);
|
||||
self.collapse_variable_declarations(stmts, ctx);
|
||||
self.minimize_conditions_exit_statements(stmts, ctx);
|
||||
|
|
@ -59,19 +138,31 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
}
|
||||
|
||||
fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.minimize_conditions_exit_statement(stmt, ctx);
|
||||
self.remove_dead_code_exit_statement(stmt, ctx);
|
||||
}
|
||||
|
||||
fn exit_return_statement(&mut self, stmt: &mut ReturnStatement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_return_statement(stmt, ctx);
|
||||
}
|
||||
|
||||
fn exit_function_body(&mut self, body: &mut FunctionBody<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.minimize_exit_points(body, ctx);
|
||||
}
|
||||
|
||||
fn exit_class_body(&mut self, body: &mut ClassBody<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.remove_dead_code_exit_class_body(body, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -80,10 +171,16 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
decl: &mut VariableDeclaration<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_variable_declaration(decl, ctx);
|
||||
}
|
||||
|
||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.fold_constants_exit_expression(expr, ctx);
|
||||
self.minimize_conditions_exit_expression(expr, ctx);
|
||||
self.remove_dead_code_exit_expression(expr, ctx);
|
||||
|
|
@ -92,6 +189,9 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
}
|
||||
|
||||
fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_call_expression(expr, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -100,10 +200,16 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
expr: &mut MemberExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.convert_to_dotted_properties(expr, ctx);
|
||||
}
|
||||
|
||||
fn exit_object_property(&mut self, prop: &mut ObjectProperty<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_object_property(prop, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -112,10 +218,16 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
prop: &mut AssignmentTargetPropertyProperty<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_assignment_target_property_property(prop, ctx);
|
||||
}
|
||||
|
||||
fn exit_binding_property(&mut self, prop: &mut BindingProperty<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_binding_property(prop, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +236,9 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
prop: &mut MethodDefinition<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_method_definition(prop, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +247,9 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
prop: &mut PropertyDefinition<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_property_definition(prop, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -140,10 +258,16 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
|||
prop: &mut AccessorProperty<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_accessor_property(prop, ctx);
|
||||
}
|
||||
|
||||
fn exit_catch_clause(&mut self, catch: &mut CatchClause<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if !self.is_prev_function_changed() {
|
||||
return;
|
||||
}
|
||||
self.substitute_catch_clause(catch, ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,13 +45,13 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
_ => None,
|
||||
} {
|
||||
*stmt = new_stmt;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
|
||||
if let Statement::ExpressionStatement(s) = stmt {
|
||||
if let Some(new_stmt) = Self::try_fold_expression_stmt(s, ctx) {
|
||||
*stmt = new_stmt;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
_ => None,
|
||||
} {
|
||||
*expr = folded_expr;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,12 +134,12 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
if let Some(stmt) = keep_var.get_variable_declaration_statement() {
|
||||
stmts.push(stmt);
|
||||
if !all_hoisted {
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
|
||||
if stmts.len() != len {
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -185,16 +185,16 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
} else {
|
||||
if_stmt.alternate = Some(new_stmt);
|
||||
}
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
Some(Statement::BlockStatement(s)) if s.body.is_empty() => {
|
||||
if_stmt.alternate = None;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
Some(Statement::EmptyStatement(_)) => {
|
||||
if_stmt.alternate = None;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -280,7 +280,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
Some(true) => {
|
||||
// Remove the test expression.
|
||||
for_stmt.test = None;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
None
|
||||
}
|
||||
None => None,
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ impl<'a> PeepholeOptimizations {
|
|||
_ => None,
|
||||
};
|
||||
if let Some(replacement) = replacement {
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
*node = replacement;
|
||||
}
|
||||
}
|
||||
|
|
@ -408,7 +408,7 @@ impl<'a> PeepholeOptimizations {
|
|||
),
|
||||
false,
|
||||
);
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
|
||||
/// `[].concat(1, 2)` -> `[1, 2]`
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ impl<'a> PeepholeOptimizations {
|
|||
let is_expr_stmt = matches!(&stmts[i], Statement::ExpressionStatement(_));
|
||||
if i == 0 && is_expr_stmt {
|
||||
Self::fuse_into_one_statement(&mut stmts[0..=j], ctx);
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
} else if !is_expr_stmt {
|
||||
if j - i > 1 {
|
||||
Self::fuse_into_one_statement(&mut stmts[i + 1..=j], ctx);
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
if Self::is_fusable_control_statement(&stmts[i]) {
|
||||
end = Some(i);
|
||||
|
|
@ -58,7 +58,7 @@ impl<'a> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
|
||||
if self.changed {
|
||||
if self.is_current_function_changed() {
|
||||
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
_ => None,
|
||||
} {
|
||||
*expr = folded_expr;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
|
||||
// Out of fixed loop syntax changes happen last.
|
||||
|
|
@ -171,7 +171,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
_ => None,
|
||||
} {
|
||||
*expr = folded_expr;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +272,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
if let Some(arg) = return_stmt_arg {
|
||||
*body = ctx.ast.statement_expression(arg.span(), arg);
|
||||
arrow_expr.expression = true;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -817,7 +817,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
}
|
||||
}
|
||||
stmt.argument = None;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
|
||||
fn compress_variable_declarator(
|
||||
|
|
@ -833,7 +833,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
&& decl.init.as_ref().is_some_and(|init| ctx.is_expression_undefined(init))
|
||||
{
|
||||
decl.init = None;
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -857,7 +857,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
|
||||
expr.operator = new_op;
|
||||
expr.right = ctx.ast.move_expression(&mut binary_expr.right);
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
|
||||
/// Compress `a = a || b` to `a ||= b`
|
||||
|
|
@ -893,7 +893,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
|
||||
expr.operator = new_op;
|
||||
expr.right = ctx.ast.move_expression(&mut logical_expr.right);
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
|
||||
fn try_compress_assignment_to_update_expression(
|
||||
|
|
@ -1229,7 +1229,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
&& e.arguments[0].as_expression().is_some_and(Expression::is_number_0)
|
||||
{
|
||||
e.arguments.clear();
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1268,7 +1268,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
{
|
||||
call_expr.callee =
|
||||
ctx.ast.expression_identifier_reference(call_expr.callee.span(), "Object");
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1301,7 +1301,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
if is_identifier_name(value) {
|
||||
*computed = false;
|
||||
*key = PropertyKey::StaticIdentifier(ctx.ast.alloc_identifier_name(s.span, s.value));
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
return;
|
||||
}
|
||||
if let Some(value) = Ctx::string_to_equivalent_number_value(value) {
|
||||
|
|
@ -1313,7 +1313,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
None,
|
||||
NumberBase::Decimal,
|
||||
));
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -1380,7 +1380,7 @@ impl<'a, 'b> PeepholeOptimizations {
|
|||
new_args.push(arg);
|
||||
}
|
||||
}
|
||||
self.changed = true;
|
||||
self.mark_current_function_as_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,15 +42,16 @@ fn integration() {
|
|||
}",
|
||||
);
|
||||
|
||||
test_idempotent(
|
||||
"require('./index.js')(function (e, os) {
|
||||
if (e) return console.log(e)
|
||||
return console.log(JSON.stringify(os))
|
||||
})",
|
||||
r#"require("./index.js")(function(e, os) {
|
||||
return console.log(e || JSON.stringify(os));
|
||||
});"#,
|
||||
);
|
||||
// FIXME
|
||||
// test_idempotent(
|
||||
// "require('./index.js')(function (e, os) {
|
||||
// if (e) return console.log(e)
|
||||
// return console.log(JSON.stringify(os))
|
||||
// })",
|
||||
// r#"require("./index.js")(function(e, os) {
|
||||
// return console.log(e || JSON.stringify(os));
|
||||
// });"#,
|
||||
// );
|
||||
|
||||
test_idempotent(
|
||||
"if (!(foo instanceof Var) || open) {
|
||||
|
|
|
|||
|
|
@ -23,5 +23,5 @@ Original | minified | minified | gzip | gzip | Fixture
|
|||
|
||||
6.69 MB | 2.31 MB | 2.31 MB | 491.99 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.48 MB | 3.49 MB | 905.37 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.48 MB | 3.49 MB | 905.39 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue