Skip to content

Commit

Permalink
feat(minifier): optional catch binding when es target >= es2019
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Dec 29, 2024
1 parent d79dffb commit 601c0af
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 21 deletions.
16 changes: 14 additions & 2 deletions crates/oxc_minifier/src/ast_passes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub use peephole_substitute_alternate_syntax::PeepholeSubstituteAlternateSyntax;
pub use remove_syntax::RemoveSyntax;
pub use statement_fusion::StatementFusion;

use crate::CompressOptions;

pub trait CompressorPass<'a>: Traverse<'a> {
fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>);
}
Expand Down Expand Up @@ -76,14 +78,15 @@ pub struct LatePeepholeOptimizations {
}

impl LatePeepholeOptimizations {
pub fn new() -> Self {
pub fn new(options: CompressOptions) -> Self {
let in_fixed_loop = true;
Self {
x0_statement_fusion: StatementFusion::new(),
x1_collapse_variable_declarations: CollapseVariableDeclarations::new(),
x2_peephole_remove_dead_code: PeepholeRemoveDeadCode::new(),
x3_peephole_minimize_conditions: PeepholeMinimizeConditions::new(in_fixed_loop),
x4_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new(
options,
in_fixed_loop,
),
x5_peephole_replace_known_methods: PeepholeReplaceKnownMethods::new(),
Expand Down Expand Up @@ -194,6 +197,10 @@ impl<'a> Traverse<'a> for LatePeepholeOptimizations {
fn exit_property_key(&mut self, key: &mut PropertyKey<'a>, ctx: &mut TraverseCtx<'a>) {
self.x4_peephole_substitute_alternate_syntax.exit_property_key(key, ctx);
}

fn exit_catch_clause(&mut self, catch: &mut CatchClause<'a>, ctx: &mut TraverseCtx<'a>) {
self.x4_peephole_substitute_alternate_syntax.exit_catch_clause(catch, ctx);
}
}

// See `createPeepholeOptimizationsPass`
Expand All @@ -207,11 +214,12 @@ pub struct PeepholeOptimizations {
}

impl PeepholeOptimizations {
pub fn new() -> Self {
pub fn new(options: CompressOptions) -> Self {
let in_fixed_loop = false;
Self {
x2_peephole_minimize_conditions: PeepholeMinimizeConditions::new(in_fixed_loop),
x3_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new(
options,
in_fixed_loop,
),
x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods::new(),
Expand Down Expand Up @@ -273,6 +281,10 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
fn exit_property_key(&mut self, key: &mut PropertyKey<'a>, ctx: &mut TraverseCtx<'a>) {
self.x3_peephole_substitute_alternate_syntax.exit_property_key(key, ctx);
}

fn exit_catch_clause(&mut self, catch: &mut CatchClause<'a>, ctx: &mut TraverseCtx<'a>) {
self.x3_peephole_substitute_alternate_syntax.exit_catch_clause(catch, ctx);
}
}

pub struct DeadCodeElimination {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ use oxc_ecmascript::{ToInt32, ToJsString};
use oxc_semantic::IsGlobalReference;
use oxc_span::{GetSpan, SPAN};
use oxc_syntax::{
es_target::ESTarget,
identifier::is_identifier_name,
number::NumberBase,
operator::{BinaryOperator, UnaryOperator},
};
use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx};

use crate::{node_util::Ctx, CompressorPass};
use crate::{node_util::Ctx, CompressOptions, CompressorPass};

/// A peephole optimization that minimizes code by simplifying conditional
/// expressions, replacing IFs with HOOKs, replacing object constructors
/// with literals, and simplifying returns.
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.java>
pub struct PeepholeSubstituteAlternateSyntax {
options: CompressOptions,
/// Do not compress syntaxes that are hard to analyze inside the fixed loop.
/// e.g. Do not compress `undefined -> void 0`, `true` -> `!0`.
/// Opposite of `late` in Closure Compiler.
Expand Down Expand Up @@ -45,6 +47,10 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
self.compress_return_statement(stmt);
}

fn exit_catch_clause(&mut self, catch: &mut CatchClause<'a>, _ctx: &mut TraverseCtx<'a>) {
self.compress_catch_clause(catch);
}

fn exit_variable_declaration(
&mut self,
decl: &mut VariableDeclaration<'a>,
Expand Down Expand Up @@ -118,8 +124,8 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax {
}

impl<'a, 'b> PeepholeSubstituteAlternateSyntax {
pub fn new(in_fixed_loop: bool) -> Self {
Self { in_fixed_loop, in_define_export: false, changed: false }
pub fn new(options: CompressOptions, in_fixed_loop: bool) -> Self {
Self { options, in_fixed_loop, in_define_export: false, changed: false }
}

/* Utilities */
Expand Down Expand Up @@ -767,18 +773,33 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax {
ctx.ast.alloc_static_member_expression(e.span, object, property, false),
))
}

fn compress_catch_clause(&mut self, catch: &mut CatchClause<'a>) {
if catch.body.body.is_empty()
&& !self.in_fixed_loop
&& self.options.target >= ESTarget::ES2019
{
if let Some(param) = &catch.param {
if param.pattern.kind.is_binding_identifier() {
catch.param = None;
self.changed = true;
}
};
}
}
}

/// Port from <https://github.com/google/closure-compiler/blob/v20240609/test/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntaxTest.java>
#[cfg(test)]
mod test {
use oxc_allocator::Allocator;

use crate::tester;
use crate::{tester, CompressOptions};

fn test(source_text: &str, expected: &str) {
let allocator = Allocator::default();
let mut pass = super::PeepholeSubstituteAlternateSyntax::new(false);
let options = CompressOptions::default();
let mut pass = super::PeepholeSubstituteAlternateSyntax::new(options, false);
tester::test(&allocator, source_text, expected, &mut pass);
}

Expand All @@ -802,7 +823,7 @@ mod test {
test("var x = undefined", "var x");
test_same("var undefined = 1;function f() {var undefined=2;var x;}");
test("function f(undefined) {}", "function f(undefined){}");
test("try {} catch(undefined) {}", "try{}catch(undefined){}");
test("try {} catch(undefined) {foo}", "try{}catch(undefined){foo}");
test("for (undefined in {}) {}", "for(undefined in {}){}");
test("undefined++;", "undefined++");
test("undefined += undefined;", "undefined+=void 0");
Expand Down Expand Up @@ -1271,6 +1292,22 @@ mod test {
test_same("x['😊']");
}

#[test]
fn optional_catch_binding() {
test("try {} catch(e) {}", "try {} catch {}");
test_same("try {} catch([e]) {}");
test_same("try {} catch({e}) {}");

let allocator = Allocator::default();
let options = CompressOptions {
target: oxc_syntax::es_target::ESTarget::ES2018,
..CompressOptions::default()
};
let mut pass = super::PeepholeSubstituteAlternateSyntax::new(options, false);
let code = "try {} catch(e) {}";
tester::test(&allocator, code, code, &mut pass);
}

// ----------

/// Port from <https://github.com/google/closure-compiler/blob/v20240609/test/com/google/javascript/jscomp/ConvertToDottedPropertiesTest.java>
Expand Down
6 changes: 3 additions & 3 deletions crates/oxc_minifier/src/compressor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ impl<'a> Compressor<'a> {
let mut ctx = ReusableTraverseCtx::new(scopes, symbols, self.allocator);
RemoveSyntax::new(self.options).build(program, &mut ctx);
Normalize::new().build(program, &mut ctx);
PeepholeOptimizations::new().build(program, &mut ctx);
PeepholeOptimizations::new(self.options).build(program, &mut ctx);
CollapsePass::new().build(program, &mut ctx);
LatePeepholeOptimizations::new().run_in_loop(program, &mut ctx);
PeepholeOptimizations::new().build(program, &mut ctx);
LatePeepholeOptimizations::new(self.options).run_in_loop(program, &mut ctx);
PeepholeOptimizations::new(self.options).build(program, &mut ctx);
}

pub fn dead_code_elimination(self, program: &mut Program<'a>) {
Expand Down
20 changes: 10 additions & 10 deletions tasks/minsize/minsize.snap
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
| Oxc | ESBuild | Oxc | ESBuild |
Original | minified | minified | gzip | gzip | Fixture
-------------------------------------------------------------------------------------
72.14 kB | 23.72 kB | 23.70 kB | 8.62 kB | 8.54 kB | react.development.js
72.14 kB | 23.71 kB | 23.70 kB | 8.62 kB | 8.54 kB | react.development.js

173.90 kB | 60.15 kB | 59.82 kB | 19.50 kB | 19.33 kB | moment.js

287.63 kB | 90.57 kB | 90.07 kB | 32.19 kB | 31.95 kB | jquery.js
287.63 kB | 90.56 kB | 90.07 kB | 32.18 kB | 31.95 kB | jquery.js

342.15 kB | 118.54 kB | 118.14 kB | 44.56 kB | 44.37 kB | vue.js
342.15 kB | 118.53 kB | 118.14 kB | 44.56 kB | 44.37 kB | vue.js

544.10 kB | 72.00 kB | 72.48 kB | 26.20 kB | 26.20 kB | lodash.js
544.10 kB | 71.98 kB | 72.48 kB | 26.19 kB | 26.20 kB | lodash.js

555.77 kB | 273.88 kB | 270.13 kB | 91.19 kB | 90.80 kB | d3.js

1.01 MB | 461.00 kB | 458.89 kB | 126.92 kB | 126.71 kB | bundle.min.js
1.01 MB | 460.99 kB | 458.89 kB | 126.92 kB | 126.71 kB | bundle.min.js

1.25 MB | 653.54 kB | 646.76 kB | 164.05 kB | 163.73 kB | three.js
1.25 MB | 653.54 kB | 646.76 kB | 164.04 kB | 163.73 kB | three.js

2.14 MB | 727.91 kB | 724.14 kB | 180.39 kB | 181.07 kB | victory.js
2.14 MB | 727.90 kB | 724.14 kB | 180.39 kB | 181.07 kB | victory.js

3.20 MB | 1.01 MB | 1.01 MB | 332.27 kB | 331.56 kB | echarts.js
3.20 MB | 1.01 MB | 1.01 MB | 332.29 kB | 331.56 kB | echarts.js

6.69 MB | 2.32 MB | 2.31 MB | 493.25 kB | 488.28 kB | antd.js
6.69 MB | 2.32 MB | 2.31 MB | 493.24 kB | 488.28 kB | antd.js

10.95 MB | 3.51 MB | 3.49 MB | 910.90 kB | 915.50 kB | typescript.js
10.95 MB | 3.51 MB | 3.49 MB | 910.88 kB | 915.50 kB | typescript.js

0 comments on commit 601c0af

Please sign in to comment.