fix(es/minifier): Fix minifier (#1985)

swc_ecma_minifier:
 - Don't create an identifier starting with number. (#1983)
 - Fix panic. (#1984)
 - Don't use time api on wasm. (#1982)
This commit is contained in:
강동윤 2021-07-31 22:34:16 +09:00 committed by GitHub
parent d600d52157
commit be23e66ca8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 173 additions and 67 deletions

4
Cargo.lock generated
View File

@ -2502,7 +2502,7 @@ dependencies = [
[[package]]
name = "swc_ecma_minifier"
version = "0.16.0"
version = "0.16.1"
dependencies = [
"ansi_term 0.12.1",
"anyhow",
@ -3197,7 +3197,7 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm"
version = "1.2.69"
version = "1.2.70"
dependencies = [
"console_error_panic_hook",
"once_cell",

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.16.0"
version = "0.16.1"
[features]
debug = []

View File

@ -1,6 +1,7 @@
use self::ctx::Ctx;
use crate::util::can_end_conditionally;
use crate::util::idents_used_by;
use crate::util::now;
use fxhash::FxHashMap;
use fxhash::FxHashSet;
use std::collections::hash_map::Entry;
@ -25,7 +26,7 @@ pub(crate) fn analyze<N>(n: &N) -> ProgramData
where
N: VisitWith<UsageAnalyzer>,
{
let start_time = Instant::now();
let start_time = now();
let mut v = UsageAnalyzer {
data: Default::default(),
@ -36,9 +37,11 @@ where
let top_scope = v.scope;
v.data.top.merge(top_scope, false);
let end_time = Instant::now();
if let Some(start_time) = start_time {
let end_time = Instant::now();
log::debug!("Scope analysis took {:?}", end_time - start_time);
log::debug!("Scope analysis took {:?}", end_time - start_time);
}
v.data
}

View File

@ -8,6 +8,7 @@ use crate::debug::dump;
use crate::debug::invoke;
use crate::marks::Marks;
use crate::option::CompressOptions;
use crate::util::now;
use crate::util::Optional;
#[cfg(feature = "pretty_assertions")]
use pretty_assertions::assert_eq;
@ -157,7 +158,7 @@ impl VisitMut for Compressor<'_> {
self.pass
);
let start_time = Instant::now();
let start_time = now();
let mut visitor = expr_simplifier();
n.visit_mut_with(&mut visitor);
@ -169,13 +170,15 @@ impl VisitMut for Compressor<'_> {
}
}
let end_time = Instant::now();
if let Some(start_time) = start_time {
let end_time = Instant::now();
log::info!(
"compress: expr_simplifier took {:?} (pass = {})",
end_time - start_time,
self.pass
);
log::info!(
"compress: expr_simplifier took {:?} (pass = {})",
end_time - start_time,
self.pass
);
}
if cfg!(feature = "debug") && !visitor.changed() {
let simplified = dump(&n.clone().fold_with(&mut fixer(None)));
@ -193,7 +196,7 @@ impl VisitMut for Compressor<'_> {
{
log::info!("compress: Running optimizer (pass = {})", self.pass);
let start_time = Instant::now();
let start_time = now();
// TODO: reset_opt_flags
//
// This is swc version of `node.optimize(this);`.
@ -207,13 +210,15 @@ impl VisitMut for Compressor<'_> {
n.visit_mut_with(&mut visitor);
self.changed |= visitor.changed();
let end_time = Instant::now();
if let Some(start_time) = start_time {
let end_time = Instant::now();
log::info!(
"compress: Optimizer took {:?} (pass = {})",
end_time - start_time,
self.pass
);
log::info!(
"compress: Optimizer took {:?} (pass = {})",
end_time - start_time,
self.pass
);
}
// let done = dump(&*n);
// log::debug!("===== Result =====\n{}", done);
}
@ -225,12 +230,20 @@ impl VisitMut for Compressor<'_> {
"".into()
};
let start_time = Instant::now();
let start_time = now();
let mut v = dead_branch_remover();
n.map_with_mut(|n| n.fold_with(&mut v));
let end_time = Instant::now();
if let Some(start_time) = start_time {
let end_time = Instant::now();
log::info!(
"compress: dead_branch_remover took {:?} (pass = {})",
end_time - start_time,
self.pass
);
}
if cfg!(feature = "debug") {
let simplified = dump(&*n);
@ -244,12 +257,6 @@ impl VisitMut for Compressor<'_> {
}
}
log::info!(
"compress: dead_branch_remover took {:?} (pass = {})",
end_time - start_time,
self.pass
);
self.changed |= v.changed();
}

View File

@ -681,6 +681,8 @@ impl Optimizer<'_> {
if cons.callee.eq_ignore_span(&alt.callee) {
if cons.args.as_ref().map(|v| v.len() <= 1).unwrap_or(true)
&& alt.args.as_ref().map(|v| v.len() <= 1).unwrap_or(true)
&& cons.args.as_ref().map(|v| v.len()).unwrap_or(0)
== alt.args.as_ref().map(|v| v.len()).unwrap_or(0)
&& (cons.args.is_some()
&& cons
.args
@ -698,15 +700,17 @@ impl Optimizer<'_> {
{
let mut args = vec![];
args.push(ExprOrSpread {
spread: None,
expr: Box::new(Expr::Cond(CondExpr {
span: DUMMY_SP,
test: test.take(),
cons: cons.args.as_mut().unwrap()[0].expr.take(),
alt: alt.args.as_mut().unwrap()[0].expr.take(),
})),
});
if cons.args.as_ref().map(|v| v.len()).unwrap_or(0) == 1 {
args.push(ExprOrSpread {
spread: None,
expr: Box::new(Expr::Cond(CondExpr {
span: DUMMY_SP,
test: test.take(),
cons: cons.args.as_mut().unwrap()[0].expr.take(),
alt: alt.args.as_mut().unwrap()[0].expr.take(),
})),
});
}
log::debug!(
"Compreessing if statement into a condiotnal expression of `new` as \

View File

@ -25,6 +25,7 @@ pub use crate::pass::hygiene::optimize_hygiene;
use crate::pass::mangle_names::name_mangler;
use crate::pass::mangle_props::mangle_properties;
use crate::pass::precompress::precompress_optimizer;
use crate::util::now;
use analyzer::analyze;
use pass::postcompress::postcompress_optimizer;
use std::time::Instant;
@ -57,7 +58,7 @@ pub fn optimize(
) -> Module {
let marks = Marks::new();
let start = Instant::now();
let start = now();
if let Some(defs) = options.compress.as_ref().map(|c| &c.global_defs) {
// Apply global defs.
//
@ -70,12 +71,16 @@ pub fn optimize(
m.visit_mut_with(&mut global_defs::globals_defs(defs, extra.top_level_mark));
}
}
log::info!("global_defs took {:?}", Instant::now() - start);
if let Some(start) = start {
log::info!("global_defs took {:?}", Instant::now() - start);
}
if let Some(options) = &options.compress {
let start = Instant::now();
let start = now();
m.visit_mut_with(&mut precompress_optimizer(options.clone()));
log::info!("precompress took {:?}", Instant::now() - start);
if let Some(start) = start {
log::info!("precompress took {:?}", Instant::now() - start);
}
}
m.visit_mut_with(&mut unique_marker());
@ -108,14 +113,18 @@ pub fn optimize(
t.section("compress");
}
if let Some(options) = &options.compress {
let start = Instant::now();
let start = now();
m = m.fold_with(&mut compressor(cm.clone(), marks, &options, comments));
log::info!("compressor took {:?}", Instant::now() - start);
if let Some(start) = start {
log::info!("compressor took {:?}", Instant::now() - start);
}
// Again, we don't need to validate ast
let start = Instant::now();
let start = now();
m.visit_mut_with(&mut postcompress_optimizer(options));
log::info!("postcompressor took {:?}", Instant::now() - start);
if let Some(start) = start {
log::info!("postcompressor took {:?}", Instant::now() - start);
}
}
if let Some(ref mut _t) = timings {

View File

@ -4,6 +4,7 @@ use crate::analyzer::analyze;
use crate::analyzer::ProgramData;
use crate::pass::hygiene::analyzer::HygieneAnalyzer;
use crate::pass::hygiene::analyzer::HygieneData;
use crate::util::now;
use swc_common::Mark;
use swc_common::SyntaxContext;
use swc_common::DUMMY_SP;
@ -70,7 +71,7 @@ impl VisitMut for Optimizer {
fn visit_mut_module(&mut self, n: &mut Module) {
log::info!("hygiene: Analyzing span hygiene");
let start = Instant::now();
let start = now();
let mut analyzer = HygieneAnalyzer {
data: &self.data,
@ -81,13 +82,18 @@ impl VisitMut for Optimizer {
n.visit_with(&Invalid { span: DUMMY_SP }, &mut analyzer);
self.hygiene = analyzer.hygiene;
let end = Instant::now();
log::info!("hygiene: Span hygiene analysis took {:?}", end - start);
let start = end;
if let Some(start) = start {
let end = Instant::now();
log::info!("hygiene: Span hygiene analysis took {:?}", end - start);
}
let start = now();
log::info!("hygiene: Optimizing span hygiene");
n.visit_mut_children_with(self);
let end = Instant::now();
log::info!("hygiene: Span hygiene optimiation took {:?}", end - start);
if let Some(start) = start {
let end = Instant::now();
log::info!("hygiene: Span hygiene optimiation took {:?}", end - start);
}
}
}

View File

@ -56,7 +56,15 @@ impl Mangler {
}
loop {
let sym: JsWord = base54(self.n).into();
let sym = match base54(self.n) {
Some(v) => v,
None => {
self.n += 1;
continue;
}
};
let sym: JsWord = sym.into();
self.n += 1;
if self.preserved_symbols.contains(&sym) {
continue;
@ -75,12 +83,22 @@ impl Mangler {
let new_sym = if let Some(cached) = self.renamed_private.get(&id) {
cached.clone()
} else {
let sym: JsWord = base54(self.private_n).into();
self.private_n += 1;
loop {
let sym = match base54(self.private_n) {
Some(v) => v,
None => {
self.private_n += 1;
continue;
}
};
self.renamed_private.insert(id.clone(), sym.clone());
let sym: JsWord = sym.into();
self.private_n += 1;
sym
self.renamed_private.insert(id.clone(), sym.clone());
break sym;
}
};
private_name.id.sym = new_sym;

View File

@ -91,11 +91,20 @@ impl ManglePropertiesState {
if let Some(cached) = self.cache.get(name) {
Some(cached.clone())
} else {
let n = self.n;
self.n += 1;
let mangled_name: JsWord = base54(n).into();
self.cache.insert(name.clone(), mangled_name.clone());
Some(mangled_name)
loop {
let n = self.n;
self.n += 1;
let sym = match base54(n) {
Some(v) => v,
None => {
continue;
}
};
let mangled_name: JsWord = sym.into();
self.cache.insert(name.clone(), mangled_name.clone());
return Some(mangled_name);
}
}
} else {
None

View File

@ -1,6 +1,8 @@
use std::io::{self, Write};
use std::time::{Duration, Instant};
use crate::util::now;
/// TOOD: Add timings.
#[derive(Default, Debug)]
pub struct Timings {
@ -18,7 +20,7 @@ impl Timings {
pub fn section(&mut self, name: &str) {
self.end_section();
self.current_section = Some((String::from(name), Instant::now()));
self.current_section = now().map(|now| (String::from(name), now));
}
pub fn end_section(&mut self) {

View File

@ -1,7 +1,10 @@
const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
/// Note: This returns `a` for 0.
pub(crate) fn base54(mut n: usize) -> String {
///
/// Returns [None] if the value is is not a valid ideitifer.
pub(crate) fn base54(init: usize) -> Option<String> {
let mut n = init;
let mut ret = String::new();
let mut base = 54;
@ -10,10 +13,21 @@ pub(crate) fn base54(mut n: usize) -> String {
while n > 0 {
n -= 1;
ret.push(CHARS[n % base] as char);
let c = CHARS[n % base] as char;
if ret.is_empty() && c.is_digit(10) {
return None;
}
ret.push(c);
n = n / base;
base = 64;
}
ret
if ret == "do" {
return None;
}
Some(ret)
}

View File

@ -1,3 +1,5 @@
use std::time::Instant;
use fxhash::FxHashSet;
use swc_common::pass::CompilerPass;
use swc_common::pass::Repeated;
@ -475,3 +477,11 @@ pub(crate) fn can_end_conditionally(s: &Stmt) -> bool {
can_end(s, true)
}
pub fn now() -> Option<Instant> {
if cfg!(target_arch = "wasm32") {
None
} else {
Some(Instant::now())
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@swc/core",
"version": "1.2.69",
"version": "1.2.70",
"description": "Super-fast alternative for babel",
"homepage": "https://swc.rs",
"main": "./index.js",

View File

@ -715,3 +715,27 @@ fn tests(dir: PathBuf) {
.map(|_| ())
.expect("failed");
}
#[test]
fn issue_1984() {
testing::run_test2(false, |cm, handler| {
let c = Compiler::new(cm.clone(), Arc::new(handler));
let fm = c.cm.new_source_file(
FileName::Anon,
"
function Set() {}
function useSelection(selectionType, derivedHalfSelectedKeys) {
return selectionType === 'radio'
? new Set()
: new Set(derivedHalfSelectedKeys);
}
"
.into(),
);
c.minify(fm, &serde_json::from_str("{}").unwrap()).unwrap();
Ok(())
})
.unwrap()
}

View File

@ -6,7 +6,7 @@ license = "Apache-2.0 AND MIT"
name = "wasm"
publish = false
repository = "https://github.com/swc-project/swc.git"
version = "1.2.69"
version = "1.2.70"
[lib]
crate-type = ["cdylib"]