Reduce arrow function allocations in the parser.

This commit is contained in:
Cyrus Najmabadi
2014-12-02 00:02:15 -08:00
parent 581ef9f2b0
commit deca94427c

View File

@@ -1347,11 +1347,11 @@ module ts {
}
function parseIdentifierName(): Identifier {
return createIdentifier(token >= SyntaxKind.Identifier);
return createIdentifier(isIdentifierOrKeyword());
}
function isLiteralPropertyName(): boolean {
return token >= SyntaxKind.Identifier ||
return isIdentifierOrKeyword() ||
token === SyntaxKind.StringLiteral ||
token === SyntaxKind.NumericLiteral;
}
@@ -1439,7 +1439,9 @@ module ts {
case ParsingContext.ObjectLiteralMembers:
return token === SyntaxKind.OpenBracketToken || token === SyntaxKind.AsteriskToken || isLiteralPropertyName();
case ParsingContext.TypeReferences:
return isIdentifier() && ((token !== SyntaxKind.ExtendsKeyword && token !== SyntaxKind.ImplementsKeyword) || !lookAhead(() => (nextToken(), isIdentifier())));
// We want to make sure that the "extends" in "extends foo" or the "implements" in
// "implements foo" is not considered a type name.
return isIdentifier() && !isNotHeritageClauseTypeName();
case ParsingContext.VariableDeclarations:
case ParsingContext.TypeParameters:
return isIdentifier();
@@ -1459,6 +1461,21 @@ module ts {
Debug.fail("Non-exhaustive case in 'isListElement'.");
}
function nextTokenIsIdentifier() {
nextToken();
return isIdentifier();
}
function isNotHeritageClauseTypeName(): boolean {
if (token === SyntaxKind.ImplementsKeyword ||
token === SyntaxKind.ExtendsKeyword) {
return lookAhead(nextTokenIsIdentifier);
}
return false;
}
// True if positioned at a list terminator
function isListTerminator(kind: ParsingContext): boolean {
if (token === SyntaxKind.EndOfFileToken) {
@@ -1689,10 +1706,7 @@ module ts {
// In the first case though, ASI will not take effect because there is not a
// line terminator after the keyword.
if (scanner.hasPrecedingLineBreak() && scanner.isReservedWord()) {
var matchesPattern = lookAhead(() => {
nextToken();
return !scanner.hasPrecedingLineBreak() && (scanner.isIdentifier() || scanner.isReservedWord());
});
var matchesPattern = lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine);
if (matchesPattern) {
// Report that we need an identifier. However, report it right after the dot,
@@ -1705,6 +1719,8 @@ module ts {
return allowIdentifierNames ? parseIdentifierName() : parseIdentifier();
}
function parseTokenNode<T extends Node>(): T {
var node = <T>createNode(token);
nextToken();
@@ -1972,58 +1988,62 @@ module ts {
return false;
}
return lookAhead(() => {
// The only allowed sequence is:
//
// [id:
//
// However, for error recovery, we also check the following cases:
//
// [...
// [id,
// [id?,
// [id?:
// [id?]
// [public id
// [private id
// [protected id
// []
//
if (nextToken() === SyntaxKind.DotDotDotToken || token === SyntaxKind.CloseBracketToken) {
return lookAhead(isUnambiguouslyIndexSignature);
}
function isUnambiguouslyIndexSignature() {
// The only allowed sequence is:
//
// [id:
//
// However, for error recovery, we also check the following cases:
//
// [...
// [id,
// [id?,
// [id?:
// [id?]
// [public id
// [private id
// [protected id
// []
//
nextToken();
if (token === SyntaxKind.DotDotDotToken || token === SyntaxKind.CloseBracketToken) {
return true;
}
if (isModifier(token)) {
nextToken();
if (isIdentifier()) {
return true;
}
}
else if (!isIdentifier()) {
return false;
}
else {
// Skip the identifier
nextToken();
}
if (isModifier(token)) {
nextToken();
if (isIdentifier()) {
return true;
}
}
else if (!isIdentifier()) {
return false;
}
else {
// Skip the identifier
nextToken();
}
// A colon signifies a well formed indexer
// A comma should be a badly formed indexer because comma expressions are not allowed
// in computed properties.
if (token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken) {
return true;
}
// A colon signifies a well formed indexer
// A comma should be a badly formed indexer because comma expressions are not allowed
// in computed properties.
if (token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken) {
return true;
}
// Question mark could be an indexer with an optional property,
// or it could be a conditional expression in a computed property.
if (token !== SyntaxKind.QuestionToken) {
return false;
}
// Question mark could be an indexer with an optional property,
// or it could be a conditional expression in a computed property.
if (token !== SyntaxKind.QuestionToken) {
return false;
}
// If any of the following tokens are after the question mark, it cannot
// be a conditional expression, so treat it as an indexer.
return nextToken() === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken || token === SyntaxKind.CloseBracketToken;
});
// If any of the following tokens are after the question mark, it cannot
// be a conditional expression, so treat it as an indexer.
nextToken();
return token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken || token === SyntaxKind.CloseBracketToken;
}
function parseIndexSignatureDeclaration(fullStart: number, modifiers: ModifiersArray): IndexSignatureDeclaration {
@@ -2069,15 +2089,19 @@ module ts {
case SyntaxKind.OpenBracketToken: // Both for indexers and computed properties
return true;
default:
return isLiteralPropertyName() && lookAhead(() =>
nextToken() === SyntaxKind.OpenParenToken ||
token === SyntaxKind.LessThanToken ||
token === SyntaxKind.QuestionToken ||
token === SyntaxKind.ColonToken ||
canParseSemicolon());
return isLiteralPropertyName() && lookAhead(isTypeMemberWithLiteralPropertyName);
}
}
function isTypeMemberWithLiteralPropertyName() {
nextToken();
return token === SyntaxKind.OpenParenToken ||
token === SyntaxKind.LessThanToken ||
token === SyntaxKind.QuestionToken ||
token === SyntaxKind.ColonToken ||
canParseSemicolon();
}
function parseTypeMember(): Declaration {
switch (token) {
case SyntaxKind.OpenParenToken:
@@ -2087,19 +2111,25 @@ module ts {
// Indexer or computed property
return isIndexSignature() ? parseIndexSignatureDeclaration(scanner.getStartPos(), /*modifiers:*/ undefined) : parsePropertyOrMethod();
case SyntaxKind.NewKeyword:
if (lookAhead(() => nextToken() === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken)) {
if (lookAhead(isStartOfConstructSignature)) {
return parseSignatureMember(SyntaxKind.ConstructSignature);
}
// fall through.
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
return parsePropertyOrMethod();
default:
if (token >= SyntaxKind.Identifier) {
if (isIdentifierOrKeyword()) {
return parsePropertyOrMethod();
}
}
}
function isStartOfConstructSignature() {
nextToken();
return token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken;
}
function parseTypeLiteral(): TypeLiteralNode {
var node = <TypeLiteralNode>createNode(SyntaxKind.TypeLiteral);
node.members = parseObjectTypeMembers();
@@ -2187,15 +2217,17 @@ module ts {
case SyntaxKind.OpenParenToken:
// Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier,
// or something that starts a type. We don't want to consider things like '(1)' a type.
return lookAhead(() => {
nextToken();
return token === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType();
});
return lookAhead(isStartOfParenthesizedOrFunctionType);
default:
return isIdentifier();
}
}
function isStartOfParenthesizedOrFunctionType() {
nextToken();
return token === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType();
}
function parseArrayTypeOrHigher(): TypeNode {
var type = parseNonArrayType();
while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
@@ -2224,35 +2256,41 @@ module ts {
}
function isStartOfFunctionType(): boolean {
return token === SyntaxKind.LessThanToken || token === SyntaxKind.OpenParenToken && lookAhead(() => {
if (token === SyntaxKind.LessThanToken) {
return true;
}
return token === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType);
}
function isUnambiguouslyStartOfFunctionType() {
nextToken();
if (token === SyntaxKind.CloseParenToken || token === SyntaxKind.DotDotDotToken) {
// ( )
// ( ...
return true;
}
if (isIdentifier() || isModifier(token)) {
nextToken();
if (token === SyntaxKind.CloseParenToken || token === SyntaxKind.DotDotDotToken) {
// ( )
// ( ...
if (token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken ||
token === SyntaxKind.QuestionToken || token === SyntaxKind.EqualsToken ||
isIdentifier() || isModifier(token)) {
// ( id :
// ( id ,
// ( id ?
// ( id =
// ( modifier id
return true;
}
if (isIdentifier() || isModifier(token)) {
if (token === SyntaxKind.CloseParenToken) {
nextToken();
if (token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken ||
token === SyntaxKind.QuestionToken || token === SyntaxKind.EqualsToken ||
isIdentifier() || isModifier(token)) {
// ( id :
// ( id ,
// ( id ?
// ( id =
// ( modifier id
if (token === SyntaxKind.EqualsGreaterThanToken) {
// ( id ) =>
return true;
}
if (token === SyntaxKind.CloseParenToken) {
nextToken();
if (token === SyntaxKind.EqualsGreaterThanToken) {
// ( id ) =>
return true;
}
}
}
return false;
});
}
return false;
}
function parseType(): TypeNode {
@@ -2455,15 +2493,17 @@ module ts {
// for now we just check if the next token is an identifier. More heuristics
// can be added here later as necessary. We just need to make sure that we
// don't accidently consume something legal.
return lookAhead(() => {
nextToken();
return !scanner.hasPrecedingLineBreak() && isIdentifier();
});
return lookAhead(nextTokenIsIdentifierOnSameLine);
}
return false;
}
function nextTokenIsIdentifierOnSameLine() {
nextToken();
return !scanner.hasPrecedingLineBreak() && isIdentifier()
}
function parseYieldExpression(): YieldExpression {
var node = <YieldExpression>createNode(SyntaxKind.YieldExpression);
@@ -2546,66 +2586,9 @@ module ts {
// Speculatively look ahead to be sure, and rollback if not.
function isParenthesizedArrowFunctionExpression(): Tristate {
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
return lookAhead(() => {
var first = token;
var second = nextToken();
if (first === SyntaxKind.OpenParenToken) {
if (second === SyntaxKind.CloseParenToken) {
// Simple cases: "() =>", "(): ", and "() {".
// This is an arrow function with no parameters.
// The last one is not actually an arrow function,
// but this is probably what the user intended.
var third = nextToken();
switch (third) {
case SyntaxKind.EqualsGreaterThanToken:
case SyntaxKind.ColonToken:
case SyntaxKind.OpenBraceToken:
return Tristate.True;
default:
return Tristate.False;
}
}
// Simple case: "(..."
// This is an arrow function with a rest parameter.
if (second === SyntaxKind.DotDotDotToken) {
return Tristate.True;
}
// If we had "(" followed by something that's not an identifier,
// then this definitely doesn't look like a lambda.
// Note: we could be a little more lenient and allow
// "(public" or "(private". These would not ever actually be allowed,
// but we could provide a good error message instead of bailing out.
if (!isIdentifier()) {
return Tristate.False;
}
// If we have something like "(a:", then we must have a
// type-annotated parameter in an arrow function expression.
if (nextToken() === SyntaxKind.ColonToken) {
return Tristate.True;
}
// This *could* be a parenthesized arrow function.
// Return Unknown to let the caller know.
return Tristate.Unknown;
}
else {
Debug.assert(first === SyntaxKind.LessThanToken);
// If we have "<" not followed by an identifier,
// then this definitely is not an arrow function.
if (!isIdentifier()) {
return Tristate.False;
}
// This *could* be a parenthesized arrow function.
return Tristate.Unknown;
}
});
return lookAhead(isParenthesizedArrowFunctionExpressionWorker);
}
if (token === SyntaxKind.EqualsGreaterThanToken) {
// ERROR RECOVERY TWEAK:
// If we see a standalone => try to parse it as an arrow function expression as that's
@@ -2616,6 +2599,66 @@ module ts {
return Tristate.False;
}
function isParenthesizedArrowFunctionExpressionWorker() {
var first = token;
var second = nextToken();
if (first === SyntaxKind.OpenParenToken) {
if (second === SyntaxKind.CloseParenToken) {
// Simple cases: "() =>", "(): ", and "() {".
// This is an arrow function with no parameters.
// The last one is not actually an arrow function,
// but this is probably what the user intended.
var third = nextToken();
switch (third) {
case SyntaxKind.EqualsGreaterThanToken:
case SyntaxKind.ColonToken:
case SyntaxKind.OpenBraceToken:
return Tristate.True;
default:
return Tristate.False;
}
}
// Simple case: "(..."
// This is an arrow function with a rest parameter.
if (second === SyntaxKind.DotDotDotToken) {
return Tristate.True;
}
// If we had "(" followed by something that's not an identifier,
// then this definitely doesn't look like a lambda.
// Note: we could be a little more lenient and allow
// "(public" or "(private". These would not ever actually be allowed,
// but we could provide a good error message instead of bailing out.
if (!isIdentifier()) {
return Tristate.False;
}
// If we have something like "(a:", then we must have a
// type-annotated parameter in an arrow function expression.
if (nextToken() === SyntaxKind.ColonToken) {
return Tristate.True;
}
// This *could* be a parenthesized arrow function.
// Return Unknown to let the caller know.
return Tristate.Unknown;
}
else {
Debug.assert(first === SyntaxKind.LessThanToken);
// If we have "<" not followed by an identifier,
// then this definitely is not an arrow function.
if (!isIdentifier()) {
return Tristate.False;
}
// This *could* be a parenthesized arrow function.
return Tristate.Unknown;
}
}
function tryParseSignatureIfArrowOrBraceFollows(): ParsedSignature {
return tryParse(() => {
// Arrow functions are never generators.
@@ -3479,7 +3522,11 @@ module ts {
}
function isLabel(): boolean {
return isIdentifier() && lookAhead(() => nextToken() === SyntaxKind.ColonToken);
return isIdentifier() && lookAhead(nextTokenIsColonToken);
}
function nextTokenIsColonToken() {
return nextToken() === SyntaxKind.ColonToken;
}
function parseLabeledStatement(): LabeledStatement {
@@ -3532,7 +3579,7 @@ module ts {
// const keyword can precede enum keyword when defining constant enums
// 'const enum' do not start statement.
// In ES 6 'enum' is a future reserved keyword, so it should not be used as identifier
var isConstEnum = lookAhead(() => nextToken() === SyntaxKind.EnumKeyword);
var isConstEnum = lookAhead(nextTokenIsEnumKeyword);
return !isConstEnum;
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.ClassKeyword:
@@ -3551,7 +3598,7 @@ module ts {
case SyntaxKind.StaticKeyword:
// When followed by an identifier or keyword, these do not start a statement but
// might instead be following type members
if (lookAhead(() => nextToken() >= SyntaxKind.Identifier && !scanner.hasPrecedingLineBreak())) {
if (lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine)) {
return false;
}
default:
@@ -3559,6 +3606,16 @@ module ts {
}
}
function nextTokenIsEnumKeyword() {
nextToken();
return token === SyntaxKind.EnumKeyword
}
function nextTokenIsIdentifierOrKeywordOnSameLine() {
nextToken();
return isIdentifierOrKeyword() && !scanner.hasPrecedingLineBreak();
}
function parseStatement(): Statement {
switch (token) {
case SyntaxKind.OpenBraceToken:
@@ -3817,7 +3874,7 @@ module ts {
}
// It is very important that we check this *after* checking indexers because
// the [ token can start an index signature or a computed property name
if (token >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral ||
if (isIdentifierOrKeyword() || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral ||
token === SyntaxKind.AsteriskToken || token === SyntaxKind.OpenBracketToken) {
return parsePropertyMemberDeclaration(fullStart, modifiers);
}
@@ -3975,14 +4032,12 @@ module ts {
}
function isExternalModuleReference() {
if (token === SyntaxKind.RequireKeyword) {
return lookAhead(() => {
nextToken();
return token === SyntaxKind.OpenParenToken;
});
}
return token === SyntaxKind.RequireKeyword &&
lookAhead(nextTokenIsOpenParen);
}
return false;
function nextTokenIsOpenParen() {
return nextToken() === SyntaxKind.OpenParenToken;
}
function parseImportDeclaration(fullStart: number, modifiers: ModifiersArray): ImportDeclaration {
@@ -4039,23 +4094,47 @@ module ts {
case SyntaxKind.ImportKeyword:
case SyntaxKind.TypeKeyword:
// Not true keywords so ensure an identifier follows
return lookAhead(() => nextToken() >= SyntaxKind.Identifier);
return lookAhead(nextTokenIsIdentifierOrKeyword);
case SyntaxKind.ModuleKeyword:
// Not a true keyword so ensure an identifier or string literal follows
return lookAhead(() => nextToken() >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral);
return lookAhead(nextTokenIsIdentifierOrKeywordOrStringLiteral);
case SyntaxKind.ExportKeyword:
// Check for export assignment or modifier on source element
return lookAhead(() => nextToken() === SyntaxKind.EqualsToken || isDeclarationStart());
return lookAhead(nextTokenIsEqualsTokenOrDeclarationStart);
case SyntaxKind.DeclareKeyword:
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.StaticKeyword:
// Check for modifier on source element
return lookAhead(() => { nextToken(); return isDeclarationStart(); });
return lookAhead(nextTokenIsDeclarationStart);
}
}
function isIdentifierOrKeyword() {
return token >= SyntaxKind.Identifier;
}
function nextTokenIsIdentifierOrKeyword() {
nextToken();
return isIdentifierOrKeyword();
}
function nextTokenIsIdentifierOrKeywordOrStringLiteral() {
nextToken();
return isIdentifierOrKeyword() || token === SyntaxKind.StringLiteral;
}
function nextTokenIsEqualsTokenOrDeclarationStart() {
nextToken();
return token === SyntaxKind.EqualsToken || isDeclarationStart();
}
function nextTokenIsDeclarationStart() {
nextToken();
return isDeclarationStart();
}
function parseDeclaration(): ModuleElement {
var fullStart = getNodePos();
var modifiers = parseModifiers();