fix(es/compat): Init this in sub class constructor for async (#9446)

**Related issue:**

 - Closes https://github.com/swc-project/swc/issues/8452
 - Closes https://github.com/swc-project/swc/issues/9432
This commit is contained in:
Austaras 2024-08-19 08:22:36 +08:00 committed by GitHub
parent 08dd948289
commit bfaf31bc4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 182 additions and 17 deletions

View File

@ -0,0 +1,7 @@
---
swc_core: patch
swc_ecma_compat_es2017: patch
swc_ecma_compat_es2015: patch
---
fix(es/compat): Init this in sub class constructor for async

View File

@ -5,8 +5,8 @@ import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator";
export var CompanyBgStore = function CompanyBgStore() {
"use strict";
_class_call_check(this, CompanyBgStore);
_define_property(this, "corpName", 123);
var _this = this;
_define_property(this, "corpName", 123);
_define_property(this, "getBusinessInfo", _async_to_generator(function() {
var corpName;
var _arguments = arguments;

View File

@ -13,10 +13,9 @@ var A = function A() {
];
});
});
var _this1 = this;
this.bar = /*#__PURE__*/ _async_to_generator._(function() {
return _ts_generator._(this, function(_state) {
_this1.x();
_this.x();
return [
2
];

View File

@ -5,9 +5,8 @@ class A {
this.foo = /*#__PURE__*/ _async_to_generator._(function*() {
_this.x();
});
var _this1 = this;
this.bar = /*#__PURE__*/ _async_to_generator._(function*() {
_this1.x();
_this.x();
});
}
}

View File

@ -75,11 +75,13 @@ impl VisitMut for Arrow {
noop_visit_mut_type!(fail);
fn visit_mut_class(&mut self, c: &mut Class) {
let old = self.in_subclass;
if c.super_class.is_some() {
self.in_subclass = true;
}
c.visit_mut_children_with(self);
self.in_subclass = false;
self.in_subclass = old;
}
fn visit_mut_constructor(&mut self, c: &mut Constructor) {

View File

@ -1,4 +1,4 @@
use std::iter;
use std::{iter, mem};
use serde::Deserialize;
use swc_common::{
@ -9,8 +9,8 @@ use swc_ecma_transforms_base::{helper, helper_expr, perf::Check};
use swc_ecma_transforms_macros::fast_path;
use swc_ecma_utils::{
contains_this_expr, find_pat_ids,
function::{FnEnvHoister, FnWrapperResult, FunctionWrapper},
private_ident, quote_ident, ExprFactory, Remapper, StmtLike,
function::{init_this, FnEnvHoister, FnWrapperResult, FunctionWrapper},
prepend_stmt, private_ident, quote_ident, ExprFactory, Remapper, StmtLike,
};
use swc_ecma_visit::{
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>(
as_folder(AsyncToGenerator {
c,
comments,
in_subclass: false,
unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
})
}
@ -62,6 +63,7 @@ pub struct Config {
struct AsyncToGenerator<C: Comments + Clone> {
c: Config,
comments: Option<C>,
in_subclass: bool,
unresolved_ctxt: SyntaxContext,
}
@ -69,9 +71,11 @@ struct Actual<C: Comments> {
c: Config,
comments: Option<C>,
in_subclass: bool,
hoister: FnEnvHoister,
unresolved_ctxt: SyntaxContext,
extra_stmts: Vec<Stmt>,
hoist_stmts: Vec<Stmt>,
}
#[swc_trace]
@ -79,6 +83,14 @@ struct Actual<C: Comments> {
impl<C: Comments + Clone> VisitMut for AsyncToGenerator<C> {
noop_visit_mut_type!(fail);
fn visit_mut_class(&mut self, c: &mut Class) {
if c.super_class.is_some() {
self.in_subclass = true;
}
c.visit_mut_children_with(self);
self.in_subclass = false;
}
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
self.visit_mut_stmt_like(n);
}
@ -98,16 +110,19 @@ impl<C: Comments + Clone> AsyncToGenerator<C> {
let mut stmts_updated = Vec::with_capacity(stmts.len());
for mut stmt in stmts.drain(..) {
let hoister = FnEnvHoister::new(self.unresolved_ctxt);
let mut actual = Actual {
c: self.c,
comments: self.comments.clone(),
in_subclass: self.in_subclass,
hoister,
unresolved_ctxt: self.unresolved_ctxt,
extra_stmts: Vec::new(),
hoist_stmts: Vec::new(),
};
stmt.visit_mut_with(&mut actual);
stmts_updated.extend(actual.hoist_stmts.into_iter().map(T::from));
stmts_updated.extend(actual.hoister.to_stmt().into_iter().map(T::from));
stmts_updated.push(stmt);
stmts_updated.extend(actual.extra_stmts.into_iter().map(T::from));
}
@ -122,6 +137,45 @@ impl<C: Comments + Clone> AsyncToGenerator<C> {
impl<C: Comments> VisitMut for Actual<C> {
noop_visit_mut_type!(fail);
fn visit_mut_class(&mut self, c: &mut Class) {
let old = self.in_subclass;
if c.super_class.is_some() {
self.in_subclass = true;
}
c.visit_mut_children_with(self);
self.in_subclass = old;
}
fn visit_mut_constructor(&mut self, c: &mut Constructor) {
c.params.visit_mut_children_with(self);
if let Some(BlockStmt { span: _, stmts, .. }) = &mut c.body {
let old_rep = self.hoister.take();
stmts.visit_mut_children_with(self);
if self.in_subclass {
let (decl, this_id) =
mem::replace(&mut self.hoister, old_rep).to_stmt_in_subclass();
if let Some(this_id) = this_id {
init_this(stmts, &this_id)
}
if let Some(decl) = decl {
prepend_stmt(stmts, decl)
}
} else {
let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
if let Some(decl) = decl {
prepend_stmt(stmts, decl)
}
}
}
}
fn visit_mut_class_method(&mut self, m: &mut ClassMethod) {
if m.function.body.is_none() {
return;
@ -192,6 +246,51 @@ impl<C: Comments> VisitMut for Actual<C> {
self.visit_mut_expr_with_binding(expr, None, false);
}
fn visit_mut_function(&mut self, f: &mut Function) {
let old_rep = self.hoister.take();
f.visit_mut_children_with(self);
let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
if let (Some(body), Some(decl)) = (&mut f.body, decl) {
prepend_stmt(&mut body.stmts, decl);
}
}
fn visit_mut_getter_prop(&mut self, f: &mut GetterProp) {
f.key.visit_mut_with(self);
if let Some(body) = &mut f.body {
let old_rep = self.hoister.take();
body.visit_mut_with(self);
let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
if let Some(stmt) = decl {
prepend_stmt(&mut body.stmts, stmt);
}
}
}
fn visit_mut_setter_prop(&mut self, f: &mut SetterProp) {
f.key.visit_mut_with(self);
f.param.visit_mut_with(self);
if let Some(body) = &mut f.body {
let old_rep = self.hoister.take();
body.visit_mut_with(self);
let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
if let Some(stmt) = decl {
prepend_stmt(&mut body.stmts, stmt);
}
}
}
fn visit_mut_fn_decl(&mut self, f: &mut FnDecl) {
f.visit_mut_children_with(self);
if !f.function.is_async {
@ -342,11 +441,7 @@ impl<C: Comments> Actual<C> {
match expr {
Expr::Arrow(arrow_expr @ ArrowExpr { is_async: true, .. }) => {
let mut state = FnEnvHoister::new(self.unresolved_ctxt);
arrow_expr.visit_mut_with(&mut state);
self.hoist_stmts.extend(state.to_stmt());
arrow_expr.visit_mut_with(&mut self.hoister);
let mut wrapper = FunctionWrapper::from(arrow_expr.take());
wrapper.ignore_function_name = self.c.ignore_function_name;

View File

@ -0,0 +1,15 @@
class Test0 {
}
class Test extends Test0 {
constructor(){
var _this;
super(), _this = this, console.log(function() {
var _ref = _async_to_generator(function*(e) {
yield _this.test();
});
return function(e) {
return _ref.apply(this, arguments);
};
}());
}
}

View File

@ -0,0 +1,10 @@
class Foo extends Bar {
constructor(options){
var _this;
super({
callA: _async_to_generator(function*() {
_this.callA();
})
}), _this = this;
}
}

View File

@ -2077,6 +2077,44 @@ test!(
"
);
test!(
Syntax::default(),
|_| async_to_generator::<SingleThreadedComments>(Default::default(), None, Mark::new()),
issue_8452,
r#"
class Test0 {}
class Test extends Test0 {
constructor() {
super(),
console.log(async (e) => {
await this.test();
});
}
}
"#
);
test!(
Syntax::default(),
|_| with_resolver(),
issue_9432,
r#"
class Foo extends Bar {
constructor(options) {
super(
{
callA: async () => {
this.callA();
},
}
);
}
}
"#
);
#[testing::fixture("tests/async-to-generator/**/exec.js")]
fn exec(input: PathBuf) {
let input = read_to_string(input).unwrap();