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.
This commit is contained in:
Cyrus Najmabadi 2014-12-04 09:04:35 -08:00
parent d796bf1e0a
commit 589e6274f8
2 changed files with 47 additions and 35 deletions

View File

@ -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<T>(callback: () => T, alwaysResetState: boolean): T {
function speculationHelper<T>(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<T>(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<T>(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<TypeNode>) {
return checkForDisallowedTrailingComma(typeArguments) ||
checkForAtLeastOneTypeArgument(typeArguments) ||
checkForMissingTypeArgument(typeArguments);
checkForAtLeastOneTypeArgument(typeArguments);
}
function checkForOmittedArgument(arguments: NodeArray<Expression>) {
@ -4646,17 +4648,6 @@ module ts {
}
}
function checkForMissingTypeArgument(typeArguments: NodeArray<TypeNode>) {
if (typeArguments) {
for (var i = 0, n = typeArguments.length; i < n; i++) {
var arg = typeArguments[i];
if (arg.kind === SyntaxKind.TypeReference && getFullWidth((<TypeReferenceNode>arg).typeName) === 0) {
return grammarErrorAtPos(arg.pos, 0, Diagnostics.Type_expected);
}
}
}
}
function checkForAtLeastOneTypeArgument(typeArguments: NodeArray<TypeNode>) {
if (typeArguments && typeArguments.length === 0) {
var start = typeArguments.pos - "<".length;

View File

@ -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<T>(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<T>(callback: () => T): T;
}
@ -1174,7 +1183,7 @@ module ts {
return token = scanTemplateAndSetTokenValue();
}
function tryScan<T>(callback: () => T): T {
function speculationHelper<T>(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<T>(callback: () => T): T {
return speculationHelper(callback, /*isLookahead:*/ true);
}
function tryScan<T>(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,
};
}
}