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:
강동윤 2021-07-02 12:20:24 +09:00 committed by GitHub
parent 1a01d0f2c5
commit ab161793a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 994 additions and 964 deletions

View File

@ -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 = []

View File

@ -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;
}

View File

@ -1,5 +1,3 @@
console.log(
(function () {
1 + 1;
}.a = 1)
);
console.log((function() {
2;
}).a = 1);

View File

@ -1,5 +1,3 @@
console.log(
(function () {
1 + 1;
}.a = 1)
);
console.log((function() {
2;
}).a = 1);

View File

@ -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]

View File

@ -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
}
}

View File

@ -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 }
}
}

View File

@ -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,