mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 17:54:15 +03:00
fix(es/transforms): Fix bugs (#2181)
swc_ecma_transforms_compat: - `regenerator`: Fix variable hoisting for for-in/of loops. (#2164) - Ensure #2071 is fixed. (#2071)
This commit is contained in:
parent
b2c99719fd
commit
ee16139a19
@ -5,7 +5,7 @@ use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::{find_ids, private_ident};
|
||||
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
|
||||
|
||||
pub(super) type Vars = SmallVec<[Ident; 32]>;
|
||||
pub(super) type Vars = SmallVec<[Ident; 12]>;
|
||||
|
||||
pub(super) fn hoist<T>(node: T) -> (T, Hoister)
|
||||
where
|
||||
@ -138,7 +138,10 @@ impl Fold for Hoister {
|
||||
|
||||
VarDeclOrPat::VarDecl(mut var) => {
|
||||
if var.decls.len() == 1 && var.decls[0].init.is_none() {
|
||||
return var.decls.remove(0).name.into();
|
||||
let pat = var.decls.remove(0).name;
|
||||
self.vars.extend(find_ids(&pat));
|
||||
|
||||
return pat.into();
|
||||
}
|
||||
|
||||
var.into()
|
||||
@ -154,4 +157,20 @@ impl Fold for Hoister {
|
||||
_ => var.fold_children_with(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_opt_var_decl_or_expr(&mut self, v: Option<VarDeclOrExpr>) -> Option<VarDeclOrExpr> {
|
||||
let v = v.fold_children_with(self);
|
||||
|
||||
match &v {
|
||||
Some(VarDeclOrExpr::Expr(e)) => {
|
||||
if e.is_invalid() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ pub fn regenerator(global_mark: Mark) -> impl Fold {
|
||||
Regenerator {
|
||||
global_mark,
|
||||
regenerator_runtime: Default::default(),
|
||||
outer_fn_vars: Default::default(),
|
||||
top_level_vars: Default::default(),
|
||||
}
|
||||
}
|
||||
@ -26,8 +25,6 @@ struct Regenerator {
|
||||
global_mark: Mark,
|
||||
/// [Some] if used.
|
||||
regenerator_runtime: Option<Ident>,
|
||||
/// Variables declared in outer function.
|
||||
outer_fn_vars: Vec<VarDeclarator>,
|
||||
/// mark
|
||||
top_level_vars: Vec<VarDeclarator>,
|
||||
}
|
||||
@ -322,28 +319,27 @@ impl Regenerator {
|
||||
f.body = f.body.fold_with(&mut FnSentVisitor { ctx: ctx.clone() });
|
||||
let uses_this = contains_this_expr(&f.body);
|
||||
let (body, hoister) = hoist(f.body.unwrap());
|
||||
self.outer_fn_vars
|
||||
.extend(hoister.vars.into_iter().map(|id| VarDeclarator {
|
||||
let mut outer_fn_vars = vec![];
|
||||
outer_fn_vars.extend(hoister.vars.into_iter().map(|id| VarDeclarator {
|
||||
span: DUMMY_SP,
|
||||
name: Pat::Ident(id.into()),
|
||||
init: None,
|
||||
definite: false,
|
||||
}));
|
||||
outer_fn_vars.extend(hoister.arguments.into_iter().map(|id| {
|
||||
VarDeclarator {
|
||||
span: DUMMY_SP,
|
||||
name: Pat::Ident(id.into()),
|
||||
init: None,
|
||||
name: Pat::Ident(id.clone().into()),
|
||||
init: Some(Box::new(
|
||||
Ident {
|
||||
sym: js_word!("arguments"),
|
||||
..id
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
definite: false,
|
||||
}));
|
||||
self.outer_fn_vars
|
||||
.extend(hoister.arguments.into_iter().map(|id| {
|
||||
VarDeclarator {
|
||||
span: DUMMY_SP,
|
||||
name: Pat::Ident(id.clone().into()),
|
||||
init: Some(Box::new(
|
||||
Ident {
|
||||
sym: js_word!("arguments"),
|
||||
..id
|
||||
}
|
||||
.into(),
|
||||
)),
|
||||
definite: false,
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
||||
handler.explode_stmts(body.stmts);
|
||||
|
||||
@ -419,11 +415,11 @@ impl Regenerator {
|
||||
span: body_span,
|
||||
stmts: {
|
||||
let mut buf = vec![];
|
||||
if !self.outer_fn_vars.is_empty() {
|
||||
if !outer_fn_vars.is_empty() {
|
||||
buf.push(Stmt::Decl(Decl::Var(VarDecl {
|
||||
span: DUMMY_SP,
|
||||
kind: VarDeclKind::Var,
|
||||
decls: take(&mut self.outer_fn_vars),
|
||||
decls: outer_fn_vars,
|
||||
declare: false,
|
||||
})));
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::{fs::read_to_string, path::PathBuf};
|
||||
|
||||
use swc_common::{chain, Mark};
|
||||
use swc_ecma_parser::Syntax;
|
||||
use swc_ecma_transforms_base::resolver::resolver;
|
||||
@ -7,7 +9,7 @@ use swc_ecma_transforms_compat::{
|
||||
es2016, es2017, es2018, es2020,
|
||||
es2020::class_properties,
|
||||
};
|
||||
use swc_ecma_transforms_testing::{test, test_exec, Tester};
|
||||
use swc_ecma_transforms_testing::{compare_stdout, test, test_exec, Tester};
|
||||
use swc_ecma_visit::Fold;
|
||||
|
||||
fn syntax() -> Syntax {
|
||||
@ -6531,3 +6533,13 @@ var Extended = function(Base) {
|
||||
}(Base);
|
||||
"
|
||||
);
|
||||
|
||||
#[testing::fixture("tests/fixture/classes/**/exec.js")]
|
||||
fn exec(input: PathBuf) {
|
||||
let src = read_to_string(&input).unwrap();
|
||||
compare_stdout(
|
||||
Default::default(),
|
||||
|t| classes(Some(t.comments.clone())),
|
||||
&src,
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
const sleep = (ms) => new Promise((resolve, reject) => { setTimeout(() => resolve(), ms) })
|
||||
|
||||
class Project {
|
||||
constructor(name) {
|
||||
this.name = name
|
||||
}
|
||||
};
|
||||
|
||||
Project.f = async function () {
|
||||
await sleep(100)
|
||||
console.log(this['a'])
|
||||
return new this('a')
|
||||
}
|
||||
|
||||
Project.f().then((project) => {
|
||||
console.log(project.name)
|
||||
})
|
@ -0,0 +1,16 @@
|
||||
const sleep = (ms) => new Promise((resolve, reject) => { setTimeout(() => resolve(), ms) })
|
||||
|
||||
class Project {
|
||||
constructor(name) {
|
||||
this.name = name
|
||||
}
|
||||
static async g() {
|
||||
await sleep(100)
|
||||
console.log(this['a'])
|
||||
return new this('a')
|
||||
}
|
||||
};
|
||||
|
||||
Project.g().then((project) => {
|
||||
console.log(project.name)
|
||||
})
|
5
tests/fixture/issue-2164/es2015/input/.swcrc
Normal file
5
tests/fixture/issue-2164/es2015/input/.swcrc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"jsc": {
|
||||
"target": "es2015"
|
||||
}
|
||||
}
|
3
tests/fixture/issue-2164/es2015/input/index.js
Normal file
3
tests/fixture/issue-2164/es2015/input/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
async function fn() {
|
||||
for (const key in {});
|
||||
}
|
49
tests/fixture/issue-2164/es2015/output/index.js
Normal file
49
tests/fixture/issue-2164/es2015/output/index.js
Normal file
@ -0,0 +1,49 @@
|
||||
import regeneratorRuntime from "regenerator-runtime";
|
||||
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);
|
||||
});
|
||||
};
|
||||
}
|
||||
function _fn() {
|
||||
_fn = _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
|
||||
var key;
|
||||
return regeneratorRuntime.wrap(function _callee$(_ctx) {
|
||||
while(1)switch(_ctx.prev = _ctx.next){
|
||||
case 0:
|
||||
for(key in {
|
||||
});
|
||||
case 1:
|
||||
case "end":
|
||||
return _ctx.stop();
|
||||
}
|
||||
}, _callee);
|
||||
}));
|
||||
return _fn.apply(this, arguments);
|
||||
}
|
||||
function fn() {
|
||||
return _fn.apply(this, arguments);
|
||||
}
|
5
tests/fixture/issue-2164/es2016/input/.swcrc
Normal file
5
tests/fixture/issue-2164/es2016/input/.swcrc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"jsc": {
|
||||
"target": "es2016"
|
||||
}
|
||||
}
|
3
tests/fixture/issue-2164/es2016/input/index.js
Normal file
3
tests/fixture/issue-2164/es2016/input/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
async function fn() {
|
||||
for (const key in {});
|
||||
}
|
39
tests/fixture/issue-2164/es2016/output/index.js
Normal file
39
tests/fixture/issue-2164/es2016/output/index.js
Normal 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);
|
||||
});
|
||||
};
|
||||
}
|
||||
function _fn() {
|
||||
_fn = _asyncToGenerator(function*() {
|
||||
for(const key in {
|
||||
});
|
||||
});
|
||||
return _fn.apply(this, arguments);
|
||||
}
|
||||
function fn() {
|
||||
return _fn.apply(this, arguments);
|
||||
}
|
5
tests/fixture/issue-2164/es2018/input/.swcrc
Normal file
5
tests/fixture/issue-2164/es2018/input/.swcrc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"jsc": {
|
||||
"target": "es2018"
|
||||
}
|
||||
}
|
3
tests/fixture/issue-2164/es2018/input/index.js
Normal file
3
tests/fixture/issue-2164/es2018/input/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
async function fn() {
|
||||
for (const key in {});
|
||||
}
|
4
tests/fixture/issue-2164/es2018/output/index.js
Normal file
4
tests/fixture/issue-2164/es2018/output/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
async function fn() {
|
||||
for(const key in {
|
||||
});
|
||||
}
|
5
tests/fixture/issue-2164/es5/input/.swcrc
Normal file
5
tests/fixture/issue-2164/es5/input/.swcrc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"jsc": {
|
||||
"target": "es5"
|
||||
}
|
||||
}
|
3
tests/fixture/issue-2164/es5/input/index.js
Normal file
3
tests/fixture/issue-2164/es5/input/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
async function fn() {
|
||||
for (const key in {});
|
||||
}
|
49
tests/fixture/issue-2164/es5/output/index.js
Normal file
49
tests/fixture/issue-2164/es5/output/index.js
Normal file
@ -0,0 +1,49 @@
|
||||
import regeneratorRuntime from "regenerator-runtime";
|
||||
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);
|
||||
});
|
||||
};
|
||||
}
|
||||
function _fn() {
|
||||
_fn = _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
|
||||
var key;
|
||||
return regeneratorRuntime.wrap(function _callee$(_ctx) {
|
||||
while(1)switch(_ctx.prev = _ctx.next){
|
||||
case 0:
|
||||
for(key in {
|
||||
});
|
||||
case 1:
|
||||
case "end":
|
||||
return _ctx.stop();
|
||||
}
|
||||
}, _callee);
|
||||
}));
|
||||
return _fn.apply(this, arguments);
|
||||
}
|
||||
function fn() {
|
||||
return _fn.apply(this, arguments);
|
||||
}
|
Loading…
Reference in New Issue
Block a user