refactor(es/minifier): Cleanup (#4020)

**Description:**

 - `Optimizer.data`: `Option<ProgramData>` => `ProgramData`.
 - `Optimizer.done`: Remvoed.
 - `Optimzer.done_ctxt`: Removed.
 - `Pure`: Add `data: Option<&'a ProgramData>`.
This commit is contained in:
Donny/강동윤 2022-03-15 17:42:50 +09:00 committed by GitHub
parent 1902682bed
commit 3173047f58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 104 additions and 226 deletions

View File

@ -325,6 +325,7 @@ where
let mut visitor = pure_optimizer(
self.options,
None,
self.marks,
M::force_str_for_tpl(),
self.pass > 1,
@ -348,12 +349,12 @@ where
{
let _timer = timer!("apply full optimizer");
let mut data = analyze(&*n, Some(self.marks));
// TODO: reset_opt_flags
//
// This is swc version of `node.optimize(this);`.
let mut data = analyze(&*n, Some(self.marks));
let mut visitor = optimizer(
self.marks,
self.options,

View File

@ -78,8 +78,8 @@ where
}) => true,
Pat::Ident(i) => self
.data
.as_ref()
.and_then(|v| v.vars.get(&i.id.to_id()))
.vars
.get(&i.id.to_id())
.map(|v| v.declared_count >= 2)
.unwrap_or(false),
_ => true,

View File

@ -136,9 +136,7 @@ where
) => {
// TODO?
if let Expr::Ident(arg) = &**arg {
if let Some(usage) =
o.data.as_ref().and_then(|data| data.vars.get(&arg.to_id()))
{
if let Some(usage) = o.data.vars.get(&arg.to_id()) {
if !usage.declared {
return false;
}

View File

@ -31,11 +31,7 @@ where
},
};
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&left.to_id()))
{
if let Some(usage) = self.data.vars.get(&left.to_id()) {
if !usage.declared
|| !usage.is_fn_local
|| usage.assign_count != 1

View File

@ -12,7 +12,6 @@ use crate::{
util::{always_terminates, negate_cost},
},
mode::Mode,
util::SpanExt,
DISABLE_BUGGY_PASSES,
};
@ -296,9 +295,9 @@ where
);
self.changed = true;
*s = Stmt::Expr(ExprStmt {
span: stmt.span.with_mark(self.done),
span: stmt.span,
expr: Box::new(Expr::Cond(CondExpr {
span: DUMMY_SP.with_ctxt(self.done_ctxt),
span: DUMMY_SP,
test: stmt.test.take(),
cons: Box::new(cons.take()),
alt: Box::new(alt.take()),
@ -353,10 +352,8 @@ where
match (cons, alt) {
(Expr::Call(cons), Expr::Call(alt)) => {
if let Some(data) = &self.data {
if data.contains_unresolved(&**test) {
return None;
}
if self.data.contains_unresolved(&**test) {
return None;
}
let cons_callee = cons.callee.as_expr().and_then(|e| e.as_ident())?;
@ -368,8 +365,8 @@ where
let side_effect_free = self
.data
.as_ref()
.and_then(|data| data.vars.get(&cons_callee.to_id()))
.vars
.get(&cons_callee.to_id())
.map(|v| v.is_fn_local && v.declared)
.unwrap_or(false);
@ -453,7 +450,7 @@ where
arguments is 1"
);
return Some(Expr::Call(CallExpr {
span: DUMMY_SP.with_ctxt(self.done_ctxt),
span: DUMMY_SP,
callee: cons.callee.take(),
args,
type_args: Default::default(),
@ -463,7 +460,7 @@ where
if !side_effect_free && is_for_if_stmt {
tracing::debug!("Compressing if into cond while preserving side effects");
return Some(Expr::Cond(CondExpr {
span: DUMMY_SP.with_ctxt(self.done_ctxt),
span: DUMMY_SP,
test: test.take(),
cons: Box::new(Expr::Call(cons.take())),
alt: Box::new(Expr::Call(alt.take())),
@ -474,10 +471,8 @@ where
}
(Expr::New(cons), Expr::New(alt)) => {
if let Some(data) = &self.data {
if data.contains_unresolved(&**test) {
return None;
}
if self.data.contains_unresolved(&**test) {
return None;
}
// TODO: Handle new expression with no args.
@ -521,7 +516,7 @@ where
there's no side effect and the number of arguments is 1"
);
return Some(Expr::New(NewExpr {
span: DUMMY_SP.with_ctxt(self.done_ctxt),
span: DUMMY_SP,
callee: cons.callee.take(),
args: Some(args),
type_args: Default::default(),
@ -555,7 +550,7 @@ where
(Expr::Cond(cons), alt) if (*cons.alt).eq_ignore_span(&*alt) => {
tracing::debug!("conditionals: a ? b ? c() : d() : d() => a && b ? c() : d()");
Some(Expr::Cond(CondExpr {
span: DUMMY_SP.with_ctxt(self.done_ctxt),
span: DUMMY_SP,
test: Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: test.take(),

View File

@ -52,8 +52,8 @@ where
//
if self
.data
.as_ref()
.and_then(|data| data.vars.get(&lhs.to_id()))
.vars
.get(&lhs.to_id())
.map(|var| var.is_fn_local && !var.declared_as_fn_param)
.unwrap_or(false)
{
@ -75,8 +75,8 @@ where
//
if self
.data
.as_ref()
.and_then(|data| data.vars.get(&lhs.to_id()))
.vars
.get(&lhs.to_id())
.map(|var| var.is_fn_local)
.unwrap_or(false)
{

View File

@ -15,7 +15,7 @@ where
{
/// Evaluate expression if possible.
///
/// This method call apppropriate methods for each ast types.
/// This method call appropriate methods for each ast types.
pub(super) fn evaluate(&mut self, e: &mut Expr) {
self.eval_global_vars(e);
@ -37,8 +37,8 @@ where
if let Expr::Ident(i) = e {
if self
.data
.as_ref()
.and_then(|data| data.vars.get(&i.to_id()))
.vars
.get(&i.to_id())
.map(|var| var.declared)
.unwrap_or(false)
{
@ -65,7 +65,7 @@ where
tracing::debug!("evaluate: `Infinity` -> `1 / 0`");
self.changed = true;
*e = Expr::Bin(BinExpr {
span: span.with_ctxt(self.done_ctxt),
span: *span,
op: op!("/"),
left: Box::new(Expr::Lit(Lit::Num(Number {
span: DUMMY_SP,
@ -187,7 +187,7 @@ where
if let Some(v) = char::from_u32(v) {
self.changed = true;
tracing::debug!(
"evanluate: Evaluated `String.charCodeAt({})` as `{}`",
"evaluate: Evaluated `String.charCodeAt({})` as `{}`",
char_code,
v
);
@ -306,7 +306,7 @@ where
if let Known(l) = l {
if let Known(r) = r {
self.changed = true;
tracing::debug!("evaluate: Evaulated `{:?} ** {:?}`", l, r);
tracing::debug!("evaluate: Evaluated `{:?} ** {:?}`", l, r);
let value = l.powf(r);
*e = Expr::Lit(Lit::Num(Number {
@ -337,13 +337,9 @@ where
// If a variable named `NaN` is in scope, don't convert e into NaN.
if self
.data
.as_ref()
.map(|data| {
data.vars
.iter()
.any(|(name, v)| v.declared && name.0 == js_word!("NaN"))
})
.unwrap_or(false)
.vars
.iter()
.any(|(name, v)| v.declared && name.0 == js_word!("NaN"))
{
return;
}

View File

@ -24,14 +24,13 @@ where
// smart.
if !self
.data
.as_ref()
.and_then(|data| {
data.vars.get(&name.to_id()).map(|v| {
!v.mutated
&& !v.reassigned_with_assignment
&& !v.reassigned_with_var_decl
&& !v.is_infected()
})
.vars
.get(&name.to_id())
.map(|v| {
!v.mutated
&& !v.reassigned_with_assignment
&& !v.reassigned_with_var_decl
&& !v.is_infected()
})
.unwrap_or(false)
{
@ -41,12 +40,9 @@ where
// We should abort if unknown property is used.
let mut unknown_used_props = self
.data
.as_ref()
.and_then(|data| {
data.vars
.get(&name.to_id())
.map(|v| v.accessed_props.clone())
})
.vars
.get(&name.to_id())
.map(|v| v.accessed_props.clone())
.unwrap_or_default();
if let Some(Expr::Object(init)) = n.init.as_deref() {
@ -120,15 +116,14 @@ where
// If the variable is used multiple time, just ignore it.
if !self
.data
.as_ref()
.and_then(|data| {
data.vars.get(&name.to_id()).map(|v| {
v.ref_count == 1
&& v.has_property_access
&& v.is_fn_local
&& !v.executed_multiple_time
&& !v.used_in_cond
})
.vars
.get(&name.to_id())
.map(|v| {
v.ref_count == 1
&& v.has_property_access
&& v.is_fn_local
&& !v.executed_multiple_time
&& !v.used_in_cond
})
.unwrap_or(false)
{

View File

@ -185,11 +185,7 @@ where
for (idx, param) in params.iter().enumerate() {
let arg = e.args.get(idx).map(|v| &v.expr);
if let Pat::Ident(param) = &param {
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&param.to_id()))
{
if let Some(usage) = self.data.vars.get(&param.to_id()) {
if usage.reassigned() {
continue;
}
@ -514,11 +510,7 @@ where
// Don't create top-level variables.
if !param_ids.is_empty() && self.ctx.in_top_level() {
for pid in param_ids {
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&pid.to_id()))
{
if let Some(usage) = self.data.vars.get(&pid.to_id()) {
if usage.ref_count > 1 || usage.assign_count > 0 || usage.inline_prevented {
return false;
}

View File

@ -38,12 +38,7 @@ where
);
}
if self
.data
.as_ref()
.map(|v| v.top.has_eval_call)
.unwrap_or(false)
{
if self.data.top.has_eval_call {
return;
}
@ -60,11 +55,7 @@ where
}
// Store variables if it's used only once
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&i.to_id()))
{
if let Some(usage) = self.data.vars.get(&i.to_id()) {
if usage.declared_as_catch_param {
return;
}
@ -255,11 +246,7 @@ where
}
if let Expr::Ident(v) = &**init {
if let Some(v_usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&v.to_id()))
{
if let Some(v_usage) = self.data.vars.get(&v.to_id()) {
if v_usage.reassigned() {
return;
}
@ -351,11 +338,7 @@ where
return;
}
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&i.to_id()))
{
if let Some(usage) = self.data.vars.get(&i.to_id()) {
if !usage.reassigned() {
tracing::trace!("typeofs: Storing typeof `{}{:?}`", i.sym, i.span.ctxt);
match &*decl {
@ -428,20 +411,11 @@ where
return;
}
if self
.data
.as_ref()
.map(|data| data.top.has_eval_call || data.top.has_with_stmt)
.unwrap_or_default()
{
if self.data.top.has_eval_call || self.data.top.has_with_stmt {
return;
}
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&i.to_id()))
{
if let Some(usage) = self.data.vars.get(&i.to_id()) {
if usage.declared_as_catch_param {
if cfg!(feature = "debug") {
tracing::trace!("inline: [x] Declared as a catch parameter");

View File

@ -5,7 +5,7 @@ use std::{fmt::Write, iter::once, mem::take};
use retain_mut::RetainMut;
use swc_atoms::{js_word, JsWord};
use swc_common::{
collections::AHashMap, iter::IdentifyLast, pass::Repeated, util::take::Take, Mark, Spanned,
collections::AHashMap, iter::IdentifyLast, pass::Repeated, util::take::Take, Spanned,
SyntaxContext, DUMMY_SP,
};
use swc_ecma_ast::*;
@ -64,8 +64,6 @@ where
"top_retain should not contain empty string"
);
let done = Mark::fresh(Mark::root());
let done_ctxt = SyntaxContext::empty().apply_mark(done);
Optimizer {
marks,
changed: false,
@ -78,10 +76,8 @@ where
simple_props: Default::default(),
_simple_array_values: Default::default(),
typeofs: Default::default(),
data: Some(data),
data,
ctx: Default::default(),
done,
done_ctxt,
label: Default::default(),
mode,
debug_infinite_loop,
@ -208,11 +204,8 @@ struct Optimizer<'a, M> {
///
/// This is calculated multiple time, but only once per one
/// `visit_mut_module`.
data: Option<&'a mut ProgramData>,
data: &'a mut ProgramData,
ctx: Ctx,
/// In future: This will be used to `mark` node as done.
done: Mark,
done_ctxt: SyntaxContext,
/// Closest label.
///
@ -244,12 +237,6 @@ where
T: StmtLike + ModuleItemLike + ModuleItemExt + VisitMutWith<Self> + VisitWith<AssertValid>,
Vec<T>: VisitMutWith<Self> + VisitWith<UsageAnalyzer> + VisitWith<AssertValid>,
{
match self.data {
Some(..) => {}
None => {
unreachable!()
}
}
let mut use_asm = false;
let prepend_stmts = self.prepend_stmts.take();
let append_stmts = self.append_stmts.take();
@ -894,11 +881,7 @@ where
if let Expr::Ident(callee) = &**callee {
if self.options.reduce_vars && self.options.side_effects {
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&callee.to_id()))
{
if let Some(usage) = self.data.vars.get(&callee.to_id()) {
if !usage.reassigned() && usage.pure_fn {
let args = args
.take()
@ -2852,11 +2835,7 @@ where
if let Some(Expr::Invalid(..)) = var.init.as_deref() {
if let Pat::Ident(i) = &var.name {
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&i.id.to_id()))
{
if let Some(usage) = self.data.vars.get(&i.id.to_id()) {
if usage.declared_as_catch_param {
var.init = None;
return true;

View File

@ -1404,11 +1404,7 @@ where
}
};
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&left_id.to_id()))
{
if let Some(usage) = self.data.vars.get(&left_id.to_id()) {
if usage.inline_prevented {
return Ok(false);
}
@ -1440,11 +1436,7 @@ where
_ => return Ok(false),
};
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&left.to_id()))
{
if let Some(usage) = self.data.vars.get(&left.to_id()) {
if usage.ref_count != 1 {
return Ok(false);
}

View File

@ -126,8 +126,8 @@ where
}
if self
.data
.as_ref()
.and_then(|data| data.vars.get(&i.to_id()))
.vars
.get(&i.to_id())
.map(|v| v.assign_count == 0 && !v.declared_as_fn_param)
.unwrap_or(false)
{

View File

@ -81,11 +81,7 @@ where
return;
}
if let Some(scope) = self
.data
.as_ref()
.and_then(|data| data.scopes.get(&self.ctx.scope))
{
if let Some(scope) = self.data.scopes.get(&self.ctx.scope) {
if scope.has_eval_call || scope.has_with_stmt {
return;
}
@ -159,11 +155,7 @@ where
}
}
if let Some(scope) = self
.data
.as_ref()
.and_then(|data| data.scopes.get(&self.ctx.scope))
{
if let Some(scope) = self.data.scopes.get(&self.ctx.scope) {
if scope.has_eval_call || scope.has_with_stmt {
if cfg!(feature = "debug") {
tracing::trace!(
@ -213,11 +205,7 @@ where
return;
}
if let Some(v) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&i.to_id()).cloned())
{
if let Some(v) = self.data.vars.get(&i.to_id()).cloned() {
if v.ref_count == 0
&& v.usage_count == 0
&& !v.reassigned_with_assignment
@ -282,11 +270,7 @@ where
}
}
if let Some(usage) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&e.to_id()))
{
if let Some(usage) = self.data.vars.get(&e.to_id()) {
if !usage.declared {
return true;
}
@ -476,11 +460,7 @@ where
return;
}
if let Some(scope) = self
.data
.as_ref()
.and_then(|data| data.scopes.get(&self.ctx.scope))
{
if let Some(scope) = self.data.scopes.get(&self.ctx.scope) {
if scope.has_eval_call || scope.has_with_stmt {
return;
}
@ -502,8 +482,8 @@ where
// If it is not used, drop it.
if self
.data
.as_ref()
.and_then(|data| data.vars.get(&ident.to_id()))
.vars
.get(&ident.to_id())
.map(|v| v.usage_count == 0 && !v.has_property_mutation)
.unwrap_or(false)
{
@ -545,28 +525,21 @@ where
return;
}
if self
.data
.as_ref()
.map(|v| v.top.has_eval_call || v.top.has_with_stmt)
.unwrap_or(false)
{
if self.data.top.has_eval_call || self.data.top.has_with_stmt {
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
)
})
.scopes
.get(&self.ctx.scope)
.unwrap_or_else(|| {
unreachable!(
"scope should exist\nScopes: {:?};\nCtxt: {:?}",
self.data.scopes, self.ctx.scope
)
})
.map(|scope| scope.used_arguments)
.unwrap_or(false);
.used_arguments;
if cfg!(feature = "debug") {
tracing::trace!(
@ -603,11 +576,7 @@ where
return;
}
if let Some(var) = self
.data
.as_ref()
.and_then(|data| data.vars.get(&i.to_id()))
{
if let Some(var) = self.data.vars.get(&i.to_id()) {
if var.is_fn_local
&& var.usage_count == 0
&& var.declared
@ -647,18 +616,17 @@ where
}
if let Some(i) = &name {
if let Some(data) = &self.data {
let can_remove_ident = data
.vars
.get(&i.to_id())
.map(|v| (v.ref_count == 0 && v.usage_count == 0) || v.var_kind.is_some())
.unwrap_or(true);
let can_remove_ident = self
.data
.vars
.get(&i.to_id())
.map(|v| (v.ref_count == 0 && v.usage_count == 0) || v.var_kind.is_some())
.unwrap_or(true);
if can_remove_ident {
self.changed = true;
tracing::debug!("Removing ident of an class / function expression");
*name = None;
}
if can_remove_ident {
self.changed = true;
tracing::debug!("Removing ident of an class / function expression");
*name = None;
}
}
}

View File

@ -74,21 +74,13 @@ where
span.has_mark(self.marks.noinline)
}
#[allow(unused)]
#[allow(clippy::wrong_self_convention)]
pub(super) fn is_done(&mut self, span: Span) -> bool {
span.has_mark(self.done)
}
/// 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");
}
self.data.scopes.get(&scope_ctxt).expect("scope not found");
}
}

View File

@ -11,6 +11,7 @@ use tracing::{span, Level};
use self::{ctx::Ctx, misc::DropOpts};
use crate::{
analyzer::ProgramData,
debug::{dump, AssertValid},
marks::Marks,
option::CompressOptions,
@ -38,6 +39,7 @@ mod vars;
#[allow(clippy::needless_lifetimes)]
pub(crate) fn pure_optimizer<'a>(
options: &'a CompressOptions,
data: Option<&'a ProgramData>,
marks: Marks,
force_str_for_tpl: bool,
enable_everything: bool,
@ -46,6 +48,7 @@ pub(crate) fn pure_optimizer<'a>(
Pure {
options,
marks,
data,
ctx: Ctx {
force_str_for_tpl,
..Default::default()
@ -60,6 +63,8 @@ pub(crate) fn pure_optimizer<'a>(
struct Pure<'a> {
options: &'a CompressOptions,
marks: Marks,
#[allow(unused)]
data: Option<&'a ProgramData>,
ctx: Ctx,
changed: bool,
enable_everything: bool,
@ -153,13 +158,9 @@ impl Pure<'_> {
if self.ctx.par_depth >= MAX_PAR_DEPTH * 2 || cfg!(target_arch = "wasm32") {
for node in nodes {
let mut v = Pure {
options: self.options,
marks: self.marks,
ctx: self.ctx,
changed: false,
enable_everything: self.enable_everything,
debug_infinite_loop: self.debug_infinite_loop,
bindings: self.bindings.clone(),
..*self
};
node.visit_mut_with(&mut v);
@ -172,16 +173,13 @@ impl Pure<'_> {
.map(|node| {
GLOBALS.set(globals, || {
let mut v = Pure {
options: self.options,
marks: self.marks,
ctx: Ctx {
par_depth: self.ctx.par_depth + 1,
..self.ctx
},
changed: false,
enable_everything: self.enable_everything,
debug_infinite_loop: self.debug_infinite_loop,
bindings: self.bindings.clone(),
..*self
};
node.visit_mut_with(&mut v);

View File

@ -222,6 +222,7 @@ impl Evaluator {
{
e.visit_mut_with(&mut pure_optimizer(
&serde_json::from_str("{}").unwrap(),
None,
self.marks,
Eval::force_str_for_tpl(),
true,

View File

@ -141,6 +141,7 @@ pub fn optimize(
m.visit_mut_with(&mut postcompress_optimizer(options));
m.visit_mut_with(&mut Repeat::new(pure_optimizer(
options,
None,
marks,
Minification::force_str_for_tpl(),
true,