From 8386d491abacdf2f43aacace0efcaa37b27b748d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 27 Dec 2016 11:17:33 -0800 Subject: [PATCH] Use sparse arrays for number-keyed maps --- src/compiler/checker.ts | 9 +- src/compiler/core.ts | 36 +++--- src/compiler/factory.ts | 30 ++--- src/compiler/sourcemap.ts | 2 +- src/compiler/transformer.ts | 22 ++-- src/compiler/transformers/generators.ts | 28 ++--- src/compiler/transformers/module/module.ts | 55 ++++----- src/compiler/transformers/module/system.ts | 53 +++++---- src/compiler/transformers/ts.ts | 14 +-- src/compiler/tsc.ts | 16 +-- src/compiler/types.ts | 22 ++-- src/compiler/utilities.ts | 6 +- src/compiler/visitor.ts | 104 +++++++++--------- src/harness/unittests/session.ts | 10 +- .../unittests/tsserverProjectSystem.ts | 22 ++-- src/services/codefixes/codeFixProvider.ts | 10 +- src/services/codefixes/importFixes.ts | 22 ++-- 17 files changed, 240 insertions(+), 221 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2394d5017a3..47a7db16986 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4054,16 +4054,15 @@ namespace ts { enumType.symbol = symbol; if (enumHasLiteralMembers(symbol)) { const memberTypeList: Type[] = []; - const memberTypes = createMap(); + const memberTypes = sparseArray(); for (const declaration of enumType.symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { computeEnumMemberValues(declaration); for (const member of (declaration).members) { const memberSymbol = getSymbolOfNode(member); const value = getEnumMemberValue(member); - if (!memberTypes.has(value)) { - const memberType = createEnumLiteralType(memberSymbol, enumType, "" + value); - memberTypes.set(value, memberType); + if (!memberTypes[value]) { + const memberType = memberTypes[value] = createEnumLiteralType(memberSymbol, enumType, "" + value); memberTypeList.push(memberType); } } @@ -4085,7 +4084,7 @@ namespace ts { if (!links.declaredType) { const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol)); links.declaredType = enumType.flags & TypeFlags.Union ? - enumType.memberTypes.get(getEnumMemberValue(symbol.valueDeclaration)) : + enumType.memberTypes[getEnumMemberValue(symbol.valueDeclaration)] : enumType; } return links.declaredType; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 6239dc93146..36847796b6e 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -39,6 +39,8 @@ namespace ts { return map; } + export const sparseArray: () => SparseArray = createMapLike; + /** Create a new map. If a template object is provided, the map will copy entries from it. */ export function createMap(template?: MapLike): Map { const map: Map = new MapCtr(); @@ -52,17 +54,6 @@ namespace ts { return map; } - /** Create a map from [key, value] pairs. This avoids casting keys to strings. */ - export function createMapFromPairs(...pairs: [number, T][]): Map { - const map: Map = new MapCtr(); - - for (const [key, value] of pairs) { - map.set(key, value); - } - - return map; - } - /** Methods on native maps but not on shim maps. Only used in this file. */ interface ES6Map extends Map { entries(): Iterator<[string, T]>; @@ -92,21 +83,21 @@ namespace ts { return class implements ShimMap { private data = createMapLike(); - get(key: MapKey): T { + get(key: string): T { return this.data[key]; } - set(key: MapKey, value: T): this { + set(key: string, value: T): this { this.data[key] = value; return this; } - has(key: MapKey): boolean { + has(key: string): boolean { // tslint:disable-next-line:no-in-operator return key in this.data; } - delete(key: MapKey): boolean { + delete(key: string): boolean { const had = this.has(key); if (had) { delete this.data[key]; @@ -890,7 +881,7 @@ namespace ts { * Array of every key in a map. * May not actually return string[] if numbers were put into the map. */ - export function keysOfMap(map: Map): string[] { + export function keysOfMap(map: Map<{}>): string[] { const keys: string[] = []; forEachKeyInMap(map, key => { keys.push(key); @@ -1040,7 +1031,7 @@ namespace ts { * 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. */ - export function multiMapAdd(map: Map, key: string | number, value: V): V[] { + export function multiMapAdd(map: Map, key: string, value: V): V[] { let values = map.get(key); if (values) { values.push(value); @@ -1051,6 +1042,17 @@ namespace ts { return values; } + export function multiMapSparseArrayAdd(map: SparseArray, key: number, value: V): V[] { + let values = map[key]; + if (values) { + values.push(value); + } + else { + map[key] = values = [value]; + } + return values; + } + /** * Removes a value from an array of values associated with the key. * Does not preserve the order of those values. diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 2ef73dea727..15a946648b1 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1,4 +1,4 @@ -/// +/// /// /* @internal */ @@ -2641,9 +2641,11 @@ namespace ts { return destEmitNode; } - function mergeTokenSourceMapRanges(sourceRanges: Map, destRanges: Map) { - if (!destRanges) destRanges = createMap(); - copyMapEntries(sourceRanges, destRanges); + function mergeTokenSourceMapRanges(sourceRanges: SparseArray, destRanges: SparseArray) { + if (!destRanges) destRanges = []; + for (const key in sourceRanges) { + destRanges[key] = sourceRanges[key]; + } return destRanges; } @@ -2745,7 +2747,7 @@ namespace ts { export function getTokenSourceMapRange(node: Node, token: SyntaxKind) { const emitNode = node.emitNode; const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges; - return tokenSourceMapRanges && tokenSourceMapRanges.get(token); + return tokenSourceMapRanges && tokenSourceMapRanges[token]; } /** @@ -2757,8 +2759,8 @@ namespace ts { */ export function setTokenSourceMapRange(node: T, token: SyntaxKind, range: TextRange) { const emitNode = getOrCreateEmitNode(node); - const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap()); - tokenSourceMapRanges.set(token, range); + const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = []); + tokenSourceMapRanges[token] = range; return node; } @@ -3270,7 +3272,7 @@ namespace ts { externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers exportSpecifiers: Map; // export specifiers by name - exportedBindings: Map; // exported names of local declarations + exportedBindings: SparseArray; // exported names of local declarations exportedNames: Identifier[]; // all exported names local to module exportEquals: ExportAssignment | undefined; // an export= declaration if one was present hasExportStarsToExportValues: boolean; // whether this module contains export* @@ -3279,7 +3281,7 @@ namespace ts { export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; const exportSpecifiers = createMap(); - const exportedBindings = createMap(); + const exportedBindings = sparseArray(); const uniqueExports = createMap(); let exportedNames: Identifier[]; let hasExportDefault = false; @@ -3338,7 +3340,7 @@ namespace ts { || resolver.getReferencedValueDeclaration(name); if (decl) { - multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); } uniqueExports.set(specifier.name.text, true); @@ -3368,7 +3370,7 @@ namespace ts { if (hasModifier(node, ModifierFlags.Default)) { // export default function() { } if (!hasExportDefault) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); hasExportDefault = true; } } @@ -3376,7 +3378,7 @@ namespace ts { // export function x() { } const name = (node).name; if (!uniqueExports.get(name.text)) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); uniqueExports.set(name.text, true); exportedNames = append(exportedNames, name); } @@ -3389,7 +3391,7 @@ namespace ts { if (hasModifier(node, ModifierFlags.Default)) { // export default class { } if (!hasExportDefault) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); hasExportDefault = true; } } @@ -3397,7 +3399,7 @@ namespace ts { // export class x { } const name = (node).name; if (!uniqueExports.get(name.text)) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); uniqueExports.set(name.text, true); exportedNames = append(exportedNames, name); } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index a814a91c355..650b9b0ef02 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -362,7 +362,7 @@ namespace ts { const emitNode = node && node.emitNode; const emitFlags = emitNode && emitNode.flags; - const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges.get(token); + const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token]; tokenPos = skipTrivia(currentSourceText, range ? range.pos : tokenPos); if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) { diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 1440481d38e..cef8c701455 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,4 +1,4 @@ -/// +/// /// /// /// @@ -13,14 +13,16 @@ /* @internal */ namespace ts { - const moduleTransformerMap = createMapFromPairs( - [ModuleKind.ES2015, transformES2015Module], - [ModuleKind.System, transformSystemModule], - [ModuleKind.AMD, transformModule], - [ModuleKind.CommonJS, transformModule], - [ModuleKind.UMD, transformModule], - [ModuleKind.None, transformModule], - ); + function getModuleTransformer(moduleKind: ModuleKind): Transformer { + switch (moduleKind) { + case ModuleKind.ES2015: + return transformES2015Module; + case ModuleKind.System: + return transformSystemModule; + default: + return transformModule; + } + } const enum SyntaxKindFeatureFlags { Substitution = 1 << 0, @@ -56,7 +58,7 @@ namespace ts { transformers.push(transformGenerators); } - transformers.push(moduleTransformerMap.get(moduleKind) || moduleTransformerMap.get(ModuleKind.None)); + transformers.push(getModuleTransformer(moduleKind)); // The ES5 transformer is last so that it can substitute expressions like `exports.default` // for ES3. diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 0b853649e81..85aba96387b 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -1,4 +1,4 @@ -/// +/// /// // Transforms generator functions into a compatible ES5 representation with similar runtime @@ -217,13 +217,15 @@ namespace ts { Endfinally = 7, } - const instructionNames = createMapFromPairs( - [Instruction.Return, "return"], - [Instruction.Break, "break"], - [Instruction.Yield, "yield"], - [Instruction.YieldStar, "yield*"], - [Instruction.Endfinally, "endfinally"], - ); + function getInstructionName(instruction: Instruction): string { + switch (instruction) { + case Instruction.Return: return "return"; + case Instruction.Break: return "break"; + case Instruction.Yield: return "yield"; + case Instruction.YieldStar: return "yield*"; + case Instruction.Endfinally: return "endfinally"; + } + } export function transformGenerators(context: TransformationContext) { const { @@ -241,7 +243,7 @@ namespace ts { let currentSourceFile: SourceFile; let renamedCatchVariables: Map; - let renamedCatchVariableDeclarations: Map; + let renamedCatchVariableDeclarations: SparseArray; let inGeneratorFunctionBody: boolean; let inStatementContainingYield: boolean; @@ -1926,7 +1928,7 @@ namespace ts { if (isIdentifier(original) && original.parent) { const declaration = resolver.getReferencedValueDeclaration(original); if (declaration) { - const name = renamedCatchVariableDeclarations.get(getOriginalNodeId(declaration)); + const name = renamedCatchVariableDeclarations[getOriginalNodeId(declaration)]; if (name) { const clone = getMutableClone(name); setSourceMapRange(clone, node); @@ -2092,12 +2094,12 @@ namespace ts { if (!renamedCatchVariables) { renamedCatchVariables = createMap(); - renamedCatchVariableDeclarations = createMap(); + renamedCatchVariableDeclarations = sparseArray(); context.enableSubstitution(SyntaxKind.Identifier); } renamedCatchVariables.set(text, true); - renamedCatchVariableDeclarations.set(getOriginalNodeId(variable), name); + renamedCatchVariableDeclarations[getOriginalNodeId(variable)] = name; const exception = peekBlock(); Debug.assert(exception.state < ExceptionBlockState.Catch); @@ -2401,7 +2403,7 @@ namespace ts { */ function createInstruction(instruction: Instruction): NumericLiteral { const literal = createLiteral(instruction); - literal.trailingComment = instructionNames.get(instruction); + literal.trailingComment = getInstructionName(instruction); return literal; } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 5d05667e4c2..e5e27c8360d 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -1,4 +1,4 @@ -/// +/// /// /*@internal*/ @@ -10,12 +10,13 @@ namespace ts { importAliasNames: ParameterDeclaration[]; } - const transformModuleDelegates = createMapFromPairs<(node: SourceFile) => SourceFile>( - [ModuleKind.None, transformCommonJSModule], - [ModuleKind.CommonJS, transformCommonJSModule], - [ModuleKind.AMD, transformAMDModule], - [ModuleKind.UMD, transformUMDModule], - ); + function getTransformModuleDelegate(moduleKind: ModuleKind): (node: SourceFile) => SourceFile { + switch (moduleKind) { + case ModuleKind.AMD: return transformAMDModule; + case ModuleKind.UMD: return transformUMDModule; + default: return transformCommonJSModule; + } + } const { startLexicalEnvironment, @@ -38,12 +39,12 @@ namespace ts { context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes shorthand property assignments for imported/exported symbols. context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap = createMap(); // The ExternalModuleInfo for each file. - const deferredExports = createMap(); // Exports to defer until an EndOfDeclarationMarker is found. + const moduleInfoMap = sparseArray(); // The ExternalModuleInfo for each file. + const deferredExports = sparseArray(); // Exports to defer until an EndOfDeclarationMarker is found. let currentSourceFile: SourceFile; // The current file. let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file. - let noSubstitution: Map; // Set of nodes for which substitution rules should be ignored. + let noSubstitution: SparseArray; // Set of nodes for which substitution rules should be ignored. return transformSourceFile; @@ -61,10 +62,10 @@ namespace ts { currentSourceFile = node; currentModuleInfo = collectExternalModuleInfo(node, resolver, compilerOptions); - moduleInfoMap.set(getOriginalNodeId(node), currentModuleInfo); + moduleInfoMap[getOriginalNodeId(node)] = currentModuleInfo; // Perform the transformation. - const transformModule = transformModuleDelegates.get(moduleKind) || transformModuleDelegates.get(ModuleKind.None); + const transformModule = getTransformModuleDelegate(moduleKind); const updated = transformModule(node); currentSourceFile = undefined; @@ -445,7 +446,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfImportDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -524,7 +525,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfImportEqualsDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -611,7 +612,7 @@ namespace ts { if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportStatement(deferredExports.get(id), createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true)); + deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true); } else { statements = appendExportStatement(statements, createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true); @@ -652,7 +653,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -691,7 +692,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -742,7 +743,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node); } else { statements = appendExportsOfVariableStatement(statements, node); @@ -795,7 +796,7 @@ namespace ts { // statement. if (hasAssociatedEndOfDeclarationMarker(node) && node.original.kind === SyntaxKind.VariableStatement) { const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node.original)); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original); } return node; @@ -821,9 +822,9 @@ namespace ts { // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. const id = getOriginalNodeId(node); - const statements = deferredExports.get(id); + const statements = deferredExports[id]; if (statements) { - deferredExports.delete(id); + delete deferredExports[id]; return append(statements, node); } @@ -1103,8 +1104,8 @@ namespace ts { function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { currentSourceFile = node; - currentModuleInfo = moduleInfoMap.get(getOriginalNodeId(currentSourceFile)); - noSubstitution = createMap(); + currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)]; + noSubstitution = sparseArray(); previousOnEmitNode(emitContext, node, emitCallback); @@ -1129,7 +1130,7 @@ namespace ts { */ function onSubstituteNode(emitContext: EmitContext, node: Node) { node = previousOnSubstituteNode(emitContext, node); - if (node.id && noSubstitution.get(node.id)) { + if (node.id && noSubstitution[node.id]) { return node; } @@ -1255,7 +1256,7 @@ namespace ts { let expression: Expression = node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. - noSubstitution.set(getNodeId(expression), true); + noSubstitution[getNodeId(expression)] = true; expression = createExportExpression(exportName, expression, /*location*/ node); } @@ -1297,7 +1298,7 @@ namespace ts { : node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. - noSubstitution.set(getNodeId(expression), true); + noSubstitution[getNodeId(expression)] = true; expression = createExportExpression(exportName, expression); } @@ -1319,7 +1320,7 @@ namespace ts { || resolver.getReferencedValueDeclaration(name); if (valueDeclaration) { return currentModuleInfo - && currentModuleInfo.exportedBindings.get(getOriginalNodeId(valueDeclaration)); + && currentModuleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]; } } } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index bc613bcfaf6..c20e6b5cf32 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1,4 +1,4 @@ -/// +/// /// /*@internal*/ @@ -28,10 +28,10 @@ namespace ts { context.enableSubstitution(SyntaxKind.PostfixUnaryExpression); // Substitutes updates to exported symbols. context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap = createMap(); // The ExternalModuleInfo for each file. - const deferredExports = createMap(); // Exports to defer until an EndOfDeclarationMarker is found. - const exportFunctionsMap = createMap(); // The export function associated with a source file. - const noSubstitutionMap = createMap>(); // Set of nodes for which substitution rules should be ignored for each file. + const moduleInfoMap = sparseArray(); // The ExternalModuleInfo for each file. + const deferredExports = sparseArray(); // Exports to defer until an EndOfDeclarationMarker is found. + const exportFunctionsMap = sparseArray(); // The export function associated with a source file. + const noSubstitutionMap = sparseArray>(); // Set of nodes for which substitution rules should be ignored for each file. let currentSourceFile: SourceFile; // The current file. let moduleInfo: ExternalModuleInfo; // ExternalModuleInfo for the current file. @@ -39,7 +39,7 @@ namespace ts { let contextObject: Identifier; // The context object for the current file. let hoistedStatements: Statement[]; let enclosingBlockScopedContainer: Node; - let noSubstitution: Map; // Set of nodes for which substitution rules should be ignored. + let noSubstitution: SparseArray; // Set of nodes for which substitution rules should be ignored. return transformSourceFile; @@ -73,13 +73,12 @@ namespace ts { // see comment to 'substitutePostfixUnaryExpression' for more details // Collect information about the external module and dependency groups. - moduleInfo = collectExternalModuleInfo(node, resolver, compilerOptions); - moduleInfoMap.set(id, moduleInfo); + moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(node, resolver, compilerOptions); // Make sure that the name of the 'exports' function does not conflict with // existing identifiers. exportFunction = createUniqueName("exports"); - exportFunctionsMap.set(id, exportFunction); + exportFunctionsMap[id] = exportFunction; contextObject = createUniqueName("context"); // Add the body of the module. @@ -123,7 +122,7 @@ namespace ts { } if (noSubstitution) { - noSubstitutionMap.set(id, noSubstitution); + noSubstitutionMap[id] = noSubstitution; noSubstitution = undefined; } @@ -610,7 +609,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfImportDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -633,7 +632,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfImportEqualsDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -658,7 +657,7 @@ namespace ts { if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportStatement(deferredExports.get(id), createIdentifier("default"), expression, /*allowComments*/ true)); + deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), expression, /*allowComments*/ true); } else { return createExportStatement(createIdentifier("default"), expression, /*allowComments*/ true); @@ -690,7 +689,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { hoistedStatements = appendExportsOfHoistedDeclaration(hoistedStatements, node); @@ -732,7 +731,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -772,7 +771,7 @@ namespace ts { if (isMarkedDeclaration) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node, isExportedDeclaration)); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node, isExportedDeclaration); } else { statements = appendExportsOfVariableStatement(statements, node, /*exportSelf*/ false); @@ -885,7 +884,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node) && node.original.kind === SyntaxKind.VariableStatement) { const id = getOriginalNodeId(node); const isExportedDeclaration = hasModifier(node.original, ModifierFlags.Export); - deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node.original, isExportedDeclaration)); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original, isExportedDeclaration); } return node; @@ -911,9 +910,9 @@ namespace ts { // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. const id = getOriginalNodeId(node); - const statements = deferredExports.get(id); + const statements = deferredExports[id]; if (statements) { - deferredExports.delete(id); + delete deferredExports[id]; return append(statements, node); } @@ -1556,12 +1555,12 @@ namespace ts { if (node.kind === SyntaxKind.SourceFile) { const id = getOriginalNodeId(node); currentSourceFile = node; - moduleInfo = moduleInfoMap.get(id); - exportFunction = exportFunctionsMap.get(id); - noSubstitution = noSubstitutionMap.get(id); + moduleInfo = moduleInfoMap[id]; + exportFunction = exportFunctionsMap[id]; + noSubstitution = noSubstitutionMap[id]; if (noSubstitution) { - noSubstitutionMap.delete(id); + delete noSubstitutionMap[id]; } previousOnEmitNode(emitContext, node, emitCallback); @@ -1758,7 +1757,7 @@ namespace ts { exportedNames = append(exportedNames, getDeclarationName(valueDeclaration)); } - exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings.get(getOriginalNodeId(valueDeclaration))); + exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]); } } @@ -1771,8 +1770,8 @@ namespace ts { * @param node The node which should not be substituted. */ function preventSubstitution(node: T): T { - if (noSubstitution === undefined) noSubstitution = createMap(); - noSubstitution.set(getNodeId(node), true); + if (noSubstitution === undefined) noSubstitution = sparseArray(); + noSubstitution[getNodeId(node)] = true; return node; } @@ -1782,7 +1781,7 @@ namespace ts { * @param node The node to test. */ function isSubstitutionPrevented(node: Node) { - return noSubstitution && node.id && noSubstitution.get(node.id); + return noSubstitution && node.id && noSubstitution[node.id]; } } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 4b0f981f27c..ef7001f3735 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1,4 +1,4 @@ -/// +/// /// /// @@ -60,7 +60,7 @@ namespace ts { * A map that keeps track of aliases created for classes with decorators to avoid issues * with the double-binding behavior of classes. */ - let classAliases: Map; + let classAliases: SparseArray; /** * Keeps track of whether we are within any containing namespaces when performing @@ -752,7 +752,7 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { // record an alias as the class name is not in scope for statics. enableSubstitutionForClassAliases(); - classAliases.set(getOriginalNodeId(node), getSynthesizedClone(temp)); + classAliases[getOriginalNodeId(node)] = getSynthesizedClone(temp); } // To preserve the behavior of the old emitter, we explicitly indent @@ -1420,7 +1420,7 @@ namespace ts { return undefined; } - const classAlias = classAliases && classAliases.get(getOriginalNodeId(node)); + const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; const localName = getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); const decorate = createDecorateHelper(context, decoratorExpressions, localName); const expression = createAssignment(localName, classAlias ? createAssignment(classAlias, decorate) : decorate); @@ -3085,7 +3085,7 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { enableSubstitutionForClassAliases(); const classAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default"); - classAliases.set(getOriginalNodeId(node), classAlias); + classAliases[getOriginalNodeId(node)] = classAlias; hoistVariableDeclaration(classAlias); return classAlias; } @@ -3117,7 +3117,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); // Keep track of class aliases. - classAliases = createMap(); + classAliases = sparseArray(); } } @@ -3230,7 +3230,7 @@ namespace ts { // constructor references in static property initializers. const declaration = resolver.getReferencedValueDeclaration(node); if (declaration) { - const classAlias = classAliases.get(declaration.id); + const classAlias = classAliases[declaration.id]; if (classAlias) { const clone = getSynthesizedClone(classAlias); setSourceMapRange(clone, node); diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 5de16a20fc2..ba5a0d8faea 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -1,4 +1,4 @@ -/// +/// /// namespace ts { @@ -67,11 +67,13 @@ namespace ts { const gutterSeparator = " "; const resetEscapeSequence = "\u001b[0m"; const ellipsis = "..."; - const categoryFormatMap = createMapFromPairs( - [DiagnosticCategory.Warning, yellowForegroundEscapeSequence], - [DiagnosticCategory.Error, redForegroundEscapeSequence], - [DiagnosticCategory.Message, blueForegroundEscapeSequence], - ); + function getCategoryFormat(category: DiagnosticCategory): string { + switch (category) { + case DiagnosticCategory.Warning: return yellowForegroundEscapeSequence; + case DiagnosticCategory.Error: return redForegroundEscapeSequence; + case DiagnosticCategory.Message: return blueForegroundEscapeSequence; + } + } function formatAndReset(text: string, formatStyle: string) { return formatStyle + text + resetEscapeSequence; @@ -139,7 +141,7 @@ namespace ts { output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `; } - const categoryColor = categoryFormatMap.get(diagnostic.category); + const categoryColor = getCategoryFormat(diagnostic.category); const category = DiagnosticCategory[diagnostic.category].toLowerCase(); output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`; output += sys.newLine + sys.newLine; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b98a5a493ba..75ff806d605 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,4 +1,4 @@ -namespace ts { +namespace ts { /** * Type of objects whose values are all of the same type. * The `in` and `for-in` operators can *not* be safely used, @@ -8,15 +8,19 @@ namespace ts { [index: string]: T; } - /** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */ - export type MapKey = string | number; + /** + * Like MapLike, but keys must be numbers. + */ + export interface SparseArray { + [key: number]: T; + } /** Minimal ES6 Map interface. Does not include iterators as those are hard to shim performantly. */ export interface Map { - get(key: MapKey): T; - has(key: MapKey): boolean; - set(key: MapKey, value: T): this; - delete(key: MapKey): boolean; + get(key: string): T; + has(key: string): boolean; + set(key: string, value: T): this; + delete(key: string): boolean; clear(): void; /** `key` may *not* be a string if it was set with a number and we are not using the shim. */ forEach(action: (value: T, key: string) => void): void; @@ -2853,7 +2857,7 @@ namespace ts { // Enum types (TypeFlags.Enum) export interface EnumType extends Type { - memberTypes: Map; + memberTypes: SparseArray; } // Enum types (TypeFlags.EnumLiteral) @@ -3682,7 +3686,7 @@ namespace ts { flags?: EmitFlags; // Flags that customize emit commentRange?: TextRange; // The text range to use when emitting leading or trailing comments sourceMapRange?: TextRange; // The text range to use when emitting leading or trailing source mappings - tokenSourceMapRanges?: Map; // The text range to use when emitting source mappings for tokens + tokenSourceMapRanges?: SparseArray; // The text range to use when emitting source mappings for tokens constantValue?: number; // The constant value of an expression externalHelpersModuleName?: Identifier; // The local name for an imported helpers module helpers?: EmitHelper[]; // Emit helpers for the node diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 25973cae5cb..ead1a3b62de 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3311,12 +3311,12 @@ namespace ts { return false; } - const syntaxKindCache = createMap(); + const syntaxKindCache = sparseArray(); export function formatSyntaxKind(kind: SyntaxKind): string { const syntaxKindEnum = (ts).SyntaxKind; if (syntaxKindEnum) { - const cached = syntaxKindCache.get(kind); + const cached = syntaxKindCache[kind]; if (cached !== undefined) { return cached; } @@ -3324,7 +3324,7 @@ namespace ts { for (const name in syntaxKindEnum) { if (syntaxKindEnum[name] === kind) { const result = `${kind} (${name})`; - syntaxKindCache.set(kind, result); + syntaxKindCache[kind] = result; return result; } } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index c9ea2af1a87..2afb1f8301e 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -1,4 +1,4 @@ -/// +/// /// /// @@ -46,54 +46,56 @@ namespace ts { * supplant the existing `forEachChild` implementation if performance is not * significantly impacted. */ - const nodeEdgeTraversalMap = createMapFromPairs( - [SyntaxKind.QualifiedName, [ - { name: "left", test: isEntityName }, - { name: "right", test: isIdentifier } - ]], - [SyntaxKind.Decorator, [ - { name: "expression", test: isLeftHandSideExpression } - ]], - [SyntaxKind.TypeAssertionExpression, [ - { name: "type", test: isTypeNode }, - { name: "expression", test: isUnaryExpression } - ]], - [SyntaxKind.AsExpression, [ - { name: "expression", test: isExpression }, - { name: "type", test: isTypeNode } - ]], - [SyntaxKind.NonNullExpression, [ - { name: "expression", test: isLeftHandSideExpression } - ]], - [SyntaxKind.EnumDeclaration, [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isIdentifier }, - { name: "members", test: isEnumMember } - ]], - [SyntaxKind.ModuleDeclaration, [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isModuleName }, - { name: "body", test: isModuleBody } - ]], - [SyntaxKind.ModuleBlock, [ - { name: "statements", test: isStatement } - ]], - [SyntaxKind.ImportEqualsDeclaration, [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isIdentifier }, - { name: "moduleReference", test: isModuleReference } - ]], - [SyntaxKind.ExternalModuleReference, [ - { name: "expression", test: isExpression, optional: true } - ]], - [SyntaxKind.EnumMember, [ - { name: "name", test: isPropertyName }, - { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } - ]] - ); + function getNodeEdgeTraversal(kind: SyntaxKind): NodeTraversalPath { + switch (kind) { + case SyntaxKind.QualifiedName: return [ + { name: "left", test: isEntityName }, + { name: "right", test: isIdentifier } + ]; + case SyntaxKind.Decorator: return [ + { name: "expression", test: isLeftHandSideExpression } + ]; + case SyntaxKind.TypeAssertionExpression: return [ + { name: "type", test: isTypeNode }, + { name: "expression", test: isUnaryExpression } + ]; + case SyntaxKind.AsExpression: return [ + { name: "expression", test: isExpression }, + { name: "type", test: isTypeNode } + ]; + case SyntaxKind.NonNullExpression: return [ + { name: "expression", test: isLeftHandSideExpression } + ]; + case SyntaxKind.EnumDeclaration: return [ + { name: "decorators", test: isDecorator }, + { name: "modifiers", test: isModifier }, + { name: "name", test: isIdentifier }, + { name: "members", test: isEnumMember } + ]; + case SyntaxKind.ModuleDeclaration: return [ + { name: "decorators", test: isDecorator }, + { name: "modifiers", test: isModifier }, + { name: "name", test: isModuleName }, + { name: "body", test: isModuleBody } + ]; + case SyntaxKind.ModuleBlock: return [ + { name: "statements", test: isStatement } + ]; + case SyntaxKind.ImportEqualsDeclaration: return [ + { name: "decorators", test: isDecorator }, + { name: "modifiers", test: isModifier }, + { name: "name", test: isIdentifier }, + { name: "moduleReference", test: isModuleReference } + ]; + case SyntaxKind.ExternalModuleReference: return [ + { name: "expression", test: isExpression, optional: true } + ]; + case SyntaxKind.EnumMember: return [ + { name: "name", test: isPropertyName }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } + ]; + } + } function reduceNode(node: Node, f: (memo: T, node: Node) => T, initial: T) { return node ? f(initial, node) : initial; @@ -530,7 +532,7 @@ namespace ts { break; default: - const edgeTraversalPath = nodeEdgeTraversalMap.get(kind); + const edgeTraversalPath = getNodeEdgeTraversal(kind); if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { const value = (>node)[edge.name]; @@ -1188,7 +1190,7 @@ namespace ts { default: let updated: Node & MapLike; - const edgeTraversalPath = nodeEdgeTraversalMap.get(kind); + const edgeTraversalPath = getNodeEdgeTraversal(kind); if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { const value = >(>node)[edge.name]; diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index da24fe1606b..70cda27747b 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -1,4 +1,4 @@ -/// +/// const expect: typeof _chai.expect = _chai.expect; @@ -416,16 +416,16 @@ namespace ts.server { class InProcClient { private server: InProcSession; private seq = 0; - private callbacks = createMap<(resp: protocol.Response) => void>(); + private callbacks = sparseArray<(resp: protocol.Response) => void>(); private eventHandlers = createMap<(args: any) => void>(); handle(msg: protocol.Message): void { if (msg.type === "response") { const response = msg; - const handler = this.callbacks.get(response.request_seq); + const handler = this.callbacks[response.request_seq]; if (handler) { handler(response); - this.callbacks.delete(response.request_seq); + delete this.callbacks[response.request_seq]; } } else if (msg.type === "event") { @@ -460,7 +460,7 @@ namespace ts.server { command, arguments: args }); - this.callbacks.set(this.seq, callback); + this.callbacks[this.seq] = callback; } }; diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 5b4ba7d7223..ae213bd0a50 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1,4 +1,4 @@ -/// +/// /// namespace ts.projectSystem { @@ -290,30 +290,34 @@ namespace ts.projectSystem { } export class Callbacks { - private map = createMap(); + private map = sparseArray(); private nextId = 1; register(cb: (...args: any[]) => void, args: any[]) { const timeoutId = this.nextId; this.nextId++; - this.map.set(timeoutId, cb.bind(undefined, ...args)); + this.map[timeoutId] = cb.bind(undefined, ...args); return timeoutId; } unregister(id: any) { if (typeof id === "number") { - this.map.delete(id); + delete this.map[id]; } } count() { - return this.map.size; + let n = 0; + for (const _ in this.map) { + n++; + } + return n; } invoke() { - this.map.forEach(cb => { - cb(); - }); - this.map.clear(); + for (const key in this.map) { + this.map[key](); + } + this.map = sparseArray(); } } diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index c6373fc1909..fe3e71f1e1c 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -16,25 +16,25 @@ namespace ts { } export namespace codefix { - const codeFixes = createMap(); + const codeFixes = sparseArray(); export function registerCodeFix(action: CodeFix) { forEach(action.errorCodes, error => { - let fixes = codeFixes.get(error); + let fixes = codeFixes[error]; if (!fixes) { fixes = []; - codeFixes.set(error, fixes); + codeFixes[error] = fixes; } fixes.push(action); }); } export function getSupportedErrorCodes() { - return keysOfMap(codeFixes); + return keysOfSparseArray(codeFixes); } export function getFixes(context: CodeFixContext): CodeAction[] { - const fixes = codeFixes.get(context.errorCode); + const fixes = codeFixes[context.errorCode]; let allActions: CodeAction[] = []; forEach(fixes, f => { diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 0e7743e29fc..0df3c3134fe 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -14,16 +14,16 @@ namespace ts.codefix { } class ImportCodeActionMap { - private symbolIdToActionMap = createMap(); + private symbolIdToActionMap = sparseArray(); addAction(symbolId: number, newAction: ImportCodeAction) { if (!newAction) { return; } - const actions = this.symbolIdToActionMap.get(symbolId); + const actions = this.symbolIdToActionMap[symbolId]; if (!actions) { - this.symbolIdToActionMap.set(symbolId, [newAction]); + this.symbolIdToActionMap[symbolId] = [newAction]; return; } @@ -33,7 +33,7 @@ namespace ts.codefix { } const updatedNewImports: ImportCodeAction[] = []; - for (const existingAction of this.symbolIdToActionMap.get(symbolId)) { + for (const existingAction of this.symbolIdToActionMap[symbolId]) { if (existingAction.kind === "CodeChange") { // only import actions should compare updatedNewImports.push(existingAction); @@ -63,7 +63,7 @@ namespace ts.codefix { } // if we reach here, it means the new one is better or equal to all of the existing ones. updatedNewImports.push(newAction); - this.symbolIdToActionMap.set(symbolId, updatedNewImports); + this.symbolIdToActionMap[symbolId] = updatedNewImports; } addActions(symbolId: number, newActions: ImportCodeAction[]) { @@ -74,9 +74,9 @@ namespace ts.codefix { getAllActions() { let result: ImportCodeAction[] = []; - this.symbolIdToActionMap.forEach(actions => { - result = concatenate(result, actions); - }); + for (const key in this.symbolIdToActionMap) { + result = concatenate(result, this.symbolIdToActionMap[key]) + } return result; } @@ -125,7 +125,7 @@ namespace ts.codefix { const symbolIdActionMap = new ImportCodeActionMap(); // this is a module id -> module import declaration map - const cachedImportDeclarations = createMap<(ImportDeclaration | ImportEqualsDeclaration)[]>(); + const cachedImportDeclarations = sparseArray<(ImportDeclaration | ImportEqualsDeclaration)[]>(); let cachedNewImportInsertPosition: number; const allPotentialModules = checker.getAmbientModules(); @@ -163,7 +163,7 @@ namespace ts.codefix { function getImportDeclarations(moduleSymbol: Symbol) { const moduleSymbolId = getUniqueSymbolId(moduleSymbol); - const cached = cachedImportDeclarations.get(moduleSymbolId); + const cached = cachedImportDeclarations[moduleSymbolId]; if (cached) { return cached; } @@ -175,7 +175,7 @@ namespace ts.codefix { existingDeclarations.push(getImportDeclaration(importModuleSpecifier)); } } - cachedImportDeclarations.set(moduleSymbolId, existingDeclarations); + cachedImportDeclarations[moduleSymbolId] = existingDeclarations; return existingDeclarations; function getImportDeclaration(moduleSpecifier: LiteralExpression) {