Skip to content

Commit bfaf31b

Browse files
authored
fix(es/compat): Init this in sub class constructor for async (#9446)
**Related issue:** - Closes #8452 - Closes #9432
1 parent 08dd948 commit bfaf31b

File tree

9 files changed

+182
-17
lines changed

9 files changed

+182
-17
lines changed

.changeset/grumpy-cars-move.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
swc_core: patch
3+
swc_ecma_compat_es2017: patch
4+
swc_ecma_compat_es2015: patch
5+
---
6+
7+
fix(es/compat): Init this in sub class constructor for async

crates/swc/tests/fixture/issues-4xxx/4208/1/output/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator";
55
export var CompanyBgStore = function CompanyBgStore() {
66
"use strict";
77
_class_call_check(this, CompanyBgStore);
8-
_define_property(this, "corpName", 123);
98
var _this = this;
9+
_define_property(this, "corpName", 123);
1010
_define_property(this, "getBusinessInfo", _async_to_generator(function() {
1111
var corpName;
1212
var _arguments = arguments;

crates/swc/tests/fixture/issues-4xxx/4224/1/output/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ var A = function A() {
1313
];
1414
});
1515
});
16-
var _this1 = this;
1716
this.bar = /*#__PURE__*/ _async_to_generator._(function() {
1817
return _ts_generator._(this, function(_state) {
19-
_this1.x();
18+
_this.x();
2019
return [
2120
2
2221
];

crates/swc/tests/fixture/issues-4xxx/4224/2/output/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ class A {
55
this.foo = /*#__PURE__*/ _async_to_generator._(function*() {
66
_this.x();
77
});
8-
var _this1 = this;
98
this.bar = /*#__PURE__*/ _async_to_generator._(function*() {
10-
_this1.x();
9+
_this.x();
1110
});
1211
}
1312
}

crates/swc_ecma_compat_es2015/src/arrow.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,13 @@ impl VisitMut for Arrow {
7575
noop_visit_mut_type!(fail);
7676

7777
fn visit_mut_class(&mut self, c: &mut Class) {
78+
let old = self.in_subclass;
79+
7880
if c.super_class.is_some() {
7981
self.in_subclass = true;
8082
}
8183
c.visit_mut_children_with(self);
82-
self.in_subclass = false;
84+
self.in_subclass = old;
8385
}
8486

8587
fn visit_mut_constructor(&mut self, c: &mut Constructor) {

crates/swc_ecma_compat_es2017/src/async_to_generator.rs

Lines changed: 106 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::iter;
1+
use std::{iter, mem};
22

33
use serde::Deserialize;
44
use swc_common::{
@@ -9,8 +9,8 @@ use swc_ecma_transforms_base::{helper, helper_expr, perf::Check};
99
use swc_ecma_transforms_macros::fast_path;
1010
use swc_ecma_utils::{
1111
contains_this_expr, find_pat_ids,
12-
function::{FnEnvHoister, FnWrapperResult, FunctionWrapper},
13-
private_ident, quote_ident, ExprFactory, Remapper, StmtLike,
12+
function::{init_this, FnEnvHoister, FnWrapperResult, FunctionWrapper},
13+
prepend_stmt, private_ident, quote_ident, ExprFactory, Remapper, StmtLike,
1414
};
1515
use swc_ecma_visit::{
1616
as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith,
@@ -45,6 +45,7 @@ pub fn async_to_generator<C: Comments + Clone>(
4545
as_folder(AsyncToGenerator {
4646
c,
4747
comments,
48+
in_subclass: false,
4849
unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
4950
})
5051
}
@@ -62,23 +63,34 @@ pub struct Config {
6263
struct AsyncToGenerator<C: Comments + Clone> {
6364
c: Config,
6465
comments: Option<C>,
66+
in_subclass: bool,
6567
unresolved_ctxt: SyntaxContext,
6668
}
6769

6870
struct Actual<C: Comments> {
6971
c: Config,
7072
comments: Option<C>,
7173

74+
in_subclass: bool,
75+
hoister: FnEnvHoister,
76+
7277
unresolved_ctxt: SyntaxContext,
7378
extra_stmts: Vec<Stmt>,
74-
hoist_stmts: Vec<Stmt>,
7579
}
7680

7781
#[swc_trace]
7882
#[fast_path(ShouldWork)]
7983
impl<C: Comments + Clone> VisitMut for AsyncToGenerator<C> {
8084
noop_visit_mut_type!(fail);
8185

86+
fn visit_mut_class(&mut self, c: &mut Class) {
87+
if c.super_class.is_some() {
88+
self.in_subclass = true;
89+
}
90+
c.visit_mut_children_with(self);
91+
self.in_subclass = false;
92+
}
93+
8294
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
8395
self.visit_mut_stmt_like(n);
8496
}
@@ -98,16 +110,19 @@ impl<C: Comments + Clone> AsyncToGenerator<C> {
98110
let mut stmts_updated = Vec::with_capacity(stmts.len());
99111

100112
for mut stmt in stmts.drain(..) {
113+
let hoister = FnEnvHoister::new(self.unresolved_ctxt);
114+
101115
let mut actual = Actual {
102116
c: self.c,
103117
comments: self.comments.clone(),
118+
in_subclass: self.in_subclass,
119+
hoister,
104120
unresolved_ctxt: self.unresolved_ctxt,
105121
extra_stmts: Vec::new(),
106-
hoist_stmts: Vec::new(),
107122
};
108123

109124
stmt.visit_mut_with(&mut actual);
110-
stmts_updated.extend(actual.hoist_stmts.into_iter().map(T::from));
125+
stmts_updated.extend(actual.hoister.to_stmt().into_iter().map(T::from));
111126
stmts_updated.push(stmt);
112127
stmts_updated.extend(actual.extra_stmts.into_iter().map(T::from));
113128
}
@@ -122,6 +137,45 @@ impl<C: Comments + Clone> AsyncToGenerator<C> {
122137
impl<C: Comments> VisitMut for Actual<C> {
123138
noop_visit_mut_type!(fail);
124139

140+
fn visit_mut_class(&mut self, c: &mut Class) {
141+
let old = self.in_subclass;
142+
143+
if c.super_class.is_some() {
144+
self.in_subclass = true;
145+
}
146+
c.visit_mut_children_with(self);
147+
self.in_subclass = old;
148+
}
149+
150+
fn visit_mut_constructor(&mut self, c: &mut Constructor) {
151+
c.params.visit_mut_children_with(self);
152+
153+
if let Some(BlockStmt { span: _, stmts, .. }) = &mut c.body {
154+
let old_rep = self.hoister.take();
155+
156+
stmts.visit_mut_children_with(self);
157+
158+
if self.in_subclass {
159+
let (decl, this_id) =
160+
mem::replace(&mut self.hoister, old_rep).to_stmt_in_subclass();
161+
162+
if let Some(this_id) = this_id {
163+
init_this(stmts, &this_id)
164+
}
165+
166+
if let Some(decl) = decl {
167+
prepend_stmt(stmts, decl)
168+
}
169+
} else {
170+
let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
171+
172+
if let Some(decl) = decl {
173+
prepend_stmt(stmts, decl)
174+
}
175+
}
176+
}
177+
}
178+
125179
fn visit_mut_class_method(&mut self, m: &mut ClassMethod) {
126180
if m.function.body.is_none() {
127181
return;
@@ -192,6 +246,51 @@ impl<C: Comments> VisitMut for Actual<C> {
192246
self.visit_mut_expr_with_binding(expr, None, false);
193247
}
194248

249+
fn visit_mut_function(&mut self, f: &mut Function) {
250+
let old_rep = self.hoister.take();
251+
252+
f.visit_mut_children_with(self);
253+
254+
let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
255+
256+
if let (Some(body), Some(decl)) = (&mut f.body, decl) {
257+
prepend_stmt(&mut body.stmts, decl);
258+
}
259+
}
260+
261+
fn visit_mut_getter_prop(&mut self, f: &mut GetterProp) {
262+
f.key.visit_mut_with(self);
263+
264+
if let Some(body) = &mut f.body {
265+
let old_rep = self.hoister.take();
266+
267+
body.visit_mut_with(self);
268+
269+
let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
270+
271+
if let Some(stmt) = decl {
272+
prepend_stmt(&mut body.stmts, stmt);
273+
}
274+
}
275+
}
276+
277+
fn visit_mut_setter_prop(&mut self, f: &mut SetterProp) {
278+
f.key.visit_mut_with(self);
279+
f.param.visit_mut_with(self);
280+
281+
if let Some(body) = &mut f.body {
282+
let old_rep = self.hoister.take();
283+
284+
body.visit_mut_with(self);
285+
286+
let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
287+
288+
if let Some(stmt) = decl {
289+
prepend_stmt(&mut body.stmts, stmt);
290+
}
291+
}
292+
}
293+
195294
fn visit_mut_fn_decl(&mut self, f: &mut FnDecl) {
196295
f.visit_mut_children_with(self);
197296
if !f.function.is_async {
@@ -342,11 +441,7 @@ impl<C: Comments> Actual<C> {
342441

343442
match expr {
344443
Expr::Arrow(arrow_expr @ ArrowExpr { is_async: true, .. }) => {
345-
let mut state = FnEnvHoister::new(self.unresolved_ctxt);
346-
347-
arrow_expr.visit_mut_with(&mut state);
348-
349-
self.hoist_stmts.extend(state.to_stmt());
444+
arrow_expr.visit_mut_with(&mut self.hoister);
350445

351446
let mut wrapper = FunctionWrapper::from(arrow_expr.take());
352447
wrapper.ignore_function_name = self.c.ignore_function_name;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class Test0 {
2+
}
3+
class Test extends Test0 {
4+
constructor(){
5+
var _this;
6+
super(), _this = this, console.log(function() {
7+
var _ref = _async_to_generator(function*(e) {
8+
yield _this.test();
9+
});
10+
return function(e) {
11+
return _ref.apply(this, arguments);
12+
};
13+
}());
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Foo extends Bar {
2+
constructor(options){
3+
var _this;
4+
super({
5+
callA: _async_to_generator(function*() {
6+
_this.callA();
7+
})
8+
}), _this = this;
9+
}
10+
}

crates/swc_ecma_transforms_compat/tests/es2017_async_to_generator.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2077,6 +2077,44 @@ test!(
20772077
"
20782078
);
20792079

2080+
test!(
2081+
Syntax::default(),
2082+
|_| async_to_generator::<SingleThreadedComments>(Default::default(), None, Mark::new()),
2083+
issue_8452,
2084+
r#"
2085+
class Test0 {}
2086+
2087+
class Test extends Test0 {
2088+
constructor() {
2089+
super(),
2090+
console.log(async (e) => {
2091+
await this.test();
2092+
});
2093+
}
2094+
}
2095+
2096+
"#
2097+
);
2098+
2099+
test!(
2100+
Syntax::default(),
2101+
|_| with_resolver(),
2102+
issue_9432,
2103+
r#"
2104+
class Foo extends Bar {
2105+
constructor(options) {
2106+
super(
2107+
{
2108+
callA: async () => {
2109+
this.callA();
2110+
},
2111+
}
2112+
);
2113+
}
2114+
}
2115+
"#
2116+
);
2117+
20802118
#[testing::fixture("tests/async-to-generator/**/exec.js")]
20812119
fn exec(input: PathBuf) {
20822120
let input = read_to_string(input).unwrap();

0 commit comments

Comments
 (0)