diff --git a/ecmascript/transforms/compat/src/es2015/regenerator/hoist.rs b/ecmascript/transforms/compat/src/es2015/regenerator/hoist.rs index 4bceb4e90a5..e576a62335e 100644 --- a/ecmascript/transforms/compat/src/es2015/regenerator/hoist.rs +++ b/ecmascript/transforms/compat/src/es2015/regenerator/hoist.rs @@ -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(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) -> Option { + let v = v.fold_children_with(self); + + match &v { + Some(VarDeclOrExpr::Expr(e)) => { + if e.is_invalid() { + return None; + } + } + + _ => {} + } + + v + } } diff --git a/ecmascript/transforms/compat/src/es2015/regenerator/mod.rs b/ecmascript/transforms/compat/src/es2015/regenerator/mod.rs index c016cd3dc51..c6be29a0485 100644 --- a/ecmascript/transforms/compat/src/es2015/regenerator/mod.rs +++ b/ecmascript/transforms/compat/src/es2015/regenerator/mod.rs @@ -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, - /// Variables declared in outer function. - outer_fn_vars: Vec, /// mark top_level_vars: Vec, } @@ -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, }))); } diff --git a/ecmascript/transforms/compat/tests/es2015_classes.rs b/ecmascript/transforms/compat/tests/es2015_classes.rs index 5a1818bfa1a..4913b9d3946 100644 --- a/ecmascript/transforms/compat/tests/es2015_classes.rs +++ b/ecmascript/transforms/compat/tests/es2015_classes.rs @@ -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, + ); +} diff --git a/ecmascript/transforms/compat/tests/fixture/classes/case1/exec.js b/ecmascript/transforms/compat/tests/fixture/classes/case1/exec.js new file mode 100644 index 00000000000..6938d62e8b1 --- /dev/null +++ b/ecmascript/transforms/compat/tests/fixture/classes/case1/exec.js @@ -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) +}) \ No newline at end of file diff --git a/ecmascript/transforms/compat/tests/fixture/classes/case2/exec.js b/ecmascript/transforms/compat/tests/fixture/classes/case2/exec.js new file mode 100644 index 00000000000..992398ce4bd --- /dev/null +++ b/ecmascript/transforms/compat/tests/fixture/classes/case2/exec.js @@ -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) +}) \ No newline at end of file diff --git a/tests/fixture/issue-2164/es2015/input/.swcrc b/tests/fixture/issue-2164/es2015/input/.swcrc new file mode 100644 index 00000000000..99a58761f54 --- /dev/null +++ b/tests/fixture/issue-2164/es2015/input/.swcrc @@ -0,0 +1,5 @@ +{ + "jsc": { + "target": "es2015" + } +} \ No newline at end of file diff --git a/tests/fixture/issue-2164/es2015/input/index.js b/tests/fixture/issue-2164/es2015/input/index.js new file mode 100644 index 00000000000..3cbe24c56fa --- /dev/null +++ b/tests/fixture/issue-2164/es2015/input/index.js @@ -0,0 +1,3 @@ +async function fn() { + for (const key in {}); +} \ No newline at end of file diff --git a/tests/fixture/issue-2164/es2015/output/index.js b/tests/fixture/issue-2164/es2015/output/index.js new file mode 100644 index 00000000000..d979091920a --- /dev/null +++ b/tests/fixture/issue-2164/es2015/output/index.js @@ -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); +} diff --git a/tests/fixture/issue-2164/es2016/input/.swcrc b/tests/fixture/issue-2164/es2016/input/.swcrc new file mode 100644 index 00000000000..1d664422ac4 --- /dev/null +++ b/tests/fixture/issue-2164/es2016/input/.swcrc @@ -0,0 +1,5 @@ +{ + "jsc": { + "target": "es2016" + } +} \ No newline at end of file diff --git a/tests/fixture/issue-2164/es2016/input/index.js b/tests/fixture/issue-2164/es2016/input/index.js new file mode 100644 index 00000000000..3cbe24c56fa --- /dev/null +++ b/tests/fixture/issue-2164/es2016/input/index.js @@ -0,0 +1,3 @@ +async function fn() { + for (const key in {}); +} \ No newline at end of file diff --git a/tests/fixture/issue-2164/es2016/output/index.js b/tests/fixture/issue-2164/es2016/output/index.js new file mode 100644 index 00000000000..f621aac5ead --- /dev/null +++ b/tests/fixture/issue-2164/es2016/output/index.js @@ -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); +} diff --git a/tests/fixture/issue-2164/es2018/input/.swcrc b/tests/fixture/issue-2164/es2018/input/.swcrc new file mode 100644 index 00000000000..f53b49e5134 --- /dev/null +++ b/tests/fixture/issue-2164/es2018/input/.swcrc @@ -0,0 +1,5 @@ +{ + "jsc": { + "target": "es2018" + } +} \ No newline at end of file diff --git a/tests/fixture/issue-2164/es2018/input/index.js b/tests/fixture/issue-2164/es2018/input/index.js new file mode 100644 index 00000000000..3cbe24c56fa --- /dev/null +++ b/tests/fixture/issue-2164/es2018/input/index.js @@ -0,0 +1,3 @@ +async function fn() { + for (const key in {}); +} \ No newline at end of file diff --git a/tests/fixture/issue-2164/es2018/output/index.js b/tests/fixture/issue-2164/es2018/output/index.js new file mode 100644 index 00000000000..293d0f68ed4 --- /dev/null +++ b/tests/fixture/issue-2164/es2018/output/index.js @@ -0,0 +1,4 @@ +async function fn() { + for(const key in { + }); +} diff --git a/tests/fixture/issue-2164/es5/input/.swcrc b/tests/fixture/issue-2164/es5/input/.swcrc new file mode 100644 index 00000000000..088bc6e5217 --- /dev/null +++ b/tests/fixture/issue-2164/es5/input/.swcrc @@ -0,0 +1,5 @@ +{ + "jsc": { + "target": "es5" + } +} \ No newline at end of file diff --git a/tests/fixture/issue-2164/es5/input/index.js b/tests/fixture/issue-2164/es5/input/index.js new file mode 100644 index 00000000000..3cbe24c56fa --- /dev/null +++ b/tests/fixture/issue-2164/es5/input/index.js @@ -0,0 +1,3 @@ +async function fn() { + for (const key in {}); +} \ No newline at end of file diff --git a/tests/fixture/issue-2164/es5/output/index.js b/tests/fixture/issue-2164/es5/output/index.js new file mode 100644 index 00000000000..d979091920a --- /dev/null +++ b/tests/fixture/issue-2164/es5/output/index.js @@ -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); +}