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, - }; } }