diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ad35852c22865..ca3399375a28a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -15,7 +15,7 @@ namespace ts { referenced: boolean; } - export function getModuleInstanceState(node: ModuleDeclaration, visited?: Map): ModuleInstanceState { + export function getModuleInstanceState(node: ModuleDeclaration, visited?: Map): ModuleInstanceState { if (node.body && !node.body.parent) { // getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already setParent(node.body, node); @@ -24,8 +24,8 @@ namespace ts { return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated; } - function getModuleInstanceStateCached(node: Node, visited = createMap()) { - const nodeId = "" + getNodeId(node); + function getModuleInstanceStateCached(node: Node, visited = new Map()) { + const nodeId = getNodeId(node); if (visited.has(nodeId)) { return visited.get(nodeId) || ModuleInstanceState.NonInstantiated; } @@ -35,7 +35,7 @@ namespace ts { return result; } - function getModuleInstanceStateWorker(node: Node, visited: Map): ModuleInstanceState { + function getModuleInstanceStateWorker(node: Node, visited: Map): ModuleInstanceState { // A module is uninstantiated if it contains only switch (node.kind) { // 1. interface declarations, type alias declarations @@ -107,7 +107,7 @@ namespace ts { return ModuleInstanceState.Instantiated; } - function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: Map) { + function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: Map) { const name = specifier.propertyName || specifier.name; let p: Node | undefined = specifier.parent; while (p) { @@ -2884,8 +2884,7 @@ namespace ts { function addLateBoundAssignmentDeclarationToSymbol(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol | undefined) { if (symbol) { - const members = symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = createMap()); - members.set("" + getNodeId(node), node); + (symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = new Map())).set(getNodeId(node), node); } } diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 16707941c63ac..ea92c99c5172f 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -24,11 +24,11 @@ namespace ts { /** * Cache of bind and check diagnostics for files with their Path being the key */ - semanticDiagnosticsPerFile?: ReadonlyMap | undefined; + semanticDiagnosticsPerFile?: ReadonlyMap | undefined; /** * The map has key by source file's path that has been changed */ - changedFilesSet?: ReadonlyMap; + changedFilesSet?: ReadonlySet; /** * Set of affected files being iterated */ @@ -41,7 +41,7 @@ namespace ts { * Map of file signatures, with key being file path, calculated while getting current changed file's affected files * These will be committed whenever the iteration through affected files of current changed file is complete */ - currentAffectedFilesSignatures?: ReadonlyMap | undefined; + currentAffectedFilesSignatures?: ReadonlyMap | undefined; /** * Newly computed visible to outside referencedSet */ @@ -49,7 +49,7 @@ namespace ts { /** * True if the semantic diagnostics were copied from the old state */ - semanticDiagnosticsFromOldState?: Map; + semanticDiagnosticsFromOldState?: Set; /** * program corresponding to this state */ @@ -65,7 +65,7 @@ namespace ts { /** * Files pending to be emitted kind. */ - affectedFilesPendingEmitKind?: ReadonlyMap | undefined; + affectedFilesPendingEmitKind?: ReadonlyMap | undefined; /** * Current index to retrieve pending affected file */ @@ -89,11 +89,11 @@ namespace ts { /** * Cache of bind and check diagnostics for files with their Path being the key */ - semanticDiagnosticsPerFile: Map | undefined; + semanticDiagnosticsPerFile: Map | undefined; /** * The map has key by source file's path that has been changed */ - changedFilesSet: Map; + changedFilesSet: Set; /** * Set of affected files being iterated */ @@ -110,7 +110,7 @@ namespace ts { * Map of file signatures, with key being file path, calculated while getting current changed file's affected files * These will be committed whenever the iteration through affected files of current changed file is complete */ - currentAffectedFilesSignatures: Map | undefined; + currentAffectedFilesSignatures: Map | undefined; /** * Newly computed visible to outside referencedSet */ @@ -118,7 +118,7 @@ namespace ts { /** * Already seen affected files */ - seenAffectedFiles: Map | undefined; + seenAffectedFiles: Set | undefined; /** * whether this program has cleaned semantic diagnostics cache for lib files */ @@ -126,7 +126,7 @@ namespace ts { /** * True if the semantic diagnostics were copied from the old state */ - semanticDiagnosticsFromOldState?: Map; + semanticDiagnosticsFromOldState?: Set; /** * program corresponding to this state */ @@ -142,7 +142,7 @@ namespace ts { /** * Files pending to be emitted kind. */ - affectedFilesPendingEmitKind: Map | undefined; + affectedFilesPendingEmitKind: Map | undefined; /** * Current index to retrieve pending affected file */ @@ -154,16 +154,16 @@ namespace ts { /** * Already seen emitted files */ - seenEmittedFiles: Map | undefined; + seenEmittedFiles: Map | undefined; /** * true if program has been emitted */ programEmitComplete?: true; } - function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined): boolean { + function hasSameKeys(map1: ReadonlyCollection | undefined, map2: ReadonlyCollection | undefined): boolean { // Has same size and every key is present in both maps - return map1 as ReadonlyMap === map2 || map1 !== undefined && map2 !== undefined && map1.size === map2.size && !forEachKey(map1, key => !map2.has(key)); + return map1 === map2 || map1 !== undefined && map2 !== undefined && map1.size === map2.size && !forEachKey(map1, key => !map2.has(key)); } /** @@ -176,9 +176,9 @@ namespace ts { state.compilerOptions = compilerOptions; // With --out or --outFile, any change affects all semantic diagnostics so no need to cache them if (!outFile(compilerOptions)) { - state.semanticDiagnosticsPerFile = createMap(); + state.semanticDiagnosticsPerFile = new Map(); } - state.changedFilesSet = createMap(); + state.changedFilesSet = new Set(); const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState); const oldCompilerOptions = useOldState ? oldState!.compilerOptions : undefined; @@ -196,14 +196,12 @@ namespace ts { } // Copy old state's changed files set - if (changedFilesSet) { - copyEntries(changedFilesSet, state.changedFilesSet); - } + changedFilesSet?.forEach(value => state.changedFilesSet.add(value)); if (!outFile(compilerOptions) && oldState!.affectedFilesPendingEmit) { state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit.slice(); - state.affectedFilesPendingEmitKind = cloneMapOrUndefined(oldState!.affectedFilesPendingEmitKind); + state.affectedFilesPendingEmitKind = oldState!.affectedFilesPendingEmitKind && new Map(oldState!.affectedFilesPendingEmitKind); state.affectedFilesPendingEmitIndex = oldState!.affectedFilesPendingEmitIndex; - state.seenAffectedFiles = createMap(); + state.seenAffectedFiles = new Set(); } } @@ -227,10 +225,10 @@ namespace ts { // Referenced file was deleted in the new program newReferences && forEachKey(newReferences, path => !state.fileInfos.has(path) && oldState!.fileInfos.has(path))) { // Register file as changed file and do not copy semantic diagnostics, since all changed files need to be re-evaluated - state.changedFilesSet.set(sourceFilePath, true); + state.changedFilesSet.add(sourceFilePath); } else if (canCopySemanticDiagnostics) { - const sourceFile = newProgram.getSourceFileByPath(sourceFilePath as Path)!; + const sourceFile = newProgram.getSourceFileByPath(sourceFilePath)!; if (sourceFile.isDeclarationFile && !copyDeclarationFileDiagnostics) { return; } if (sourceFile.hasNoDefaultLib && !copyLibFileDiagnostics) { return; } @@ -240,9 +238,9 @@ namespace ts { if (diagnostics) { state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as readonly ReusableDiagnostic[], newProgram, getCanonicalFileName) : diagnostics as readonly Diagnostic[]); if (!state.semanticDiagnosticsFromOldState) { - state.semanticDiagnosticsFromOldState = createMap(); + state.semanticDiagnosticsFromOldState = new Set(); } - state.semanticDiagnosticsFromOldState.set(sourceFilePath, true); + state.semanticDiagnosticsFromOldState.add(sourceFilePath); } } }); @@ -250,13 +248,13 @@ namespace ts { // If the global file is removed, add all files as changed if (useOldState && forEachEntry(oldState!.fileInfos, (info, sourceFilePath) => info.affectsGlobalScope && !state.fileInfos.has(sourceFilePath))) { BuilderState.getAllFilesExcludingDefaultLibraryFile(state, newProgram, /*firstSourceFile*/ undefined) - .forEach(file => state.changedFilesSet.set(file.resolvedPath, true)); + .forEach(file => state.changedFilesSet.add(file.resolvedPath)); } else if (oldCompilerOptions && !outFile(compilerOptions) && compilerOptionsAffectEmit(compilerOptions, oldCompilerOptions)) { // Add all files to affectedFilesPendingEmit since emit changed newProgram.getSourceFiles().forEach(f => addToAffectedFilesPendingEmit(state, f.resolvedPath, BuilderFileEmit.Full)); Debug.assert(!state.seenAffectedFiles || !state.seenAffectedFiles.size); - state.seenAffectedFiles = state.seenAffectedFiles || createMap(); + state.seenAffectedFiles = state.seenAffectedFiles || new Set(); } state.buildInfoEmitPending = !!state.changedFilesSet.size; @@ -307,22 +305,22 @@ namespace ts { */ function cloneBuilderProgramState(state: Readonly): BuilderProgramState { const newState = BuilderState.clone(state) as BuilderProgramState; - newState.semanticDiagnosticsPerFile = cloneMapOrUndefined(state.semanticDiagnosticsPerFile); - newState.changedFilesSet = cloneMap(state.changedFilesSet); + newState.semanticDiagnosticsPerFile = state.semanticDiagnosticsPerFile && new Map(state.semanticDiagnosticsPerFile); + newState.changedFilesSet = new Set(state.changedFilesSet); newState.affectedFiles = state.affectedFiles; newState.affectedFilesIndex = state.affectedFilesIndex; newState.currentChangedFilePath = state.currentChangedFilePath; - newState.currentAffectedFilesSignatures = cloneMapOrUndefined(state.currentAffectedFilesSignatures); - newState.currentAffectedFilesExportedModulesMap = cloneMapOrUndefined(state.currentAffectedFilesExportedModulesMap); - newState.seenAffectedFiles = cloneMapOrUndefined(state.seenAffectedFiles); + newState.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures && new Map(state.currentAffectedFilesSignatures); + newState.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap && new Map(state.currentAffectedFilesExportedModulesMap); + newState.seenAffectedFiles = state.seenAffectedFiles && new Set(state.seenAffectedFiles); newState.cleanedDiagnosticsOfLibFiles = state.cleanedDiagnosticsOfLibFiles; - newState.semanticDiagnosticsFromOldState = cloneMapOrUndefined(state.semanticDiagnosticsFromOldState); + newState.semanticDiagnosticsFromOldState = state.semanticDiagnosticsFromOldState && new Set(state.semanticDiagnosticsFromOldState); newState.program = state.program; newState.compilerOptions = state.compilerOptions; newState.affectedFilesPendingEmit = state.affectedFilesPendingEmit && state.affectedFilesPendingEmit.slice(); - newState.affectedFilesPendingEmitKind = cloneMapOrUndefined(state.affectedFilesPendingEmitKind); + newState.affectedFilesPendingEmitKind = state.affectedFilesPendingEmitKind && new Map(state.affectedFilesPendingEmitKind); newState.affectedFilesPendingEmitIndex = state.affectedFilesPendingEmitIndex; - newState.seenEmittedFiles = cloneMapOrUndefined(state.seenEmittedFiles); + newState.seenEmittedFiles = state.seenEmittedFiles && new Map(state.seenEmittedFiles); newState.programEmitComplete = state.programEmitComplete; return newState; } @@ -384,14 +382,14 @@ namespace ts { } // Get next batch of affected files - state.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures || createMap(); + if (!state.currentAffectedFilesSignatures) state.currentAffectedFilesSignatures = new Map(); if (state.exportedModulesMap) { - state.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap || createMap(); + if (!state.currentAffectedFilesExportedModulesMap) state.currentAffectedFilesExportedModulesMap = new Map(); } - state.affectedFiles = BuilderState.getFilesAffectedBy(state, program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap); - state.currentChangedFilePath = nextKey.value as Path; + state.affectedFiles = BuilderState.getFilesAffectedBy(state, program, nextKey.value, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap); + state.currentChangedFilePath = nextKey.value; state.affectedFilesIndex = 0; - state.seenAffectedFiles = state.seenAffectedFiles || createMap(); + if (!state.seenAffectedFiles) state.seenAffectedFiles = new Set(); } } @@ -401,7 +399,7 @@ namespace ts { function getNextAffectedFilePendingEmit(state: BuilderProgramState) { const { affectedFilesPendingEmit } = state; if (affectedFilesPendingEmit) { - const seenEmittedFiles = state.seenEmittedFiles || (state.seenEmittedFiles = createMap()); + const seenEmittedFiles = (state.seenEmittedFiles || (state.seenEmittedFiles = new Map())); for (let i = state.affectedFilesPendingEmitIndex!; i < affectedFilesPendingEmit.length; i++) { const affectedFile = Debug.checkDefined(state.program).getSourceFileByPath(affectedFilesPendingEmit[i]); if (affectedFile) { @@ -516,7 +514,7 @@ namespace ts { // Since isolated modules dont change js files, files affected by change in signature is itself // But we need to cleanup semantic diagnostics and queue dts emit for affected files if (state.compilerOptions.isolatedModules) { - const seenFileNamesMap = createMap(); + const seenFileNamesMap = new Map(); seenFileNamesMap.set(affectedFile.resolvedPath, true); const queue = BuilderState.getReferencedByPaths(state, affectedFile.resolvedPath); while (queue.length > 0) { @@ -533,13 +531,13 @@ namespace ts { } Debug.assert(!!state.currentAffectedFilesExportedModulesMap); - const seenFileAndExportsOfFile = createMap(); + const seenFileAndExportsOfFile = new Set(); // Go through exported modules from cache first // If exported modules has path, all files referencing file exported from are affected if (forEachEntry(state.currentAffectedFilesExportedModulesMap, (exportedModules, exportedFromPath) => exportedModules && exportedModules.has(affectedFile.resolvedPath) && - forEachFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn) + forEachFilesReferencingPath(state, exportedFromPath, seenFileAndExportsOfFile, fn) )) { return; } @@ -548,24 +546,24 @@ namespace ts { forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) => !state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it exportedModules.has(affectedFile.resolvedPath) && - forEachFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn) + forEachFilesReferencingPath(state, exportedFromPath, seenFileAndExportsOfFile, fn) ); } /** * Iterate on files referencing referencedPath */ - function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Map, fn: (state: BuilderProgramState, filePath: Path) => boolean) { + function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => boolean) { return forEachEntry(state.referencedMap!, (referencesInFile, filePath) => - referencesInFile.has(referencedPath) && forEachFileAndExportsOfFile(state, filePath as Path, seenFileAndExportsOfFile, fn) + referencesInFile.has(referencedPath) && forEachFileAndExportsOfFile(state, filePath, seenFileAndExportsOfFile, fn) ); } /** * fn on file and iterate on anything that exports this file */ - function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Map, fn: (state: BuilderProgramState, filePath: Path) => boolean): boolean { - if (!addToSeen(seenFileAndExportsOfFile, filePath)) { + function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => boolean): boolean { + if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) { return false; } @@ -580,7 +578,7 @@ namespace ts { if (forEachEntry(state.currentAffectedFilesExportedModulesMap, (exportedModules, exportedFromPath) => exportedModules && exportedModules.has(filePath) && - forEachFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn) + forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn) )) { return true; } @@ -589,7 +587,7 @@ namespace ts { if (forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) => !state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it exportedModules.has(filePath) && - forEachFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn) + forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn) )) { return true; } @@ -598,7 +596,7 @@ namespace ts { return !!forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) => referencesInFile.has(filePath) && !seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file - fn(state, referencingFilePath as Path) // Dont add to seen since this is not yet done with the export removal + fn(state, referencingFilePath) // Dont add to seen since this is not yet done with the export removal ); } @@ -622,9 +620,9 @@ namespace ts { state.programEmitComplete = true; } else { - state.seenAffectedFiles!.set((affected as SourceFile).resolvedPath, true); + state.seenAffectedFiles!.add((affected as SourceFile).resolvedPath); if (emitKind !== undefined) { - (state.seenEmittedFiles || (state.seenEmittedFiles = createMap())).set((affected as SourceFile).resolvedPath, emitKind); + (state.seenEmittedFiles || (state.seenEmittedFiles = new Map())).set((affected as SourceFile).resolvedPath, emitKind); } if (isPendingEmit) { state.affectedFilesPendingEmitIndex!++; @@ -760,9 +758,9 @@ namespace ts { if (state.affectedFilesPendingEmit) { const affectedFilesPendingEmit: ProgramBuilderInfoFilePendingEmit[] = []; - const seenFiles = createMap(); + const seenFiles = new Set(); for (const path of state.affectedFilesPendingEmit.slice(state.affectedFilesPendingEmitIndex).sort(compareStringsCaseSensitive)) { - if (addToSeen(seenFiles, path)) { + if (tryAddToSet(seenFiles, path)) { affectedFilesPendingEmit.push([relativeToBuildInfo(path), state.affectedFilesPendingEmitKind!.get(path)!]); } } @@ -1127,7 +1125,7 @@ namespace ts { function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilePendingEmit: Path, kind: BuilderFileEmit) { if (!state.affectedFilesPendingEmit) state.affectedFilesPendingEmit = []; - if (!state.affectedFilesPendingEmitKind) state.affectedFilesPendingEmitKind = createMap(); + if (!state.affectedFilesPendingEmitKind) state.affectedFilesPendingEmitKind = new Map(); const existingKind = state.affectedFilesPendingEmitKind.get(affectedFilePendingEmit); state.affectedFilesPendingEmit.push(affectedFilePendingEmit); @@ -1142,14 +1140,14 @@ namespace ts { } } - function getMapOfReferencedSet(mapLike: MapLike | undefined, toPath: (path: string) => Path): ReadonlyMap | undefined { + function getMapOfReferencedSet(mapLike: MapLike | undefined, toPath: (path: string) => Path): ReadonlyMap | undefined { if (!mapLike) return undefined; - const map = createMap(); + const map = new Map(); // Copies keys/values from template. Note that for..in will not throw if // template is undefined, and instead will just exit the loop. for (const key in mapLike) { if (hasProperty(mapLike, key)) { - map.set(toPath(key), arrayToSet(mapLike[key], toPath)); + map.set(toPath(key), new Set(mapLike[key].map(toPath))); } } return map; @@ -1159,7 +1157,7 @@ namespace ts { const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory())); const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); - const fileInfos = createMap(); + const fileInfos = new Map(); for (const key in program.fileInfos) { if (hasProperty(program.fileInfos, key)) { fileInfos.set(toPath(key), program.fileInfos[key]); diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 315e67bdbb2d5..311f92ceabe12 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -15,42 +15,42 @@ namespace ts { /** * Information of the file eg. its version, signature etc */ - fileInfos: ReadonlyMap; + fileInfos: ReadonlyMap; /** * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled * Otherwise undefined * Thus non undefined value indicates, module emit */ - readonly referencedMap?: ReadonlyMap | undefined; + readonly referencedMap?: ReadonlyMap | undefined; /** * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled * Otherwise undefined */ - readonly exportedModulesMap?: ReadonlyMap | undefined; + readonly exportedModulesMap?: ReadonlyMap | undefined; } export interface BuilderState { /** * Information of the file eg. its version, signature etc */ - fileInfos: Map; + fileInfos: Map; /** * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled * Otherwise undefined * Thus non undefined value indicates, module emit */ - readonly referencedMap: ReadonlyMap | undefined; + readonly referencedMap: ReadonlyMap | undefined; /** * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled * Otherwise undefined */ - readonly exportedModulesMap: Map | undefined; + readonly exportedModulesMap: Map | undefined; /** * Map of files that have already called update signature. * That means hence forth these files are assumed to have * no change in their signature for this version of the program */ - hasCalledUpdateShapeSignature: Map; + hasCalledUpdateShapeSignature: Set; /** * Cache of all files excluding default library file for the current program */ @@ -73,7 +73,7 @@ namespace ts { /** * Referenced files with values for the keys as referenced file's path to be true */ - export type ReferencedSet = ReadonlyMap; + export type ReferencedSet = ReadonlySet; /** * Compute the hash to store the shape of the file */ @@ -83,7 +83,7 @@ namespace ts { * Exported modules to from declaration emit being computed. * This can contain false in the affected file path to specify that there are no exported module(types from other modules) for this file */ - export type ComputingExportedModulesMap = Map; + export type ComputingExportedModulesMap = Map; /** * Get the referencedFile from the imported module symbol @@ -113,8 +113,8 @@ namespace ts { /** * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true */ - function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): Map | undefined { - let referencedFiles: Map | undefined; + function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): Set | undefined { + let referencedFiles: Set | undefined; // We need to use a set here since the code can contain the same import twice, // but that will only be one dependency. @@ -185,17 +185,14 @@ namespace ts { } function addReferencedFile(referencedPath: Path) { - if (!referencedFiles) { - referencedFiles = createMap(); - } - referencedFiles.set(referencedPath, true); + (referencedFiles || (referencedFiles = new Set())).add(referencedPath); } } /** * Returns true if oldState is reusable, that is the emitKind = module/non module has not changed */ - export function canReuseOldState(newReferencedMap: ReadonlyMap | undefined, oldState: Readonly | undefined) { + export function canReuseOldState(newReferencedMap: ReadonlyMap | undefined, oldState: Readonly | undefined) { return oldState && !oldState.referencedMap === !newReferencedMap; } @@ -203,10 +200,10 @@ namespace ts { * Creates the state of file references and signature for the new program from oldState if it is safe */ export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly): BuilderState { - const fileInfos = createMap(); - const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createMap() : undefined; - const exportedModulesMap = referencedMap ? createMap() : undefined; - const hasCalledUpdateShapeSignature = createMap(); + const fileInfos = new Map(); + const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? new Map() : undefined; + const exportedModulesMap = referencedMap ? new Map() : undefined; + const hasCalledUpdateShapeSignature = new Set(); const useOldState = canReuseOldState(referencedMap, oldState); // Create the reference map, and set the file infos @@ -249,28 +246,24 @@ namespace ts { * Creates a clone of the state */ export function clone(state: Readonly): BuilderState { - const fileInfos = createMap(); - state.fileInfos.forEach((value, key) => { - fileInfos.set(key, { ...value }); - }); // Dont need to backup allFiles info since its cache anyway return { - fileInfos, - referencedMap: cloneMapOrUndefined(state.referencedMap), - exportedModulesMap: cloneMapOrUndefined(state.exportedModulesMap), - hasCalledUpdateShapeSignature: cloneMap(state.hasCalledUpdateShapeSignature), + fileInfos: new Map(state.fileInfos), + referencedMap: state.referencedMap && new Map(state.referencedMap), + exportedModulesMap: state.exportedModulesMap && new Map(state.exportedModulesMap), + hasCalledUpdateShapeSignature: new Set(state.hasCalledUpdateShapeSignature), }; } /** * Gets the files affected by the path from the program */ - export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map, exportedModulesMapCache?: ComputingExportedModulesMap): readonly SourceFile[] { + export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map, exportedModulesMapCache?: ComputingExportedModulesMap): readonly SourceFile[] { // Since the operation could be cancelled, the signatures are always stored in the cache // They will be committed once it is safe to use them // eg when calling this api from tsserver, if there is no cancellation of the operation // In the other cases the affected files signatures are committed only after the iteration through the result is complete - const signatureCache = cacheToUpdateSignature || createMap(); + const signatureCache = cacheToUpdateSignature || new Map(); const sourceFile = programOfThisState.getSourceFileByPath(path); if (!sourceFile) { return emptyArray; @@ -292,19 +285,19 @@ namespace ts { * Updates the signatures from the cache into state's fileinfo signatures * This should be called whenever it is safe to commit the state of the builder */ - export function updateSignaturesFromCache(state: BuilderState, signatureCache: Map) { - signatureCache.forEach((signature, path) => updateSignatureOfFile(state, signature, path as Path)); + export function updateSignaturesFromCache(state: BuilderState, signatureCache: Map) { + signatureCache.forEach((signature, path) => updateSignatureOfFile(state, signature, path)); } export function updateSignatureOfFile(state: BuilderState, signature: string | undefined, path: Path) { state.fileInfos.get(path)!.signature = signature; - state.hasCalledUpdateShapeSignature.set(path, true); + state.hasCalledUpdateShapeSignature.add(path); } /** * Returns if the shape of the signature has changed since last emit */ - export function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) { + export function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) { Debug.assert(!!sourceFile); Debug.assert(!exportedModulesMapCache || !!state.exportedModulesMap, "Compute visible to outside map only if visibleToOutsideReferencedMap present in the state"); @@ -365,16 +358,16 @@ namespace ts { return; } - let exportedModules: Map | undefined; + let exportedModules: Set | undefined; exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFileFromImportedModuleSymbol(symbol))); exportedModulesMapCache.set(sourceFile.resolvedPath, exportedModules || false); function addExportedModule(exportedModulePath: Path | undefined) { if (exportedModulePath) { if (!exportedModules) { - exportedModules = createMap(); + exportedModules = new Set(); } - exportedModules.set(exportedModulePath, true); + exportedModules.add(exportedModulePath); } } } @@ -413,26 +406,23 @@ namespace ts { } // Get the references, traversing deep from the referenceMap - const seenMap = createMap(); + const seenMap = new Set(); const queue = [sourceFile.resolvedPath]; while (queue.length) { const path = queue.pop()!; if (!seenMap.has(path)) { - seenMap.set(path, true); + seenMap.add(path); const references = state.referencedMap.get(path); if (references) { const iterator = references.keys(); for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { - queue.push(iterResult.value as Path); + queue.push(iterResult.value); } } } } - return arrayFrom(mapDefinedIterator(seenMap.keys(), path => { - const file = programOfThisState.getSourceFileByPath(path as Path); - return file ? file.fileName : path; - })); + return arrayFrom(mapDefinedIterator(seenMap.keys(), path => programOfThisState.getSourceFileByPath(path)?.fileName ?? path)); } /** @@ -451,7 +441,7 @@ namespace ts { */ export function getReferencedByPaths(state: Readonly, referencedFilePath: Path) { return arrayFrom(mapDefinedIterator(state.referencedMap!.entries(), ([filePath, referencesInFile]) => - referencesInFile.has(referencedFilePath) ? filePath as Path : undefined + referencesInFile.has(referencedFilePath) ? filePath : undefined )); } @@ -528,7 +518,7 @@ namespace ts { /** * When program emits modular code, gets the files affected by the sourceFile whose shape has changed */ - function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { + function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) { return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); } @@ -541,7 +531,7 @@ namespace ts { // Now we need to if each file in the referencedBy list has a shape change as well. // Because if so, its own referencedBy files need to be saved as well to make the // emitting result consistent with files on disk. - const seenFileNamesMap = createMap(); + const seenFileNamesMap = new Map(); // Start with the paths this file was referenced by seenFileNamesMap.set(sourceFileWithUpdatedShape.resolvedPath, sourceFileWithUpdatedShape); @@ -562,8 +552,4 @@ namespace ts { return arrayFrom(mapDefinedIterator(seenFileNamesMap.values(), value => value)); } } - - export function cloneMapOrUndefined(map: ReadonlyMap | undefined) { - return map ? cloneMap(map) : undefined; - } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ea91ff3fbaa54..b3d1183b6f31e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -134,7 +134,7 @@ namespace ts { EmptyObjectFacts = All, } - const typeofEQFacts: ReadonlyMap = createMapFromTemplate({ + const typeofEQFacts: ReadonlyMap = createMapFromTemplate({ string: TypeFacts.TypeofEQString, number: TypeFacts.TypeofEQNumber, bigint: TypeFacts.TypeofEQBigInt, @@ -145,7 +145,7 @@ namespace ts { function: TypeFacts.TypeofEQFunction }); - const typeofNEFacts: ReadonlyMap = createMapFromTemplate({ + const typeofNEFacts: ReadonlyMap = createMapFromTemplate({ string: TypeFacts.TypeofNEString, number: TypeFacts.TypeofNENumber, bigint: TypeFacts.TypeofNEBigInt, @@ -277,13 +277,13 @@ namespace ts { } export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker { - const getPackagesSet: () => Map = memoize(() => { - const set = createMap(); + const getPackagesSet = memoize(() => { + const set = new Set(); host.getSourceFiles().forEach(sf => { if (!sf.resolvedModules) return; forEachEntry(sf.resolvedModules, r => { - if (r && r.packageId) set.set(r.packageId.name, true); + if (r && r.packageId) set.add(r.packageId.name); }); }); return set; @@ -826,10 +826,10 @@ namespace ts { readonly firstFile: SourceFile; readonly secondFile: SourceFile; /** Key is symbol name. */ - readonly conflictingSymbols: Map; + readonly conflictingSymbols: Map; } /** Key is "/path/to/a.ts|/path/to/b.ts". */ - let amalgamatedDuplicates: Map | undefined; + let amalgamatedDuplicates: Map | undefined; const reverseMappedCache = createMap(); let inInferTypeForHomomorphicMappedType = false; let ambientModulesCache: Symbol[] | undefined; @@ -839,7 +839,7 @@ namespace ts { * This is only used if there is no exact match. */ let patternAmbientModules: PatternAmbientModule[]; - let patternAmbientModuleAugmentations: Map | undefined; + let patternAmbientModuleAugmentations: Map | undefined; let globalObjectType: ObjectType; let globalFunctionType: ObjectType; @@ -907,7 +907,7 @@ namespace ts { const mergedSymbols: Symbol[] = []; const symbolLinks: SymbolLinks[] = []; const nodeLinks: NodeLinks[] = []; - const flowLoopCaches: Map[] = []; + const flowLoopCaches: Map[] = []; const flowLoopNodes: FlowNode[] = []; const flowLoopKeys: string[] = []; const flowLoopTypes: Type[][] = []; @@ -923,7 +923,7 @@ namespace ts { const diagnostics = createDiagnosticCollection(); const suggestionDiagnostics = createDiagnosticCollection(); - const typeofTypesByName: ReadonlyMap = createMapFromTemplate({ + const typeofTypesByName: ReadonlyMap = createMapFromTemplate({ string: stringType, number: numberType, bigint: bigintType, @@ -3753,7 +3753,7 @@ namespace ts { return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace; } - function getAccessibleSymbolChain(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean, visitedSymbolTablesMap: Map = createMap()): Symbol[] | undefined { + function getAccessibleSymbolChain(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean, visitedSymbolTablesMap: Map = createMap()): Symbol[] | undefined { if (!(symbol && !isPropertyOrMethodDeclarationSymbol(symbol))) { return undefined; } @@ -4458,7 +4458,7 @@ namespace ts { function typeToTypeNodeOrCircularityElision(type: Type) { if (type.flags & TypeFlags.Union) { - if (context.visitedTypes && context.visitedTypes.has("" + getTypeId(type))) { + if (context.visitedTypes?.has(getTypeId(type))) { if (!(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier)) { context.encounteredError = true; context.tracker?.reportCyclicStructureError?.(); @@ -4491,7 +4491,7 @@ namespace ts { } function createAnonymousTypeNode(type: ObjectType): TypeNode { - const typeId = "" + type.id; + const typeId = type.id; const symbol = type.symbol; if (symbol) { if (isJSConstructor(symbol.valueDeclaration)) { @@ -4505,7 +4505,7 @@ namespace ts { shouldWriteTypeOfFunctionSymbol()) { return symbolToTypeNode(symbol, context, SymbolFlags.Value); } - else if (context.visitedTypes && context.visitedTypes.has(typeId)) { + else if (context.visitedTypes?.has(typeId)) { // If type is an anonymous type literal in a type alias declaration, use type alias name const typeAlias = getTypeAliasForTypeLiteral(type); if (typeAlias) { @@ -4533,14 +4533,14 @@ namespace ts { declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock)); if (isStaticMethodSymbol || isNonLocalFunctionSymbol) { // typeof is allowed only for static/non local functions - return (!!(context.flags & NodeBuilderFlags.UseTypeOfFunction) || (context.visitedTypes && context.visitedTypes.has(typeId))) && // it is type of the symbol uses itself recursively + return (!!(context.flags & NodeBuilderFlags.UseTypeOfFunction) || (context.visitedTypes?.has(typeId))) && // it is type of the symbol uses itself recursively (!(context.flags & NodeBuilderFlags.UseStructuralFallback) || isValueSymbolAccessible(symbol, context.enclosingDeclaration)); // And the build is going to succeed without visibility error or there is no structural fallback allowed } } } function visitAndTransformType(type: Type, transform: (type: Type) => T) { - const typeId = "" + type.id; + const typeId = type.id; const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class; const id = getObjectFlags(type) & ObjectFlags.Reference && (type).node ? "N" + getNodeId((type).node!) : type.symbol ? (isConstructorObject ? "+" : "") + getSymbolId(type.symbol) : @@ -4548,7 +4548,7 @@ namespace ts { // Since instantiations of the same anonymous type have the same symbol, tracking symbols instead // of types allows us to catch circular references to instantiations of the same anonymous type if (!context.visitedTypes) { - context.visitedTypes = createMap(); + context.visitedTypes = new Set(); } if (id && !context.symbolDepth) { context.symbolDepth = createMap(); @@ -4562,7 +4562,7 @@ namespace ts { } context.symbolDepth!.set(id, depth + 1); } - context.visitedTypes.set(typeId, true); + context.visitedTypes.add(typeId); const result = transform(type); context.visitedTypes.delete(typeId); if (id) { @@ -5251,11 +5251,11 @@ namespace ts { function lookupTypeParameterNodes(chain: Symbol[], index: number, context: NodeBuilderContext) { Debug.assert(chain && 0 <= index && index < chain.length); const symbol = chain[index]; - const symbolId = "" + getSymbolId(symbol); - if (context.typeParameterSymbolList && context.typeParameterSymbolList.get(symbolId)) { + const symbolId = getSymbolId(symbol); + if (context.typeParameterSymbolList?.has(symbolId)) { return undefined; } - (context.typeParameterSymbolList || (context.typeParameterSymbolList = createMap())).set(symbolId, true); + (context.typeParameterSymbolList || (context.typeParameterSymbolList = new Set())).add(symbolId); let typeParameterNodes: readonly TypeNode[] | readonly TypeParameterDeclaration[] | undefined; if (context.flags & NodeBuilderFlags.WriteTypeParametersInQualifiedName && index < (chain.length - 1)) { const parentSymbol = symbol; @@ -5468,7 +5468,7 @@ namespace ts { const rawtext = result.escapedText as string; let i = 0; let text = rawtext; - while ((context.typeParameterNamesByText && context.typeParameterNamesByText.get(text)) || typeParameterShadowsNameInScope(text as __String, context, type)) { + while (context.typeParameterNamesByText?.has(text) || typeParameterShadowsNameInScope(text as __String, context, type)) { i++; text = `${rawtext}_${i}`; } @@ -5476,7 +5476,7 @@ namespace ts { result = factory.createIdentifier(text, result.typeArguments); } (context.typeParameterNames || (context.typeParameterNames = createMap())).set("" + getTypeId(type), result); - (context.typeParameterNamesByText || (context.typeParameterNamesByText = createMap())).set(result.escapedText as string, true); + (context.typeParameterNamesByText || (context.typeParameterNamesByText = new Set())).add(result.escapedText as string); } return result; } @@ -5635,10 +5635,10 @@ namespace ts { initial.typeParameterNames = cloneMap(initial.typeParameterNames); } if (initial.typeParameterNamesByText) { - initial.typeParameterNamesByText = cloneMap(initial.typeParameterNamesByText); + initial.typeParameterNamesByText = new Set(initial.typeParameterNamesByText); } if (initial.typeParameterSymbolList) { - initial.typeParameterSymbolList = cloneMap(initial.typeParameterSymbolList); + initial.typeParameterSymbolList = new Set(initial.typeParameterSymbolList); } return initial; } @@ -5877,12 +5877,12 @@ namespace ts { // we're trying to emit from later on) const enclosingDeclaration = context.enclosingDeclaration!; let results: Statement[] = []; - const visitedSymbols: Map = createMap(); - let deferredPrivates: Map | undefined; + const visitedSymbols = new Set(); + let deferredPrivates: Map | undefined; const oldcontext = context; context = { ...oldcontext, - usedSymbolNames: createMap(), + usedSymbolNames: new Set(oldcontext.usedSymbolNames), remappedSymbolNames: createMap(), tracker: { ...oldcontext.tracker, @@ -5901,11 +5901,6 @@ namespace ts { } } }; - if (oldcontext.usedSymbolNames) { - oldcontext.usedSymbolNames.forEach((_, name) => { - context.usedSymbolNames!.set(name, true); - }); - } forEachEntry(symbolTable, (symbol, name) => { const baseName = unescapeLeadingUnderscores(name); void getInternalSymbolName(symbol, baseName); // Called to cache values into `usedSymbolNames` and `remappedSymbolNames` @@ -6116,10 +6111,10 @@ namespace ts { // cache visited list based on merged symbol, since we want to use the unmerged top-level symbol, but // still skip reserializing it if we encounter the merged product later on const visitedSym = getMergedSymbol(symbol); - if (visitedSymbols.has("" + getSymbolId(visitedSym))) { + if (visitedSymbols.has(getSymbolId(visitedSym))) { return; // Already printed } - visitedSymbols.set("" + getSymbolId(visitedSym), true); + visitedSymbols.add(getSymbolId(visitedSym)); // Only actually serialize symbols within the correct enclosing declaration, otherwise do nothing with the out-of-context symbol const skipMembershipCheck = !isPrivate; // We only call this on exported symbols when we know they're in the correct scope if (skipMembershipCheck || (!!length(symbol.declarations) && some(symbol.declarations, d => !!findAncestor(d, n => n === enclosingDeclaration)))) { @@ -7074,11 +7069,11 @@ namespace ts { } let i = 0; const original = input; - while (context.usedSymbolNames!.has(input)) { + while (context.usedSymbolNames?.has(input)) { i++; input = `${original}_${i}`; } - context.usedSymbolNames!.set(input, true); + context.usedSymbolNames?.add(input); if (symbol) { context.remappedSymbolNames!.set("" + getSymbolId(symbol), input); } @@ -7190,16 +7185,16 @@ namespace ts { // State encounteredError: boolean; - visitedTypes: Map | undefined; - symbolDepth: Map | undefined; + visitedTypes: Set | undefined; + symbolDepth: Map | undefined; inferTypeParameters: TypeParameter[] | undefined; approximateLength: number; truncating?: boolean; - typeParameterSymbolList?: Map; - typeParameterNames?: Map; - typeParameterNamesByText?: Map; - usedSymbolNames?: Map; - remappedSymbolNames?: Map; + typeParameterSymbolList?: Set; + typeParameterNames?: Map; + typeParameterNamesByText?: Set; + usedSymbolNames?: Set; + remappedSymbolNames?: Map; } function isDefaultBindingContext(location: Node) { @@ -7395,10 +7390,10 @@ namespace ts { exportSymbol = getTargetOfExportSpecifier(node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } let result: Node[] | undefined; - let visited: Map | undefined; + let visited: Set | undefined; if (exportSymbol) { - visited = createMap(); - visited.set("" + getSymbolId(exportSymbol), true); + visited = new Set(); + visited.add(getSymbolId(exportSymbol)); buildVisibleNodeList(exportSymbol.declarations); } return result; @@ -7420,10 +7415,10 @@ namespace ts { const firstIdentifier = getFirstIdentifier(internalModuleReference); const importSymbol = resolveName(declaration, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, undefined, undefined, /*isUse*/ false); - const id = importSymbol && "" + getSymbolId(importSymbol); - if (importSymbol && !visited!.has(id!)) { - visited!.set(id!, true); - buildVisibleNodeList(importSymbol.declarations); + if (importSymbol && visited) { + if (tryAddToSet(visited, getSymbolId(importSymbol))) { + buildVisibleNodeList(importSymbol.declarations); + } } } }); @@ -10824,7 +10819,7 @@ namespace ts { function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol | undefined { let singleProp: Symbol | undefined; - let propSet: Map | undefined; + let propSet: Map | undefined; let indexTypes: Type[] | undefined; const isUnion = containingType.flags & TypeFlags.Union; // Flags we want to propagate to the result if they exist in all source symbols @@ -12885,7 +12880,7 @@ namespace ts { return links.resolvedType; } - function addTypeToIntersection(typeSet: Map, includes: TypeFlags, type: Type) { + function addTypeToIntersection(typeSet: Map, includes: TypeFlags, type: Type) { const flags = type.flags; if (flags & TypeFlags.Intersection) { return addTypesToIntersection(typeSet, includes, (type).types); @@ -12915,7 +12910,7 @@ namespace ts { // Add the given types to the given type set. Order is preserved, freshness is removed from literal // types, duplicates are removed, and nested types of the given kind are flattened into the set. - function addTypesToIntersection(typeSet: Map, includes: TypeFlags, types: readonly Type[]) { + function addTypesToIntersection(typeSet: Map, includes: TypeFlags, types: readonly Type[]) { for (const type of types) { includes = addTypeToIntersection(typeSet, includes, getRegularTypeOfLiteralType(type)); } @@ -13032,7 +13027,7 @@ namespace ts { // Also, unlike union types, the order of the constituent types is preserved in order that overload resolution // for intersections of types with signatures can be deterministic. function getIntersectionType(types: readonly Type[], aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { - const typeMembershipMap: Map = createMap(); + const typeMembershipMap: Map = createMap(); const includes = addTypesToIntersection(typeMembershipMap, 0, types); const typeSet: Type[] = arrayFrom(typeMembershipMap.values()); // An intersection type is considered empty if it contains @@ -15072,7 +15067,7 @@ namespace ts { function checkTypeRelatedToAndOptionallyElaborate( source: Type, target: Type, - relation: Map, + relation: Map, errorNode: Node | undefined, expr: Expression | undefined, headMessage: DiagnosticMessage | undefined, @@ -15094,7 +15089,7 @@ namespace ts { node: Expression | undefined, source: Type, target: Type, - relation: Map, + relation: Map, headMessage: DiagnosticMessage | undefined, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined @@ -15131,7 +15126,7 @@ namespace ts { node: Expression, source: Type, target: Type, - relation: Map, + relation: Map, headMessage: DiagnosticMessage | undefined, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined @@ -15160,7 +15155,7 @@ namespace ts { node: ArrowFunction, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ): boolean { @@ -15247,7 +15242,7 @@ namespace ts { iterator: ElaborationIterator, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ) { @@ -15361,7 +15356,7 @@ namespace ts { node: JsxAttributes, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ) { @@ -15462,7 +15457,7 @@ namespace ts { node: ArrayLiteralExpression, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ) { @@ -15515,7 +15510,7 @@ namespace ts { node: ObjectLiteralExpression, source: Type, target: Type, - relation: Map, + relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined ) { @@ -15806,7 +15801,7 @@ namespace ts { return true; } - function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { + function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { const s = source.flags; const t = target.flags; if (t & TypeFlags.AnyOrUnknown || s & TypeFlags.Never || source === wildcardType) return true; @@ -15843,7 +15838,7 @@ namespace ts { return false; } - function isTypeRelatedTo(source: Type, target: Type, relation: Map) { + function isTypeRelatedTo(source: Type, target: Type, relation: Map) { if (isFreshLiteralType(source)) { source = (source).regularType; } @@ -15906,7 +15901,7 @@ namespace ts { function checkTypeRelatedTo( source: Type, target: Type, - relation: Map, + relation: Map, errorNode: Node | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, @@ -18028,7 +18023,7 @@ namespace ts { * To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters. * For other cases, the types ids are used. */ - function getRelationKey(source: Type, target: Type, intersectionState: IntersectionState, relation: Map) { + function getRelationKey(source: Type, target: Type, intersectionState: IntersectionState, relation: Map) { if (relation === identityRelation && source.id > target.id) { const temp = source; source = target; @@ -19212,7 +19207,7 @@ namespace ts { function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) { let symbolOrTypeStack: (Symbol | Type)[]; - let visited: Map; + let visited: Map; let bivariant = false; let propagationType: Type; let inferencePriority = InferencePriority.MaxValue; @@ -25904,7 +25899,7 @@ namespace ts { function checkApplicableSignatureForJsxOpeningLikeElement( node: JsxOpeningLikeElement, signature: Signature, - relation: Map, + relation: Map, checkMode: CheckMode, reportErrors: boolean, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, @@ -26004,7 +25999,7 @@ namespace ts { node: CallLikeExpression, args: readonly Expression[], signature: Signature, - relation: Map, + relation: Map, checkMode: CheckMode, reportErrors: boolean, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, @@ -26528,7 +26523,7 @@ namespace ts { return getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray); - function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { + function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { candidatesForArgumentError = undefined; candidateForArgumentArityError = undefined; candidateForTypeArgumentError = undefined; @@ -32268,7 +32263,7 @@ namespace ts { return !(getMergedSymbol(typeParameter.symbol).isReferenced! & SymbolFlags.TypeParameter) && !isIdentifierThatStartsWithUnderscore(typeParameter.name); } - function addToGroup(map: Map<[K, V[]]>, key: K, value: V, getKey: (key: K) => number | string): void { + function addToGroup(map: Map, key: K, value: V, getKey: (key: K) => number | string): void { const keyString = String(getKey(key)); const group = map.get(keyString); if (group) { @@ -37159,7 +37154,7 @@ namespace ts { // this variable and functions that use it are deliberately moved here from the outer scope // to avoid scope pollution const resolvedTypeReferenceDirectives = host.getResolvedTypeReferenceDirectives(); - let fileToDirective: Map; + let fileToDirective: Map; if (resolvedTypeReferenceDirectives) { // populate reverse mapping: file path -> type reference directive that was resolved to this file fileToDirective = createMap(); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index e76ffc766b73d..b2884340dfce0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -76,7 +76,7 @@ namespace ts { * option as well as for resolving lib reference directives. */ /* @internal */ - export const libMap = createMapFromEntries(libEntries); + export const libMap = new Map(libEntries); // Watch related options /* @internal */ @@ -1090,8 +1090,8 @@ namespace ts { /* @internal */ export interface OptionsNameMap { - optionsNameMap: Map; - shortOptionNames: Map; + optionsNameMap: Map; + shortOptionNames: Map; } /*@internal*/ @@ -1469,7 +1469,7 @@ namespace ts { configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost, - extendedConfigCache?: Map, + extendedConfigCache?: Map, watchOptionsToExtend?: WatchOptions, extraFileExtensions?: readonly FileExtensionInfo[], ): ParsedCommandLine | undefined { @@ -1562,15 +1562,15 @@ namespace ts { optionTypeMismatchDiagnostic: Diagnostics.Watch_option_0_requires_a_value_of_type_1 }; - let commandLineCompilerOptionsMapCache: Map; + let commandLineCompilerOptionsMapCache: Map; function getCommandLineCompilerOptionsMap() { return commandLineCompilerOptionsMapCache || (commandLineCompilerOptionsMapCache = commandLineOptionsToMap(optionDeclarations)); } - let commandLineWatchOptionsMapCache: Map; + let commandLineWatchOptionsMapCache: Map; function getCommandLineWatchOptionsMap() { return commandLineWatchOptionsMapCache || (commandLineWatchOptionsMapCache = commandLineOptionsToMap(optionsForWatch)); } - let commandLineTypeAcquisitionMapCache: Map; + let commandLineTypeAcquisitionMapCache: Map; function getCommandLineTypeAcquisitionMap() { return commandLineTypeAcquisitionMapCache || (commandLineTypeAcquisitionMapCache = commandLineOptionsToMap(typeAcquisitionDeclarations)); } @@ -1703,13 +1703,13 @@ namespace ts { return convertPropertyValueToJson(sourceFile.statements[0].expression, knownRootOptions); - function isRootOptionMap(knownOptions: Map | undefined) { + function isRootOptionMap(knownOptions: Map | undefined) { return knownRootOptions && (knownRootOptions as TsConfigOnlyOption).elementOptions === knownOptions; } function convertObjectLiteralExpressionToJson( node: ObjectLiteralExpression, - knownOptions: Map | undefined, + knownOptions: Map | undefined, extraKeyDiagnostics: DidYouMeanOptionsDiagnostics | undefined, parentOption: string | undefined ): any { @@ -1964,7 +1964,7 @@ namespace ts { return config; } - function optionMapToObject(optionMap: Map): object { + function optionMapToObject(optionMap: Map): object { return { ...arrayFrom(optionMap.entries()).reduce((prev, cur) => ({ ...prev, [cur[0]]: cur[1] }), {}), }; @@ -1994,7 +1994,7 @@ namespace ts { return _ => true; } - function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { + function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean" || optionDefinition.type === "object") { // this is of a type CommandLineOptionOfPrimitiveType return undefined; @@ -2007,7 +2007,7 @@ namespace ts { } } - function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { + function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { // There is a typeMap associated with this command-line option so use it to map value back to its name return forEachEntry(customTypeMap, (mapValue, key) => { if (mapValue === value) { @@ -2019,7 +2019,7 @@ namespace ts { function serializeCompilerOptions( options: CompilerOptions, pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean } - ): Map { + ): Map { return serializeOptionBaseObject(options, getOptionsNameMap(), pathOptions); } @@ -2031,7 +2031,7 @@ namespace ts { options: OptionsBase, { optionsNameMap }: OptionsNameMap, pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean } - ): Map { + ): Map { const result = createMap(); const getCanonicalFileName = pathOptions && createGetCanonicalFileName(pathOptions.useCaseSensitiveFileNames); @@ -2220,7 +2220,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map, existingWatchOptions?: WatchOptions): ParsedCommandLine { + export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map, existingWatchOptions?: WatchOptions): ParsedCommandLine { return parseJsonConfigFileContentWorker(json, /*sourceFile*/ undefined, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); } @@ -2231,7 +2231,7 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map, existingWatchOptions?: WatchOptions): ParsedCommandLine { + export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map, existingWatchOptions?: WatchOptions): ParsedCommandLine { return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); } @@ -2271,7 +2271,7 @@ namespace ts { configFileName?: string, resolutionStack: Path[] = [], extraFileExtensions: readonly FileExtensionInfo[] = [], - extendedConfigCache?: Map + extendedConfigCache?: Map ): ParsedCommandLine { Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined)); const errors: Diagnostic[] = []; @@ -2456,7 +2456,7 @@ namespace ts { configFileName: string | undefined, resolutionStack: string[], errors: Push, - extendedConfigCache?: Map + extendedConfigCache?: Map ): ParsedTsconfig { basePath = normalizeSlashes(basePath); const resolvedPath = getNormalizedAbsolutePath(configFileName || "", basePath); @@ -2645,7 +2645,7 @@ namespace ts { basePath: string, resolutionStack: string[], errors: Push, - extendedConfigCache?: Map + extendedConfigCache?: Map ): ParsedTsconfig | undefined { const path = host.useCaseSensitiveFileNames ? extendedConfigPath : toFileNameLowerCase(extendedConfigPath); let value: ExtendedConfigCacheEntry | undefined; @@ -2750,11 +2750,11 @@ namespace ts { return convertOptionsFromJson(getCommandLineWatchOptionsMap(), jsonOptions, basePath, /*defaultOptions*/ undefined, watchOptionsDidYouMeanDiagnostics, errors); } - function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, + function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, defaultOptions: undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push): WatchOptions | undefined; - function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, + function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, defaultOptions: CompilerOptions | TypeAcquisition, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push): CompilerOptions | TypeAcquisition; - function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, + function convertOptionsFromJson(optionsNameMap: Map, jsonOptions: any, basePath: string, defaultOptions: CompilerOptions | TypeAcquisition | WatchOptions | undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push) { if (!jsonOptions) { @@ -3174,7 +3174,7 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: readonly string[], keyMapper: (value: string) => string) { + function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: readonly string[], keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority, extensions); for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { @@ -3196,7 +3196,7 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: readonly string[], keyMapper: (value: string) => string) { + function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: readonly string[], keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority, extensions); for (let i = nextExtensionPriority; i < extensions.length; i++) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index f93756318c835..573a45b9f3b1d 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1,26 +1,56 @@ - /* @internal */ namespace ts { + type GetIteratorCallback = | ReadonlyMap | undefined>(iterable: I) => Iterator< + I extends ReadonlyMap ? [K, V] : + I extends ReadonlySet ? T : + I extends readonly (infer T)[] ? T : + I extends undefined ? undefined : + never>; + + function getCollectionImplementation< + K1 extends MatchingKeys any>, + K2 extends MatchingKeys ReturnType<(typeof NativeCollections)[K1]>> + >(name: string, nativeFactory: K1, shimFactory: K2): NonNullable> { + // NOTE: ts.ShimCollections will be defined for typescriptServices.js but not for tsc.js, so we must test for it. + const constructor = NativeCollections[nativeFactory]() ?? ShimCollections?.[shimFactory](getIterator); + if (constructor) return constructor as NonNullable>; + throw new Error(`TypeScript requires an environment that provides a compatible native ${name} implementation.`); + } + + export const Map = getCollectionImplementation("Map", "tryGetNativeMap", "createMapShim"); + export const Set = getCollectionImplementation("Set", "tryGetNativeSet", "createSetShim"); + + export function getIterator | ReadonlyMap | undefined>(iterable: I): Iterator< + I extends ReadonlyMap ? [K, V] : + I extends ReadonlySet ? T : + I extends readonly (infer T)[] ? T : + I extends undefined ? undefined : + never>; + export function getIterator(iterable: ReadonlyMap): Iterator<[K, V]>; + export function getIterator(iterable: ReadonlyMap | undefined): Iterator<[K, V]> | undefined; + export function getIterator(iterable: readonly T[] | ReadonlySet): Iterator; + export function getIterator(iterable: readonly T[] | ReadonlySet | undefined): Iterator | undefined; + export function getIterator(iterable: readonly any[] | ReadonlySet | ReadonlyMap | undefined): Iterator | undefined { + if (iterable) { + if (isArray(iterable)) return arrayIterator(iterable); + if (iterable instanceof Map) return iterable.entries(); + if (iterable instanceof Set) return iterable.values(); + throw new Error("Iteration not supported."); + } + } export const emptyArray: never[] = [] as never[]; /** Create a new map. */ - export function createMap(): Map { - return new Map(); - } - - /** Create a new map from an array of entries. */ - export function createMapFromEntries(entries: [string, T][]): Map { - const map = createMap(); - for (const [key, value] of entries) { - map.set(key, value); - } - return map; + export function createMap(): Map; + export function createMap(): Map; + export function createMap(): Map { + return new Map(); } /** Create a new map from a template object is provided, the map will copy entries from it. */ - export function createMapFromTemplate(template: MapLike): Map { - const map: Map = new Map(); + export function createMapFromTemplate(template: MapLike): Map { + const map: Map = new Map(); // Copies keys/values from template. Note that for..in will not throw if // template is undefined, and instead will just exit the loop. @@ -97,6 +127,16 @@ namespace ts { } } + export function reduceLeftIterator(iterator: Iterator | undefined, f: (memo: U, value: T, i: number) => U, initial: U): U { + let result = initial; + if (iterator) { + for (let step = iterator.next(), pos = 0; !step.done; step = iterator.next(), pos++) { + result = f(result, step.value, pos); + } + } + return result; + } + export function zipWith(arrayA: readonly T[], arrayB: readonly U[], callback: (a: T, b: U, index: number) => V): V[] { const result: V[] = []; Debug.assertEqual(arrayA.length, arrayB.length); @@ -120,9 +160,9 @@ namespace ts { }; } - export function zipToMap(keys: readonly string[], values: readonly T[]): Map { + export function zipToMap(keys: readonly K[], values: readonly V[]): Map { Debug.assert(keys.length === values.length); - const map = createMap(); + const map = new Map(); for (let i = 0; i < keys.length; ++i) { map.set(keys[i], values[i]); } @@ -514,17 +554,50 @@ namespace ts { }; } - export function mapDefinedMap(map: ReadonlyMap, mapValue: (value: T, key: string) => U | undefined, mapKey: (key: string) => string = identity): Map { - const result = createMap(); + export function mapDefinedEntries(map: ReadonlyMap, f: (key: K1, value: V1) => readonly [K2, V2] | undefined): Map; + export function mapDefinedEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): Map | undefined; + export function mapDefinedEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2 | undefined, V2 | undefined] | undefined): Map | undefined { + if (!map) { + return undefined; + } + + const result = new Map(); map.forEach((value, key) => { - const mapped = mapValue(value, key); - if (mapped !== undefined) { - result.set(mapKey(key), mapped); + const entry = f(key, value); + if (entry !== undefined) { + const [newKey, newValue] = entry; + if (newKey !== undefined && newValue !== undefined) { + result.set(newKey, newValue); + } } }); + return result; } + export function mapDefinedValues(set: ReadonlySet, f: (value: V1) => V2 | undefined): Set; + export function mapDefinedValues(set: ReadonlySet | undefined, f: (value: V1) => V2 | undefined): Set | undefined; + export function mapDefinedValues(set: ReadonlySet | undefined, f: (value: V1) => V2 | undefined): Set | undefined { + if (set) { + const result = new Set(); + set.forEach(value => { + const newValue = f(value); + if (newValue !== undefined) { + result.add(newValue); + } + }); + return result; + } + } + + export function tryAddToSet(set: Set, value: T) { + if (!set.has(value)) { + set.add(value); + return true; + } + return false; + } + export const emptyIterator: Iterator = { next: () => ({ value: undefined as never, done: true }) }; export function singleIterator(value: T): Iterator { @@ -587,20 +660,21 @@ namespace ts { return result; } - export function mapEntries(map: ReadonlyMap, f: (key: string, value: T) => [string, U]): Map; - export function mapEntries(map: ReadonlyMap | undefined, f: (key: string, value: T) => [string, U]): Map | undefined; - export function mapEntries(map: ReadonlyMap | undefined, f: (key: string, value: T) => [string, U]): Map | undefined { + export function mapEntries(map: ReadonlyMap, f: (key: K1, value: V1) => readonly [K2, V2]): Map; + export function mapEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): Map | undefined; + export function mapEntries(map: ReadonlyMap | undefined, f: (key: K1, value: V1) => readonly [K2, V2]): Map | undefined { if (!map) { return undefined; } - const result = createMap(); + const result = new Map(); map.forEach((value, key) => { const [newKey, newValue] = f(key, value); result.set(newKey, newValue); }); return result; } + export function some(array: readonly T[] | undefined): array is readonly T[]; export function some(array: readonly T[] | undefined, predicate: (value: T) => boolean): boolean; export function some(array: readonly T[] | undefined, predicate?: (value: T) => boolean): boolean { @@ -1268,10 +1342,12 @@ namespace ts { * the same key with the given 'makeKey' function, then the element with the higher * index in the array will be the one associated with the produced key. */ - export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined): Map; - export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined, makeValue: (value: T) => U): Map; - export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined, makeValue: (value: T) => T | U = identity): Map { - const result = createMap(); + export function arrayToMap(array: readonly V[], makeKey: (value: V) => K | undefined): Map; + export function arrayToMap(array: readonly V1[], makeKey: (value: V1) => K | undefined, makeValue: (value: V1) => V2): Map; + export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined): Map; + export function arrayToMap(array: readonly T[], makeKey: (value: T) => string | undefined, makeValue: (value: T) => U): Map; + export function arrayToMap(array: readonly V1[], makeKey: (value: V1) => K | undefined, makeValue: (value: V1) => V1 | V2 = identity): Map { + const result = new Map(); for (const value of array) { const key = makeKey(value); if (key !== undefined) result.set(key, makeValue(value)); @@ -1289,10 +1365,10 @@ namespace ts { return result; } - export function arrayToMultiMap(values: readonly T[], makeKey: (value: T) => string): MultiMap; - export function arrayToMultiMap(values: readonly T[], makeKey: (value: T) => string, makeValue: (value: T) => U): MultiMap; - export function arrayToMultiMap(values: readonly T[], makeKey: (value: T) => string, makeValue: (value: T) => T | U = identity): MultiMap { - const result = createMultiMap(); + export function arrayToMultiMap(values: readonly V[], makeKey: (value: V) => K): MultiMap; + export function arrayToMultiMap(values: readonly V[], makeKey: (value: V) => K, makeValue: (value: V) => U): MultiMap; + export function arrayToMultiMap(values: readonly V[], makeKey: (value: V) => K, makeValue: (value: V) => V | U = identity): MultiMap { + const result = createMultiMap(); for (const value of values) { result.add(makeKey(value), makeValue(value)); } @@ -1349,35 +1425,29 @@ namespace ts { return fn ? fn.bind(obj) : undefined; } - export function mapMap(map: Map, f: (t: T, key: string) => [string, U]): Map; - export function mapMap(map: UnderscoreEscapedMap, f: (t: T, key: __String) => [string, U]): Map; - export function mapMap(map: Map | UnderscoreEscapedMap, f: ((t: T, key: string) => [string, U]) | ((t: T, key: __String) => [string, U])): Map { - const result = createMap(); - map.forEach((t: T, key: string & __String) => result.set(...(f(t, key)))); - return result; - } - - export interface MultiMap extends Map { + export interface MultiMap extends Map { /** * Adds the value to an array of values associated with the key, and returns the array. * Creates the array if it does not already exist. */ - add(key: string, value: T): T[]; + add(key: K, value: V): V[]; /** * Removes a value from an array of values associated with the key. * Does not preserve the order of those values. * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. */ - remove(key: string, value: T): void; + remove(key: K, value: V): void; } - export function createMultiMap(): MultiMap { - const map = createMap() as MultiMap; + export function createMultiMap(): MultiMap; + export function createMultiMap(): MultiMap; + export function createMultiMap(): MultiMap { + const map = new Map() as MultiMap; map.add = multiMapAdd; map.remove = multiMapRemove; return map; } - function multiMapAdd(this: MultiMap, key: string, value: T) { + function multiMapAdd(this: MultiMap, key: K, value: V) { let values = this.get(key); if (values) { values.push(value); @@ -1387,7 +1457,7 @@ namespace ts { } return values; } - function multiMapRemove(this: MultiMap, key: string, value: T) { + function multiMapRemove(this: MultiMap, key: K, value: V) { const values = this.get(key); if (values) { unorderedRemoveItem(values, value); @@ -1412,7 +1482,7 @@ namespace ts { } export function createUnderscoreEscapedMultiMap(): UnderscoreEscapedMultiMap { - return createMultiMap() as UnderscoreEscapedMultiMap; + return createMultiMap() as UnderscoreEscapedMultiMap; } /** @@ -1653,7 +1723,7 @@ namespace ts { * Case-insensitive comparisons compare both strings one code-point at a time using the integer * value of each code-point after applying `toUpperCase` to each string. We always map both * strings to their upper-case form as some unicode characters do not properly round-trip to - * lowercase (such as `ẞ` (German sharp capital s)). + * lowercase (such as `ẞ` (German sharp capital s)). */ export function compareStringsCaseInsensitive(a: string, b: string) { if (a === b) return Comparison.EqualTo; @@ -1725,7 +1795,7 @@ namespace ts { // // For case insensitive comparisons we always map both strings to their // upper-case form as some unicode characters do not properly round-trip to - // lowercase (such as `ẞ` (German sharp capital s)). + // lowercase (such as `ẞ` (German sharp capital s)). return (a, b) => compareWithCallback(a, b, compareDictionaryOrder); function compareDictionaryOrder(a: string, b: string) { diff --git a/src/compiler/corePublic.ts b/src/compiler/corePublic.ts index f77b4bda4c260..f580dc5b92451 100644 --- a/src/compiler/corePublic.ts +++ b/src/compiler/corePublic.ts @@ -22,54 +22,57 @@ namespace ts { " __sortedArrayBrand": any; } - /** ES6 Map interface, only read methods included. */ - export interface ReadonlyMap { - get(key: string): T | undefined; - has(key: string): boolean; - forEach(action: (value: T, key: string) => void): void; + /** Common read methods for ES6 Map/Set. */ + export interface ReadonlyCollection { readonly size: number; - keys(): Iterator; - values(): Iterator; - entries(): Iterator<[string, T]>; + has(key: K): boolean; + keys(): Iterator; } - /** ES6 Map interface. */ - export interface Map extends ReadonlyMap { - set(key: string, value: T): this; - delete(key: string): boolean; + /** Common write methods for ES6 Map/Set. */ + export interface Collection extends ReadonlyCollection { + delete(key: K): boolean; clear(): void; } + /** ES6 Map interface, only read methods included. */ + export interface ReadonlyMap extends ReadonlyCollection { + get(key: K): V | undefined; + values(): Iterator; + entries(): Iterator<[K, V]>; + forEach(action: (value: V, key: K) => void): void; + } + + /** ES6 Map interface. */ + export interface Map extends ReadonlyMap, Collection { + set(key: K, value: V): this; + } + /* @internal */ export interface MapConstructor { // eslint-disable-next-line @typescript-eslint/prefer-function-type - new (): Map; + new (iterable?: readonly (readonly [K, V])[] | ReadonlyMap): Map; } - /** - * Returns the native Map implementation if it is available and compatible (i.e. supports iteration). - */ - /* @internal */ - export function tryGetNativeMap(): MapConstructor | undefined { - // Internet Explorer's Map doesn't support iteration, so don't use it. - // Natives - // NOTE: TS doesn't strictly allow in-line declares, but if we suppress the error, the declaration - // is still used for typechecking _and_ correctly elided, which is out goal, as this prevents us from - // needing to pollute an outer scope with a declaration of `Map` just to satisfy the checks in this function - //@ts-ignore - declare const Map: (new () => Map) | undefined; - // eslint-disable-next-line no-in-operator - return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined; + /** ES6 Set interface, only read methods included. */ + export interface ReadonlySet extends ReadonlyCollection { + has(value: T): boolean; + values(): Iterator; + entries(): Iterator<[T, T]>; + forEach(action: (value: T, key: T) => void): void; + } + + /** ES6 Set interface. */ + export interface Set extends ReadonlySet, Collection { + add(value: T): this; + delete(value: T): boolean; } /* @internal */ - export const Map: MapConstructor = tryGetNativeMap() || (() => { - // NOTE: createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it. - if (typeof createMapShim === "function") { - return createMapShim(); - } - throw new Error("TypeScript requires an environment that provides a compatible native Map implementation."); - })(); + export interface SetConstructor { + // eslint-disable-next-line @typescript-eslint/prefer-function-type + new (iterable?: readonly T[] | ReadonlySet): Set; + } /** ES6 Iterator type. */ export interface Iterator { @@ -94,4 +97,28 @@ namespace ts { EqualTo = 0, GreaterThan = 1 } + + /* @internal */ + export namespace NativeCollections { + declare const Map: MapConstructor | undefined; + declare const Set: SetConstructor | undefined; + + /** + * Returns the native Map implementation if it is available and compatible (i.e. supports iteration). + */ + export function tryGetNativeMap(): MapConstructor | undefined { + // Internet Explorer's Map doesn't support iteration, so don't use it. + // eslint-disable-next-line no-in-operator + return typeof Map !== "undefined" && "entries" in Map.prototype && new Map([[0, 0]]).size === 1 ? Map : undefined; + } + + /** + * Returns the native Set implementation if it is available and compatible (i.e. supports iteration). + */ + export function tryGetNativeSet(): SetConstructor | undefined { + // Internet Explorer's Set doesn't support iteration, so don't use it. + // eslint-disable-next-line no-in-operator + return typeof Set !== "undefined" && "entries" in Set.prototype && new Set([0]).size === 1 ? Set : undefined; + } + } } \ No newline at end of file diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c9829f914fbc2..6cbe25b92671e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -839,11 +839,11 @@ namespace ts { let currentSourceFile: SourceFile | undefined; let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes. let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables. - let generatedNames: Map; // Set of names generated by the NameGenerator. + let generatedNames: Set; // Set of names generated by the NameGenerator. let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes. let tempFlags: TempFlags; // TempFlags for the current name generation scope. - let reservedNamesStack: Map[]; // Stack of TempFlags reserved in enclosing name generation scopes. - let reservedNames: Map; // TempFlags to reserve in nested name generation scopes. + let reservedNamesStack: Set[]; // Stack of TempFlags reserved in enclosing name generation scopes. + let reservedNames: Set; // TempFlags to reserve in nested name generation scopes. let preserveSourceNewlines = printerOptions.preserveSourceNewlines; // Can be overridden inside nodes with the `IgnoreSourceNewlines` emit flag. let writer: EmitTextWriter; @@ -1119,7 +1119,7 @@ namespace ts { function reset() { nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; - generatedNames = createMap(); + generatedNames = new Set(); tempFlagsStack = []; tempFlags = TempFlags.Auto; reservedNamesStack = []; @@ -3718,7 +3718,7 @@ namespace ts { * Emits any prologue directives at the start of a Statement list, returning the * number of prologue directives written to the output. */ - function emitPrologueDirectives(statements: readonly Node[], sourceFile?: SourceFile, seenPrologueDirectives?: Map, recordBundleFileSection?: true): number { + function emitPrologueDirectives(statements: readonly Node[], sourceFile?: SourceFile, seenPrologueDirectives?: Set, recordBundleFileSection?: true): number { let needsToSetSourceFile = !!sourceFile; for (let i = 0; i < statements.length; i++) { const statement = statements[i]; @@ -3734,7 +3734,7 @@ namespace ts { emit(statement); if (recordBundleFileSection && bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prologue, data: statement.expression.text }); if (seenPrologueDirectives) { - seenPrologueDirectives.set(statement.expression.text, true); + seenPrologueDirectives.add(statement.expression.text); } } } @@ -3747,7 +3747,7 @@ namespace ts { return statements.length; } - function emitUnparsedPrologues(prologues: readonly UnparsedPrologue[], seenPrologueDirectives: Map) { + function emitUnparsedPrologues(prologues: readonly UnparsedPrologue[], seenPrologueDirectives: Set) { for (const prologue of prologues) { if (!seenPrologueDirectives.has(prologue.data)) { writeLine(); @@ -3755,7 +3755,7 @@ namespace ts { emit(prologue); if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Prologue, data: prologue.data }); if (seenPrologueDirectives) { - seenPrologueDirectives.set(prologue.data, true); + seenPrologueDirectives.add(prologue.data); } } } @@ -3766,7 +3766,7 @@ namespace ts { emitPrologueDirectives(sourceFileOrBundle.statements, sourceFileOrBundle); } else { - const seenPrologueDirectives = createMap(); + const seenPrologueDirectives = new Set(); for (const prepend of sourceFileOrBundle.prepends) { emitUnparsedPrologues((prepend as UnparsedSource).prologues, seenPrologueDirectives); } @@ -3778,7 +3778,7 @@ namespace ts { } function getPrologueDirectivesFromBundledSourceFiles(bundle: Bundle): SourceFilePrologueInfo[] | undefined { - const seenPrologueDirectives = createMap(); + const seenPrologueDirectives = new Set(); let prologues: SourceFilePrologueInfo[] | undefined; for (let index = 0; index < bundle.sourceFiles.length; index++) { const sourceFile = bundle.sourceFiles[index]; @@ -3787,7 +3787,7 @@ namespace ts { for (const statement of sourceFile.statements) { if (!isPrologueDirective(statement)) break; if (seenPrologueDirectives.has(statement.expression.text)) continue; - seenPrologueDirectives.set(statement.expression.text, true); + seenPrologueDirectives.add(statement.expression.text); (directives || (directives = [])).push({ pos: statement.pos, end: statement.end, @@ -4539,9 +4539,9 @@ namespace ts { function reserveNameInNestedScopes(name: string) { if (!reservedNames || reservedNames === lastOrUndefined(reservedNamesStack)) { - reservedNames = createMap(); + reservedNames = new Set(); } - reservedNames.set(name, true); + reservedNames.add(name); } function generateNames(node: Node | undefined) { @@ -4758,7 +4758,7 @@ namespace ts { reserveNameInNestedScopes(baseName); } else { - generatedNames.set(baseName, true); + generatedNames.add(baseName); } return baseName; } @@ -4775,7 +4775,7 @@ namespace ts { reserveNameInNestedScopes(generatedName); } else { - generatedNames.set(generatedName, true); + generatedNames.add(generatedName); } return generatedName; } diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index 9a76e759a2671..e59c06f151647 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -841,7 +841,7 @@ namespace ts { };` }; - let allUnscopedEmitHelpers: ReadonlyMap | undefined; + let allUnscopedEmitHelpers: ReadonlyMap | undefined; export function getAllUnscopedEmitHelpers() { return allUnscopedEmitHelpers || (allUnscopedEmitHelpers = arrayToMap([ diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 82e39ecfad460..50d7e9a4d0f53 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -442,8 +442,8 @@ namespace ts { * This assumes that any module id will have the same resolution for sibling files located in the same folder. */ export interface ModuleResolutionCache extends NonRelativeModuleNameResolutionCache { - getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map; - /*@internal*/ directoryToModuleNameMap: CacheWithRedirects>; + getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map; + /*@internal*/ directoryToModuleNameMap: CacheWithRedirects>; } /** @@ -472,18 +472,18 @@ namespace ts { /*@internal*/ export interface CacheWithRedirects { - ownMap: Map; - redirectsMap: Map>; - getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map; + ownMap: Map; + redirectsMap: Map>; + getOrCreateMapOfCacheRedirects(redirectedReference: ResolvedProjectReference | undefined): Map; clear(): void; setOwnOptions(newOptions: CompilerOptions): void; - setOwnMap(newOwnMap: Map): void; + setOwnMap(newOwnMap: Map): void; } /*@internal*/ export function createCacheWithRedirects(options?: CompilerOptions): CacheWithRedirects { - let ownMap: Map = createMap(); - const redirectsMap: Map> = createMap(); + let ownMap: Map = createMap(); + const redirectsMap = new Map>(); return { ownMap, redirectsMap, @@ -497,7 +497,7 @@ namespace ts { options = newOptions; } - function setOwnMap(newOwnMap: Map) { + function setOwnMap(newOwnMap: Map) { ownMap = newOwnMap; } @@ -523,7 +523,7 @@ namespace ts { /*@internal*/ export function createModuleResolutionCacheWithMaps( - directoryToModuleNameMap: CacheWithRedirects>, + directoryToModuleNameMap: CacheWithRedirects>, moduleNameToDirectoryMap: CacheWithRedirects, currentDirectory: string, getCanonicalFileName: GetCanonicalFileName): ModuleResolutionCache { @@ -532,7 +532,7 @@ namespace ts { function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) { const path = toPath(directoryName, currentDirectory, getCanonicalFileName); - return getOrCreateCache>(directoryToModuleNameMap, redirectedReference, path, createMap); + return getOrCreateCache>(directoryToModuleNameMap, redirectedReference, path, createMap); } function getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index ff50656a68ca5..2e8729b29c220 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -722,13 +722,13 @@ namespace ts { let currentToken: SyntaxKind; let nodeCount: number; - let identifiers: Map; - let privateIdentifiers: Map; + let identifiers: Map; + let privateIdentifiers: Map; let identifierCount: number; let parsingContext: ParsingContext; - let notParenthesizedArrow: Map | undefined; + let notParenthesizedArrow: Set | undefined; // Flags that dictate what parsing context we're in. For example: // Whether or not we are in strict parsing mode. All that changes in strict parsing mode is @@ -4121,13 +4121,13 @@ namespace ts { function parsePossibleParenthesizedArrowFunctionExpression(): ArrowFunction | undefined { const tokenPos = scanner.getTokenPos(); - if (notParenthesizedArrow && notParenthesizedArrow.has(tokenPos.toString())) { + if (notParenthesizedArrow?.has(tokenPos)) { return undefined; } const result = parseParenthesizedArrowFunctionExpression(/*allowAmbiguity*/ false); if (!result) { - (notParenthesizedArrow || (notParenthesizedArrow = createMap())).set(tokenPos.toString(), true); + (notParenthesizedArrow || (notParenthesizedArrow = new Set())).add(tokenPos); } return result; diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 4a7f5c99278b2..3da794c638b16 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -8,9 +8,9 @@ namespace ts.performance { let enabled = false; let profilerStart = 0; - let counts: Map; - let marks: Map; - let measures: Map; + let counts: Map; + let marks: Map; + let measures: Map; export interface Timer { enter(): void; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 63873b9ac5d95..8cee3aae854d7 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -126,7 +126,7 @@ namespace ts { } } - let outputFingerprints: Map; + let outputFingerprints: Map; function writeFileWorker(fileName: string, data: string, writeByteOrderMark: boolean) { if (!isWatchSet(options) || !system.createHash || !system.getModifiedTime) { system.writeFile(fileName, data, writeByteOrderMark); @@ -533,7 +533,7 @@ namespace ts { export const inferredTypesContainingFile = "__inferred type names__.ts"; interface DiagnosticCache { - perFile?: Map; + perFile?: Map; allDiagnostics?: readonly T[]; } @@ -702,14 +702,14 @@ namespace ts { let processingDefaultLibFiles: SourceFile[] | undefined; let processingOtherFiles: SourceFile[] | undefined; let files: SourceFile[]; - let symlinks: ReadonlyMap | undefined; + let symlinks: ReadonlyMap | undefined; let commonSourceDirectory: string; let diagnosticsProducingTypeChecker: TypeChecker; let noDiagnosticsTypeChecker: TypeChecker; let classifiableNames: UnderscoreEscapedMap; const ambientModuleNameToUnmodifiedFileName = createMap(); // Todo:: Use this to report why file was included in --extendedDiagnostics - let refFileMap: MultiMap | undefined; + let refFileMap: MultiMap | undefined; const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache = {}; const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {}; @@ -803,9 +803,9 @@ namespace ts { // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files let resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined; - let projectReferenceRedirects: Map | undefined; - let mapFromFileToProjectReferenceRedirects: Map | undefined; - let mapFromToProjectReferenceRedirectSource: Map | undefined; + let projectReferenceRedirects: Map | undefined; + let mapFromFileToProjectReferenceRedirects: Map | undefined; + let mapFromToProjectReferenceRedirectSource: Map | undefined; const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect?.() && !options.disableSourceOfProjectReferenceRedirect; @@ -2028,7 +2028,7 @@ namespace ts { ): readonly U[] { const cachedResult = sourceFile - ? cache.perFile && cache.perFile.get(sourceFile.path) + ? cache.perFile?.get(sourceFile.path) : cache.allDiagnostics; if (cachedResult) { @@ -2036,10 +2036,7 @@ namespace ts { } const result = getDiagnostics(sourceFile, cancellationToken); if (sourceFile) { - if (!cache.perFile) { - cache.perFile = createMap(); - } - cache.perFile.set(sourceFile.path, result); + (cache.perFile || (cache.perFile = new Map())).set(sourceFile.path, result); } else { cache.allDiagnostics = result; @@ -2546,7 +2543,7 @@ namespace ts { */ function getResolvedProjectReferenceToRedirect(fileName: string) { if (mapFromFileToProjectReferenceRedirects === undefined) { - mapFromFileToProjectReferenceRedirects = createMap(); + mapFromFileToProjectReferenceRedirects = new Map(); forEachResolvedProjectReference((referencedProject, referenceProjectPath) => { // not input file from the referenced project, ignore if (referencedProject && @@ -2892,13 +2889,13 @@ namespace ts { function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean { let allFilesBelongToPath = true; const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory)); - let rootPaths: Map | undefined; + let rootPaths: Set | undefined; for (const sourceFile of sourceFiles) { if (!sourceFile.isDeclarationFile) { const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory)); if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) { - if (!rootPaths) rootPaths = arrayToSet(rootNames, toPath); + if (!rootPaths) rootPaths = new Set(rootNames.map(toPath)); addProgramDiagnosticAtRefPath( sourceFile, rootPaths, @@ -2916,7 +2913,7 @@ namespace ts { function parseProjectReferenceConfigFile(ref: ProjectReference): ResolvedProjectReference | undefined { if (!projectReferenceRedirects) { - projectReferenceRedirects = createMap(); + projectReferenceRedirects = new Map(); } // The actual filename (i.e. add "/tsconfig.json" if necessary) @@ -3015,7 +3012,7 @@ namespace ts { // List of collected files is complete; validate exhautiveness if this is a project with a file list if (options.composite) { - const rootPaths = arrayToSet(rootNames, toPath); + const rootPaths = new Set(rootNames.map(toPath)); for (const file of files) { // Ignore file that is not emitted if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) { @@ -3204,7 +3201,7 @@ namespace ts { // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files if (!options.noEmit && !options.suppressOutputPathCheck) { const emitHost = getEmitHost(); - const emitFilesSeen = createMap(); + const emitFilesSeen = new Set(); forEachEmittedFile(emitHost, (emitFileNames) => { if (!options.emitDeclarationOnly) { verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen); @@ -3214,7 +3211,7 @@ namespace ts { } // Verify that all the emit files are unique and don't overwrite input files - function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Map) { + function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Set) { if (emitFileName) { const emitFilePath = toPath(emitFileName); // Report error if the output overwrites input file @@ -3235,7 +3232,7 @@ namespace ts { blockEmittingOfFile(emitFileName, createCompilerDiagnostic(Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName)); } else { - emitFilesSeen.set(emitFileKey, true); + emitFilesSeen.add(emitFileKey); } } } @@ -3262,8 +3259,8 @@ namespace ts { return createFileDiagnostic(refFile, pos, end - pos, message, ...args); } - function addProgramDiagnosticAtRefPath(file: SourceFile, rootPaths: Map, message: DiagnosticMessage, ...args: (string | number | undefined)[]) { - const refPaths = refFileMap && refFileMap.get(file.path); + function addProgramDiagnosticAtRefPath(file: SourceFile, rootPaths: Set, message: DiagnosticMessage, ...args: (string | number | undefined)[]) { + const refPaths = refFileMap?.get(file.path); const refPathToReportErrorOn = forEach(refPaths, refPath => rootPaths.has(refPath.file) ? refPath : undefined) || elementAt(refPaths, 0); programDiagnostics.add( @@ -3455,7 +3452,7 @@ namespace ts { return comparePaths(file1, file2, currentDirectory, !host.useCaseSensitiveFileNames()) === Comparison.EqualTo; } - function getProbableSymlinks(): ReadonlyMap { + function getProbableSymlinks(): ReadonlyMap { if (host.getSymlinks) { return host.getSymlinks(); } @@ -3481,9 +3478,9 @@ namespace ts { } function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSourceOfProjectReferenceRedirect) { - let mapOfDeclarationDirectories: Map | undefined; - let symlinkedDirectories: Map | undefined; - let symlinkedFiles: Map | undefined; + let setOfDeclarationDirectories: Set | undefined; + let symlinkedDirectories: Map | undefined; + let symlinkedFiles: Map | undefined; const originalFileExists = host.compilerHost.fileExists; const originalDirectoryExists = host.compilerHost.directoryExists; @@ -3506,19 +3503,19 @@ namespace ts { if (!host.getResolvedProjectReferences()) return false; - if (!mapOfDeclarationDirectories) { - mapOfDeclarationDirectories = createMap(); + if (!setOfDeclarationDirectories) { + setOfDeclarationDirectories = new Set(); host.forEachResolvedProjectReference(ref => { if (!ref) return; const out = outFile(ref.commandLine.options); if (out) { - mapOfDeclarationDirectories!.set(getDirectoryPath(host.toPath(out)), true); + setOfDeclarationDirectories!.add(getDirectoryPath(host.toPath(out))); } else { // Set declaration's in different locations only, if they are next to source the directory present doesnt change const declarationDir = ref.commandLine.options.declarationDir || ref.commandLine.options.outDir; if (declarationDir) { - mapOfDeclarationDirectories!.set(host.toPath(declarationDir), true); + setOfDeclarationDirectories!.add(host.toPath(declarationDir)); } } }); @@ -3576,7 +3573,7 @@ namespace ts { const dirPath = host.toPath(dir); const dirPathWithTrailingDirectorySeparator = `${dirPath}${directorySeparator}`; return forEachKey( - mapOfDeclarationDirectories!, + setOfDeclarationDirectories!, declDirPath => dirPath === declDirPath || // Any parent directory of declaration dir startsWith(declDirPath, dirPathWithTrailingDirectorySeparator) || @@ -3590,7 +3587,7 @@ namespace ts { // Because we already watch node_modules, handle symlinks in there if (!originalRealpath || !stringContains(directory, nodeModulesPathPart)) return; - if (!symlinkedDirectories) symlinkedDirectories = createMap(); + if (!symlinkedDirectories) symlinkedDirectories = new Map(); const directoryPath = ensureTrailingDirectorySeparator(host.toPath(directory)); if (symlinkedDirectories.has(directoryPath)) return; @@ -3629,7 +3626,7 @@ namespace ts { if (!symlinkedDirectory || !startsWith(fileOrDirectoryPath, directoryPath)) return undefined; const result = fileOrDirectoryExistsUsingSource(fileOrDirectoryPath.replace(directoryPath, symlinkedDirectory.realPath)); if (isFile && result) { - if (!symlinkedFiles) symlinkedFiles = createMap(); + if (!symlinkedFiles) symlinkedFiles = new Map(); // Store the real path for the file' const absolutePath = getNormalizedAbsolutePath(fileOrDirectory, host.compilerHost.getCurrentDirectory()); symlinkedFiles.set( diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 547a8f4c9d243..dc79fe0fabff9 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -13,7 +13,7 @@ namespace ts { invalidateResolutionOfFile(filePath: Path): void; removeResolutionsOfFile(filePath: Path): void; removeResolutionsFromProjectReferenceRedirects(filePath: Path): void; - setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map): void; + setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map): void; createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution; hasChangedAutomaticTypeDirectiveNames(): boolean; @@ -143,8 +143,8 @@ namespace ts { export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string | undefined, logChangesWhenResolvingModule: boolean): ResolutionCache { let filesWithChangedSetOfUnresolvedImports: Path[] | undefined; - let filesWithInvalidatedResolutions: Map | undefined; - let filesWithInvalidatedNonRelativeUnresolvedImports: ReadonlyMap | undefined; + let filesWithInvalidatedResolutions: Set | undefined; + let filesWithInvalidatedNonRelativeUnresolvedImports: ReadonlyMap | undefined; const nonRelativeExternalModuleResolutions = createMultiMap(); const resolutionsWithFailedLookups: ResolutionWithFailedLookupLocations[] = []; @@ -161,8 +161,8 @@ namespace ts { // The resolvedModuleNames and resolvedTypeReferenceDirectives are the cache of resolutions per file. // The key in the map is source file's path. // The values are Map of resolutions with key being name lookedup. - const resolvedModuleNames = createMap>(); - const perDirectoryResolvedModuleNames: CacheWithRedirects> = createCacheWithRedirects(); + const resolvedModuleNames = new Map>(); + const perDirectoryResolvedModuleNames: CacheWithRedirects> = createCacheWithRedirects(); const nonRelativeModuleNameCache: CacheWithRedirects = createCacheWithRedirects(); const moduleResolutionCache = createModuleResolutionCacheWithMaps( perDirectoryResolvedModuleNames, @@ -171,8 +171,8 @@ namespace ts { resolutionHost.getCanonicalFileName ); - const resolvedTypeReferenceDirectives = createMap>(); - const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects> = createCacheWithRedirects(); + const resolvedTypeReferenceDirectives = new Map>(); + const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects> = createCacheWithRedirects(); /** * These are the extensions that failed lookup files will have by default, @@ -334,8 +334,8 @@ namespace ts { names: readonly string[]; containingFile: string; redirectedReference: ResolvedProjectReference | undefined; - cache: Map>; - perDirectoryCacheWithRedirects: CacheWithRedirects>; + cache: Map>; + perDirectoryCacheWithRedirects: CacheWithRedirects>; loader: (name: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference) => T; getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName; shouldRetryResolution: (t: T) => boolean; @@ -684,7 +684,7 @@ namespace ts { } function removeResolutionsOfFileFromCache( - cache: Map>, + cache: Map>, filePath: Path, getResolutionWithResolvedFileName: GetResolutionWithResolvedFileName, ) { @@ -722,7 +722,7 @@ namespace ts { if (resolution.isInvalidated || !canInvalidate(resolution)) continue; resolution.isInvalidated = invalidated = true; for (const containingFilePath of Debug.assertDefined(resolution.files)) { - (filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap())).set(containingFilePath, true); + (filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = new Set())).add(containingFilePath); // When its a file with inferred types resolution, invalidate type reference directive resolution hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames || containingFilePath.endsWith(inferredTypesContainingFile); } @@ -741,7 +741,7 @@ namespace ts { } } - function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: ReadonlyMap) { + function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: ReadonlyMap) { Debug.assert(filesWithInvalidatedNonRelativeUnresolvedImports === filesMap || filesWithInvalidatedNonRelativeUnresolvedImports === undefined); filesWithInvalidatedNonRelativeUnresolvedImports = filesMap; } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index b578db78ae1f5..ab2a37032ced2 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -330,7 +330,7 @@ namespace ts { lookupInUnicodeMap(code, unicodeES3IdentifierPart); } - function makeReverseMap(source: Map): string[] { + function makeReverseMap(source: Map): string[] { const result: string[] = []; source.forEach((value, name) => { result[value] = name; diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 5fae21601a388..3e70d335566bb 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -16,7 +16,7 @@ namespace ts { let sourcesContent: (string | null)[] | undefined; const names: string[] = []; - let nameToNameIndexMap: Map | undefined; + let nameToNameIndexMap: Map | undefined; let mappings = ""; // Last recorded and encoded mappings @@ -622,7 +622,7 @@ namespace ts { const generatedAbsoluteFilePath = getNormalizedAbsolutePath(map.file, mapDirectory); const generatedFile = host.getSourceFileLike(generatedAbsoluteFilePath); const sourceFileAbsolutePaths = map.sources.map(source => getNormalizedAbsolutePath(source, sourceRoot)); - const sourceToSourceIndexMap = createMapFromEntries(sourceFileAbsolutePaths.map((source, i) => [host.getCanonicalFileName(source), i] as [string, number])); + const sourceToSourceIndexMap = new Map(sourceFileAbsolutePaths.map((source, i) => [host.getCanonicalFileName(source), i])); let decodedMappings: readonly MappedPosition[] | undefined; let generatedMappings: SortedReadonlyArray | undefined; let sourceMappings: readonly SortedReadonlyArray[] | undefined; diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 725d2330d214a..fcbaddb966ed2 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -310,7 +310,7 @@ namespace ts { function createUseFsEventsOnParentDirectoryWatchFile(fsWatch: FsWatch, useCaseSensitiveFileNames: boolean): HostWatchFile { // One file can have multiple watchers const fileWatcherCallbacks = createMultiMap(); - const dirWatchers = createMap(); + const dirWatchers = new Map(); const toCanonicalName = createGetCanonicalFileName(useCaseSensitiveFileNames); return nonPollingWatchFile; @@ -370,7 +370,7 @@ namespace ts { watcher: FileWatcher; refCount: number; } - const cache = createMap(); + const cache = new Map(); const callbacksCache = createMultiMap(); const toCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); @@ -473,9 +473,9 @@ namespace ts { refCount: number; } - const cache = createMap(); - const callbackCache = createMultiMap<{ dirName: string; callback: DirectoryWatcherCallback; }>(); - const cacheToUpdateChildWatches = createMap<{ dirName: string; options: WatchOptions | undefined; fileNames: string[]; }>(); + const cache = new Map(); + const callbackCache = createMultiMap(); + const cacheToUpdateChildWatches = new Map(); let timerToUpdateChildWatches: any; const filePathComparer = getStringComparer(!host.useCaseSensitiveFileNames); @@ -538,7 +538,7 @@ namespace ts { }; } - type InvokeMap = Map; + type InvokeMap = Map; function invokeCallbacks(dirPath: Path, fileName: string): void; function invokeCallbacks(dirPath: Path, invokeMap: InvokeMap, fileNames: string[] | undefined): void; function invokeCallbacks(dirPath: Path, fileNameOrInvokeMap: string | InvokeMap, fileNames?: string[]) { @@ -608,7 +608,7 @@ namespace ts { timerToUpdateChildWatches = undefined; sysLog(`sysLog:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size}`); const start = timestamp(); - const invokeMap = createMap(); + const invokeMap = new Map(); while (!timerToUpdateChildWatches && cacheToUpdateChildWatches.size) { const { value: [dirPath, { dirName, options, fileNames }], done } = cacheToUpdateChildWatches.entries().next(); @@ -616,8 +616,8 @@ namespace ts { cacheToUpdateChildWatches.delete(dirPath); // Because the child refresh is fresh, we would need to invalidate whole root directory being watched // to ensure that all the changes are reflected at this time - const hasChanges = updateChildWatches(dirName, dirPath as Path, options); - invokeCallbacks(dirPath as Path, invokeMap, hasChanges ? undefined : fileNames); + const hasChanges = updateChildWatches(dirName, dirPath, options); + invokeCallbacks(dirPath, invokeMap, hasChanges ? undefined : fileNames); } sysLog(`sysLog:: invokingWatchers:: ${timestamp() - start}ms:: ${cacheToUpdateChildWatches.size}`); @@ -1316,7 +1316,7 @@ namespace ts { */ function cleanupPaths(profile: import("inspector").Profiler.Profile) { let externalFileCounter = 0; - const remappedPaths = createMap(); + const remappedPaths = new Map(); const normalizedDir = normalizeSlashes(__dirname); // Windows rooted dir names need an extra `/` prepended to be valid file:/// urls const fileUrlRoot = `file://${getRootLength(normalizedDir) === 1 ? "" : "/"}${normalizedDir}`; diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index ba4c01beb8ba8..8d05347bc6f31 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -58,9 +58,9 @@ namespace ts { let needsScopeFixMarker = false; let resultHasScopeMarker = false; let enclosingDeclaration: Node; - let necessaryTypeReferences: Map | undefined; + let necessaryTypeReferences: Set | undefined; let lateMarkedStatements: LateVisibilityPaintedStatement[] | undefined; - let lateStatementReplacementMap: Map>; + let lateStatementReplacementMap: Map>; let suppressNewDiagnosticContexts: boolean; let exportedModulesFromDeclarationEmit: Symbol[] | undefined; @@ -81,8 +81,8 @@ namespace ts { let errorNameNode: DeclarationName | undefined; let currentSourceFile: SourceFile; - let refs: Map; - let libs: Map; + let refs: Map; + let libs: Map; let emittedImports: readonly AnyImportSyntax[] | undefined; // must be declared in container so it can be `undefined` while transformer's first pass const resolver = context.getEmitResolver(); const options = context.getCompilerOptions(); @@ -93,9 +93,9 @@ namespace ts { if (!typeReferenceDirectives) { return; } - necessaryTypeReferences = necessaryTypeReferences || createMap(); + necessaryTypeReferences = necessaryTypeReferences || new Set(); for (const ref of typeReferenceDirectives) { - necessaryTypeReferences.set(ref, true); + necessaryTypeReferences.add(ref); } } @@ -402,7 +402,7 @@ namespace ts { } } - function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: Map) { + function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: Map) { if (noResolve || (!isUnparsedSource(sourceFile) && isSourceFileJS(sourceFile))) return ret; forEach(sourceFile.referencedFiles, f => { const elem = host.getSourceFileFromReference(sourceFile, f); @@ -413,7 +413,7 @@ namespace ts { return ret; } - function collectLibs(sourceFile: SourceFile | UnparsedSource, ret: Map) { + function collectLibs(sourceFile: SourceFile | UnparsedSource, ret: Map) { forEach(sourceFile.libReferenceDirectives, ref => { const lib = host.getLibFileFromReference(ref); if (lib) { diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index dc4078caa2433..f9d94f3ee5976 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -72,15 +72,15 @@ namespace ts { * set of labels that occurred inside the converted loop * used to determine if labeled jump can be emitted as is or it should be dispatched to calling code */ - labels?: Map; + labels?: Map; /* * collection of labeled jumps that transfer control outside the converted loop. * maps store association 'label -> labelMarker' where * - label - value of label as it appear in code * - label marker - return value that should be interpreted by calling code as 'jump to