swc_ecma_codegen:
 - Print multiline string correctly. (#1233)

swc_ecma_transforms:
 - `optional_chaining`: Ensure that #1149 is fixed. (#1149)
 - `async_to_generator`: Remove method parameters. (#1215, #1235)
 - `regenerator`: Don't emit useless expressions. (#1125)
 - `regenerator`: Track `finally` properly. (#1125)

spack:
 - LRU cache for resolver.
This commit is contained in:
강동윤 2020-11-30 19:20:21 +09:00 committed by GitHub
parent ea6beaa06d
commit faa1c5f4e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 395 additions and 53 deletions

View File

@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
license = "Apache-2.0/MIT" license = "Apache-2.0/MIT"
name = "swc_ecma_codegen" name = "swc_ecma_codegen"
repository = "https://github.com/swc-project/swc.git" repository = "https://github.com/swc-project/swc.git"
version = "0.41.2" version = "0.41.3"
[dependencies] [dependencies]
bitflags = "1" bitflags = "1"

View File

@ -2445,6 +2445,14 @@ fn escape<'s>(cm: &SourceMap, span: Span, s: &'s str, single_quote: Option<bool>
let mut s_iter = s.chars(); let mut s_iter = s.chars();
while let Some(orig_c) = orig_iter.next() { while let Some(orig_c) = orig_iter.next() {
// Javascript literal should not contain newlines
if orig_c == '\n' && single_quote.is_some() {
s_iter.next();
s_iter.next();
buf.push_str("\\n");
continue;
}
if orig_c == '\\' { if orig_c == '\\' {
buf.push('\\'); buf.push('\\');
match orig_iter.next() { match orig_iter.next() {

View File

@ -1,2 +1 @@
"Hello\ w "Hello\ w\nworld";
world";

View File

@ -1,2 +1 @@
('\ ('\ \n');
');

View File

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

View File

@ -1474,8 +1474,6 @@ expect(foo()).toBe(false);
}, _callee1); }, _callee1);
})))); }))));
case 2: case 2:
_ctx.sent;
case 3:
case "end": case "end":
return _ctx.stop(); return _ctx.stop();
} }

View File

@ -75,15 +75,13 @@ impl<'a> CaseHandler<'a> {
} }
impl CaseHandler<'_> { impl CaseHandler<'_> {
fn with_entry<F>(&mut self, entry: Entry, op: F) fn with_entry<F>(&mut self, entry: Entry, op: F) -> Entry
where where
F: FnOnce(&mut Self), F: FnOnce(&mut Self),
{ {
self.leaps.push(entry); self.leaps.push(entry);
let ret = op(self); op(self);
self.leaps.pop(); self.leaps.pop().unwrap()
ret
} }
pub fn get_try_locs_list(&mut self) -> Option<ArrayLit> { pub fn get_try_locs_list(&mut self) -> Option<ArrayLit> {
@ -984,17 +982,8 @@ impl CaseHandler<'_> {
Stmt::With(..) => panic!("WithStatement not supported in generator functions"), Stmt::With(..) => panic!("WithStatement not supported in generator functions"),
Stmt::Expr(ExprStmt { span, expr, .. }) => { Stmt::Expr(ExprStmt { expr, .. }) => {
let expr = expr.map(|expr| self.explode_expr(expr, true)); self.explode_expr(*expr, true);
match *expr {
Expr::Unary(UnaryExpr {
op: op!("void"),
ref arg,
..
}) if arg.is_lit() => {}
_ => self.emit(Stmt::Expr(ExprStmt { span, expr })),
}
} }
Stmt::Return(ret) => { Stmt::Return(ret) => {
@ -1205,10 +1194,13 @@ impl CaseHandler<'_> {
CatchClause { body, ..handler } CatchClause { body, ..handler }
}); });
folder.with_entry( try_entry.catch_entry = match folder.with_entry(
Entry::Catch(try_entry.catch_entry.clone().unwrap()), Entry::Catch(try_entry.catch_entry.clone().unwrap()),
|folder| folder.explode_stmts(handler.unwrap().body.stmts), |folder| folder.explode_stmts(handler.unwrap().body.stmts),
); ) {
Entry::Catch(e) => Some(e),
_ => unreachable!(),
};
} }
if let Some(finally_loc) = finally_loc { if let Some(finally_loc) = finally_loc {
@ -1216,10 +1208,13 @@ impl CaseHandler<'_> {
folder.update_ctx_prev_loc(Some(&mut loc)); folder.update_ctx_prev_loc(Some(&mut loc));
try_entry.finally_entry.as_mut().unwrap().first_loc = loc; try_entry.finally_entry.as_mut().unwrap().first_loc = loc;
folder.with_entry( try_entry.finally_entry = match folder.with_entry(
Entry::Finally(try_entry.finally_entry.clone().unwrap()), Entry::Finally(try_entry.finally_entry.clone().unwrap()),
|folder| folder.explode_stmts(finalizer.unwrap().stmts), |folder| folder.explode_stmts(finalizer.unwrap().stmts),
); ) {
Entry::Finally(e) => Some(e),
_ => unreachable!(),
};
let callee = folder let callee = folder
.ctx .ctx
@ -1247,9 +1242,16 @@ impl CaseHandler<'_> {
} }
}); });
self.try_entries.push(try_entry); let after = self.mark(after);
self.mark(after); match &mut try_entry.finally_entry {
Some(fe) => {
fe.after_loc = after;
}
None => {}
}
self.try_entries.push(try_entry);
} }
Stmt::While(s) => { Stmt::While(s) => {

View File

@ -81,7 +81,7 @@ impl AsyncToGenerator {
impl Fold for Actual { impl Fold for Actual {
noop_fold_type!(); noop_fold_type!();
fn fold_class_method(&mut self, m: ClassMethod) -> ClassMethod { fn fold_class_method(&mut self, mut m: ClassMethod) -> ClassMethod {
if m.function.body.is_none() { if m.function.body.is_none() {
return m; return m;
} }
@ -91,6 +91,7 @@ impl Fold for Actual {
let params = m.function.params.clone(); let params = m.function.params.clone();
let mut folder = MethodFolder { vars: vec![] }; let mut folder = MethodFolder { vars: vec![] };
m.function.params.clear();
let function = m.function.fold_children_with(&mut folder); let function = m.function.fold_children_with(&mut folder);
let expr = make_fn_ref(FnExpr { let expr = make_fn_ref(FnExpr {
ident: None, ident: None,

View File

@ -141,9 +141,8 @@ var _default = function _callee() {
return 5; return 5;
case 2: case 2:
x = _ctx.sent; x = _ctx.sent;
void 0;
return _ctx.abrupt('return', 5); return _ctx.abrupt('return', 5);
case 5: case 4:
case 'end': return _ctx.stop(); case 'end': return _ctx.stop();
} }
}, _callee); }, _callee);
@ -1070,8 +1069,6 @@ function myGenerator() {
3 3
], _ctx.t0, 1); ], _ctx.t0, 1);
case 1: case 1:
_ctx.t0;
case 2:
case 'end': case 'end':
return _ctx.stop(); return _ctx.stop();
} }
@ -1103,8 +1100,6 @@ export function myGenerator() {
3 3
], _ctx.t0, 1); ], _ctx.t0, 1);
case 1: case 1:
_ctx.t0;
case 2:
case 'end': case 'end':
return _ctx.stop(); return _ctx.stop();
} }
@ -1219,3 +1214,161 @@ test_exec!(
expect(v.next()).toEqual({ done: true}) expect(v.next()).toEqual({ done: true})
" "
); );
test_exec!(
Syntax::default(),
|_| chain!(async_to_generator(), tr(())),
issue_1125_1,
"
async function test() {
try {
await 1
} finally {
console.log(2)
}
}
test()
"
);
test!(
Syntax::default(),
|_| tr(()),
issue_1125_2,
"
function _test() {
_test = _asyncToGenerator(function* () {
try {
yield 1;
} finally {
console.log(2);
}
});
return _test.apply(this, arguments);
}
function test() {
return _test.apply(this, arguments);
}
test();
",
"
var regeneratorRuntime = require('regenerator-runtime');
var _marked = regeneratorRuntime.mark(_test);
function _test() {
_test = _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_ctx) {
while (1)
switch (_ctx.prev = _ctx.next) {
case 0:
_ctx.prev = 0;
_ctx.next = 3;
return 1;
case 3:
_ctx.prev = 3;
console.log(2);
return _ctx.finish(3);
case 6:
case 'end':
return _ctx.stop();
}
}, _callee, null, [[0,, 3, 6]]);
}));
return _test.apply(this, arguments);
}
function test() {
return _test.apply(this, arguments);
}
test();
"
);
test!(
Syntax::default(),
|_| tr(()),
issue_1125_3,
"
function* foo() {
try {
yield 1;
} finally {
console.log(2);
}
}
",
"
var regeneratorRuntime = require('regenerator-runtime');
var _marked = regeneratorRuntime.mark(foo);
function foo() {
return regeneratorRuntime.wrap(function foo$(_ctx) {
while (1)
switch (_ctx.prev = _ctx.next) {
case 0:
_ctx.prev = 0;
_ctx.next = 3;
return 1;
case 3:
_ctx.prev = 3;
console.log(2);
return _ctx.finish(3);
case 6:
case 'end':
return _ctx.stop();
}
}, _marked, null, [[0,, 3, 6]]);
}
"
);
test!(
Syntax::default(),
|_| tr(()),
issue_1125_4,
"
function* foo() {
try {
yield 1;
} catch(e) {
console.log(2);
}
}
",
"
var regeneratorRuntime = require('regenerator-runtime');
var _marked = regeneratorRuntime.mark(foo);
function foo() {
return regeneratorRuntime.wrap(function foo$(_ctx) {
while (1)
switch (_ctx.prev = _ctx.next) {
case 0:
_ctx.prev = 0;
_ctx.next = 3;
return 1;
case 3:
_ctx.next = 8;
break;
case 5:
_ctx.prev = 5;
_ctx.t0 = _ctx['catch'](0);
console.log(2);
case 8:
case 'end':
return _ctx.stop();
}
}, _marked, null, [[0, 5]]);
}
"
);

View File

@ -8,6 +8,7 @@ use swc_ecma_transforms::{
es2017::async_to_generator, es2017::async_to_generator,
}, },
fixer, resolver, fixer, resolver,
typescript::strip,
}; };
use swc_ecma_visit::{Fold, FoldWith}; use swc_ecma_visit::{Fold, FoldWith};
@ -37,6 +38,10 @@ fn syntax() -> Syntax {
Syntax::default() Syntax::default()
} }
fn ts_syntax() -> Syntax {
Syntax::Typescript(Default::default())
}
fn tr() -> impl Fold { fn tr() -> impl Fold {
chain!( chain!(
ParenRemover, ParenRemover,
@ -2230,3 +2235,66 @@ test!(
}); });
" "
); );
test!(
ts_syntax(),
|_| chain!(strip(), async_to_generator()),
issue_1235_1,
"
class Service {
async is(a: string): Promise<boolean> {
return a.toUpperCase() === a;
}
}
(async() => { await (new Service()).is('ABC'); })();
",
"
class Service {
is(a) {
return _asyncToGenerator(function* () {
return a.toUpperCase() === a;
})();
}
}
_asyncToGenerator(function* () {
yield new Service().is('ABC');
})();
"
);
test!(
Syntax::default(),
|_| async_to_generator(),
issue_1125_1,
"
async function test() {
try {
await 1
} finally {
console.log(2)
}
}
test()
",
"
function _test() {
_test = _asyncToGenerator(function* () {
try {
yield 1;
} finally {
console.log(2);
}
});
return _test.apply(this, arguments);
}
function test() {
return _test.apply(this, arguments);
}
test();
"
);

View File

@ -1,6 +1,7 @@
#![feature(test)] #![feature(test)]
use swc_common::chain;
use swc_ecma_parser::{Syntax, TsConfig}; use swc_ecma_parser::{Syntax, TsConfig};
use swc_ecma_transforms::compat::es2020::optional_chaining; use swc_ecma_transforms::{compat::es2020::optional_chaining, typescript::strip};
use swc_ecma_visit::Fold; use swc_ecma_visit::Fold;
#[macro_use] #[macro_use]
@ -702,3 +703,15 @@ test!(
const patch = (ref = _obj) === null || ref === void 0 ? void 0 : ref.call(_obj); const patch = (ref = _obj) === null || ref === void 0 ? void 0 : ref.call(_obj);
" "
); );
test!(
syntax(),
|_| chain!(strip(), tr(())),
issue_1149_1,
"
const tmp = tt?.map((t: any) => t).join((v: any) => v);
",
"
const tmp = tt === null || tt === void 0 ? void 0 : tt.map((t) => t).join((v) => v);
"
);

View File

@ -16,6 +16,7 @@ anyhow = "1"
dashmap = "3" dashmap = "3"
is-macro = "0.1.8" is-macro = "0.1.8"
log = "0.4.8" log = "0.4.8"
lru = "0.6.1"
once_cell = "1" once_cell = "1"
regex = "1" regex = "1"
serde = {version = "1", features = ["derive"]} serde = {version = "1", features = ["derive"]}

View File

@ -3,11 +3,13 @@
//! See: https://github.com/goto-bus-stop/node-resolve //! See: https://github.com/goto-bus-stop/node-resolve
use anyhow::{bail, Context, Error}; use anyhow::{bail, Context, Error};
use lru::LruCache;
use serde::Deserialize; use serde::Deserialize;
use std::{ use std::{
fs::File, fs::File,
io::BufReader, io::BufReader,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Mutex,
}; };
use swc_bundler::Resolve; use swc_bundler::Resolve;
use swc_common::FileName; use swc_common::FileName;
@ -22,19 +24,23 @@ struct PackageJson {
main: Option<String>, main: Option<String>,
} }
pub struct NodeResolver; pub struct NodeResolver {
cache: Mutex<LruCache<(PathBuf, String), PathBuf>>,
}
static EXTENSIONS: &[&str] = &["ts", "tsx", "js", "jsx", "json", "node"]; static EXTENSIONS: &[&str] = &["ts", "tsx", "js", "jsx", "json", "node"];
impl NodeResolver { impl NodeResolver {
pub fn new() -> Self { pub fn new() -> Self {
Self Self {
cache: Mutex::new(LruCache::new(40)),
}
} }
fn wrap(&self, path: PathBuf) -> Result<FileName, Error> { fn wrap(&self, base: &PathBuf, target: &str, path: PathBuf) -> Result<FileName, Error> {
Ok(FileName::Real( let path = path.canonicalize().context("failaed to canonicalize")?;
path.canonicalize().context("failaed to canonicalize")?, self.store(base, target, path.clone());
)) Ok(FileName::Real(path))
} }
/// Resolve a path as a file. If `path` refers to a file, it is returned; /// Resolve a path as a file. If `path` refers to a file, it is returned;
@ -126,6 +132,16 @@ impl NodeResolver {
None => bail!("not found"), None => bail!("not found"),
} }
} }
fn store(&self, base: &PathBuf, target: &str, result: PathBuf) {
let lock = self.cache.lock();
match lock {
Ok(mut lock) => {
lock.put((base.clone(), target.to_string()), result.to_path_buf());
}
Err(_) => {}
}
}
} }
impl Resolve for NodeResolver { impl Resolve for NodeResolver {
@ -135,6 +151,19 @@ impl Resolve for NodeResolver {
_ => bail!("node-resolver supports only files"), _ => bail!("node-resolver supports only files"),
}; };
{
let lock = self.cache.lock();
match lock {
Ok(mut lock) => {
//
if let Some(v) = lock.get(&(base.clone(), target.to_string())) {
return Ok(FileName::Real(v.clone()));
}
}
Err(_) => {}
}
}
// Absolute path // Absolute path
if target.starts_with("/") { if target.starts_with("/") {
let base_dir = &Path::new("/"); let base_dir = &Path::new("/");
@ -143,7 +172,7 @@ impl Resolve for NodeResolver {
return self return self
.resolve_as_file(&path) .resolve_as_file(&path)
.or_else(|_| self.resolve_as_directory(&path)) .or_else(|_| self.resolve_as_directory(&path))
.and_then(|p| self.wrap(p)); .and_then(|p| self.wrap(base, target, p));
} }
let cwd = &Path::new("."); let cwd = &Path::new(".");
@ -154,10 +183,10 @@ impl Resolve for NodeResolver {
return self return self
.resolve_as_file(&path) .resolve_as_file(&path)
.or_else(|_| self.resolve_as_directory(&path)) .or_else(|_| self.resolve_as_directory(&path))
.and_then(|p| self.wrap(p)); .and_then(|p| self.wrap(base, target, p));
} }
self.resolve_node_modules(base_dir, target) self.resolve_node_modules(base_dir, target)
.and_then(|p| self.wrap(p)) .and_then(|p| self.wrap(base, target, p))
} }
} }

View File

@ -69,11 +69,10 @@ _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
_ctx.t0 = _ctx.sent; _ctx.t0 = _ctx.sent;
case 7: case 7:
obj = _ctx.t0; obj = _ctx.t0;
void 0;
console.log({ console.log({
obj: obj obj: obj
}); });
case 10: case 9:
case "end": case "end":
return _ctx.stop(); return _ctx.stop();
} }

View File

@ -69,11 +69,10 @@ _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
_ctx.t0 = _ctx.sent; _ctx.t0 = _ctx.sent;
case 7: case 7:
obj = _ctx.t0; obj = _ctx.t0;
void 0;
console.log({ console.log({
obj: obj obj: obj
}); });
case 10: case 9:
case "end": case "end":
return _ctx.stop(); return _ctx.stop();
} }

View File

@ -19,11 +19,10 @@ function foo() {
_ctx.t0 = _ctx.sent; _ctx.t0 = _ctx.sent;
case 7: case 7:
val = _ctx.t0; val = _ctx.t0;
void 0;
console.log({ console.log({
val: val val: val
}); });
case 10: case 9:
case "end": case "end":
return _ctx.stop(); return _ctx.stop();
} }

View File

@ -0,0 +1,8 @@
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": true
}
}
}

View File

@ -0,0 +1,8 @@
function Component() {
return (
<div
name="A
B"
/>
);
}

View File

@ -0,0 +1,5 @@
function Component() {
return React.createElement("div", {
name: "A\n B"
});
}

View File

@ -0,0 +1,8 @@
{
"jsc": {
"target": "es2017",
"parser": {
"syntax": "typescript"
}
}
}

View File

@ -0,0 +1,6 @@
class Service {
async is(a: string): Promise<boolean> {
return a.toUpperCase() === a;
}
}
(async () => { await (new Service()).is('ABC'); })();

View File

@ -0,0 +1,39 @@
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function() {
var self = this, args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
class Service {
is(a) {
return _asyncToGenerator(function*() {
return a.toUpperCase() === a;
})();
}
}
_asyncToGenerator(function*() {
yield new Service().is('ABC');
})();