fix(es/minifier): Fix bugs (#2283)

swc_ecma_transforms_optimization:
 - `expr_simplifier`: Don't inject `0` needlessly. (#2257)
This commit is contained in:
Donny/강동윤 2021-09-27 21:05:31 +09:00 committed by GitHub
parent 650e1494d4
commit e8a1710a21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 44613 additions and 42 deletions

6
Cargo.lock generated
View File

@ -2647,10 +2647,11 @@ dependencies = [
[[package]]
name = "swc_ecma_minifier"
version = "0.30.0"
version = "0.30.1"
dependencies = [
"ansi_term 0.12.1",
"anyhow",
"backtrace",
"indexmap",
"once_cell",
"pretty_assertions 0.6.1",
@ -2847,7 +2848,7 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_optimization"
version = "0.44.0"
version = "0.44.1"
dependencies = [
"dashmap",
"indexmap",
@ -3498,6 +3499,7 @@ dependencies = [
"swc",
"swc_common",
"swc_ecmascript",
"tracing",
"wasm-bindgen",
"wasm-bindgen-futures",
]

View File

@ -7,12 +7,13 @@ 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.30.0"
version = "0.30.1"
[features]
debug = []
debug = ["backtrace"]
[dependencies]
backtrace = {version = "0.3.61", optional = true}
indexmap = "1.7.0"
once_cell = "1.5.2"
pretty_assertions = {version = "0.6.1", optional = true}

View File

@ -6,7 +6,7 @@ set -eux
./scripts/run.sh
export RUST_BACKTRACE=1
export RUST_LOG=swc_ecma_minifier=trace
export RUST_LOG=debug,swc_ecma_minifier=trace
UPDATE=1 cargo test -q --test compress projects__files --all-features || true

View File

@ -11,7 +11,7 @@
#
set -eux
export RUST_LOG=swc_ecma_minifier=trace
export RUST_LOG=debug,swc_ecma_minifier=trace
# Run unit tests.
cargo test --all-features --lib

View File

@ -36,6 +36,7 @@ use swc_ecma_transforms::{
};
use swc_ecma_utils::StmtLike;
use swc_ecma_visit::{as_folder, noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith};
use tracing::{span, Level};
mod drop_console;
mod hoist_decls;
@ -234,6 +235,12 @@ where
{
self.data = Some(analyze(&*n, Some(self.marks)));
let _tracing = if cfg!(feature = "debug") {
Some(span!(Level::ERROR, "compressor", "pass" = self.pass).entered())
} else {
None
};
if self.options.passes != 0 && self.options.passes + 1 <= self.pass {
let done = dump(&*n);
tracing::debug!("===== Done =====\n{}", done);
@ -302,7 +309,7 @@ where
let start_time = now();
let mut visitor = pure_optimizer(&self.options, self.marks, self.mode);
let mut visitor = pure_optimizer(&self.options, self.marks, self.mode, self.pass >= 20);
n.apply(&mut visitor);
self.changed |= visitor.changed();
@ -338,6 +345,7 @@ where
self.data.as_ref().unwrap(),
&mut self.optimizer_state,
self.mode,
self.pass >= 20,
);
n.apply(&mut visitor);
self.changed |= visitor.changed();

View File

@ -92,7 +92,7 @@ where
match &p.key {
PropName::Str(s) => {
tracing::debug!(
tracing::trace!(
"hoist_props: Storing a varaible to inline \
properties"
);
@ -100,7 +100,7 @@ where
.insert((name.to_id(), s.value.clone()), value);
}
PropName::Ident(i) => {
tracing::debug!(
tracing::trace!(
"hoist_props: Storing a varaible to inline \
properties"
);

View File

@ -448,6 +448,10 @@ where
span: DUMMY_SP,
expr: Box::new(cur),
}))
} else {
if cfg!(feature = "debug") {
tracing::debug!("if_return: Ignoring return value");
}
}
}
_ => {

View File

@ -218,7 +218,7 @@ where
let should_be_inlined = self.can_be_inlined_for_iife(arg);
if should_be_inlined {
tracing::debug!(
tracing::trace!(
"iife: Trying to inline argument ({}{:?})",
param.id.sym,
param.id.span.ctxt
@ -226,7 +226,7 @@ where
vars.insert(param.to_id(), arg.clone());
}
} else {
tracing::debug!(
tracing::trace!(
"iife: Trying to inline argument ({}{:?}) (undefined)",
param.id.sym,
param.id.span.ctxt
@ -245,11 +245,11 @@ where
let mut optimizer = self.with_ctx(ctx);
match find_body(callee) {
Some(Either::Left(body)) => {
tracing::debug!("inline: Inlining arguments");
tracing::trace!("inline: Inlining arguments");
optimizer.inline_vars_in_node(body, vars);
}
Some(Either::Right(body)) => {
tracing::debug!("inline: Inlining arguments");
tracing::trace!("inline: Inlining arguments");
optimizer.inline_vars_in_node(body, vars);
}
_ => {}
@ -261,7 +261,9 @@ where
where
N: VisitMutWith<Self>,
{
tracing::debug!("inline: inline_vars_in_node");
if cfg!(feature = "debug") {
tracing::trace!("inline: inline_vars_in_node");
}
let ctx = Ctx {
inline_prevented: false,
..self.ctx

View File

@ -21,6 +21,7 @@ use swc_ecma_utils::{
Value,
};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith};
use tracing::{span, Level};
use Value::Known;
mod arguments;
@ -56,6 +57,7 @@ pub(super) fn optimizer<'a, M>(
data: &'a ProgramData,
state: &'a mut OptimizerState,
mode: &'a M,
debug_inifinite_loop: bool,
) -> impl 'a + VisitMut + Repeated
where
M: Mode,
@ -85,6 +87,7 @@ where
done_ctxt,
label: Default::default(),
mode,
debug_inifinite_loop,
}
}
@ -221,6 +224,8 @@ struct Optimizer<'a, M> {
label: Option<Id>,
mode: &'a M,
debug_inifinite_loop: bool,
}
impl<M> Repeated for Optimizer<'_, M> {
@ -667,6 +672,7 @@ where
/// Returns [None] if expression is side-effect-free.
/// If an expression has a side effect, only side effects are returned.
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, e)))]
fn ignore_return_value(&mut self, e: &mut Expr) -> Option<Expr> {
self.optimize_bang_within_logical_ops(e, true);
@ -674,7 +680,9 @@ where
match e {
Expr::Ident(..) | Expr::This(_) | Expr::Invalid(_) | Expr::Lit(..) => {
tracing::debug!("ignore_return_value: Dropping unused expr");
if cfg!(feature = "debug") {
tracing::debug!("ignore_return_value: Dropping unused expr: {}", dump(&*e));
}
self.changed = true;
return None;
}
@ -1586,7 +1594,8 @@ where
span: DUMMY_SP,
value: 0.0,
})));
tracing::trace!("injecting zero to preserve `this` in call");
self.changed = true;
tracing::debug!("injecting zero to preserve `this` in call");
*callee = Box::new(Expr::Seq(SeqExpr {
span: callee.span(),
@ -1819,7 +1828,12 @@ where
_ => {}
}
let expr = self.ignore_return_value(&mut n.expr);
n.expr = expr.map(Box::new).unwrap_or_else(|| undefined(DUMMY_SP));
n.expr = expr.map(Box::new).unwrap_or_else(|| {
if cfg!(feature = "debug") {
tracing::debug!("visit_mut_expr_stmt: Dropped an expression statement");
}
undefined(DUMMY_SP)
});
} else {
match &mut *n.expr {
Expr::Seq(e) => {
@ -2083,6 +2097,7 @@ where
}
}
#[cfg_attr(feature = "debug", tracing::instrument(skip(self, n)))]
fn visit_mut_seq_expr(&mut self, n: &mut SeqExpr) {
{
let ctx = Ctx {
@ -2124,7 +2139,7 @@ where
|| !self.ctx.is_this_aware_callee
|| !should_preserve_zero);
if can_remove {
let ret = if can_remove {
// If negate_iife is true, it's already handled by
// visit_mut_children_with(self) above.
if !self.options.negate_iife {
@ -2134,7 +2149,9 @@ where
self.ignore_return_value(&mut **expr).map(Box::new)
} else {
Some(expr.take())
}
};
ret
})
.collect::<Vec<_>>();
n.exprs = exprs;
@ -2146,6 +2163,18 @@ where
}
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
let _tracing = if cfg!(feature = "debug") && self.debug_inifinite_loop {
let text = dump(&*s);
if text.lines().count() < 10 {
Some(span!(Level::ERROR, "visit_mut_stmt", "start" = &*text).entered())
} else {
None
}
} else {
None
};
let ctx = Ctx {
is_callee: false,
is_delete_arg: false,
@ -2158,6 +2187,14 @@ where
};
s.visit_mut_children_with(&mut *self.with_ctx(ctx));
if cfg!(feature = "debug") && self.debug_inifinite_loop {
let text = dump(&*s);
if text.lines().count() < 10 {
tracing::debug!("after: visit_mut_children_with: {}", text);
}
}
match s {
Stmt::Expr(ExprStmt { expr, .. }) => {
if is_pure_undefined(expr) {
@ -2225,6 +2262,14 @@ where
self.optimize_const_switches(s);
self.optimize_switches(s);
if cfg!(feature = "debug") && self.debug_inifinite_loop {
let text = dump(&*s);
if text.lines().count() < 10 {
tracing::debug!("after: visit_mut_stmt: {}", text);
}
}
}
fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {

View File

@ -1085,7 +1085,7 @@ where
span: DUMMY_SP,
value: 0.0,
})));
tracing::trace!("injecting zero to preserve `this` in call");
tracing::debug!("injecting zero to preserve `this` in call");
*b_callee = Box::new(Expr::Seq(SeqExpr {
span: b_callee.span(),

View File

@ -33,6 +33,7 @@ where
self.drop_unused_vars(var.span, &mut var.name, Some(init));
if var.name.is_invalid() {
tracing::debug!("unused: Removing an unused variable declarator");
let side_effects = self.ignore_return_value(init);
if let Some(e) = side_effects {
if prepend {

View File

@ -1,11 +1,13 @@
use self::ctx::Ctx;
use crate::{
marks::Marks, mode::Mode, option::CompressOptions, util::MoudleItemExt, MAX_PAR_DEPTH,
debug::dump, marks::Marks, mode::Mode, option::CompressOptions, util::MoudleItemExt,
MAX_PAR_DEPTH,
};
use rayon::prelude::*;
use swc_common::{pass::Repeated, util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith};
use tracing::{span, Level};
mod arrows;
mod bools;
@ -27,6 +29,7 @@ pub(crate) fn pure_optimizer<'a, M>(
options: &'a CompressOptions,
marks: Marks,
mode: &'a M,
debug_inifinite_loop: bool,
) -> impl 'a + VisitMut + Repeated
where
M: Mode,
@ -37,6 +40,7 @@ where
ctx: Default::default(),
changed: Default::default(),
mode,
debug_inifinite_loop,
}
}
@ -46,6 +50,8 @@ struct Pure<'a, M> {
ctx: Ctx,
changed: bool,
mode: &'a M,
debug_inifinite_loop: bool,
}
impl<M> Repeated for Pure<'_, M> {
@ -92,6 +98,7 @@ where
ctx: self.ctx,
changed: false,
mode: self.mode,
debug_inifinite_loop: self.debug_inifinite_loop,
};
node.visit_mut_with(&mut v);
@ -110,6 +117,7 @@ where
},
changed: false,
mode: self.mode,
debug_inifinite_loop: self.debug_inifinite_loop,
};
node.visit_mut_with(&mut v);
@ -407,6 +415,18 @@ where
}
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
let _tracing = if cfg!(feature = "debug") && self.debug_inifinite_loop {
let text = dump(&*s);
if text.lines().count() < 10 {
Some(span!(Level::ERROR, "visit_mut_stmt", "start" = &*text).entered())
} else {
None
}
} else {
None
};
{
let ctx = Ctx {
is_update_arg: false,
@ -418,6 +438,14 @@ where
s.visit_mut_children_with(&mut *self.with_ctx(ctx));
}
if cfg!(feature = "debug") && self.debug_inifinite_loop {
let text = dump(&*s);
if text.lines().count() < 10 {
tracing::debug!("after: visit_mut_children_with: {}", text);
}
}
if self.options.drop_debugger {
match s {
Stmt::Debugger(..) => {
@ -440,6 +468,14 @@ where
}
_ => {}
}
if cfg!(feature = "debug") && self.debug_inifinite_loop {
let text = dump(&*s);
if text.lines().count() < 10 {
tracing::debug!("after: visit_mut_stmt: {}", text);
}
}
}
fn visit_mut_stmts(&mut self, items: &mut Vec<Stmt>) {

View File

@ -229,6 +229,7 @@ impl Evaluator {
&serde_json::from_str("{}").unwrap(),
self.marks,
&data,
false,
));
}

View File

@ -496,10 +496,16 @@ fn value_to_expr(v: Value) -> Box<Expr> {
span: DUMMY_SP,
value,
}))),
Value::Number(v) => Box::new(Expr::Lit(Lit::Num(Number {
span: DUMMY_SP,
value: v.as_f64().unwrap(),
}))),
Value::Number(v) => {
if cfg!(feature = "debug") {
tracing::debug!("Creating a numeric literal from value");
}
Box::new(Expr::Lit(Lit::Num(Number {
span: DUMMY_SP,
value: v.as_f64().unwrap(),
})))
}
Value::String(v) => Box::new(Expr::Lit(Lit::Str(Str {
span: DUMMY_SP,
value: v.into(),

View File

@ -15,6 +15,9 @@ pub(crate) mod unit;
///
pub(crate) fn make_number(span: Span, value: f64) -> Expr {
if cfg!(feature = "debug") {
tracing::debug!("Creating a numeric literal");
}
Expr::Lit(Lit::Num(Number { span, value }))
}
@ -84,6 +87,9 @@ impl MoudleItemExt for ModuleItem {
/// - `!0` for true
/// - `!1` for false
pub(crate) fn make_bool(span: Span, value: bool) -> Expr {
if cfg!(feature = "debug") {
tracing::debug!("Creating a boolean literal");
}
Expr::Unary(UnaryExpr {
span,
op: op!("!"),

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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.44.0"
version = "0.44.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1177,20 +1177,30 @@ impl VisitMut for SimplifyExpr {
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,
}))),
);
if let Some(
Expr::Member(..)
| Expr::Ident(Ident {
sym: js_word!("eval"),
..
}),
) = seq.exprs.last().map(|v| &**v)
{
match seq.exprs.get(0).map(|v| &**v) {
Some(Expr::Lit(..) | Expr::Ident(..)) => {}
_ => {
tracing::debug!("Injecting `0` to preserve `this = undefined`");
seq.exprs.insert(
0,
Box::new(Expr::Lit(Lit::Num(Number {
span: DUMMY_SP,
value: 0.0,
}))),
);
}
}
}
seq.visit_mut_with(self);
seq.visit_mut_with(self);
}
}
}
_ => {
@ -1447,6 +1457,8 @@ impl VisitMut for SimplifyExpr {
span: DUMMY_SP,
value: 0.0,
}))));
tracing::trace!("expr_simplifier: Preserving first zero");
}
}
@ -1458,6 +1470,8 @@ impl VisitMut for SimplifyExpr {
span: DUMMY_SP,
value: 0.0,
}))));
tracing::debug!("expr_simplifier: Injected first zero");
}
}
@ -1487,10 +1501,7 @@ impl VisitMut for SimplifyExpr {
self.changed |= len != exprs.len();
*e = SeqExpr {
exprs,
span: e.span,
}
e.exprs = exprs;
}
fn visit_mut_stmt(&mut self, s: &mut Stmt) {

View File

@ -21,5 +21,6 @@ serde_json = "1"
swc = {path = "../"}
swc_common = {path = "../common"}
swc_ecmascript = {path = "../ecmascript"}
tracing = {version = "0.1.28", features = ["release_max_level_off"]}
wasm-bindgen = {version = "0.2", features = ["serde-serialize"]}
wasm-bindgen-futures = "0.4.8"