Consolidate Document and SoruceFile

This commit is contained in:
Mohamed Hegazy 2014-08-04 11:45:35 -07:00
parent c17a24738c
commit aa75de9c70
9 changed files with 168 additions and 198 deletions

View File

@ -417,7 +417,7 @@ module ts {
nodeIsNestedInLabel(label: Identifier, requireIterationStatement: boolean, stopAtFunctionBoundary: boolean): ControlBlockContext;
}
export function createSourceFile(filename: string, sourceText: string, languageVersion: ScriptTarget): SourceFile {
export function createSourceFile(filename: string, sourceText: string, languageVersion: ScriptTarget, byteOrderMark: ByteOrderMark, version: number = 0, isOpen: boolean = false): SourceFile {
var file: SourceFile;
var scanner: Scanner;
var token: SyntaxKind;
@ -3523,6 +3523,10 @@ module ts {
file.externalModuleIndicator = getExternalModuleIndicator();
file.nodeCount = nodeCount;
file.identifierCount = identifierCount;
file.version = version;
file.byteOrderMark = byteOrderMark;
file.isOpen = isOpen;
file.languageVersion = languageVersion;
return file;
}

View File

@ -135,7 +135,7 @@ module ts {
}
text = "";
}
return text !== undefined ? createSourceFile(filename, text, languageVersion) : undefined;
return text !== undefined ? createSourceFile(filename, text, languageVersion, ByteOrderMark.None) : undefined;
}
function writeFile(fileName: string, data: string, onError?: (message: string) => void) {

View File

@ -525,6 +525,10 @@ module ts {
nodeCount: number;
identifierCount: number;
symbolCount: number;
byteOrderMark: ByteOrderMark;
isOpen: boolean;
version: number;
languageVersion: ScriptTarget;
}
export interface Program {

View File

@ -1895,12 +1895,12 @@ module FourSlash {
var result = '';
var fourslashFilename = 'fourslash.ts';
var tsFn = 'tests/cases/fourslash/' + fourslashFilename;
fourslashSourceFile = fourslashSourceFile || ts.createSourceFile(tsFn, Harness.IO.readFile(tsFn), ts.ScriptTarget.ES5);
libdtsSourceFile = libdtsSourceFile || ts.createSourceFile('lib.d.ts', Harness.Compiler.libTextMinimal, ts.ScriptTarget.ES3);
fourslashSourceFile = fourslashSourceFile || ts.createSourceFile(tsFn, Harness.IO.readFile(tsFn), ts.ScriptTarget.ES5, ts.ByteOrderMark.None, /*version*/ 0, /*isOpen*/ false);
libdtsSourceFile = libdtsSourceFile || ts.createSourceFile('lib.d.ts', Harness.Compiler.libTextMinimal, ts.ScriptTarget.ES3, ts.ByteOrderMark.None, /*version*/ 0, /*isOpen*/ false);
var files: { [filename: string]: ts.SourceFile; } = {};
files[fourslashFilename] = fourslashSourceFile;
files[fileName] = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES5);
files[fileName] = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES5, ts.ByteOrderMark.None, /*version*/ 0, /*isOpen*/ false);
files['lib.d.ts'] = libdtsSourceFile;
var host = Harness.Compiler.createCompilerHost(files, (fn, contents) => result = contents);

View File

@ -544,7 +544,7 @@ module Harness {
} else {
var lib = 'lib.d.ts';
if (fn.substr(fn.length - lib.length) === lib) {
return filemap[fn] = ts.createSourceFile('lib.d.ts', libTextMinimal, languageVersion);
return filemap[fn] = ts.createSourceFile('lib.d.ts', libTextMinimal, languageVersion, ts.ByteOrderMark.None);
}
// Don't throw here -- the compiler might be looking for a test that actually doesn't exist as part of the TC
return null;
@ -720,7 +720,7 @@ module Harness {
var filemap: { [name: string]: ts.SourceFile; } = {};
var register = (file: { unitName: string; content: string; }) => {
var filename = Path.switchToForwardSlashes(file.unitName);
filemap[filename] = ts.createSourceFile(filename, file.content, options.target);
filemap[filename] = ts.createSourceFile(filename, file.content, options.target, ts.ByteOrderMark.None);
};
inputFiles.forEach(register);
otherFiles.forEach(register);

View File

@ -147,20 +147,19 @@ module Harness.LanguageService {
scriptSnapshot: TypeScript.IScriptSnapshot,
byteOrderMark: ts.ByteOrderMark,
version: number,
isOpen: boolean,
referencedFiles: string[]= []): ts.Document {
return ts.createDocument(compilationSettings, fileName, scriptSnapshot, byteOrderMark, version, isOpen, referencedFiles);
isOpen: boolean): ts.SourceFile {
return ts.createSourceFile(fileName, scriptSnapshot.getText(0, scriptSnapshot.getLength()), compilationSettings.target, byteOrderMark, version, isOpen);
}
public updateDocument(
document: ts.Document,
document: ts.SourceFile,
fileName: string,
compilationSettings: ts.CompilerOptions,
scriptSnapshot: TypeScript.IScriptSnapshot,
version: number,
isOpen: boolean,
textChangeRange: TypeScript.TextChangeRange
): ts.Document {
): ts.SourceFile {
return document.update(scriptSnapshot, version, isOpen, textChangeRange);
}

View File

@ -139,7 +139,7 @@ class ProjectRunner extends RunnerBase {
function getSourceFile(filename: string, languageVersion: ts.ScriptTarget): ts.SourceFile {
var sourceFile: ts.SourceFile = undefined;
if (filename === 'lib.d.ts') {
sourceFile = ts.createSourceFile('lib.d.ts', Harness.Compiler.libTextMinimal, languageVersion);
sourceFile = ts.createSourceFile('lib.d.ts', Harness.Compiler.libTextMinimal, languageVersion, ts.ByteOrderMark.None);
}
else {
assert.isTrue(!ts.filter(readInputFiles, sourceFile => sourceFile.filename == filename).length, "Compiler trying to read same file again: " + filename);
@ -154,7 +154,7 @@ class ProjectRunner extends RunnerBase {
}
if (text !== undefined) {
sourceFile = ts.createSourceFile(filename, text, languageVersion)
sourceFile = ts.createSourceFile(filename, text, languageVersion, ts.ByteOrderMark.None);
}
}

View File

@ -107,7 +107,7 @@ module RWC {
catch (e) {
// Leave fileContents undefined;
}
return ts.createSourceFile(fileName, fileContents, languageVersion);
return ts.createSourceFile(fileName, fileContents, languageVersion, ts.ByteOrderMark.None);
},
getDefaultLibFilename: () => libPath,
writeFile: (fn, contents) => emitterIOHost.writeFile(fn, contents, false),

View File

@ -67,6 +67,13 @@ module ts {
getReturnType(): Type;
}
export interface SourceFile {
getSourceUnit(): TypeScript.SourceUnitSyntax;
getSyntaxTree(): TypeScript.SyntaxTree;
getBloomFilter(): TypeScript.BloomFilter;
update(scriptSnapshot: TypeScript.IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): SourceFile;
}
var scanner: Scanner = createScanner(ScriptTarget.ES5);
var emptyArray: any [] = [];
@ -81,7 +88,6 @@ module ts {
}
class NodeObject implements Node {
public kind: SyntaxKind;
public pos: number;
public end: number;
@ -294,6 +300,130 @@ module ts {
}
}
var incrementalParse: IncrementalParse = TypeScript.IncrementalParser.parse;
class SourceFileObject extends NodeObject implements SourceFile {
public filename: string;
public text: string;
public getLineAndCharacterFromPosition(position: number): { line: number; character: number } { return null; }
public amdDependencies: string[];
public referencedFiles: FileReference[];
public syntacticErrors: Diagnostic[];
public semanticErrors: Diagnostic[];
public hasNoDefaultLib: boolean;
public externalModuleIndicator: Node; // The first node that causes this file to be an external module
public nodeCount: number;
public identifierCount: number;
public symbolCount: number;
public statements: NodeArray<Statement>;
public byteOrderMark: ByteOrderMark;
public version: number;
public isOpen: boolean;
public languageVersion: ScriptTarget;
private bloomFilter: TypeScript.BloomFilter;
private syntaxTree: TypeScript.SyntaxTree;
private scriptSnapshot: TypeScript.IScriptSnapshot;
public getSourceUnit(): TypeScript.SourceUnitSyntax {
// If we don't have a script, create one from our parse tree.
return this.getSyntaxTree().sourceUnit();
}
public getLineMap(): TypeScript.LineMap {
return this.getSyntaxTree().lineMap();
}
public getSyntaxTree(): TypeScript.SyntaxTree {
if (!this.syntaxTree) {
var start = new Date().getTime();
this.syntaxTree = TypeScript.Parser.parse(
this.filename, TypeScript.SimpleText.fromScriptSnapshot(this.scriptSnapshot), this.languageVersion, this.isDeclareFile());
var time = new Date().getTime() - start;
//TypeScript.syntaxTreeParseTime += time;
}
return this.syntaxTree;
}
private isDeclareFile(): boolean {
return TypeScript.isDTSFile(this.filename);
}
public getBloomFilter(): TypeScript.BloomFilter {
if (!this.bloomFilter) {
var identifiers = TypeScript.createIntrinsicsObject<boolean>();
var pre = function (cur: TypeScript.ISyntaxElement) {
if (TypeScript.ASTHelpers.isValidAstNode(cur)) {
if (cur.kind() === TypeScript.SyntaxKind.IdentifierName) {
var nodeText = TypeScript.tokenValueText((<TypeScript.ISyntaxToken>cur));
identifiers[nodeText] = true;
}
}
};
TypeScript.getAstWalkerFactory().simpleWalk(this.getSourceUnit(), pre, null, identifiers);
var identifierCount = 0;
for (var name in identifiers) {
if (identifiers[name]) {
identifierCount++;
}
}
this.bloomFilter = new TypeScript.BloomFilter(identifierCount);
this.bloomFilter.addKeys(identifiers);
}
return this.bloomFilter;
}
public update(scriptSnapshot: TypeScript.IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): SourceFile {
// See if we are currently holding onto a syntax tree. We may not be because we're
// either a closed file, or we've just been lazy and haven't had to create the syntax
// tree yet. Access the field instead of the method so we don't accidently realize
// the old syntax tree.
var oldSyntaxTree = this.syntaxTree;
if (textChangeRange !== null && Debug.shouldAssert(AssertionLevel.Normal)) {
var oldText = this.scriptSnapshot;
var newText = scriptSnapshot;
TypeScript.Debug.assert((oldText.getLength() - textChangeRange.span().length() + textChangeRange.newLength()) === newText.getLength());
if (Debug.shouldAssert(AssertionLevel.VeryAggressive)) {
var oldTextPrefix = oldText.getText(0, textChangeRange.span().start());
var newTextPrefix = newText.getText(0, textChangeRange.span().start());
TypeScript.Debug.assert(oldTextPrefix === newTextPrefix);
var oldTextSuffix = oldText.getText(textChangeRange.span().end(), oldText.getLength());
var newTextSuffix = newText.getText(textChangeRange.newSpan().end(), newText.getLength());
TypeScript.Debug.assert(oldTextSuffix === newTextSuffix);
}
}
var text = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot);
// If we don't have a text change, or we don't have an old syntax tree, then do a full
// parse. Otherwise, do an incremental parse.
var newSyntaxTree = textChangeRange === null || oldSyntaxTree === null
? TypeScript.Parser.parse(this.filename, text, this.languageVersion, TypeScript.isDTSFile(this.filename))
: TypeScript.IncrementalParser.parse(oldSyntaxTree, textChangeRange, text);
return SourceFileObject.createSourceFileObject(this.languageVersion, this.filename, scriptSnapshot, this.byteOrderMark, version, isOpen, newSyntaxTree);
}
public static createSourceFileObject(languageVersion: ScriptTarget, filename: string, scriptSnapshot: TypeScript.IScriptSnapshot, byteOrderMark: ByteOrderMark, version: number, isOpen: boolean, syntaxTree: TypeScript.SyntaxTree) {
var newSoruceFile = <SourceFileObject><any>createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), languageVersion, byteOrderMark, version, isOpen);
newSoruceFile.scriptSnapshot = scriptSnapshot;
newSoruceFile.syntaxTree = syntaxTree;
return newSoruceFile;
}
}
export interface Logger {
information(): boolean;
debug(): boolean;
@ -563,17 +693,17 @@ module ts {
byteOrderMark: ByteOrderMark,
version: number,
isOpen: boolean,
referencedFiles: string[]): Document;
referencedFiles: string[]): SourceFile;
updateDocument(
document: Document,
document: SourceFile,
filename: string,
compilationSettings: CompilerOptions,
scriptSnapshot: TypeScript.IScriptSnapshot,
version: number,
isOpen: boolean,
textChangeRange: TypeScript.TextChangeRange
): Document;
): SourceFile;
releaseDocument(filename: string, compilationSettings: CompilerOptions): void
}
@ -665,172 +795,6 @@ module ts {
(oldSyntaxTree: TypeScript.SyntaxTree, textChangeRange: TypeScript.TextChangeRange, newText: TypeScript.ISimpleText): TypeScript.SyntaxTree
}
export interface Document {
getFilename(): string;
getByteOrderMark(): ByteOrderMark;
getVersion(): number;
isOpen(): boolean;
getSourceUnit(): TypeScript.SourceUnitSyntax;
getSyntaxTree(): TypeScript.SyntaxTree;
getSourceFile(): SourceFile;
getBloomFilter(): TypeScript.BloomFilter;
update(scriptSnapshot: TypeScript.IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): Document;
}
class DocumentObject implements Document {
private bloomFilter: TypeScript.BloomFilter = null;
// By default, our Document class doesn't support incremental update of its conten
// However, we enable other layers (like teh services layer) to inject the capability
// into us by setting this function.
public static incrementalParse: IncrementalParse = TypeScript.IncrementalParser.parse;
constructor(private compilationSettings: CompilerOptions,
public filename: string,
public referencedFiles: string[],
private scriptSnapshot: TypeScript.IScriptSnapshot,
public byteOrderMark: ByteOrderMark,
public version: number,
public _isOpen: boolean,
private syntaxTree: TypeScript.SyntaxTree,
private soruceFile: SourceFile) {
}
public getFilename(): string {
return this.filename;
}
public getVersion(): number {
return this.version;
}
public isOpen(): boolean {
return this._isOpen;
}
public getByteOrderMark(): ByteOrderMark {
return this.byteOrderMark;
}
public isDeclareFile(): boolean {
return TypeScript.isDTSFile(this.filename);
}
public getSourceUnit(): TypeScript.SourceUnitSyntax {
// If we don't have a script, create one from our parse tree.
return this.getSyntaxTree().sourceUnit();
}
public getLineMap(): TypeScript.LineMap {
return this.getSyntaxTree().lineMap();
}
public getSyntaxTree(): TypeScript.SyntaxTree {
if (!this.syntaxTree) {
var start = new Date().getTime();
this.syntaxTree = TypeScript.Parser.parse(
this.filename, TypeScript.SimpleText.fromScriptSnapshot(this.scriptSnapshot), this.compilationSettings.target, this.isDeclareFile());
var time = new Date().getTime() - start;
//TypeScript.syntaxTreeParseTime += time;
}
return this.syntaxTree;
}
public getSourceFile(): SourceFile {
if (!this.soruceFile) {
var start = new Date().getTime();
this.soruceFile = createSourceFile(this.filename, this.scriptSnapshot.getText(0, this.scriptSnapshot.getLength()), this.compilationSettings.target);
var time = new Date().getTime() - start;
//TypeScript.astParseTime += time;
}
return this.soruceFile;
}
public getBloomFilter(): TypeScript.BloomFilter {
if (!this.bloomFilter) {
var identifiers = TypeScript.createIntrinsicsObject<boolean>();
var pre = function (cur: TypeScript.ISyntaxElement) {
if (TypeScript.ASTHelpers.isValidAstNode(cur)) {
if (cur.kind() === TypeScript.SyntaxKind.IdentifierName) {
var nodeText = TypeScript.tokenValueText((<TypeScript.ISyntaxToken>cur));
identifiers[nodeText] = true;
}
}
};
TypeScript.getAstWalkerFactory().simpleWalk(this.getSourceUnit(), pre, null, identifiers);
var identifierCount = 0;
for (var name in identifiers) {
if (identifiers[name]) {
identifierCount++;
}
}
this.bloomFilter = new TypeScript.BloomFilter(identifierCount);
this.bloomFilter.addKeys(identifiers);
}
return this.bloomFilter;
}
// Returns true if this file should get emitted into its own unique output file.
// Otherwise, it should be written into a single output file along with the rest of hte
// documents in the compilation.
public emitToOwnOutputFile(): boolean {
// If we haven't specified an output file in our settings, then we're definitely
// emitting to our own file. Also, if we're an external module, then we're
// definitely emitting to our own file.
return !this.compilationSettings.out || this.getSyntaxTree().isExternalModule();
}
public update(scriptSnapshot: TypeScript.IScriptSnapshot, version: number, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): Document {
// See if we are currently holding onto a syntax tree. We may not be because we're
// either a closed file, or we've just been lazy and haven't had to create the syntax
// tree yet. Access the field instead of the method so we don't accidently realize
// the old syntax tree.
var oldSyntaxTree = this.syntaxTree;
if (textChangeRange !== null && Debug.shouldAssert(AssertionLevel.Normal)) {
var oldText = this.scriptSnapshot;
var newText = scriptSnapshot;
TypeScript.Debug.assert((oldText.getLength() - textChangeRange.span().length() + textChangeRange.newLength()) === newText.getLength());
if (Debug.shouldAssert(AssertionLevel.VeryAggressive)) {
var oldTextPrefix = oldText.getText(0, textChangeRange.span().start());
var newTextPrefix = newText.getText(0, textChangeRange.span().start());
TypeScript.Debug.assert(oldTextPrefix === newTextPrefix);
var oldTextSuffix = oldText.getText(textChangeRange.span().end(), oldText.getLength());
var newTextSuffix = newText.getText(textChangeRange.newSpan().end(), newText.getLength());
TypeScript.Debug.assert(oldTextSuffix === newTextSuffix);
}
}
var text = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot);
// If we don't have a text change, or we don't have an old syntax tree, then do a full
// parse. Otherwise, do an incremental parse.
var newSyntaxTree = textChangeRange === null || oldSyntaxTree === null || DocumentObject.incrementalParse === null
? TypeScript.Parser.parse(this.filename, text, this.compilationSettings.target, TypeScript.isDTSFile(this.filename))
: DocumentObject.incrementalParse(oldSyntaxTree, textChangeRange, text);
return new DocumentObject(this.compilationSettings, this.filename, this.referencedFiles, scriptSnapshot, this.byteOrderMark, version, isOpen, newSyntaxTree, /*soruceFile*/ null);
}
}
export function createDocument(compilationSettings: CompilerOptions, fileName: string, scriptSnapshot: TypeScript.IScriptSnapshot, byteOrderMark: ByteOrderMark, version: number, isOpen: boolean, referencedFiles: string[]): Document {
return new DocumentObject(compilationSettings, fileName, referencedFiles, scriptSnapshot, byteOrderMark, version, isOpen, /*syntaxTree:*/ null, /*soruceFile*/ null);
}
/// Language Service
interface CompletionSession {
@ -859,7 +823,7 @@ module ts {
}
interface DocumentRegistryEntry {
document: Document;
document: SourceFile;
refCount: number;
owners: string[];
}
@ -1172,13 +1136,12 @@ module ts {
scriptSnapshot: TypeScript.IScriptSnapshot,
byteOrderMark: ByteOrderMark,
version: number,
isOpen: boolean,
referencedFiles: string[]= []): Document {
isOpen: boolean): SourceFile {
var bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true);
var entry = lookUp(bucket, filename);
if (!entry) {
var document = createDocument(compilationSettings, filename, scriptSnapshot, byteOrderMark, version, isOpen, referencedFiles);
var document = createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), compilationSettings.target, byteOrderMark, version, isOpen);
bucket[filename] = entry = {
document: document,
@ -1192,21 +1155,21 @@ module ts {
}
function updateDocument(
document: Document,
document: SourceFile,
filename: string,
compilationSettings: CompilerOptions,
scriptSnapshot: TypeScript.IScriptSnapshot,
version: number,
isOpen: boolean,
textChangeRange: TypeScript.TextChangeRange
): Document {
): SourceFile {
var bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ false);
Debug.assert(bucket);
var entry = lookUp(bucket, filename);
Debug.assert(entry);
if (entry.document.isOpen() === isOpen && entry.document.getVersion() === version) {
if (entry.document.isOpen === isOpen && entry.document.version === version) {
return entry.document;
}
@ -1252,7 +1215,7 @@ module ts {
var program: Program;
var typeChecker: TypeChecker;
var useCaseSensitivefilenames = false;
var documentsByName: Map<Document> = {};
var documentsByName: Map<SourceFile> = {};
var documentRegistry = documentRegistry;
var cancellationToken = new CancellationTokenObject(host.getCancellationToken());
var activeCompletionSession: CompletionSession; // The current active completion session, used to get the completion entry details
@ -1262,7 +1225,7 @@ module ts {
TypeScript.LocalizedDiagnosticMessages = host.getLocalizedDiagnosticMessages();
}
function getDocument(filename: string): Document {
function getDocument(filename: string): SourceFile {
return lookUp(documentsByName, filename);
}
@ -1339,12 +1302,12 @@ module ts {
var isOpen = hostCache.isOpen(filename);
var scriptSnapshot = hostCache.getScriptSnapshot(filename);
var document: Document = getDocument(filename);
var document: SourceFile = getDocument(filename);
if (document) {
//
// If the document is the same, assume no update
//
if (document.getVersion() === version && document.isOpen() === isOpen) {
if (document.version === version && document.isOpen === isOpen) {
continue;
}
@ -1356,7 +1319,7 @@ module ts {
// new text buffer).
var textChangeRange: TypeScript.TextChangeRange = null;
if (document.isOpen && isOpen) {
textChangeRange = hostCache.getScriptTextChangeRangeSinceVersion(filename, document.getVersion());
textChangeRange = hostCache.getScriptTextChangeRangeSinceVersion(filename, document.version);
}
document = documentRegistry.updateDocument(document, filename, compilationSettings, scriptSnapshot, version, isOpen, textChangeRange);
@ -2217,7 +2180,7 @@ module ts {
getNodeConstructor: kind => {
function Node() {
}
var proto = new NodeObject();
var proto = kind === SyntaxKind.SourceFile ? new SourceFileObject() : new NodeObject();
proto.kind = kind;
proto.pos = 0;
proto.end = 0;