Improve transforms (#1035)

swc_ecma_transforms:
 - Fix `this` in optional chaining expression. (https://github.com/Brooooooklyn/swc-node/issues/62)
 - Optimize typescript stripper
 - Optimize fixer
This commit is contained in:
강동윤 2020-09-06 19:47:15 +09:00 committed by GitHub
parent eb2162cbd2
commit 57112fc42b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 305 additions and 240 deletions

View File

@ -76,12 +76,15 @@ jobs:
run: |
git clone --depth 1 https://github.com/swc-project/ts-parser-test-ref.git ecmascript/parser/tests/typescript/tsc
- name: Run cargo test
- name: Run fast cargo test
run: |
export PATH="$PATH:$HOME/npm/bin"
EXEC=0 cargo test --color always --all --exclude node --exclude wasm
cargo test --color always --all --exclude node --exclude wasm
- name: Run slow cargo test
run: |
export PATH="$PATH:$HOME/npm/bin"
cargo test --color always --all --exclude node --exclude wasm
#
deploy-docs:
runs-on: ubuntu-latest

View File

@ -107,39 +107,6 @@ fn config_for_file(b: &mut Bencher) {
}
/// This benchmark exists to know exact execution time of each pass.
#[bench]
fn config_transforms(b: &mut Bencher) {
let c = mk();
let module = parse(&c);
b.iter(|| {
let program = module.clone();
let mut config = c
.config_for_file(
&Options {
config: Some(Config {
jsc: JscConfig {
target: JscTarget::Es2016,
syntax: Some(Syntax::Typescript(TsConfig {
..Default::default()
})),
..Default::default()
},
module: None,
..Default::default()
}),
swcrc: false,
is_module: true,
..Default::default()
},
&FileName::Real("rxjs/src/internal/observable/dom/AjaxObservable.ts".into()),
)
.unwrap();
let program = c.run_transform(true, || program.fold_with(&mut config.pass));
black_box(program)
});
}
fn bench_codegen(b: &mut Bencher, _target: JscTarget) {
let c = mk();
@ -220,3 +187,63 @@ compat!(full_es2017, JscTarget::Es2017);
compat!(full_es2018, JscTarget::Es2018);
compat!(full_es2019, JscTarget::Es2019);
compat!(full_es2020, JscTarget::Es2020);
macro_rules! tr_only {
($name:ident, $target:expr) => {
#[bench]
fn $name(b: &mut Bencher) {
let c = mk();
let module = parse(&c);
b.iter(|| {
let program = module.clone();
let mut config = c
.config_for_file(
&Options {
config: Some(Config {
jsc: JscConfig {
target: $target,
syntax: Some(Syntax::Typescript(TsConfig {
..Default::default()
})),
..Default::default()
},
module: None,
..Default::default()
}),
swcrc: false,
is_module: true,
..Default::default()
},
&FileName::Real(
"rxjs/src/internal/observable/dom/AjaxObservable.ts".into(),
),
)
.unwrap();
let program = c.run_transform(true, || program.fold_with(&mut config.pass));
black_box(program)
});
}
};
}
tr_only!(transforms_es3, JscTarget::Es3);
tr_only!(transforms_es5, JscTarget::Es5);
tr_only!(transforms_es2015, JscTarget::Es2015);
tr_only!(transforms_es2016, JscTarget::Es2016);
tr_only!(transforms_es2017, JscTarget::Es2017);
tr_only!(transforms_es2018, JscTarget::Es2018);
tr_only!(transforms_es2019, JscTarget::Es2019);
tr_only!(transforms_es2020, JscTarget::Es2020);
#[bench]
fn parser(b: &mut Bencher) {
let c = mk();
//TODO: Use target
b.iter(|| {
black_box(parse(&c));
})
}

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms"
repository = "https://github.com/swc-project/swc.git"
version = "0.23.2"
version = "0.23.3"
[features]
const-modules = ["dashmap"]

View File

@ -6,6 +6,7 @@ use std::{fmt::Debug, iter::once, mem};
use swc_common::{Spanned, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_macros::fast_path;
use swc_ecma_utils::alias_if_required;
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith, Node, Visit};
pub fn optional_chaining() -> impl Fold {
@ -330,9 +331,8 @@ impl OptChaining {
type_args,
..
}) => {
let obj = *obj;
let obj_span = obj.span();
let is_super_access = match obj {
let is_super_access = match *obj {
Expr::Member(MemberExpr {
obj: ExprOrSuper::Super(..),
..
@ -340,9 +340,35 @@ impl OptChaining {
_ => false,
};
let (left, right, alt) = match obj {
Expr::Ident(..) => (Box::new(obj.clone()), Box::new(obj), e.expr),
let (left, right, alt) = match *obj {
Expr::Ident(..) => (obj.clone(), obj, e.expr),
_ => {
let this_as_super;
let (this_obj, aliased) = alias_if_required(
match &*obj {
Expr::Member(m) => match &m.obj {
ExprOrSuper::Super(s) => {
this_as_super = Expr::This(ThisExpr { span: s.span });
&this_as_super
}
ExprOrSuper::Expr(obj) => &**obj,
},
_ => &*obj,
},
"_obj",
);
let obj = if !is_super_access && aliased {
self.vars.push(VarDeclarator {
span: obj_span,
definite: false,
name: Pat::Ident(this_obj.clone()),
init: Some(obj),
});
Box::new(Expr::Ident(this_obj.clone()))
} else {
obj
};
let i = private_ident!(obj_span, "ref");
self.vars.push(VarDeclarator {
span: obj_span,
@ -356,7 +382,7 @@ impl OptChaining {
span: DUMMY_SP,
left: PatOrExpr::Pat(Box::new(Pat::Ident(i.clone()))),
op: op!("="),
right: Box::new(obj),
right: obj,
})),
Box::new(Expr::Ident(i.clone())),
Box::new(Expr::Call(CallExpr {
@ -367,11 +393,10 @@ impl OptChaining {
prop: Box::new(Expr::Ident(Ident::new("call".into(), span))),
computed: false,
}))),
// TODO;
args: once(if is_super_access {
ThisExpr { span }.as_arg()
} else {
i.as_arg()
this_obj.as_arg()
})
.chain(args)
.collect(),

View File

@ -92,6 +92,18 @@ impl VisitMut for Fixer<'_> {
self.ctx = old;
}
fn visit_mut_assign_expr(&mut self, expr: &mut AssignExpr) {
expr.visit_mut_children_with(self);
match &mut *expr.right {
// `foo = (bar = baz)` => foo = bar = baz
Expr::Assign(AssignExpr { ref left, .. }) if left.as_ident().is_some() => {}
Expr::Seq(..) => self.wrap(&mut expr.right),
_ => {}
}
}
fn visit_mut_arrow_expr(&mut self, node: &mut ArrowExpr) {
let old = self.ctx;
self.ctx = Context::Default;
@ -105,6 +117,116 @@ impl VisitMut for Fixer<'_> {
self.ctx = old;
}
fn visit_mut_bin_expr(&mut self, expr: &mut BinExpr) {
expr.visit_mut_children_with(self);
match &mut *expr.right {
Expr::Assign(..)
| Expr::Seq(..)
| Expr::Yield(..)
| Expr::Cond(..)
| Expr::Arrow(..) => {
self.wrap(&mut expr.right);
}
Expr::Bin(BinExpr { op: op_of_rhs, .. }) => {
if op_of_rhs.precedence() <= expr.op.precedence() {
self.wrap(&mut expr.right);
}
}
_ => {}
};
match &mut *expr.left {
// While simplifying, (1 + x) * Nan becomes `1 + x * Nan`.
// But it should be `(1 + x) * Nan`
Expr::Bin(BinExpr { op: op_of_lhs, .. }) => {
if op_of_lhs.precedence() < expr.op.precedence() {
self.wrap(&mut expr.left);
}
}
Expr::Seq(..)
| Expr::Update(..)
| Expr::Unary(UnaryExpr {
op: op!("delete"), ..
})
| Expr::Unary(UnaryExpr {
op: op!("void"), ..
})
| Expr::Yield(..)
| Expr::Cond(..)
| Expr::Assign(..)
| Expr::Arrow(..) => {
self.wrap(&mut expr.left);
}
Expr::Object(..)
if expr.op == op!("instanceof")
|| expr.op == op!("==")
|| expr.op == op!("===")
|| expr.op == op!("!=")
|| expr.op == op!("!==") =>
{
self.wrap(&mut expr.left)
}
_ => {}
}
}
fn visit_mut_member_expr(&mut self, n: &mut MemberExpr) {
n.obj.visit_mut_with(self);
n.prop.visit_mut_with(self);
match n {
MemberExpr { obj, .. }
if obj.as_expr().map(|e| e.is_object()).unwrap_or(false)
&& match self.ctx {
Context::ForcedExpr { is_var_decl: true } => true,
_ => false,
} => {}
MemberExpr {
obj: ExprOrSuper::Expr(ref mut obj),
..
} if obj.is_fn_expr()
|| obj.is_cond()
|| obj.is_unary()
|| obj.is_seq()
|| obj.is_update()
|| obj.is_bin()
|| obj.is_object()
|| obj.is_assign()
|| obj.is_arrow()
|| obj.is_class()
|| obj.is_yield_expr()
|| obj.is_await_expr()
|| match **obj {
Expr::New(NewExpr { args: None, .. }) => true,
_ => false,
} =>
{
self.wrap(&mut **obj);
return;
}
_ => {}
}
}
fn visit_mut_unary_expr(&mut self, n: &mut UnaryExpr) {
n.visit_mut_children_with(self);
match *n.arg {
Expr::Assign(..)
| Expr::Bin(..)
| Expr::Seq(..)
| Expr::Cond(..)
| Expr::Arrow(..)
| Expr::Yield(..) => self.wrap(&mut n.arg),
_ => {}
}
}
fn visit_mut_assign_pat_prop(&mut self, node: &mut AssignPatProp) {
node.key.visit_mut_children_with(self);
@ -154,12 +276,11 @@ impl VisitMut for Fixer<'_> {
}
fn visit_mut_expr(&mut self, e: &mut Expr) {
e.visit_mut_children_with(self);
self.unwrap_expr(e);
e.visit_mut_children_with(self);
self.wrap_with_paren_if_required(e)
}
fn visit_mut_expr_or_spread(&mut self, e: &mut ExprOrSpread) {
e.visit_mut_children_with(self);
@ -217,24 +338,13 @@ impl VisitMut for Fixer<'_> {
}
}
fn visit_mut_stmt(&mut self, stmt: &mut Stmt) {
match stmt {
Stmt::Expr(expr) => {
let old = self.ctx;
self.ctx = Context::Default;
expr.visit_mut_with(self);
self.ctx = old;
}
_ => stmt.visit_mut_children_with(self),
};
fn visit_mut_expr_stmt(&mut self, s: &mut ExprStmt) {
let old = self.ctx;
self.ctx = Context::Default;
s.expr.visit_mut_with(self);
self.ctx = old;
match stmt {
Stmt::Expr(ExprStmt { ref mut expr, .. }) => {
self.handle_expr_stmt(&mut **expr);
}
_ => {}
}
self.handle_expr_stmt(&mut *s.expr);
}
fn visit_mut_var_declarator(&mut self, node: &mut VarDeclarator) {
@ -280,39 +390,6 @@ impl VisitMut for Fixer<'_> {
impl Fixer<'_> {
fn wrap_with_paren_if_required(&mut self, e: &mut Expr) {
match e {
Expr::Member(MemberExpr { obj, .. })
if obj.as_expr().map(|e| e.is_object()).unwrap_or(false)
&& match self.ctx {
Context::ForcedExpr { is_var_decl: true } => true,
_ => false,
} =>
{
return
}
Expr::Member(MemberExpr {
obj: ExprOrSuper::Expr(ref mut obj),
..
}) if obj.is_fn_expr()
|| obj.is_cond()
|| obj.is_unary()
|| obj.is_seq()
|| obj.is_update()
|| obj.is_bin()
|| obj.is_object()
|| obj.is_assign()
|| obj.is_arrow()
|| obj.is_class()
|| obj.is_yield_expr()
|| obj.is_await_expr()
|| match **obj {
Expr::New(NewExpr { args: None, .. }) => true,
_ => false,
} =>
{
self.wrap(&mut **obj);
}
// Flatten seq expr
Expr::Seq(SeqExpr { span, exprs }) => {
let len = exprs
@ -364,7 +441,13 @@ impl Fixer<'_> {
}
}
}
_ => buf.push(expr.take()),
_ => {
if is_last {
buf.push(expr.take());
} else {
buf.extend(ignore_return_value(expr.take()));
}
}
}
}
@ -391,59 +474,6 @@ impl Fixer<'_> {
};
}
Expr::Bin(ref mut expr) => {
match &mut *expr.right {
Expr::Assign(..)
| Expr::Seq(..)
| Expr::Yield(..)
| Expr::Cond(..)
| Expr::Arrow(..) => {
self.wrap(&mut expr.right);
}
Expr::Bin(BinExpr { op: op_of_rhs, .. }) => {
if op_of_rhs.precedence() <= expr.op.precedence() {
self.wrap(&mut expr.right);
}
}
_ => {}
};
match &mut *expr.left {
// While simplifying, (1 + x) * Nan becomes `1 + x * Nan`.
// But it should be `(1 + x) * Nan`
Expr::Bin(BinExpr { op: op_of_lhs, .. }) => {
if op_of_lhs.precedence() < expr.op.precedence() {
self.wrap(&mut expr.left);
}
}
Expr::Seq(..)
| Expr::Update(..)
| Expr::Unary(UnaryExpr {
op: op!("delete"), ..
})
| Expr::Unary(UnaryExpr {
op: op!("void"), ..
})
| Expr::Yield(..)
| Expr::Cond(..)
| Expr::Assign(..)
| Expr::Arrow(..) => {
self.wrap(&mut expr.left);
}
Expr::Object(..)
if expr.op == op!("instanceof")
|| expr.op == op!("==")
|| expr.op == op!("===")
|| expr.op == op!("!=")
|| expr.op == op!("!==") =>
{
self.wrap(&mut expr.left)
}
_ => {}
}
}
Expr::Cond(expr) => {
match &mut *expr.test {
Expr::Seq(..) | Expr::Assign(..) | Expr::Cond(..) | Expr::Arrow(..) => {
@ -474,27 +504,6 @@ impl Fixer<'_> {
}
}
Expr::Unary(expr) => match *expr.arg {
Expr::Assign(..)
| Expr::Bin(..)
| Expr::Seq(..)
| Expr::Cond(..)
| Expr::Arrow(..)
| Expr::Yield(..) => self.wrap(&mut expr.arg),
_ => {}
},
Expr::Assign(expr) => {
match &mut *expr.right {
// `foo = (bar = baz)` => foo = bar = baz
Expr::Assign(AssignExpr { ref left, .. }) if left.as_ident().is_some() => {}
// Handle `foo = bar = init()
Expr::Seq(..) => self.wrap(&mut expr.right),
_ => {}
}
}
Expr::Call(CallExpr {
callee: ExprOrSuper::Expr(ref mut callee),
..
@ -593,6 +602,25 @@ impl Fixer<'_> {
fn ignore_return_value(expr: Box<Expr>) -> Option<Box<Expr>> {
match *expr {
Expr::Ident(..) | Expr::Fn(..) | Expr::Lit(..) => None,
Expr::Seq(SeqExpr { span, exprs }) => {
let len = exprs.len();
let mut exprs: Vec<_> = exprs
.into_iter()
.enumerate()
.filter_map(|(i, expr)| {
if i + 1 == len {
Some(expr)
} else {
ignore_return_value(expr)
}
})
.collect();
match exprs.len() {
0 | 1 => exprs.pop(),
_ => Some(Box::new(Expr::Seq(SeqExpr { span, exprs }))),
}
}
Expr::Unary(UnaryExpr {
op: op!("void"),
arg,

View File

@ -671,14 +671,6 @@ impl SimplifyExpr {
op!("~") => {
if let Known(value) = arg.as_number() {
println!(
"Value: {} -> {} -> {} -> {}",
value,
value as u32,
!(value as u32),
!(value as u32) as i32
);
println!("{}", value as i32 as u32);
if value.fract() == 0.0 {
return Expr::Lit(Lit::Num(Number {
span,

View File

@ -105,62 +105,43 @@ impl Strip {
fn handle_expr<'a>(&mut self, n: &'a mut Expr) -> Vec<&'a mut Expr> {
match n {
Expr::Bin(BinExpr { left, right, .. }) => return vec![&mut **left, &mut **right],
_ => {}
}
n.map_with_mut(|expr| {
let mut expr = match expr {
Expr::TsAs(TsAsExpr { expr, .. })
| Expr::TsNonNull(TsNonNullExpr { expr, .. })
| Expr::TsTypeAssertion(TsTypeAssertion { expr, .. })
| Expr::TsConstAssertion(TsConstAssertion { expr, .. })
| Expr::TsTypeCast(TsTypeCastExpr { expr, .. }) => {
let mut expr = *expr;
expr.visit_mut_with(self);
expr
}
_ => expr,
};
// Remove types
Expr::TsAs(TsAsExpr { expr, .. })
| Expr::TsNonNull(TsNonNullExpr { expr, .. })
| Expr::TsTypeAssertion(TsTypeAssertion { expr, .. })
| Expr::TsConstAssertion(TsConstAssertion { expr, .. })
| Expr::TsTypeCast(TsTypeCastExpr { expr, .. }) => {
expr.visit_mut_with(self);
let expr = *expr.take();
*n = expr;
}
let expr = match expr {
Expr::Bin(..) => expr,
Expr::Member(MemberExpr {
span,
mut obj,
mut prop,
computed,
}) => {
obj.visit_mut_with(self);
Expr::Member(MemberExpr {
obj,
prop,
computed,
..
}) => {
obj.visit_mut_with(self);
let prop = if computed {
prop.visit_mut_with(self);
prop
} else {
match *prop {
Expr::Ident(i) => Box::new(Expr::Ident(Ident {
optional: false,
type_ann: None,
..i
})),
_ => prop,
if *computed {
prop.visit_mut_with(self);
} else {
match &mut **prop {
Expr::Ident(i) => {
i.type_ann = None;
i.optional = false;
}
};
Expr::Member(MemberExpr {
span,
obj,
prop,
computed,
})
_ => {}
}
}
_ => {
expr.visit_mut_children_with(self);
expr
}
};
}
expr
});
_ => {
n.visit_mut_children_with(self);
}
}
vec![]
}

View File

@ -115,12 +115,12 @@ function test(foo) {
foo === null || foo === void 0 ? void 0 : (ref = foo.bar) === null || ref === void 0 ? void 0 : ref.baz;
foo === null || foo === void 0 ? void 0 : foo(foo);
foo === null || foo === void 0 ? void 0 : foo.bar();
(ref1 = foo.bar) === null || ref1 === void 0 ? void 0 : ref1.call(ref1, foo.bar, false);
foo === null || foo === void 0 ? void 0 : (ref2 = foo.bar) === null || ref2 === void 0 ? void 0 : ref2.call(ref2, foo.bar, true);
(ref1 = foo.bar) === null || ref1 === void 0 ? void 0 : ref1.call(foo, foo.bar, false);
foo === null || foo === void 0 ? void 0 : (ref2 = foo.bar) === null || ref2 === void 0 ? void 0 : ref2.call(foo, foo.bar, true);
(ref3 = foo.bar) === null || ref3 === void 0 ? void 0 : ref3.baz(foo.bar, false);
foo === null || foo === void 0 ? void 0 : (ref4 = foo.bar) === null || ref4 === void 0 ? void 0 : ref4.baz(foo.bar, true);
(ref5 = foo.bar) === null || ref5 === void 0 ? void 0 : (ref6 = ref5.baz) === null || ref6 === void 0 ? void 0 : ref6.call(ref6, foo.bar, false);
foo === null || foo === void 0 ? void 0 : (ref7 = foo.bar) === null || ref7 === void 0 ? void 0 : (ref8 = ref7.baz) === null || ref8 === void 0 ? void 0 : ref8.call(ref8, foo.bar, true);
(ref5 = foo.bar) === null || ref5 === void 0 ? void 0 : (ref6 = ref5.baz) === null || ref6 === void 0 ? void 0 : ref6.call(ref5, foo.bar, false);
foo === null || foo === void 0 ? void 0 : (ref7 = foo.bar) === null || ref7 === void 0 ? void 0 : (ref8 = ref7.baz) === null || ref8 === void 0 ? void 0 : ref8.call(ref7, foo.bar, true);
}
"#
);
@ -302,15 +302,14 @@ foo?.bar?.()?.baz
var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8;
foo === null || foo === void 0 ? void 0 : foo(foo);
foo === null || foo === void 0 ? void 0 : foo.bar();
(ref = foo.bar) === null || ref === void 0 ? void 0 : ref.call(ref, foo.bar, false);
foo === null || foo === void 0 ? void 0 : (ref1 = foo.bar) === null || ref1 === void 0 ? void 0 : ref1.call(ref1, foo.bar, true);
(ref = foo.bar) === null || ref === void 0 ? void 0 : ref.call(foo, foo.bar, false);
foo === null || foo === void 0 ? void 0 : (ref1 = foo.bar) === null || ref1 === void 0 ? void 0 : ref1.call(foo, foo.bar, true);
foo === null || foo === void 0 ? void 0 : foo().bar;
foo === null || foo === void 0 ? void 0 : (ref2 = foo()) === null || ref2 === void 0 ? void 0 : ref2.bar;
(ref3 = foo.bar) === null || ref3 === void 0 ? void 0 : ref3.call(ref3).baz;
(ref4 = foo.bar) === null || ref4 === void 0 ? void 0 : (ref5 = ref4.call(ref4)) === null || ref5 === void 0 ? void 0 : ref5.baz;
foo === null || foo === void 0 ? void 0 : (ref6 = foo.bar) === null || ref6 === void 0 ? void 0 : ref6.call(ref6).baz;
foo === null || foo === void 0 ? void 0 : (ref7 = foo.bar) === null || ref7 === void 0 ? void 0 : (ref8 = ref7.call(ref7)) === null || ref8 === void 0 ? void 0 : ref8.baz;
"#
(ref3 = foo.bar) === null || ref3 === void 0 ? void 0 : ref3.call(foo).baz;
(ref4 = foo.bar) === null || ref4 === void 0 ? void 0 : (ref5 = ref4.call(foo)) === null || ref5 === void 0 ? void 0 : ref5.baz;
foo === null || foo === void 0 ? void 0 : (ref6 = foo.bar) === null || ref6 === void 0 ? void 0 : ref6.call(foo).baz;
foo === null || foo === void 0 ? void 0 : (ref7 = foo.bar) === null || ref7 === void 0 ? void 0 : (ref8 = ref7.call(foo)) === null || ref8 === void 0 ? void 0 : ref8.baz;"#
);
// general_unary_exec
@ -539,7 +538,7 @@ a === null || a === void 0
? void 0
: (ref1 = ref.c) === null || ref1 === void 0
? void 0
: ref1.call(ref1);"
: ref1.call(ref);"
);
test!(
@ -568,3 +567,13 @@ test!(
"var ref;
(ref = test.a) === null || ref === void 0 ? void 0 : ref.b.c.d.e.f.g.h.i"
);
// https://github.com/Brooooooklyn/swc-node/issues/62
test!(
syntax(),
|_| tr(()),
swc_node_issue_62,
"a.focus?.()",
"var ref;
(ref = a.focus) === null || ref === void 0 ? void 0 : ref.call(a);"
);