Skip to content

Commit 113b7ed

Browse files
committed
Merge pull request #3368 from Microsoft/fileMap
introduce FileMap to store mappings with filenames as keys
2 parents 7607679 + ef54047 commit 113b7ed

File tree

5 files changed

+92
-35
lines changed

5 files changed

+92
-35
lines changed

src/compiler/core.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,42 @@ module ts {
1515
True = -1
1616
}
1717

18+
export function createFileMap<T>(getCanonicalFileName: (fileName: string) => string): FileMap<T> {
19+
let files: Map<T> = {};
20+
return {
21+
get,
22+
set,
23+
contains,
24+
delete: deleteItem,
25+
forEachValue: forEachValueInMap
26+
}
27+
28+
function set(fileName: string, value: T) {
29+
files[normalizeKey(fileName)] = value;
30+
}
31+
32+
function get(fileName: string) {
33+
return files[normalizeKey(fileName)];
34+
}
35+
36+
function contains(fileName: string) {
37+
return hasProperty(files, normalizeKey(fileName));
38+
}
39+
40+
function deleteItem (fileName: string) {
41+
let key = normalizeKey(fileName);
42+
delete files[key];
43+
}
44+
45+
function forEachValueInMap(f: (value: T) => void) {
46+
forEachValue(files, f);
47+
}
48+
49+
function normalizeKey(key: string) {
50+
return getCanonicalFileName(normalizeSlashes(key));
51+
}
52+
}
53+
1854
export const enum Comparison {
1955
LessThan = -1,
2056
EqualTo = 0,

src/compiler/program.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ module ts {
143143
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost): Program {
144144
let program: Program;
145145
let files: SourceFile[] = [];
146-
let filesByName: Map<SourceFile> = {};
147146
let diagnostics = createDiagnosticCollection();
148147
let seenNoDefaultLib = options.noLib;
149148
let commonSourceDirectory: string;
@@ -153,6 +152,8 @@ module ts {
153152
let start = new Date().getTime();
154153

155154
host = host || createCompilerHost(options);
155+
let filesByName = createFileMap<SourceFile>(host.getCanonicalFileName);
156+
156157
forEach(rootNames, name => processRootFile(name, false));
157158
if (!seenNoDefaultLib) {
158159
processRootFile(host.getDefaultLibFileName(options), true);
@@ -233,8 +234,7 @@ module ts {
233234
}
234235

235236
function getSourceFile(fileName: string) {
236-
fileName = host.getCanonicalFileName(normalizeSlashes(fileName));
237-
return hasProperty(filesByName, fileName) ? filesByName[fileName] : undefined;
237+
return filesByName.get(fileName);
238238
}
239239

240240
function getDiagnosticsHelper(sourceFile: SourceFile, getDiagnostics: (sourceFile: SourceFile) => Diagnostic[]): Diagnostic[] {
@@ -359,19 +359,19 @@ module ts {
359359
// Get source file from normalized fileName
360360
function findSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile {
361361
let canonicalName = host.getCanonicalFileName(normalizeSlashes(fileName));
362-
if (hasProperty(filesByName, canonicalName)) {
362+
if (filesByName.contains(canonicalName)) {
363363
// We've already looked for this file, use cached result
364364
return getSourceFileFromCache(fileName, canonicalName, /*useAbsolutePath*/ false);
365365
}
366366
else {
367367
let normalizedAbsolutePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
368368
let canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath);
369-
if (hasProperty(filesByName, canonicalAbsolutePath)) {
369+
if (filesByName.contains(canonicalAbsolutePath)) {
370370
return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true);
371371
}
372372

373373
// We haven't looked for this file, do so now and cache result
374-
let file = filesByName[canonicalName] = host.getSourceFile(fileName, options.target, hostErrorMessage => {
374+
let file = host.getSourceFile(fileName, options.target, hostErrorMessage => {
375375
if (refFile) {
376376
diagnostics.add(createFileDiagnostic(refFile, refStart, refLength,
377377
Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
@@ -380,11 +380,12 @@ module ts {
380380
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
381381
}
382382
});
383+
filesByName.set(canonicalName, file);
383384
if (file) {
384385
seenNoDefaultLib = seenNoDefaultLib || file.hasNoDefaultLib;
385386

386387
// Set the source file for normalized absolute path
387-
filesByName[canonicalAbsolutePath] = file;
388+
filesByName.set(canonicalAbsolutePath, file);
388389

389390
if (!options.noResolve) {
390391
let basePath = getDirectoryPath(fileName);
@@ -403,7 +404,7 @@ module ts {
403404
}
404405

405406
function getSourceFileFromCache(fileName: string, canonicalName: string, useAbsolutePath: boolean): SourceFile {
406-
let file = filesByName[canonicalName];
407+
let file = filesByName.get(canonicalName);
407408
if (file && host.useCaseSensitiveFileNames()) {
408409
let sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory()) : file.fileName;
409410
if (canonicalName !== sourceFileName) {

src/compiler/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ module ts {
33
[index: string]: T;
44
}
55

6+
export interface FileMap<T> {
7+
get(fileName: string): T;
8+
set(fileName: string, value: T): void;
9+
contains(fileName: string): boolean;
10+
delete(fileName: string): void;
11+
forEachValue(f: (v: T) => void): void;
12+
}
13+
614
export interface TextRange {
715
pos: number;
816
end: number;

src/services/services.ts

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,7 @@ module ts {
960960
log? (s: string): void;
961961
trace? (s: string): void;
962962
error? (s: string): void;
963+
useCaseSensitiveFileNames? (): boolean;
963964
}
964965

965966
//
@@ -1632,12 +1633,12 @@ module ts {
16321633
// at each language service public entry point, since we don't know when
16331634
// set of scripts handled by the host changes.
16341635
class HostCache {
1635-
private fileNameToEntry: Map<HostFileInformation>;
1636+
private fileNameToEntry: FileMap<HostFileInformation>;
16361637
private _compilationSettings: CompilerOptions;
16371638

1638-
constructor(private host: LanguageServiceHost, private getCanonicalFileName: (fileName: string) => string) {
1639+
constructor(private host: LanguageServiceHost, getCanonicalFileName: (fileName: string) => string) {
16391640
// script id => script index
1640-
this.fileNameToEntry = {};
1641+
this.fileNameToEntry = createFileMap<HostFileInformation>(getCanonicalFileName);
16411642

16421643
// Initialize the list with the root file names
16431644
let rootFileNames = host.getScriptFileNames();
@@ -1653,10 +1654,6 @@ module ts {
16531654
return this._compilationSettings;
16541655
}
16551656

1656-
private normalizeFileName(fileName: string): string {
1657-
return this.getCanonicalFileName(normalizeSlashes(fileName));
1658-
}
1659-
16601657
private createEntry(fileName: string) {
16611658
let entry: HostFileInformation;
16621659
let scriptSnapshot = this.host.getScriptSnapshot(fileName);
@@ -1668,15 +1665,16 @@ module ts {
16681665
};
16691666
}
16701667

1671-
return this.fileNameToEntry[this.normalizeFileName(fileName)] = entry;
1668+
this.fileNameToEntry.set(fileName, entry);
1669+
return entry;
16721670
}
16731671

16741672
private getEntry(fileName: string): HostFileInformation {
1675-
return lookUp(this.fileNameToEntry, this.normalizeFileName(fileName));
1673+
return this.fileNameToEntry.get(fileName);
16761674
}
16771675

16781676
private contains(fileName: string): boolean {
1679-
return hasProperty(this.fileNameToEntry, this.normalizeFileName(fileName));
1677+
return this.fileNameToEntry.contains(fileName);
16801678
}
16811679

16821680
public getOrCreateEntry(fileName: string): HostFileInformation {
@@ -1690,10 +1688,9 @@ module ts {
16901688
public getRootFileNames(): string[] {
16911689
let fileNames: string[] = [];
16921690

1693-
forEachKey(this.fileNameToEntry, key => {
1694-
let entry = this.getEntry(key);
1695-
if (entry) {
1696-
fileNames.push(entry.hostFileName);
1691+
this.fileNameToEntry.forEachValue(value => {
1692+
if (value) {
1693+
fileNames.push(value.hostFileName);
16971694
}
16981695
});
16991696

@@ -1885,20 +1882,28 @@ module ts {
18851882
return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents:*/ true);
18861883
}
18871884

1888-
export function createDocumentRegistry(): DocumentRegistry {
1885+
function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string {
1886+
return useCaseSensitivefileNames
1887+
? ((fileName) => fileName)
1888+
: ((fileName) => fileName.toLowerCase());
1889+
}
1890+
1891+
1892+
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean): DocumentRegistry {
18891893
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
18901894
// for those settings.
1891-
let buckets: Map<Map<DocumentRegistryEntry>> = {};
1895+
let buckets: Map<FileMap<DocumentRegistryEntry>> = {};
1896+
let getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
18921897

18931898
function getKeyFromCompilationSettings(settings: CompilerOptions): string {
18941899
return "_" + settings.target; // + "|" + settings.propagateEnumConstantoString()
18951900
}
18961901

1897-
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): Map<DocumentRegistryEntry> {
1902+
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
18981903
let key = getKeyFromCompilationSettings(settings);
18991904
let bucket = lookUp(buckets, key);
19001905
if (!bucket && createIfMissing) {
1901-
buckets[key] = bucket = {};
1906+
buckets[key] = bucket = createFileMap<DocumentRegistryEntry>(getCanonicalFileName);
19021907
}
19031908
return bucket;
19041909
}
@@ -1908,7 +1913,7 @@ module ts {
19081913
let entries = lookUp(buckets, name);
19091914
let sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
19101915
for (let i in entries) {
1911-
let entry = entries[i];
1916+
let entry = entries.get(i);
19121917
sourceFiles.push({
19131918
name: i,
19141919
refCount: entry.languageServiceRefCount,
@@ -1940,18 +1945,19 @@ module ts {
19401945
acquiring: boolean): SourceFile {
19411946

19421947
let bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true);
1943-
let entry = lookUp(bucket, fileName);
1948+
let entry = bucket.get(fileName);
19441949
if (!entry) {
19451950
Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?");
19461951

19471952
// Have never seen this file with these settings. Create a new source file for it.
19481953
let sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents:*/ false);
19491954

1950-
bucket[fileName] = entry = {
1955+
entry = {
19511956
sourceFile: sourceFile,
19521957
languageServiceRefCount: 0,
19531958
owners: []
19541959
};
1960+
bucket.set(fileName, entry);
19551961
}
19561962
else {
19571963
// We have an entry for this file. However, it may be for a different version of
@@ -1979,12 +1985,12 @@ module ts {
19791985
let bucket = getBucketForCompilationSettings(compilationSettings, false);
19801986
Debug.assert(bucket !== undefined);
19811987

1982-
let entry = lookUp(bucket, fileName);
1988+
let entry = bucket.get(fileName);
19831989
entry.languageServiceRefCount--;
19841990

19851991
Debug.assert(entry.languageServiceRefCount >= 0);
19861992
if (entry.languageServiceRefCount === 0) {
1987-
delete bucket[fileName];
1993+
bucket.delete(fileName);
19881994
}
19891995
}
19901996

@@ -2412,9 +2418,7 @@ module ts {
24122418
}
24132419
}
24142420

2415-
function getCanonicalFileName(fileName: string) {
2416-
return useCaseSensitivefileNames ? fileName : fileName.toLowerCase();
2417-
}
2421+
let getCanonicalFileName = createGetCanonicalFileName(useCaseSensitivefileNames);
24182422

24192423
function getValidSourceFile(fileName: string): SourceFile {
24202424
fileName = normalizeSlashes(fileName);

src/services/shims.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ module ts {
5656
getDefaultLibFileName(options: string): string;
5757
getNewLine?(): string;
5858
getProjectVersion?(): string;
59+
useCaseSensitiveFileNames?(): boolean;
5960
}
6061

6162
/** Public interface of the the of a config service shim instance.*/
@@ -270,6 +271,10 @@ module ts {
270271
return this.shimHost.getProjectVersion();
271272
}
272273

274+
public useCaseSensitiveFileNames(): boolean {
275+
return this.shimHost.useCaseSensitiveFileNames ? this.shimHost.useCaseSensitiveFileNames() : false;
276+
}
277+
273278
public getCompilationSettings(): CompilerOptions {
274279
var settingsJson = this.shimHost.getCompilationSettings();
275280
if (settingsJson == null || settingsJson == "") {
@@ -909,7 +914,7 @@ module ts {
909914

910915
export class TypeScriptServicesFactory implements ShimFactory {
911916
private _shims: Shim[] = [];
912-
private documentRegistry: DocumentRegistry = createDocumentRegistry();
917+
private documentRegistry: DocumentRegistry;
913918

914919
/*
915920
* Returns script API version.
@@ -920,6 +925,9 @@ module ts {
920925

921926
public createLanguageServiceShim(host: LanguageServiceShimHost): LanguageServiceShim {
922927
try {
928+
if (this.documentRegistry === undefined) {
929+
this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
930+
}
923931
var hostAdapter = new LanguageServiceShimHostAdapter(host);
924932
var languageService = createLanguageService(hostAdapter, this.documentRegistry);
925933
return new LanguageServiceShimObject(this, host, languageService);

0 commit comments

Comments
 (0)