Skip to content

Commit 269ae3a

Browse files
committed
introduce FileMap to store mappings with filenames as keys
1 parent bf95bff commit 269ae3a

File tree

4 files changed

+80
-35
lines changed

4 files changed

+80
-35
lines changed

src/compiler/core.ts

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

18+
export class FileMap<T> {
19+
private files: Map<T> = {};
20+
21+
constructor(private getCanonicalFileName: (fileName: string) => string) {
22+
}
23+
24+
public set(fileName: string, value: T) {
25+
this.files[this.normalizeKey(fileName)] = value;
26+
}
27+
28+
public get(fileName: string) {
29+
return this.files[this.normalizeKey(fileName)];
30+
}
31+
32+
public contains(fileName: string) {
33+
return hasProperty(this.files, this.normalizeKey(fileName));
34+
}
35+
36+
public delete(fileName: string) {
37+
let key = this.normalizeKey(fileName);
38+
delete this.files[key];
39+
}
40+
41+
public forEachValue(f: (value: T) => void) {
42+
forEachValue(this.files, f);
43+
}
44+
45+
private normalizeKey(key: string) {
46+
return this.getCanonicalFileName(normalizeSlashes(key));
47+
}
48+
}
49+
1850
export const enum Comparison {
1951
LessThan = -1,
2052
EqualTo = 0,

src/compiler/program.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ module ts {
149149
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost): Program {
150150
let program: Program;
151151
let files: SourceFile[] = [];
152-
let filesByName: Map<SourceFile> = {};
153152
let diagnostics = createDiagnosticCollection();
154153
let seenNoDefaultLib = options.noLib;
155154
let commonSourceDirectory: string;
@@ -159,6 +158,8 @@ module ts {
159158
let start = new Date().getTime();
160159

161160
host = host || createCompilerHost(options);
161+
let filesByName: FileMap<SourceFile> = new FileMap<SourceFile>(host.getCanonicalFileName);
162+
162163
forEach(rootNames, name => processRootFile(name, false));
163164
if (!seenNoDefaultLib) {
164165
processRootFile(host.getDefaultLibFileName(options), true);
@@ -238,8 +239,7 @@ module ts {
238239
}
239240

240241
function getSourceFile(fileName: string) {
241-
fileName = host.getCanonicalFileName(normalizeSlashes(fileName));
242-
return hasProperty(filesByName, fileName) ? filesByName[fileName] : undefined;
242+
return filesByName.get(fileName);
243243
}
244244

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

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

385386
// Set the source file for normalized absolute path
386-
filesByName[canonicalAbsolutePath] = file;
387+
filesByName.set(canonicalAbsolutePath, file);
387388

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

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

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 = new FileMap<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

@@ -1873,20 +1870,28 @@ module ts {
18731870
return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents:*/ true);
18741871
}
18751872

1876-
export function createDocumentRegistry(): DocumentRegistry {
1873+
function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string {
1874+
return useCaseSensitivefileNames
1875+
? ((fileName) => fileName)
1876+
: ((fileName) => fileName.toLowerCase());
1877+
}
1878+
1879+
1880+
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean): DocumentRegistry {
18771881
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
18781882
// for those settings.
1879-
let buckets: Map<Map<DocumentRegistryEntry>> = {};
1883+
let buckets: Map<FileMap<DocumentRegistryEntry>> = {};
1884+
let getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames || false);
18801885

18811886
function getKeyFromCompilationSettings(settings: CompilerOptions): string {
18821887
return "_" + settings.target; // + "|" + settings.propagateEnumConstantoString()
18831888
}
18841889

1885-
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): Map<DocumentRegistryEntry> {
1890+
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
18861891
let key = getKeyFromCompilationSettings(settings);
18871892
let bucket = lookUp(buckets, key);
18881893
if (!bucket && createIfMissing) {
1889-
buckets[key] = bucket = {};
1894+
buckets[key] = bucket = new FileMap<DocumentRegistryEntry>(getCanonicalFileName);
18901895
}
18911896
return bucket;
18921897
}
@@ -1896,7 +1901,7 @@ module ts {
18961901
let entries = lookUp(buckets, name);
18971902
let sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
18981903
for (let i in entries) {
1899-
let entry = entries[i];
1904+
let entry = entries.get(i);
19001905
sourceFiles.push({
19011906
name: i,
19021907
refCount: entry.languageServiceRefCount,
@@ -1928,18 +1933,19 @@ module ts {
19281933
acquiring: boolean): SourceFile {
19291934

19301935
let bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true);
1931-
let entry = lookUp(bucket, fileName);
1936+
let entry = bucket.get(fileName);
19321937
if (!entry) {
19331938
Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?");
19341939

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

1938-
bucket[fileName] = entry = {
1943+
entry = {
19391944
sourceFile: sourceFile,
19401945
languageServiceRefCount: 0,
19411946
owners: []
19421947
};
1948+
bucket.set(fileName, entry);
19431949
}
19441950
else {
19451951
// We have an entry for this file. However, it may be for a different version of
@@ -1967,12 +1973,12 @@ module ts {
19671973
let bucket = getBucketForCompilationSettings(compilationSettings, false);
19681974
Debug.assert(bucket !== undefined);
19691975

1970-
let entry = lookUp(bucket, fileName);
1976+
let entry = bucket.get(fileName);
19711977
entry.languageServiceRefCount--;
19721978

19731979
Debug.assert(entry.languageServiceRefCount >= 0);
19741980
if (entry.languageServiceRefCount === 0) {
1975-
delete bucket[fileName];
1981+
bucket.delete(fileName);
19761982
}
19771983
}
19781984

@@ -2400,9 +2406,7 @@ module ts {
24002406
}
24012407
}
24022408

2403-
function getCanonicalFileName(fileName: string) {
2404-
return useCaseSensitivefileNames ? fileName : fileName.toLowerCase();
2405-
}
2409+
let getCanonicalFileName = createGetCanonicalFileName(useCaseSensitivefileNames);
24062410

24072411
function getValidSourceFile(fileName: string): SourceFile {
24082412
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.useCaseSensitiveFileNames();
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)