Skip to content

Commit

Permalink
introduce FileMap to store mappings with filenames as keys
Browse files Browse the repository at this point in the history
  • Loading branch information
vladima committed Jun 3, 2015
1 parent bf95bff commit 269ae3a
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 35 deletions.
32 changes: 32 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,38 @@ module ts {
True = -1
}

export class FileMap<T> {
private files: Map<T> = {};

constructor(private getCanonicalFileName: (fileName: string) => string) {
}

public set(fileName: string, value: T) {
this.files[this.normalizeKey(fileName)] = value;
}

public get(fileName: string) {
return this.files[this.normalizeKey(fileName)];
}

public contains(fileName: string) {
return hasProperty(this.files, this.normalizeKey(fileName));
}

public delete(fileName: string) {
let key = this.normalizeKey(fileName);
delete this.files[key];
}

public forEachValue(f: (value: T) => void) {
forEachValue(this.files, f);
}

private normalizeKey(key: string) {
return this.getCanonicalFileName(normalizeSlashes(key));
}
}

export const enum Comparison {
LessThan = -1,
EqualTo = 0,
Expand Down
17 changes: 9 additions & 8 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ module ts {
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost): Program {
let program: Program;
let files: SourceFile[] = [];
let filesByName: Map<SourceFile> = {};
let diagnostics = createDiagnosticCollection();
let seenNoDefaultLib = options.noLib;
let commonSourceDirectory: string;
Expand All @@ -159,6 +158,8 @@ module ts {
let start = new Date().getTime();

host = host || createCompilerHost(options);
let filesByName: FileMap<SourceFile> = new FileMap<SourceFile>(host.getCanonicalFileName);

This comment has been minimized.

Copy link
@Arnavion

Arnavion Jun 5, 2015

Contributor

This breaks if host.getCanonicalFileName needs a this reference. See #1545


forEach(rootNames, name => processRootFile(name, false));
if (!seenNoDefaultLib) {
processRootFile(host.getDefaultLibFileName(options), true);
Expand Down Expand Up @@ -238,8 +239,7 @@ module ts {
}

function getSourceFile(fileName: string) {
fileName = host.getCanonicalFileName(normalizeSlashes(fileName));
return hasProperty(filesByName, fileName) ? filesByName[fileName] : undefined;
return filesByName.get(fileName);
}

function getDiagnosticsHelper(sourceFile: SourceFile, getDiagnostics: (sourceFile: SourceFile) => Diagnostic[]): Diagnostic[] {
Expand Down Expand Up @@ -358,19 +358,19 @@ module ts {
// Get source file from normalized fileName
function findSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile {
let canonicalName = host.getCanonicalFileName(normalizeSlashes(fileName));
if (hasProperty(filesByName, canonicalName)) {
if (filesByName.contains(canonicalName)) {
// We've already looked for this file, use cached result
return getSourceFileFromCache(fileName, canonicalName, /*useAbsolutePath*/ false);
}
else {
let normalizedAbsolutePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
let canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath);
if (hasProperty(filesByName, canonicalAbsolutePath)) {
if (filesByName.contains(canonicalAbsolutePath)) {
return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true);
}

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

// Set the source file for normalized absolute path
filesByName[canonicalAbsolutePath] = file;
filesByName.set(canonicalAbsolutePath, file);

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

function getSourceFileFromCache(fileName: string, canonicalName: string, useAbsolutePath: boolean): SourceFile {
let file = filesByName[canonicalName];
let file = filesByName.get(canonicalName);
if (file && host.useCaseSensitiveFileNames()) {
let sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.fileName, host.getCurrentDirectory()) : file.fileName;
if (canonicalName !== sourceFileName) {
Expand Down
56 changes: 30 additions & 26 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,7 @@ module ts {
log? (s: string): void;
trace? (s: string): void;
error? (s: string): void;
useCaseSensitiveFileNames? (): boolean;
}

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

constructor(private host: LanguageServiceHost, private getCanonicalFileName: (fileName: string) => string) {
constructor(private host: LanguageServiceHost, getCanonicalFileName: (fileName: string) => string) {
// script id => script index
this.fileNameToEntry = {};
this.fileNameToEntry = new FileMap<HostFileInformation>(getCanonicalFileName);

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

private normalizeFileName(fileName: string): string {
return this.getCanonicalFileName(normalizeSlashes(fileName));
}

private createEntry(fileName: string) {
let entry: HostFileInformation;
let scriptSnapshot = this.host.getScriptSnapshot(fileName);
Expand All @@ -1668,15 +1665,16 @@ module ts {
};
}

return this.fileNameToEntry[this.normalizeFileName(fileName)] = entry;
this.fileNameToEntry.set(fileName, entry);
return entry;
}

private getEntry(fileName: string): HostFileInformation {
return lookUp(this.fileNameToEntry, this.normalizeFileName(fileName));
return this.fileNameToEntry.get(fileName);
}

private contains(fileName: string): boolean {
return hasProperty(this.fileNameToEntry, this.normalizeFileName(fileName));
return this.fileNameToEntry.contains(fileName);
}

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

forEachKey(this.fileNameToEntry, key => {
let entry = this.getEntry(key);
if (entry) {
fileNames.push(entry.hostFileName);
this.fileNameToEntry.forEachValue(value => {
if (value) {
fileNames.push(value.hostFileName);
}
});

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

export function createDocumentRegistry(): DocumentRegistry {
function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string {
return useCaseSensitivefileNames
? ((fileName) => fileName)
: ((fileName) => fileName.toLowerCase());
}


export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean): DocumentRegistry {
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
// for those settings.
let buckets: Map<Map<DocumentRegistryEntry>> = {};
let buckets: Map<FileMap<DocumentRegistryEntry>> = {};
let getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames || false);

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

function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): Map<DocumentRegistryEntry> {
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
let key = getKeyFromCompilationSettings(settings);
let bucket = lookUp(buckets, key);
if (!bucket && createIfMissing) {
buckets[key] = bucket = {};
buckets[key] = bucket = new FileMap<DocumentRegistryEntry>(getCanonicalFileName);
}
return bucket;
}
Expand All @@ -1896,7 +1901,7 @@ module ts {
let entries = lookUp(buckets, name);
let sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
for (let i in entries) {
let entry = entries[i];
let entry = entries.get(i);
sourceFiles.push({
name: i,
refCount: entry.languageServiceRefCount,
Expand Down Expand Up @@ -1928,18 +1933,19 @@ module ts {
acquiring: boolean): SourceFile {

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

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

bucket[fileName] = entry = {
entry = {
sourceFile: sourceFile,
languageServiceRefCount: 0,
owners: []
};
bucket.set(fileName, entry);
}
else {
// We have an entry for this file. However, it may be for a different version of
Expand Down Expand Up @@ -1967,12 +1973,12 @@ module ts {
let bucket = getBucketForCompilationSettings(compilationSettings, false);
Debug.assert(bucket !== undefined);

let entry = lookUp(bucket, fileName);
let entry = bucket.get(fileName);
entry.languageServiceRefCount--;

Debug.assert(entry.languageServiceRefCount >= 0);
if (entry.languageServiceRefCount === 0) {
delete bucket[fileName];
bucket.delete(fileName);
}
}

Expand Down Expand Up @@ -2400,9 +2406,7 @@ module ts {
}
}

function getCanonicalFileName(fileName: string) {
return useCaseSensitivefileNames ? fileName : fileName.toLowerCase();
}
let getCanonicalFileName = createGetCanonicalFileName(useCaseSensitivefileNames);

function getValidSourceFile(fileName: string): SourceFile {
fileName = normalizeSlashes(fileName);
Expand Down
10 changes: 9 additions & 1 deletion src/services/shims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ module ts {
getDefaultLibFileName(options: string): string;
getNewLine?(): string;
getProjectVersion?(): string;
useCaseSensitiveFileNames?(): boolean;
}

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

public useCaseSensitiveFileNames(): boolean {
return this.shimHost.useCaseSensitiveFileNames && this.useCaseSensitiveFileNames();
}

public getCompilationSettings(): CompilerOptions {
var settingsJson = this.shimHost.getCompilationSettings();
if (settingsJson == null || settingsJson == "") {
Expand Down Expand Up @@ -909,7 +914,7 @@ module ts {

export class TypeScriptServicesFactory implements ShimFactory {
private _shims: Shim[] = [];
private documentRegistry: DocumentRegistry = createDocumentRegistry();
private documentRegistry: DocumentRegistry;

/*
* Returns script API version.
Expand All @@ -920,6 +925,9 @@ module ts {

public createLanguageServiceShim(host: LanguageServiceShimHost): LanguageServiceShim {
try {
if (this.documentRegistry === undefined) {
this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
}
var hostAdapter = new LanguageServiceShimHostAdapter(host);
var languageService = createLanguageService(hostAdapter, this.documentRegistry);
return new LanguageServiceShimObject(this, host, languageService);
Expand Down

0 comments on commit 269ae3a

Please sign in to comment.