Skip to content

Commit 937927a

Browse files
committed
Merge pull request #3498 from Microsoft/portFileMap
Port FileMap into release-1.5
2 parents ab61bf2 + 522248a commit 937927a

File tree

5 files changed

+92
-35
lines changed

5 files changed

+92
-35
lines changed

src/compiler/core.ts

+36
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+
remove,
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 remove (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

+9-8
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[] {
@@ -352,19 +352,19 @@ module ts {
352352
// Get source file from normalized fileName
353353
function findSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile {
354354
let canonicalName = host.getCanonicalFileName(normalizeSlashes(fileName));
355-
if (hasProperty(filesByName, canonicalName)) {
355+
if (filesByName.contains(canonicalName)) {
356356
// We've already looked for this file, use cached result
357357
return getSourceFileFromCache(fileName, canonicalName, /*useAbsolutePath*/ false);
358358
}
359359
else {
360360
let normalizedAbsolutePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
361361
let canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath);
362-
if (hasProperty(filesByName, canonicalAbsolutePath)) {
362+
if (filesByName.contains(canonicalAbsolutePath)) {
363363
return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true);
364364
}
365365

366366
// We haven't looked for this file, do so now and cache result
367-
let file = filesByName[canonicalName] = host.getSourceFile(fileName, options.target, hostErrorMessage => {
367+
let file = host.getSourceFile(fileName, options.target, hostErrorMessage => {
368368
if (refFile) {
369369
diagnostics.add(createFileDiagnostic(refFile, refStart, refLength,
370370
Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
@@ -373,11 +373,12 @@ module ts {
373373
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage));
374374
}
375375
});
376+
filesByName.set(canonicalName, file);
376377
if (file) {
377378
seenNoDefaultLib = seenNoDefaultLib || file.hasNoDefaultLib;
378379

379380
// Set the source file for normalized absolute path
380-
filesByName[canonicalAbsolutePath] = file;
381+
filesByName.set(canonicalAbsolutePath, file);
381382

382383
if (!options.noResolve) {
383384
let basePath = getDirectoryPath(fileName);
@@ -396,7 +397,7 @@ module ts {
396397
}
397398

398399
function getSourceFileFromCache(fileName: string, canonicalName: string, useAbsolutePath: boolean): SourceFile {
399-
let file = filesByName[canonicalName];
400+
let file = filesByName.get(canonicalName);
400401
if (file && host.useCaseSensitiveFileNames()) {
401402
let sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory()) : file.fileName;
402403
if (canonicalName !== sourceFileName) {

src/compiler/types.ts

+8
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+
remove(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

+30-26
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
//
@@ -1630,12 +1631,12 @@ module ts {
16301631
// at each language service public entry point, since we don't know when
16311632
// set of scripts handled by the host changes.
16321633
class HostCache {
1633-
private fileNameToEntry: Map<HostFileInformation>;
1634+
private fileNameToEntry: FileMap<HostFileInformation>;
16341635
private _compilationSettings: CompilerOptions;
16351636

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

16401641
// Initialize the list with the root file names
16411642
let rootFileNames = host.getScriptFileNames();
@@ -1651,10 +1652,6 @@ module ts {
16511652
return this._compilationSettings;
16521653
}
16531654

1654-
private normalizeFileName(fileName: string): string {
1655-
return this.getCanonicalFileName(normalizeSlashes(fileName));
1656-
}
1657-
16581655
private createEntry(fileName: string) {
16591656
let entry: HostFileInformation;
16601657
let scriptSnapshot = this.host.getScriptSnapshot(fileName);
@@ -1666,15 +1663,16 @@ module ts {
16661663
};
16671664
}
16681665

1669-
return this.fileNameToEntry[this.normalizeFileName(fileName)] = entry;
1666+
this.fileNameToEntry.set(fileName, entry);
1667+
return entry;
16701668
}
16711669

16721670
private getEntry(fileName: string): HostFileInformation {
1673-
return lookUp(this.fileNameToEntry, this.normalizeFileName(fileName));
1671+
return this.fileNameToEntry.get(fileName);
16741672
}
16751673

16761674
private contains(fileName: string): boolean {
1677-
return hasProperty(this.fileNameToEntry, this.normalizeFileName(fileName));
1675+
return this.fileNameToEntry.contains(fileName);
16781676
}
16791677

16801678
public getOrCreateEntry(fileName: string): HostFileInformation {
@@ -1688,10 +1686,9 @@ module ts {
16881686
public getRootFileNames(): string[] {
16891687
let fileNames: string[] = [];
16901688

1691-
forEachKey(this.fileNameToEntry, key => {
1692-
let entry = this.getEntry(key);
1693-
if (entry) {
1694-
fileNames.push(entry.hostFileName);
1689+
this.fileNameToEntry.forEachValue(value => {
1690+
if (value) {
1691+
fileNames.push(value.hostFileName);
16951692
}
16961693
});
16971694

@@ -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.remove(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

+9-1
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)