Fix optimizer (#661)

This commit is contained in:
강동윤 2020-02-13 15:03:38 +09:00 committed by GitHub
parent 348052b017
commit f26ef0cfb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 121 additions and 22 deletions

View File

@ -145,10 +145,7 @@ where
let item = if preserved.contains(&idx) {
item
} else {
log::info!("Dce.should_include({})", idx);
if self.should_include(&item) {
log::info!("Preserving {}", idx);
preserved.insert(idx);
self.changed = true;
item = self.fold_in_marking_phase(item)

View File

@ -1,16 +1,13 @@
use super::Dce;
use swc_common::{Fold, FoldWith};
use swc_common::Fold;
use swc_ecma_ast::*;
use swc_ecma_utils::find_ids;
impl Fold<ImportDecl> for Dce<'_> {
fn fold(&mut self, import: ImportDecl) -> ImportDecl {
fn fold(&mut self, mut import: ImportDecl) -> ImportDecl {
if self.is_marked(import.span) {
return import;
}
let mut import: ImportDecl = import.fold_children(self);
// Side effect import
if import.specifiers.is_empty() {
import.span = import.span.apply_mark(self.config.used_mark);
@ -22,19 +19,11 @@ impl Fold<ImportDecl> for Dce<'_> {
return import;
}
// TODO: Drop unused imports.
// e.g) import { foo, bar } from './foo';
// foo()
// Drop unused imports.
import.specifiers.retain(|s| self.should_include(&s));
let ids: Vec<Ident> = find_ids(&import.specifiers);
for id in ids {
for c in &self.included {
if c.0 == id.sym && c.1 == id.span.ctxt() {
import.span = import.span.apply_mark(self.config.used_mark);
return import;
}
}
if !import.specifiers.is_empty() {
import.span = import.span.apply_mark(self.config.used_mark);
}
import

View File

@ -1,5 +1,6 @@
use super::Dce;
use fxhash::FxHashSet;
use swc_atoms::JsWord;
use swc_common::{Visit, VisitWith};
use swc_ecma_ast::*;
use swc_ecma_utils::{ident::IdentLike, ExprExt, Id};
@ -21,6 +22,18 @@ impl Dce<'_> {
}
}
impl SideEffectVisitor<'_> {
fn is_exported(&self, i: &JsWord) -> bool {
self.exports.is_none()
|| self
.exports
.as_ref()
.unwrap()
.iter()
.any(|exported| exported.0 == *i)
}
}
pub(super) struct SideEffectVisitor<'a> {
included: &'a mut FxHashSet<Id>,
exports: Option<&'a [Id]>,
@ -198,3 +211,44 @@ impl Visit<DoWhileStmt> for SideEffectVisitor<'_> {
self.found = true;
}
}
impl Visit<ImportDecl> for SideEffectVisitor<'_> {
fn visit(&mut self, import: &ImportDecl) {
if self.found {
return;
}
if import.specifiers.is_empty() {
self.found = true;
return;
}
import.visit_children(self)
}
}
impl Visit<ExportDecl> for SideEffectVisitor<'_> {
fn visit(&mut self, _: &ExportDecl) {
self.found = true
}
}
impl Visit<ExportDefaultExpr> for SideEffectVisitor<'_> {
fn visit(&mut self, _: &ExportDefaultExpr) {
if self.is_exported(&js_word!("default")) {
self.found = true
}
}
}
impl Visit<NamedExport> for SideEffectVisitor<'_> {
fn visit(&mut self, _: &NamedExport) {
self.found = true
}
}
impl Visit<ExportDefaultDecl> for SideEffectVisitor<'_> {
fn visit(&mut self, _: &ExportDefaultDecl) {
self.found = true;
}
}

View File

@ -3,8 +3,11 @@
#![feature(box_patterns)]
#![feature(specialization)]
use swc_common::chain;
use swc_ecma_transforms::{optimization::simplify::dce::dce, resolver};
use swc_common::{chain, SyntaxContext};
use swc_ecma_transforms::{
optimization::simplify::dce::{self, dce},
resolver,
};
#[macro_use]
mod common;
@ -21,6 +24,26 @@ macro_rules! to {
};
}
fn used(ids: &[&str], src: &str, expected: &str) {
test_transform!(
Default::default(),
|_| chain!(
resolver(),
dce(dce::Config {
used: Some(
ids.into_iter()
.map(|&v| { (v.into(), SyntaxContext::empty()) })
.collect()
),
..Default::default()
})
),
src,
expected,
false
);
}
macro_rules! optimized_out {
($name:ident, $src:expr) => {
to!($name, $src, "");
@ -104,3 +127,39 @@ console.log(c);"
optimized_out!(simple_const, "{const x = 1}");
noop!(assign_op, "x *= 2; use(x)");
optimized_out!(import_default_unused, "import foo from 'foo'");
optimized_out!(import_specific_unused, "import {foo} from 'foo'");
optimized_out!(import_mixed_unused, "import foo, { bar } from 'foo'");
noop!(export_named, "export { x };");
noop!(export_named_from, "export {foo} from 'src';");
noop!(
import_default_export_named,
"import foo from 'src'; export { foo }; "
);
to!(
import_unused_export_named,
"import foo, { bar } from 'src'; export { foo }; ",
"import foo from 'src'; export { foo }; "
);
#[test]
fn export_named_unused() {
used(&["foo"], "export { foo, bat }", "export { foo }");
}
#[test]
fn export_default_expr_unused() {
used(&[], "export default 5;", "");
}
#[test]
fn export_default_expr_used() {
used(&["default"], "export default 5;", "export default 5;");
}