Skip to content

Commit cd11d3d

Browse files
committed
Merge pull request #8560 from Microsoft/perfWork
expose code path that will use Path type to avoid redundant string conversions
2 parents d68cd20 + 5e94c76 commit cd11d3d

File tree

4 files changed

+111
-25
lines changed

4 files changed

+111
-25
lines changed

src/compiler/program.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,7 @@ namespace ts {
10391039
program = {
10401040
getRootFileNames: () => rootNames,
10411041
getSourceFile,
1042+
getSourceFileByPath,
10421043
getSourceFiles: () => files,
10431044
getCompilerOptions: () => options,
10441045
getSyntacticDiagnostics,
@@ -1115,7 +1116,11 @@ namespace ts {
11151116
(oldOptions.allowJs !== options.allowJs) ||
11161117
(oldOptions.rootDir !== options.rootDir) ||
11171118
(oldOptions.typesSearchPaths !== options.typesSearchPaths) ||
1118-
(oldOptions.configFilePath !== options.configFilePath)) {
1119+
(oldOptions.configFilePath !== options.configFilePath) ||
1120+
(oldOptions.baseUrl !== options.baseUrl) ||
1121+
(oldOptions.typesRoot !== options.typesRoot) ||
1122+
!arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) ||
1123+
!mapIsEqualTo(oldOptions.paths, options.paths)) {
11191124
return false;
11201125
}
11211126

@@ -1137,7 +1142,10 @@ namespace ts {
11371142
const modifiedSourceFiles: SourceFile[] = [];
11381143

11391144
for (const oldSourceFile of oldProgram.getSourceFiles()) {
1140-
let newSourceFile = host.getSourceFile(oldSourceFile.fileName, options.target);
1145+
let newSourceFile = host.getSourceFileByPath
1146+
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
1147+
: host.getSourceFile(oldSourceFile.fileName, options.target);
1148+
11411149
if (!newSourceFile) {
11421150
return false;
11431151
}
@@ -1232,6 +1240,7 @@ namespace ts {
12321240
getCurrentDirectory: () => currentDirectory,
12331241
getNewLine: () => host.getNewLine(),
12341242
getSourceFile: program.getSourceFile,
1243+
getSourceFileByPath: program.getSourceFileByPath,
12351244
getSourceFiles: program.getSourceFiles,
12361245
writeFile: writeFileCallback || (
12371246
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
@@ -1307,7 +1316,11 @@ namespace ts {
13071316
}
13081317

13091318
function getSourceFile(fileName: string): SourceFile {
1310-
return filesByName.get(toPath(fileName, currentDirectory, getCanonicalFileName));
1319+
return getSourceFileByPath(toPath(fileName, currentDirectory, getCanonicalFileName));
1320+
}
1321+
1322+
function getSourceFileByPath(path: Path): SourceFile {
1323+
return filesByName.get(path);
13111324
}
13121325

13131326
function getDiagnosticsHelper(

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,7 @@ namespace ts {
16271627
export interface ScriptReferenceHost {
16281628
getCompilerOptions(): CompilerOptions;
16291629
getSourceFile(fileName: string): SourceFile;
1630+
getSourceFileByPath(path: Path): SourceFile;
16301631
getCurrentDirectory(): string;
16311632
}
16321633

@@ -2813,6 +2814,7 @@ namespace ts {
28132814

28142815
export interface CompilerHost extends ModuleResolutionHost {
28152816
getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
2817+
getSourceFileByPath?(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
28162818
getCancellationToken?(): CancellationToken;
28172819
getDefaultLibFileName(options: CompilerOptions): string;
28182820
getDefaultLibLocation?(): string;

src/compiler/utilities.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,25 @@ namespace ts {
8484
return node.end - node.pos;
8585
}
8686

87+
export function mapIsEqualTo<T>(map1: Map<T>, map2: Map<T>): boolean {
88+
if (!map1 || !map2) {
89+
return map1 === map2;
90+
}
91+
return containsAll(map1, map2) && containsAll(map2, map1);
92+
}
93+
94+
function containsAll<T>(map: Map<T>, other: Map<T>): boolean {
95+
for (const key in map) {
96+
if (!hasProperty(map, key)) {
97+
continue;
98+
}
99+
if (!hasProperty(other, key) || map[key] !== other[key]) {
100+
return false;
101+
}
102+
}
103+
return true;
104+
}
105+
87106
export function arrayIsEqualTo<T>(array1: T[], array2: T[], equaler?: (a: T, b: T) => boolean): boolean {
88107
if (!array1 || !array2) {
89108
return array1 === array2;

src/services/services.ts

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,15 @@ namespace ts {
14811481
version: string,
14821482
scriptKind?: ScriptKind): SourceFile;
14831483

1484+
acquireDocumentWithKey(
1485+
fileName: string,
1486+
path: Path,
1487+
compilationSettings: CompilerOptions,
1488+
key: DocumentRegistryBucketKey,
1489+
scriptSnapshot: IScriptSnapshot,
1490+
version: string,
1491+
scriptKind?: ScriptKind): SourceFile;
1492+
14841493
/**
14851494
* Request an updated version of an already existing SourceFile with a given fileName
14861495
* and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
@@ -1500,6 +1509,16 @@ namespace ts {
15001509
version: string,
15011510
scriptKind?: ScriptKind): SourceFile;
15021511

1512+
updateDocumentWithKey(
1513+
fileName: string,
1514+
path: Path,
1515+
compilationSettings: CompilerOptions,
1516+
key: DocumentRegistryBucketKey,
1517+
scriptSnapshot: IScriptSnapshot,
1518+
version: string,
1519+
scriptKind?: ScriptKind): SourceFile;
1520+
1521+
getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey;
15031522
/**
15041523
* Informs the DocumentRegistry that a file is not needed any longer.
15051524
*
@@ -1511,9 +1530,13 @@ namespace ts {
15111530
*/
15121531
releaseDocument(fileName: string, compilationSettings: CompilerOptions): void;
15131532

1533+
releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void;
1534+
15141535
reportStats(): string;
15151536
}
15161537

1538+
export type DocumentRegistryBucketKey = string & { __bucketKey: any };
1539+
15171540
// TODO: move these to enums
15181541
export namespace ScriptElementKind {
15191542
export const unknown = "";
@@ -1783,11 +1806,13 @@ namespace ts {
17831806

17841807
public getOrCreateEntry(fileName: string): HostFileInformation {
17851808
const path = toPath(fileName, this.currentDirectory, this.getCanonicalFileName);
1786-
if (this.contains(path)) {
1787-
return this.getEntry(path);
1788-
}
1809+
return this.getOrCreateEntryByPath(fileName, path);
1810+
}
17891811

1790-
return this.createEntry(fileName, path);
1812+
public getOrCreateEntryByPath(fileName: string, path: Path): HostFileInformation {
1813+
return this.contains(path)
1814+
? this.getEntry(path)
1815+
: this.createEntry(fileName, path);
17911816
}
17921817

17931818
public getRootFileNames(): string[] {
@@ -2041,12 +2066,11 @@ namespace ts {
20412066
const buckets: Map<FileMap<DocumentRegistryEntry>> = {};
20422067
const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
20432068

2044-
function getKeyFromCompilationSettings(settings: CompilerOptions): string {
2045-
return "_" + settings.target + "|" + settings.module + "|" + settings.noResolve + "|" + settings.jsx + +"|" + settings.allowJs;
2069+
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
2070+
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${settings.typesRoot}|${settings.typesSearchPaths}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
20462071
}
20472072

2048-
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
2049-
const key = getKeyFromCompilationSettings(settings);
2073+
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
20502074
let bucket = lookUp(buckets, key);
20512075
if (!bucket && createIfMissing) {
20522076
buckets[key] = bucket = createFileMap<DocumentRegistryEntry>();
@@ -2075,23 +2099,36 @@ namespace ts {
20752099
}
20762100

20772101
function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
2078-
return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true, scriptKind);
2102+
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
2103+
const key = getKeyForCompilationSettings(compilationSettings);
2104+
return acquireDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
2105+
}
2106+
2107+
function acquireDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
2108+
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ true, scriptKind);
20792109
}
20802110

20812111
function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
2082-
return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false, scriptKind);
2112+
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
2113+
const key = getKeyForCompilationSettings(compilationSettings);
2114+
return updateDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
2115+
}
2116+
2117+
function updateDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
2118+
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ false, scriptKind);
20832119
}
20842120

20852121
function acquireOrUpdateDocument(
20862122
fileName: string,
2123+
path: Path,
20872124
compilationSettings: CompilerOptions,
2125+
key: DocumentRegistryBucketKey,
20882126
scriptSnapshot: IScriptSnapshot,
20892127
version: string,
20902128
acquiring: boolean,
20912129
scriptKind?: ScriptKind): SourceFile {
20922130

2093-
const bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true);
2094-
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
2131+
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true);
20952132
let entry = bucket.get(path);
20962133
if (!entry) {
20972134
Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?");
@@ -2129,10 +2166,14 @@ namespace ts {
21292166
}
21302167

21312168
function releaseDocument(fileName: string, compilationSettings: CompilerOptions): void {
2132-
const bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/false);
2133-
Debug.assert(bucket !== undefined);
2134-
21352169
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
2170+
const key = getKeyForCompilationSettings(compilationSettings);
2171+
return releaseDocumentWithKey(path, key);
2172+
}
2173+
2174+
function releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void {
2175+
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/false);
2176+
Debug.assert(bucket !== undefined);
21362177

21372178
const entry = bucket.get(path);
21382179
entry.languageServiceRefCount--;
@@ -2145,9 +2186,13 @@ namespace ts {
21452186

21462187
return {
21472188
acquireDocument,
2189+
acquireDocumentWithKey,
21482190
updateDocument,
2191+
updateDocumentWithKey,
21492192
releaseDocument,
2150-
reportStats
2193+
releaseDocumentWithKey,
2194+
reportStats,
2195+
getKeyForCompilationSettings
21512196
};
21522197
}
21532198

@@ -2858,6 +2903,7 @@ namespace ts {
28582903
// Now create a new compiler
28592904
const compilerHost: CompilerHost = {
28602905
getSourceFile: getOrCreateSourceFile,
2906+
getSourceFileByPath: getOrCreateSourceFileByPath,
28612907
getCancellationToken: () => cancellationToken,
28622908
getCanonicalFileName,
28632909
useCaseSensitiveFileNames: () => useCaseSensitivefileNames,
@@ -2893,15 +2939,17 @@ namespace ts {
28932939
};
28942940
}
28952941

2942+
const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
28962943
const newProgram = createProgram(hostCache.getRootFileNames(), newSettings, compilerHost, program);
28972944

28982945
// Release any files we have acquired in the old program but are
28992946
// not part of the new program.
29002947
if (program) {
29012948
const oldSourceFiles = program.getSourceFiles();
2949+
const oldSettingsKey = documentRegistry.getKeyForCompilationSettings(oldSettings);
29022950
for (const oldSourceFile of oldSourceFiles) {
29032951
if (!newProgram.getSourceFile(oldSourceFile.fileName) || changesInCompilationSettingsAffectSyntax) {
2904-
documentRegistry.releaseDocument(oldSourceFile.fileName, oldSettings);
2952+
documentRegistry.releaseDocumentWithKey(oldSourceFile.path, oldSettingsKey);
29052953
}
29062954
}
29072955
}
@@ -2918,11 +2966,15 @@ namespace ts {
29182966
return;
29192967

29202968
function getOrCreateSourceFile(fileName: string): SourceFile {
2969+
return getOrCreateSourceFileByPath(fileName, toPath(fileName, currentDirectory, getCanonicalFileName));
2970+
}
2971+
2972+
function getOrCreateSourceFileByPath(fileName: string, path: Path): SourceFile {
29212973
Debug.assert(hostCache !== undefined);
29222974
// The program is asking for this file, check first if the host can locate it.
29232975
// If the host can not locate the file, then it does not exist. return undefined
29242976
// to the program to allow reporting of errors for missing files.
2925-
const hostFileInformation = hostCache.getOrCreateEntry(fileName);
2977+
const hostFileInformation = hostCache.getOrCreateEntryByPath(fileName, path);
29262978
if (!hostFileInformation) {
29272979
return undefined;
29282980
}
@@ -2932,7 +2984,7 @@ namespace ts {
29322984
// can not be reused. we have to dump all syntax trees and create new ones.
29332985
if (!changesInCompilationSettingsAffectSyntax) {
29342986
// Check if the old program had this file already
2935-
const oldSourceFile = program && program.getSourceFile(fileName);
2987+
const oldSourceFile = program && program.getSourceFileByPath(path);
29362988
if (oldSourceFile) {
29372989
// We already had a source file for this file name. Go to the registry to
29382990
// ensure that we get the right up to date version of it. We need this to
@@ -2959,16 +3011,16 @@ namespace ts {
29593011
// We do not support the scenario where a host can modify a registered
29603012
// file's script kind, i.e. in one project some file is treated as ".ts"
29613013
// and in another as ".js"
2962-
Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + fileName);
3014+
Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + path);
29633015

2964-
return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
3016+
return documentRegistry.updateDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
29653017
}
29663018

29673019
// We didn't already have the file. Fall through and acquire it from the registry.
29683020
}
29693021

29703022
// Could not find this file in the old program, create a new SourceFile for it.
2971-
return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
3023+
return documentRegistry.acquireDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
29723024
}
29733025

29743026
function sourceFileUpToDate(sourceFile: SourceFile): boolean {

0 commit comments

Comments
 (0)