Skip to content

Commit 1769bb1

Browse files
committed
Transform private-named methods
Signed-off-by: Max Heiber <[email protected]>
1 parent a7303e1 commit 1769bb1

10 files changed

+403
-56
lines changed

src/compiler/transformers/esnext.ts

+61-48
Original file line numberDiff line numberDiff line change
@@ -383,20 +383,18 @@ namespace ts {
383383

384384
pendingExpressions = pendingExpressions || [];
385385
last(privateNameEnvironmentStack).forEach(entry => {
386-
const { placement } = entry;
387-
switch (placement) {
386+
switch (entry.placement) {
388387
case PrivateNamePlacement.InstanceField:
389388
break;
390389
case PrivateNamePlacement.InstanceMethod:
391-
entry = entry as PrivateNamedInstanceMethod;
392-
const func = privateNamedMethodToFunction(entry.origFunc, entry.funcName, entry.accumulator);
390+
const func = privateNamedMethodToFunction(entry.origFunc, entry.funcName);
393391
(pendingExpressions = pendingExpressions || []).push(createAssignment(
394392
entry.funcName,
395393
func
396394
));
397395
break;
398396
default:
399-
Debug.assertNever(placement, "unexpected Private Name Placement");
397+
Debug.assertNever(entry, "unexpected private name placement");
400398
}
401399
});
402400
const members: ClassElement[] = [];
@@ -616,29 +614,16 @@ namespace ts {
616614
privateNameEnvironmentStack.pop();
617615
}
618616

619-
function privateNamedMethodToFunction (declaration: MethodDeclaration, funcName: Identifier, accumulator: Identifier): FunctionExpression {
617+
function privateNamedMethodToFunction (declaration: MethodDeclaration, funcName: Identifier): FunctionExpression {
620618
const params = declaration.parameters;
621-
let body = getMutableClone(declaration.body || createBlock([], true));
622-
body = visitEachChild(body, visitor, context);
623-
const toPrepend = startOnNewLine(
624-
createStatement(
625-
createClassPrivateNamedCallCheckHelper(context, accumulator)
626-
)
627-
);
628-
body.statements = setTextRange(
629-
createNodeArray([
630-
toPrepend,
631-
...body.statements
632-
]),
633-
body.statements
634-
);
619+
const body = visitEachChild(declaration.body, visitor, context) || createBlock([], /* multiLine */ true);
635620
const func = createFunctionExpression(
636-
/* modifiers */ undefined,
637-
/* asteriskToken */ undefined,
638-
funcName,
639-
/* typeParameters */ undefined,
640-
params,
641-
/* type */ undefined,
621+
/* modifiers */ undefined,
622+
/* asteriskToken */ undefined,
623+
funcName,
624+
/* typeParameters*/ undefined,
625+
params,
626+
/* type */ undefined,
642627
body);
643628
return func;
644629
}
@@ -647,21 +632,22 @@ namespace ts {
647632
function addPrivateName(element: ClassElement & { name: PrivateName }) {
648633
const env = last(privateNameEnvironmentStack);
649634
const text = getTextOfPropertyName(element.name) as string;
650-
const accumulator = createUniqueName(`_${text.substring(1)}`);
651635
const { escapedText } = element.name;
652-
hoistVariableDeclaration(accumulator);
653636

654637
let identifierName: string;
638+
let accumulator: Identifier;
655639
if (hasModifier(element, ModifierFlags.Static)) {
656640
// statics not supported yet
657641
return;
658642
}
659643
if (isPropertyDeclaration(element)) {
660644
identifierName = "WeakMap";
645+
accumulator = createUniqueName(`_${text.slice(1)}WeakMap`);
661646
env.set(escapedText, { placement: PrivateNamePlacement.InstanceField, accumulator });
662647
}
663648
else if (isMethodDeclaration(element)) {
664649
identifierName = "WeakSet";
650+
accumulator = createUniqueName(`_${text.slice(1)}WeakSet`);
665651
const escapedText = element.name.escapedText;
666652
const escapedTextNoHash = `_${`${escapedText}`.slice(1)}`;
667653
const funcName: Identifier = createUniqueName(escapedTextNoHash);
@@ -671,6 +657,7 @@ namespace ts {
671657
else {
672658
return;
673659
}
660+
hoistVariableDeclaration(accumulator);
674661
(pendingExpressions = pendingExpressions || []).push(
675662
createAssignment(
676663
accumulator,
@@ -696,18 +683,36 @@ namespace ts {
696683
function visitPropertyAccessExpression(node: PropertyAccessExpression) {
697684
if (isPrivateName(node.name)) {
698685
const privateNameInfo = accessPrivateName(node.name);
699-
if (privateNameInfo && privateNameInfo.placement === PrivateNamePlacement.InstanceField) {
700-
return setOriginalNode(
701-
setTextRange(
702-
createClassPrivateFieldGetHelper(
703-
context,
704-
visitNode(node.expression, visitor, isExpression),
705-
privateNameInfo.accumulator
686+
if (!privateNameInfo) {
687+
return visitEachChild(node, visitor, context);
688+
}
689+
switch (privateNameInfo.placement) {
690+
case PrivateNamePlacement.InstanceField:
691+
return setOriginalNode(
692+
setTextRange(
693+
createClassPrivateFieldGetHelper(
694+
context,
695+
visitNode(node.expression, visitor, isExpression),
696+
privateNameInfo.accumulator
697+
),
698+
node
706699
),
707700
node
708-
),
709-
node
710-
);
701+
);
702+
case PrivateNamePlacement.InstanceMethod:
703+
return setOriginalNode(
704+
setTextRange(
705+
createClassPrivateNamedMethodGetHelper(
706+
context,
707+
privateNameInfo.accumulator,
708+
privateNameInfo.funcName
709+
),
710+
node
711+
),
712+
node
713+
);
714+
default:
715+
Debug.assertNever(privateNameInfo, "Unexpected private name placement");
711716
}
712717
}
713718
return visitEachChild(node, visitor, context);
@@ -804,13 +809,18 @@ namespace ts {
804809
setTextRange(
805810
createCall(
806811
createPropertyAccess(
807-
privateNameEntry.funcName,
812+
createClassPrivateNamedMethodGetHelper(
813+
context,
814+
privateNameEntry.accumulator,
815+
privateNameEntry.funcName
816+
),
808817
"call"
809818
),
810-
/*typeArguments*/ undefined,
811-
[createThis(), ...node.arguments]
819+
/* typeArguments */ undefined,
820+
// node.arguments
821+
concatenate([createThis()], node.arguments)
812822
),
813-
/* location */ node
823+
/* location */ node
814824
),
815825
node
816826
);
@@ -1985,15 +1995,18 @@ namespace ts {
19851995
context.requestEmitHelper(classPrivateFieldSetHelper);
19861996
return createCall(getHelperName("_classPrivateFieldSet"), /* typeArguments */ undefined, [receiver, privateField, value]);
19871997
}
1988-
const classPrivateNamedCallCheckHelper: EmitHelper = {
1989-
name: "typescript:classPrivateNamedCallCheck",
1998+
1999+
const classPrivateNamedMethodGet: EmitHelper = {
2000+
name: "typescript:classPrivateNamedMethodGet",
19902001
scoped: false,
1991-
text: `var _classPrivateNamedCallCheck = function (receiver, privateSet) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get weak field on non-instance"); }};`
2002+
text: `function _classPrivateNamedMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }`
19922003
};
19932004

1994-
function createClassPrivateNamedCallCheckHelper(context: TransformationContext, weakSet: Identifier) {
1995-
context.requestEmitHelper(classPrivateNamedCallCheckHelper);
1996-
return createCall(getHelperName("_classPrivateNamedCallCheck"), /* typeArguments */ undefined, [ createThis(), weakSet ]);
2005+
function createClassPrivateNamedMethodGetHelper(context: TransformationContext, weakSet: Identifier, name: Identifier) {
2006+
2007+
context.requestEmitHelper(classPrivateNamedMethodGet);
2008+
return createCall(getHelperName("_classPrivateNamedMethodGet"), /* typeArguments */ undefined,
2009+
[ createThis(), weakSet, name ]);
19972010
}
19982011

19992012
}

tests/baselines/reference/privateNamedMemberOrder.js

+7-8
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,16 @@ class A {
99

1010
//// [privateNamedMemberOrder.js]
1111
var _classPrivateFieldGet = function (receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); };
12-
var _classPrivateNamedCallCheck = function (receiver, privateSet) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get weak field on non-instance"); }};
13-
var _foo_1, _bar_1, _bar_2;
12+
function _classPrivateNamedMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
13+
var _fooWeakMap_1, _bar_1, _barWeakSet_1;
1414
var A = /** @class */ (function () {
1515
function A() {
16-
_bar_1.add(this);
17-
_foo_1.set(this, void 0);
18-
this.baz = _bar_2.call(this);
16+
_barWeakSet_1.add(this);
17+
_fooWeakMap_1.set(this, void 0);
18+
this.baz = _classPrivateNamedMethodGet(this, _barWeakSet_1, _bar_1).call(this);
1919
}
2020
return A;
2121
}());
22-
_foo_1 = new WeakMap(), _bar_1 = new WeakSet(), _bar_2 = function _bar_2() {
23-
_classPrivateNamedCallCheck(this, _bar_1);
24-
return _classPrivateFieldGet(this, _foo_1);
22+
_fooWeakMap_1 = new WeakMap(), _barWeakSet_1 = new WeakSet(), _bar_1 = function _bar_1() {
23+
return _classPrivateFieldGet(this, _fooWeakMap_1);
2524
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//// [privateNamedMethod.ts]
2+
class A {
3+
#foo = "hello";
4+
#log(name: string) {
5+
console.log(this.#foo);
6+
console.log(name);
7+
}
8+
#logIfTallEnough(height: number, name: string) {
9+
if (height >= 50) {
10+
this.#log(name);
11+
}
12+
}
13+
#logAll(height: number, ...names: string[]) {
14+
for (const name of names) {
15+
this.#logIfTallEnough(height, name);
16+
}
17+
}
18+
constructor() {
19+
this.#logIfTallEnough(100, "world");
20+
this.#logAll(100, ...["a", "b", "c"]);
21+
}
22+
}
23+
24+
25+
//// [privateNamedMethod.js]
26+
var _classPrivateFieldGet = function (receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); };
27+
function _classPrivateNamedMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
28+
var _fooWeakMap_1, _log_1, _logWeakSet_1, _logIfTallEnough_1, _logIfTallEnoughWeakSet_1, _logAll_1, _logAllWeakSet_1;
29+
"use strict";
30+
class A {
31+
constructor() {
32+
_logWeakSet_1.add(this);
33+
_logIfTallEnoughWeakSet_1.add(this);
34+
_logAllWeakSet_1.add(this);
35+
_fooWeakMap_1.set(this, "hello");
36+
_classPrivateNamedMethodGet(this, _logIfTallEnoughWeakSet_1, _logIfTallEnough_1).call(this, 100, "world");
37+
_classPrivateNamedMethodGet(this, _logAllWeakSet_1, _logAll_1).call(this, 100, ...["a", "b", "c"]);
38+
}
39+
}
40+
_fooWeakMap_1 = new WeakMap(), _logWeakSet_1 = new WeakSet(), _logIfTallEnoughWeakSet_1 = new WeakSet(), _logAllWeakSet_1 = new WeakSet(), _log_1 = function _log_1(name) {
41+
console.log(_classPrivateFieldGet(this, _fooWeakMap_1));
42+
console.log(name);
43+
}, _logIfTallEnough_1 = function _logIfTallEnough_1(height, name) {
44+
if (height >= 50) {
45+
_classPrivateNamedMethodGet(this, _logWeakSet_1, _log_1).call(this, name);
46+
}
47+
}, _logAll_1 = function _logAll_1(height, ...names) {
48+
for (const name of names) {
49+
_classPrivateNamedMethodGet(this, _logIfTallEnoughWeakSet_1, _logIfTallEnough_1).call(this, height, name);
50+
}
51+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
=== tests/cases/conformance/classes/members/privateNames/privateNamedMethod.ts ===
2+
class A {
3+
>A : Symbol(A, Decl(privateNamedMethod.ts, 0, 0))
4+
5+
#foo = "hello";
6+
>#foo : Symbol(A.#foo, Decl(privateNamedMethod.ts, 0, 9))
7+
8+
#log(name: string) {
9+
>#log : Symbol(A.#log, Decl(privateNamedMethod.ts, 1, 19))
10+
>name : Symbol(name, Decl(privateNamedMethod.ts, 2, 9))
11+
12+
console.log(this.#foo);
13+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
14+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
15+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
16+
>this.#foo : Symbol(A.#foo, Decl(privateNamedMethod.ts, 0, 9))
17+
>this : Symbol(A, Decl(privateNamedMethod.ts, 0, 0))
18+
19+
console.log(name);
20+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
21+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
22+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
23+
>name : Symbol(name, Decl(privateNamedMethod.ts, 2, 9))
24+
}
25+
#logIfTallEnough(height: number, name: string) {
26+
>#logIfTallEnough : Symbol(A.#logIfTallEnough, Decl(privateNamedMethod.ts, 5, 5))
27+
>height : Symbol(height, Decl(privateNamedMethod.ts, 6, 21))
28+
>name : Symbol(name, Decl(privateNamedMethod.ts, 6, 36))
29+
30+
if (height >= 50) {
31+
>height : Symbol(height, Decl(privateNamedMethod.ts, 6, 21))
32+
33+
this.#log(name);
34+
>this.#log : Symbol(A.#log, Decl(privateNamedMethod.ts, 1, 19))
35+
>this : Symbol(A, Decl(privateNamedMethod.ts, 0, 0))
36+
>name : Symbol(name, Decl(privateNamedMethod.ts, 6, 36))
37+
}
38+
}
39+
#logAll(height: number, ...names: string[]) {
40+
>#logAll : Symbol(A.#logAll, Decl(privateNamedMethod.ts, 10, 5))
41+
>height : Symbol(height, Decl(privateNamedMethod.ts, 11, 12))
42+
>names : Symbol(names, Decl(privateNamedMethod.ts, 11, 27))
43+
44+
for (const name of names) {
45+
>name : Symbol(name, Decl(privateNamedMethod.ts, 12, 18))
46+
>names : Symbol(names, Decl(privateNamedMethod.ts, 11, 27))
47+
48+
this.#logIfTallEnough(height, name);
49+
>this.#logIfTallEnough : Symbol(A.#logIfTallEnough, Decl(privateNamedMethod.ts, 5, 5))
50+
>this : Symbol(A, Decl(privateNamedMethod.ts, 0, 0))
51+
>height : Symbol(height, Decl(privateNamedMethod.ts, 11, 12))
52+
>name : Symbol(name, Decl(privateNamedMethod.ts, 12, 18))
53+
}
54+
}
55+
constructor() {
56+
this.#logIfTallEnough(100, "world");
57+
>this.#logIfTallEnough : Symbol(A.#logIfTallEnough, Decl(privateNamedMethod.ts, 5, 5))
58+
>this : Symbol(A, Decl(privateNamedMethod.ts, 0, 0))
59+
60+
this.#logAll(100, ...["a", "b", "c"]);
61+
>this.#logAll : Symbol(A.#logAll, Decl(privateNamedMethod.ts, 10, 5))
62+
>this : Symbol(A, Decl(privateNamedMethod.ts, 0, 0))
63+
}
64+
}
65+

0 commit comments

Comments
 (0)