mirror of
https://github.com/swc-project/swc.git
synced 2025-01-04 03:21:58 +03:00
fix(es/transforms/optimization): Migrate to VisitMut (#1880)
swc_ecma_transforms_optimization: - Migrate `expr_simplifier` to `VisitMut`. - Migrate `dead_branch_remover` to `VisitMut`.
This commit is contained in:
parent
1a01d0f2c5
commit
ab161793a1
@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "src/lists/*.json"]
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_ecma_minifier"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
|
@ -113,7 +113,16 @@ impl Optimizer<'_> {
|
||||
|
||||
match &*e.prop {
|
||||
Expr::Lit(Lit::Str(s)) => {
|
||||
if s.value == js_word!("") || s.value.starts_with(|c: char| c.is_digit(10)) {
|
||||
if s.value == js_word!("")
|
||||
|| s.value.starts_with(|c: char| c.is_digit(10))
|
||||
|| s.value.contains(|c: char| match c {
|
||||
'0'..='9' => false,
|
||||
'a'..='z' => false,
|
||||
'A'..='Z' => false,
|
||||
'$' => false,
|
||||
_ => true,
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
console.log(
|
||||
(function () {
|
||||
1 + 1;
|
||||
}.a = 1)
|
||||
);
|
||||
console.log((function() {
|
||||
2;
|
||||
}).a = 1);
|
||||
|
@ -1,5 +1,3 @@
|
||||
console.log(
|
||||
(function () {
|
||||
1 + 1;
|
||||
}.a = 1)
|
||||
);
|
||||
console.log((function() {
|
||||
2;
|
||||
}).a = 1);
|
||||
|
@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_ecma_transforms_optimization"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.27.1"
|
||||
version = "0.27.2"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
@ -6,7 +6,7 @@ use swc_common::{
|
||||
Spanned, DUMMY_SP,
|
||||
};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::pass::RepeatedJsPass;
|
||||
use swc_ecma_transforms_base::{ext::MapWithMut, pass::RepeatedJsPass};
|
||||
use swc_ecma_utils::extract_var_ids;
|
||||
use swc_ecma_utils::is_literal;
|
||||
use swc_ecma_utils::prepend;
|
||||
@ -19,8 +19,8 @@ use swc_ecma_utils::IsEmpty;
|
||||
use swc_ecma_utils::StmtExt;
|
||||
use swc_ecma_utils::StmtLike;
|
||||
use swc_ecma_utils::Value::Known;
|
||||
use swc_ecma_visit::noop_visit_type;
|
||||
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith, Node, Visit, VisitWith};
|
||||
use swc_ecma_visit::{as_folder, noop_visit_mut_type, noop_visit_type, VisitMut, VisitMutWith};
|
||||
use swc_ecma_visit::{Node, Visit, VisitWith};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@ -28,8 +28,8 @@ mod tests;
|
||||
/// Not intended for general use. Use [simplifier] instead.
|
||||
///
|
||||
/// Ported from `PeepholeRemoveDeadCode` of google closure compiler.
|
||||
pub fn dead_branch_remover() -> impl RepeatedJsPass + 'static {
|
||||
Remover::default()
|
||||
pub fn dead_branch_remover() -> impl RepeatedJsPass + VisitMut + 'static {
|
||||
as_folder(Remover::default())
|
||||
}
|
||||
|
||||
impl CompilerPass for Remover {
|
||||
@ -54,11 +54,11 @@ struct Remover {
|
||||
normal_block: bool,
|
||||
}
|
||||
|
||||
impl Fold for Remover {
|
||||
noop_fold_type!();
|
||||
impl VisitMut for Remover {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn fold_array_pat(&mut self, p: ArrayPat) -> ArrayPat {
|
||||
let mut p: ArrayPat = p.fold_children_with(self);
|
||||
fn visit_mut_array_pat(&mut self, p: &mut ArrayPat) {
|
||||
p.visit_mut_children_with(self);
|
||||
|
||||
let mut preserved = None;
|
||||
let len = p.elems.len();
|
||||
@ -77,12 +77,10 @@ impl Fold for Remover {
|
||||
if let Some(i) = preserved {
|
||||
p.elems.drain(i..);
|
||||
}
|
||||
|
||||
ArrayPat { ..p }
|
||||
}
|
||||
|
||||
fn fold_expr(&mut self, e: Expr) -> Expr {
|
||||
let e: Expr = e.fold_children_with(self);
|
||||
fn visit_mut_expr(&mut self, e: &mut Expr) {
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
match e {
|
||||
Expr::Assign(AssignExpr {
|
||||
@ -90,15 +88,16 @@ impl Fold for Remover {
|
||||
left: PatOrExpr::Pat(l),
|
||||
right: r,
|
||||
..
|
||||
}) if match &*l {
|
||||
Pat::Ident(l) => match &*r {
|
||||
}) if match &**l {
|
||||
Pat::Ident(l) => match &**r {
|
||||
Expr::Ident(r) => l.id.sym == r.sym && l.id.span.ctxt() == r.span.ctxt(),
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
return Expr::Ident(r.ident().unwrap())
|
||||
*e = Expr::Ident(r.take().ident().unwrap());
|
||||
return;
|
||||
}
|
||||
|
||||
Expr::Assign(AssignExpr {
|
||||
@ -106,12 +105,13 @@ impl Fold for Remover {
|
||||
left: PatOrExpr::Pat(left),
|
||||
right,
|
||||
..
|
||||
}) if match &*left {
|
||||
}) if match &**left {
|
||||
Pat::Array(arr) => arr.elems.is_empty() || arr.elems.iter().all(|v| v.is_none()),
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
return *right;
|
||||
*e = *right.take();
|
||||
return;
|
||||
}
|
||||
|
||||
Expr::Assign(AssignExpr {
|
||||
@ -119,18 +119,19 @@ impl Fold for Remover {
|
||||
left: PatOrExpr::Pat(left),
|
||||
right,
|
||||
..
|
||||
}) if match &*left {
|
||||
}) if match &**left {
|
||||
Pat::Object(obj) => obj.props.is_empty(),
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
return *right;
|
||||
*e = *right.take();
|
||||
return;
|
||||
}
|
||||
|
||||
Expr::Cond(e)
|
||||
if !e.test.may_have_side_effects()
|
||||
&& (e.cons.is_undefined()
|
||||
|| match *e.cons {
|
||||
Expr::Cond(cond)
|
||||
if !cond.test.may_have_side_effects()
|
||||
&& (cond.cons.is_undefined()
|
||||
|| match *cond.cons {
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("void"),
|
||||
ref arg,
|
||||
@ -138,8 +139,8 @@ impl Fold for Remover {
|
||||
}) if !arg.may_have_side_effects() => true,
|
||||
_ => false,
|
||||
})
|
||||
&& (e.alt.is_undefined()
|
||||
|| match *e.alt {
|
||||
&& (cond.alt.is_undefined()
|
||||
|| match *cond.alt {
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("void"),
|
||||
ref arg,
|
||||
@ -148,25 +149,28 @@ impl Fold for Remover {
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
return *e.cons
|
||||
*e = *cond.cons.take();
|
||||
return;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
e
|
||||
}
|
||||
|
||||
fn fold_for_stmt(&mut self, s: ForStmt) -> ForStmt {
|
||||
let s = s.fold_children_with(self);
|
||||
fn visit_mut_for_stmt(&mut self, s: &mut ForStmt) {
|
||||
s.visit_mut_children_with(self);
|
||||
|
||||
ForStmt {
|
||||
init: s.init.and_then(|e| match e {
|
||||
s.init = s.init.take().and_then(|e| match e {
|
||||
VarDeclOrExpr::Expr(e) => ignore_result(*e).map(Box::new).map(VarDeclOrExpr::from),
|
||||
_ => Some(e),
|
||||
}),
|
||||
update: s.update.and_then(|e| ignore_result(*e).map(Box::new)),
|
||||
test: s.test.and_then(|e| {
|
||||
});
|
||||
|
||||
s.update = s
|
||||
.update
|
||||
.take()
|
||||
.and_then(|e| ignore_result(*e).map(Box::new));
|
||||
|
||||
s.test = s.test.take().and_then(|e| {
|
||||
let span = e.span();
|
||||
if let Known(value) = e.as_pure_bool() {
|
||||
return if value {
|
||||
@ -177,20 +181,22 @@ impl Fold for Remover {
|
||||
}
|
||||
|
||||
Some(e)
|
||||
}),
|
||||
..s
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn fold_object_pat(&mut self, p: ObjectPat) -> ObjectPat {
|
||||
let mut p = p.fold_children_with(self);
|
||||
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
|
||||
self.fold_stmt_like(n)
|
||||
}
|
||||
|
||||
fn visit_mut_object_pat(&mut self, p: &mut ObjectPat) {
|
||||
p.visit_mut_children_with(self);
|
||||
|
||||
// Don't remove if there exists a rest pattern
|
||||
if p.props.iter().any(|p| match p {
|
||||
ObjectPatProp::Rest(..) => true,
|
||||
_ => false,
|
||||
}) {
|
||||
return p;
|
||||
return;
|
||||
}
|
||||
|
||||
fn is_computed(k: &PropName) -> bool {
|
||||
@ -220,12 +226,10 @@ impl Fold for Remover {
|
||||
}
|
||||
_ => true,
|
||||
});
|
||||
|
||||
p
|
||||
}
|
||||
|
||||
fn fold_object_pat_prop(&mut self, p: ObjectPatProp) -> ObjectPatProp {
|
||||
let p = p.fold_children_with(self);
|
||||
fn visit_mut_object_pat_prop(&mut self, p: &mut ObjectPatProp) {
|
||||
p.visit_mut_children_with(self);
|
||||
|
||||
match p {
|
||||
ObjectPatProp::Assign(AssignPatProp {
|
||||
@ -233,7 +237,7 @@ impl Fold for Remover {
|
||||
key,
|
||||
value: Some(expr),
|
||||
}) if expr.is_undefined()
|
||||
|| match *expr {
|
||||
|| match **expr {
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("void"),
|
||||
ref arg,
|
||||
@ -242,26 +246,25 @@ impl Fold for Remover {
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
return ObjectPatProp::Assign(AssignPatProp {
|
||||
span,
|
||||
key,
|
||||
*p = ObjectPatProp::Assign(AssignPatProp {
|
||||
span: *span,
|
||||
key: key.take(),
|
||||
value: None,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
p
|
||||
}
|
||||
|
||||
fn fold_pat(&mut self, p: Pat) -> Pat {
|
||||
let p = p.fold_children_with(self);
|
||||
fn visit_mut_pat(&mut self, p: &mut Pat) {
|
||||
p.visit_mut_children_with(self);
|
||||
|
||||
match p {
|
||||
Pat::Assign(p)
|
||||
if p.right.is_undefined()
|
||||
|| match *p.right {
|
||||
Pat::Assign(assign)
|
||||
if assign.right.is_undefined()
|
||||
|| match *assign.right {
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("void"),
|
||||
ref arg,
|
||||
@ -270,40 +273,44 @@ impl Fold for Remover {
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
return *p.left;
|
||||
*p = *assign.left.take();
|
||||
return;
|
||||
}
|
||||
|
||||
Pat::Assign(p)
|
||||
if match *p.left {
|
||||
Pat::Assign(assign)
|
||||
if match *assign.left {
|
||||
Pat::Object(ref o) => o.props.is_empty(),
|
||||
_ => false,
|
||||
} && p.right.is_number() =>
|
||||
} && assign.right.is_number() =>
|
||||
{
|
||||
return *p.left;
|
||||
*p = *assign.left.take();
|
||||
return;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
p
|
||||
}
|
||||
|
||||
fn fold_seq_expr(&mut self, e: SeqExpr) -> SeqExpr {
|
||||
let mut e: SeqExpr = e.fold_children_with(self);
|
||||
fn visit_mut_seq_expr(&mut self, e: &mut SeqExpr) {
|
||||
e.visit_mut_children_with(self);
|
||||
if e.exprs.is_empty() {
|
||||
return e;
|
||||
return;
|
||||
}
|
||||
|
||||
let last = e.exprs.pop().unwrap();
|
||||
let mut exprs = e.exprs.move_flat_map(|e| ignore_result(*e).map(Box::new));
|
||||
let mut exprs = e
|
||||
.exprs
|
||||
.take()
|
||||
.move_flat_map(|e| ignore_result(*e).map(Box::new));
|
||||
exprs.push(last);
|
||||
|
||||
SeqExpr { exprs, ..e }
|
||||
e.exprs = exprs;
|
||||
}
|
||||
|
||||
fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
|
||||
let stmt = stmt.fold_children_with(self);
|
||||
fn visit_mut_stmt(&mut self, stmt: &mut Stmt) {
|
||||
stmt.visit_mut_children_with(self);
|
||||
|
||||
stmt.map_with_mut(|stmt| {
|
||||
match stmt {
|
||||
Stmt::If(IfStmt {
|
||||
span,
|
||||
@ -359,7 +366,9 @@ impl Fold for Remover {
|
||||
|
||||
self.changed = true;
|
||||
|
||||
return Stmt::Block(BlockStmt { span, stmts }).fold_with(self);
|
||||
let mut block = Stmt::Block(BlockStmt { span, stmts });
|
||||
block.visit_mut_with(self);
|
||||
return block;
|
||||
}
|
||||
|
||||
let alt = match &alt {
|
||||
@ -432,7 +441,9 @@ impl Fold for Remover {
|
||||
&& !is_block_scoped_stuff(&stmts[0])
|
||||
&& stmt_depth(&stmts[0]) <= 1
|
||||
{
|
||||
stmts.into_iter().next().unwrap().fold_with(self)
|
||||
let mut v = stmts.into_iter().next().unwrap();
|
||||
v.visit_mut_with(self);
|
||||
v
|
||||
} else {
|
||||
Stmt::Block(BlockStmt { span, stmts })
|
||||
}
|
||||
@ -509,9 +520,10 @@ impl Fold for Remover {
|
||||
return Some(Stmt::Decl(Decl::Var(VarDecl {
|
||||
span: DUMMY_SP,
|
||||
kind: VarDeclKind::Var,
|
||||
decls: var
|
||||
.decls
|
||||
.move_map(|decl| VarDeclarator { init: None, ..decl }),
|
||||
decls: var.decls.move_map(|decl| VarDeclarator {
|
||||
init: None,
|
||||
..decl
|
||||
}),
|
||||
declare: false,
|
||||
})))
|
||||
}
|
||||
@ -568,11 +580,12 @@ impl Fold for Remover {
|
||||
prepend(&mut stmts, expr.into_stmt());
|
||||
}
|
||||
|
||||
return Stmt::Block(BlockStmt {
|
||||
let mut block = Stmt::Block(BlockStmt {
|
||||
span: s.span,
|
||||
stmts,
|
||||
})
|
||||
.fold_with(self);
|
||||
});
|
||||
block.visit_mut_with(self);
|
||||
return block;
|
||||
}
|
||||
|
||||
let mut non_constant_case_idx = None;
|
||||
@ -671,11 +684,12 @@ impl Fold for Remover {
|
||||
);
|
||||
}
|
||||
|
||||
return Stmt::Block(BlockStmt {
|
||||
let mut block = Stmt::Block(BlockStmt {
|
||||
span: s.span,
|
||||
stmts,
|
||||
})
|
||||
.fold_with(self);
|
||||
});
|
||||
block.visit_mut_with(self);
|
||||
return block;
|
||||
}
|
||||
} else if are_all_tests_known {
|
||||
match *s.discriminant {
|
||||
@ -686,11 +700,12 @@ impl Fold for Remover {
|
||||
let stmts = s.cases.remove(i).cons;
|
||||
let stmts = remove_break(stmts);
|
||||
|
||||
return Stmt::Block(BlockStmt {
|
||||
let mut block = Stmt::Block(BlockStmt {
|
||||
span: s.span,
|
||||
stmts,
|
||||
})
|
||||
.fold_with(self);
|
||||
});
|
||||
block.visit_mut_with(self);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -703,7 +718,8 @@ impl Fold for Remover {
|
||||
let mut breaked = false;
|
||||
// Remove unmatchable cases.
|
||||
s.cases = s.cases.move_flat_map(|case| {
|
||||
if non_constant_case_idx.is_some() && idx >= non_constant_case_idx.unwrap()
|
||||
if non_constant_case_idx.is_some()
|
||||
&& idx >= non_constant_case_idx.unwrap()
|
||||
{
|
||||
idx += 1;
|
||||
return Some(case);
|
||||
@ -766,11 +782,12 @@ impl Fold for Remover {
|
||||
{
|
||||
let stmts = s.cases.pop().unwrap().cons;
|
||||
let stmts = remove_break(stmts);
|
||||
return Stmt::Block(BlockStmt {
|
||||
let mut block = Stmt::Block(BlockStmt {
|
||||
span: s.span,
|
||||
stmts,
|
||||
})
|
||||
.fold_with(self);
|
||||
});
|
||||
block.visit_mut_with(self);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
@ -866,7 +883,8 @@ impl Fold for Remover {
|
||||
} else {
|
||||
let body = s.body.extract_var_ids_as_var();
|
||||
let body = body.map(Decl::Var).map(Stmt::Decl);
|
||||
let body = body.unwrap_or_else(|| Stmt::Empty(EmptyStmt { span: s.span }));
|
||||
let body =
|
||||
body.unwrap_or_else(|| Stmt::Empty(EmptyStmt { span: s.span }));
|
||||
|
||||
if purity.is_pure() {
|
||||
body
|
||||
@ -898,17 +916,17 @@ impl Fold for Remover {
|
||||
body: s.body,
|
||||
})
|
||||
} else {
|
||||
let mut body = prepare_loop_body_for_inlining(*s.body);
|
||||
body.visit_mut_with(self);
|
||||
|
||||
if let Some(test) = ignore_result(*s.test) {
|
||||
BlockStmt {
|
||||
span: s.span,
|
||||
stmts: vec![
|
||||
prepare_loop_body_for_inlining(*s.body).fold_with(self),
|
||||
test.into_stmt(),
|
||||
],
|
||||
stmts: vec![body, test.into_stmt()],
|
||||
}
|
||||
.into()
|
||||
} else {
|
||||
prepare_loop_body_for_inlining(*s.body).fold_with(self)
|
||||
body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -944,16 +962,21 @@ impl Fold for Remover {
|
||||
|
||||
_ => stmt,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn fold_switch_stmt(&mut self, s: SwitchStmt) -> SwitchStmt {
|
||||
let s: SwitchStmt = s.fold_children_with(self);
|
||||
fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
|
||||
self.fold_stmt_like(n)
|
||||
}
|
||||
|
||||
fn visit_mut_switch_stmt(&mut self, s: &mut SwitchStmt) {
|
||||
s.visit_mut_children_with(self);
|
||||
|
||||
if s.cases.iter().any(|case| match case.test.as_deref() {
|
||||
Some(Expr::Update(..)) => true,
|
||||
_ => false,
|
||||
}) {
|
||||
return s;
|
||||
return;
|
||||
}
|
||||
|
||||
if s.cases.iter().all(|case| {
|
||||
@ -966,35 +989,26 @@ impl Fold for Remover {
|
||||
_ => false,
|
||||
}
|
||||
}) {
|
||||
return SwitchStmt { cases: vec![], ..s };
|
||||
s.cases.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn fold_module_items(&mut self, n: Vec<ModuleItem>) -> Vec<ModuleItem> {
|
||||
self.fold_stmt_like(n)
|
||||
}
|
||||
|
||||
fn fold_stmts(&mut self, n: Vec<Stmt>) -> Vec<Stmt> {
|
||||
self.fold_stmt_like(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl Remover {
|
||||
fn fold_stmt_like<T>(&mut self, stmts: Vec<T>) -> Vec<T>
|
||||
fn fold_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: StmtLike + VisitWith<Hoister> + FoldWith<Self>,
|
||||
T: StmtLike + VisitWith<Hoister> + VisitMutWith<Self>,
|
||||
{
|
||||
let is_block_stmt = self.normal_block;
|
||||
self.normal_block = false;
|
||||
|
||||
let mut buf = Vec::with_capacity(stmts.len());
|
||||
let mut new_stmts = Vec::with_capacity(stmts.len());
|
||||
|
||||
let mut iter = stmts.into_iter();
|
||||
while let Some(stmt_like) = iter.next() {
|
||||
let mut iter = stmts.take().into_iter();
|
||||
while let Some(mut stmt_like) = iter.next() {
|
||||
self.normal_block = true;
|
||||
let stmt_like = stmt_like.fold_with(self);
|
||||
stmt_like.visit_mut_with(self);
|
||||
self.normal_block = false;
|
||||
|
||||
let stmt_like = match stmt_like.try_into_stmt() {
|
||||
@ -1031,12 +1045,12 @@ impl Remover {
|
||||
});
|
||||
decls.extend(ids);
|
||||
}
|
||||
Err(item) => buf.push(item),
|
||||
Err(item) => new_stmts.push(item),
|
||||
}
|
||||
}
|
||||
|
||||
if !decls.is_empty() {
|
||||
buf.push(T::from_stmt(Stmt::Decl(Decl::Var(VarDecl {
|
||||
new_stmts.push(T::from_stmt(Stmt::Decl(Decl::Var(VarDecl {
|
||||
span: DUMMY_SP,
|
||||
kind: VarDeclKind::Var,
|
||||
decls,
|
||||
@ -1044,27 +1058,26 @@ impl Remover {
|
||||
}))));
|
||||
}
|
||||
|
||||
buf.extend(hoisted_fns);
|
||||
new_stmts.extend(hoisted_fns);
|
||||
|
||||
let stmt_like = T::from_stmt(stmt);
|
||||
buf.push(stmt_like);
|
||||
|
||||
return buf;
|
||||
new_stmts.push(stmt_like);
|
||||
*stmts = new_stmts;
|
||||
return;
|
||||
}
|
||||
|
||||
Stmt::Block(BlockStmt { span, stmts, .. }) => {
|
||||
Stmt::Block(BlockStmt {
|
||||
span, mut stmts, ..
|
||||
}) => {
|
||||
if stmts.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !is_ok_to_inline_block(&stmts) {
|
||||
BlockStmt {
|
||||
span,
|
||||
stmts: stmts.fold_with(self),
|
||||
}
|
||||
.into()
|
||||
stmts.visit_mut_with(self);
|
||||
BlockStmt { span, stmts }.into()
|
||||
} else {
|
||||
buf.extend(
|
||||
new_stmts.extend(
|
||||
stmts
|
||||
.into_iter()
|
||||
.filter(|s| match s {
|
||||
@ -1092,7 +1105,7 @@ impl Remover {
|
||||
let expr = ignore_result(*test);
|
||||
|
||||
if let Some(expr) = expr {
|
||||
buf.push(T::from_stmt(Stmt::Expr(ExprStmt {
|
||||
new_stmts.push(T::from_stmt(Stmt::Expr(ExprStmt {
|
||||
span: DUMMY_SP,
|
||||
expr: Box::new(expr),
|
||||
})));
|
||||
@ -1104,13 +1117,13 @@ impl Remover {
|
||||
if let Some(var) =
|
||||
alt.and_then(|alt| alt.extract_var_ids_as_var())
|
||||
{
|
||||
buf.push(T::from_stmt(Stmt::Decl(Decl::Var(var))))
|
||||
new_stmts.push(T::from_stmt(Stmt::Decl(Decl::Var(var))))
|
||||
}
|
||||
*cons
|
||||
} else {
|
||||
// Hoist vars from cons
|
||||
if let Some(var) = cons.extract_var_ids_as_var() {
|
||||
buf.push(T::from_stmt(Stmt::Decl(Decl::Var(var))))
|
||||
new_stmts.push(T::from_stmt(Stmt::Decl(Decl::Var(var))))
|
||||
}
|
||||
match alt {
|
||||
Some(alt) => *alt,
|
||||
@ -1135,10 +1148,10 @@ impl Remover {
|
||||
Err(stmt_like) => stmt_like,
|
||||
};
|
||||
|
||||
buf.push(stmt_like);
|
||||
new_stmts.push(stmt_like);
|
||||
}
|
||||
|
||||
buf
|
||||
*stmts = new_stmts
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use swc_common::{
|
||||
Span, Spanned,
|
||||
};
|
||||
use swc_ecma_ast::{Ident, Lit, *};
|
||||
use swc_ecma_transforms_base::ext::ExprRefExt;
|
||||
use swc_ecma_transforms_base::ext::{ExprRefExt, MapWithMut};
|
||||
use swc_ecma_transforms_base::pass::RepeatedJsPass;
|
||||
use swc_ecma_utils::alias_ident_for;
|
||||
use swc_ecma_utils::extract_side_effects_to;
|
||||
@ -26,7 +26,7 @@ use swc_ecma_utils::StringType;
|
||||
use swc_ecma_utils::SymbolType;
|
||||
use swc_ecma_utils::UndefinedType;
|
||||
use swc_ecma_utils::Value;
|
||||
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
|
||||
use swc_ecma_visit::{as_folder, noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
use Value::Known;
|
||||
use Value::Unknown;
|
||||
|
||||
@ -45,8 +45,8 @@ macro_rules! try_val {
|
||||
/// Not intended for general use. Use [simplifier] instead.
|
||||
///
|
||||
/// Ported from `PeepholeFoldConstants` of google closure compiler.
|
||||
pub fn expr_simplifier() -> impl RepeatedJsPass + 'static {
|
||||
SimplifyExpr::default()
|
||||
pub fn expr_simplifier() -> impl RepeatedJsPass + VisitMut + 'static {
|
||||
as_folder(SimplifyExpr::default())
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -495,11 +495,12 @@ impl SimplifyExpr {
|
||||
*node
|
||||
} else {
|
||||
self.changed = true;
|
||||
let seq = SeqExpr {
|
||||
let mut seq = SeqExpr {
|
||||
span,
|
||||
exprs: vec![left, node],
|
||||
}
|
||||
.fold_with(self);
|
||||
};
|
||||
|
||||
seq.visit_mut_with(self);
|
||||
|
||||
Expr::Seq(seq)
|
||||
};
|
||||
@ -1145,48 +1146,95 @@ impl SimplifyExpr {
|
||||
}
|
||||
}
|
||||
|
||||
impl Fold for SimplifyExpr {
|
||||
noop_fold_type!();
|
||||
impl VisitMut for SimplifyExpr {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
/// Currently noop
|
||||
#[inline]
|
||||
fn fold_opt_chain_expr(&mut self, n: OptChainExpr) -> OptChainExpr {
|
||||
n
|
||||
}
|
||||
|
||||
fn fold_assign_expr(&mut self, n: AssignExpr) -> AssignExpr {
|
||||
fn visit_mut_assign_expr(&mut self, n: &mut AssignExpr) {
|
||||
let old = self.is_modifying;
|
||||
self.is_modifying = true;
|
||||
let left = n.left.fold_with(self);
|
||||
n.left.visit_mut_with(self);
|
||||
self.is_modifying = old;
|
||||
|
||||
self.is_modifying = false;
|
||||
let right = n.right.fold_with(self);
|
||||
n.right.visit_mut_with(self);
|
||||
self.is_modifying = old;
|
||||
|
||||
AssignExpr { left, right, ..n }
|
||||
}
|
||||
|
||||
fn fold_expr(&mut self, expr: Expr) -> Expr {
|
||||
/// This is overriden to preserve `this`.
|
||||
fn visit_mut_call_expr(&mut self, n: &mut CallExpr) {
|
||||
let old_in_callee = self.in_callee;
|
||||
|
||||
self.in_callee = true;
|
||||
match &mut n.callee {
|
||||
ExprOrSuper::Super(..) => {}
|
||||
ExprOrSuper::Expr(e) => match &mut **e {
|
||||
Expr::Seq(seq) => {
|
||||
if seq.exprs.len() == 1 {
|
||||
let mut expr = seq.exprs.take().into_iter().next().unwrap();
|
||||
expr.visit_mut_with(self);
|
||||
*e = expr;
|
||||
} else {
|
||||
match seq.exprs.get(0).map(|v| &**v) {
|
||||
Some(Expr::Lit(..) | Expr::Ident(..)) => {}
|
||||
_ => {
|
||||
seq.exprs.insert(
|
||||
0,
|
||||
Box::new(Expr::Lit(Lit::Num(Number {
|
||||
span: DUMMY_SP,
|
||||
value: 0.0,
|
||||
}))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
seq.visit_mut_with(self);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
e.visit_mut_with(self);
|
||||
}
|
||||
},
|
||||
}
|
||||
self.in_callee = old_in_callee;
|
||||
|
||||
n.args.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_expr(&mut self, expr: &mut Expr) {
|
||||
match expr {
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("delete"), ..
|
||||
}) => return expr,
|
||||
}) => return,
|
||||
_ => {}
|
||||
}
|
||||
// fold children before doing something more.
|
||||
let expr = expr.fold_children_with(self);
|
||||
expr.visit_mut_children_with(self);
|
||||
|
||||
match expr {
|
||||
// Do nothing.
|
||||
Expr::Lit(_) | Expr::This(..) => expr,
|
||||
Expr::Lit(_) | Expr::This(..) => return,
|
||||
|
||||
// Remove parenthesis. This may break ast, but it will be fixed up later.
|
||||
Expr::Paren(ParenExpr { expr, .. }) => {
|
||||
Expr::Paren(ParenExpr { expr: e, .. }) => {
|
||||
self.changed = true;
|
||||
*expr
|
||||
*expr = *e.take();
|
||||
return;
|
||||
}
|
||||
|
||||
Expr::Unary(..)
|
||||
| Expr::Bin(..)
|
||||
| Expr::Member(..)
|
||||
| Expr::Cond(..)
|
||||
| Expr::Seq(..)
|
||||
| Expr::Array(..)
|
||||
| Expr::Object(..)
|
||||
| Expr::New(..) => {}
|
||||
|
||||
_ => return,
|
||||
}
|
||||
|
||||
expr.map_with_mut(|expr| {
|
||||
match expr {
|
||||
Expr::Unary(expr) => self.fold_unary(expr),
|
||||
Expr::Bin(expr) => self.fold_bin(expr),
|
||||
|
||||
@ -1264,7 +1312,9 @@ impl Fold for SimplifyExpr {
|
||||
|
||||
for p in props {
|
||||
match p {
|
||||
PropOrSpread::Spread(SpreadElement { expr, .. }) if expr.is_object() => {
|
||||
PropOrSpread::Spread(SpreadElement { expr, .. })
|
||||
if expr.is_object() =>
|
||||
{
|
||||
let props = expr.object().unwrap().props;
|
||||
ps.extend(props)
|
||||
}
|
||||
@ -1302,15 +1352,39 @@ impl Fold for SimplifyExpr {
|
||||
// be conservative.
|
||||
_ => expr,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn fold_pat(&mut self, p: Pat) -> Pat {
|
||||
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
|
||||
let mut child = Self::default();
|
||||
|
||||
n.visit_mut_children_with(&mut child);
|
||||
self.changed |= child.changed;
|
||||
|
||||
if !child.vars.is_empty() {
|
||||
prepend(
|
||||
n,
|
||||
ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
||||
span: DUMMY_SP,
|
||||
kind: VarDeclKind::Var,
|
||||
declare: false,
|
||||
decls: child.vars,
|
||||
}))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently noop
|
||||
#[inline]
|
||||
fn visit_mut_opt_chain_expr(&mut self, _: &mut OptChainExpr) {}
|
||||
|
||||
fn visit_mut_pat(&mut self, p: &mut Pat) {
|
||||
p.visit_mut_children_with(self);
|
||||
|
||||
match p {
|
||||
Pat::Assign(a) => AssignPat {
|
||||
right: {
|
||||
let default = a.right.fold_with(self);
|
||||
if default.is_undefined()
|
||||
|| match *default {
|
||||
Pat::Assign(a) => {
|
||||
if a.right.is_undefined()
|
||||
|| match *a.right {
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("void"),
|
||||
ref arg,
|
||||
@ -1319,61 +1393,17 @@ impl Fold for SimplifyExpr {
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
return *a.left;
|
||||
}
|
||||
|
||||
default
|
||||
},
|
||||
..a
|
||||
}
|
||||
.into(),
|
||||
_ => p,
|
||||
*p = *a.left.take();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// This is overriden to preserve `this`.
|
||||
fn fold_call_expr(&mut self, n: CallExpr) -> CallExpr {
|
||||
let old_in_callee = self.in_callee;
|
||||
|
||||
self.in_callee = true;
|
||||
let callee = match n.callee {
|
||||
ExprOrSuper::Super(..) => n.callee,
|
||||
ExprOrSuper::Expr(e) => match *e {
|
||||
Expr::Seq(mut seq) => {
|
||||
if seq.exprs.len() == 1 {
|
||||
ExprOrSuper::Expr(seq.exprs.into_iter().next().unwrap().fold_with(self))
|
||||
} else {
|
||||
match seq.exprs.get(0).map(|v| &**v) {
|
||||
Some(Expr::Lit(..) | Expr::Ident(..)) => {}
|
||||
_ => {
|
||||
seq.exprs.insert(
|
||||
0,
|
||||
Box::new(Expr::Lit(Lit::Num(Number {
|
||||
span: DUMMY_SP,
|
||||
value: 0.0,
|
||||
}))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ExprOrSuper::Expr(Box::new(Expr::Seq(seq.fold_with(self))))
|
||||
}
|
||||
}
|
||||
_ => ExprOrSuper::Expr(e.fold_with(self)),
|
||||
},
|
||||
};
|
||||
self.in_callee = old_in_callee;
|
||||
|
||||
CallExpr {
|
||||
callee,
|
||||
args: n.args.fold_with(self),
|
||||
..n
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Drops unused values
|
||||
fn fold_seq_expr(&mut self, e: SeqExpr) -> SeqExpr {
|
||||
let mut e = e.fold_children_with(self);
|
||||
fn visit_mut_seq_expr(&mut self, e: &mut SeqExpr) {
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
let len = e.exprs.len();
|
||||
|
||||
@ -1382,7 +1412,7 @@ impl Fold for SimplifyExpr {
|
||||
// Expressions except last one
|
||||
let mut exprs = Vec::with_capacity(e.exprs.len() + 1);
|
||||
|
||||
for expr in e.exprs {
|
||||
for expr in e.exprs.take() {
|
||||
match *expr {
|
||||
Expr::Lit(Lit::Num(n)) if self.in_callee && n.value == 0.0 => {
|
||||
if exprs.is_empty() {
|
||||
@ -1429,21 +1459,31 @@ impl Fold for SimplifyExpr {
|
||||
|
||||
self.changed |= len != exprs.len();
|
||||
|
||||
SeqExpr {
|
||||
*e = SeqExpr {
|
||||
exprs,
|
||||
span: e.span,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_stmts(&mut self, mut n: Vec<Stmt>) -> Vec<Stmt> {
|
||||
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
|
||||
let old_is_modifying = self.is_modifying;
|
||||
self.is_modifying = false;
|
||||
let old_is_arg_of_update = self.is_arg_of_update;
|
||||
self.is_arg_of_update = false;
|
||||
s.visit_mut_children_with(self);
|
||||
self.is_arg_of_update = old_is_arg_of_update;
|
||||
self.is_modifying = old_is_modifying;
|
||||
}
|
||||
|
||||
fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
|
||||
let mut child = Self::default();
|
||||
|
||||
n = n.fold_children_with(&mut child);
|
||||
n.visit_mut_children_with(&mut child);
|
||||
self.changed |= child.changed;
|
||||
|
||||
if !child.vars.is_empty() {
|
||||
prepend(
|
||||
&mut n,
|
||||
n,
|
||||
Stmt::Decl(Decl::Var(VarDecl {
|
||||
span: DUMMY_SP,
|
||||
kind: VarDeclKind::Var,
|
||||
@ -1452,48 +1492,20 @@ impl Fold for SimplifyExpr {
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
n
|
||||
}
|
||||
|
||||
fn fold_module_items(&mut self, mut n: Vec<ModuleItem>) -> Vec<ModuleItem> {
|
||||
let mut child = Self::default();
|
||||
|
||||
n = n.fold_children_with(&mut child);
|
||||
self.changed |= child.changed;
|
||||
|
||||
if !child.vars.is_empty() {
|
||||
prepend(
|
||||
&mut n,
|
||||
ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
||||
span: DUMMY_SP,
|
||||
kind: VarDeclKind::Var,
|
||||
declare: false,
|
||||
decls: child.vars,
|
||||
}))),
|
||||
);
|
||||
}
|
||||
|
||||
n
|
||||
}
|
||||
|
||||
fn fold_stmt(&mut self, s: Stmt) -> Stmt {
|
||||
let old_is_modifying = self.is_modifying;
|
||||
self.is_modifying = false;
|
||||
let old_is_arg_of_update = self.is_arg_of_update;
|
||||
self.is_arg_of_update = false;
|
||||
let s = s.fold_children_with(self);
|
||||
self.is_arg_of_update = old_is_arg_of_update;
|
||||
self.is_modifying = old_is_modifying;
|
||||
s
|
||||
}
|
||||
|
||||
fn fold_update_expr(&mut self, n: UpdateExpr) -> UpdateExpr {
|
||||
fn visit_mut_update_expr(&mut self, n: &mut UpdateExpr) {
|
||||
let old = self.is_modifying;
|
||||
self.is_modifying = true;
|
||||
let arg = n.arg.fold_with(self);
|
||||
n.arg.visit_mut_with(self);
|
||||
self.is_modifying = old;
|
||||
}
|
||||
|
||||
fn visit_mut_var_decl_or_pat(&mut self, n: &mut VarDeclOrPat) {
|
||||
let old = self.is_modifying;
|
||||
self.is_modifying = true;
|
||||
n.visit_mut_children_with(self);
|
||||
self.is_modifying = old;
|
||||
UpdateExpr { arg, ..n }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
use super::SimplifyExpr;
|
||||
use super::expr_simplifier;
|
||||
use swc_ecma_transforms_testing::test_transform;
|
||||
|
||||
fn fold(src: &str, expected: &str) {
|
||||
test_transform(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|_| SimplifyExpr::default(),
|
||||
|_| expr_simplifier(),
|
||||
src,
|
||||
expected,
|
||||
true,
|
||||
|
Loading…
Reference in New Issue
Block a user