From 589e6274f80957eda8009042d9471025c08f0779 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2014 09:04:35 -0800 Subject: [PATCH] Remove function expression allocation when speculatively parsing or looking ahead. Also, remove unnecessary grammar check now that the previous hack to insert a missing type argument node has been removed. --- src/compiler/parser.ts | 57 +++++++++++++++++------------------------ src/compiler/scanner.ts | 25 ++++++++++++++++-- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 758b4253ddc..42af2b52210 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -997,6 +997,10 @@ module ts { // // Getting this all correct is tricky and requires careful reading of the grammar to // understand when these values should be changed versus when they should be inherited. + // + // Note: it should not be necessary to save/restore these flags during speculative/lookahead + // parsing. These context flags are naturally stored and restored through normal recursive + // descent parsing and unwinding. var contextFlags: ParserContextFlags = 0; function setContextFlag(val: Boolean, flag: ParserContextFlags) { @@ -1156,16 +1160,21 @@ module ts { return token = scanner.reScanTemplateToken(); } - function lookAheadHelper(callback: () => T, alwaysResetState: boolean): T { + function speculationHelper(callback: () => T, isLookAhead: boolean): T { // Keep track of the state we'll need to rollback to if lookahead fails (or if the // caller asked us to always reset our state). var saveToken = token; var saveParseDiagnosticsLength = sourceFile.parseDiagnostics.length; - var result = callback(); + // If we're only looking ahead, then tell the scanner to only lookahead as well. + // If we're actually speculatively parsing, then tell the scanner to do the same. + var result = isLookAhead + ? scanner.lookAhead(callback) + : scanner.tryScan(callback); - // Now restore as appropriate. - if (!result || alwaysResetState) { + // If our callback returned something 'falsy' or we're just looking ahead, + // then unconditionally restore us to where we were. + if (!result || isLookAhead) { token = saveToken; sourceFile.parseDiagnostics.length = saveParseDiagnosticsLength; } @@ -1173,25 +1182,19 @@ module ts { return result; } + // Invokes the provided callback then unconditionally restores the parser to the state it + // was in immediately prior to invoking the callback. The result of invoking the callback + // is returned from this function. function lookAhead(callback: () => T): T { - var result: T; - scanner.tryScan(() => { - result = lookAheadHelper(callback, /*alwaysResetState:*/ true); - - // Returning false here indicates to the scanner that it should always jump - // back to where it started. This makes sense as 'lookahead' acts as if - // neither the parser nor scanner was affected by the operation. - // - // Note: the rewinding of the parser state is already handled in lookAheadHelper - // (because we passed 'true' for alwaysResetState). - return false; - }); - - return result; + return speculationHelper(callback, /*isLookAhead:*/ true); } - + + // Invokes the provided callback. If the callback returns something falsy, then it restores + // the parser to the state it was in immediately prior to invoking the callback. If the + // callback returns something truthy, then the parser state is not rolled back. The result + // of invoking the callback is returned from this function. function tryParse(callback: () => T): T { - return scanner.tryScan(() => lookAheadHelper(callback, /*alwaysResetState:*/ false)); + return speculationHelper(callback, /*isLookAhead:*/ false); } function isIdentifier(): boolean { @@ -4631,8 +4634,7 @@ module ts { function checkTypeArguments(typeArguments: NodeArray) { return checkForDisallowedTrailingComma(typeArguments) || - checkForAtLeastOneTypeArgument(typeArguments) || - checkForMissingTypeArgument(typeArguments); + checkForAtLeastOneTypeArgument(typeArguments); } function checkForOmittedArgument(arguments: NodeArray) { @@ -4646,17 +4648,6 @@ module ts { } } - function checkForMissingTypeArgument(typeArguments: NodeArray) { - if (typeArguments) { - for (var i = 0, n = typeArguments.length; i < n; i++) { - var arg = typeArguments[i]; - if (arg.kind === SyntaxKind.TypeReference && getFullWidth((arg).typeName) === 0) { - return grammarErrorAtPos(arg.pos, 0, Diagnostics.Type_expected); - } - } - } - } - function checkForAtLeastOneTypeArgument(typeArguments: NodeArray) { if (typeArguments && typeArguments.length === 0) { var start = typeArguments.pos - "<".length; diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index fcb8c9b0c31..3493ad7b512 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -29,6 +29,15 @@ module ts { scan(): SyntaxKind; setText(text: string): void; setTextPos(textPos: number): void; + // Invokes the provided callback then unconditionally restores the scanner to the state it + // was in immediately prior to invoking the callback. The result of invoking the callback + // is returned from this function. + lookAhead(callback: () => T): T; + + // Invokes the provided callback. If the callback returns something falsy, then it restores + // the scanner to the state it was in immediately prior to invoking the callback. If the + // callback returns something truthy, then the scanner state is not rolled back. The result + // of invoking the callback is returned from this function. tryScan(callback: () => T): T; } @@ -1174,7 +1183,7 @@ module ts { return token = scanTemplateAndSetTokenValue(); } - function tryScan(callback: () => T): T { + function speculationHelper(callback: () => T, isLookahead: boolean): T { var savePos = pos; var saveStartPos = startPos; var saveTokenPos = tokenPos; @@ -1182,7 +1191,10 @@ module ts { var saveTokenValue = tokenValue; var savePrecedingLineBreak = precedingLineBreak; var result = callback(); - if (!result) { + + // If our callback returned something 'falsy' or we're just looking ahead, + // then unconditionally restore us to where we were. + if (!result || isLookahead) { pos = savePos; startPos = saveStartPos; tokenPos = saveTokenPos; @@ -1193,6 +1205,14 @@ module ts { return result; } + function lookAhead(callback: () => T): T { + return speculationHelper(callback, /*isLookahead:*/ true); + } + + function tryScan(callback: () => T): T { + return speculationHelper(callback, /*isLookahead:*/ false); + } + function setText(newText: string) { text = newText || ""; len = text.length; @@ -1228,6 +1248,7 @@ module ts { setText, setTextPos, tryScan, + lookAhead, }; } }