Skip to content

Commit

Permalink
refactor(minifier): ast passes infrastructure
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Aug 4, 2024
1 parent 73d2558 commit e1abb5a
Show file tree
Hide file tree
Showing 35 changed files with 1,130 additions and 5,628 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,9 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for NumericLiteral<'a> {
let bytes = result.as_str();
p.print_str(bytes);
need_space_before_dot(bytes, p);
} else if self.value == f64::INFINITY {
p.print_str("Infinity");
need_space_before_dot("Infinity", p);
} else {
p.print_str(self.raw);
need_space_before_dot(self.raw, p);
Expand Down Expand Up @@ -1661,7 +1664,7 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for ArrowFunctionExpression<'a> {
p.print_str("=>");
p.print_soft_space();
if self.expression {
if let Statement::ExpressionStatement(stmt) = &self.body.statements[0] {
if let Some(Statement::ExpressionStatement(stmt)) = &self.body.statements.first() {
p.start_of_arrow_expr = p.code_len();
stmt.expression.gen_expr(p, Precedence::Comma, ctx.and_forbid_in(true));
}
Expand Down
34 changes: 0 additions & 34 deletions crates/oxc_minifier/examples/dce.rs

This file was deleted.

83 changes: 83 additions & 0 deletions crates/oxc_minifier/src/ast_passes/collapse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use oxc_allocator::Vec;
use oxc_ast::{ast::*, visit::walk_mut, AstBuilder, VisitMut};

use crate::CompressOptions;

/// Collapse variable declarations (TODO: and assignments).
///
/// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2`
/// TODO: `a = null; b = null;` => `a = b = null`
pub struct Collapse<'a> {
ast: AstBuilder<'a>,
options: CompressOptions,
}

impl<'a> VisitMut<'a> for Collapse<'a> {
fn visit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
if self.options.join_vars {
self.join_vars(stmts);
}

walk_mut::walk_statements(self, stmts);
}
}

impl<'a> Collapse<'a> {
pub fn new(ast: AstBuilder<'a>, options: CompressOptions) -> Self {
Self { ast, options }
}

pub fn build(&mut self, program: &mut Program<'a>) {
self.visit_program(program);
}

/// Join consecutive var statements
fn join_vars(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
// Collect all the consecutive ranges that contain joinable vars.
// This is required because Rust prevents in-place vec mutation.
let mut ranges = vec![];
let mut range = 0..0;
let mut i = 1usize;
let mut capacity = 0usize;
for window in stmts.windows(2) {
let [prev, cur] = window else { unreachable!() };
if let (
Statement::VariableDeclaration(cur_decl),
Statement::VariableDeclaration(prev_decl),
) = (cur, prev)
{
if cur_decl.kind == prev_decl.kind {
if i - 1 != range.end {
range.start = i - 1;
}
range.end = i + 1;
}
}
if (range.end != i || i == stmts.len() - 1) && range.start < range.end {
capacity += range.end - range.start - 1;
ranges.push(range.clone());
range = 0..0;
}
i += 1;
}

if ranges.is_empty() {
return;
}

// Reconstruct the stmts array by joining consecutive ranges
let mut new_stmts = self.ast.vec_with_capacity(stmts.len() - capacity);
for (i, stmt) in stmts.drain(..).enumerate() {
if i > 0 && ranges.iter().any(|range| range.contains(&(i - 1)) && range.contains(&i)) {
if let Statement::VariableDeclaration(prev_decl) = new_stmts.last_mut().unwrap() {
if let Statement::VariableDeclaration(mut cur_decl) = stmt {
prev_decl.declarations.append(&mut cur_decl.declarations);
}
}
} else {
new_stmts.push(stmt);
}
}
*stmts = new_stmts;
}
}
Loading

0 comments on commit e1abb5a

Please sign in to comment.