mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(minifier): loop compressor passes (#6013)
This commit is contained in:
parent
18371dda3d
commit
5c323a2105
13 changed files with 212 additions and 128 deletions
|
|
@ -27,8 +27,9 @@ fn main() -> std::io::Result<()> {
|
|||
println!("{printed}");
|
||||
|
||||
if twice {
|
||||
let printed = minify(&printed, source_type, mangle);
|
||||
println!("{printed}");
|
||||
let printed2 = minify(&printed, source_type, mangle);
|
||||
println!("{printed2}");
|
||||
println!("same = {}", printed == printed2);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -9,25 +9,37 @@ use crate::{CompressOptions, CompressorPass};
|
|||
/// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2`
|
||||
pub struct CollapseVariableDeclarations {
|
||||
options: CompressOptions,
|
||||
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for CollapseVariableDeclarations {}
|
||||
impl<'a> CompressorPass<'a> for CollapseVariableDeclarations {
|
||||
fn changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.changed = false;
|
||||
oxc_traverse::walk_program(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for CollapseVariableDeclarations {
|
||||
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.join_vars {
|
||||
self.join_vars(stmts, ctx);
|
||||
}
|
||||
self.join_vars(stmts, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CollapseVariableDeclarations {
|
||||
pub fn new(options: CompressOptions) -> Self {
|
||||
Self { options }
|
||||
Self { options, changed: false }
|
||||
}
|
||||
|
||||
/// Join consecutive var statements
|
||||
fn join_vars(&self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
fn join_vars(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.join_vars {
|
||||
return;
|
||||
}
|
||||
// Collect all the consecutive ranges that contain joinable vars.
|
||||
// This is required because Rust prevents in-place vec mutation.
|
||||
let mut ranges = vec![];
|
||||
|
|
@ -83,6 +95,7 @@ impl<'a> CollapseVariableDeclarations {
|
|||
}
|
||||
}
|
||||
*stmts = new_stmts;
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,31 @@
|
|||
use oxc_traverse::Traverse;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::CompressorPass;
|
||||
|
||||
/// Tries to chain assignments together.
|
||||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/ExploitAssigns.java>
|
||||
pub struct ExploitAssigns;
|
||||
pub struct ExploitAssigns {
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for ExploitAssigns {}
|
||||
impl<'a> CompressorPass<'a> for ExploitAssigns {
|
||||
fn changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.changed = false;
|
||||
oxc_traverse::walk_program(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for ExploitAssigns {}
|
||||
|
||||
impl ExploitAssigns {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
Self { changed: false }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub use statement_fusion::StatementFusion;
|
|||
|
||||
use oxc_ast::ast::Program;
|
||||
use oxc_semantic::{ScopeTree, SymbolTable};
|
||||
use oxc_traverse::{walk_program, Traverse, TraverseCtx};
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::node_util::NodeUtil;
|
||||
|
||||
|
|
@ -33,11 +33,7 @@ impl<'a> NodeUtil for TraverseCtx<'a> {
|
|||
}
|
||||
|
||||
pub trait CompressorPass<'a>: Traverse<'a> {
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>)
|
||||
where
|
||||
Self: Traverse<'a>,
|
||||
Self: Sized,
|
||||
{
|
||||
walk_program(self, program, ctx);
|
||||
}
|
||||
fn changed(&self) -> bool;
|
||||
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,19 @@ use crate::{
|
|||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
|
||||
pub struct PeepholeFoldConstants {
|
||||
evaluate: bool,
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for PeepholeFoldConstants {}
|
||||
impl<'a> CompressorPass<'a> for PeepholeFoldConstants {
|
||||
fn changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.changed = false;
|
||||
oxc_traverse::walk_program(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for PeepholeFoldConstants {
|
||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
|
|
@ -35,17 +44,12 @@ impl<'a> Traverse<'a> for PeepholeFoldConstants {
|
|||
|
||||
impl<'a> PeepholeFoldConstants {
|
||||
pub fn new() -> Self {
|
||||
Self { evaluate: false }
|
||||
}
|
||||
|
||||
pub fn with_evaluate(mut self, yes: bool) -> Self {
|
||||
self.evaluate = yes;
|
||||
self
|
||||
Self { changed: false }
|
||||
}
|
||||
|
||||
// [optimizeSubtree](https://github.com/google/closure-compiler/blob/75335a5138dde05030747abfd3c852cd34ea7429/src/com/google/javascript/jscomp/PeepholeFoldConstants.java#L72)
|
||||
// TODO: tryReduceOperandsForOp
|
||||
pub fn fold_expression(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
pub fn fold_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if let Some(folded_expr) = match expr {
|
||||
Expression::BinaryExpression(e) => self.try_fold_binary_operator(e, ctx),
|
||||
Expression::LogicalExpression(e)
|
||||
|
|
@ -60,6 +64,7 @@ impl<'a> PeepholeFoldConstants {
|
|||
_ => None,
|
||||
} {
|
||||
*expr = folded_expr;
|
||||
self.changed = true;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -92,13 +97,7 @@ impl<'a> PeepholeFoldConstants {
|
|||
&binary_expr.right,
|
||||
ctx,
|
||||
),
|
||||
// NOTE: string concat folding breaks our current evaluation of Test262 tests. The
|
||||
// minifier is tested by comparing output of running the minifier once and twice,
|
||||
// respectively. Since Test262Error messages include string concats, the outputs
|
||||
// don't match (even though the produced code is valid). Additionally, We'll likely
|
||||
// want to add `evaluate` checks for all constant folding, not just additions, but
|
||||
// we're adding this here until a decision is made.
|
||||
BinaryOperator::Addition if self.evaluate => {
|
||||
BinaryOperator::Addition => {
|
||||
self.try_fold_addition(binary_expr.span, &binary_expr.left, &binary_expr.right, ctx)
|
||||
}
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -10,9 +10,20 @@ use crate::CompressorPass;
|
|||
/// with `? :` and short-circuit binary operators.
|
||||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java>
|
||||
pub struct PeepholeMinimizeConditions;
|
||||
pub struct PeepholeMinimizeConditions {
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for PeepholeMinimizeConditions {}
|
||||
impl<'a> CompressorPass<'a> for PeepholeMinimizeConditions {
|
||||
fn changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.changed = false;
|
||||
oxc_traverse::walk_program(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for PeepholeMinimizeConditions {
|
||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
|
|
@ -21,13 +32,14 @@ impl<'a> Traverse<'a> for PeepholeMinimizeConditions {
|
|||
_ => None,
|
||||
} {
|
||||
*expr = folded_expr;
|
||||
self.changed = true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PeepholeMinimizeConditions {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
Self { changed: false }
|
||||
}
|
||||
|
||||
/// Try to minimize NOT nodes such as `!(x==y)`.
|
||||
|
|
|
|||
|
|
@ -10,17 +10,30 @@ use crate::{keep_var::KeepVar, node_util::NodeUtil, tri::Tri, CompressorPass};
|
|||
/// Terser option: `dead_code: true`.
|
||||
///
|
||||
/// See `KeepVar` at the end of this file for `var` hoisting logic.
|
||||
pub struct PeepholeRemoveDeadCode;
|
||||
pub struct PeepholeRemoveDeadCode {
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for PeepholeRemoveDeadCode {}
|
||||
impl<'a> CompressorPass<'a> for PeepholeRemoveDeadCode {
|
||||
fn changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.changed = false;
|
||||
oxc_traverse::walk_program(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for PeepholeRemoveDeadCode {
|
||||
fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
Self::fold_if_statement(stmt, ctx);
|
||||
self.fold_if_statement(stmt, ctx);
|
||||
}
|
||||
|
||||
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
|
||||
if stmts.iter().any(|stmt| matches!(stmt, Statement::EmptyStatement(_))) {
|
||||
stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_)));
|
||||
}
|
||||
self.dead_code_elimination(stmts, ctx);
|
||||
}
|
||||
|
||||
|
|
@ -30,13 +43,14 @@ impl<'a> Traverse<'a> for PeepholeRemoveDeadCode {
|
|||
_ => None,
|
||||
} {
|
||||
*expr = folded_expr;
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PeepholeRemoveDeadCode {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
Self { changed: false }
|
||||
}
|
||||
|
||||
/// Removes dead code thats comes after `return` statements after inlining `if` statements
|
||||
|
|
@ -76,6 +90,7 @@ impl<'a> PeepholeRemoveDeadCode {
|
|||
}
|
||||
|
||||
let mut i = 0;
|
||||
let len = stmts.len();
|
||||
stmts.retain(|s| {
|
||||
i += 1;
|
||||
if i - 1 <= index {
|
||||
|
|
@ -88,25 +103,35 @@ impl<'a> PeepholeRemoveDeadCode {
|
|||
false
|
||||
});
|
||||
|
||||
let all_hoisted = keep_var.all_hoisted();
|
||||
if let Some(stmt) = keep_var.get_variable_declaration_statement() {
|
||||
stmts.push(stmt);
|
||||
if !all_hoisted {
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if stmts.len() != len {
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_if_statement(stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
fn fold_if_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
let Statement::IfStatement(if_stmt) = stmt else { return };
|
||||
|
||||
// Descend and remove `else` blocks first.
|
||||
if let Some(alternate) = &mut if_stmt.alternate {
|
||||
Self::fold_if_statement(alternate, ctx);
|
||||
self.fold_if_statement(alternate, ctx);
|
||||
if matches!(alternate, Statement::EmptyStatement(_)) {
|
||||
if_stmt.alternate = None;
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
match ctx.get_boolean_value(&if_stmt.test) {
|
||||
Tri::True => {
|
||||
*stmt = ctx.ast.move_statement(&mut if_stmt.consequent);
|
||||
self.changed = true;
|
||||
}
|
||||
Tri::False => {
|
||||
*stmt = if let Some(alternate) = &mut if_stmt.alternate {
|
||||
|
|
@ -119,6 +144,7 @@ impl<'a> PeepholeRemoveDeadCode {
|
|||
.get_variable_declaration_statement()
|
||||
.unwrap_or_else(|| ctx.ast.statement_empty(SPAN))
|
||||
};
|
||||
self.changed = true;
|
||||
}
|
||||
Tri::Unknown => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,19 @@ use crate::{node_util::NodeUtil, CompressOptions, CompressorPass};
|
|||
pub struct PeepholeSubstituteAlternateSyntax {
|
||||
options: CompressOptions,
|
||||
in_define_export: bool,
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for PeepholeSubstituteAlternateSyntax {}
|
||||
impl<'a> CompressorPass<'a> for PeepholeSubstituteAlternateSyntax {
|
||||
fn changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.changed = false;
|
||||
oxc_traverse::walk_program(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
|
||||
fn enter_statement(&mut self, stmt: &mut Statement<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
|
|
@ -30,7 +40,7 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
|
|||
_ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
// We may fold `void 1` to `void 0`, so compress it after visiting
|
||||
Self::compress_return_statement(stmt);
|
||||
self.compress_return_statement(stmt);
|
||||
}
|
||||
|
||||
fn enter_variable_declaration(
|
||||
|
|
@ -39,7 +49,7 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
|
|||
_ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
for declarator in decl.declarations.iter_mut() {
|
||||
Self::compress_variable_declarator(declarator);
|
||||
self.compress_variable_declarator(declarator);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +93,7 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
|
|||
|
||||
impl<'a> PeepholeSubstituteAlternateSyntax {
|
||||
pub fn new(options: CompressOptions) -> Self {
|
||||
Self { options, in_define_export: false }
|
||||
Self { options, in_define_export: false, changed: false }
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
|
@ -120,14 +130,14 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
|
|||
|
||||
/// Remove block from single line blocks
|
||||
/// `{ block } -> block`
|
||||
#[allow(clippy::only_used_in_recursion)] // `&self` is only used in recursion
|
||||
fn compress_block(&self, stmt: &mut Statement<'a>) {
|
||||
fn compress_block(&mut self, stmt: &mut Statement<'a>) {
|
||||
if let Statement::BlockStatement(block) = stmt {
|
||||
// Avoid compressing `if (x) { var x = 1 }` to `if (x) var x = 1` due to different
|
||||
// semantics according to AnnexB, which lead to different semantics.
|
||||
if block.body.len() == 1 && !block.body[0].is_declaration() {
|
||||
*stmt = block.body.remove(0);
|
||||
self.compress_block(stmt);
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -230,18 +240,20 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
|
|||
///
|
||||
/// `return undefined` -> `return`
|
||||
/// `return void 0` -> `return`
|
||||
fn compress_return_statement(stmt: &mut ReturnStatement<'a>) {
|
||||
fn compress_return_statement(&mut self, stmt: &mut ReturnStatement<'a>) {
|
||||
if stmt.argument.as_ref().is_some_and(|expr| expr.is_undefined() || expr.is_void_0()) {
|
||||
stmt.argument = None;
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn compress_variable_declarator(decl: &mut VariableDeclarator<'a>) {
|
||||
fn compress_variable_declarator(&mut self, decl: &mut VariableDeclarator<'a>) {
|
||||
if decl.kind.is_const() {
|
||||
return;
|
||||
}
|
||||
if decl.init.as_ref().is_some_and(|init| init.is_undefined() || init.is_void_0()) {
|
||||
decl.init = None;
|
||||
self.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,15 @@ pub struct RemoveSyntax {
|
|||
options: CompressOptions,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for RemoveSyntax {}
|
||||
impl<'a> CompressorPass<'a> for RemoveSyntax {
|
||||
fn changed(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
oxc_traverse::walk_program(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for RemoveSyntax {
|
||||
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) {
|
||||
|
|
|
|||
|
|
@ -10,32 +10,43 @@ use crate::CompressorPass;
|
|||
/// Tries to fuse all the statements in a block into a one statement by using COMMAs or statements.
|
||||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/StatementFusion.java>
|
||||
pub struct StatementFusion;
|
||||
pub struct StatementFusion {
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl<'a> CompressorPass<'a> for StatementFusion {}
|
||||
impl<'a> CompressorPass<'a> for StatementFusion {
|
||||
fn changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.changed = false;
|
||||
oxc_traverse::walk_program(self, program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for StatementFusion {
|
||||
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
Self::fuse_statements(&mut program.body, ctx);
|
||||
self.fuse_statements(&mut program.body, ctx);
|
||||
}
|
||||
|
||||
fn exit_function_body(&mut self, body: &mut FunctionBody<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
Self::fuse_statements(&mut body.statements, ctx);
|
||||
self.fuse_statements(&mut body.statements, ctx);
|
||||
}
|
||||
|
||||
fn exit_block_statement(&mut self, block: &mut BlockStatement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
Self::fuse_statements(&mut block.body, ctx);
|
||||
self.fuse_statements(&mut block.body, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StatementFusion {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
Self { changed: false }
|
||||
}
|
||||
|
||||
fn fuse_statements(stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
fn fuse_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
if Self::can_fuse_into_one_statement(stmts) {
|
||||
Self::fuse_into_one_statement(stmts, ctx);
|
||||
self.fuse_into_one_statement(stmts, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +86,11 @@ impl<'a> StatementFusion {
|
|||
}
|
||||
}
|
||||
|
||||
fn fuse_into_one_statement(stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
fn fuse_into_one_statement(
|
||||
&mut self,
|
||||
stmts: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let len = stmts.len();
|
||||
let mut expressions = ctx.ast.vec();
|
||||
|
||||
|
|
@ -103,6 +118,7 @@ impl<'a> StatementFusion {
|
|||
Self::fuse_expression_into_control_flow_statement(last, expressions, ctx);
|
||||
|
||||
*stmts = ctx.ast.vec1(ctx.ast.move_statement(last));
|
||||
self.changed = true;
|
||||
}
|
||||
|
||||
fn fuse_expression_into_control_flow_statement(
|
||||
|
|
|
|||
|
|
@ -35,69 +35,50 @@ impl<'a> Compressor<'a> {
|
|||
program: &mut Program<'a>,
|
||||
) {
|
||||
let mut ctx = TraverseCtx::new(scopes, symbols, self.allocator);
|
||||
self.remove_syntax(program, &mut ctx);
|
||||
RemoveSyntax::new(self.options).build(program, &mut ctx);
|
||||
|
||||
if self.options.dead_code_elimination {
|
||||
self.dead_code_elimination(program, &mut ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// earlyPeepholeOptimizations
|
||||
// TODO: MinimizeExitPoints
|
||||
self.minimize_conditions(program, &mut ctx);
|
||||
self.substitute_alternate_syntax(program, &mut ctx);
|
||||
// TODO: PeepholeReplaceKnownMethods
|
||||
self.remove_dead_code(program, &mut ctx);
|
||||
self.fold_constants(program, &mut ctx);
|
||||
ExploitAssigns::new().build(program, &mut ctx);
|
||||
CollapseVariableDeclarations::new(self.options).build(program, &mut ctx);
|
||||
|
||||
// latePeepholeOptimizations
|
||||
// TODO: StatementFusion
|
||||
self.remove_dead_code(program, &mut ctx);
|
||||
self.minimize_conditions(program, &mut ctx);
|
||||
self.substitute_alternate_syntax(program, &mut ctx);
|
||||
// TODO: PeepholeReplaceKnownMethods
|
||||
self.fold_constants(program, &mut ctx);
|
||||
// See `latePeepholeOptimizations`
|
||||
let mut passes: [&mut dyn CompressorPass; 5] = [
|
||||
&mut StatementFusion::new(),
|
||||
&mut PeepholeRemoveDeadCode::new(),
|
||||
// TODO: MinimizeExitPoints
|
||||
&mut PeepholeMinimizeConditions::new(),
|
||||
&mut PeepholeSubstituteAlternateSyntax::new(self.options),
|
||||
// TODO: PeepholeReplaceKnownMethods
|
||||
&mut PeepholeFoldConstants::new(),
|
||||
];
|
||||
|
||||
self.exploit_assigns(program, &mut ctx);
|
||||
self.collapse_variable_declarations(program, &mut ctx);
|
||||
let mut i = 0;
|
||||
loop {
|
||||
let mut changed = false;
|
||||
for pass in &mut passes {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
fn dead_code_elimination(self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.fold_constants(program, ctx);
|
||||
self.minimize_conditions(program, ctx);
|
||||
self.remove_dead_code(program, ctx);
|
||||
}
|
||||
|
||||
fn remove_syntax(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
RemoveSyntax::new(self.options).build(program, ctx);
|
||||
}
|
||||
|
||||
fn minimize_conditions(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
PeepholeFoldConstants::new().build(program, ctx);
|
||||
PeepholeMinimizeConditions::new().build(program, ctx);
|
||||
}
|
||||
|
||||
fn fold_constants(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
PeepholeFoldConstants::new().with_evaluate(self.options.evaluate).build(program, ctx);
|
||||
}
|
||||
|
||||
fn substitute_alternate_syntax(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
PeepholeSubstituteAlternateSyntax::new(self.options).build(program, ctx);
|
||||
}
|
||||
|
||||
fn remove_dead_code(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
PeepholeRemoveDeadCode::new().build(program, ctx);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn statement_fusion(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
StatementFusion::new().build(program, ctx);
|
||||
}
|
||||
|
||||
fn collapse_variable_declarations(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
CollapseVariableDeclarations::new(self.options).build(program, ctx);
|
||||
}
|
||||
|
||||
fn exploit_assigns(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
ExploitAssigns::new().build(program, ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use oxc_span::{Atom, Span, SPAN};
|
|||
pub struct KeepVar<'a> {
|
||||
ast: AstBuilder<'a>,
|
||||
vars: std::vec::Vec<(Atom<'a>, Span)>,
|
||||
all_hoisted: bool,
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for KeepVar<'a> {
|
||||
|
|
@ -37,6 +38,9 @@ impl<'a> Visit<'a> for KeepVar<'a> {
|
|||
decl.bound_names(&mut |ident| {
|
||||
self.vars.push((ident.name.clone(), ident.span));
|
||||
});
|
||||
if decl.has_init() {
|
||||
self.all_hoisted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -46,7 +50,11 @@ impl<'a> Visit<'a> for KeepVar<'a> {
|
|||
|
||||
impl<'a> KeepVar<'a> {
|
||||
pub fn new(ast: AstBuilder<'a>) -> Self {
|
||||
Self { ast, vars: std::vec![] }
|
||||
Self { ast, vars: std::vec![], all_hoisted: true }
|
||||
}
|
||||
|
||||
pub fn all_hoisted(&self) -> bool {
|
||||
self.all_hoisted
|
||||
}
|
||||
|
||||
pub fn get_variable_declaration_statement(self) -> Option<Statement<'a>> {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
Original | Minified | esbuild | Gzip | esbuild
|
||||
|
||||
72.14 kB | 24.32 kB | 23.70 kB | 8.71 kB | 8.54 kB | react.development.js
|
||||
72.14 kB | 24.47 kB | 23.70 kB | 8.65 kB | 8.54 kB | react.development.js
|
||||
|
||||
173.90 kB | 61.79 kB | 59.82 kB | 19.57 kB | 19.33 kB | moment.js
|
||||
173.90 kB | 61.71 kB | 59.82 kB | 19.56 kB | 19.33 kB | moment.js
|
||||
|
||||
287.63 kB | 92.89 kB | 90.07 kB | 32.32 kB | 31.95 kB | jquery.js
|
||||
287.63 kB | 92.83 kB | 90.07 kB | 32.29 kB | 31.95 kB | jquery.js
|
||||
|
||||
342.15 kB | 122.41 kB | 118.14 kB | 44.77 kB | 44.37 kB | vue.js
|
||||
342.15 kB | 124.14 kB | 118.14 kB | 44.81 kB | 44.37 kB | vue.js
|
||||
|
||||
544.10 kB | 73.54 kB | 72.48 kB | 26.13 kB | 26.20 kB | lodash.js
|
||||
544.10 kB | 74.13 kB | 72.48 kB | 26.23 kB | 26.20 kB | lodash.js
|
||||
|
||||
555.77 kB | 277.01 kB | 270.13 kB | 91.22 kB | 90.80 kB | d3.js
|
||||
555.77 kB | 278.71 kB | 270.13 kB | 91.40 kB | 90.80 kB | d3.js
|
||||
|
||||
1.01 MB | 468.10 kB | 458.89 kB | 126.67 kB | 126.71 kB | bundle.min.js
|
||||
1.01 MB | 470.11 kB | 458.89 kB | 126.97 kB | 126.71 kB | bundle.min.js
|
||||
|
||||
1.25 MB | 663.51 kB | 646.76 kB | 163.76 kB | 163.73 kB | three.js
|
||||
1.25 MB | 671.02 kB | 646.76 kB | 164.72 kB | 163.73 kB | three.js
|
||||
|
||||
2.14 MB | 742.01 kB | 724.14 kB | 181.59 kB | 181.07 kB | victory.js
|
||||
2.14 MB | 756.70 kB | 724.14 kB | 182.87 kB | 181.07 kB | victory.js
|
||||
|
||||
3.20 MB | 1.03 MB | 1.01 MB | 332.45 kB | 331.56 kB | echarts.js
|
||||
3.20 MB | 1.05 MB | 1.01 MB | 334.11 kB | 331.56 kB | echarts.js
|
||||
|
||||
6.69 MB | 2.39 MB | 2.31 MB | 496.45 kB | 488.28 kB | antd.js
|
||||
6.69 MB | 2.44 MB | 2.31 MB | 498.90 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.56 MB | 3.49 MB | 911.22 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.59 MB | 3.49 MB | 913.91 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue