From 85fb16c3a241bbd87066a119357ad560e336457d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Wed, 8 Jan 2025 19:21:32 +0900 Subject: [PATCH] fix(es/minifier): Improve DCE (#9853) **Description:** - We should check for the superclass while dropping class declarations. - Add early-check for `null && foo` and `true || foo`. **Related issue:** - Closes https://github.com/swc-project/swc/issues/9823 --- .../accessorsOverrideProperty3.2.minified.js | 1 + .../accessorsOverrideProperty4.2.minified.js | 1 + ...terEvaluation(target=es2015).2.minified.js | 7 ++--- ...terEvaluation(target=es2017).2.minified.js | 5 ++-- ...terEvaluation(target=es2018).2.minified.js | 1 + .../tsc-references/override19.2.minified.js | 6 +++++ .../overrideInterfaceProperty.2.minified.js | 1 + ...isPropertyOverridesAccessors.2.minified.js | 1 + .../fixture/deno-9591/output/entry.inlined.ts | 7 +++++ .../tests/fixture/deno-9591/output/entry.ts | 7 +++++ .../fixture/issues/9823/1-class/input.js | 26 +++++++++++++++++++ .../fixture/issues/9823/1-class/output.js | 1 + .../issues/9823/2-class-extends/input.js | 21 +++++++++++++++ .../issues/9823/2-class-extends/output.js | 1 + .../tests/fixture/issues/9823/3/input.js | 7 +++++ .../tests/fixture/issues/9823/3/output.js | 1 + .../src/simplify/dce/mod.rs | 26 ++++++++++++++++++- 17 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 crates/swc_ecma_minifier/tests/fixture/issues/9823/1-class/input.js create mode 100644 crates/swc_ecma_minifier/tests/fixture/issues/9823/1-class/output.js create mode 100644 crates/swc_ecma_minifier/tests/fixture/issues/9823/2-class-extends/input.js create mode 100644 crates/swc_ecma_minifier/tests/fixture/issues/9823/2-class-extends/output.js create mode 100644 crates/swc_ecma_minifier/tests/fixture/issues/9823/3/input.js create mode 100644 crates/swc_ecma_minifier/tests/fixture/issues/9823/3/output.js diff --git a/crates/swc/tests/tsc-references/accessorsOverrideProperty3.2.minified.js b/crates/swc/tests/tsc-references/accessorsOverrideProperty3.2.minified.js index 4384206355cf..4936961e6126 100644 --- a/crates/swc/tests/tsc-references/accessorsOverrideProperty3.2.minified.js +++ b/crates/swc/tests/tsc-references/accessorsOverrideProperty3.2.minified.js @@ -1 +1,2 @@ //// [accessorsOverrideProperty3.ts] +Animal; diff --git a/crates/swc/tests/tsc-references/accessorsOverrideProperty4.2.minified.js b/crates/swc/tests/tsc-references/accessorsOverrideProperty4.2.minified.js index 86ba1b05ba0d..29d45afa7bab 100644 --- a/crates/swc/tests/tsc-references/accessorsOverrideProperty4.2.minified.js +++ b/crates/swc/tests/tsc-references/accessorsOverrideProperty4.2.minified.js @@ -1 +1,2 @@ //// [accessorsOverrideProperty4.ts] +Animal; diff --git a/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2015).2.minified.js b/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2015).2.minified.js index 3e67f5b07831..1ae010a60f8b 100644 --- a/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2015).2.minified.js +++ b/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2015).2.minified.js @@ -1,4 +1,5 @@ //// [asyncGeneratorParameterEvaluation.ts] -import "@swc/helpers/_/_extends"; -import "@swc/helpers/_/_object_destructuring_empty"; -import "@swc/helpers/_/_wrap_async_generator"; +import { _ as _extends } from "@swc/helpers/_/_extends"; +import { _ as _object_destructuring_empty } from "@swc/helpers/_/_object_destructuring_empty"; +import { _ as _wrap_async_generator } from "@swc/helpers/_/_wrap_async_generator"; +Super; diff --git a/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2017).2.minified.js b/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2017).2.minified.js index 2e9f37544614..33284cffe707 100644 --- a/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2017).2.minified.js +++ b/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2017).2.minified.js @@ -1,3 +1,4 @@ //// [asyncGeneratorParameterEvaluation.ts] -import "@swc/helpers/_/_extends"; -import "@swc/helpers/_/_object_destructuring_empty"; +import { _ as _extends } from "@swc/helpers/_/_extends"; +import { _ as _object_destructuring_empty } from "@swc/helpers/_/_object_destructuring_empty"; +Super; diff --git a/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2018).2.minified.js b/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2018).2.minified.js index 01afa63f1ac2..ff9808add0e7 100644 --- a/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2018).2.minified.js +++ b/crates/swc/tests/tsc-references/asyncGeneratorParameterEvaluation(target=es2018).2.minified.js @@ -1 +1,2 @@ //// [asyncGeneratorParameterEvaluation.ts] +Super; diff --git a/crates/swc/tests/tsc-references/override19.2.minified.js b/crates/swc/tests/tsc-references/override19.2.minified.js index 0f276e93dbfa..4413b986ed94 100644 --- a/crates/swc/tests/tsc-references/override19.2.minified.js +++ b/crates/swc/tests/tsc-references/override19.2.minified.js @@ -1 +1,7 @@ //// [override19.ts] +class Context { +} +class A { + doSomething() {} +} +CreateMixin(Context, A), CreateMixin(Context, A); diff --git a/crates/swc/tests/tsc-references/overrideInterfaceProperty.2.minified.js b/crates/swc/tests/tsc-references/overrideInterfaceProperty.2.minified.js index baac0577a379..bf3fc035ac74 100644 --- a/crates/swc/tests/tsc-references/overrideInterfaceProperty.2.minified.js +++ b/crates/swc/tests/tsc-references/overrideInterfaceProperty.2.minified.js @@ -1 +1,2 @@ //// [overrideInterfaceProperty.ts] +Mup, Mup; diff --git a/crates/swc/tests/tsc-references/thisPropertyOverridesAccessors.2.minified.js b/crates/swc/tests/tsc-references/thisPropertyOverridesAccessors.2.minified.js index b14c28cee2a9..26d426a10b9a 100644 --- a/crates/swc/tests/tsc-references/thisPropertyOverridesAccessors.2.minified.js +++ b/crates/swc/tests/tsc-references/thisPropertyOverridesAccessors.2.minified.js @@ -1,2 +1,3 @@ //// [foo.ts] //// [bar.js] +Foo; diff --git a/crates/swc_bundler/tests/fixture/deno-9591/output/entry.inlined.ts b/crates/swc_bundler/tests/fixture/deno-9591/output/entry.inlined.ts index f9695cc0991a..a262929cd7f6 100644 --- a/crates/swc_bundler/tests/fixture/deno-9591/output/entry.inlined.ts +++ b/crates/swc_bundler/tests/fixture/deno-9591/output/entry.inlined.ts @@ -1827,6 +1827,13 @@ function copyBytes(src, dst, off = 0) { return src.byteLength; } const DEFAULT_BUF_SIZE = 4096; +class PartialReadError extends Deno.errors.UnexpectedEof { + name = "PartialReadError"; + partial; + constructor(){ + super("Encountered UnexpectedEof, data only partially read"); + } +} class AbstractBufBase { buf; usedBufferBytes = 0; diff --git a/crates/swc_bundler/tests/fixture/deno-9591/output/entry.ts b/crates/swc_bundler/tests/fixture/deno-9591/output/entry.ts index 750be882cd4e..b86c90b042f0 100644 --- a/crates/swc_bundler/tests/fixture/deno-9591/output/entry.ts +++ b/crates/swc_bundler/tests/fixture/deno-9591/output/entry.ts @@ -1837,6 +1837,13 @@ function copyBytes(src, dst, off = 0) { return src.byteLength; } const DEFAULT_BUF_SIZE = 4096; +class PartialReadError extends Deno.errors.UnexpectedEof { + name = "PartialReadError"; + partial; + constructor(){ + super("Encountered UnexpectedEof, data only partially read"); + } +} class AbstractBufBase { buf; usedBufferBytes = 0; diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/9823/1-class/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/9823/1-class/input.js new file mode 100644 index 000000000000..2e77d060e2bd --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/9823/1-class/input.js @@ -0,0 +1,26 @@ +(() => { + "use strict"; + class Element { } + class PointElement extends Element { + static id = 'point'; + constructor(cfg) { + super(); + } + } + + // var chart_elements = /*#__PURE__*/ Object.freeze({ + // PointElement: PointElement + // }); + + var chart_elements = /*#__PURE__*/(null && (Object.freeze({ + PointElement: PointElement + }))); + + const registerables = null && ([ + chart_elements, + chart_plugins, + ]); + + console.log('Done 1') +})() + ; \ No newline at end of file diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/9823/1-class/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/9823/1-class/output.js new file mode 100644 index 000000000000..76e7bd0f5f74 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/9823/1-class/output.js @@ -0,0 +1 @@ +console.log('Done 1'); diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/9823/2-class-extends/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/9823/2-class-extends/input.js new file mode 100644 index 000000000000..40f32149bb3a --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/9823/2-class-extends/input.js @@ -0,0 +1,21 @@ +(() => { + "use strict"; + class Element { } + + + // var chart_elements = /*#__PURE__*/ Object.freeze({ + // PointElement: PointElement + // }); + + var chart_elements = /*#__PURE__*/(null && (Object.freeze({ + Element: Element + }))); + + const registerables = null && ([ + chart_elements, + chart_plugins + ]); + + console.log('Done 2') +})() + ; \ No newline at end of file diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/9823/2-class-extends/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/9823/2-class-extends/output.js new file mode 100644 index 000000000000..5ceeb115903e --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/9823/2-class-extends/output.js @@ -0,0 +1 @@ +console.log('Done 2'); diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/9823/3/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/9823/3/input.js new file mode 100644 index 000000000000..ffebe4eea543 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/9823/3/input.js @@ -0,0 +1,7 @@ +(function () { + function foo() { + + } + + console.log('Done') +})() \ No newline at end of file diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/9823/3/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/9823/3/output.js new file mode 100644 index 000000000000..a11af772ea8e --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/9823/3/output.js @@ -0,0 +1 @@ +console.log('Done'); diff --git a/crates/swc_ecma_transforms_optimization/src/simplify/dce/mod.rs b/crates/swc_ecma_transforms_optimization/src/simplify/dce/mod.rs index ecc0e775135e..832c7e2fc029 100644 --- a/crates/swc_ecma_transforms_optimization/src/simplify/dce/mod.rs +++ b/crates/swc_ecma_transforms_optimization/src/simplify/dce/mod.rs @@ -16,7 +16,7 @@ use swc_ecma_transforms_base::{ perf::{cpu_count, ParVisitMut, Parallel}, }; use swc_ecma_utils::{ - collect_decls, find_pat_ids, ExprCtx, ExprExt, IsEmpty, ModuleItemLike, StmtLike, + collect_decls, find_pat_ids, ExprCtx, ExprExt, IsEmpty, ModuleItemLike, StmtLike, Value::Known, }; use swc_ecma_visit::{ noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith, VisitWith, @@ -655,6 +655,24 @@ impl TreeShaker { .map(|v| v.usage == 0) .unwrap_or_default() } + + /// Drops RHS from `null && foo` + fn optimize_bin_expr(&mut self, n: &mut Expr) { + let Expr::Bin(b) = n else { + return; + }; + + if b.op == op!("&&") && b.left.as_pure_bool(&self.expr_ctx) == Known(false) { + *n = *b.left.take(); + self.changed = true; + return; + } + + if b.op == op!("||") && b.left.as_pure_bool(&self.expr_ctx) == Known(true) { + *n = *b.left.take(); + self.changed = true; + } + } } impl VisitMut for TreeShaker { @@ -697,6 +715,10 @@ impl VisitMut for TreeShaker { } Decl::Class(c) => { if self.can_drop_binding(c.ident.to_id(), false) + && c.class + .super_class + .as_deref() + .map_or(true, |e| !e.may_have_side_effects(&self.expr_ctx)) && c.class.body.iter().all(|m| match m { ClassMember::Method(m) => !matches!(m.key, PropName::Computed(..)), ClassMember::ClassProp(m) => { @@ -757,6 +779,8 @@ impl VisitMut for TreeShaker { fn visit_mut_expr(&mut self, n: &mut Expr) { n.visit_mut_children_with(self); + self.optimize_bin_expr(n); + if let Expr::Call(CallExpr { callee: Callee::Expr(callee), args,