Skip to content

Commit

Permalink
fix(es/compat): Init this in sub class constructor for async
Browse files Browse the repository at this point in the history
  • Loading branch information
Austaras committed Aug 18, 2024
1 parent 99738ef commit 9b387f9
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions crates/swc/tests/fixture/issues-4xxx/4224/1/output/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
];
Expand Down
3 changes: 1 addition & 2 deletions crates/swc/tests/fixture/issues-4xxx/4224/2/output/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/swc_ecma_compat_es2015/src/arrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
117 changes: 106 additions & 11 deletions crates/swc_ecma_compat_es2017/src/async_to_generator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::iter;
use std::{iter, mem};

use serde::Deserialize;
use swc_common::{
Expand All @@ -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,
Expand Down Expand Up @@ -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),
})
}
Expand All @@ -62,23 +63,34 @@ pub struct Config {
struct AsyncToGenerator<C: Comments + Clone> {
c: Config,
comments: Option<C>,
in_subclass: bool,
unresolved_ctxt: SyntaxContext,
}

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]
#[fast_path(ShouldWork)]
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);
}
Expand All @@ -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));
}
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
};
}());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class Foo extends Bar {
constructor(options){
var _this;
super({
callA: _async_to_generator(function*() {
_this.callA();
})
}), _this = this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 9b387f9

Please sign in to comment.