Skip to content

Commit 4f59597

Browse files
author
Joseph Watts
committed
Transform call expressions on private names to properly bind this
Signed-off-by: Joseph Watts <[email protected]>
1 parent 0945301 commit 4f59597

File tree

5 files changed

+140
-0
lines changed

5 files changed

+140
-0
lines changed

src/compiler/transformers/esnext.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ namespace ts {
169169
return visitPrefixUnaryExpression(node as PrefixUnaryExpression);
170170
case SyntaxKind.PostfixUnaryExpression:
171171
return visitPostfixUnaryExpression(node as PostfixUnaryExpression);
172+
case SyntaxKind.CallExpression:
173+
return visitCallExpression(node as CallExpression);
172174
default:
173175
return visitEachChild(node, visitor, context);
174176
}
@@ -676,6 +678,40 @@ namespace ts {
676678
return visitEachChild(node, visitor, context);
677679
}
678680

681+
function visitCallExpression(node: CallExpression) {
682+
if (isPrivateNamedPropertyAccessExpression(node.expression)) {
683+
// Transform call expressions of private names to properly bind the `this` parameter.
684+
let exprForPropertyAccess: Expression = node.expression.expression;
685+
let receiver = node.expression.expression;
686+
if (!isSimpleInlineableExpression(node.expression.expression)) {
687+
const generatedName = getGeneratedNameForNode(node);
688+
hoistVariableDeclaration(generatedName);
689+
exprForPropertyAccess = setOriginalNode(
690+
createAssignment(generatedName, exprForPropertyAccess),
691+
node.expression.expression
692+
);
693+
receiver = generatedName;
694+
}
695+
return visitNode(
696+
updateCall(
697+
node,
698+
createPropertyAccess(
699+
updatePropertyAccess(
700+
node.expression,
701+
exprForPropertyAccess,
702+
node.expression.name
703+
),
704+
"call"
705+
),
706+
/*typeArguments*/ undefined,
707+
[receiver, ...node.arguments]
708+
),
709+
visitor
710+
);
711+
}
712+
return visitEachChild(node, visitor, context);
713+
}
714+
679715
function enableSubstitutionForClassAliases() {
680716
if ((enabledSubstitutions & ESNextSubstitutionFlags.ClassAliases) === 0) {
681717
enabledSubstitutions |= ESNextSubstitutionFlags.ClassAliases;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//// [privateNameFieldCallExpression.ts]
2+
class A {
3+
#fieldFunc = () => this.x = 10;
4+
x = 1;
5+
test() {
6+
this.#fieldFunc();
7+
const func = this.#fieldFunc;
8+
func();
9+
}
10+
}
11+
12+
13+
//// [privateNameFieldCallExpression.js]
14+
var _classPrivateFieldGet = function (receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); };
15+
var _fieldFunc;
16+
var A = /** @class */ (function () {
17+
function A() {
18+
var _this = this;
19+
_fieldFunc.set(this, function () { return _this.x = 10; });
20+
this.x = 1;
21+
}
22+
A.prototype.test = function () {
23+
_classPrivateFieldGet(this, _fieldFunc).call(this);
24+
var func = _classPrivateFieldGet(this, _fieldFunc);
25+
func();
26+
};
27+
return A;
28+
}());
29+
_fieldFunc = new WeakMap();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameFieldCallExpression.ts ===
2+
class A {
3+
>A : Symbol(A, Decl(privateNameFieldCallExpression.ts, 0, 0))
4+
5+
#fieldFunc = () => this.x = 10;
6+
>#fieldFunc : Symbol(A[#fieldFunc], Decl(privateNameFieldCallExpression.ts, 0, 9))
7+
>this.x : Symbol(A.x, Decl(privateNameFieldCallExpression.ts, 1, 35))
8+
>this : Symbol(A, Decl(privateNameFieldCallExpression.ts, 0, 0))
9+
>x : Symbol(A.x, Decl(privateNameFieldCallExpression.ts, 1, 35))
10+
11+
x = 1;
12+
>x : Symbol(A.x, Decl(privateNameFieldCallExpression.ts, 1, 35))
13+
14+
test() {
15+
>test : Symbol(A.test, Decl(privateNameFieldCallExpression.ts, 2, 10))
16+
17+
this.#fieldFunc();
18+
>this.#fieldFunc : Symbol(A[#fieldFunc], Decl(privateNameFieldCallExpression.ts, 0, 9))
19+
>this : Symbol(A, Decl(privateNameFieldCallExpression.ts, 0, 0))
20+
21+
const func = this.#fieldFunc;
22+
>func : Symbol(func, Decl(privateNameFieldCallExpression.ts, 5, 13))
23+
>this.#fieldFunc : Symbol(A[#fieldFunc], Decl(privateNameFieldCallExpression.ts, 0, 9))
24+
>this : Symbol(A, Decl(privateNameFieldCallExpression.ts, 0, 0))
25+
26+
func();
27+
>func : Symbol(func, Decl(privateNameFieldCallExpression.ts, 5, 13))
28+
}
29+
}
30+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNameFieldCallExpression.ts ===
2+
class A {
3+
>A : A
4+
5+
#fieldFunc = () => this.x = 10;
6+
>#fieldFunc : () => number
7+
>() => this.x = 10 : () => number
8+
>this.x = 10 : 10
9+
>this.x : number
10+
>this : this
11+
>x : number
12+
>10 : 10
13+
14+
x = 1;
15+
>x : number
16+
>1 : 1
17+
18+
test() {
19+
>test : () => void
20+
21+
this.#fieldFunc();
22+
>this.#fieldFunc() : number
23+
>this.#fieldFunc : () => number
24+
>this : this
25+
26+
const func = this.#fieldFunc;
27+
>func : () => number
28+
>this.#fieldFunc : () => number
29+
>this : this
30+
31+
func();
32+
>func() : number
33+
>func : () => number
34+
}
35+
}
36+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class A {
2+
#fieldFunc = () => this.x = 10;
3+
x = 1;
4+
test() {
5+
this.#fieldFunc();
6+
const func = this.#fieldFunc;
7+
func();
8+
}
9+
}

0 commit comments

Comments
 (0)