Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ES6 built-in symbols #1978

Merged
merged 44 commits into from
Feb 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
11d75ef
Allow Symbol indexer in ES6
JsonFreeman Jan 26, 2015
b30d8f3
Change computed property error messages to be about symbols
JsonFreeman Jan 28, 2015
39952b1
Syntactically allow computed properties everywhere if the name looks …
JsonFreeman Jan 28, 2015
d788624
Move hasDynamicName to utilities.ts
JsonFreeman Jan 28, 2015
07f3641
Update hasDynamicName to take well known symbols into account
JsonFreeman Jan 28, 2015
f344654
Add named property symbol for known Symbol properties
JsonFreeman Jan 28, 2015
30892af
Change computed property error message to mention Symbols
JsonFreeman Jan 29, 2015
9cb38fb
Create global Symbol type
JsonFreeman Jan 30, 2015
25fcbe2
Change certain hasDynamicName checks to check the SyntaxKind instead
JsonFreeman Jan 30, 2015
b60fa14
Add tests for operators with symbol operand
JsonFreeman Jan 30, 2015
779661c
Add tests for symbol properties
JsonFreeman Feb 2, 2015
95af997
Accept correct baselines for symbol property tests
JsonFreeman Feb 3, 2015
e508bf7
Add symbol keyword
JsonFreeman Feb 3, 2015
ebdd96b
Update tests to use new symbol keyword
JsonFreeman Feb 3, 2015
e346b70
Change isTypeOfKind calls to pass symbol TypeFlag when needed
JsonFreeman Feb 4, 2015
59a704e
Rename references in es6.d.ts from Symbol to symbol
JsonFreeman Feb 4, 2015
d793658
Change Symbol to symbol in error messages
JsonFreeman Feb 4, 2015
2d16474
Fix expression checking for symbols
JsonFreeman Feb 5, 2015
6a6c03b
Fix error message wording
JsonFreeman Feb 5, 2015
92617f5
Don't pass prop.name directly for error reporting
JsonFreeman Feb 5, 2015
fbeadbc
Add test for new Symbol()
JsonFreeman Feb 5, 2015
9f39a53
Make Symbol the apparent type of symbol
JsonFreeman Feb 5, 2015
df826de
symbols in type guards
JsonFreeman Feb 5, 2015
d07ed67
Support indexing with known symbols
JsonFreeman Feb 6, 2015
8325862
Fix error message
JsonFreeman Feb 6, 2015
3834edd
Refactor part of getPropertyNameForIndexedAccess into checkSymbolName…
JsonFreeman Feb 6, 2015
4c09ccd
Check that Symbol properties are proper, and support downlevel type c…
JsonFreeman Feb 7, 2015
3560442
Declaration emit for symbol properties
JsonFreeman Feb 7, 2015
2f3c32a
Navigation bar support for symbols
JsonFreeman Feb 7, 2015
eb50619
Disable symbol indexer
JsonFreeman Feb 7, 2015
52cb13e
Uncomment symbol properties in es6.d.ts
JsonFreeman Feb 7, 2015
75382c1
Accept baselines after rebase
JsonFreeman Feb 7, 2015
18276e5
Address feedback from @yuit
JsonFreeman Feb 11, 2015
a94e61b
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Feb 11, 2015
486cebd
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Feb 12, 2015
4942c5f
Address feedback
JsonFreeman Feb 13, 2015
9c273d8
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Feb 13, 2015
65d831e
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Feb 16, 2015
ac829a8
Error for naming an interface 'symbol'
JsonFreeman Feb 16, 2015
7d7d54f
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Feb 16, 2015
935c602
Rebaseline after merge
JsonFreeman Feb 16, 2015
59dc7d3
Address feedback
JsonFreeman Feb 17, 2015
dd6a129
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Feb 17, 2015
47404bc
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Feb 18, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 7 additions & 13 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,6 @@ module ts {
}
}

/**
* A declaration has a dynamic name if both of the following are true:
* 1. The declaration has a computed property name
* 2. The computed name is *not* expressed as Symbol.<name>, where name
* is a property of the Symbol constructor that denotes a built in
* Symbol.
*/
export function hasDynamicName(declaration: Declaration): boolean {
return declaration.name && declaration.name.kind === SyntaxKind.ComputedPropertyName;
}

export function bindSourceFile(file: SourceFile): void {
var start = new Date().getTime();
bindSourceFileWorker(file);
Expand Down Expand Up @@ -98,13 +87,18 @@ module ts {
if (symbolKind & SymbolFlags.Value && !symbol.valueDeclaration) symbol.valueDeclaration = node;
}

// Should not be called on a declaration with a computed property name.
// Should not be called on a declaration with a computed property name,
// unless it is a well known Symbol.
function getDeclarationName(node: Declaration): string {
if (node.name) {
if (node.kind === SyntaxKind.ModuleDeclaration && node.name.kind === SyntaxKind.StringLiteral) {
return '"' + (<LiteralExpression>node.name).text + '"';
}
Debug.assert(!hasDynamicName(node));
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
var nameExpression = (<ComputedPropertyName>node.name).expression;
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
return getPropertyNameForKnownSymbolName((<PropertyAccessExpression>nameExpression).name.text);
}
return (<Identifier | LiteralExpression>node.name).text;
}
switch (node.kind) {
Expand Down
312 changes: 244 additions & 68 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

44 changes: 24 additions & 20 deletions src/compiler/diagnosticInformationMap.generated.ts

Large diffs are not rendered by default.

56 changes: 36 additions & 20 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -483,27 +483,27 @@
"category": "Error",
"code": 1164
},
"Computed property names are not allowed in an ambient context.": {
"A computed property name in an ambient context must directly refer to a built-in symbol.": {
"category": "Error",
"code": 1165
},
"Computed property names are not allowed in class property declarations.": {
"A computed property name in a class property declaration must directly refer to a built-in symbol.": {
"category": "Error",
"code": 1166
},
"Computed property names are only available when targeting ECMAScript 6 and higher.": {
"category": "Error",
"code": 1167
},
"Computed property names are not allowed in method overloads.": {
"A computed property name in a method overload must directly refer to a built-in symbol.": {
"category": "Error",
"code": 1168
},
"Computed property names are not allowed in interfaces.": {
"A computed property name in an interface must directly refer to a built-in symbol.": {
"category": "Error",
"code": 1169
},
"Computed property names are not allowed in type literals.": {
"A computed property name in a type literal must directly refer to a built-in symbol.": {
"category": "Error",
"code": 1170
},
Expand Down Expand Up @@ -656,7 +656,7 @@
"category": "Error",
"code": 2318
},
"Named properties '{0}' of types '{1}' and '{2}' are not identical.": {
"Named property '{0}' of types '{1}' and '{2}' are not identical.": {
"category": "Error",
"code": 2319
},
Expand Down Expand Up @@ -744,7 +744,7 @@
"category": "Error",
"code": 2341
},
"An index expression argument must be of type 'string', 'number', or 'any'.": {
"An index expression argument must be of type 'string', 'number', 'symbol, or 'any'.": {
"category": "Error",
"code": 2342
},
Expand Down Expand Up @@ -808,7 +808,7 @@
"category": "Error",
"code": 2359
},
"The left-hand side of an 'in' expression must be of types 'any', 'string' or 'number'.": {
"The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.": {
"category": "Error",
"code": 2360
},
Expand Down Expand Up @@ -1188,7 +1188,7 @@
"category": "Error",
"code": 2463
},
"A computed property name must be of type 'string', 'number', or 'any'.": {
"A computed property name must be of type 'string', 'number', 'symbol', or 'any'.": {
"category": "Error",
"code": 2464
},
Expand All @@ -1202,48 +1202,64 @@
},
"A computed property name cannot reference a type parameter from its containing type.": {
"category": "Error",
"code": 2466
"code": 2467
},
"Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.": {
"Cannot find global value '{0}'.": {
"category": "Error",
"code": 2468
},
"Enum declarations must all be const or non-const.": {
"The '{0}' operator cannot be applied to type 'symbol'.": {
"category": "Error",
"code": 2469
},
"In 'const' enum declarations member initializer must be constant expression.": {
"'Symbol' reference does not refer to the global Symbol constructor object.": {
"category": "Error",
"code": 2470
},
"'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment.": {
"A computed property name of the form '{0}' must be of type 'symbol'.": {
"category": "Error",
"code": 2471
},
"A const enum member can only be accessed using a string literal.": {
"Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.": {
"category": "Error",
"code": 2472
},
"'const' enum member initializer was evaluated to a non-finite value.": {
"Enum declarations must all be const or non-const.": {
"category": "Error",
"code": 2473
},
"'const' enum member initializer was evaluated to disallowed value 'NaN'.": {
"In 'const' enum declarations member initializer must be constant expression.": {
"category": "Error",
"code": 2474
},
"Property '{0}' does not exist on 'const' enum '{1}'.": {
"'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment.": {
"category": "Error",
"code": 2475
},
"'let' is not allowed to be used as a name in 'let' or 'const' declarations.": {
"A const enum member can only be accessed using a string literal.": {
"category": "Error",
"code": 2476
},
"Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'.": {
"'const' enum member initializer was evaluated to a non-finite value.": {
"category": "Error",
"code": 2477
},
"'const' enum member initializer was evaluated to disallowed value 'NaN'.": {
"category": "Error",
"code": 2478
},
"Property '{0}' does not exist on 'const' enum '{1}'.": {
"category": "Error",
"code": 2479
},
"'let' is not allowed to be used as a name in 'let' or 'const' declarations.": {
"category": "Error",
"code": 2480
},
"Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'.": {
"category": "Error",
"code": 2481
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
28 changes: 16 additions & 12 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ module ts {
var firstAccessor: AccessorDeclaration;
var getAccessor: AccessorDeclaration;
var setAccessor: AccessorDeclaration;
if (accessor.name.kind === SyntaxKind.ComputedPropertyName) {
if (hasDynamicName(accessor)) {
firstAccessor = accessor;
if (accessor.kind === SyntaxKind.GetAccessor) {
getAccessor = accessor;
Expand All @@ -289,19 +289,22 @@ module ts {
}
else {
forEach(node.members,(member: Declaration) => {
if ((member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) &&
(<Identifier>member.name).text === (<Identifier>accessor.name).text &&
(member.flags & NodeFlags.Static) === (accessor.flags & NodeFlags.Static)) {
if (!firstAccessor) {
firstAccessor = <AccessorDeclaration>member;
}
if ((member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor)
&& (member.flags & NodeFlags.Static) === (accessor.flags & NodeFlags.Static)) {
var memberName = getPropertyNameForPropertyNameNode(member.name);
var accessorName = getPropertyNameForPropertyNameNode(accessor.name);
if (memberName === accessorName) {
if (!firstAccessor) {
firstAccessor = <AccessorDeclaration>member;
}

if (member.kind === SyntaxKind.GetAccessor && !getAccessor) {
getAccessor = <AccessorDeclaration>member;
}
if (member.kind === SyntaxKind.GetAccessor && !getAccessor) {
getAccessor = <AccessorDeclaration>member;
}

if (member.kind === SyntaxKind.SetAccessor && !setAccessor) {
setAccessor = <AccessorDeclaration>member;
if (member.kind === SyntaxKind.SetAccessor && !setAccessor) {
setAccessor = <AccessorDeclaration>member;
}
}
}
});
Expand Down Expand Up @@ -579,6 +582,7 @@ module ts {
case SyntaxKind.StringKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.StringLiteral:
return writeTextOfNode(currentSourceFile, type);
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2593,6 +2593,7 @@ module ts {
case SyntaxKind.StringKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
// If these are followed by a dot, then parse these out as a dotted type reference instead.
var node = tryParse(parseKeywordAndNoDot);
return node || parseTypeReference();
Expand All @@ -2617,6 +2618,7 @@ module ts {
case SyntaxKind.StringKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.OpenBraceToken:
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ module ts {
"string": SyntaxKind.StringKeyword,
"super": SyntaxKind.SuperKeyword,
"switch": SyntaxKind.SwitchKeyword,
"symbol": SyntaxKind.SymbolKeyword,
"this": SyntaxKind.ThisKeyword,
"throw": SyntaxKind.ThrowKeyword,
"true": SyntaxKind.TrueKeyword,
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ module ts {
NumberKeyword,
SetKeyword,
StringKeyword,
SymbolKeyword,
TypeKeyword,

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

Intrinsic = Any | String | Number | Boolean | Void | Undefined | Null,
Primitive = String | Number | Boolean | Void | Undefined | Null | StringLiteral | Enum,
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
Primitive = String | Number | Boolean | ESSymbol | Void | Undefined | Null | StringLiteral | Enum,
StringLike = String | StringLiteral,
NumberLike = Number | Enum,
ObjectType = Class | Interface | Reference | Tuple | Anonymous,
Expand Down
48 changes: 48 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,54 @@ module ts {
return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken;
}

/**
* A declaration has a dynamic name if both of the following are true:
* 1. The declaration has a computed property name
* 2. The computed name is *not* expressed as Symbol.<name>, where name
* is a property of the Symbol constructor that denotes a built in
* Symbol.
*/
export function hasDynamicName(declaration: Declaration): boolean {
return declaration.name &&
declaration.name.kind === SyntaxKind.ComputedPropertyName &&
!isWellKnownSymbolSyntactically((<ComputedPropertyName>declaration.name).expression);
}

/**
* Checks if the expression is of the form:
* Symbol.name
* where Symbol is literally the word "Symbol", and name is any identifierName
*/
export function isWellKnownSymbolSyntactically(node: Expression): boolean {
return node.kind === SyntaxKind.PropertyAccessExpression && isESSymbolIdentifier((<PropertyAccessExpression>node).expression);
}

export function getPropertyNameForPropertyNameNode(name: DeclarationName): string {
if (name.kind === SyntaxKind.Identifier || name.kind === SyntaxKind.StringLiteral || name.kind === SyntaxKind.NumericLiteral) {
return (<Identifier | LiteralExpression>name).text;
}
if (name.kind === SyntaxKind.ComputedPropertyName) {
var nameExpression = (<ComputedPropertyName>name).expression;
if (isWellKnownSymbolSyntactically(nameExpression)) {
var rightHandSideName = (<PropertyAccessExpression>nameExpression).name.text;
return getPropertyNameForKnownSymbolName(rightHandSideName);
}
}

return undefined;
}

export function getPropertyNameForKnownSymbolName(symbolName: string): string {
return "__@" + symbolName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly happens when I make an object like the following?

var x = {
    "__@iterator": 10;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a test for that (tests/cases/conformance/es6/Symbols/symbolProperty17.ts). Internally it becomes "___@iterator" (with 3 underscores). This is done by escapeIdentifier

}

/**
* Includes the word "Symbol" with unicode escapes
*/
export function isESSymbolIdentifier(node: Node): boolean {
return node.kind === SyntaxKind.Identifier && (<Identifier>node).text === "Symbol";
}

export function isModifier(token: SyntaxKind): boolean {
switch (token) {
case SyntaxKind.PublicKeyword:
Expand Down
Loading