From 5e94c761905f51fcf1d6c23e14ad500835483092 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 10 May 2016 23:43:26 -0700 Subject: [PATCH] expose code path that will use Path type to avoid redundant string conversions --- src/compiler/program.ts | 19 ++++++-- src/compiler/types.ts | 2 + src/compiler/utilities.ts | 19 ++++++++ src/services/services.ts | 96 ++++++++++++++++++++++++++++++--------- 4 files changed, 111 insertions(+), 25 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a6e05cd60f9..52547cb165d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1039,6 +1039,7 @@ namespace ts { program = { getRootFileNames: () => rootNames, getSourceFile, + getSourceFileByPath, getSourceFiles: () => files, getCompilerOptions: () => options, getSyntacticDiagnostics, @@ -1115,7 +1116,11 @@ namespace ts { (oldOptions.allowJs !== options.allowJs) || (oldOptions.rootDir !== options.rootDir) || (oldOptions.typesSearchPaths !== options.typesSearchPaths) || - (oldOptions.configFilePath !== options.configFilePath)) { + (oldOptions.configFilePath !== options.configFilePath) || + (oldOptions.baseUrl !== options.baseUrl) || + (oldOptions.typesRoot !== options.typesRoot) || + !arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) || + !mapIsEqualTo(oldOptions.paths, options.paths)) { return false; } @@ -1137,7 +1142,10 @@ namespace ts { const modifiedSourceFiles: SourceFile[] = []; for (const oldSourceFile of oldProgram.getSourceFiles()) { - let newSourceFile = host.getSourceFile(oldSourceFile.fileName, options.target); + let newSourceFile = host.getSourceFileByPath + ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target) + : host.getSourceFile(oldSourceFile.fileName, options.target); + if (!newSourceFile) { return false; } @@ -1232,6 +1240,7 @@ namespace ts { getCurrentDirectory: () => currentDirectory, getNewLine: () => host.getNewLine(), getSourceFile: program.getSourceFile, + getSourceFileByPath: program.getSourceFileByPath, getSourceFiles: program.getSourceFiles, writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), @@ -1307,7 +1316,11 @@ namespace ts { } function getSourceFile(fileName: string): SourceFile { - return filesByName.get(toPath(fileName, currentDirectory, getCanonicalFileName)); + return getSourceFileByPath(toPath(fileName, currentDirectory, getCanonicalFileName)); + } + + function getSourceFileByPath(path: Path): SourceFile { + return filesByName.get(path); } function getDiagnosticsHelper( diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c367bffea3b..3a5602717c1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1627,6 +1627,7 @@ namespace ts { export interface ScriptReferenceHost { getCompilerOptions(): CompilerOptions; getSourceFile(fileName: string): SourceFile; + getSourceFileByPath(path: Path): SourceFile; getCurrentDirectory(): string; } @@ -2813,6 +2814,7 @@ namespace ts { export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile; + getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile; getCancellationToken?(): CancellationToken; getDefaultLibFileName(options: CompilerOptions): string; getDefaultLibLocation?(): string; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 208723159c2..9096a4ac7cd 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -84,6 +84,25 @@ namespace ts { return node.end - node.pos; } + export function mapIsEqualTo(map1: Map, map2: Map): boolean { + if (!map1 || !map2) { + return map1 === map2; + } + return containsAll(map1, map2) && containsAll(map2, map1); + } + + function containsAll(map: Map, other: Map): boolean { + for (const key in map) { + if (!hasProperty(map, key)) { + continue; + } + if (!hasProperty(other, key) || map[key] !== other[key]) { + return false; + } + } + return true; + } + export function arrayIsEqualTo(array1: T[], array2: T[], equaler?: (a: T, b: T) => boolean): boolean { if (!array1 || !array2) { return array1 === array2; diff --git a/src/services/services.ts b/src/services/services.ts index 8b89056abba..83f0d7152fd 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1481,6 +1481,15 @@ namespace ts { version: string, scriptKind?: ScriptKind): SourceFile; + acquireDocumentWithKey( + fileName: string, + path: Path, + compilationSettings: CompilerOptions, + key: DocumentRegistryBucketKey, + scriptSnapshot: IScriptSnapshot, + version: string, + scriptKind?: ScriptKind): SourceFile; + /** * Request an updated version of an already existing SourceFile with a given fileName * and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile @@ -1500,6 +1509,16 @@ namespace ts { version: string, scriptKind?: ScriptKind): SourceFile; + updateDocumentWithKey( + fileName: string, + path: Path, + compilationSettings: CompilerOptions, + key: DocumentRegistryBucketKey, + scriptSnapshot: IScriptSnapshot, + version: string, + scriptKind?: ScriptKind): SourceFile; + + getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey; /** * Informs the DocumentRegistry that a file is not needed any longer. * @@ -1511,9 +1530,13 @@ namespace ts { */ releaseDocument(fileName: string, compilationSettings: CompilerOptions): void; + releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void; + reportStats(): string; } + export type DocumentRegistryBucketKey = string & { __bucketKey: any }; + // TODO: move these to enums export namespace ScriptElementKind { export const unknown = ""; @@ -1783,11 +1806,13 @@ namespace ts { public getOrCreateEntry(fileName: string): HostFileInformation { const path = toPath(fileName, this.currentDirectory, this.getCanonicalFileName); - if (this.contains(path)) { - return this.getEntry(path); - } + return this.getOrCreateEntryByPath(fileName, path); + } - return this.createEntry(fileName, path); + public getOrCreateEntryByPath(fileName: string, path: Path): HostFileInformation { + return this.contains(path) + ? this.getEntry(path) + : this.createEntry(fileName, path); } public getRootFileNames(): string[] { @@ -2041,12 +2066,11 @@ namespace ts { const buckets: Map> = {}; const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames); - function getKeyFromCompilationSettings(settings: CompilerOptions): string { - return "_" + settings.target + "|" + settings.module + "|" + settings.noResolve + "|" + settings.jsx + +"|" + settings.allowJs; + function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey { + return `_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${settings.typesRoot}|${settings.typesSearchPaths}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`; } - function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap { - const key = getKeyFromCompilationSettings(settings); + function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap { let bucket = lookUp(buckets, key); if (!bucket && createIfMissing) { buckets[key] = bucket = createFileMap(); @@ -2075,23 +2099,36 @@ namespace ts { } function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true, scriptKind); + const path = toPath(fileName, currentDirectory, getCanonicalFileName); + const key = getKeyForCompilationSettings(compilationSettings); + return acquireDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind); + } + + function acquireDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile { + return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ true, scriptKind); } function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false, scriptKind); + const path = toPath(fileName, currentDirectory, getCanonicalFileName); + const key = getKeyForCompilationSettings(compilationSettings); + return updateDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind); + } + + function updateDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile { + return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ false, scriptKind); } function acquireOrUpdateDocument( fileName: string, + path: Path, compilationSettings: CompilerOptions, + key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, acquiring: boolean, scriptKind?: ScriptKind): SourceFile { - const bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true); - const path = toPath(fileName, currentDirectory, getCanonicalFileName); + const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true); let entry = bucket.get(path); if (!entry) { Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?"); @@ -2129,10 +2166,14 @@ namespace ts { } function releaseDocument(fileName: string, compilationSettings: CompilerOptions): void { - const bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/false); - Debug.assert(bucket !== undefined); - const path = toPath(fileName, currentDirectory, getCanonicalFileName); + const key = getKeyForCompilationSettings(compilationSettings); + return releaseDocumentWithKey(path, key); + } + + function releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void { + const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/false); + Debug.assert(bucket !== undefined); const entry = bucket.get(path); entry.languageServiceRefCount--; @@ -2145,9 +2186,13 @@ namespace ts { return { acquireDocument, + acquireDocumentWithKey, updateDocument, + updateDocumentWithKey, releaseDocument, - reportStats + releaseDocumentWithKey, + reportStats, + getKeyForCompilationSettings }; } @@ -2858,6 +2903,7 @@ namespace ts { // Now create a new compiler const compilerHost: CompilerHost = { getSourceFile: getOrCreateSourceFile, + getSourceFileByPath: getOrCreateSourceFileByPath, getCancellationToken: () => cancellationToken, getCanonicalFileName, useCaseSensitiveFileNames: () => useCaseSensitivefileNames, @@ -2893,15 +2939,17 @@ namespace ts { }; } + const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings); const newProgram = createProgram(hostCache.getRootFileNames(), newSettings, compilerHost, program); // Release any files we have acquired in the old program but are // not part of the new program. if (program) { const oldSourceFiles = program.getSourceFiles(); + const oldSettingsKey = documentRegistry.getKeyForCompilationSettings(oldSettings); for (const oldSourceFile of oldSourceFiles) { if (!newProgram.getSourceFile(oldSourceFile.fileName) || changesInCompilationSettingsAffectSyntax) { - documentRegistry.releaseDocument(oldSourceFile.fileName, oldSettings); + documentRegistry.releaseDocumentWithKey(oldSourceFile.path, oldSettingsKey); } } } @@ -2918,11 +2966,15 @@ namespace ts { return; function getOrCreateSourceFile(fileName: string): SourceFile { + return getOrCreateSourceFileByPath(fileName, toPath(fileName, currentDirectory, getCanonicalFileName)); + } + + function getOrCreateSourceFileByPath(fileName: string, path: Path): SourceFile { Debug.assert(hostCache !== undefined); // The program is asking for this file, check first if the host can locate it. // If the host can not locate the file, then it does not exist. return undefined // to the program to allow reporting of errors for missing files. - const hostFileInformation = hostCache.getOrCreateEntry(fileName); + const hostFileInformation = hostCache.getOrCreateEntryByPath(fileName, path); if (!hostFileInformation) { return undefined; } @@ -2932,7 +2984,7 @@ namespace ts { // can not be reused. we have to dump all syntax trees and create new ones. if (!changesInCompilationSettingsAffectSyntax) { // Check if the old program had this file already - const oldSourceFile = program && program.getSourceFile(fileName); + const oldSourceFile = program && program.getSourceFileByPath(path); if (oldSourceFile) { // We already had a source file for this file name. Go to the registry to // ensure that we get the right up to date version of it. We need this to @@ -2959,16 +3011,16 @@ namespace ts { // We do not support the scenario where a host can modify a registered // file's script kind, i.e. in one project some file is treated as ".ts" // and in another as ".js" - Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + fileName); + Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + path); - return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); + return documentRegistry.updateDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } // We didn't already have the file. Fall through and acquire it from the registry. } // Could not find this file in the old program, create a new SourceFile for it. - return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); + return documentRegistry.acquireDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } function sourceFileUpToDate(sourceFile: SourceFile): boolean {