fix(ecmascript/transforms): Fix dce (#1301)

swc_bunder:
 - Fix `keywords` pass.

swc_ecma_codegen:
 - Ensure that the code generator handles unicode characters properly. (denoland/deno#8925)

swc_ecma_parser:
 - Ensure that the parser handles unicode characters properly. (denoland/deno#8925)

swc_ecma_transforms:
 - Fix dce.
This commit is contained in:
강동윤 2021-01-05 18:45:26 +09:00 committed by GitHub
parent 5d88e8ba54
commit 842b6f953c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 495 additions and 62 deletions

View File

@ -8,7 +8,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_bundler"
repository = "https://github.com/swc-project/swc.git"
version = "0.19.0"
version = "0.19.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
@ -42,7 +42,7 @@ hex = "0.4"
ntest = "0.7.2"
reqwest = {version = "0.10.8", features = ["blocking"]}
sha-1 = "0.9"
swc_ecma_transforms = {version = "0.32.0", path = "../ecmascript/transforms", features = ["react"]}
swc_ecma_transforms = {version = "0.32.0", path = "../ecmascript/transforms", features = ["react", "typescript"]}
tempfile = "3.1.0"
testing = {version = "0.10.0", path = "../testing"}
url = "2.1.1"

View File

@ -1,4 +1,5 @@
use super::plan::{DepType, Plan};
use super::plan::DepType;
use super::plan::Plan;
use crate::bundler::chunk::export::inject_export;
use crate::bundler::keywords::KeywordRenamer;
use crate::{
@ -1061,7 +1062,7 @@ where
ModuleDecl::ExportDefaultExpr(mut export) => {
vars.push(
VarDeclarator {
span: DUMMY_SP.with_ctxt(injected_ctxt),
span: DUMMY_SP,
name: Pat::Ident(Ident::new(
js_word!("default"),
DUMMY_SP.with_ctxt(info.export_ctxt()),

View File

@ -76,4 +76,22 @@ impl VisitMut for KeywordRenamer {
n.orig = renamed;
}
}
fn visit_mut_class_prop(&mut self, n: &mut ClassProp) {
if n.computed {
n.key.visit_mut_with(self);
}
n.decorators.visit_mut_with(self);
n.value.visit_mut_with(self);
}
fn visit_mut_private_prop(&mut self, n: &mut PrivateProp) {
if n.computed {
n.key.visit_mut_with(self);
}
n.decorators.visit_mut_with(self);
n.value.visit_mut_with(self);
}
}

View File

@ -0,0 +1,6 @@
import * as React from "https://esm.sh/react@17.0.1"
console.log(React)
if (!React) {
throw new Error()
}

View File

@ -0,0 +1,6 @@
import React from "https://esm.sh/react@17.0.1"
console.log(React)
if (!React) {
throw new Error()
}

View File

@ -0,0 +1 @@
export * as flags from 'https://deno.land/std@0.83.0/flags/mod.ts';

View File

@ -0,0 +1,3 @@
import { flags } from './deps.ts';
console.log(flags.parse(Deno.args));

View File

@ -0,0 +1 @@
export * from 'https://deno.land/std@0.82.0/flags/mod.ts';

View File

@ -0,0 +1 @@
export * from 'https://deno.land/std@0.83.0/log/mod.ts';

View File

@ -0,0 +1,8 @@
class T {
private throws: number[] = [];
m(): void {
console.log(this.throws.length);
}
}
new T().m();

View File

@ -1,19 +1,15 @@
const m = "test";
if (!m) {
throw new Error('b');
}
export class Comparator {
constructor(comp1, optionsOrLoose = {
}){
}
parse(comp) {
const m1 = "another";
if (!m1) {
const m = "another";
if (!m) {
throw new TypeError("Invalid comparator: " + comp);
}
const m11 = m1[1];
console.log(m11);
if (!m1[2]) {
const m1 = m[1];
console.log(m1);
if (!m[2]) {
console.log('other');
}
}

View File

@ -4,7 +4,6 @@ const [a, b, c] = [
3
];
const b1 = b;
const b2 = b1;
const mod = function() {
return {
b: b1

View File

@ -498,11 +498,8 @@ fn dneo_8541_1() {
}
#[test]
fn denoo_8541_2() {
test_from_to(
"React.createElement('span', null, '\\u00b7');",
"React.createElement('span', null, '\\u00b7');",
);
fn deno_8925() {
assert_pretty("const 𝒫 = 2;", "const 𝒫 = 2;");
}
#[test]
@ -533,6 +530,14 @@ fn test_escape_without_source() {
es2020("\u{10ffff}", "\\u{10ffff}");
}
#[test]
fn deno_8541_2() {
test_from_to(
"React.createElement('span', null, '\\u00b7');",
"React.createElement('span', null, '\\u00b7');",
);
}
#[derive(Debug, Clone)]
struct Buf(Arc<RwLock<Vec<u8>>>);
impl Write for Buf {

View File

@ -0,0 +1,2 @@
const 𝒫 = 2;
console.log(𝒫)

View File

@ -0,0 +1,116 @@
{
"type": "Script",
"span": {
"start": 0,
"end": 33,
"ctxt": 0
},
"body": [
{
"type": "VariableDeclaration",
"span": {
"start": 0,
"end": 15,
"ctxt": 0
},
"kind": "const",
"declare": false,
"declarations": [
{
"type": "VariableDeclarator",
"span": {
"start": 6,
"end": 14,
"ctxt": 0
},
"id": {
"type": "Identifier",
"span": {
"start": 6,
"end": 10,
"ctxt": 0
},
"value": "𝒫",
"typeAnnotation": null,
"optional": false
},
"init": {
"type": "NumericLiteral",
"span": {
"start": 13,
"end": 14,
"ctxt": 0
},
"value": 2.0
},
"definite": false
}
]
},
{
"type": "ExpressionStatement",
"span": {
"start": 16,
"end": 33,
"ctxt": 0
},
"expression": {
"type": "CallExpression",
"span": {
"start": 16,
"end": 33,
"ctxt": 0
},
"callee": {
"type": "MemberExpression",
"span": {
"start": 16,
"end": 27,
"ctxt": 0
},
"object": {
"type": "Identifier",
"span": {
"start": 16,
"end": 23,
"ctxt": 0
},
"value": "console",
"typeAnnotation": null,
"optional": false
},
"property": {
"type": "Identifier",
"span": {
"start": 24,
"end": 27,
"ctxt": 0
},
"value": "log",
"typeAnnotation": null,
"optional": false
},
"computed": false
},
"arguments": [
{
"spread": null,
"expression": {
"type": "Identifier",
"span": {
"start": 28,
"end": 32,
"ctxt": 0
},
"value": "𝒫",
"typeAnnotation": null,
"optional": false
}
}
],
"typeArguments": null
}
}
],
"interpreter": null
}

View File

@ -0,0 +1,2 @@
export const 𝒫 = 2;
console.log(𝒫)

View File

@ -0,0 +1,124 @@
{
"type": "Module",
"span": {
"start": 0,
"end": 40,
"ctxt": 0
},
"body": [
{
"type": "ExportDeclaration",
"span": {
"start": 0,
"end": 22,
"ctxt": 0
},
"declaration": {
"type": "VariableDeclaration",
"span": {
"start": 7,
"end": 22,
"ctxt": 0
},
"kind": "const",
"declare": false,
"declarations": [
{
"type": "VariableDeclarator",
"span": {
"start": 13,
"end": 21,
"ctxt": 0
},
"id": {
"type": "Identifier",
"span": {
"start": 13,
"end": 17,
"ctxt": 0
},
"value": "𝒫",
"typeAnnotation": null,
"optional": false
},
"init": {
"type": "NumericLiteral",
"span": {
"start": 20,
"end": 21,
"ctxt": 0
},
"value": 2.0
},
"definite": false
}
]
}
},
{
"type": "ExpressionStatement",
"span": {
"start": 23,
"end": 40,
"ctxt": 0
},
"expression": {
"type": "CallExpression",
"span": {
"start": 23,
"end": 40,
"ctxt": 0
},
"callee": {
"type": "MemberExpression",
"span": {
"start": 23,
"end": 34,
"ctxt": 0
},
"object": {
"type": "Identifier",
"span": {
"start": 23,
"end": 30,
"ctxt": 0
},
"value": "console",
"typeAnnotation": null,
"optional": false
},
"property": {
"type": "Identifier",
"span": {
"start": 31,
"end": 34,
"ctxt": 0
},
"value": "log",
"typeAnnotation": null,
"optional": false
},
"computed": false
},
"arguments": [
{
"spread": null,
"expression": {
"type": "Identifier",
"span": {
"start": 35,
"end": 39,
"ctxt": 0
},
"value": "𝒫",
"typeAnnotation": null,
"optional": false
}
}
],
"typeArguments": null
}
}
],
"interpreter": null
}

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

View File

@ -139,6 +139,57 @@ impl Repeated for Dce<'_> {
}
}
/// This is currently overly conservative.
///
/// TODO: Skip visiting key if the key of class member or property is not
/// computed.
macro_rules! normal {
(
$name:ident,
$T:ty,
$($singluar_props:ident),*
) => {
normal!($name, $T, [$($singluar_props),*], []);
};
(
$name:ident,
$T:ty,
[$($singluar_props:ident),*],
[$($array_like_props:ident),*]
) => {
fn $name(&mut self, node: &mut $T) {
log::trace!("Visit<{}>: marking = {}; {:?}", stringify!($T), self.marking_phase, node);
if self.is_marked(node.span()) {
log::trace!("Visit<{}>: Already marked", stringify!($T));
return;
}
node.visit_mut_children_with(self);
if self.marking_phase
$(
|| self.is_marked(node.$singluar_props.span())
)*
$(
|| self.has_marked_elem(&node.$array_like_props)
)*
{
log::trace!("Visit<{}>: Marking", stringify!($T));
node.span = node.span.apply_mark(self.config.used_mark);
$(
self.mark(&mut node.$singluar_props);
)*
$(
self.mark(&mut node.$array_like_props);
)*
}
}
};
}
impl VisitMut for Dce<'_> {
noop_visit_mut_type!();
@ -159,19 +210,6 @@ impl VisitMut for Dce<'_> {
}
}
fn visit_mut_class_decl(&mut self, node: &mut ClassDecl) {
if self.is_marked(node.span()) {
return;
}
if self.marking_phase || self.included.contains(&node.ident.to_id()) {
node.class.span = node.class.span.apply_mark(self.config.used_mark);
self.mark(&mut node.class);
}
node.visit_mut_children_with(self)
}
fn visit_mut_do_while_stmt(&mut self, node: &mut DoWhileStmt) {
if self.is_marked(node.span) {
return;
@ -521,33 +559,8 @@ impl VisitMut for Dce<'_> {
}
}
fn visit_mut_throw_stmt(&mut self, node: &mut ThrowStmt) {
if self.is_marked(node.span) {
return;
}
node.span = node.span.apply_mark(self.config.used_mark);
self.mark(&mut node.arg)
}
fn visit_mut_try_stmt(&mut self, node: &mut TryStmt) {
if self.is_marked(node.span) {
return;
}
node.visit_mut_children_with(self);
if self.is_marked(node.block.span())
|| self.is_marked(node.handler.span())
|| self.is_marked(node.finalizer.span())
{
node.span = node.span.apply_mark(self.config.used_mark);
self.mark(&mut node.block);
self.mark(&mut node.handler);
self.mark(&mut node.finalizer);
}
}
normal!(visit_mut_throw_stmt, ThrowStmt, arg);
normal!(visit_mut_try_stmt, TryStmt, block, handler, finalizer);
fn visit_mut_update_expr(&mut self, node: &mut UpdateExpr) {
if self.is_marked(node.span) {
@ -627,6 +640,24 @@ impl VisitMut for Dce<'_> {
}
}
fn visit_mut_bin_expr(&mut self, node: &mut BinExpr) {
if self.is_marked(node.span) {
return;
}
node.visit_mut_children_with(self);
if self.marking_phase
|| self.is_marked(node.left.span())
|| self.is_marked(node.right.span())
{
node.span = node.span.apply_mark(self.config.used_mark);
self.mark(&mut node.left);
self.mark(&mut node.right);
}
}
fn visit_mut_new_expr(&mut self, node: &mut NewExpr) {
if self.is_marked(node.span) {
return;
@ -678,6 +709,112 @@ impl VisitMut for Dce<'_> {
}
self.visit_mut_stmt_like(n)
}
fn visit_mut_private_name(&mut self, _: &mut PrivateName) {}
fn visit_mut_prop_name(&mut self, n: &mut PropName) {
match n {
PropName::Computed(_) => n.visit_mut_children_with(self),
_ => {}
}
}
fn visit_mut_class_decl(&mut self, node: &mut ClassDecl) {
if self.is_marked(node.span()) {
return;
}
node.visit_mut_children_with(self);
if self.marking_phase
|| self.included.contains(&node.ident.to_id())
|| self.is_marked(node.ident.span())
|| self.is_marked(node.class.span())
{
self.mark(&mut node.ident);
self.mark(&mut node.class);
}
}
// ---- ---- ----- ---- -----
// If Spanned::span is synthesized, we should some work
// normal!(visit_mut_assign_prop, AssignProp, key, value);
// normal!(visit_mut_key_value_pat_prop, KeyValuePatProp, key, value);
// normal!(visit_mut_key_value_prop, KeyValueProp, key, value);
// fn visit_mut_spread_element(&mut self, n: &mut SpreadElement) {}
// ---- ---- ----- ---- -----
// If an ast node delagates Spanned::span to a field, we don't need to handle it
// because of the way Spanned::span works.
// normal!(visit_mut_class_expr, ClassExpr, ident, class);
// normal!(visit_mut_method_prop, MethodProp, key, value);
// normal!(
// visit_mut_export_default_specifier,
// ExportDefaultSpecifier,
// exported
// );
// normal!(visit_mut_fn_expr, FnExpr, ident, function);
// ---- ---- ----- ---- -----
// If an ast node has a span and not a declarations, use general logic.
normal!(visit_mut_array_lit, ArrayLit, [], [elems]);
normal!(visit_mut_array_pat, ArrayPat, [], [elems]);
normal!(visit_mut_arrow_expr, ArrowExpr, [body], [params]);
normal!(visit_mut_assign_expr, AssignExpr, left, right);
normal!(visit_mut_assign_pat, AssignPat, left, right);
normal!(visit_mut_assign_pat_prop, AssignPatProp, key, value);
normal!(visit_mut_await_expr, AwaitExpr, arg);
normal!(visit_mut_catch_clause, CatchClause, param, body);
normal!(visit_mut_class, Class, [super_class], [decorators, body]);
normal!(visit_mut_class_method, ClassMethod, key, function);
normal!(visit_mut_class_prop, ClassProp, [key, value], [decorators]);
normal!(visit_mut_computed_prop_name, ComputedPropName, expr);
normal!(visit_mut_cond_expr, CondExpr, test, cons, alt);
normal!(visit_mut_constructor, Constructor, [key, body], [params]);
normal!(visit_mut_decorator, Decorator, expr);
normal!(visit_mut_export_named_specifier, ExportNamedSpecifier, orig);
normal!(
visit_mut_export_namespace_specifier,
ExportNamespaceSpecifier,
name
);
// normal!(visit_mut_function,Function, [body], [params, decorators]);
normal!(visit_mut_getter_prop, GetterProp, key, body);
normal!(
visit_mut_import_default_specifier,
ImportDefaultSpecifier,
local
);
normal!(
visit_mut_import_named_specifier,
ImportNamedSpecifier,
local
);
normal!(
visit_mut_import_star_as_specifier,
ImportStarAsSpecifier,
local
);
normal!(visit_mut_object_lit, ObjectLit, [], [props]);
normal!(visit_mut_object_pat, ObjectPat, [], [props]);
normal!(visit_mut_opt_chain_expr, OptChainExpr, expr);
normal!(visit_mut_param, Param, [pat], [decorators]);
normal!(visit_mut_paren_expr, ParenExpr, expr);
normal!(visit_mut_private_method, PrivateMethod, key, function);
normal!(
visit_mut_private_prop,
PrivateProp,
[key, value],
[decorators]
);
normal!(visit_mut_rest_pat, RestPat, arg);
normal!(visit_mut_seq_expr, SeqExpr, [], [exprs]);
normal!(visit_mut_setter_prop, SetterProp, key, param, body);
normal!(visit_mut_tagged_tpl, TaggedTpl, [tag], [exprs]);
normal!(visit_mut_tpl, Tpl, [], [exprs]);
normal!(visit_mut_yield_expr, YieldExpr, arg);
}
impl Dce<'_> {
@ -779,6 +916,13 @@ impl Dce<'_> {
}
}
pub fn has_marked_elem<T>(&self, n: &[T]) -> bool
where
T: Spanned,
{
n.iter().any(|n| self.is_marked(n.span()))
}
pub fn should_preserve_export(&self, i: &JsWord) -> bool {
self.config.used.is_none()
|| self
@ -790,9 +934,9 @@ impl Dce<'_> {
.any(|exported| exported.0 == *i)
}
// pub fn with_child<T, F>(&mut self, op: F) -> T
// pub fn with_child<T, F>(&mut self, op: F) -> T
// where
// F: for<'any> FnOnce(&mut Dce<'any>) -> T,
// F: for<'any> FnOnce(&mut Dce<'any>) -> T,
// {
// let mut child = Dce {
// changed: false,