feat(es/minifier): Consider arguments while dropping unused assignments (#3775)

swc_ecma_minifier:
 - `analyze`: Track usage of `arguments`.
This commit is contained in:
Donny/강동윤 2022-02-28 21:57:09 +09:00 committed by GitHub
parent f71eb26bd4
commit 714e05e553
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 48 additions and 8 deletions

View File

@ -1,6 +1,7 @@
name: Maintenance
on:
pull_request:
pull_request_review:
jobs:

View File

@ -48,7 +48,9 @@ where
};
n.visit_with(&mut v);
let top_scope = v.scope;
v.data.top_scope().merge(top_scope, false);
v.data.top_scope().merge(top_scope.clone(), false);
v.data.scope(SyntaxContext::empty()).merge(top_scope, false);
v.data
}
@ -138,6 +140,7 @@ pub(crate) enum ScopeKind {
pub(crate) struct ScopeData {
pub has_with_stmt: bool,
pub has_eval_call: bool,
pub used_arguments: bool,
}
/// Analyzed info of a whole program we are working on.
@ -218,6 +221,10 @@ where
}
fn report_usage(&mut self, i: &Ident, is_assign: bool) {
if i.sym == js_word!("arguments") {
self.scope.mark_used_arguments();
}
self.data.report_usage(self.ctx, i, is_assign)
}
@ -253,9 +260,7 @@ where
match &n.body {
BlockStmtOrExpr::BlockStmt(body) => {
// We use visit_children_with instead of visit_with to bypass block scope
// handler.
body.visit_children_with(child);
body.visit_with(child);
}
BlockStmtOrExpr::Expr(body) => {
body.visit_with(child);

View File

@ -30,11 +30,13 @@ pub(crate) trait Storage: Sized + Default {
) -> &mut Self::VarData;
}
pub(crate) trait ScopeDataLike: Sized + Default {
pub(crate) trait ScopeDataLike: Sized + Default + Clone {
fn add_declared_symbol(&mut self, id: &Ident);
fn merge(&mut self, other: Self, is_child: bool);
fn mark_used_arguments(&mut self);
fn mark_eval_called(&mut self);
fn mark_with_stmt(&mut self);

View File

@ -164,6 +164,11 @@ impl ScopeDataLike for ScopeData {
fn merge(&mut self, other: Self, _: bool) {
self.has_with_stmt |= other.has_with_stmt;
self.has_eval_call |= other.has_eval_call;
self.used_arguments |= other.used_arguments;
}
fn mark_used_arguments(&mut self) {
self.used_arguments = true;
}
fn mark_eval_called(&mut self) {

View File

@ -440,6 +440,20 @@ where
return;
}
let used_arguments = self
.data
.as_ref()
.map(|data| {
data.scopes.get(&self.ctx.scope).unwrap_or_else(|| {
unreachable!(
"scope should exist\nScopes: {:?};\nCtxt: {:?}",
data.scopes, self.ctx.scope
)
})
})
.map(|scope| scope.used_arguments)
.unwrap_or(false);
if cfg!(feature = "debug") {
tracing::trace!(
"unused: drop_unused_assignments: Target: `{}`",
@ -480,7 +494,11 @@ where
.as_ref()
.and_then(|data| data.vars.get(&i.to_id()))
{
if var.is_fn_local && var.usage_count == 0 && var.declared {
if var.is_fn_local
&& var.usage_count == 0
&& var.declared
&& (!var.declared_as_fn_param || !used_arguments)
{
tracing::debug!(
"unused: Dropping assignment to var '{}{:?}', which is never used",
i.id.sym,

View File

@ -83,6 +83,15 @@ where
/// RAII guard to change context temporarically
#[inline]
pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx<'_, 'b, M> {
if cfg!(debug_assertions) {
let scope_ctxt = ctx.scope;
if self.ctx.scope != scope_ctxt {
if let Some(data) = &self.data {
data.scopes.get(&scope_ctxt).expect("scope not found");
}
}
}
let orig_ctx = self.ctx;
self.ctx = ctx;
WithCtx {

View File

@ -1,4 +1,6 @@
arguments/arguments_and_destructuring_1/input.js
arguments/arguments_and_destructuring_2/input.js
arguments/arguments_and_destructuring_3/input.js
arguments/arguments_in_arrow_func_1/input.js
arguments/arguments_in_arrow_func_2/input.js
arguments/modified_strict/input.js

View File

@ -1,5 +1,3 @@
arguments/arguments_and_destructuring_2/input.js
arguments/arguments_and_destructuring_3/input.js
arguments/duplicate_parameter_with_arguments/input.js
arguments/issue_687/input.js
arguments/modified/input.js