refactor(minifier): improve ast passes (#7518)

This commit is contained in:
Boshen 2024-11-28 09:33:31 +00:00
parent 32f860d238
commit 625a5bad02
12 changed files with 145 additions and 127 deletions

View file

@ -9,14 +9,10 @@ use crate::CompressorPass;
/// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2`
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/CollapseVariableDeclarations.java>
pub struct CollapseVariableDeclarations {
changed: bool,
pub(crate) changed: bool,
}
impl<'a> CompressorPass<'a> for CollapseVariableDeclarations {
fn changed(&self) -> bool {
self.changed
}
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);

View file

@ -7,14 +7,10 @@ use crate::CompressorPass;
///
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/ExploitAssigns.java>
pub struct ExploitAssigns {
changed: bool,
pub(crate) changed: bool,
}
impl<'a> CompressorPass<'a> for ExploitAssigns {
fn changed(&self) -> bool {
self.changed
}
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);

View file

@ -23,13 +23,46 @@ use oxc_ast::ast::*;
use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx};
pub trait CompressorPass<'a>: Traverse<'a> {
fn changed(&self) -> bool;
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>);
}
// See `peepholeOptimizationsOnce`
// For pass:
// ```
// if (options.collapseVariableDeclarations) {
// passes.maybeAdd(exploitAssign);
// passes.maybeAdd(collapseVariableDeclarations);
// }
// ```
pub struct CollapsePass {
_x0_exploit_assigns: ExploitAssigns,
x1_collapse_variable_declarations: CollapseVariableDeclarations,
}
impl CollapsePass {
pub fn new() -> Self {
Self {
_x0_exploit_assigns: ExploitAssigns::new(),
x1_collapse_variable_declarations: CollapseVariableDeclarations::new(),
}
}
}
impl<'a> CompressorPass<'a> for CollapsePass {
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
traverse_mut_with_ctx(self, program, ctx);
}
}
impl<'a> Traverse<'a> for CollapsePass {
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.x1_collapse_variable_declarations.enter_statements(stmts, ctx);
}
}
// See `latePeepholeOptimizations`
pub struct EarlyPass {
pub struct LatePeepholeOptimizations {
x0_statement_fusion: StatementFusion,
x1_peephole_remove_dead_code: PeepholeRemoveDeadCode,
// TODO: MinimizeExitPoints
@ -37,10 +70,9 @@ pub struct EarlyPass {
x3_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax,
x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods,
x5_peephole_fold_constants: PeepholeFoldConstants,
changed: bool,
}
impl EarlyPass {
impl LatePeepholeOptimizations {
pub fn new() -> Self {
Self {
x0_statement_fusion: StatementFusion::new(),
@ -51,29 +83,55 @@ impl EarlyPass {
),
x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods::new(),
x5_peephole_fold_constants: PeepholeFoldConstants::new(),
changed: false,
}
}
fn reset_changed(&mut self) {
self.x0_statement_fusion.changed = false;
self.x1_peephole_remove_dead_code.changed = false;
self.x2_peephole_minimize_conditions.changed = false;
self.x3_peephole_substitute_alternate_syntax.changed = false;
self.x4_peephole_replace_known_methods.changed = false;
self.x5_peephole_fold_constants.changed = false;
}
fn changed(&self) -> bool {
self.x0_statement_fusion.changed
|| self.x1_peephole_remove_dead_code.changed
|| self.x2_peephole_minimize_conditions.changed
|| self.x3_peephole_substitute_alternate_syntax.changed
|| self.x4_peephole_replace_known_methods.changed
|| self.x5_peephole_fold_constants.changed
}
pub fn run_in_loop<'a>(
&mut self,
program: &mut Program<'a>,
ctx: &mut ReusableTraverseCtx<'a>,
) {
let mut i = 0;
loop {
self.reset_changed();
self.build(program, ctx);
if !self.changed() {
break;
}
if i > 10 {
debug_assert!(false, "Ran loop more than 10 times.");
break;
}
i += 1;
}
}
}
impl<'a> CompressorPass<'a> for EarlyPass {
fn changed(&self) -> bool {
self.changed
}
impl<'a> CompressorPass<'a> for LatePeepholeOptimizations {
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);
self.changed = self.x0_statement_fusion.changed()
|| self.x1_peephole_remove_dead_code.changed()
|| self.x2_peephole_minimize_conditions.changed()
|| self.x3_peephole_substitute_alternate_syntax.changed()
|| self.x4_peephole_replace_known_methods.changed()
|| self.x5_peephole_fold_constants.changed();
}
}
impl<'a> Traverse<'a> for EarlyPass {
impl<'a> Traverse<'a> for LatePeepholeOptimizations {
fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
self.x1_peephole_remove_dead_code.enter_statement(stmt, ctx);
}
@ -141,49 +199,56 @@ impl<'a> Traverse<'a> for EarlyPass {
}
}
// Passes listed in `getFinalization` in `DefaultPassConfig`
pub struct LatePass {
x0_exploit_assigns: ExploitAssigns,
x1_collapse_variable_declarations: CollapseVariableDeclarations,
x2_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax,
changed: bool,
// See `createPeepholeOptimizationsPass`
pub struct PeepholeOptimizations {
// TODO: MinimizeExitPoints
x2_peephole_minimize_conditions: PeepholeMinimizeConditions,
x3_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax,
x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods,
x5_peephole_remove_dead_code: PeepholeRemoveDeadCode,
x6_peephole_fold_constants: PeepholeFoldConstants,
}
impl LatePass {
impl PeepholeOptimizations {
pub fn new() -> Self {
Self {
x0_exploit_assigns: ExploitAssigns::new(),
x1_collapse_variable_declarations: CollapseVariableDeclarations::new(),
x2_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new(
x2_peephole_minimize_conditions: PeepholeMinimizeConditions::new(),
x3_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new(
/* in_fixed_loop */ false,
),
changed: false,
x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods::new(),
x5_peephole_remove_dead_code: PeepholeRemoveDeadCode::new(),
x6_peephole_fold_constants: PeepholeFoldConstants::new(),
}
}
}
impl<'a> CompressorPass<'a> for LatePass {
fn changed(&self) -> bool {
self.changed
}
impl<'a> CompressorPass<'a> for PeepholeOptimizations {
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);
self.changed = self.x0_exploit_assigns.changed()
|| self.x0_exploit_assigns.changed()
|| self.x1_collapse_variable_declarations.changed()
|| self.x2_peephole_substitute_alternate_syntax.changed();
}
}
impl<'a> Traverse<'a> for LatePass {
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.x1_collapse_variable_declarations.enter_statements(stmts, ctx);
impl<'a> Traverse<'a> for PeepholeOptimizations {
fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
self.x5_peephole_remove_dead_code.enter_statement(stmt, ctx);
}
fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
self.x2_peephole_minimize_conditions.exit_statement(stmt, ctx);
self.x5_peephole_remove_dead_code.exit_statement(stmt, ctx);
}
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.x5_peephole_remove_dead_code.exit_program(program, ctx);
}
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.x5_peephole_remove_dead_code.exit_statements(stmts, ctx);
}
fn exit_return_statement(&mut self, stmt: &mut ReturnStatement<'a>, ctx: &mut TraverseCtx<'a>) {
self.x2_peephole_substitute_alternate_syntax.exit_return_statement(stmt, ctx);
self.x3_peephole_substitute_alternate_syntax.exit_return_statement(stmt, ctx);
}
fn enter_variable_declaration(
@ -191,23 +256,27 @@ impl<'a> Traverse<'a> for LatePass {
decl: &mut VariableDeclaration<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.x2_peephole_substitute_alternate_syntax.enter_variable_declaration(decl, ctx);
}
fn enter_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x2_peephole_substitute_alternate_syntax.enter_call_expression(expr, ctx);
}
fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x2_peephole_substitute_alternate_syntax.exit_call_expression(expr, ctx);
self.x3_peephole_substitute_alternate_syntax.enter_variable_declaration(decl, ctx);
}
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x2_peephole_substitute_alternate_syntax.enter_expression(expr, ctx);
self.x3_peephole_substitute_alternate_syntax.enter_expression(expr, ctx);
self.x4_peephole_replace_known_methods.enter_expression(expr, ctx);
}
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x2_peephole_substitute_alternate_syntax.exit_expression(expr, ctx);
self.x2_peephole_minimize_conditions.exit_expression(expr, ctx);
self.x3_peephole_substitute_alternate_syntax.exit_expression(expr, ctx);
self.x5_peephole_remove_dead_code.exit_expression(expr, ctx);
self.x6_peephole_fold_constants.exit_expression(expr, ctx);
}
fn enter_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x3_peephole_substitute_alternate_syntax.enter_call_expression(expr, ctx);
}
fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x3_peephole_substitute_alternate_syntax.exit_call_expression(expr, ctx);
}
fn enter_binary_expression(
@ -215,6 +284,6 @@ impl<'a> Traverse<'a> for LatePass {
expr: &mut BinaryExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.x2_peephole_substitute_alternate_syntax.enter_binary_expression(expr, ctx);
self.x3_peephole_substitute_alternate_syntax.enter_binary_expression(expr, ctx);
}
}

View file

@ -16,14 +16,10 @@ use crate::{node_util::Ctx, CompressorPass};
///
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
pub struct PeepholeFoldConstants {
changed: bool,
pub(crate) changed: bool,
}
impl<'a> CompressorPass<'a> for PeepholeFoldConstants {
fn changed(&self) -> bool {
self.changed
}
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);

View file

@ -13,14 +13,10 @@ use crate::CompressorPass;
///
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java>
pub struct PeepholeMinimizeConditions {
changed: bool,
pub(crate) changed: bool,
}
impl<'a> CompressorPass<'a> for PeepholeMinimizeConditions {
fn changed(&self) -> bool {
self.changed
}
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);

View file

@ -14,14 +14,10 @@ use crate::{keep_var::KeepVar, CompressorPass};
/// See `KeepVar` at the end of this file for `var` hoisting logic.
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeRemoveDeadCode.java>
pub struct PeepholeRemoveDeadCode {
changed: bool,
pub(crate) changed: bool,
}
impl<'a> CompressorPass<'a> for PeepholeRemoveDeadCode {
fn changed(&self) -> bool {
self.changed
}
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);

View file

@ -11,14 +11,10 @@ use crate::{node_util::Ctx, CompressorPass};
/// Minimize With Known Methods
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeReplaceKnownMethods.java>
pub struct PeepholeReplaceKnownMethods {
changed: bool,
pub(crate) changed: bool,
}
impl<'a> CompressorPass<'a> for PeepholeReplaceKnownMethods {
fn changed(&self) -> bool {
self.changed
}
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);

View file

@ -24,14 +24,10 @@ pub struct PeepholeSubstituteAlternateSyntax {
// states
in_define_export: bool,
changed: bool,
pub(crate) changed: bool,
}
impl<'a> CompressorPass<'a> for PeepholeSubstituteAlternateSyntax {
fn changed(&self) -> bool {
self.changed
}
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);

View file

@ -15,10 +15,6 @@ pub struct RemoveSyntax {
}
impl<'a> CompressorPass<'a> for RemoveSyntax {
fn changed(&self) -> bool {
false
}
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
traverse_mut_with_ctx(self, program, ctx);
}

View file

@ -12,14 +12,10 @@ use crate::CompressorPass;
///
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/StatementFusion.java>
pub struct StatementFusion {
changed: bool,
pub(crate) changed: bool,
}
impl<'a> CompressorPass<'a> for StatementFusion {
fn changed(&self) -> bool {
self.changed
}
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {
self.changed = false;
traverse_mut_with_ctx(self, program, ctx);

View file

@ -5,8 +5,8 @@ use oxc_traverse::ReusableTraverseCtx;
use crate::{
ast_passes::{
EarlyPass, LatePass, PeepholeFoldConstants, PeepholeMinimizeConditions,
PeepholeRemoveDeadCode, RemoveSyntax,
CollapsePass, LatePeepholeOptimizations, PeepholeFoldConstants, PeepholeMinimizeConditions,
PeepholeOptimizations, PeepholeRemoveDeadCode, RemoveSyntax,
},
CompressOptions, CompressorPass,
};
@ -41,25 +41,10 @@ impl<'a> Compressor<'a> {
return;
}
let mut i = 0;
loop {
let mut changed = false;
let mut pass = EarlyPass::new();
pass.build(program, &mut ctx);
if pass.changed() {
changed = true;
}
if !changed {
break;
}
if i > 50 {
debug_assert!(false, "Ran in a infinite loop.");
break;
}
i += 1;
}
LatePass::new().build(program, &mut ctx);
PeepholeOptimizations::new().build(program, &mut ctx);
CollapsePass::new().build(program, &mut ctx);
LatePeepholeOptimizations::new().run_in_loop(program, &mut ctx);
PeepholeOptimizations::new().build(program, &mut ctx);
}
fn dead_code_elimination(program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) {

View file

@ -4,7 +4,7 @@ Original | Minified | esbuild | Gzip | esbuild
173.90 kB | 61.16 kB | 59.82 kB | 19.62 kB | 19.33 kB | moment.js
287.63 kB | 91.70 kB | 90.07 kB | 32.35 kB | 31.95 kB | jquery.js
287.63 kB | 91.70 kB | 90.07 kB | 32.34 kB | 31.95 kB | jquery.js
342.15 kB | 120.23 kB | 118.14 kB | 44.72 kB | 44.37 kB | vue.js
@ -12,15 +12,15 @@ Original | Minified | esbuild | Gzip | esbuild
555.77 kB | 275.23 kB | 270.13 kB | 91.33 kB | 90.80 kB | d3.js
1.01 MB | 464.89 kB | 458.89 kB | 127.05 kB | 126.71 kB | bundle.min.js
1.01 MB | 464.89 kB | 458.89 kB | 127.04 kB | 126.71 kB | bundle.min.js
1.25 MB | 660.45 kB | 646.76 kB | 164.57 kB | 163.73 kB | three.js
1.25 MB | 660.45 kB | 646.76 kB | 164.46 kB | 163.73 kB | three.js
2.14 MB | 739.98 kB | 724.14 kB | 181.63 kB | 181.07 kB | victory.js
3.20 MB | 1.02 MB | 1.01 MB | 333.05 kB | 331.56 kB | echarts.js
3.20 MB | 1.02 MB | 1.01 MB | 333.04 kB | 331.56 kB | echarts.js
6.69 MB | 2.39 MB | 2.31 MB | 496.76 kB | 488.28 kB | antd.js
6.69 MB | 2.39 MB | 2.31 MB | 496.74 kB | 488.28 kB | antd.js
10.95 MB | 3.55 MB | 3.49 MB | 913.60 kB | 915.50 kB | typescript.js
10.95 MB | 3.55 MB | 3.49 MB | 913.59 kB | 915.50 kB | typescript.js