Skip to content

Commit fa17cdf

Browse files
author
Joseph Watts
committed
Transform call expressions on private names to properly bind this
Signed-off-by: Joseph Watts <jwatts43@bloomberg.net>
1 parent 7bf2171 commit fa17cdf

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
}
@@ -683,6 +685,40 @@ namespace ts {
683685
return visitEachChild(node, visitor, context);
684686
}
685687

688+
function visitCallExpression(node: CallExpression) {
689+
if (isPrivateNamedPropertyAccessExpression(node.expression)) {
690+
// Transform call expressions of private names to properly bind the `this` parameter.
691+
let exprForPropertyAccess: Expression = node.expression.expression;
692+
let receiver = node.expression.expression;
693+
if (!isSimpleInlineableExpression(node.expression.expression)) {
694+
const generatedName = getGeneratedNameForNode(node);
695+
hoistVariableDeclaration(generatedName);
696+
exprForPropertyAccess = setOriginalNode(
697+
createAssignment(generatedName, exprForPropertyAccess),
698+
node.expression.expression
699+
);
700+
receiver = generatedName;
701+
}
702+
return visitNode(
703+
updateCall(
704+
node,
705+
createPropertyAccess(
706+
updatePropertyAccess(
707+
node.expression,
708+
exprForPropertyAccess,
709+
node.expression.name
710+
),
711+
"call"
712+
),
713+
/*typeArguments*/ undefined,
714+
[receiver, ...node.arguments]
715+
),
716+
visitor
717+
);
718+
}
719+
return visitEachChild(node, visitor, context);
720+
}
721+
686722
function enableSubstitutionForClassAliases() {
687723
if ((enabledSubstitutions & ESNextSubstitutionFlags.ClassAliases) === 0) {
688724
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)