diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts
index 0271ac73105..6e956889b49 100644
--- a/src/compiler/diagnosticInformationMap.generated.ts
+++ b/src/compiler/diagnosticInformationMap.generated.ts
@@ -1,6 +1,5 @@
//
///
-/* @internal */
module ts {
export var Diagnostics = {
Unterminated_string_literal: { code: 1002, category: DiagnosticCategory.Error, key: "Unterminated string literal." },
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 93f8404d881..5e65103f91c 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -1006,6 +1006,10 @@ module ts {
return result;
}
+ // Share a single scanner across all calls to parse a source file. This helps speed things
+ // up by avoiding the cost of creating/compiling scanners over and over again.
+ let scanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ true);
+
function parseSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, syntaxCursor: SyntaxCursor, setParentNodes = false): SourceFile {
const disallowInAndDecoratorContext = ParserContextFlags.DisallowIn | ParserContextFlags.Decorator;
@@ -1105,7 +1109,10 @@ module ts {
let parseErrorBeforeNextFinishedNode: boolean = false;
// Create and prime the scanner before parsing the source elements.
- let scanner = createScanner(languageVersion, /*skipTrivia*/ true, sourceText, scanError);
+
+ scanner.setText(sourceText);
+ scanner.setOnError(scanError);
+ scanner.setScriptTarget(languageVersion);
token = nextToken();
processReferenceComments(sourceFile);
@@ -1125,6 +1132,10 @@ module ts {
}
syntaxCursor = undefined;
+
+ // Clear out the text the scanner is pointing at, so it doesn't keep anything alive unnecessarily.
+ scanner.setText("");
+ scanner.setOnError(undefined);
return sourceFile;
function setContextFlag(val: Boolean, flag: ParserContextFlags) {
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index 46466478029..3cf0f34e54f 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -54,6 +54,7 @@ module ts {
}
text = "";
}
+
return text !== undefined ? createSourceFile(fileName, text, languageVersion, setParentNodes) : undefined;
}
diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts
index 327fb5261a0..53d213f5bd5 100644
--- a/src/compiler/scanner.ts
+++ b/src/compiler/scanner.ts
@@ -25,6 +25,8 @@ module ts {
reScanTemplateToken(): SyntaxKind;
scan(): SyntaxKind;
setText(text: string): void;
+ setOnError(onError: ErrorCallback): void;
+ setScriptTarget(scriptTarget: ScriptTarget): 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
@@ -599,6 +601,7 @@ module ts {
export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean, text?: string, onError?: ErrorCallback): Scanner {
let pos: number; // Current position (end position of text of current token)
let len: number; // Length of text
+
let startPos: number; // Start position of whitespace before current token
let tokenPos: number; // Start position of text of current token
let token: SyntaxKind;
@@ -607,6 +610,32 @@ module ts {
let hasExtendedUnicodeEscape: boolean;
let tokenIsUnterminated: boolean;
+ setText(text);
+
+ return {
+ getStartPos: () => startPos,
+ getTextPos: () => pos,
+ getToken: () => token,
+ getTokenPos: () => tokenPos,
+ getTokenText: () => text.substring(tokenPos, pos),
+ getTokenValue: () => tokenValue,
+ hasExtendedUnicodeEscape: () => hasExtendedUnicodeEscape,
+ hasPrecedingLineBreak: () => precedingLineBreak,
+ isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord,
+ isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord,
+ isUnterminated: () => tokenIsUnterminated,
+ reScanGreaterToken,
+ reScanSlashToken,
+ reScanTemplateToken,
+ scan,
+ setText,
+ setScriptTarget,
+ setOnError,
+ setTextPos,
+ tryScan,
+ lookAhead,
+ };
+
function error(message: DiagnosticMessage, length?: number): void {
if (onError) {
onError(message, length || 0);
@@ -1450,37 +1479,24 @@ module ts {
setTextPos(0);
}
+ function setOnError(errorCallback: ErrorCallback) {
+ onError = errorCallback;
+ }
+
+ function setScriptTarget(scriptTarget: ScriptTarget) {
+ languageVersion = scriptTarget;
+ }
+
function setTextPos(textPos: number) {
pos = textPos;
startPos = textPos;
tokenPos = textPos;
token = SyntaxKind.Unknown;
precedingLineBreak = false;
+
+ tokenValue = undefined;
+ hasExtendedUnicodeEscape = false;
+ tokenIsUnterminated = false;
}
-
- setText(text);
-
-
- return {
- getStartPos: () => startPos,
- getTextPos: () => pos,
- getToken: () => token,
- getTokenPos: () => tokenPos,
- getTokenText: () => text.substring(tokenPos, pos),
- getTokenValue: () => tokenValue,
- hasExtendedUnicodeEscape: () => hasExtendedUnicodeEscape,
- hasPrecedingLineBreak: () => precedingLineBreak,
- isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord,
- isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord,
- isUnterminated: () => tokenIsUnterminated,
- reScanGreaterToken,
- reScanSlashToken,
- reScanTemplateToken,
- scan,
- setText,
- setTextPos,
- tryScan,
- lookAhead,
- };
}
}