Skip to content

Commit eb2f4e2

Browse files
authored
Switch to ES Map/Set internally (#33771)
* Add full implemention of Map and Set to shims * Update default Map interface * Remove WeakMap/WeakSet * Add tests for set shim * Update most usages of Map<K, true> to Set * PR Feedback * Fix lint issues * Change key in fsWatchCallback * Simpler shim, more tests * Fix typo in collection shim
1 parent 95690c0 commit eb2f4e2

File tree

87 files changed

+1768
-1136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1768
-1136
lines changed

src/compiler/binder.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace ts {
1515
referenced: boolean;
1616
}
1717

18-
export function getModuleInstanceState(node: ModuleDeclaration, visited?: Map<ModuleInstanceState | undefined>): ModuleInstanceState {
18+
export function getModuleInstanceState(node: ModuleDeclaration, visited?: Map<number, ModuleInstanceState | undefined>): ModuleInstanceState {
1919
if (node.body && !node.body.parent) {
2020
// getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already
2121
setParent(node.body, node);
@@ -24,8 +24,8 @@ namespace ts {
2424
return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated;
2525
}
2626

27-
function getModuleInstanceStateCached(node: Node, visited = createMap<ModuleInstanceState | undefined>()) {
28-
const nodeId = "" + getNodeId(node);
27+
function getModuleInstanceStateCached(node: Node, visited = new Map<number, ModuleInstanceState | undefined>()) {
28+
const nodeId = getNodeId(node);
2929
if (visited.has(nodeId)) {
3030
return visited.get(nodeId) || ModuleInstanceState.NonInstantiated;
3131
}
@@ -35,7 +35,7 @@ namespace ts {
3535
return result;
3636
}
3737

38-
function getModuleInstanceStateWorker(node: Node, visited: Map<ModuleInstanceState | undefined>): ModuleInstanceState {
38+
function getModuleInstanceStateWorker(node: Node, visited: Map<number, ModuleInstanceState | undefined>): ModuleInstanceState {
3939
// A module is uninstantiated if it contains only
4040
switch (node.kind) {
4141
// 1. interface declarations, type alias declarations
@@ -107,7 +107,7 @@ namespace ts {
107107
return ModuleInstanceState.Instantiated;
108108
}
109109

110-
function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: Map<ModuleInstanceState | undefined>) {
110+
function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: Map<number, ModuleInstanceState | undefined>) {
111111
const name = specifier.propertyName || specifier.name;
112112
let p: Node | undefined = specifier.parent;
113113
while (p) {
@@ -2884,8 +2884,7 @@ namespace ts {
28842884

28852885
function addLateBoundAssignmentDeclarationToSymbol(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol | undefined) {
28862886
if (symbol) {
2887-
const members = symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = createMap());
2888-
members.set("" + getNodeId(node), node);
2887+
(symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = new Map())).set(getNodeId(node), node);
28892888
}
28902889
}
28912890

src/compiler/builder.ts

+59-61
Large diffs are not rendered by default.

src/compiler/builderState.ts

+37-51
Original file line numberDiff line numberDiff line change
@@ -15,42 +15,42 @@ namespace ts {
1515
/**
1616
* Information of the file eg. its version, signature etc
1717
*/
18-
fileInfos: ReadonlyMap<BuilderState.FileInfo>;
18+
fileInfos: ReadonlyMap<Path, BuilderState.FileInfo>;
1919
/**
2020
* Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled
2121
* Otherwise undefined
2222
* Thus non undefined value indicates, module emit
2323
*/
24-
readonly referencedMap?: ReadonlyMap<BuilderState.ReferencedSet> | undefined;
24+
readonly referencedMap?: ReadonlyMap<Path, BuilderState.ReferencedSet> | undefined;
2525
/**
2626
* Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled
2727
* Otherwise undefined
2828
*/
29-
readonly exportedModulesMap?: ReadonlyMap<BuilderState.ReferencedSet> | undefined;
29+
readonly exportedModulesMap?: ReadonlyMap<Path, BuilderState.ReferencedSet> | undefined;
3030
}
3131

3232
export interface BuilderState {
3333
/**
3434
* Information of the file eg. its version, signature etc
3535
*/
36-
fileInfos: Map<BuilderState.FileInfo>;
36+
fileInfos: Map<Path, BuilderState.FileInfo>;
3737
/**
3838
* Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled
3939
* Otherwise undefined
4040
* Thus non undefined value indicates, module emit
4141
*/
42-
readonly referencedMap: ReadonlyMap<BuilderState.ReferencedSet> | undefined;
42+
readonly referencedMap: ReadonlyMap<Path, BuilderState.ReferencedSet> | undefined;
4343
/**
4444
* Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled
4545
* Otherwise undefined
4646
*/
47-
readonly exportedModulesMap: Map<BuilderState.ReferencedSet> | undefined;
47+
readonly exportedModulesMap: Map<Path, BuilderState.ReferencedSet> | undefined;
4848
/**
4949
* Map of files that have already called update signature.
5050
* That means hence forth these files are assumed to have
5151
* no change in their signature for this version of the program
5252
*/
53-
hasCalledUpdateShapeSignature: Map<true>;
53+
hasCalledUpdateShapeSignature: Set<Path>;
5454
/**
5555
* Cache of all files excluding default library file for the current program
5656
*/
@@ -73,7 +73,7 @@ namespace ts {
7373
/**
7474
* Referenced files with values for the keys as referenced file's path to be true
7575
*/
76-
export type ReferencedSet = ReadonlyMap<true>;
76+
export type ReferencedSet = ReadonlySet<Path>;
7777
/**
7878
* Compute the hash to store the shape of the file
7979
*/
@@ -83,7 +83,7 @@ namespace ts {
8383
* Exported modules to from declaration emit being computed.
8484
* This can contain false in the affected file path to specify that there are no exported module(types from other modules) for this file
8585
*/
86-
export type ComputingExportedModulesMap = Map<ReferencedSet | false>;
86+
export type ComputingExportedModulesMap = Map<Path, ReferencedSet | false>;
8787

8888
/**
8989
* Get the referencedFile from the imported module symbol
@@ -113,8 +113,8 @@ namespace ts {
113113
/**
114114
* Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true
115115
*/
116-
function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): Map<true> | undefined {
117-
let referencedFiles: Map<true> | undefined;
116+
function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): Set<Path> | undefined {
117+
let referencedFiles: Set<Path> | undefined;
118118

119119
// We need to use a set here since the code can contain the same import twice,
120120
// but that will only be one dependency.
@@ -185,28 +185,25 @@ namespace ts {
185185
}
186186

187187
function addReferencedFile(referencedPath: Path) {
188-
if (!referencedFiles) {
189-
referencedFiles = createMap<true>();
190-
}
191-
referencedFiles.set(referencedPath, true);
188+
(referencedFiles || (referencedFiles = new Set())).add(referencedPath);
192189
}
193190
}
194191

195192
/**
196193
* Returns true if oldState is reusable, that is the emitKind = module/non module has not changed
197194
*/
198-
export function canReuseOldState(newReferencedMap: ReadonlyMap<ReferencedSet> | undefined, oldState: Readonly<ReusableBuilderState> | undefined) {
195+
export function canReuseOldState(newReferencedMap: ReadonlyMap<Path, ReferencedSet> | undefined, oldState: Readonly<ReusableBuilderState> | undefined) {
199196
return oldState && !oldState.referencedMap === !newReferencedMap;
200197
}
201198

202199
/**
203200
* Creates the state of file references and signature for the new program from oldState if it is safe
204201
*/
205202
export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly<ReusableBuilderState>): BuilderState {
206-
const fileInfos = createMap<FileInfo>();
207-
const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createMap<ReferencedSet>() : undefined;
208-
const exportedModulesMap = referencedMap ? createMap<ReferencedSet>() : undefined;
209-
const hasCalledUpdateShapeSignature = createMap<true>();
203+
const fileInfos = new Map<Path, FileInfo>();
204+
const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? new Map<Path, ReferencedSet>() : undefined;
205+
const exportedModulesMap = referencedMap ? new Map<Path, ReferencedSet>() : undefined;
206+
const hasCalledUpdateShapeSignature = new Set<Path>();
210207
const useOldState = canReuseOldState(referencedMap, oldState);
211208

212209
// Create the reference map, and set the file infos
@@ -249,28 +246,24 @@ namespace ts {
249246
* Creates a clone of the state
250247
*/
251248
export function clone(state: Readonly<BuilderState>): BuilderState {
252-
const fileInfos = createMap<FileInfo>();
253-
state.fileInfos.forEach((value, key) => {
254-
fileInfos.set(key, { ...value });
255-
});
256249
// Dont need to backup allFiles info since its cache anyway
257250
return {
258-
fileInfos,
259-
referencedMap: cloneMapOrUndefined(state.referencedMap),
260-
exportedModulesMap: cloneMapOrUndefined(state.exportedModulesMap),
261-
hasCalledUpdateShapeSignature: cloneMap(state.hasCalledUpdateShapeSignature),
251+
fileInfos: new Map(state.fileInfos),
252+
referencedMap: state.referencedMap && new Map(state.referencedMap),
253+
exportedModulesMap: state.exportedModulesMap && new Map(state.exportedModulesMap),
254+
hasCalledUpdateShapeSignature: new Set(state.hasCalledUpdateShapeSignature),
262255
};
263256
}
264257

265258
/**
266259
* Gets the files affected by the path from the program
267260
*/
268-
export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map<string>, exportedModulesMapCache?: ComputingExportedModulesMap): readonly SourceFile[] {
261+
export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map<Path, string>, exportedModulesMapCache?: ComputingExportedModulesMap): readonly SourceFile[] {
269262
// Since the operation could be cancelled, the signatures are always stored in the cache
270263
// They will be committed once it is safe to use them
271264
// eg when calling this api from tsserver, if there is no cancellation of the operation
272265
// In the other cases the affected files signatures are committed only after the iteration through the result is complete
273-
const signatureCache = cacheToUpdateSignature || createMap();
266+
const signatureCache = cacheToUpdateSignature || new Map();
274267
const sourceFile = programOfThisState.getSourceFileByPath(path);
275268
if (!sourceFile) {
276269
return emptyArray;
@@ -292,19 +285,19 @@ namespace ts {
292285
* Updates the signatures from the cache into state's fileinfo signatures
293286
* This should be called whenever it is safe to commit the state of the builder
294287
*/
295-
export function updateSignaturesFromCache(state: BuilderState, signatureCache: Map<string>) {
296-
signatureCache.forEach((signature, path) => updateSignatureOfFile(state, signature, path as Path));
288+
export function updateSignaturesFromCache(state: BuilderState, signatureCache: Map<Path, string>) {
289+
signatureCache.forEach((signature, path) => updateSignatureOfFile(state, signature, path));
297290
}
298291

299292
export function updateSignatureOfFile(state: BuilderState, signature: string | undefined, path: Path) {
300293
state.fileInfos.get(path)!.signature = signature;
301-
state.hasCalledUpdateShapeSignature.set(path, true);
294+
state.hasCalledUpdateShapeSignature.add(path);
302295
}
303296

304297
/**
305298
* Returns if the shape of the signature has changed since last emit
306299
*/
307-
export function updateShapeSignature(state: Readonly<BuilderState>, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map<string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) {
300+
export function updateShapeSignature(state: Readonly<BuilderState>, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map<Path, string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) {
308301
Debug.assert(!!sourceFile);
309302
Debug.assert(!exportedModulesMapCache || !!state.exportedModulesMap, "Compute visible to outside map only if visibleToOutsideReferencedMap present in the state");
310303

@@ -365,16 +358,16 @@ namespace ts {
365358
return;
366359
}
367360

368-
let exportedModules: Map<true> | undefined;
361+
let exportedModules: Set<Path> | undefined;
369362
exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFileFromImportedModuleSymbol(symbol)));
370363
exportedModulesMapCache.set(sourceFile.resolvedPath, exportedModules || false);
371364

372365
function addExportedModule(exportedModulePath: Path | undefined) {
373366
if (exportedModulePath) {
374367
if (!exportedModules) {
375-
exportedModules = createMap<true>();
368+
exportedModules = new Set();
376369
}
377-
exportedModules.set(exportedModulePath, true);
370+
exportedModules.add(exportedModulePath);
378371
}
379372
}
380373
}
@@ -413,26 +406,23 @@ namespace ts {
413406
}
414407

415408
// Get the references, traversing deep from the referenceMap
416-
const seenMap = createMap<true>();
409+
const seenMap = new Set<Path>();
417410
const queue = [sourceFile.resolvedPath];
418411
while (queue.length) {
419412
const path = queue.pop()!;
420413
if (!seenMap.has(path)) {
421-
seenMap.set(path, true);
414+
seenMap.add(path);
422415
const references = state.referencedMap.get(path);
423416
if (references) {
424417
const iterator = references.keys();
425418
for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) {
426-
queue.push(iterResult.value as Path);
419+
queue.push(iterResult.value);
427420
}
428421
}
429422
}
430423
}
431424

432-
return arrayFrom(mapDefinedIterator(seenMap.keys(), path => {
433-
const file = programOfThisState.getSourceFileByPath(path as Path);
434-
return file ? file.fileName : path;
435-
}));
425+
return arrayFrom(mapDefinedIterator(seenMap.keys(), path => programOfThisState.getSourceFileByPath(path)?.fileName ?? path));
436426
}
437427

438428
/**
@@ -451,7 +441,7 @@ namespace ts {
451441
*/
452442
export function getReferencedByPaths(state: Readonly<BuilderState>, referencedFilePath: Path) {
453443
return arrayFrom(mapDefinedIterator(state.referencedMap!.entries(), ([filePath, referencesInFile]) =>
454-
referencesInFile.has(referencedFilePath) ? filePath as Path : undefined
444+
referencesInFile.has(referencedFilePath) ? filePath : undefined
455445
));
456446
}
457447

@@ -528,7 +518,7 @@ namespace ts {
528518
/**
529519
* When program emits modular code, gets the files affected by the sourceFile whose shape has changed
530520
*/
531-
function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map<string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) {
521+
function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map<Path, string>, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined, exportedModulesMapCache: ComputingExportedModulesMap | undefined) {
532522
if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) {
533523
return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape);
534524
}
@@ -541,7 +531,7 @@ namespace ts {
541531
// Now we need to if each file in the referencedBy list has a shape change as well.
542532
// Because if so, its own referencedBy files need to be saved as well to make the
543533
// emitting result consistent with files on disk.
544-
const seenFileNamesMap = createMap<SourceFile>();
534+
const seenFileNamesMap = new Map<Path, SourceFile>();
545535

546536
// Start with the paths this file was referenced by
547537
seenFileNamesMap.set(sourceFileWithUpdatedShape.resolvedPath, sourceFileWithUpdatedShape);
@@ -562,8 +552,4 @@ namespace ts {
562552
return arrayFrom(mapDefinedIterator(seenFileNamesMap.values(), value => value));
563553
}
564554
}
565-
566-
export function cloneMapOrUndefined<T>(map: ReadonlyMap<T> | undefined) {
567-
return map ? cloneMap(map) : undefined;
568-
}
569555
}

0 commit comments

Comments
 (0)