fix(es/transforms/compat): Remove .bind(this) (#2776)

swc_ecma_transforms_compat:
 - `async_to_generator`: Create `_this` instead of using `.bind(this)`.
This commit is contained in:
OJ Kwon 2021-11-18 06:39:12 -08:00 committed by GitHub
parent 8615a64f2f
commit 5261df52d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 149 additions and 51 deletions

View File

@ -30,16 +30,17 @@ function _asyncToGenerator(fn) {
}
class A {
foo() {
return _asyncToGenerator((function*() {
var _this = this;
return _asyncToGenerator(function*() {
try {
return yield _asyncToGenerator((function*(x) {
return x + this.val;
}).bind(this)).bind(this)('a'); // this is undefined
return yield _asyncToGenerator(function*(x) {
return x + _this.val;
})('a'); // this is undefined
// return await Promise.all(['a', 'b'].map(async (x) => x + this.val)); // this is undefined
} catch (e) {
throw e;
}
}).bind(this))();
})();
}
constructor(){
this.val = '1';

View File

@ -63,8 +63,6 @@ impl AsyncToGenerator {
T: StmtLike + VisitMutWith<Actual>,
Vec<T>: VisitMutWith<Self>,
{
stmts.visit_mut_children_with(self);
let mut stmts_updated = Vec::with_capacity(stmts.len());
for mut stmt in stmts.drain(..) {
@ -80,6 +78,7 @@ impl AsyncToGenerator {
}
*stmts = stmts_updated;
stmts.visit_mut_children_with(self);
}
}
@ -121,17 +120,38 @@ impl VisitMut for Actual {
}
let params = m.function.params.clone();
let mut folder = MethodFolder { vars: vec![] };
let mut folder = MethodFolder {
vars: vec![],
scoped_this_ident: None,
};
m.function.params.clear();
m.function.visit_mut_children_with(&mut folder);
let this_decl = if let Some(this_ident) = folder.scoped_this_ident {
let decl = VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(this_ident.into()),
init: Some(Box::new(Expr::This(ThisExpr { span: DUMMY_SP }))),
definite: false,
}],
};
Some(Stmt::Decl(Decl::Var(decl)))
} else {
None
};
let expr = make_fn_ref(
FnExpr {
ident: None,
function: m.function.take(),
},
self.in_object_prop || self.in_prototype_assignment,
false,
);
let hoisted_super = if folder.vars.is_empty() {
@ -154,6 +174,7 @@ impl VisitMut for Actual {
span: DUMMY_SP,
stmts: hoisted_super
.into_iter()
.chain(this_decl.into_iter())
.chain(iter::once(Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(Box::new(Expr::Call(CallExpr {
@ -459,6 +480,49 @@ impl VisitMut for Actual {
fn visit_mut_stmts(&mut self, _n: &mut Vec<Stmt>) {}
}
struct ThisReplacer<'a> {
to: &'a Ident,
}
impl VisitMut for ThisReplacer<'_> {
noop_visit_mut_type!();
fn visit_mut_arrow_expr(&mut self, _: &mut ArrowExpr) {}
fn visit_mut_constructor(&mut self, _: &mut Constructor) {}
fn visit_mut_expr(&mut self, e: &mut Expr) {
e.visit_mut_children_with(self);
match e {
Expr::This(..) => {
*e = Expr::Ident(self.to.clone());
}
_ => {}
}
}
fn visit_mut_function(&mut self, _: &mut Function) {}
/// Don't recurse into fn
fn visit_mut_getter_prop(&mut self, n: &mut GetterProp) {
n.key.visit_mut_with(self);
}
/// Don't recurse into fn
fn visit_mut_method_prop(&mut self, n: &mut MethodProp) {
n.key.visit_mut_with(self);
n.function.visit_mut_with(self);
}
/// Don't recurse into fn
fn visit_mut_setter_prop(&mut self, n: &mut SetterProp) {
n.key.visit_mut_with(self);
n.param.visit_mut_with(self);
}
}
/// Hoists super access
///
/// ## In
@ -489,6 +553,7 @@ impl VisitMut for Actual {
/// ```
struct MethodFolder {
vars: Vec<VarDeclarator>,
scoped_this_ident: Option<Ident>,
}
impl MethodFolder {
@ -564,6 +629,35 @@ impl VisitMut for MethodFolder {
noop_visit_mut_type!();
fn visit_mut_expr(&mut self, expr: &mut Expr) {
match expr {
Expr::Arrow(ArrowExpr { body, .. }) if contains_this_expr(body) => {
let this_id = self
.scoped_this_ident
.get_or_insert(private_ident!("_this"))
.clone();
body.visit_mut_with(&mut ThisReplacer { to: &this_id });
}
Expr::Fn(FnExpr {
function: Function {
body: Some(body), ..
},
..
}) if contains_this_expr(body) => {
if self.scoped_this_ident.is_none() {
self.scoped_this_ident = Some(private_ident!("_this"));
}
body.visit_mut_with(&mut ThisReplacer {
to: &self
.scoped_this_ident
.clone()
.expect("this scope should exist"),
});
}
_ => {}
};
// TODO(kdy): Cache (Reuse declaration for same property)
match expr {

View File

@ -994,34 +994,35 @@ class Class {
r#"
class Class {
method() {
return _asyncToGenerator((function*() {
this;
()=>this
;
()=>{
this;
()=>this
;
function x() {
this;
()=>{
this;
};
_asyncToGenerator((function*() {
this;
}).bind(this)).bind(this);
}
};
function x() {
this;
()=>{
this;
};
_asyncToGenerator((function*() {
this;
}).bind(this)).bind(this);
}
}).bind(this))();
var _this = this;
return _asyncToGenerator((function*() {
this;
()=>_this
;
()=>{
_this;
()=>_this
;
function x() {
this;
()=>{
_this;
};
_asyncToGenerator(function*() {
_this;
});
}
};
function x() {
this;
()=>{
_this;
};
_asyncToGenerator(function*() {
_this;
});
}
}).bind(this))();
}
}
"#
@ -2261,15 +2262,16 @@ test!(
class A {
val = '1';
foo() {
return _asyncToGenerator((function*() {
try {
return yield _asyncToGenerator((function*(x) {
return x + this.val;
}).bind(this)).bind(this)('a');
} catch (e) {
throw e;
}
}).bind(this))();
var _this = this;
return _asyncToGenerator(function*() {
try {
return yield _asyncToGenerator(function*(x) {
return x + _this.val;
})('a');
} catch (e) {
throw e;
}
})();
}
}
"
@ -2315,11 +2317,12 @@ test!(
class A {
val = '1';
foo() {
return _asyncToGenerator((function*() {
return yield _asyncToGenerator((function*(x) {
return x + this.val;
}).bind(this)).bind(this)('a');
}).bind(this))();
var _this = this;
return _asyncToGenerator(function*() {
return yield _asyncToGenerator(function*(x) {
return x + _this.val;
})('a');
})();
}
}
"