Skip to content

Commit aa13a97

Browse files
committed
Merge pull request #1978 from Microsoft/esSymbols
Support ES6 built-in symbols
2 parents 9788acf + 47404bc commit aa13a97

File tree

487 files changed

+7458
-1278
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

487 files changed

+7458
-1278
lines changed

src/compiler/binder.ts

+7-13
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,6 @@ module ts {
5151
}
5252
}
5353

54-
/**
55-
* A declaration has a dynamic name if both of the following are true:
56-
* 1. The declaration has a computed property name
57-
* 2. The computed name is *not* expressed as Symbol.<name>, where name
58-
* is a property of the Symbol constructor that denotes a built in
59-
* Symbol.
60-
*/
61-
export function hasDynamicName(declaration: Declaration): boolean {
62-
return declaration.name && declaration.name.kind === SyntaxKind.ComputedPropertyName;
63-
}
64-
6554
export function bindSourceFile(file: SourceFile): void {
6655
var start = new Date().getTime();
6756
bindSourceFileWorker(file);
@@ -98,13 +87,18 @@ module ts {
9887
if (symbolKind & SymbolFlags.Value && !symbol.valueDeclaration) symbol.valueDeclaration = node;
9988
}
10089

101-
// Should not be called on a declaration with a computed property name.
90+
// Should not be called on a declaration with a computed property name,
91+
// unless it is a well known Symbol.
10292
function getDeclarationName(node: Declaration): string {
10393
if (node.name) {
10494
if (node.kind === SyntaxKind.ModuleDeclaration && node.name.kind === SyntaxKind.StringLiteral) {
10595
return '"' + (<LiteralExpression>node.name).text + '"';
10696
}
107-
Debug.assert(!hasDynamicName(node));
97+
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
98+
var nameExpression = (<ComputedPropertyName>node.name).expression;
99+
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
100+
return getPropertyNameForKnownSymbolName((<PropertyAccessExpression>nameExpression).name.text);
101+
}
108102
return (<Identifier | LiteralExpression>node.name).text;
109103
}
110104
switch (node.kind) {

src/compiler/checker.ts

+244-68
Large diffs are not rendered by default.

src/compiler/diagnosticInformationMap.generated.ts

+24-20
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

+36-20
Original file line numberDiff line numberDiff line change
@@ -483,27 +483,27 @@
483483
"category": "Error",
484484
"code": 1164
485485
},
486-
"Computed property names are not allowed in an ambient context.": {
486+
"A computed property name in an ambient context must directly refer to a built-in symbol.": {
487487
"category": "Error",
488488
"code": 1165
489489
},
490-
"Computed property names are not allowed in class property declarations.": {
490+
"A computed property name in a class property declaration must directly refer to a built-in symbol.": {
491491
"category": "Error",
492492
"code": 1166
493493
},
494494
"Computed property names are only available when targeting ECMAScript 6 and higher.": {
495495
"category": "Error",
496496
"code": 1167
497497
},
498-
"Computed property names are not allowed in method overloads.": {
498+
"A computed property name in a method overload must directly refer to a built-in symbol.": {
499499
"category": "Error",
500500
"code": 1168
501501
},
502-
"Computed property names are not allowed in interfaces.": {
502+
"A computed property name in an interface must directly refer to a built-in symbol.": {
503503
"category": "Error",
504504
"code": 1169
505505
},
506-
"Computed property names are not allowed in type literals.": {
506+
"A computed property name in a type literal must directly refer to a built-in symbol.": {
507507
"category": "Error",
508508
"code": 1170
509509
},
@@ -656,7 +656,7 @@
656656
"category": "Error",
657657
"code": 2318
658658
},
659-
"Named properties '{0}' of types '{1}' and '{2}' are not identical.": {
659+
"Named property '{0}' of types '{1}' and '{2}' are not identical.": {
660660
"category": "Error",
661661
"code": 2319
662662
},
@@ -744,7 +744,7 @@
744744
"category": "Error",
745745
"code": 2341
746746
},
747-
"An index expression argument must be of type 'string', 'number', or 'any'.": {
747+
"An index expression argument must be of type 'string', 'number', 'symbol, or 'any'.": {
748748
"category": "Error",
749749
"code": 2342
750750
},
@@ -808,7 +808,7 @@
808808
"category": "Error",
809809
"code": 2359
810810
},
811-
"The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.": {
811+
"The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.": {
812812
"category": "Error",
813813
"code": 2360
814814
},
@@ -1188,7 +1188,7 @@
11881188
"category": "Error",
11891189
"code": 2463
11901190
},
1191-
"A computed property name must be of type 'string', 'number', or 'any'.": {
1191+
"A computed property name must be of type 'string', 'number', 'symbol', or 'any'.": {
11921192
"category": "Error",
11931193
"code": 2464
11941194
},
@@ -1202,48 +1202,64 @@
12021202
},
12031203
"A computed property name cannot reference a type parameter from its containing type.": {
12041204
"category": "Error",
1205-
"code": 2466
1205+
"code": 2467
12061206
},
1207-
"Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.": {
1207+
"Cannot find global value '{0}'.": {
12081208
"category": "Error",
12091209
"code": 2468
12101210
},
1211-
"Enum declarations must all be const or non-const.": {
1211+
"The '{0}' operator cannot be applied to type 'symbol'.": {
12121212
"category": "Error",
12131213
"code": 2469
12141214
},
1215-
"In 'const' enum declarations member initializer must be constant expression.": {
1215+
"'Symbol' reference does not refer to the global Symbol constructor object.": {
12161216
"category": "Error",
12171217
"code": 2470
12181218
},
1219-
"'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment.": {
1219+
"A computed property name of the form '{0}' must be of type 'symbol'.": {
12201220
"category": "Error",
12211221
"code": 2471
12221222
},
1223-
"A const enum member can only be accessed using a string literal.": {
1223+
"Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.": {
12241224
"category": "Error",
12251225
"code": 2472
12261226
},
1227-
"'const' enum member initializer was evaluated to a non-finite value.": {
1227+
"Enum declarations must all be const or non-const.": {
12281228
"category": "Error",
12291229
"code": 2473
12301230
},
1231-
"'const' enum member initializer was evaluated to disallowed value 'NaN'.": {
1231+
"In 'const' enum declarations member initializer must be constant expression.": {
12321232
"category": "Error",
12331233
"code": 2474
12341234
},
1235-
"Property '{0}' does not exist on 'const' enum '{1}'.": {
1235+
"'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment.": {
12361236
"category": "Error",
12371237
"code": 2475
12381238
},
1239-
"'let' is not allowed to be used as a name in 'let' or 'const' declarations.": {
1239+
"A const enum member can only be accessed using a string literal.": {
12401240
"category": "Error",
12411241
"code": 2476
12421242
},
1243-
"Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'.": {
1243+
"'const' enum member initializer was evaluated to a non-finite value.": {
12441244
"category": "Error",
12451245
"code": 2477
12461246
},
1247+
"'const' enum member initializer was evaluated to disallowed value 'NaN'.": {
1248+
"category": "Error",
1249+
"code": 2478
1250+
},
1251+
"Property '{0}' does not exist on 'const' enum '{1}'.": {
1252+
"category": "Error",
1253+
"code": 2479
1254+
},
1255+
"'let' is not allowed to be used as a name in 'let' or 'const' declarations.": {
1256+
"category": "Error",
1257+
"code": 2480
1258+
},
1259+
"Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'.": {
1260+
"category": "Error",
1261+
"code": 2481
1262+
},
12471263

12481264
"Import declaration '{0}' is using private name '{1}'.": {
12491265
"category": "Error",

src/compiler/emitter.ts

+16-12
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ module ts {
275275
var firstAccessor: AccessorDeclaration;
276276
var getAccessor: AccessorDeclaration;
277277
var setAccessor: AccessorDeclaration;
278-
if (accessor.name.kind === SyntaxKind.ComputedPropertyName) {
278+
if (hasDynamicName(accessor)) {
279279
firstAccessor = accessor;
280280
if (accessor.kind === SyntaxKind.GetAccessor) {
281281
getAccessor = accessor;
@@ -289,19 +289,22 @@ module ts {
289289
}
290290
else {
291291
forEach(node.members,(member: Declaration) => {
292-
if ((member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) &&
293-
(<Identifier>member.name).text === (<Identifier>accessor.name).text &&
294-
(member.flags & NodeFlags.Static) === (accessor.flags & NodeFlags.Static)) {
295-
if (!firstAccessor) {
296-
firstAccessor = <AccessorDeclaration>member;
297-
}
292+
if ((member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor)
293+
&& (member.flags & NodeFlags.Static) === (accessor.flags & NodeFlags.Static)) {
294+
var memberName = getPropertyNameForPropertyNameNode(member.name);
295+
var accessorName = getPropertyNameForPropertyNameNode(accessor.name);
296+
if (memberName === accessorName) {
297+
if (!firstAccessor) {
298+
firstAccessor = <AccessorDeclaration>member;
299+
}
298300

299-
if (member.kind === SyntaxKind.GetAccessor && !getAccessor) {
300-
getAccessor = <AccessorDeclaration>member;
301-
}
301+
if (member.kind === SyntaxKind.GetAccessor && !getAccessor) {
302+
getAccessor = <AccessorDeclaration>member;
303+
}
302304

303-
if (member.kind === SyntaxKind.SetAccessor && !setAccessor) {
304-
setAccessor = <AccessorDeclaration>member;
305+
if (member.kind === SyntaxKind.SetAccessor && !setAccessor) {
306+
setAccessor = <AccessorDeclaration>member;
307+
}
305308
}
306309
}
307310
});
@@ -579,6 +582,7 @@ module ts {
579582
case SyntaxKind.StringKeyword:
580583
case SyntaxKind.NumberKeyword:
581584
case SyntaxKind.BooleanKeyword:
585+
case SyntaxKind.SymbolKeyword:
582586
case SyntaxKind.VoidKeyword:
583587
case SyntaxKind.StringLiteral:
584588
return writeTextOfNode(currentSourceFile, type);

src/compiler/parser.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2593,6 +2593,7 @@ module ts {
25932593
case SyntaxKind.StringKeyword:
25942594
case SyntaxKind.NumberKeyword:
25952595
case SyntaxKind.BooleanKeyword:
2596+
case SyntaxKind.SymbolKeyword:
25962597
// If these are followed by a dot, then parse these out as a dotted type reference instead.
25972598
var node = tryParse(parseKeywordAndNoDot);
25982599
return node || parseTypeReference();
@@ -2617,6 +2618,7 @@ module ts {
26172618
case SyntaxKind.StringKeyword:
26182619
case SyntaxKind.NumberKeyword:
26192620
case SyntaxKind.BooleanKeyword:
2621+
case SyntaxKind.SymbolKeyword:
26202622
case SyntaxKind.VoidKeyword:
26212623
case SyntaxKind.TypeOfKeyword:
26222624
case SyntaxKind.OpenBraceToken:

src/compiler/scanner.ts

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ module ts {
8282
"string": SyntaxKind.StringKeyword,
8383
"super": SyntaxKind.SuperKeyword,
8484
"switch": SyntaxKind.SwitchKeyword,
85+
"symbol": SyntaxKind.SymbolKeyword,
8586
"this": SyntaxKind.ThisKeyword,
8687
"throw": SyntaxKind.ThrowKeyword,
8788
"true": SyntaxKind.TrueKeyword,

src/compiler/types.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ module ts {
140140
NumberKeyword,
141141
SetKeyword,
142142
StringKeyword,
143+
SymbolKeyword,
143144
TypeKeyword,
144145

145146
// Parse tree nodes
@@ -1298,9 +1299,10 @@ module ts {
12981299
ObjectLiteral = 0x00020000, // Originates in an object literal
12991300
ContainsUndefinedOrNull = 0x00040000, // Type is or contains Undefined or Null type
13001301
ContainsObjectLiteral = 0x00080000, // Type is or contains object literal type
1302+
ESSymbol = 0x00100000, // Type of symbol primitive introduced in ES6
13011303

1302-
Intrinsic = Any | String | Number | Boolean | Void | Undefined | Null,
1303-
Primitive = String | Number | Boolean | Void | Undefined | Null | StringLiteral | Enum,
1304+
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
1305+
Primitive = String | Number | Boolean | ESSymbol | Void | Undefined | Null | StringLiteral | Enum,
13041306
StringLike = String | StringLiteral,
13051307
NumberLike = Number | Enum,
13061308
ObjectType = Class | Interface | Reference | Tuple | Anonymous,

src/compiler/utilities.ts

+48
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,54 @@ module ts {
835835
return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken;
836836
}
837837

838+
/**
839+
* A declaration has a dynamic name if both of the following are true:
840+
* 1. The declaration has a computed property name
841+
* 2. The computed name is *not* expressed as Symbol.<name>, where name
842+
* is a property of the Symbol constructor that denotes a built in
843+
* Symbol.
844+
*/
845+
export function hasDynamicName(declaration: Declaration): boolean {
846+
return declaration.name &&
847+
declaration.name.kind === SyntaxKind.ComputedPropertyName &&
848+
!isWellKnownSymbolSyntactically((<ComputedPropertyName>declaration.name).expression);
849+
}
850+
851+
/**
852+
* Checks if the expression is of the form:
853+
* Symbol.name
854+
* where Symbol is literally the word "Symbol", and name is any identifierName
855+
*/
856+
export function isWellKnownSymbolSyntactically(node: Expression): boolean {
857+
return node.kind === SyntaxKind.PropertyAccessExpression && isESSymbolIdentifier((<PropertyAccessExpression>node).expression);
858+
}
859+
860+
export function getPropertyNameForPropertyNameNode(name: DeclarationName): string {
861+
if (name.kind === SyntaxKind.Identifier || name.kind === SyntaxKind.StringLiteral || name.kind === SyntaxKind.NumericLiteral) {
862+
return (<Identifier | LiteralExpression>name).text;
863+
}
864+
if (name.kind === SyntaxKind.ComputedPropertyName) {
865+
var nameExpression = (<ComputedPropertyName>name).expression;
866+
if (isWellKnownSymbolSyntactically(nameExpression)) {
867+
var rightHandSideName = (<PropertyAccessExpression>nameExpression).name.text;
868+
return getPropertyNameForKnownSymbolName(rightHandSideName);
869+
}
870+
}
871+
872+
return undefined;
873+
}
874+
875+
export function getPropertyNameForKnownSymbolName(symbolName: string): string {
876+
return "__@" + symbolName;
877+
}
878+
879+
/**
880+
* Includes the word "Symbol" with unicode escapes
881+
*/
882+
export function isESSymbolIdentifier(node: Node): boolean {
883+
return node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "Symbol";
884+
}
885+
838886
export function isModifier(token: SyntaxKind): boolean {
839887
switch (token) {
840888
case SyntaxKind.PublicKeyword:

0 commit comments

Comments
 (0)