fix(es/compat): Fix private methods of a class (#3123)

swc_ecma_transforms_compat:
 - `class_properties`: Handle private method calls properly. (Closes #3055)
This commit is contained in:
magic-akari 2021-12-27 03:37:46 +08:00 committed by GitHub
parent 836cbd63a8
commit 1b6ac25d5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 44 deletions

View File

@ -0,0 +1,8 @@
{
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2019"
}
}

View File

@ -0,0 +1,16 @@
export class Node {
childNodes: Node[] = [];
parent?: Node;
link(): void {
this.#link(this);
}
#link(parent: Node): void {
this.parent = parent;
for (const childNode of this.childNodes) {
childNode.#link(this);
}
}
}

View File

@ -0,0 +1,22 @@
function _classPrivateMethodGet(receiver, privateSet, fn) {
if (!privateSet.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return fn;
}
var _link = new WeakSet();
export class Node {
link() {
_classPrivateMethodGet(this, _link, link).call(this, this);
}
constructor(){
_link.add(this);
this.childNodes = [];
}
}
function link(parent) {
this.parent = parent;
for (const childNode of this.childNodes){
_classPrivateMethodGet(childNode, _link, link).call(childNode, this);
}
}

View File

@ -624,33 +624,32 @@ impl<'a> FieldAccessFolder<'a> {
};
}
if is_method {
let h = helper!(class_private_method_get, "classPrivateMethodGet");
return (
CallExpr {
span: DUMMY_SP,
callee: h,
args: vec![obj.as_arg(), ident.as_arg(), method_name.as_arg()],
type_args: Default::default(),
}
.into(),
Some(Expr::This(ThisExpr { span: DUMMY_SP })),
);
}
let get = helper!(class_private_field_get, "classPrivateFieldGet");
let get = if is_method {
helper!(class_private_method_get, "classPrivateMethodGet")
} else {
helper!(class_private_field_get, "classPrivateFieldGet")
};
match &*obj {
Expr::This(this) => (
CallExpr {
span: DUMMY_SP,
callee: get,
args: vec![this.as_arg(), ident.as_arg()],
if is_method {
CallExpr {
span: DUMMY_SP,
callee: get,
args: vec![obj.clone().as_arg(), ident.as_arg(), method_name.as_arg()],
type_args: Default::default(),
}
.into()
} else {
CallExpr {
span: DUMMY_SP,
callee: get,
args: vec![this.as_arg(), ident.as_arg()],
type_args: Default::default(),
}
.into(),
type_args: Default::default(),
}
.into()
},
Some(Expr::This(*this)),
),
_ => {
@ -669,31 +668,33 @@ impl<'a> FieldAccessFolder<'a> {
var
});
let first_arg = if is_alias_initialized {
var.clone().as_arg()
} else {
if aliased {
AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(Box::new(Pat::Ident(var.clone().into()))),
op: op!("="),
right: obj.take(),
}
.as_arg()
} else {
var.clone().as_arg()
}
};
let args = if is_method {
vec![first_arg, ident.as_arg(), method_name.as_arg()]
} else {
vec![first_arg, ident.as_arg()]
};
(
CallExpr {
span: DUMMY_SP,
callee: get,
args: vec![
if is_alias_initialized {
var.clone().as_arg()
} else {
if aliased {
AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(Box::new(Pat::Ident(
var.clone().into(),
))),
op: op!("="),
right: obj.take(),
}
.as_arg()
} else {
var.clone().as_arg()
}
},
ident.as_arg(),
],
args,
type_args: Default::default(),
}
.into(),

View File

@ -5236,6 +5236,45 @@ test!(
"
);
test!(
syntax(),
|_| class_properties(class_properties::Config { loose: false }),
issue_3055_1,
"
export class Node {
foo() {
this.#bar(this);
}
#bar(parent) {
parent.#baz(this);
parent.baz.#baz(this);
}
#baz(child) { }
}
",
"
var _bar = new WeakSet(),
_baz = new WeakSet();
export class Node {
foo() {
_classPrivateMethodGet(this, _bar, bar).call(this, this);
}
constructor() {
_bar.add(this);
_baz.add(this);
}
}
function bar(parent) {
var _baz1;
_classPrivateMethodGet(parent, _baz, baz).call(parent, this);
_classPrivateMethodGet(_baz1 = parent.baz, _baz, baz).call(_baz1, this);
}
function baz(child) {}
"
);
test!(
syntax(),
|_| chain!(