Skip to content

Commit f0f169e

Browse files
committed
emit as helper with runtime type check
Signed-off-by: Ashley Claymore <acutmore@users.noreply.github.com>
1 parent 6602fbf commit f0f169e

File tree

7 files changed

+119
-52
lines changed

7 files changed

+119
-52
lines changed

src/compiler/factory/emitHelpers.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ namespace ts {
3434
// Class Fields Helpers
3535
createClassPrivateFieldGetHelper(receiver: Expression, privateField: Identifier): Expression;
3636
createClassPrivateFieldSetHelper(receiver: Expression, privateField: Identifier, value: Expression): Expression;
37+
createClassPrivateFieldInHelper(receiver: Expression, privateField: Identifier): Expression;
3738
}
3839

3940
export function createEmitHelperFactory(context: TransformationContext): EmitHelperFactory {
@@ -72,6 +73,7 @@ namespace ts {
7273
// Class Fields Helpers
7374
createClassPrivateFieldGetHelper,
7475
createClassPrivateFieldSetHelper,
76+
createClassPrivateFieldInHelper
7577
};
7678

7779
/**
@@ -377,6 +379,12 @@ namespace ts {
377379
context.requestEmitHelper(classPrivateFieldSetHelper);
378380
return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldSet"), /*typeArguments*/ undefined, [receiver, privateField, value]);
379381
}
382+
383+
function createClassPrivateFieldInHelper(receiver: Expression, privateField: Identifier) {
384+
// TODO(aclaymore): will need to change emit for static private fields
385+
context.requestEmitHelper(classPrivateFieldInHelper);
386+
return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [receiver, privateField]);
387+
}
380388
}
381389

382390
/* @internal */
@@ -844,6 +852,19 @@ namespace ts {
844852
};`
845853
};
846854

855+
export const classPrivateFieldInHelper: UnscopedEmitHelper = {
856+
name: "typescript:classPrivateFieldIn",
857+
importName: "__classPrivateFieldIn",
858+
scoped: false,
859+
text: `
860+
var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, privateMap) {
861+
if (receiver === null || (typeof receiver !== 'object' && typeof receiver !== 'function')) {
862+
throw new TypeError("Cannot use 'in' operator on non-object");
863+
}
864+
return privateMap.has(receiver);
865+
};`
866+
};
867+
847868
let allUnscopedEmitHelpers: ReadonlyESMap<string, UnscopedEmitHelper> | undefined;
848869

849870
export function getAllUnscopedEmitHelpers() {
@@ -869,6 +890,7 @@ namespace ts {
869890
exportStarHelper,
870891
classPrivateFieldGetHelper,
871892
classPrivateFieldSetHelper,
893+
classPrivateFieldInHelper,
872894
createBindingHelper,
873895
setModuleDefaultHelper
874896
], helper => helper.name));
@@ -897,4 +919,4 @@ namespace ts {
897919
&& (getEmitFlags(firstSegment.expression) & EmitFlags.HelperName)
898920
&& firstSegment.expression.escapedText === helperName;
899921
}
900-
}
922+
}

src/compiler/transformers/classFields.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -146,18 +146,8 @@ namespace ts {
146146
if (info) {
147147
const receiver = visitNode(node.expression, visitor, isExpression);
148148

149-
// TODO(aclaymore): will need to change emit for static private fields
150-
151-
// TODO(aclaymore): Should this be abstracted into a factory function?
152149
return setOriginalNode(
153-
factory.createCallExpression(
154-
factory.createPropertyAccessExpression(
155-
info.weakMapName,
156-
"has"
157-
),
158-
/* typeArguments: */ undefined,
159-
[receiver]
160-
),
150+
context.getEmitHelperFactory().createClassPrivateFieldInHelper(receiver, info.weakMapName),
161151
node
162152
);
163153
}

tests/baselines/reference/privateNameInInExpressionTransform.errors.txt

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(15,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
22
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(15,21): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
33
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(17,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
4-
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(33,12): error TS18016: Private identifiers are not allowed outside class bodies.
4+
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,21): error TS1005: ';' expected.
5+
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(25,18): error TS1005: ';' expected.
6+
tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(37,12): error TS18016: Private identifiers are not allowed outside class bodies.
57

68

7-
==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (4 errors) ====
9+
==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (6 errors) ====
810
// TODO(aclaymore) add cases for static fields
911

1012
class Foo {
1113
#p1 = 1;
1214
check(v: any) {
13-
#p1 in v; // expect `_p1.has(v)`
15+
#p1 in v; // expect WeakMap '_p1'
1416
}
1517
precedence(v: any) {
1618
// '==' has lower precedence than 'in'
@@ -33,17 +35,25 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr
3335

3436
#p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v))
3537
}
38+
invalidLHS(v: any) {
39+
'prop' in v = 10;
40+
~
41+
!!! error TS1005: ';' expected.
42+
#p1 in v = 10;
43+
~
44+
!!! error TS1005: ';' expected.
45+
}
3646
}
3747

3848
class Bar {
3949
#p1 = 1;
4050
check(v: any) {
41-
#p1 in v; // expect `_p1_1.has(v)`
51+
#p1 in v; // expect WeakMap '_p1_1'
4252
}
4353
}
4454

45-
function error(v: Foo) {
46-
return #p1 in v; // expect `in v`
55+
function syntaxError(v: Foo) {
56+
return #p1 in v; // expect `return in v` so runtime will have a syntax error
4757
~~~
4858
!!! error TS18016: Private identifiers are not allowed outside class bodies.
4959
}

tests/baselines/reference/privateNameInInExpressionTransform.js

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
class Foo {
55
#p1 = 1;
66
check(v: any) {
7-
#p1 in v; // expect `_p1.has(v)`
7+
#p1 in v; // expect WeakMap '_p1'
88
}
99
precedence(v: any) {
1010
// '==' has lower precedence than 'in'
@@ -21,41 +21,57 @@ class Foo {
2121

2222
#p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v))
2323
}
24+
invalidLHS(v: any) {
25+
'prop' in v = 10;
26+
#p1 in v = 10;
27+
}
2428
}
2529

2630
class Bar {
2731
#p1 = 1;
2832
check(v: any) {
29-
#p1 in v; // expect `_p1_1.has(v)`
33+
#p1 in v; // expect WeakMap '_p1_1'
3034
}
3135
}
3236

33-
function error(v: Foo) {
34-
return #p1 in v; // expect `in v`
37+
function syntaxError(v: Foo) {
38+
return #p1 in v; // expect `return in v` so runtime will have a syntax error
3539
}
3640

3741
export { }
3842

3943

4044
//// [privateNameInInExpressionTransform.js]
4145
// TODO(aclaymore) add cases for static fields
46+
var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, privateMap) {
47+
if (receiver === null || (typeof receiver !== 'object' && typeof receiver !== 'function')) {
48+
throw new TypeError("Cannot use 'in' operator on non-object");
49+
}
50+
return privateMap.has(receiver);
51+
};
4252
var _p1, _p1_1;
4353
class Foo {
4454
constructor() {
4555
_p1.set(this, 1);
4656
}
4757
check(v) {
48-
_p1.has(v); // expect `_p1.has(v)`
58+
__classPrivateFieldIn(v, _p1); // expect WeakMap '_p1'
4959
}
5060
precedence(v) {
5161
// '==' has lower precedence than 'in'
5262
// '<' has same precedence than 'in'
5363
// '<<' has higher precedence than 'in'
54-
v == _p1.has(v) == v; // Good precedence: ((v == (#p1 in v)) == v)
55-
v << _p1.has(v << v); // Good precedence: (v << (#p1 in (v << v)))
56-
v << _p1.has(v) == v; // Good precedence: ((v << (#p1 in v)) == v)
57-
v == _p1.has(v) < v; // Good precedence: (v == ((#p1 in v) < v))
58-
_p1.has(v) && _p1.has(v); // Good precedence: ((#p1 in v) && (#p1 in v))
64+
v == __classPrivateFieldIn(v, _p1) == v; // Good precedence: ((v == (#p1 in v)) == v)
65+
v << __classPrivateFieldIn(v << v, _p1); // Good precedence: (v << (#p1 in (v << v)))
66+
v << __classPrivateFieldIn(v, _p1) == v; // Good precedence: ((v << (#p1 in v)) == v)
67+
v == __classPrivateFieldIn(v, _p1) < v; // Good precedence: (v == ((#p1 in v) < v))
68+
__classPrivateFieldIn(v, _p1) && __classPrivateFieldIn(v, _p1); // Good precedence: ((#p1 in v) && (#p1 in v))
69+
}
70+
invalidLHS(v) {
71+
'prop' in v;
72+
10;
73+
__classPrivateFieldIn(v, _p1);
74+
10;
5975
}
6076
}
6177
_p1 = new WeakMap();
@@ -64,11 +80,11 @@ class Bar {
6480
_p1_1.set(this, 1);
6581
}
6682
check(v) {
67-
_p1_1.has(v); // expect `_p1_1.has(v)`
83+
__classPrivateFieldIn(v, _p1_1); // expect WeakMap '_p1_1'
6884
}
6985
}
7086
_p1_1 = new WeakMap();
71-
function error(v) {
72-
return in v; // expect `in v`
87+
function syntaxError(v) {
88+
return in v; // expect `return in v` so runtime will have a syntax error
7389
}
7490
export {};

tests/baselines/reference/privateNameInInExpressionTransform.symbols

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class Foo {
1111
>check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 3, 12))
1212
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10))
1313

14-
#p1 in v; // expect `_p1.has(v)`
14+
#p1 in v; // expect WeakMap '_p1'
1515
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 4, 10))
1616
}
1717
precedence(v: any) {
@@ -46,30 +46,40 @@ class Foo {
4646
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15))
4747
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 7, 15))
4848
}
49+
invalidLHS(v: any) {
50+
>invalidLHS : Symbol(Foo.invalidLHS, Decl(privateNameInInExpressionTransform.ts, 21, 5))
51+
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 22, 15))
52+
53+
'prop' in v = 10;
54+
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 22, 15))
55+
56+
#p1 in v = 10;
57+
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 22, 15))
58+
}
4959
}
5060

5161
class Bar {
52-
>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 22, 1))
62+
>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 26, 1))
5363

5464
#p1 = 1;
55-
>#p1 : Symbol(Bar.#p1, Decl(privateNameInInExpressionTransform.ts, 24, 11))
65+
>#p1 : Symbol(Bar.#p1, Decl(privateNameInInExpressionTransform.ts, 28, 11))
5666

5767
check(v: any) {
58-
>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 25, 12))
59-
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 26, 10))
68+
>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 29, 12))
69+
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 30, 10))
6070

61-
#p1 in v; // expect `_p1_1.has(v)`
62-
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 26, 10))
71+
#p1 in v; // expect WeakMap '_p1_1'
72+
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 30, 10))
6373
}
6474
}
6575

66-
function error(v: Foo) {
67-
>error : Symbol(error, Decl(privateNameInInExpressionTransform.ts, 29, 1))
68-
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 31, 15))
76+
function syntaxError(v: Foo) {
77+
>syntaxError : Symbol(syntaxError, Decl(privateNameInInExpressionTransform.ts, 33, 1))
78+
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 21))
6979
>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0))
7080

71-
return #p1 in v; // expect `in v`
72-
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 31, 15))
81+
return #p1 in v; // expect `return in v` so runtime will have a syntax error
82+
>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 21))
7383
}
7484

7585
export { }

tests/baselines/reference/privateNameInInExpressionTransform.types

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Foo {
1212
>check : (v: any) => void
1313
>v : any
1414

15-
#p1 in v; // expect `_p1.has(v)`
15+
#p1 in v; // expect WeakMap '_p1'
1616
>#p1 in v : boolean
1717
>v : any
1818
}
@@ -63,6 +63,21 @@ class Foo {
6363
>#p1 in v : boolean
6464
>v : Foo
6565
}
66+
invalidLHS(v: any) {
67+
>invalidLHS : (v: any) => void
68+
>v : any
69+
70+
'prop' in v = 10;
71+
>'prop' in v : boolean
72+
>'prop' : "prop"
73+
>v : any
74+
>10 : 10
75+
76+
#p1 in v = 10;
77+
>#p1 in v : boolean
78+
>v : any
79+
>10 : 10
80+
}
6681
}
6782

6883
class Bar {
@@ -76,17 +91,17 @@ class Bar {
7691
>check : (v: any) => void
7792
>v : any
7893

79-
#p1 in v; // expect `_p1_1.has(v)`
94+
#p1 in v; // expect WeakMap '_p1_1'
8095
>#p1 in v : boolean
8196
>v : any
8297
}
8398
}
8499

85-
function error(v: Foo) {
86-
>error : (v: Foo) => any
100+
function syntaxError(v: Foo) {
101+
>syntaxError : (v: Foo) => any
87102
>v : Foo
88103

89-
return #p1 in v; // expect `in v`
104+
return #p1 in v; // expect `return in v` so runtime will have a syntax error
90105
>#p1 in v : any
91106
>v : Foo
92107
}

tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
class Foo {
66
#p1 = 1;
77
check(v: any) {
8-
#p1 in v; // expect `_p1.has(v)`
8+
#p1 in v; // expect WeakMap '_p1'
99
}
1010
precedence(v: any) {
1111
// '==' has lower precedence than 'in'
@@ -22,17 +22,21 @@ class Foo {
2222

2323
#p1 in v && #p1 in v; // Good precedence: ((#p1 in v) && (#p1 in v))
2424
}
25+
invalidLHS(v: any) {
26+
'prop' in v = 10;
27+
#p1 in v = 10;
28+
}
2529
}
2630

2731
class Bar {
2832
#p1 = 1;
2933
check(v: any) {
30-
#p1 in v; // expect `_p1_1.has(v)`
34+
#p1 in v; // expect WeakMap '_p1_1'
3135
}
3236
}
3337

34-
function error(v: Foo) {
35-
return #p1 in v; // expect `in v`
38+
function syntaxError(v: Foo) {
39+
return #p1 in v; // expect `return in v` so runtime will have a syntax error
3640
}
3741

3842
export { }

0 commit comments

Comments
 (0)