fix(es/compat): Fix handling of constructor in classes (#3505)

swc_ecma_transforms_compat:
 - Use `SuperFieldAccessFolder` for `super` fields in constructors.
 - Fix handling of variables in constructors with the same name as the class name.
 - Fix handling of functions in constructors with this/super.
This commit is contained in:
Austaras 2022-02-14 17:06:44 +08:00 committed by Donny/강동윤
parent 2830438663
commit 0b162102fa
24 changed files with 284 additions and 378 deletions

View File

@ -111,7 +111,7 @@ var Derived3 = /*#__PURE__*/ function(Base2) {
_classCallCheck(this, Derived3);
var _this;
var r = function r() {
_this = _super.call(this);
super();
} // error
;
return _possibleConstructorReturn(_this);

View File

@ -73,8 +73,7 @@ var Base = function() {
}(Base2), Derived3 = function(Base22) {
"use strict";
function Derived3() {
var _this;
return _classCallCheck(this, Derived3), _possibleConstructorReturn(_this);
return _classCallCheck(this, Derived3), _possibleConstructorReturn(void 0);
}
return _inherits(Derived3, Base22), _createSuper(Derived3), Derived3;
}(Base2), Derived4 = function(Base23) {

View File

@ -239,9 +239,8 @@ var SomeDerived1 = //super.publicInstanceMemberNotFunction in constructor of der
var _super = _createSuper(SomeDerived1);
function SomeDerived1() {
_classCallCheck(this, SomeDerived1);
var _thisSuper;
var _this = _super.call(this);
_set((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(SomeDerived1.prototype)), "publicMember", 1, _thisSuper, true);
_set((_assertThisInitialized(_this), _getPrototypeOf(SomeDerived1.prototype)), "publicMember", 1, _this, true);
return _this;
}
_createClass(SomeDerived1, [
@ -286,9 +285,8 @@ var SomeDerived2 = //super.privateProperty in constructor of derived class
var _super = _createSuper(SomeDerived2);
function SomeDerived2() {
_classCallCheck(this, SomeDerived2);
var _thisSuper;
var _this = _super.call(this);
_set((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(SomeDerived2.prototype)), "privateMember", 1, _thisSuper, true);
_set((_assertThisInitialized(_this), _getPrototypeOf(SomeDerived2.prototype)), "privateMember", 1, _this, true);
return _this;
}
_createClass(SomeDerived2, [

View File

@ -151,8 +151,8 @@ var SomeDerived1 = function(SomeBase) {
var _super = _createSuper(SomeDerived1);
function SomeDerived1() {
_classCallCheck(this, SomeDerived1);
var _thisSuper, _this = _super.call(this);
return _set((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(SomeDerived1.prototype)), "publicMember", 1, _thisSuper, !0), _this;
var _this = _super.call(this);
return _set((_assertThisInitialized(_this), _getPrototypeOf(SomeDerived1.prototype)), "publicMember", 1, _this, !0), _this;
}
return _createClass(SomeDerived1, [
{
@ -181,8 +181,8 @@ var SomeDerived1 = function(SomeBase) {
var _super = _createSuper(SomeDerived2);
function SomeDerived2() {
_classCallCheck(this, SomeDerived2);
var _thisSuper, _this = _super.call(this);
return _set((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(SomeDerived2.prototype)), "privateMember", 1, _thisSuper, !0), _this;
var _this = _super.call(this);
return _set((_assertThisInitialized(_this), _getPrototypeOf(SomeDerived2.prototype)), "privateMember", 1, _this, !0), _this;
}
return _createClass(SomeDerived2, [
{

View File

@ -215,9 +215,9 @@ var c3 = /*#__PURE__*/ function(c2) {
var _super = _createSuper(c3);
function c3() {
_classCallCheck(this, c3);
var _thisSuper, _this;
var _this;
_this = _super.call(this, 10);
_this.p1 = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(c3.prototype)), "c2_p1", _thisSuper);
_this.p1 = _get((_assertThisInitialized(_this), _getPrototypeOf(c3.prototype)), "c2_p1", _this);
return _this;
}
_createClass(c3, [
@ -309,9 +309,9 @@ var c6 = /*#__PURE__*/ function(c5) {
var _super = _createSuper(c6);
function c6() {
_classCallCheck(this, c6);
var _thisSuper, _this;
var _this;
_this = _super.call(this);
_this.d = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(c6.prototype)), "b", _thisSuper);
_this.d = _get((_assertThisInitialized(_this), _getPrototypeOf(c6.prototype)), "b", _this);
return _this;
}
return c6;

View File

@ -146,8 +146,8 @@ var c2 = function() {
_inherits(c3, c2);
var _super = _createSuper(c3);
function c3() {
var _thisSuper, _this;
return _classCallCheck(this, c3), (_this = _super.call(this, 10)).p1 = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(c3.prototype)), "c2_p1", _thisSuper), _this;
var _this;
return _classCallCheck(this, c3), (_this = _super.call(this, 10)).p1 = _get((_assertThisInitialized(_this), _getPrototypeOf(c3.prototype)), "c2_p1", _this), _this;
}
return _createClass(c3, [
{
@ -191,8 +191,8 @@ var c5 = function() {
_inherits(c6, c51);
var _super = _createSuper(c6);
function c6() {
var _thisSuper, _this;
return _classCallCheck(this, c6), (_this = _super.call(this)).d = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(c6.prototype)), "b", _thisSuper), _this;
var _this;
return _classCallCheck(this, c6), (_this = _super.call(this)).d = _get((_assertThisInitialized(_this), _getPrototypeOf(c6.prototype)), "b", _this), _this;
}
return c6;
}(c5);

View File

@ -125,7 +125,6 @@ var C = /*#__PURE__*/ function(CBase) {
var _super = _createSuper(C);
function C() {
_classCallCheck(this, C);
var _thisSuper;
var _this = _super.call(this, {
method: function method(p) {
p.length;
@ -133,7 +132,7 @@ var C = /*#__PURE__*/ function(CBase) {
});
// Should be okay.
// 'p' should have type 'string'.
_get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "foo", _thisSuper).call(_thisSuper, {
_get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "foo", _this).call(_this, {
method: function method(p) {
p.length;
}

View File

@ -77,12 +77,12 @@ var CBase = function() {
});
function C() {
_classCallCheck(this, C);
var _thisSuper, _this = _super.call(this, {
var _this = _super.call(this, {
method: function(p) {
p.length;
}
});
return _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "foo", _thisSuper).call(_thisSuper, {
return _get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "foo", _this).call(_this, {
method: function(p) {
p.length;
}

View File

@ -147,9 +147,8 @@ var SomeDerivedClass = /*#__PURE__*/ function(SomeBaseClass) {
var _super = _createSuper(SomeDerivedClass);
function SomeDerivedClass() {
_classCallCheck(this, SomeDerivedClass);
var _thisSuper;
var _this = _super.call(this);
var x = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(SomeDerivedClass.prototype)), "func", _thisSuper).call(_thisSuper);
var x = _get((_assertThisInitialized(_this), _getPrototypeOf(SomeDerivedClass.prototype)), "func", _this).call(_this);
var x;
return _this;
}

View File

@ -94,8 +94,8 @@ var SomeBaseClass = function() {
});
function SomeDerivedClass() {
_classCallCheck(this, SomeDerivedClass);
var _thisSuper, _this = _super.call(this);
return _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(SomeDerivedClass.prototype)), "func", _thisSuper).call(_thisSuper), _this;
var _this = _super.call(this);
return _get((_assertThisInitialized(_this), _getPrototypeOf(SomeDerivedClass.prototype)), "func", _this).call(_this), _this;
}
return _createClass(SomeDerivedClass, [
{

View File

@ -134,12 +134,12 @@ var C = /*#__PURE__*/ function(B) {
var _super = _createSuper(C);
function C() {
_classCallCheck(this, C);
var _thisSuper, _this;
var _this;
_this = _super.apply(this, arguments);
// these should be unaffected
_this.x = 1;
_this.y = _this.x;
_this.z = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _thisSuper).call(_thisSuper);
_this.z = _get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _this).call(_this);
return _this;
}
return C;

View File

@ -76,10 +76,10 @@ var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, _tmp, C = function(B) {
return self = this, (call = result) && ("object" == ((obj = call) && "undefined" != typeof Symbol && obj.constructor === Symbol ? "symbol" : typeof obj) || "function" == typeof call) ? call : _assertThisInitialized(self);
});
function C() {
var _thisSuper, _this;
var _this;
return !function(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}(this, C), _this = _super.apply(this, arguments), _this.x = 1, _this.y = _this.x, _this.z = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _thisSuper).call(_thisSuper), _this;
}(this, C), _this = _super.apply(this, arguments), _this.x = 1, _this.y = _this.x, _this.z = _get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _this).call(_this), _this;
}
return C;
}(B);

View File

@ -134,12 +134,12 @@ var C = /*#__PURE__*/ function(B) {
var _super = _createSuper(C);
function C() {
_classCallCheck(this, C);
var _thisSuper, _this;
var _this;
_this = _super.apply(this, arguments);
// these should be unaffected
_this.x = 1;
_this.y = _this.x;
_this.z = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _thisSuper).call(_thisSuper);
_this.z = _get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _this).call(_this);
return _this;
}
return C;

View File

@ -76,10 +76,10 @@ var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, _tmp, C = function(B) {
return self = this, (call = result) && ("object" == ((obj = call) && "undefined" != typeof Symbol && obj.constructor === Symbol ? "symbol" : typeof obj) || "function" == typeof call) ? call : _assertThisInitialized(self);
});
function C() {
var _thisSuper, _this;
var _this;
return !function(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}(this, C), _this = _super.apply(this, arguments), _this.x = 1, _this.y = _this.x, _this.z = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _thisSuper).call(_thisSuper), _this;
}(this, C), _this = _super.apply(this, arguments), _this.x = 1, _this.y = _this.x, _this.z = _get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _this).call(_this), _this;
}
return C;
}(B);

View File

@ -99,12 +99,12 @@ var C = /*#__PURE__*/ function(B) {
var _super = _createSuper(C);
function C() {
_classCallCheck(this, C);
var _thisSuper, _this;
var _this;
_this = _super.apply(this, arguments);
// these should be unaffected
_this.x = 1;
_this.y = _this.x;
_this.z = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _thisSuper).call(_thisSuper);
_this.z = _get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _this).call(_this);
return _this;
}
return C;

View File

@ -55,10 +55,10 @@ var ref, ref1, C = function(B) {
return self = this, (call = result) && ("object" == ((obj = call) && "undefined" != typeof Symbol && obj.constructor === Symbol ? "symbol" : typeof obj) || "function" == typeof call) ? call : _assertThisInitialized(self);
});
function C() {
var _thisSuper, _this;
var _this;
return !function(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}(this, C), _this = _super.apply(this, arguments), _this.x = 1, _this.y = _this.x, _this.z = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _thisSuper).call(_thisSuper), _this;
}(this, C), _this = _super.apply(this, arguments), _this.x = 1, _this.y = _this.x, _this.z = _get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _this).call(_this), _this;
}
return C;
}(B);

View File

@ -99,12 +99,12 @@ var C = /*#__PURE__*/ function(B) {
var _super = _createSuper(C);
function C() {
_classCallCheck(this, C);
var _thisSuper, _this;
var _this;
_this = _super.apply(this, arguments);
// these should be unaffected
_this.x = 1;
_this.y = _this.x;
_this.z = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _thisSuper).call(_thisSuper);
_this.z = _get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _this).call(_this);
return _this;
}
return C;

View File

@ -55,10 +55,10 @@ var ref, ref1, C = function(B) {
return self = this, (call = result) && ("object" == ((obj = call) && "undefined" != typeof Symbol && obj.constructor === Symbol ? "symbol" : typeof obj) || "function" == typeof call) ? call : _assertThisInitialized(self);
});
function C() {
var _thisSuper, _this;
var _this;
return !function(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}(this, C), _this = _super.apply(this, arguments), _this.x = 1, _this.y = _this.x, _this.z = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _thisSuper).call(_thisSuper), _this;
}(this, C), _this = _super.apply(this, arguments), _this.x = 1, _this.y = _this.x, _this.z = _get((_assertThisInitialized(_this), _getPrototypeOf(C.prototype)), "f", _this).call(_this), _this;
}
return C;
}(B);

View File

@ -739,7 +739,6 @@ impl Take for ParenExpr {
}
#[ast_node]
#[allow(variant_size_differences)]
#[derive(Eq, Hash, Is, EqIgnoreSpan)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum Callee {

View File

@ -69,12 +69,12 @@ impl<'a> VisitMut for SuperFieldAccessFolder<'a> {
fn visit_mut_expr(&mut self, n: &mut Expr) {
match n {
Expr::This(ThisExpr { span }) if self.in_nested_scope => {
if self.this_alias_mark.is_none() {
self.this_alias_mark = Some(Mark::fresh(Mark::root()));
}
*n = Expr::Ident(quote_ident!(
span.apply_mark(self.this_alias_mark.unwrap()),
span.apply_mark(
*self
.this_alias_mark
.get_or_insert_with(|| Mark::fresh(Mark::root()))
),
"_this"
));
return;
@ -149,7 +149,7 @@ impl<'a> SuperFieldAccessFolder<'a> {
..
}) = &**callee_expr
{
let this = match self.constructor_this_mark {
let this = match self.this_alias_mark.or(self.constructor_this_mark) {
Some(mark) => quote_ident!(DUMMY_SP.apply_mark(mark), "_this").as_arg(),
_ => ThisExpr { span: DUMMY_SP }.as_arg(),
};
@ -286,7 +286,7 @@ impl<'a> SuperFieldAccessFolder<'a> {
}
fn super_to_get_call(&mut self, super_token: Span, prop: SuperProp) -> Expr {
let proto_arg = get_prototype_of(if self.is_static {
let mut proto_arg = get_prototype_of(if self.is_static {
// Foo
Expr::Ident(self.class_name.clone())
} else {
@ -294,8 +294,25 @@ impl<'a> SuperFieldAccessFolder<'a> {
self.class_name
.clone()
.make_member(quote_ident!("prototype"))
})
.as_arg();
});
if let Some(mark) = self.constructor_this_mark {
let this = quote_ident!(DUMMY_SP.apply_mark(mark), "_this");
proto_arg = Expr::Seq(SeqExpr {
span: DUMMY_SP,
exprs: vec![
Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(assert_this_initialized, "assertThisInitialized"),
args: vec![this.as_arg()],
type_args: Default::default(),
})
.into(),
proto_arg.into(),
],
})
}
let prop_arg = match prop {
SuperProp::Ident(Ident {
@ -311,24 +328,14 @@ impl<'a> SuperFieldAccessFolder<'a> {
.as_arg();
let this_arg = match self.constructor_this_mark {
Some(mark) => {
let this = quote_ident!(super_token.apply_mark(mark), "_this");
CallExpr {
span: DUMMY_SP,
callee: helper!(assert_this_initialized, "assertThisInitialized"),
args: vec![this.as_arg()],
type_args: Default::default(),
}
.as_arg()
}
Some(mark) => quote_ident!(super_token.apply_mark(mark), "_this").as_arg(),
None => ThisExpr { span: super_token }.as_arg(),
};
Expr::Call(CallExpr {
span: super_token,
callee: helper!(get, "get"),
args: vec![proto_arg, prop_arg, this_arg],
args: vec![proto_arg.as_arg(), prop_arg, this_arg],
type_args: Default::default(),
})
}
@ -366,12 +373,29 @@ impl<'a> SuperFieldAccessFolder<'a> {
}
}
let proto_arg = get_prototype_of(
let mut proto_arg = get_prototype_of(
self.class_name
.clone()
.make_member(quote_ident!("prototype")),
)
.as_arg();
);
if let Some(mark) = self.constructor_this_mark {
let this = quote_ident!(DUMMY_SP.apply_mark(mark), "_this");
proto_arg = Expr::Seq(SeqExpr {
span: DUMMY_SP,
exprs: vec![
Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(assert_this_initialized, "assertThisInitialized"),
args: vec![this.as_arg()],
type_args: Default::default(),
})
.into(),
proto_arg.into(),
],
})
}
let prop_arg = match prop {
SuperProp::Ident(Ident {
@ -433,13 +457,16 @@ impl<'a> SuperFieldAccessFolder<'a> {
}
};
let this_arg = ThisExpr { span: super_token }.as_arg();
let this_arg = match self.constructor_this_mark {
Some(mark) => quote_ident!(super_token.apply_mark(mark), "_this").as_arg(),
None => ThisExpr { span: super_token }.as_arg(),
};
let expr = Expr::Call(CallExpr {
span: super_token,
callee: helper!(set, "set"),
args: vec![
proto_arg,
proto_arg.as_arg(),
prop_arg,
rhs_arg,
this_arg,

View File

@ -1,11 +1,11 @@
use std::iter;
use swc_atoms::{js_word, JsWord};
use swc_atoms::JsWord;
use swc_common::{util::take::Take, Mark, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
use swc_ecma_transforms_classes::{get_prototype_of, visit_mut_only_key};
use swc_ecma_utils::{private_ident, quote_ident, ExprFactory};
use swc_ecma_utils::{quote_ident, ExprFactory};
use swc_ecma_visit::{
noop_visit_mut_type, noop_visit_type, Visit, VisitMut, VisitMutWith, VisitWith,
};
@ -44,10 +44,6 @@ impl SuperCallFinder {
macro_rules! ignore_return {
($name:ident, $T:ty) => {
fn $name(&mut self, n: &mut $T) {
if self.in_injected_define_property_call {
return;
}
let old = self.ignore_return;
self.ignore_return = true;
n.visit_mut_children_with(self);
@ -156,11 +152,6 @@ pub(super) fn constructor_fn(c: Constructor) -> Function {
pub(super) struct ConstructorFolder<'a> {
pub class_name: &'a Ident,
pub mode: Option<SuperFoldingMode>,
/// Variables named `thisSuper` will be added if `super.foo` or
/// `super.foo()` is used in constructor.
pub vars: &'a mut Vec<VarDeclarator>,
pub cur_this_super: Option<Ident>,
/// Mark for `_this`
pub mark: Mark,
@ -168,132 +159,6 @@ pub(super) struct ConstructorFolder<'a> {
pub super_var: Option<Ident>,
/// True when recursing into other function or class.
pub ignore_return: bool,
pub in_injected_define_property_call: bool,
}
impl ConstructorFolder<'_> {
/// `true` means it's processed and children are folded.
fn handle_super_assignment(&mut self, e: &mut Expr) -> bool {
if let Expr::Assign(AssignExpr {
left,
op: op!("="),
right,
span,
}) = e
{
let left = left.take().normalize_expr();
match left {
PatOrExpr::Expr(mut left_expr) => match *left_expr {
Expr::SuperProp(_) => {
right.visit_mut_children_with(self);
self.handle_super_access(&mut left_expr, Some(right.take()));
*e = *left_expr;
true
}
_ => {
*e = Expr::Assign(AssignExpr {
span: *span,
left: PatOrExpr::Expr(left_expr),
op: op!("="),
right: right.take(),
});
false
}
},
_ => {
*e = Expr::Assign(AssignExpr {
span: *span,
left,
op: op!("="),
right: right.take(),
});
false
}
}
} else {
false
}
}
fn handle_super_access(&mut self, e: &mut Expr, set_to: Option<Box<Expr>>) {
if let Expr::SuperProp(SuperPropExpr { span, prop, .. }) = e {
let this_var = quote_ident!(DUMMY_SP.apply_mark(self.mark), "_this");
let this_super = private_ident!("_thisSuper");
self.cur_this_super = Some(this_super.clone());
self.vars.push(VarDeclarator {
span: DUMMY_SP,
name: this_super.clone().into(),
init: None,
definite: Default::default(),
});
// _thisSuper = _assertThisInitialized(_this)
let init_this_super = Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(Box::new(this_super.clone().into())),
right: Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(assert_this_initialized, "assertThisInitialized"),
args: vec![this_var.as_arg()],
type_args: Default::default(),
})),
}));
// _getPrototypeOf(Foo.prototype)
let get_proto = Box::new(Expr::Call(CallExpr {
span: *span,
callee: helper!(get_prototype_of, "getPrototypeOf"),
args: vec![self
.class_name
.clone()
.make_member(quote_ident!("prototype"))
.as_arg()],
type_args: Default::default(),
}));
*e = Expr::Call(CallExpr {
span: DUMMY_SP,
callee: if set_to.is_some() {
helper!(set, "set")
} else {
helper!(get, "get")
},
args: {
let mut args = vec![
Expr::Seq(SeqExpr {
span: DUMMY_SP,
exprs: vec![init_this_super, get_proto],
})
.as_arg(),
match prop {
SuperProp::Computed(ComputedPropName { expr, .. }) => {
expr.take().as_arg()
}
SuperProp::Ident(ident) => Str {
span: ident.span,
value: ident.take().sym,
has_escape: false,
kind: Default::default(),
}
.as_arg(),
},
];
let is_set = set_to.is_some();
args.extend(set_to.map(|v| v.as_arg()));
args.push(this_super.as_arg());
if is_set {
args.push(true.as_arg());
}
args
},
type_args: Default::default(),
})
};
}
}
/// `None`: `return _possibleConstructorReturn`
@ -310,125 +175,66 @@ impl VisitMut for ConstructorFolder<'_> {
visit_mut_only_key!();
ignore_return!(visit_mut_function, Function);
ignore_return!(visit_mut_class, Class);
ignore_return!(visit_mut_arrow_expr, ArrowExpr);
ignore_return!(visit_mut_constructor, Constructor);
fn visit_mut_call_expr(&mut self, e: &mut CallExpr) {
match &e.callee {
Callee::Expr(callee) => match &**callee {
Expr::SuperProp(_) => {
let old = self.cur_this_super.take();
e.visit_mut_children_with(self);
let this_super = self.cur_this_super.take().unwrap();
self.cur_this_super = old;
e.args.insert(0, this_super.as_arg());
e.callee = e
.callee
.take()
.expect_expr()
.make_member(quote_ident!("call"))
.as_callee();
}
_ => e.visit_mut_children_with(self),
},
_ => e.visit_mut_children_with(self),
}
}
fn visit_mut_function(&mut self, _: &mut Function) {}
fn visit_mut_expr(&mut self, expr: &mut Expr) {
match self.mode {
Some(SuperFoldingMode::Assign) => {}
_ => {
if !self.handle_super_assignment(expr) {
expr.visit_mut_children_with(self);
};
self.handle_super_access(expr, None);
expr.visit_mut_children_with(self);
return;
}
}
// We pretend method folding mode for while folding injected `_defineProperty`
// calls.
expr.visit_mut_children_with(self);
if let Expr::Call(CallExpr {
callee: Callee::Expr(callee),
callee: Callee::Super(..),
args,
..
}) = expr
{
if let Expr::Ident(Ident {
sym: js_word!("_defineProperty"),
..
}) = &**callee
{
let old = self.in_injected_define_property_call;
self.in_injected_define_property_call = true;
expr.visit_mut_children_with(self);
self.in_injected_define_property_call = old;
return;
}
}
if !self.handle_super_assignment(expr) {
expr.visit_mut_children_with(self);
};
match expr {
Expr::This(e) => {
*expr = Expr::Ident(Ident::new("_this".into(), e.span.apply_mark(self.mark)))
}
Expr::Call(CallExpr {
callee: Callee::Super(..),
args,
..
}) => {
let right = match self.super_var.clone() {
Some(super_var) => Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: if self.is_constructor_default {
super_var.make_member(quote_ident!("apply")).as_callee()
} else {
super_var.make_member(quote_ident!("call")).as_callee()
},
args: if self.is_constructor_default {
vec![
ThisExpr { span: DUMMY_SP }.as_arg(),
quote_ident!("arguments").as_arg(),
]
} else {
let mut call_args = vec![ThisExpr { span: DUMMY_SP }.as_arg()];
call_args.extend(args.take());
call_args
},
type_args: Default::default(),
})),
None => Box::new(make_possible_return_value(ReturningMode::Prototype {
class_name: self.class_name.clone(),
args: Some(args.take()),
is_constructor_default: self.is_constructor_default,
})),
};
*expr = Expr::Assign(AssignExpr {
let right = match self.super_var.clone() {
Some(super_var) => Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(
quote_ident!(DUMMY_SP.apply_mark(self.mark), "_this").into(),
),
op: op!("="),
right,
});
}
callee: if self.is_constructor_default {
super_var.make_member(quote_ident!("apply")).as_callee()
} else {
super_var.make_member(quote_ident!("call")).as_callee()
},
args: if self.is_constructor_default {
vec![
ThisExpr { span: DUMMY_SP }.as_arg(),
quote_ident!("arguments").as_arg(),
]
} else {
let mut call_args = vec![ThisExpr { span: DUMMY_SP }.as_arg()];
call_args.extend(args.take());
_ => self.handle_super_access(expr, None),
call_args
},
type_args: Default::default(),
})),
None => Box::new(make_possible_return_value(ReturningMode::Prototype {
class_name: self.class_name.clone(),
args: Some(args.take()),
is_constructor_default: self.is_constructor_default,
})),
};
*expr = Expr::Assign(AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(quote_ident!(DUMMY_SP.apply_mark(self.mark), "_this").into()),
op: op!("="),
right,
});
};
}
@ -631,35 +437,17 @@ pub(super) fn replace_this_in_constructor(mark: Mark, c: &mut Constructor) -> bo
mark: Mark,
found: bool,
wrap_with_assertion: bool,
in_injected_define_property_call: bool,
}
impl VisitMut for Replacer {
noop_visit_mut_type!();
fn visit_mut_class(&mut self, _: &mut Class) {}
// let computed keys be visited
fn visit_mut_constructor(&mut self, _: &mut Constructor) {}
fn visit_mut_function(&mut self, _: &mut Function) {}
fn visit_mut_expr(&mut self, expr: &mut Expr) {
// We pretend method folding mode for while folding injected `_defineProperty`
// calls.
if let Expr::Call(CallExpr {
callee: Callee::Expr(callee),
..
}) = expr
{
if let Expr::Ident(Ident {
sym: js_word!("_defineProperty"),
..
}) = &**callee
{
let old = self.in_injected_define_property_call;
self.in_injected_define_property_call = true;
expr.visit_mut_children_with(self);
self.in_injected_define_property_call = old;
return;
}
}
match expr {
Expr::This(..) => {
self.found = true;
@ -681,13 +469,6 @@ pub(super) fn replace_this_in_constructor(mark: Mark, c: &mut Constructor) -> bo
}
}
fn visit_mut_function(&mut self, n: &mut Function) {
if self.in_injected_define_property_call {
return;
}
n.visit_mut_children_with(self)
}
fn visit_mut_member_expr(&mut self, expr: &mut MemberExpr) {
if self.mark != Mark::root() {
let old = self.wrap_with_assertion;
@ -704,9 +485,8 @@ pub(super) fn replace_this_in_constructor(mark: Mark, c: &mut Constructor) -> bo
found: false,
mark,
wrap_with_assertion: true,
in_injected_define_property_call: false,
};
c.visit_mut_with(&mut v);
c.visit_mut_children_with(&mut v);
v.found
}

View File

@ -17,7 +17,7 @@ use tracing::debug;
use self::{
constructor::{
constructor_fn, make_possible_return_value, replace_this_in_constructor, ConstructorFolder,
ReturningMode, SuperCallFinder, SuperFoldingMode, VarRenamer,
ReturningMode, SuperCallFinder, SuperFoldingMode,
},
prop_name::HashKey,
};
@ -473,10 +473,11 @@ where
constructor.unwrap_or_else(|| default_constructor(super_class_ident.is_some()));
// Rename variables to avoid conflicting with class name
constructor.body.visit_mut_with(&mut VarRenamer {
mark: Mark::fresh(Mark::root()),
class_name: &class_name.sym,
});
// TODO: bring it back once we have a proper private ident
// constructor.body.visit_mut_with(&mut VarRenamer {
// mark: Mark::fresh(Mark::root()),
// class_name: &class_name.sym,
// });
// Black magic to detect injected constructor.
let is_constructor_default = constructor.span.is_dummy();
@ -529,14 +530,10 @@ where
} else {
mode
},
vars: &mut vars,
// This if expression is required to handle super() call in all case
cur_this_super: None,
mark: this_mark,
is_constructor_default,
super_var,
ignore_return: false,
in_injected_define_property_call: false,
});
insert_this |= (mode == None && !is_always_initialized)

View File

@ -831,14 +831,13 @@ class Test extends Foo {
function Test() {
_classCallCheck(this, Test);
var _thisSuper, _thisSuper1;
var _this = _super.call(this);
_get((_assertThisInitialized(_this), _getPrototypeOf(Test.prototype)), "test", _this).whatever();
_get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(Test.prototype)), "test", _thisSuper).whatever();
_get((_thisSuper1 = _assertThisInitialized(_this), _getPrototypeOf(Test.prototype)), "test", _thisSuper1).call(_thisSuper1);
_get((_assertThisInitialized(_this), _getPrototypeOf(Test.prototype)), "test", _this).call(_this);
return _this;
}
@ -1663,7 +1662,6 @@ var Outer = function (Hello) {
var _super = _createSuper(Outer);
function Outer() {
_classCallCheck(this, Outer);
var _thisSuper;
var _this = _super.call(this);
var Inner = /*#__PURE__*/function () {
@ -1672,7 +1670,7 @@ var Outer = function (Hello) {
}
_createClass(Inner, [{
key: _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(Outer.prototype)), "toString", _thisSuper).call(_thisSuper),
key: _get((_assertThisInitialized(_this), _getPrototypeOf(Outer.prototype)), "toString", _this).call(_this),
value: function () {
return 'hello';
}
@ -1703,7 +1701,6 @@ class Foo extends Bar {
super[super().method]();
}
}
"#,
r#"
var Foo = /*#__PURE__*/function (Bar) {
@ -1715,17 +1712,15 @@ class Foo extends Bar {
function Foo() {
_classCallCheck(this, Foo);
var _thisSuper, _this;
_get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(Foo.prototype)), (_this = _super.call(this)).method, _thisSuper).call(_thisSuper);
var _this;
_get((_assertThisInitialized(_this), _getPrototypeOf(Foo.prototype)), (_this = _super.call(this)).method, _this).call(_this);
return _possibleConstructorReturn(_this);
}
return Foo;
}(Bar);
"#
);
@ -1860,20 +1855,19 @@ class Test extends Foo {
var Test = /*#__PURE__*/function (Foo) {
"use strict";
_inherits(Test, Foo);
var _super = _createSuper(Test);
function Test() {
_classCallCheck(this, Test);
var _thisSuper, _thisSuper1;
var _this = _super.call(this);
_get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(Test.prototype)), "test", _thisSuper);
_get((_thisSuper1 = _assertThisInitialized(_this), _getPrototypeOf(Test.prototype)), "test", _thisSuper1).whatever;
_get((_assertThisInitialized(_this), _getPrototypeOf(Test.prototype)), "test", _this);
_get((_assertThisInitialized(_this), _getPrototypeOf(Test.prototype)), "test", _this).whatever;
return _this;
}
return Test;
}(Foo);
@ -2205,16 +2199,16 @@ var _super = _createSuper(ColorPoint);
function ColorPoint() {
_classCallCheck(this, ColorPoint);
var _thisSuper, _thisSuper1, _this;
var _this;
_this = _super.call(this);
_this.x = 2;
_set((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(ColorPoint.prototype)), "x", 3, _thisSuper, true);
_set((_assertThisInitialized(_this), _getPrototypeOf(ColorPoint.prototype)), "x", 3, _this, true);
expect(_this.x).toBe(3); // A
expect(_get((_thisSuper1 = _assertThisInitialized(_this), _getPrototypeOf(ColorPoint.prototype)), "x", _thisSuper1)).toBeUndefined(); // B
expect(_get((_assertThisInitialized(_this), _getPrototypeOf(ColorPoint.prototype)), "x", _this)).toBeUndefined(); // B
return _this;
}
@ -3741,10 +3735,9 @@ function (Hello) {
var _super = _createSuper(Outer);
function Outer() {
_classCallCheck(this, Outer);
var _thisSuper;
var _this = _super.call(this);
var Inner = {
[_get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(Outer.prototype)), "toString", _thisSuper).call(_thisSuper)] () {
[_get((_assertThisInitialized(_this), _getPrototypeOf(Outer.prototype)), "toString", _this).call(_this)] () {
return 'hello';
}
@ -3760,6 +3753,59 @@ expect(new Outer().hello()).toBe('hello');
"#
);
test!(
syntax(),
|t| tr(t),
nested_this_in_key,
r#"
class Outer extends B {
constructor() {
class Inner {
[this]() {
return 'hello';
}
}
function foo() {
return this;
}
return new Inner();
}
}
"#,
r#"
let Outer = function(B) {
"use strict";
_inherits(Outer, B);
var _super = _createSuper(Outer);
function Outer() {
_classCallCheck(this, Outer);
var _this;
let Inner = function() {
function Inner() {
_classCallCheck(this, Inner);
}
_createClass(Inner, [
{
key: _assertThisInitialized(_this),
value: function () {
return 'hello';
}
}
]);
return Inner;
}();
function foo() {
return this;
}
return _possibleConstructorReturn(_this, new Inner());
}
return Outer;
}(B);
"#
);
// get_set_set_semantics_not_defined_on_parent_setter_on_obj_exec
test_exec!(
syntax(),
@ -4905,6 +4951,40 @@ test!(
}"
);
test!(
syntax(),
|t| chain!(resolver(), classes(Some(t.comments.clone()))),
duplicate_ident,
r#"
class Foo extends Bar {
constructor() {
var Foo = 123;
console.log(Foo)
}
}
"#,
r#"
let Foo = /*#__PURE__*/function (Bar) {
"use strict";
_inherits(Foo, Bar);
var _super = _createSuper(Foo);
function Foo() {
_classCallCheck(this, Foo);
var _this;
var Foo1 = 123;
console.log(Foo1)
return _possibleConstructorReturn(_this);
}
return Foo;
}(Bar);
"#
);
//// regression_3028
//test!(
// syntax(),
@ -6327,6 +6407,36 @@ test!(
"
);
test!(
syntax(),
|t| classes(Some(t.comments.clone())),
constructor_super_update,
"
class A extends B{
constructor() {
super.foo ++;
super.bar += 123;
}
}
",
r#"
let A = function(B) {
"use strict";
_inherits(A, B);
var _super = _createSuper(A);
function A() {
_classCallCheck(this, A);
var _ref, _superRef, _ref1;
var _this;
_set((_assertThisInitialized(_this), _getPrototypeOf(A.prototype)), _ref = "foo", (_superRef = +_get((_assertThisInitialized(_this), _getPrototypeOf(A.prototype)), _ref, _this)) + 1, _this, true), _superRef;
_set((_assertThisInitialized(_this), _getPrototypeOf(A.prototype)), _ref1 = "bar", _get((_assertThisInitialized(_this), _getPrototypeOf(A.prototype)), _ref1, _this) + 123, _this, true);
return _possibleConstructorReturn(_this);
}
return A;
}(B);
"#
);
test!(
syntax(),
|t| classes(Some(t.comments.clone())),

View File

@ -923,12 +923,11 @@ function (A) {
var _super = _createSuper(B);
function B() {
_classCallCheck(this, B);
var _thisSuper, _this;
var _this;
_this = _super.apply(this, arguments);
_defineProperty(_assertThisInitialized(_this), 'foo', _get((_thisSuper = \
_assertThisInitialized(_this), _getPrototypeOf(B.prototype)), 'foo', \
_thisSuper).call(_thisSuper));
_defineProperty(_assertThisInitialized(_this), 'foo', _get((_assertThisInitialized(_this), \
_getPrototypeOf(B.prototype)), 'foo', _this).call(_this));
return _this;
}
@ -1812,13 +1811,13 @@ function (A) {
var _super = _createSuper(B);
function B() {
_classCallCheck(this, B);
var _thisSuper, _this;
var _this;
_this = _super.apply(this, arguments);
_foo.set(_assertThisInitialized(_this), {
writable: true,
value: _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(B.prototype)), "foo", _thisSuper).call(_thisSuper)
value: _get((_assertThisInitialized(_this), _getPrototypeOf(B.prototype)), "foo", _this).call(_this)
});
return _this;
@ -1930,10 +1929,9 @@ var Outer = function (Hello) {
var _super = _createSuper(Outer);
function Outer() {
_classCallCheck(this, Outer);
var _thisSuper;
var _this = _super.call(this);
var _ref = _get((_thisSuper = _assertThisInitialized(_this), _getPrototypeOf(Outer.prototype)), "toString", _thisSuper).call(_thisSuper);
var _ref = _get((_assertThisInitialized(_this), _getPrototypeOf(Outer.prototype)), "toString", _this).call(_this);
var Inner = function Inner() {
_classCallCheck(this, Inner);