mirror of
https://github.com/swc-project/swc.git
synced 2024-12-28 08:04:43 +03:00
feat(es/minifier): Implement drop_console
(#3392)
swc_ecma_minifier: - Merge `drop_console` into pure optimizer. - Implement `drop_console`. (Closes #2321)
This commit is contained in:
parent
e215e077ef
commit
91d78000ea
@ -1,87 +0,0 @@
|
||||
use std::{borrow::Cow, mem::take};
|
||||
use swc_common::pass::CompilerPass;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms::pass::JsPass;
|
||||
use swc_ecma_visit::{as_folder, noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
|
||||
pub fn drop_console() -> impl JsPass + VisitMut {
|
||||
as_folder(DropConsole { done: false })
|
||||
}
|
||||
|
||||
struct DropConsole {
|
||||
/// Invoking this pass multiple times is simply waste of time.
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl CompilerPass for DropConsole {
|
||||
fn name() -> Cow<'static, str> {
|
||||
"drop-console".into()
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for DropConsole {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_expr(&mut self, n: &mut Expr) {
|
||||
if self.done {
|
||||
return;
|
||||
}
|
||||
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
if let Expr::Call(CallExpr {
|
||||
span, callee, args, ..
|
||||
}) = n
|
||||
{
|
||||
// Find console.log
|
||||
let callee = match callee {
|
||||
Callee::Expr(callee) => callee,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let Expr::Member(MemberExpr {
|
||||
obj: callee_obj,
|
||||
prop: MemberProp::Ident(_),
|
||||
..
|
||||
}) = &**callee
|
||||
{
|
||||
let mut loop_co = &**callee_obj;
|
||||
loop {
|
||||
match loop_co {
|
||||
Expr::Ident(obj) => {
|
||||
if obj.sym != *"console" {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Expr::Member(MemberExpr {
|
||||
obj: loop_co_obj,
|
||||
prop: MemberProp::Ident(_),
|
||||
..
|
||||
}) => {
|
||||
loop_co = loop_co_obj;
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
|
||||
// Simplifier will remove side-effect-free items.
|
||||
*n = Expr::Seq(SeqExpr {
|
||||
span: *span,
|
||||
exprs: take(args).into_iter().map(|arg| arg.expr).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_module(&mut self, n: &mut Module) {
|
||||
if self.done {
|
||||
return;
|
||||
}
|
||||
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
self.done = true;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
pub(crate) use self::pure::pure_optimizer;
|
||||
use self::{drop_console::drop_console, hoist_decls::DeclHoisterConfig, optimize::optimizer};
|
||||
use self::{hoist_decls::DeclHoisterConfig, optimize::optimizer};
|
||||
use crate::{
|
||||
analyzer::{analyze, UsageAnalyzer},
|
||||
compress::hoist_decls::decl_hoister,
|
||||
@ -35,7 +35,6 @@ use swc_ecma_visit::{as_folder, noop_visit_mut_type, VisitMut, VisitMutWith, Vis
|
||||
use swc_timer::timer;
|
||||
use tracing::error;
|
||||
|
||||
mod drop_console;
|
||||
mod hoist_decls;
|
||||
mod optimize;
|
||||
mod pure;
|
||||
@ -50,10 +49,6 @@ pub(crate) fn compressor<'a, M>(
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
let console_remover = Optional {
|
||||
enabled: options.drop_console,
|
||||
visitor: drop_console(),
|
||||
};
|
||||
let compressor = Compressor {
|
||||
globals,
|
||||
marks,
|
||||
@ -66,9 +61,11 @@ where
|
||||
};
|
||||
|
||||
chain!(
|
||||
console_remover,
|
||||
as_folder(compressor),
|
||||
expr_simplifier(ExprSimplifierConfig {})
|
||||
Optional {
|
||||
enabled: options.evaluate || options.side_effects,
|
||||
visitor: expr_simplifier(ExprSimplifierConfig {})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
compress::optimize::{unused::UnreachableHandler, Optimizer},
|
||||
compress::{optimize::Optimizer, util::UnreachableHandler},
|
||||
mode::Mode,
|
||||
};
|
||||
use swc_common::{util::take::Take, DUMMY_SP};
|
||||
|
@ -2635,6 +2635,7 @@ where
|
||||
Expr::Lit(Lit::Num(..)) => {}
|
||||
|
||||
_ => {
|
||||
tracing::debug!("Ignoring arg of `void`");
|
||||
let arg = self.ignore_return_value(&mut n.arg);
|
||||
|
||||
n.arg = Box::new(arg.unwrap_or_else(|| make_number(DUMMY_SP, 0.0)));
|
||||
|
@ -7,7 +7,6 @@ use swc_atoms::js_word;
|
||||
use swc_common::{util::take::Take, Span, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::{contains_ident_ref, ident::IdentLike};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
|
||||
/// Methods related to the option `unused`.
|
||||
impl<M> Optimizer<'_, M>
|
||||
@ -553,89 +552,3 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct UnreachableHandler {
|
||||
vars: Vec<Ident>,
|
||||
in_var_name: bool,
|
||||
in_hoisted_var: bool,
|
||||
}
|
||||
|
||||
impl UnreachableHandler {
|
||||
/// Assumes `s` is not reachable, and preserves variable declarations and
|
||||
/// function declarations in `s`.
|
||||
///
|
||||
/// Returns true if statement is changed.
|
||||
pub fn preserve_vars(s: &mut Stmt) -> bool {
|
||||
if s.is_empty() {
|
||||
return false;
|
||||
}
|
||||
if let Stmt::Decl(Decl::Var(v)) = s {
|
||||
let mut changed = false;
|
||||
for decl in &mut v.decls {
|
||||
if decl.init.is_some() {
|
||||
decl.init = None;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
let mut v = Self::default();
|
||||
s.visit_mut_with(&mut v);
|
||||
if v.vars.is_empty() {
|
||||
*s = Stmt::Empty(EmptyStmt { span: DUMMY_SP });
|
||||
} else {
|
||||
*s = Stmt::Decl(Decl::Var(VarDecl {
|
||||
span: DUMMY_SP,
|
||||
kind: VarDeclKind::Var,
|
||||
declare: false,
|
||||
decls: v
|
||||
.vars
|
||||
.into_iter()
|
||||
.map(BindingIdent::from)
|
||||
.map(Pat::Ident)
|
||||
.map(|name| VarDeclarator {
|
||||
span: DUMMY_SP,
|
||||
name,
|
||||
init: None,
|
||||
definite: false,
|
||||
})
|
||||
.collect(),
|
||||
}))
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for UnreachableHandler {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_pat(&mut self, n: &mut Pat) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
if self.in_var_name && self.in_hoisted_var {
|
||||
if let Pat::Ident(i) = n {
|
||||
self.vars.push(i.id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_var_decl(&mut self, n: &mut VarDecl) {
|
||||
self.in_hoisted_var = n.kind == VarDeclKind::Var;
|
||||
n.visit_mut_children_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_var_declarator(&mut self, n: &mut VarDeclarator) {
|
||||
self.in_var_name = true;
|
||||
n.name.visit_mut_with(self);
|
||||
self.in_var_name = false;
|
||||
n.init.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_arrow_expr(&mut self, _: &mut ArrowExpr) {}
|
||||
|
||||
fn visit_mut_function(&mut self, _: &mut Function) {}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use super::Pure;
|
||||
use crate::{
|
||||
compress::util::{always_terminates, is_fine_for_if_cons},
|
||||
mode::Mode,
|
||||
util::ModuleItemExt,
|
||||
};
|
||||
use swc_common::{util::take::Take, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
@ -12,6 +13,51 @@ impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn drop_unreachable_stmts<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: StmtLike + ModuleItemExt + Take,
|
||||
{
|
||||
if !self.options.side_effects {
|
||||
return;
|
||||
}
|
||||
|
||||
let idx = stmts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, stmt)| match stmt.as_stmt() {
|
||||
Some(s) => always_terminates(s),
|
||||
_ => false,
|
||||
});
|
||||
|
||||
if let Some((idx, _)) = idx {
|
||||
stmts.iter_mut().skip(idx + 1).for_each(|stmt| {
|
||||
match stmt.as_stmt() {
|
||||
Some(Stmt::Decl(
|
||||
Decl::Var(VarDecl {
|
||||
kind: VarDeclKind::Var,
|
||||
..
|
||||
})
|
||||
| Decl::Fn(..),
|
||||
)) => {
|
||||
// Preserve
|
||||
}
|
||||
|
||||
Some(Stmt::Empty(..)) => {
|
||||
// noop
|
||||
}
|
||||
|
||||
Some(..) => {
|
||||
tracing::debug!("Removing unreachable statements");
|
||||
self.changed = true;
|
||||
stmt.take();
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn drop_useless_blocks<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: StmtLike,
|
||||
@ -115,52 +161,4 @@ where
|
||||
|
||||
*stmts = new;
|
||||
}
|
||||
|
||||
pub(super) fn remove_unreachable_stmts<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: StmtLike,
|
||||
{
|
||||
if !self.options.side_effects {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut last = None;
|
||||
let mut terminated = false;
|
||||
for (idx, stmt) in stmts.iter().enumerate() {
|
||||
match stmt.as_stmt() {
|
||||
Some(stmt) if always_terminates(stmt) => {
|
||||
terminated = true;
|
||||
}
|
||||
_ => {
|
||||
if terminated {
|
||||
last = Some(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(last) = last {
|
||||
if stmts[last..]
|
||||
.iter()
|
||||
.all(|stmt| matches!(stmt.as_stmt(), Some(Stmt::Decl(..)) | None))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.changed = true;
|
||||
tracing::debug!("dead_code: Removing unreachable statements");
|
||||
|
||||
let extras = stmts.drain(last..).collect::<Vec<_>>();
|
||||
|
||||
for extra in extras {
|
||||
match extra.as_stmt() {
|
||||
Some(Stmt::Decl(..)) | None => {
|
||||
stmts.push(extra);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
52
crates/swc_ecma_minifier/src/compress/pure/drop_console.rs
Normal file
52
crates/swc_ecma_minifier/src/compress/pure/drop_console.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use super::Pure;
|
||||
use swc_common::DUMMY_SP;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::undefined;
|
||||
|
||||
impl<M> Pure<'_, M> {
|
||||
pub(super) fn drop_console(&mut self, e: &mut Expr) {
|
||||
if !self.options.drop_console {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Expr::Call(CallExpr { callee, .. }) = e {
|
||||
// Find console.log
|
||||
let callee = match callee {
|
||||
Callee::Expr(callee) => callee,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let Expr::Member(MemberExpr {
|
||||
obj: callee_obj,
|
||||
prop: MemberProp::Ident(_),
|
||||
..
|
||||
}) = &**callee
|
||||
{
|
||||
let mut loop_co = &**callee_obj;
|
||||
loop {
|
||||
match loop_co {
|
||||
Expr::Ident(obj) => {
|
||||
if obj.sym != *"console" {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Expr::Member(MemberExpr {
|
||||
obj: loop_co_obj,
|
||||
prop: MemberProp::Ident(_),
|
||||
..
|
||||
}) => {
|
||||
loop_co = loop_co_obj;
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
|
||||
tracing::debug!("drop_console: Removing console call");
|
||||
self.changed = true;
|
||||
*e = *undefined(DUMMY_SP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +1,9 @@
|
||||
use super::Pure;
|
||||
use crate::compress::util::{always_terminates, is_fine_for_if_cons, negate};
|
||||
use crate::compress::util::{is_fine_for_if_cons, negate};
|
||||
use swc_common::{util::take::Take, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl<M> Pure<'_, M> {
|
||||
pub(super) fn drop_unreachable_stmts(&mut self, stmts: &mut Vec<Stmt>) {
|
||||
if !self.options.dead_code && !self.options.side_effects {
|
||||
return;
|
||||
}
|
||||
|
||||
let idx = stmts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, stmt)| always_terminates(stmt));
|
||||
|
||||
if let Some((idx, _)) = idx {
|
||||
stmts.iter_mut().skip(idx + 1).for_each(|stmt| match stmt {
|
||||
Stmt::Decl(
|
||||
Decl::Var(VarDecl {
|
||||
kind: VarDeclKind::Var,
|
||||
..
|
||||
})
|
||||
| Decl::Fn(..),
|
||||
) => {
|
||||
// Preserve
|
||||
}
|
||||
|
||||
Stmt::Empty(..) => {
|
||||
// noop
|
||||
}
|
||||
|
||||
_ => {
|
||||
self.changed = true;
|
||||
stmt.take();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// # Input
|
||||
///
|
||||
/// ```js
|
||||
|
@ -18,6 +18,7 @@ mod bools;
|
||||
mod conds;
|
||||
mod ctx;
|
||||
mod dead_code;
|
||||
mod drop_console;
|
||||
mod evaluate;
|
||||
mod if_return;
|
||||
mod loops;
|
||||
@ -75,14 +76,14 @@ where
|
||||
{
|
||||
fn handle_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: ModuleItemExt,
|
||||
T: ModuleItemExt + Take,
|
||||
Vec<T>: VisitWith<self::vars::VarWithOutInitCounter>
|
||||
+ VisitMutWith<self::vars::VarPrepender>
|
||||
+ VisitMutWith<self::vars::VarMover>,
|
||||
{
|
||||
self.remove_dead_branch(stmts);
|
||||
|
||||
self.remove_unreachable_stmts(stmts);
|
||||
self.drop_unreachable_stmts(stmts);
|
||||
|
||||
self.drop_useless_blocks(stmts);
|
||||
|
||||
@ -92,8 +93,6 @@ where
|
||||
}
|
||||
|
||||
fn optimize_fn_stmts(&mut self, stmts: &mut Vec<Stmt>) {
|
||||
self.drop_unreachable_stmts(stmts);
|
||||
|
||||
self.remove_useless_return(stmts);
|
||||
|
||||
self.negate_if_terminate(stmts, true, false);
|
||||
@ -222,6 +221,10 @@ where
|
||||
|
||||
self.remove_invalid(e);
|
||||
|
||||
self.drop_console(e);
|
||||
|
||||
self.remove_invalid(e);
|
||||
|
||||
if let Expr::Seq(seq) = e {
|
||||
if seq.exprs.is_empty() {
|
||||
*e = Expr::Invalid(Invalid { span: DUMMY_SP });
|
||||
|
@ -620,3 +620,95 @@ where
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct UnreachableHandler {
|
||||
vars: Vec<Ident>,
|
||||
in_var_name: bool,
|
||||
in_hoisted_var: bool,
|
||||
}
|
||||
|
||||
impl UnreachableHandler {
|
||||
/// Assumes `s` is not reachable, and preserves variable declarations and
|
||||
/// function declarations in `s`.
|
||||
///
|
||||
/// Returns true if statement is changed.
|
||||
pub fn preserve_vars(s: &mut Stmt) -> bool {
|
||||
if s.is_empty() {
|
||||
return false;
|
||||
}
|
||||
if let Stmt::Decl(Decl::Var(v)) = s {
|
||||
let mut changed = false;
|
||||
for decl in &mut v.decls {
|
||||
if decl.init.is_some() {
|
||||
decl.init = None;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
let mut v = Self::default();
|
||||
s.visit_mut_with(&mut v);
|
||||
if v.vars.is_empty() {
|
||||
*s = Stmt::Empty(EmptyStmt { span: DUMMY_SP });
|
||||
} else {
|
||||
*s = Stmt::Decl(Decl::Var(VarDecl {
|
||||
span: DUMMY_SP,
|
||||
kind: VarDeclKind::Var,
|
||||
declare: false,
|
||||
decls: v
|
||||
.vars
|
||||
.into_iter()
|
||||
.map(BindingIdent::from)
|
||||
.map(Pat::Ident)
|
||||
.map(|name| VarDeclarator {
|
||||
span: DUMMY_SP,
|
||||
name,
|
||||
init: None,
|
||||
definite: false,
|
||||
})
|
||||
.collect(),
|
||||
}))
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for UnreachableHandler {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_arrow_expr(&mut self, _: &mut ArrowExpr) {}
|
||||
|
||||
fn visit_mut_fn_decl(&mut self, n: &mut FnDecl) {
|
||||
self.vars.push(n.ident.clone());
|
||||
|
||||
n.function.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_function(&mut self, _: &mut Function) {}
|
||||
|
||||
fn visit_mut_pat(&mut self, n: &mut Pat) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
if self.in_var_name && self.in_hoisted_var {
|
||||
if let Pat::Ident(i) = n {
|
||||
self.vars.push(i.id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_var_decl(&mut self, n: &mut VarDecl) {
|
||||
self.in_hoisted_var = n.kind == VarDeclKind::Var;
|
||||
n.visit_mut_children_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_var_declarator(&mut self, n: &mut VarDeclarator) {
|
||||
self.in_var_name = true;
|
||||
n.name.visit_mut_with(self);
|
||||
self.in_var_name = false;
|
||||
n.init.visit_mut_with(self);
|
||||
}
|
||||
}
|
||||
|
@ -317,6 +317,8 @@ destructuring/unused_destructuring_object_method_param/input.js
|
||||
directives/class_directives_compression/input.js
|
||||
directives/simple_statement_is_not_a_directive/input.js
|
||||
drop_console/drop_console_1/input.js
|
||||
drop_console/drop_console_2/input.js
|
||||
drop_console/unexpected_side_effects_dropping_console/input.js
|
||||
drop_unused/assign_binding/input.js
|
||||
drop_unused/assign_chain/input.js
|
||||
drop_unused/cascade_drop_assign/input.js
|
||||
@ -737,6 +739,7 @@ labels/labels_5/input.js
|
||||
labels/labels_8/input.js
|
||||
logical_assignment/assign_in_conditional_part_reused/input.js
|
||||
logical_assignment/assignment_in_left_part/input.js
|
||||
logical_assignment/logical_assignment_not_always_happens/input.js
|
||||
logical_assignment/prematurely_evaluate_assignment/input.js
|
||||
logical_assignment/prematurely_evaluate_assignment_inv/input.js
|
||||
loops/dead_code_condition/input.js
|
||||
|
@ -87,8 +87,6 @@ destructuring/issue_t111_2a/input.js
|
||||
destructuring/issue_t111_2b/input.js
|
||||
destructuring/issue_t111_2c/input.js
|
||||
destructuring/issue_t111_3/input.js
|
||||
drop_console/drop_console_2/input.js
|
||||
drop_console/unexpected_side_effects_dropping_console/input.js
|
||||
drop_unused/drop_toplevel_keep_assign/input.js
|
||||
drop_unused/global_var/input.js
|
||||
drop_unused/issue_1583/input.js
|
||||
@ -266,7 +264,6 @@ labels/labels_7/input.js
|
||||
labels/labels_9/input.js
|
||||
logical_assignment/assign_in_conditional_part/input.js
|
||||
logical_assignment/assignment_in_left_part_2/input.js
|
||||
logical_assignment/logical_assignment_not_always_happens/input.js
|
||||
loops/drop_if_break_3/input.js
|
||||
loops/drop_if_break_4/input.js
|
||||
loops/drop_if_else_break_1/input.js
|
||||
|
@ -1,2 +0,0 @@
|
||||
void 0;
|
||||
void 0;
|
Loading…
Reference in New Issue
Block a user