From e83d14434d0c9821f560a216f09c1496253d910c Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 29 Oct 2021 22:49:47 +0000 Subject: [PATCH 01/15] Use getNodeId() less, specifically for NodeLinks. --- src/compiler/binder.ts | 19 +++++++++---------- src/compiler/checker.ts | 18 +++++++++--------- src/compiler/emitter.ts | 11 +++++++---- src/compiler/types.ts | 4 ++-- 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8ff6712ad30ac..3db8807b5b723 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?: ESMap): ModuleInstanceState { + export function getModuleInstanceState(node: ModuleDeclaration, visited?: ESMap): 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,18 +24,17 @@ namespace ts { return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated; } - function getModuleInstanceStateCached(node: Node, visited = new Map()) { - const nodeId = getNodeId(node); - if (visited.has(nodeId)) { - return visited.get(nodeId) || ModuleInstanceState.NonInstantiated; + function getModuleInstanceStateCached(node: Node, visited = new Map()) { + if (visited.has(node)) { + return visited.get(node) || ModuleInstanceState.NonInstantiated; } - visited.set(nodeId, undefined); + visited.set(node, undefined); const result = getModuleInstanceStateWorker(node, visited); - visited.set(nodeId, result); + visited.set(node, result); return result; } - function getModuleInstanceStateWorker(node: Node, visited: ESMap): ModuleInstanceState { + function getModuleInstanceStateWorker(node: Node, visited: ESMap): ModuleInstanceState { // A module is uninstantiated if it contains only switch (node.kind) { // 1. interface declarations, type alias declarations @@ -107,7 +106,7 @@ namespace ts { return ModuleInstanceState.Instantiated; } - function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: ESMap) { + function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: ESMap) { const name = specifier.propertyName || specifier.name; let p: Node | undefined = specifier.parent; while (p) { @@ -2984,7 +2983,7 @@ namespace ts { function addLateBoundAssignmentDeclarationToSymbol(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol | undefined) { if (symbol) { - (symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = new Map())).set(getNodeId(node), node); + (symbol.assignmentDeclarationMembers ||= new Set()).add(node); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 67e3eb1f9a261..cb3fc2f8979b4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -974,7 +974,7 @@ namespace ts { const maximumSuggestionCount = 10; const mergedSymbols: Symbol[] = []; const symbolLinks: SymbolLinks[] = []; - const nodeLinks: NodeLinks[] = []; + const nodeLinks: ESMap = new Map(); const flowLoopCaches: ESMap[] = []; const flowLoopNodes: FlowNode[] = []; const flowLoopKeys: string[] = []; @@ -1463,8 +1463,11 @@ namespace ts { } function getNodeLinks(node: Node): NodeLinks { - const nodeId = getNodeId(node); - return nodeLinks[nodeId] || (nodeLinks[nodeId] = new (NodeLinks as any)()); + let result = nodeLinks.get(node); + if (!result) { + nodeLinks.set(node, result = new (NodeLinks as any)() as NodeLinks); + } + return result; } function isGlobalSourceFile(node: Node) { @@ -40316,9 +40319,8 @@ namespace ts { const enclosingFile = getSourceFileOfNode(node); const links = getNodeLinks(enclosingFile); if (!(links.flags & NodeCheckFlags.TypeChecked)) { - links.deferredNodes = links.deferredNodes || new Map(); - const id = getNodeId(node); - links.deferredNodes.set(id, node); + links.deferredNodes = links.deferredNodes || new Set(); + links.deferredNodes.add(node); } } @@ -41599,9 +41601,7 @@ namespace ts { } function getNodeCheckFlags(node: Node): NodeCheckFlags { - const nodeId = node.id || 0; - if (nodeId < 0 || nodeId >= nodeLinks.length) return 0; - return nodeLinks[nodeId]?.flags || 0; + return nodeLinks.get(node)?.flags || 0; } function getEnumMemberValue(node: EnumMember): string | number | undefined { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d4eff1e0ace5e..3025be42a8040 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -869,7 +869,7 @@ namespace ts { const bundledHelpers = new Map(); let currentSourceFile: SourceFile | undefined; - let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes. + let nodeToGeneratedName: ESMap; // Map of generated names for specific nodes. let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables. let generatedNames: Set; // Set of names generated by the NameGenerator. let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes. @@ -1154,7 +1154,7 @@ namespace ts { } function reset() { - nodeIdToGeneratedName = []; + nodeToGeneratedName = new Map(); autoGeneratedIdToGeneratedName = []; generatedNames = new Set(); tempFlagsStack = []; @@ -5008,8 +5008,11 @@ namespace ts { } function generateNameCached(node: Node, flags?: GeneratedIdentifierFlags) { - const nodeId = getNodeId(node); - return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = generateNameForNode(node, flags)); + let result = nodeToGeneratedName.get(node); + if (result === undefined) { + nodeToGeneratedName.set(node, result = generateNameForNode(node, flags)); + } + return result; } /** diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9a32a79f37f78..6018ab1b9724b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4908,7 +4908,7 @@ namespace ts { /* @internal */ isReferenced?: SymbolFlags; // True if the symbol is referenced elsewhere. Keeps track of the meaning of a reference in case a symbol is both a type parameter and parameter. /* @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? /* @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments - /* @internal */ assignmentDeclarationMembers?: ESMap; // detected late-bound assignment declarations associated with the symbol + /* @internal */ assignmentDeclarationMembers?: Set; // detected late-bound assignment declarations associated with the symbol } /* @internal */ @@ -5106,7 +5106,7 @@ namespace ts { jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node jsxImplicitImportContainer?: Symbol | false; // Resolved module symbol the implicit jsx import of this file should refer to contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive - deferredNodes?: ESMap; // Set of nodes whose checking has been deferred + deferredNodes?: Set; // Set of nodes whose checking has been deferred capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type isExhaustive?: boolean; // Is node an exhaustive switch statement From 044de3ec635a9ce802bf43dbaa025fd8d3795fb8 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Sat, 6 Nov 2021 07:59:41 +0000 Subject: [PATCH 02/15] More removal of `getNodeId` uses. --- src/compiler/checker.ts | 24 +++++-- src/compiler/factory/nodeFactory.ts | 2 +- src/compiler/program.ts | 4 +- src/compiler/transformers/classFields.ts | 36 +++++----- src/compiler/transformers/es5.ts | 9 +-- src/compiler/transformers/module/module.ts | 14 ++-- src/compiler/transformers/module/system.ts | 80 ++++++++++------------ src/compiler/transformers/ts.ts | 10 +-- src/compiler/types.ts | 1 - src/compiler/utilities.ts | 3 - src/harness/harnessUtils.ts | 1 - 11 files changed, 94 insertions(+), 90 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cb3fc2f8979b4..40f035abeda4b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -277,12 +277,23 @@ namespace ts { this.flags = 0; } + const idCache = new Map(); export function getNodeId(node: Node): number { - if (!node.id) { - node.id = nextNodeId; + let result = idCache.get(node); + if (!result) { + result = nextNodeId; + idCache.set(node, result); nextNodeId++; } - return node.id; + return result; + } + + export function getNodeIdOrDefault(node: Node): number { + return idCache.get(node) || 0; + } + + export function setNodeIdForStrangeRedirect(node: Node, id: number): void { + idCache.set(node, id); } export function getSymbolId(symbol: Symbol): SymbolId { @@ -974,7 +985,7 @@ namespace ts { const maximumSuggestionCount = 10; const mergedSymbols: Symbol[] = []; const symbolLinks: SymbolLinks[] = []; - const nodeLinks: ESMap = new Map(); + const nodeLinks = new Map(); const flowLoopCaches: ESMap[] = []; const flowLoopNodes: FlowNode[] = []; const flowLoopKeys: string[] = []; @@ -40319,7 +40330,7 @@ namespace ts { const enclosingFile = getSourceFileOfNode(node); const links = getNodeLinks(enclosingFile); if (!(links.flags & NodeCheckFlags.TypeChecked)) { - links.deferredNodes = links.deferredNodes || new Set(); + links.deferredNodes ||= new Set(); links.deferredNodes.add(node); } } @@ -41601,6 +41612,9 @@ namespace ts { } function getNodeCheckFlags(node: Node): NodeCheckFlags { + // TODO: probably not meaningful, right? + // const nodeId = getNodeIdOrDefault(node); + // if (nodeId < 0 || nodeId >= nodeLinks.length) return 0; return nodeLinks.get(node)?.flags || 0; } diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 963ce75441f6e..f41fea50edae4 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -5339,7 +5339,7 @@ namespace ts { } function flattenCommaElements(node: Expression): Expression | readonly Expression[] { - if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node.original && !node.emitNode && !node.id) { + if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node.original && !node.emitNode && !getNodeIdOrDefault(node)) { if (isCommaListExpression(node)) { return node.elements; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 57fa55bfb494f..65b5297c0215d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2593,8 +2593,8 @@ namespace ts { sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); Object.defineProperties(redirect, { id: { - get(this: SourceFile) { return this.redirectInfo!.redirectTarget.id; }, - set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo!.redirectTarget.id = value; }, + get(this: SourceFile) { return getNodeIdOrDefault(this.redirectInfo!.redirectTarget); }, + set(this: SourceFile, value: number) { setNodeIdForStrangeRedirect(this.redirectInfo!.redirectTarget, value); }, }, symbol: { get(this: SourceFile) { return this.redirectInfo!.redirectTarget.symbol; }, diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 7d4afe2baffc4..4cebe15215b10 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -147,7 +147,7 @@ namespace ts { let enabledSubstitutions: ClassPropertySubstitutionFlags; - let classAliases: Identifier[]; + let classAliases: ESMap; /** * Tracks what computed name expressions originating from elided names must be inlined @@ -162,7 +162,7 @@ namespace ts { let pendingStatements: Statement[] | undefined; const classLexicalEnvironmentStack: (ClassLexicalEnvironment | undefined)[] = []; - const classLexicalEnvironmentMap = new Map(); + const classLexicalEnvironmentMap = new WeakMap(); let currentClassLexicalEnvironment: ClassLexicalEnvironment | undefined; let currentComputedPropertyNameClassLexicalEnvironment: ClassLexicalEnvironment | undefined; let currentStaticPropertyDeclarationOrStaticBlock: PropertyDeclaration | ClassStaticBlockDeclaration | undefined; @@ -738,7 +738,7 @@ namespace ts { function transformClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration) { if (shouldTransformPrivateElementsOrClassStaticBlocks) { if (currentClassLexicalEnvironment) { - classLexicalEnvironmentMap.set(getOriginalNodeId(node), currentClassLexicalEnvironment); + classLexicalEnvironmentMap.set(getOriginalNode(node), currentClassLexicalEnvironment); } startLexicalEnvironment(); @@ -1128,7 +1128,7 @@ namespace ts { enableSubstitutionForClassAliases(); const alias = factory.cloneNode(temp) as GeneratedIdentifier; alias.autoGenerateFlags &= ~GeneratedIdentifierFlags.ReservedInNestedScopes; - classAliases[getOriginalNodeId(node)] = alias; + classAliases.set(getOriginalNode(node), alias); } // To preserve the behavior of the old emitter, we explicitly indent @@ -1375,7 +1375,7 @@ namespace ts { // capture the lexical environment for the member setOriginalNode(transformed, property); addEmitFlags(transformed, EmitFlags.AdviseOnEmitNode); - classLexicalEnvironmentMap.set(getOriginalNodeId(transformed), currentClassLexicalEnvironment); + classLexicalEnvironmentMap.set(getOriginalNode(transformed), currentClassLexicalEnvironment); } currentStaticPropertyDeclarationOrStaticBlock = savedCurrentStaticPropertyDeclarationOrStaticBlock; return transformed; @@ -1453,7 +1453,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); // Keep track of class aliases. - classAliases = []; + classAliases = new Map(); } } @@ -1516,18 +1516,16 @@ namespace ts { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { const original = getOriginalNode(node); - if (original.id) { - const classLexicalEnvironment = classLexicalEnvironmentMap.get(original.id); - if (classLexicalEnvironment) { - const savedClassLexicalEnvironment = currentClassLexicalEnvironment; - const savedCurrentComputedPropertyNameClassLexicalEnvironment = currentComputedPropertyNameClassLexicalEnvironment; - currentClassLexicalEnvironment = classLexicalEnvironment; - currentComputedPropertyNameClassLexicalEnvironment = classLexicalEnvironment; - previousOnEmitNode(hint, node, emitCallback); - currentClassLexicalEnvironment = savedClassLexicalEnvironment; - currentComputedPropertyNameClassLexicalEnvironment = savedCurrentComputedPropertyNameClassLexicalEnvironment; - return; - } + const classLexicalEnvironment = classLexicalEnvironmentMap.get(original); + if (classLexicalEnvironment) { + const savedClassLexicalEnvironment = currentClassLexicalEnvironment; + const savedCurrentComputedPropertyNameClassLexicalEnvironment = currentComputedPropertyNameClassLexicalEnvironment; + currentClassLexicalEnvironment = classLexicalEnvironment; + currentComputedPropertyNameClassLexicalEnvironment = classLexicalEnvironment; + previousOnEmitNode(hint, node, emitCallback); + currentClassLexicalEnvironment = savedClassLexicalEnvironment; + currentComputedPropertyNameClassLexicalEnvironment = savedCurrentComputedPropertyNameClassLexicalEnvironment; + return; } switch (node.kind) { @@ -1633,7 +1631,7 @@ namespace ts { // constructor references in static property initializers. const declaration = resolver.getReferencedValueDeclaration(node); if (declaration) { - const classAlias = classAliases[declaration.id!]; // TODO: GH#18217 + const classAlias = classAliases.get(declaration); if (classAlias) { const clone = factory.cloneNode(classAlias); setSourceMapRange(clone, node); diff --git a/src/compiler/transformers/es5.ts b/src/compiler/transformers/es5.ts index e82ba20cf5f9f..ade270edf179c 100644 --- a/src/compiler/transformers/es5.ts +++ b/src/compiler/transformers/es5.ts @@ -11,14 +11,14 @@ namespace ts { // enable emit notification only if using --jsx preserve or react-native let previousOnEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; - let noSubstitution: boolean[]; + let noSubstitution: Set; if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) { previousOnEmitNode = context.onEmitNode; context.onEmitNode = onEmitNode; context.enableEmitNotification(SyntaxKind.JsxOpeningElement); context.enableEmitNotification(SyntaxKind.JsxClosingElement); context.enableEmitNotification(SyntaxKind.JsxSelfClosingElement); - noSubstitution = []; + noSubstitution = new Set(); } const previousOnSubstituteNode = context.onSubstituteNode; @@ -49,7 +49,7 @@ namespace ts { case SyntaxKind.JsxClosingElement: case SyntaxKind.JsxSelfClosingElement: const tagName = (node as JsxOpeningElement | JsxClosingElement | JsxSelfClosingElement).tagName; - noSubstitution[getOriginalNodeId(tagName)] = true; + noSubstitution.add(getOriginalNode(tagName)); break; } @@ -63,7 +63,8 @@ namespace ts { * @param node The node to substitute. */ function onSubstituteNode(hint: EmitHint, node: Node) { - if (node.id && noSubstitution && noSubstitution[node.id]) { + // TODO: do we need to check for a Node ID here? + if (getNodeIdOrDefault(node) && noSubstitution?.has(node)) { return previousOnSubstituteNode(hint, node); } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 9c88471640956..496562c392274 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -45,7 +45,7 @@ namespace ts { let currentSourceFile: SourceFile; // The current file. let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file. - const noSubstitution: boolean[] = []; // Set of nodes for which substitution rules should be ignored. + const noSubstitution = new Set(); // Set of nodes for which substitution rules should be ignored. let needUMDDynamicImportHelper: boolean; return chainBundle(context, transformSourceFile); @@ -692,13 +692,13 @@ namespace ts { } for (const exportName of exportedNames) { - noSubstitution[getNodeId(expression)] = true; + noSubstitution.add(expression); expression = createExportExpression(exportName, expression); setTextRange(expression, node); } if (temp) { - noSubstitution[getNodeId(expression)] = true; + noSubstitution.add(expression);; expression = factory.createComma(expression, temp); setTextRange(expression, node); } @@ -1794,7 +1794,7 @@ namespace ts { */ function onSubstituteNode(hint: EmitHint, node: Node) { node = previousOnSubstituteNode(hint, node); - if (node.id && noSubstitution[node.id]) { + if (noSubstitution.has(node)) { return node; } @@ -1852,7 +1852,7 @@ namespace ts { function substituteCallExpression(node: CallExpression) { if (isIdentifier(node.expression)) { const expression = substituteExpressionIdentifier(node.expression); - noSubstitution[getNodeId(expression)] = true; + noSubstitution.add(expression);; if (!isIdentifier(expression)) { return addEmitFlags( factory.updateCallExpression(node, @@ -1871,7 +1871,7 @@ namespace ts { function substituteTaggedTemplateExpression(node: TaggedTemplateExpression) { if (isIdentifier(node.tag)) { const tag = substituteExpressionIdentifier(node.tag); - noSubstitution[getNodeId(tag)] = true; + noSubstitution.add(tag); if (!isIdentifier(tag)) { return addEmitFlags( factory.updateTaggedTemplateExpression(node, @@ -1962,7 +1962,7 @@ namespace ts { let expression: Expression = node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. - noSubstitution[getNodeId(expression)] = true; + noSubstitution.add(expression);; expression = createExportExpression(exportName, expression, /*location*/ node); } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 14216e5cab63f..199901b3bae62 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -26,11 +26,11 @@ namespace ts { context.enableSubstitution(SyntaxKind.MetaProperty); // Substitutes 'import.meta' context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file. - const deferredExports: (Statement[] | undefined)[] = []; // Exports to defer until an EndOfDeclarationMarker is found. - const exportFunctionsMap: Identifier[] = []; // The export function associated with a source file. - const noSubstitutionMap: boolean[][] = []; // Set of nodes for which substitution rules should be ignored for each file. - const contextObjectMap: Identifier[] = []; // The context object associated with a source file. + const moduleInfoMap = new Map(); // The ExternalModuleInfo for each file. + const deferredExports = new Map(); // Exports to defer until an EndOfDeclarationMarker is found. + const exportFunctionsMap = new Map(); // The export function associated with a source file. + const noSubstitutionMap = new Map>(); // Set of nodes for which substitution rules should be ignored for each file. + const contextObjectMap = new Map(); // The context object associated with a source file. let currentSourceFile: SourceFile; // The current file. let moduleInfo: ExternalModuleInfo; // ExternalModuleInfo for the current file. @@ -38,7 +38,7 @@ namespace ts { let contextObject: Identifier; // The context object for the current file. let hoistedStatements: Statement[] | undefined; let enclosingBlockScopedContainer: Node; - let noSubstitution: boolean[] | undefined; // Set of nodes for which substitution rules should be ignored. + let noSubstitution: Set | undefined; // Set of nodes for which substitution rules should be ignored. return chainBundle(context, transformSourceFile); @@ -52,7 +52,7 @@ namespace ts { return node; } - const id = getOriginalNodeId(node); + const originalNode = getOriginalNode(node); currentSourceFile = node; enclosingBlockScopedContainer = node; @@ -70,13 +70,13 @@ namespace ts { // see comment to 'substitutePostfixUnaryExpression' for more details // Collect information about the external module and dependency groups. - moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(context, node, resolver, compilerOptions); + moduleInfoMap.set(originalNode, moduleInfo = collectExternalModuleInfo(context, node, resolver, compilerOptions)); // Make sure that the name of the 'exports' function does not conflict with // existing identifiers. exportFunction = factory.createUniqueName("exports"); - exportFunctionsMap[id] = exportFunction; - contextObject = contextObjectMap[id] = factory.createUniqueName("context"); + exportFunctionsMap.set(originalNode, exportFunction); + contextObjectMap.set(originalNode, contextObject = factory.createUniqueName("context")); // Add the body of the module. const dependencyGroups = collectDependencyGroups(moduleInfo.externalImports); @@ -123,7 +123,7 @@ namespace ts { } if (noSubstitution) { - noSubstitutionMap[id] = noSubstitution; + noSubstitutionMap.set(originalNode, noSubstitution); noSubstitution = undefined; } @@ -596,8 +596,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); + const originalNode = getOriginalNode(node); + deferredExports.set(originalNode, appendExportsOfImportDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -624,8 +624,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); + const originalNode = getOriginalNode(node); + deferredExports.set(originalNode, appendExportsOfImportEqualsDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -649,8 +649,8 @@ namespace ts { const original = node.original; if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), expression, /*allowComments*/ true); + const originalNode = getOriginalNode(node); + deferredExports.set(originalNode, appendExportStatement(deferredExports.get(originalNode), factory.createIdentifier("default"), expression, /*allowComments*/ true)); } else { return createExportStatement(factory.createIdentifier("default"), expression, /*allowComments*/ true); @@ -682,8 +682,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); + const originalNode = getOriginalNode(node); + deferredExports.set(originalNode, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); } else { hoistedStatements = appendExportsOfHoistedDeclaration(hoistedStatements, node); @@ -729,8 +729,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); + const originalNode = getOriginalNode(node); + deferredExports.set(originalNode, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -769,8 +769,8 @@ namespace ts { if (isMarkedDeclaration) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node, isExportedDeclaration); + const originalNode = getOriginalNode(node); + deferredExports.set(originalNode, appendExportsOfVariableStatement(deferredExports.get(originalNode), node, isExportedDeclaration)); } else { statements = appendExportsOfVariableStatement(statements, node, /*exportSelf*/ false); @@ -881,9 +881,9 @@ namespace ts { // To balance the declaration, we defer the exports of the elided variable // statement until we visit this declaration's `EndOfDeclarationMarker`. if (hasAssociatedEndOfDeclarationMarker(node) && node.original!.kind === SyntaxKind.VariableStatement) { - const id = getOriginalNodeId(node); + const originalNode = getOriginalNode(node); const isExportedDeclaration = hasSyntacticModifier(node.original!, ModifierFlags.Export); - deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original as VariableStatement, isExportedDeclaration); + deferredExports.set(originalNode, appendExportsOfVariableStatement(deferredExports.get(originalNode), node.original as VariableStatement, isExportedDeclaration)); } return node; @@ -908,16 +908,15 @@ namespace ts { // For some transformations we emit an `EndOfDeclarationMarker` to mark the actual // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. - const id = getOriginalNodeId(node); - const statements = deferredExports[id]; + const originalNode = getOriginalNode(node); + const statements = deferredExports.get(originalNode); if (statements) { - delete deferredExports[id]; + deferredExports.delete(originalNode); return append(statements, node); } else { - const original = getOriginalNode(node); - if (isModuleOrEnumDeclaration(original)) { - return append(appendExportsOfDeclaration(statements, original), node); + if (isModuleOrEnumDeclaration(originalNode)) { + return append(appendExportsOfDeclaration(statements, originalNode), node); } } @@ -1678,16 +1677,14 @@ namespace ts { */ function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { - const id = getOriginalNodeId(node); + const originalNode = getOriginalNode(node); currentSourceFile = node as SourceFile; - moduleInfo = moduleInfoMap[id]; - exportFunction = exportFunctionsMap[id]; - noSubstitution = noSubstitutionMap[id]; - contextObject = contextObjectMap[id]; + moduleInfo = moduleInfoMap.get(originalNode)!; + exportFunction = exportFunctionsMap.get(originalNode)!; + noSubstitution = noSubstitutionMap.get(originalNode)!; + contextObject = contextObjectMap.get(originalNode)!; - if (noSubstitution) { - delete noSubstitutionMap[id]; - } + noSubstitutionMap.delete(originalNode); previousOnEmitNode(hint, node, emitCallback); @@ -1917,8 +1914,7 @@ namespace ts { * @param node The node which should not be substituted. */ function preventSubstitution(node: T): T { - if (noSubstitution === undefined) noSubstitution = []; - noSubstitution[getNodeId(node)] = true; + (noSubstitution ||= new Set()).add(node); return node; } @@ -1928,7 +1924,7 @@ namespace ts { * @param node The node to test. */ function isSubstitutionPrevented(node: Node) { - return noSubstitution && node.id && noSubstitution[node.id]; + return noSubstitution?.has(node); } } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8561db1c633ef..6013ec97f1144 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -78,7 +78,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: Identifier[]; + let classAliases: ESMap; /** * Keeps track of whether we are within any containing namespaces when performing @@ -1237,7 +1237,7 @@ namespace ts { return undefined; } - const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; + const classAlias = classAliases?.get(getOriginalNode(node)); // When we transform to ES5/3 this will be moved inside an IIFE and should reference the name // without any block-scoped variable collision handling @@ -3139,7 +3139,7 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { enableSubstitutionForClassAliases(); const classAlias = factory.createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? idText(node.name) : "default"); - classAliases[getOriginalNodeId(node)] = classAlias; + classAliases.set(getOriginalNode(node), classAlias); hoistVariableDeclaration(classAlias); return classAlias; } @@ -3171,7 +3171,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); // Keep track of class aliases. - classAliases = []; + classAliases = new Map(); } } @@ -3290,7 +3290,7 @@ namespace ts { // constructor references in static property initializers. const declaration = resolver.getReferencedValueDeclaration(node); if (declaration) { - const classAlias = classAliases[declaration.id!]; // TODO: GH#18217 + const classAlias = classAliases.get(declaration); if (classAlias) { const clone = factory.cloneNode(classAlias); setSourceMapRange(clone, node); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6018ab1b9724b..bc479ec932cdf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -857,7 +857,6 @@ namespace ts { /* @internal */ readonly transformFlags: TransformFlags; // Flags for transforms readonly decorators?: NodeArray; // Array of decorators (in document order) readonly modifiers?: ModifiersArray; // Array of modifiers - /* @internal */ id?: NodeId; // Unique id (used to look up NodeLinks) readonly parent: Node; // Parent node (initialized by binding) /* @internal */ original?: Node; // The original node if this is an updated node. /* @internal */ symbol: Symbol; // Symbol declared by node (initialized by binding) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8caba5b205af8..ed7707d00911b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5821,7 +5821,6 @@ namespace ts { this.pos = pos; this.end = end; this.kind = kind; - this.id = 0; this.flags = NodeFlags.None; this.modifierFlagsCache = ModifierFlags.None; this.transformFlags = TransformFlags.None; @@ -5833,7 +5832,6 @@ namespace ts { this.pos = pos; this.end = end; this.kind = kind; - this.id = 0; this.flags = NodeFlags.None; this.transformFlags = TransformFlags.None; this.parent = undefined!; @@ -5843,7 +5841,6 @@ namespace ts { this.pos = pos; this.end = end; this.kind = kind; - this.id = 0; this.flags = NodeFlags.None; this.transformFlags = TransformFlags.None; this.parent = undefined!; diff --git a/src/harness/harnessUtils.ts b/src/harness/harnessUtils.ts index cfc6f59f30050..7894eb8a28caf 100644 --- a/src/harness/harnessUtils.ts +++ b/src/harness/harnessUtils.ts @@ -219,7 +219,6 @@ namespace Utils { case "locals": case "localSymbol": case "kind": - case "id": case "nodeCount": case "symbolCount": case "identifierCount": From e64dbabab87dc474f38e1693331b475d27cbd429 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Sat, 6 Nov 2021 09:33:40 +0000 Subject: [PATCH 03/15] More removals. --- src/compiler/checker.ts | 24 ++++++---------------- src/compiler/factory/nodeFactory.ts | 2 +- src/compiler/program.ts | 4 ---- src/compiler/transformers/es5.ts | 2 +- src/compiler/transformers/module/module.ts | 6 +++--- src/compiler/types.ts | 2 +- 6 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 40f035abeda4b..61c2d1568bb9c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -288,14 +288,6 @@ namespace ts { return result; } - export function getNodeIdOrDefault(node: Node): number { - return idCache.get(node) || 0; - } - - export function setNodeIdForStrangeRedirect(node: Node, id: number): void { - idCache.set(node, id); - } - export function getSymbolId(symbol: Symbol): SymbolId { if (!symbol.id) { symbol.id = nextSymbolId; @@ -971,7 +963,7 @@ namespace ts { let flowInvocationCount = 0; let lastFlowNode: FlowNode | undefined; let lastFlowNodeReachable: boolean; - let flowTypeCache: Type[] | undefined; + let flowTypeCache: ESMap | undefined; const emptyStringType = getStringLiteralType(""); const zeroType = getNumberLiteralType(0); @@ -3899,10 +3891,9 @@ namespace ts { function getAlternativeContainingModules(symbol: Symbol, enclosingDeclaration: Node): Symbol[] { const containingFile = getSourceFileOfNode(enclosingDeclaration); - const id = getNodeId(containingFile); const links = getSymbolLinks(symbol); let results: Symbol[] | undefined; - if (links.extendedContainersByFile && (results = links.extendedContainersByFile.get(id))) { + if (links.extendedContainersByFile && (results = links.extendedContainersByFile.get(containingFile))) { return results; } if (containingFile && containingFile.imports) { @@ -3916,7 +3907,7 @@ namespace ts { results = append(results, resolvedModule); } if (length(results)) { - (links.extendedContainersByFile || (links.extendedContainersByFile = new Map())).set(id, results!); + (links.extendedContainersByFile ||= new Map()).set(containingFile, results!); return results!; } } @@ -33799,7 +33790,7 @@ namespace ts { } // If a type has been cached for the node, return it. if (node.flags & NodeFlags.TypeCached && flowTypeCache) { - const cachedType = flowTypeCache[getNodeId(node)]; + const cachedType = flowTypeCache.get(node); if (cachedType) { return cachedType; } @@ -33808,8 +33799,8 @@ namespace ts { const type = checkExpression(node); // If control flow analysis was required to determine the type, it is worth caching. if (flowInvocationCount !== startInvocationCount) { - const cache = flowTypeCache || (flowTypeCache = []); - cache[getNodeId(node)] = type; + const cache = (flowTypeCache ||= new Map()); + cache.set(node, type); setNodeFlags(node, node.flags | NodeFlags.TypeCached); } return type; @@ -41612,9 +41603,6 @@ namespace ts { } function getNodeCheckFlags(node: Node): NodeCheckFlags { - // TODO: probably not meaningful, right? - // const nodeId = getNodeIdOrDefault(node); - // if (nodeId < 0 || nodeId >= nodeLinks.length) return 0; return nodeLinks.get(node)?.flags || 0; } diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index f41fea50edae4..63b241dc9e58a 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -5339,7 +5339,7 @@ namespace ts { } function flattenCommaElements(node: Expression): Expression | readonly Expression[] { - if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node.original && !node.emitNode && !getNodeIdOrDefault(node)) { + if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node.original && !node.emitNode) { if (isCommaListExpression(node)) { return node.elements; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 65b5297c0215d..12fbb067ddcb1 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2592,10 +2592,6 @@ namespace ts { redirect.redirectInfo = { redirectTarget, unredirected }; sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); Object.defineProperties(redirect, { - id: { - get(this: SourceFile) { return getNodeIdOrDefault(this.redirectInfo!.redirectTarget); }, - set(this: SourceFile, value: number) { setNodeIdForStrangeRedirect(this.redirectInfo!.redirectTarget, value); }, - }, symbol: { get(this: SourceFile) { return this.redirectInfo!.redirectTarget.symbol; }, set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo!.redirectTarget.symbol = value; }, diff --git a/src/compiler/transformers/es5.ts b/src/compiler/transformers/es5.ts index ade270edf179c..9dc69f2147598 100644 --- a/src/compiler/transformers/es5.ts +++ b/src/compiler/transformers/es5.ts @@ -64,7 +64,7 @@ namespace ts { */ function onSubstituteNode(hint: EmitHint, node: Node) { // TODO: do we need to check for a Node ID here? - if (getNodeIdOrDefault(node) && noSubstitution?.has(node)) { + if (noSubstitution?.has(node)) { return previousOnSubstituteNode(hint, node); } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 496562c392274..df3cc9b95a3d6 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -698,7 +698,7 @@ namespace ts { } if (temp) { - noSubstitution.add(expression);; + noSubstitution.add(expression); expression = factory.createComma(expression, temp); setTextRange(expression, node); } @@ -1852,7 +1852,7 @@ namespace ts { function substituteCallExpression(node: CallExpression) { if (isIdentifier(node.expression)) { const expression = substituteExpressionIdentifier(node.expression); - noSubstitution.add(expression);; + noSubstitution.add(expression); if (!isIdentifier(expression)) { return addEmitFlags( factory.updateCallExpression(node, @@ -1962,7 +1962,7 @@ namespace ts { let expression: Expression = node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. - noSubstitution.add(expression);; + noSubstitution.add(expression); expression = createExportExpression(exportName, expression, /*location*/ node); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bc479ec932cdf..7f843c1036565 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4945,7 +4945,7 @@ namespace ts { lateSymbol?: Symbol; // Late-bound symbol for a computed property specifierCache?: ESMap; // For symbols corresponding to external modules, a cache of incoming path -> module specifier name mappings extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in - extendedContainersByFile?: ESMap; // Containers (other than the parent) which this symbol is aliased in + extendedContainersByFile?: ESMap; // Containers (other than the parent) which this symbol is aliased in variances?: VarianceFlags[]; // Alias symbol type argument variance cache deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type deferralParent?: Type; // Source union/intersection of a deferred type From ca13132e84140a5cfc43106d6626c40ebe6558fd Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Sat, 6 Nov 2021 10:39:03 +0000 Subject: [PATCH 04/15] Remove getOriginalNodeId. --- src/compiler/transformers/declarations.ts | 28 +++++++------- src/compiler/transformers/generators.ts | 8 ++-- src/compiler/transformers/module/module.ts | 44 +++++++++++----------- src/compiler/transformers/module/system.ts | 2 +- src/compiler/transformers/utilities.ts | 33 ++++------------ 5 files changed, 49 insertions(+), 66 deletions(-) diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 6cea3b85055ee..8ed26f1aeb5cd 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -60,7 +60,7 @@ namespace ts { let enclosingDeclaration: Node; let necessaryTypeReferences: Set | undefined; let lateMarkedStatements: LateVisibilityPaintedStatement[] | undefined; - let lateStatementReplacementMap: ESMap>; + let lateStatementReplacementMap: ESMap>; let suppressNewDiagnosticContexts: boolean; let exportedModulesFromDeclarationEmit: Symbol[] | undefined; @@ -84,7 +84,7 @@ namespace ts { let errorFallbackNode: Declaration | undefined; let currentSourceFile: SourceFile; - let refs: ESMap; + let refs: ESMap; let libs: ESMap; let emittedImports: readonly AnyImportSyntax[] | undefined; // must be declared in container so it can be `undefined` while transformer's first pass const resolver = context.getEmitResolver(); @@ -110,7 +110,7 @@ namespace ts { } // Otherwise we should emit a path-based reference const container = getSourceFileOfNode(node); - refs.set(getOriginalNodeId(container), container); + refs.set(getOriginalNode(container), container); } function handleSymbolAccessibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) { @@ -426,12 +426,12 @@ namespace ts { } } - function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: ESMap) { + function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: ESMap) { if (noResolve || (!isUnparsedSource(sourceFile) && isSourceFileJS(sourceFile))) return ret; forEach(sourceFile.referencedFiles, f => { const elem = host.getSourceFileFromReference(sourceFile, f); if (elem) { - ret.set(getOriginalNodeId(elem), elem); + ret.set(getOriginalNode(elem), elem); } }); return ret; @@ -812,7 +812,7 @@ namespace ts { needsDeclare = i.parent && isSourceFile(i.parent) && !(isExternalModule(i.parent) && isBundledEmit); const result = transformTopLevelDeclaration(i); needsDeclare = priorNeedsDeclare; - lateStatementReplacementMap.set(getOriginalNodeId(i), result); + lateStatementReplacementMap.set(getOriginalNode(i), result); } // And lastly, we need to get the final form of all those indetermine import declarations from before and add them to the output list @@ -821,10 +821,10 @@ namespace ts { function visitLateVisibilityMarkedStatements(statement: Statement) { if (isLateVisibilityPaintedStatement(statement)) { - const key = getOriginalNodeId(statement); - if (lateStatementReplacementMap.has(key)) { - const result = lateStatementReplacementMap.get(key); - lateStatementReplacementMap.delete(key); + const originalNode = getOriginalNode(statement); + if (lateStatementReplacementMap.has(originalNode)) { + const result = lateStatementReplacementMap.get(originalNode); + lateStatementReplacementMap.delete(originalNode); if (result) { if (isArray(result) ? some(result, needsScopeMarker) : needsScopeMarker(result)) { // Top-level declarations in .d.ts files are always considered exported even without a modifier unless there's an export assignment or specifier @@ -1146,7 +1146,7 @@ namespace ts { const result = transformTopLevelDeclaration(input); // Don't actually transform yet; just leave as original node - will be elided/swapped by late pass - lateStatementReplacementMap.set(getOriginalNodeId(input), result); + lateStatementReplacementMap.set(getOriginalNode(input), result); return input; } @@ -1348,9 +1348,9 @@ namespace ts { needsDeclare = false; visitNode(inner, visitDeclarationStatements); // eagerly transform nested namespaces (the nesting doesn't need any elision or painting done) - const id = getOriginalNodeId(inner!); // TODO: GH#18217 - const body = lateStatementReplacementMap.get(id); - lateStatementReplacementMap.delete(id); + const originalNode = getOriginalNode(inner!); // TODO: GH#18217 + const body = lateStatementReplacementMap.get(originalNode); + lateStatementReplacementMap.delete(originalNode); return cleanup(factory.updateModuleDeclaration( input, /*decorators*/ undefined, diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 73e35b4583d0e..24acc78ae3c82 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -245,7 +245,7 @@ namespace ts { context.onSubstituteNode = onSubstituteNode; let renamedCatchVariables: ESMap; - let renamedCatchVariableDeclarations: Identifier[]; + let renamedCatchVariableDeclarations: ESMap; let inGeneratorFunctionBody: boolean; let inStatementContainingYield: boolean; @@ -1969,7 +1969,7 @@ namespace ts { if (isIdentifier(original) && original.parent) { const declaration = resolver.getReferencedValueDeclaration(original); if (declaration) { - const name = renamedCatchVariableDeclarations[getOriginalNodeId(declaration)]; + const name = renamedCatchVariableDeclarations.get(getOriginalNode(declaration)); if (name) { // TODO(rbuckton): Does this need to be parented? const clone = setParent(setTextRange(factory.cloneNode(name), name), name.parent); @@ -2137,12 +2137,12 @@ namespace ts { name = declareLocal(text); if (!renamedCatchVariables) { renamedCatchVariables = new Map(); - renamedCatchVariableDeclarations = []; + renamedCatchVariableDeclarations = new Map(); context.enableSubstitution(SyntaxKind.Identifier); } renamedCatchVariables.set(text, true); - renamedCatchVariableDeclarations[getOriginalNodeId(variable)] = name; + renamedCatchVariableDeclarations.set(getOriginalNode(variable), name); } const exception = peekBlock() as ExceptionBlock; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index df3cc9b95a3d6..4768df8ad8bcc 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -40,8 +40,8 @@ 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: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file. - const deferredExports: (Statement[] | undefined)[] = []; // Exports to defer until an EndOfDeclarationMarker is found. + const moduleInfoMap = new Map(); // The ExternalModuleInfo for each file. + const deferredExports = new Map();; // Exports to defer until an EndOfDeclarationMarker is found. let currentSourceFile: SourceFile; // The current file. let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file. @@ -65,7 +65,7 @@ namespace ts { currentSourceFile = node; currentModuleInfo = collectExternalModuleInfo(context, node, resolver, compilerOptions); - moduleInfoMap[getOriginalNodeId(node)] = currentModuleInfo; + moduleInfoMap.set(getOriginalNode(node), currentModuleInfo); // Perform the transformation. const transformModule = getTransformModuleDelegate(moduleKind); @@ -981,8 +981,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); + const originalNode = getOriginalNode(node); + deferredExports.set(node, appendExportsOfImportDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -1072,8 +1072,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); + const originalNode = getOriginalNode(node); + deferredExports.set(node, appendExportsOfImportEqualsDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -1206,8 +1206,8 @@ namespace ts { const original = node.original; if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true); + const originalNode = getOriginalNode(node); + deferredExports.set(node, appendExportStatement(deferredExports.get(originalNode), factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true)); } else { statements = appendExportStatement(statements, factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true); @@ -1249,8 +1249,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); + const originalNode = getOriginalNode(node); + deferredExports.set(node, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -1290,8 +1290,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); + const originalNode = getOriginalNode(node); + deferredExports.set(node, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -1370,8 +1370,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node); + const originalNode = getOriginalNode(node); + deferredExports.set(node, appendExportsOfVariableStatement(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfVariableStatement(statements, node); @@ -1441,8 +1441,8 @@ namespace ts { // To balance the declaration, add the exports of the elided variable // statement. if (hasAssociatedEndOfDeclarationMarker(node) && node.original!.kind === SyntaxKind.VariableStatement) { - const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original as VariableStatement); + const originalNode = getOriginalNode(node); + deferredExports.set(node, appendExportsOfVariableStatement(deferredExports.get(originalNode), node.original as VariableStatement)); } return node; @@ -1467,10 +1467,10 @@ namespace ts { // For some transformations we emit an `EndOfDeclarationMarker` to mark the actual // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. - const id = getOriginalNodeId(node); - const statements = deferredExports[id]; + const originalNode = getOriginalNode(node); + const statements = deferredExports.get(originalNode); if (statements) { - delete deferredExports[id]; + deferredExports.delete(node); return append(statements, node); } @@ -1770,7 +1770,7 @@ namespace ts { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { currentSourceFile = node as SourceFile; - currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)]; + currentModuleInfo = moduleInfoMap.get(getOriginalNode(currentSourceFile))!; previousOnEmitNode(hint, node, emitCallback); @@ -1984,7 +1984,7 @@ namespace ts { || resolver.getReferencedValueDeclaration(name); if (valueDeclaration) { return currentModuleInfo - && currentModuleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]; + && currentModuleInfo.exportedBindings.get(getOriginalNode(valueDeclaration)); } } } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 199901b3bae62..cf8ef6b7af326 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1901,7 +1901,7 @@ namespace ts { exportedNames = append(exportedNames, factory.getDeclarationName(valueDeclaration)); } - exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]); + exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings.get(getOriginalNode(valueDeclaration))); } } diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index 276d034c7b92c..c920e96b32db4 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -1,15 +1,10 @@ /* @internal */ namespace ts { - export function getOriginalNodeId(node: Node) { - node = getOriginalNode(node); - return node ? getNodeId(node) : 0; - } - export interface ExternalModuleInfo { externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers exportSpecifiers: ESMap; // file-local export specifiers by name (no reexports) - exportedBindings: Identifier[][]; // exported names of local declarations + exportedBindings: MultiMap; // exported names of local declarations exportedNames: Identifier[] | undefined; // all exported names in the module, both local and reexported exportEquals: ExportAssignment | undefined; // an export= declaration if one was present hasExportStarsToExportValues: boolean; // whether this module contains export* @@ -68,7 +63,7 @@ namespace ts { export function collectExternalModuleInfo(context: TransformationContext, sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; const exportSpecifiers = createMultiMap(); - const exportedBindings: Identifier[][] = []; + const exportedBindings = createMultiMap(); const uniqueExports = new Map(); let exportedNames: Identifier[] | undefined; let hasExportDefault = false; @@ -118,7 +113,7 @@ namespace ts { else { const name = ((node as ExportDeclaration).exportClause as NamespaceExport).name; if (!uniqueExports.get(idText(name))) { - multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); + exportedBindings.add(getOriginalNode(node), name); uniqueExports.set(idText(name), true); exportedNames = append(exportedNames, name); } @@ -153,7 +148,7 @@ namespace ts { if (hasSyntacticModifier(node, ModifierFlags.Default)) { // export default function() { } if (!hasExportDefault) { - multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), context.factory.getDeclarationName(node as FunctionDeclaration)); + exportedBindings.add(getOriginalNode(node), context.factory.getDeclarationName(node as FunctionDeclaration)); hasExportDefault = true; } } @@ -161,7 +156,7 @@ namespace ts { // export function x() { } const name = (node as FunctionDeclaration).name!; if (!uniqueExports.get(idText(name))) { - multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); + exportedBindings.add(getOriginalNode(node), name); uniqueExports.set(idText(name), true); exportedNames = append(exportedNames, name); } @@ -174,7 +169,7 @@ namespace ts { if (hasSyntacticModifier(node, ModifierFlags.Default)) { // export default class { } if (!hasExportDefault) { - multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), context.factory.getDeclarationName(node as ClassDeclaration)); + exportedBindings.add(getOriginalNode(node), context.factory.getDeclarationName(node as ClassDeclaration)); hasExportDefault = true; } } @@ -182,7 +177,7 @@ namespace ts { // export class x { } const name = (node as ClassDeclaration).name; if (name && !uniqueExports.get(idText(name))) { - multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); + exportedBindings.add(getOriginalNode(node), name); uniqueExports.set(idText(name), true); exportedNames = append(exportedNames, name); } @@ -211,7 +206,7 @@ namespace ts { || resolver.getReferencedValueDeclaration(name); if (decl) { - multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); + exportedBindings.add(getOriginalNode(decl), specifier.name); } uniqueExports.set(idText(specifier.name), true); @@ -239,18 +234,6 @@ namespace ts { return exportedNames; } - /** Use a sparse array as a multi-map. */ - function multiMapSparseArrayAdd(map: V[][], key: number, value: V): V[] { - let values = map[key]; - if (values) { - values.push(value); - } - else { - map[key] = values = [value]; - } - return values; - } - /** * Used in the module transformer to check if an expression is reasonably without sideeffect, * and thus better to copy into multiple places rather than to cache in a temporary variable From 6f76b625a157fa619fcbd862247caab8072a96db Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Sat, 6 Nov 2021 10:41:57 +0000 Subject: [PATCH 05/15] Remove uses of getNodeId in compiler outside of checker. --- src/compiler/transformers/es2017.ts | 6 +++--- src/compiler/transformers/es2018.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index ade1d41b010c8..66f10db0991f0 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -46,7 +46,7 @@ namespace ts { /** Whether the async function contains an element access on super (`super[x]`). */ let hasSuperElementAccess: boolean; /** A set of node IDs for generated super accessors (variable statements). */ - const substitutedSuperAccessors: boolean[] = []; + const substitutedSuperAccessors = new Set(); let contextFlags: ContextFlags = 0; @@ -503,7 +503,7 @@ namespace ts { enableSubstitutionForAsyncMethodsWithSuper(); if (capturedSuperProperties.size) { const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties); - substitutedSuperAccessors[getNodeId(variableStatement)] = true; + substitutedSuperAccessors.add(variableStatement); insertStatementsAfterStandardPrologue(statements, [variableStatement]); } } @@ -613,7 +613,7 @@ namespace ts { } } // Disable substitution in the generated super accessor itself. - else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) { + else if (enabledSubstitutions && substitutedSuperAccessors.has(node)) { const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; enclosingSuperContainerFlags = 0; previousOnEmitNode(hint, node, emitCallback); diff --git a/src/compiler/transformers/es2018.ts b/src/compiler/transformers/es2018.ts index 846fd992ff21b..f18c0964f6175 100644 --- a/src/compiler/transformers/es2018.ts +++ b/src/compiler/transformers/es2018.ts @@ -70,7 +70,7 @@ namespace ts { /** Whether the async function contains an element access on super (`super[x]`). */ let hasSuperElementAccess: boolean; /** A set of node IDs for generated super accessors. */ - const substitutedSuperAccessors: boolean[] = []; + const substitutedSuperAccessors = new Set(); return chainBundle(context, transformSourceFile); @@ -972,7 +972,7 @@ namespace ts { if (emitSuperHelpers) { enableSubstitutionForAsyncMethodsWithSuper(); const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties); - substitutedSuperAccessors[getNodeId(variableStatement)] = true; + substitutedSuperAccessors.add(variableStatement); insertStatementsAfterStandardPrologue(statements, [variableStatement]); } @@ -1087,7 +1087,7 @@ namespace ts { } } // Disable substitution in the generated super accessor itself. - else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) { + else if (enabledSubstitutions && substitutedSuperAccessors.has(node)) { const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; enclosingSuperContainerFlags = 0 as NodeCheckFlags; previousOnEmitNode(hint, node, emitCallback); From 5479adffc343343d8fea7cadbfe1381dfd315d86 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Sat, 6 Nov 2021 10:49:42 +0000 Subject: [PATCH 06/15] node -> originalNode --- src/compiler/transformers/module/module.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 4768df8ad8bcc..1305db0629d65 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -982,7 +982,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const originalNode = getOriginalNode(node); - deferredExports.set(node, appendExportsOfImportDeclaration(deferredExports.get(originalNode), node)); + deferredExports.set(originalNode, appendExportsOfImportDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -1073,7 +1073,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const originalNode = getOriginalNode(node); - deferredExports.set(node, appendExportsOfImportEqualsDeclaration(deferredExports.get(originalNode), node)); + deferredExports.set(originalNode, appendExportsOfImportEqualsDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -1207,7 +1207,7 @@ namespace ts { if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node const originalNode = getOriginalNode(node); - deferredExports.set(node, appendExportStatement(deferredExports.get(originalNode), factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true)); + deferredExports.set(originalNode, appendExportStatement(deferredExports.get(originalNode), factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true)); } else { statements = appendExportStatement(statements, factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true); @@ -1250,7 +1250,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const originalNode = getOriginalNode(node); - deferredExports.set(node, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); + deferredExports.set(originalNode, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -1291,7 +1291,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const originalNode = getOriginalNode(node); - deferredExports.set(node, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); + deferredExports.set(originalNode, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -1371,7 +1371,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const originalNode = getOriginalNode(node); - deferredExports.set(node, appendExportsOfVariableStatement(deferredExports.get(originalNode), node)); + deferredExports.set(originalNode, appendExportsOfVariableStatement(deferredExports.get(originalNode), node)); } else { statements = appendExportsOfVariableStatement(statements, node); @@ -1442,7 +1442,7 @@ namespace ts { // statement. if (hasAssociatedEndOfDeclarationMarker(node) && node.original!.kind === SyntaxKind.VariableStatement) { const originalNode = getOriginalNode(node); - deferredExports.set(node, appendExportsOfVariableStatement(deferredExports.get(originalNode), node.original as VariableStatement)); + deferredExports.set(originalNode, appendExportsOfVariableStatement(deferredExports.get(originalNode), node.original as VariableStatement)); } return node; From 41faf73bfc36e4cb7b911e2b8b262ee274d6bd41 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 8 Nov 2021 07:09:06 +0000 Subject: [PATCH 07/15] Switch back to putting Node IDs on Nodes. --- src/compiler/checker.ts | 31 ++++++++++++++----------------- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 3 +++ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 61c2d1568bb9c..2dde040f501d6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -277,15 +277,13 @@ namespace ts { this.flags = 0; } - const idCache = new Map(); export function getNodeId(node: Node): number { - let result = idCache.get(node); - if (!result) { - result = nextNodeId; - idCache.set(node, result); + let id = node.id; + if (!id) { + node.id = id = nextNodeId; nextNodeId++; } - return result; + return id; } export function getSymbolId(symbol: Symbol): SymbolId { @@ -963,7 +961,7 @@ namespace ts { let flowInvocationCount = 0; let lastFlowNode: FlowNode | undefined; let lastFlowNodeReachable: boolean; - let flowTypeCache: ESMap | undefined; + let flowTypeCache: Type[] | undefined; const emptyStringType = getStringLiteralType(""); const zeroType = getNumberLiteralType(0); @@ -977,7 +975,7 @@ namespace ts { const maximumSuggestionCount = 10; const mergedSymbols: Symbol[] = []; const symbolLinks: SymbolLinks[] = []; - const nodeLinks = new Map(); + const nodeLinks: NodeLinks[] = []; const flowLoopCaches: ESMap[] = []; const flowLoopNodes: FlowNode[] = []; const flowLoopKeys: string[] = []; @@ -1466,11 +1464,8 @@ namespace ts { } function getNodeLinks(node: Node): NodeLinks { - let result = nodeLinks.get(node); - if (!result) { - nodeLinks.set(node, result = new (NodeLinks as any)() as NodeLinks); - } - return result; + const nodeId = getNodeId(node); + return nodeLinks[nodeId] || (nodeLinks[nodeId] = new (NodeLinks as any)()); } function isGlobalSourceFile(node: Node) { @@ -33790,7 +33785,7 @@ namespace ts { } // If a type has been cached for the node, return it. if (node.flags & NodeFlags.TypeCached && flowTypeCache) { - const cachedType = flowTypeCache.get(node); + const cachedType = flowTypeCache[getNodeId(node)]; if (cachedType) { return cachedType; } @@ -33799,8 +33794,8 @@ namespace ts { const type = checkExpression(node); // If control flow analysis was required to determine the type, it is worth caching. if (flowInvocationCount !== startInvocationCount) { - const cache = (flowTypeCache ||= new Map()); - cache.set(node, type); + const cache = (flowTypeCache ||= []); + cache[getNodeId(node)] = type; setNodeFlags(node, node.flags | NodeFlags.TypeCached); } return type; @@ -41603,7 +41598,9 @@ namespace ts { } function getNodeCheckFlags(node: Node): NodeCheckFlags { - return nodeLinks.get(node)?.flags || 0; + const nodeId = node.id || 0; + if (nodeId < 0 || nodeId >= nodeLinks.length) return 0; + return nodeLinks[nodeId]?.flags || 0; } function getEnumMemberValue(node: EnumMember): string | number | undefined { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7f843c1036565..7e3a236036a10 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -857,6 +857,7 @@ namespace ts { /* @internal */ readonly transformFlags: TransformFlags; // Flags for transforms readonly decorators?: NodeArray; // Array of decorators (in document order) readonly modifiers?: ModifiersArray; // Array of modifiers + /* @internal */ id?: NodeId; // Unique id (used to look up NodeLinks) readonly parent: Node; // Parent node (initialized by binding) /* @internal */ original?: Node; // The original node if this is an updated node. /* @internal */ symbol: Symbol; // Symbol declared by node (initialized by binding) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ed7707d00911b..8caba5b205af8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5821,6 +5821,7 @@ namespace ts { this.pos = pos; this.end = end; this.kind = kind; + this.id = 0; this.flags = NodeFlags.None; this.modifierFlagsCache = ModifierFlags.None; this.transformFlags = TransformFlags.None; @@ -5832,6 +5833,7 @@ namespace ts { this.pos = pos; this.end = end; this.kind = kind; + this.id = 0; this.flags = NodeFlags.None; this.transformFlags = TransformFlags.None; this.parent = undefined!; @@ -5841,6 +5843,7 @@ namespace ts { this.pos = pos; this.end = end; this.kind = kind; + this.id = 0; this.flags = NodeFlags.None; this.transformFlags = TransformFlags.None; this.parent = undefined!; From 6a7d2c0a3eee39be020f46f3dd81639f51dc4377 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 8 Nov 2021 07:10:36 +0000 Subject: [PATCH 08/15] Restore redirect accessors. --- src/compiler/program.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 12fbb067ddcb1..57fa55bfb494f 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2592,6 +2592,10 @@ namespace ts { redirect.redirectInfo = { redirectTarget, unredirected }; sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); Object.defineProperties(redirect, { + id: { + get(this: SourceFile) { return this.redirectInfo!.redirectTarget.id; }, + set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo!.redirectTarget.id = value; }, + }, symbol: { get(this: SourceFile) { return this.redirectInfo!.redirectTarget.symbol; }, set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo!.redirectTarget.symbol = value; }, From f9d2db35408aeb0c1dbb9495edbd2dc69ac8a509 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 8 Nov 2021 07:12:41 +0000 Subject: [PATCH 09/15] Restore id check to harness. --- src/harness/harnessUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/harness/harnessUtils.ts b/src/harness/harnessUtils.ts index 7894eb8a28caf..cfc6f59f30050 100644 --- a/src/harness/harnessUtils.ts +++ b/src/harness/harnessUtils.ts @@ -219,6 +219,7 @@ namespace Utils { case "locals": case "localSymbol": case "kind": + case "id": case "nodeCount": case "symbolCount": case "identifierCount": From ced38f315e84cc481e4ff93b7ab613bd18813ef7 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 9 Nov 2021 00:34:36 +0000 Subject: [PATCH 10/15] Same trick for `symbol.id`, and always set it to 0. --- src/compiler/checker.ts | 7 ++++--- src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2dde040f501d6..4ce54bbcabec7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -287,12 +287,13 @@ namespace ts { } export function getSymbolId(symbol: Symbol): SymbolId { - if (!symbol.id) { - symbol.id = nextSymbolId; + let id = symbol.id; + if (!id) { + symbol.id = id = nextSymbolId; nextSymbolId++; } - return symbol.id; + return id; } export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7e3a236036a10..35e5d68d9b843 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4900,7 +4900,7 @@ namespace ts { members?: SymbolTable; // Class, interface or object literal instance members exports?: SymbolTable; // Module exports globalExports?: SymbolTable; // Conditional global UMD exports - /* @internal */ id?: SymbolId; // Unique id (used to look up SymbolLinks) + /* @internal */ id: SymbolId; // Unique id (used to look up SymbolLinks) /* @internal */ mergeId?: number; // Merge id (used to look up merged symbol) /* @internal */ parent?: Symbol; // Parent symbol /* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8caba5b205af8..1eb07d3328852 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5798,7 +5798,7 @@ namespace ts { this.escapedName = name; this.declarations = undefined; this.valueDeclaration = undefined; - this.id = undefined; + this.id = 0; this.mergeId = undefined; this.parent = undefined; } From cc3836911f405b245881533471a311bb2cffab10 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 9 Nov 2021 00:35:10 +0000 Subject: [PATCH 11/15] Switch to Set of Symbols in extractSymbol. --- src/services/refactors/extractSymbol.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 6490fde538708..6b9d722157910 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1597,7 +1597,7 @@ namespace ts.refactor.extractSymbol { const functionErrorsPerScope: Diagnostic[][] = []; const constantErrorsPerScope: Diagnostic[][] = []; const visibleDeclarationsInExtractedRange: NamedDeclaration[] = []; - const exposedVariableSymbolSet = new Map(); // Key is symbol ID + const exposedVariableSymbolSet = new Set(); // Key is symbol ID const exposedVariableDeclarations: VariableDeclaration[] = []; let firstExposedNonVariableDeclaration: NamedDeclaration | undefined; @@ -1903,10 +1903,10 @@ namespace ts.refactor.extractSymbol { const decl = find(visibleDeclarationsInExtractedRange, d => d.symbol === sym); if (decl) { if (isVariableDeclaration(decl)) { - const idString = decl.symbol.id!.toString(); - if (!exposedVariableSymbolSet.has(idString)) { + const declSymbol = decl.symbol; + if (!exposedVariableSymbolSet.has(declSymbol)) { exposedVariableDeclarations.push(decl); - exposedVariableSymbolSet.set(idString, true); + exposedVariableSymbolSet.add(declSymbol); } } else { From 083db3a6775eccb77c2b472757e8e409d2e487e0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 9 Nov 2021 00:58:52 +0000 Subject: [PATCH 12/15] Fix ID usage in services. --- src/services/services.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/services.ts b/src/services/services.ts index b04052a29c0b6..1782d3be1872d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -287,6 +287,7 @@ namespace ts { class SymbolObject implements Symbol { flags: SymbolFlags; + id = 0; escapedName: __String; declarations!: Declaration[]; valueDeclaration!: Declaration; From d42b2b708d433b4b87e89dcd2171d8ff716c2095 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 9 Nov 2021 01:09:57 +0000 Subject: [PATCH 13/15] Try just the fetching change in checker. --- src/compiler/binder.ts | 19 ++--- src/compiler/checker.ts | 12 ++-- src/compiler/emitter.ts | 11 ++- src/compiler/factory/nodeFactory.ts | 2 +- src/compiler/transformers/classFields.ts | 36 +++++----- src/compiler/transformers/declarations.ts | 28 ++++---- src/compiler/transformers/es2017.ts | 6 +- src/compiler/transformers/es2018.ts | 6 +- src/compiler/transformers/es5.ts | 9 ++- src/compiler/transformers/generators.ts | 8 +-- src/compiler/transformers/module/module.ts | 58 +++++++-------- src/compiler/transformers/module/system.ts | 82 ++++++++++++---------- src/compiler/transformers/ts.ts | 10 +-- src/compiler/transformers/utilities.ts | 33 ++++++--- src/compiler/types.ts | 6 +- 15 files changed, 174 insertions(+), 152 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3db8807b5b723..8ff6712ad30ac 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?: ESMap): ModuleInstanceState { + export function getModuleInstanceState(node: ModuleDeclaration, visited?: ESMap): 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,17 +24,18 @@ namespace ts { return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated; } - function getModuleInstanceStateCached(node: Node, visited = new Map()) { - if (visited.has(node)) { - return visited.get(node) || ModuleInstanceState.NonInstantiated; + function getModuleInstanceStateCached(node: Node, visited = new Map()) { + const nodeId = getNodeId(node); + if (visited.has(nodeId)) { + return visited.get(nodeId) || ModuleInstanceState.NonInstantiated; } - visited.set(node, undefined); + visited.set(nodeId, undefined); const result = getModuleInstanceStateWorker(node, visited); - visited.set(node, result); + visited.set(nodeId, result); return result; } - function getModuleInstanceStateWorker(node: Node, visited: ESMap): ModuleInstanceState { + function getModuleInstanceStateWorker(node: Node, visited: ESMap): ModuleInstanceState { // A module is uninstantiated if it contains only switch (node.kind) { // 1. interface declarations, type alias declarations @@ -106,7 +107,7 @@ namespace ts { return ModuleInstanceState.Instantiated; } - function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: ESMap) { + function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: ESMap) { const name = specifier.propertyName || specifier.name; let p: Node | undefined = specifier.parent; while (p) { @@ -2983,7 +2984,7 @@ namespace ts { function addLateBoundAssignmentDeclarationToSymbol(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol | undefined) { if (symbol) { - (symbol.assignmentDeclarationMembers ||= new Set()).add(node); + (symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = new Map())).set(getNodeId(node), node); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4ce54bbcabec7..f74d36a2392dd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3887,9 +3887,10 @@ namespace ts { function getAlternativeContainingModules(symbol: Symbol, enclosingDeclaration: Node): Symbol[] { const containingFile = getSourceFileOfNode(enclosingDeclaration); + const id = getNodeId(containingFile); const links = getSymbolLinks(symbol); let results: Symbol[] | undefined; - if (links.extendedContainersByFile && (results = links.extendedContainersByFile.get(containingFile))) { + if (links.extendedContainersByFile && (results = links.extendedContainersByFile.get(id))) { return results; } if (containingFile && containingFile.imports) { @@ -3903,7 +3904,7 @@ namespace ts { results = append(results, resolvedModule); } if (length(results)) { - (links.extendedContainersByFile ||= new Map()).set(containingFile, results!); + (links.extendedContainersByFile || (links.extendedContainersByFile = new Map())).set(id, results!); return results!; } } @@ -33795,7 +33796,7 @@ namespace ts { const type = checkExpression(node); // If control flow analysis was required to determine the type, it is worth caching. if (flowInvocationCount !== startInvocationCount) { - const cache = (flowTypeCache ||= []); + const cache = flowTypeCache || (flowTypeCache = []); cache[getNodeId(node)] = type; setNodeFlags(node, node.flags | NodeFlags.TypeCached); } @@ -40317,8 +40318,9 @@ namespace ts { const enclosingFile = getSourceFileOfNode(node); const links = getNodeLinks(enclosingFile); if (!(links.flags & NodeCheckFlags.TypeChecked)) { - links.deferredNodes ||= new Set(); - links.deferredNodes.add(node); + links.deferredNodes = links.deferredNodes || new Map(); + const id = getNodeId(node); + links.deferredNodes.set(id, node); } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3025be42a8040..d4eff1e0ace5e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -869,7 +869,7 @@ namespace ts { const bundledHelpers = new Map(); let currentSourceFile: SourceFile | undefined; - let nodeToGeneratedName: ESMap; // Map of generated names for specific nodes. + let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes. let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables. let generatedNames: Set; // Set of names generated by the NameGenerator. let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes. @@ -1154,7 +1154,7 @@ namespace ts { } function reset() { - nodeToGeneratedName = new Map(); + nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; generatedNames = new Set(); tempFlagsStack = []; @@ -5008,11 +5008,8 @@ namespace ts { } function generateNameCached(node: Node, flags?: GeneratedIdentifierFlags) { - let result = nodeToGeneratedName.get(node); - if (result === undefined) { - nodeToGeneratedName.set(node, result = generateNameForNode(node, flags)); - } - return result; + const nodeId = getNodeId(node); + return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = generateNameForNode(node, flags)); } /** diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 63b241dc9e58a..963ce75441f6e 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -5339,7 +5339,7 @@ namespace ts { } function flattenCommaElements(node: Expression): Expression | readonly Expression[] { - if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node.original && !node.emitNode) { + if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node.original && !node.emitNode && !node.id) { if (isCommaListExpression(node)) { return node.elements; } diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 4cebe15215b10..7d4afe2baffc4 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -147,7 +147,7 @@ namespace ts { let enabledSubstitutions: ClassPropertySubstitutionFlags; - let classAliases: ESMap; + let classAliases: Identifier[]; /** * Tracks what computed name expressions originating from elided names must be inlined @@ -162,7 +162,7 @@ namespace ts { let pendingStatements: Statement[] | undefined; const classLexicalEnvironmentStack: (ClassLexicalEnvironment | undefined)[] = []; - const classLexicalEnvironmentMap = new WeakMap(); + const classLexicalEnvironmentMap = new Map(); let currentClassLexicalEnvironment: ClassLexicalEnvironment | undefined; let currentComputedPropertyNameClassLexicalEnvironment: ClassLexicalEnvironment | undefined; let currentStaticPropertyDeclarationOrStaticBlock: PropertyDeclaration | ClassStaticBlockDeclaration | undefined; @@ -738,7 +738,7 @@ namespace ts { function transformClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration) { if (shouldTransformPrivateElementsOrClassStaticBlocks) { if (currentClassLexicalEnvironment) { - classLexicalEnvironmentMap.set(getOriginalNode(node), currentClassLexicalEnvironment); + classLexicalEnvironmentMap.set(getOriginalNodeId(node), currentClassLexicalEnvironment); } startLexicalEnvironment(); @@ -1128,7 +1128,7 @@ namespace ts { enableSubstitutionForClassAliases(); const alias = factory.cloneNode(temp) as GeneratedIdentifier; alias.autoGenerateFlags &= ~GeneratedIdentifierFlags.ReservedInNestedScopes; - classAliases.set(getOriginalNode(node), alias); + classAliases[getOriginalNodeId(node)] = alias; } // To preserve the behavior of the old emitter, we explicitly indent @@ -1375,7 +1375,7 @@ namespace ts { // capture the lexical environment for the member setOriginalNode(transformed, property); addEmitFlags(transformed, EmitFlags.AdviseOnEmitNode); - classLexicalEnvironmentMap.set(getOriginalNode(transformed), currentClassLexicalEnvironment); + classLexicalEnvironmentMap.set(getOriginalNodeId(transformed), currentClassLexicalEnvironment); } currentStaticPropertyDeclarationOrStaticBlock = savedCurrentStaticPropertyDeclarationOrStaticBlock; return transformed; @@ -1453,7 +1453,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); // Keep track of class aliases. - classAliases = new Map(); + classAliases = []; } } @@ -1516,16 +1516,18 @@ namespace ts { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { const original = getOriginalNode(node); - const classLexicalEnvironment = classLexicalEnvironmentMap.get(original); - if (classLexicalEnvironment) { - const savedClassLexicalEnvironment = currentClassLexicalEnvironment; - const savedCurrentComputedPropertyNameClassLexicalEnvironment = currentComputedPropertyNameClassLexicalEnvironment; - currentClassLexicalEnvironment = classLexicalEnvironment; - currentComputedPropertyNameClassLexicalEnvironment = classLexicalEnvironment; - previousOnEmitNode(hint, node, emitCallback); - currentClassLexicalEnvironment = savedClassLexicalEnvironment; - currentComputedPropertyNameClassLexicalEnvironment = savedCurrentComputedPropertyNameClassLexicalEnvironment; - return; + if (original.id) { + const classLexicalEnvironment = classLexicalEnvironmentMap.get(original.id); + if (classLexicalEnvironment) { + const savedClassLexicalEnvironment = currentClassLexicalEnvironment; + const savedCurrentComputedPropertyNameClassLexicalEnvironment = currentComputedPropertyNameClassLexicalEnvironment; + currentClassLexicalEnvironment = classLexicalEnvironment; + currentComputedPropertyNameClassLexicalEnvironment = classLexicalEnvironment; + previousOnEmitNode(hint, node, emitCallback); + currentClassLexicalEnvironment = savedClassLexicalEnvironment; + currentComputedPropertyNameClassLexicalEnvironment = savedCurrentComputedPropertyNameClassLexicalEnvironment; + return; + } } switch (node.kind) { @@ -1631,7 +1633,7 @@ namespace ts { // constructor references in static property initializers. const declaration = resolver.getReferencedValueDeclaration(node); if (declaration) { - const classAlias = classAliases.get(declaration); + const classAlias = classAliases[declaration.id!]; // TODO: GH#18217 if (classAlias) { const clone = factory.cloneNode(classAlias); setSourceMapRange(clone, node); diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 8ed26f1aeb5cd..6cea3b85055ee 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -60,7 +60,7 @@ namespace ts { let enclosingDeclaration: Node; let necessaryTypeReferences: Set | undefined; let lateMarkedStatements: LateVisibilityPaintedStatement[] | undefined; - let lateStatementReplacementMap: ESMap>; + let lateStatementReplacementMap: ESMap>; let suppressNewDiagnosticContexts: boolean; let exportedModulesFromDeclarationEmit: Symbol[] | undefined; @@ -84,7 +84,7 @@ namespace ts { let errorFallbackNode: Declaration | undefined; let currentSourceFile: SourceFile; - let refs: ESMap; + let refs: ESMap; let libs: ESMap; let emittedImports: readonly AnyImportSyntax[] | undefined; // must be declared in container so it can be `undefined` while transformer's first pass const resolver = context.getEmitResolver(); @@ -110,7 +110,7 @@ namespace ts { } // Otherwise we should emit a path-based reference const container = getSourceFileOfNode(node); - refs.set(getOriginalNode(container), container); + refs.set(getOriginalNodeId(container), container); } function handleSymbolAccessibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) { @@ -426,12 +426,12 @@ namespace ts { } } - function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: ESMap) { + function collectReferences(sourceFile: SourceFile | UnparsedSource, ret: ESMap) { if (noResolve || (!isUnparsedSource(sourceFile) && isSourceFileJS(sourceFile))) return ret; forEach(sourceFile.referencedFiles, f => { const elem = host.getSourceFileFromReference(sourceFile, f); if (elem) { - ret.set(getOriginalNode(elem), elem); + ret.set(getOriginalNodeId(elem), elem); } }); return ret; @@ -812,7 +812,7 @@ namespace ts { needsDeclare = i.parent && isSourceFile(i.parent) && !(isExternalModule(i.parent) && isBundledEmit); const result = transformTopLevelDeclaration(i); needsDeclare = priorNeedsDeclare; - lateStatementReplacementMap.set(getOriginalNode(i), result); + lateStatementReplacementMap.set(getOriginalNodeId(i), result); } // And lastly, we need to get the final form of all those indetermine import declarations from before and add them to the output list @@ -821,10 +821,10 @@ namespace ts { function visitLateVisibilityMarkedStatements(statement: Statement) { if (isLateVisibilityPaintedStatement(statement)) { - const originalNode = getOriginalNode(statement); - if (lateStatementReplacementMap.has(originalNode)) { - const result = lateStatementReplacementMap.get(originalNode); - lateStatementReplacementMap.delete(originalNode); + const key = getOriginalNodeId(statement); + if (lateStatementReplacementMap.has(key)) { + const result = lateStatementReplacementMap.get(key); + lateStatementReplacementMap.delete(key); if (result) { if (isArray(result) ? some(result, needsScopeMarker) : needsScopeMarker(result)) { // Top-level declarations in .d.ts files are always considered exported even without a modifier unless there's an export assignment or specifier @@ -1146,7 +1146,7 @@ namespace ts { const result = transformTopLevelDeclaration(input); // Don't actually transform yet; just leave as original node - will be elided/swapped by late pass - lateStatementReplacementMap.set(getOriginalNode(input), result); + lateStatementReplacementMap.set(getOriginalNodeId(input), result); return input; } @@ -1348,9 +1348,9 @@ namespace ts { needsDeclare = false; visitNode(inner, visitDeclarationStatements); // eagerly transform nested namespaces (the nesting doesn't need any elision or painting done) - const originalNode = getOriginalNode(inner!); // TODO: GH#18217 - const body = lateStatementReplacementMap.get(originalNode); - lateStatementReplacementMap.delete(originalNode); + const id = getOriginalNodeId(inner!); // TODO: GH#18217 + const body = lateStatementReplacementMap.get(id); + lateStatementReplacementMap.delete(id); return cleanup(factory.updateModuleDeclaration( input, /*decorators*/ undefined, diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 66f10db0991f0..ade1d41b010c8 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -46,7 +46,7 @@ namespace ts { /** Whether the async function contains an element access on super (`super[x]`). */ let hasSuperElementAccess: boolean; /** A set of node IDs for generated super accessors (variable statements). */ - const substitutedSuperAccessors = new Set(); + const substitutedSuperAccessors: boolean[] = []; let contextFlags: ContextFlags = 0; @@ -503,7 +503,7 @@ namespace ts { enableSubstitutionForAsyncMethodsWithSuper(); if (capturedSuperProperties.size) { const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties); - substitutedSuperAccessors.add(variableStatement); + substitutedSuperAccessors[getNodeId(variableStatement)] = true; insertStatementsAfterStandardPrologue(statements, [variableStatement]); } } @@ -613,7 +613,7 @@ namespace ts { } } // Disable substitution in the generated super accessor itself. - else if (enabledSubstitutions && substitutedSuperAccessors.has(node)) { + else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) { const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; enclosingSuperContainerFlags = 0; previousOnEmitNode(hint, node, emitCallback); diff --git a/src/compiler/transformers/es2018.ts b/src/compiler/transformers/es2018.ts index f18c0964f6175..846fd992ff21b 100644 --- a/src/compiler/transformers/es2018.ts +++ b/src/compiler/transformers/es2018.ts @@ -70,7 +70,7 @@ namespace ts { /** Whether the async function contains an element access on super (`super[x]`). */ let hasSuperElementAccess: boolean; /** A set of node IDs for generated super accessors. */ - const substitutedSuperAccessors = new Set(); + const substitutedSuperAccessors: boolean[] = []; return chainBundle(context, transformSourceFile); @@ -972,7 +972,7 @@ namespace ts { if (emitSuperHelpers) { enableSubstitutionForAsyncMethodsWithSuper(); const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties); - substitutedSuperAccessors.add(variableStatement); + substitutedSuperAccessors[getNodeId(variableStatement)] = true; insertStatementsAfterStandardPrologue(statements, [variableStatement]); } @@ -1087,7 +1087,7 @@ namespace ts { } } // Disable substitution in the generated super accessor itself. - else if (enabledSubstitutions && substitutedSuperAccessors.has(node)) { + else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) { const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags; enclosingSuperContainerFlags = 0 as NodeCheckFlags; previousOnEmitNode(hint, node, emitCallback); diff --git a/src/compiler/transformers/es5.ts b/src/compiler/transformers/es5.ts index 9dc69f2147598..e82ba20cf5f9f 100644 --- a/src/compiler/transformers/es5.ts +++ b/src/compiler/transformers/es5.ts @@ -11,14 +11,14 @@ namespace ts { // enable emit notification only if using --jsx preserve or react-native let previousOnEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; - let noSubstitution: Set; + let noSubstitution: boolean[]; if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) { previousOnEmitNode = context.onEmitNode; context.onEmitNode = onEmitNode; context.enableEmitNotification(SyntaxKind.JsxOpeningElement); context.enableEmitNotification(SyntaxKind.JsxClosingElement); context.enableEmitNotification(SyntaxKind.JsxSelfClosingElement); - noSubstitution = new Set(); + noSubstitution = []; } const previousOnSubstituteNode = context.onSubstituteNode; @@ -49,7 +49,7 @@ namespace ts { case SyntaxKind.JsxClosingElement: case SyntaxKind.JsxSelfClosingElement: const tagName = (node as JsxOpeningElement | JsxClosingElement | JsxSelfClosingElement).tagName; - noSubstitution.add(getOriginalNode(tagName)); + noSubstitution[getOriginalNodeId(tagName)] = true; break; } @@ -63,8 +63,7 @@ namespace ts { * @param node The node to substitute. */ function onSubstituteNode(hint: EmitHint, node: Node) { - // TODO: do we need to check for a Node ID here? - if (noSubstitution?.has(node)) { + if (node.id && noSubstitution && noSubstitution[node.id]) { return previousOnSubstituteNode(hint, node); } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 24acc78ae3c82..73e35b4583d0e 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -245,7 +245,7 @@ namespace ts { context.onSubstituteNode = onSubstituteNode; let renamedCatchVariables: ESMap; - let renamedCatchVariableDeclarations: ESMap; + let renamedCatchVariableDeclarations: Identifier[]; let inGeneratorFunctionBody: boolean; let inStatementContainingYield: boolean; @@ -1969,7 +1969,7 @@ namespace ts { if (isIdentifier(original) && original.parent) { const declaration = resolver.getReferencedValueDeclaration(original); if (declaration) { - const name = renamedCatchVariableDeclarations.get(getOriginalNode(declaration)); + const name = renamedCatchVariableDeclarations[getOriginalNodeId(declaration)]; if (name) { // TODO(rbuckton): Does this need to be parented? const clone = setParent(setTextRange(factory.cloneNode(name), name), name.parent); @@ -2137,12 +2137,12 @@ namespace ts { name = declareLocal(text); if (!renamedCatchVariables) { renamedCatchVariables = new Map(); - renamedCatchVariableDeclarations = new Map(); + renamedCatchVariableDeclarations = []; context.enableSubstitution(SyntaxKind.Identifier); } renamedCatchVariables.set(text, true); - renamedCatchVariableDeclarations.set(getOriginalNode(variable), name); + renamedCatchVariableDeclarations[getOriginalNodeId(variable)] = name; } const exception = peekBlock() as ExceptionBlock; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 1305db0629d65..9c88471640956 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -40,12 +40,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 = new Map(); // The ExternalModuleInfo for each file. - const deferredExports = new Map();; // Exports to defer until an EndOfDeclarationMarker is found. + const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file. + const deferredExports: (Statement[] | undefined)[] = []; // Exports to defer until an EndOfDeclarationMarker is found. let currentSourceFile: SourceFile; // The current file. let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file. - const noSubstitution = new Set(); // Set of nodes for which substitution rules should be ignored. + const noSubstitution: boolean[] = []; // Set of nodes for which substitution rules should be ignored. let needUMDDynamicImportHelper: boolean; return chainBundle(context, transformSourceFile); @@ -65,7 +65,7 @@ namespace ts { currentSourceFile = node; currentModuleInfo = collectExternalModuleInfo(context, node, resolver, compilerOptions); - moduleInfoMap.set(getOriginalNode(node), currentModuleInfo); + moduleInfoMap[getOriginalNodeId(node)] = currentModuleInfo; // Perform the transformation. const transformModule = getTransformModuleDelegate(moduleKind); @@ -692,13 +692,13 @@ namespace ts { } for (const exportName of exportedNames) { - noSubstitution.add(expression); + noSubstitution[getNodeId(expression)] = true; expression = createExportExpression(exportName, expression); setTextRange(expression, node); } if (temp) { - noSubstitution.add(expression); + noSubstitution[getNodeId(expression)] = true; expression = factory.createComma(expression, temp); setTextRange(expression, node); } @@ -981,8 +981,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfImportDeclaration(deferredExports.get(originalNode), node)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -1072,8 +1072,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfImportEqualsDeclaration(deferredExports.get(originalNode), node)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -1206,8 +1206,8 @@ namespace ts { const original = node.original; if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportStatement(deferredExports.get(originalNode), factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true); } else { statements = appendExportStatement(statements, factory.createIdentifier("default"), visitNode(node.expression, visitor), /*location*/ node, /*allowComments*/ true); @@ -1249,8 +1249,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -1290,8 +1290,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -1370,8 +1370,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfVariableStatement(deferredExports.get(originalNode), node)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node); } else { statements = appendExportsOfVariableStatement(statements, node); @@ -1441,8 +1441,8 @@ namespace ts { // To balance the declaration, add the exports of the elided variable // statement. if (hasAssociatedEndOfDeclarationMarker(node) && node.original!.kind === SyntaxKind.VariableStatement) { - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfVariableStatement(deferredExports.get(originalNode), node.original as VariableStatement)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original as VariableStatement); } return node; @@ -1467,10 +1467,10 @@ namespace ts { // For some transformations we emit an `EndOfDeclarationMarker` to mark the actual // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. - const originalNode = getOriginalNode(node); - const statements = deferredExports.get(originalNode); + const id = getOriginalNodeId(node); + const statements = deferredExports[id]; if (statements) { - deferredExports.delete(node); + delete deferredExports[id]; return append(statements, node); } @@ -1770,7 +1770,7 @@ namespace ts { function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { currentSourceFile = node as SourceFile; - currentModuleInfo = moduleInfoMap.get(getOriginalNode(currentSourceFile))!; + currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)]; previousOnEmitNode(hint, node, emitCallback); @@ -1794,7 +1794,7 @@ namespace ts { */ function onSubstituteNode(hint: EmitHint, node: Node) { node = previousOnSubstituteNode(hint, node); - if (noSubstitution.has(node)) { + if (node.id && noSubstitution[node.id]) { return node; } @@ -1852,7 +1852,7 @@ namespace ts { function substituteCallExpression(node: CallExpression) { if (isIdentifier(node.expression)) { const expression = substituteExpressionIdentifier(node.expression); - noSubstitution.add(expression); + noSubstitution[getNodeId(expression)] = true; if (!isIdentifier(expression)) { return addEmitFlags( factory.updateCallExpression(node, @@ -1871,7 +1871,7 @@ namespace ts { function substituteTaggedTemplateExpression(node: TaggedTemplateExpression) { if (isIdentifier(node.tag)) { const tag = substituteExpressionIdentifier(node.tag); - noSubstitution.add(tag); + noSubstitution[getNodeId(tag)] = true; if (!isIdentifier(tag)) { return addEmitFlags( factory.updateTaggedTemplateExpression(node, @@ -1962,7 +1962,7 @@ namespace ts { let expression: Expression = node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. - noSubstitution.add(expression); + noSubstitution[getNodeId(expression)] = true; expression = createExportExpression(exportName, expression, /*location*/ node); } @@ -1984,7 +1984,7 @@ namespace ts { || resolver.getReferencedValueDeclaration(name); if (valueDeclaration) { return currentModuleInfo - && currentModuleInfo.exportedBindings.get(getOriginalNode(valueDeclaration)); + && currentModuleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]; } } } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index cf8ef6b7af326..14216e5cab63f 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -26,11 +26,11 @@ namespace ts { context.enableSubstitution(SyntaxKind.MetaProperty); // Substitutes 'import.meta' context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap = new Map(); // The ExternalModuleInfo for each file. - const deferredExports = new Map(); // Exports to defer until an EndOfDeclarationMarker is found. - const exportFunctionsMap = new Map(); // The export function associated with a source file. - const noSubstitutionMap = new Map>(); // Set of nodes for which substitution rules should be ignored for each file. - const contextObjectMap = new Map(); // The context object associated with a source file. + const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file. + const deferredExports: (Statement[] | undefined)[] = []; // Exports to defer until an EndOfDeclarationMarker is found. + const exportFunctionsMap: Identifier[] = []; // The export function associated with a source file. + const noSubstitutionMap: boolean[][] = []; // Set of nodes for which substitution rules should be ignored for each file. + const contextObjectMap: Identifier[] = []; // The context object associated with a source file. let currentSourceFile: SourceFile; // The current file. let moduleInfo: ExternalModuleInfo; // ExternalModuleInfo for the current file. @@ -38,7 +38,7 @@ namespace ts { let contextObject: Identifier; // The context object for the current file. let hoistedStatements: Statement[] | undefined; let enclosingBlockScopedContainer: Node; - let noSubstitution: Set | undefined; // Set of nodes for which substitution rules should be ignored. + let noSubstitution: boolean[] | undefined; // Set of nodes for which substitution rules should be ignored. return chainBundle(context, transformSourceFile); @@ -52,7 +52,7 @@ namespace ts { return node; } - const originalNode = getOriginalNode(node); + const id = getOriginalNodeId(node); currentSourceFile = node; enclosingBlockScopedContainer = node; @@ -70,13 +70,13 @@ namespace ts { // see comment to 'substitutePostfixUnaryExpression' for more details // Collect information about the external module and dependency groups. - moduleInfoMap.set(originalNode, moduleInfo = collectExternalModuleInfo(context, node, resolver, compilerOptions)); + moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(context, node, resolver, compilerOptions); // Make sure that the name of the 'exports' function does not conflict with // existing identifiers. exportFunction = factory.createUniqueName("exports"); - exportFunctionsMap.set(originalNode, exportFunction); - contextObjectMap.set(originalNode, contextObject = factory.createUniqueName("context")); + exportFunctionsMap[id] = exportFunction; + contextObject = contextObjectMap[id] = factory.createUniqueName("context"); // Add the body of the module. const dependencyGroups = collectDependencyGroups(moduleInfo.externalImports); @@ -123,7 +123,7 @@ namespace ts { } if (noSubstitution) { - noSubstitutionMap.set(originalNode, noSubstitution); + noSubstitutionMap[id] = noSubstitution; noSubstitution = undefined; } @@ -596,8 +596,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfImportDeclaration(deferredExports.get(originalNode), node)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -624,8 +624,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfImportEqualsDeclaration(deferredExports.get(originalNode), node)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -649,8 +649,8 @@ namespace ts { const original = node.original; if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportStatement(deferredExports.get(originalNode), factory.createIdentifier("default"), expression, /*allowComments*/ true)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), expression, /*allowComments*/ true); } else { return createExportStatement(factory.createIdentifier("default"), expression, /*allowComments*/ true); @@ -682,8 +682,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { hoistedStatements = appendExportsOfHoistedDeclaration(hoistedStatements, node); @@ -729,8 +729,8 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfHoistedDeclaration(deferredExports.get(originalNode), node)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -769,8 +769,8 @@ namespace ts { if (isMarkedDeclaration) { // Defer exports until we encounter an EndOfDeclarationMarker node - const originalNode = getOriginalNode(node); - deferredExports.set(originalNode, appendExportsOfVariableStatement(deferredExports.get(originalNode), node, isExportedDeclaration)); + const id = getOriginalNodeId(node); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node, isExportedDeclaration); } else { statements = appendExportsOfVariableStatement(statements, node, /*exportSelf*/ false); @@ -881,9 +881,9 @@ namespace ts { // To balance the declaration, we defer the exports of the elided variable // statement until we visit this declaration's `EndOfDeclarationMarker`. if (hasAssociatedEndOfDeclarationMarker(node) && node.original!.kind === SyntaxKind.VariableStatement) { - const originalNode = getOriginalNode(node); + const id = getOriginalNodeId(node); const isExportedDeclaration = hasSyntacticModifier(node.original!, ModifierFlags.Export); - deferredExports.set(originalNode, appendExportsOfVariableStatement(deferredExports.get(originalNode), node.original as VariableStatement, isExportedDeclaration)); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original as VariableStatement, isExportedDeclaration); } return node; @@ -908,15 +908,16 @@ namespace ts { // For some transformations we emit an `EndOfDeclarationMarker` to mark the actual // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. - const originalNode = getOriginalNode(node); - const statements = deferredExports.get(originalNode); + const id = getOriginalNodeId(node); + const statements = deferredExports[id]; if (statements) { - deferredExports.delete(originalNode); + delete deferredExports[id]; return append(statements, node); } else { - if (isModuleOrEnumDeclaration(originalNode)) { - return append(appendExportsOfDeclaration(statements, originalNode), node); + const original = getOriginalNode(node); + if (isModuleOrEnumDeclaration(original)) { + return append(appendExportsOfDeclaration(statements, original), node); } } @@ -1677,14 +1678,16 @@ namespace ts { */ function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { - const originalNode = getOriginalNode(node); + const id = getOriginalNodeId(node); currentSourceFile = node as SourceFile; - moduleInfo = moduleInfoMap.get(originalNode)!; - exportFunction = exportFunctionsMap.get(originalNode)!; - noSubstitution = noSubstitutionMap.get(originalNode)!; - contextObject = contextObjectMap.get(originalNode)!; + moduleInfo = moduleInfoMap[id]; + exportFunction = exportFunctionsMap[id]; + noSubstitution = noSubstitutionMap[id]; + contextObject = contextObjectMap[id]; - noSubstitutionMap.delete(originalNode); + if (noSubstitution) { + delete noSubstitutionMap[id]; + } previousOnEmitNode(hint, node, emitCallback); @@ -1901,7 +1904,7 @@ namespace ts { exportedNames = append(exportedNames, factory.getDeclarationName(valueDeclaration)); } - exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings.get(getOriginalNode(valueDeclaration))); + exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]); } } @@ -1914,7 +1917,8 @@ namespace ts { * @param node The node which should not be substituted. */ function preventSubstitution(node: T): T { - (noSubstitution ||= new Set()).add(node); + if (noSubstitution === undefined) noSubstitution = []; + noSubstitution[getNodeId(node)] = true; return node; } @@ -1924,7 +1928,7 @@ namespace ts { * @param node The node to test. */ function isSubstitutionPrevented(node: Node) { - return noSubstitution?.has(node); + return noSubstitution && node.id && noSubstitution[node.id]; } } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 6013ec97f1144..8561db1c633ef 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -78,7 +78,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: ESMap; + let classAliases: Identifier[]; /** * Keeps track of whether we are within any containing namespaces when performing @@ -1237,7 +1237,7 @@ namespace ts { return undefined; } - const classAlias = classAliases?.get(getOriginalNode(node)); + const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; // When we transform to ES5/3 this will be moved inside an IIFE and should reference the name // without any block-scoped variable collision handling @@ -3139,7 +3139,7 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { enableSubstitutionForClassAliases(); const classAlias = factory.createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? idText(node.name) : "default"); - classAliases.set(getOriginalNode(node), classAlias); + classAliases[getOriginalNodeId(node)] = classAlias; hoistVariableDeclaration(classAlias); return classAlias; } @@ -3171,7 +3171,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); // Keep track of class aliases. - classAliases = new Map(); + classAliases = []; } } @@ -3290,7 +3290,7 @@ namespace ts { // constructor references in static property initializers. const declaration = resolver.getReferencedValueDeclaration(node); if (declaration) { - const classAlias = classAliases.get(declaration); + const classAlias = classAliases[declaration.id!]; // TODO: GH#18217 if (classAlias) { const clone = factory.cloneNode(classAlias); setSourceMapRange(clone, node); diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index c920e96b32db4..276d034c7b92c 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -1,10 +1,15 @@ /* @internal */ namespace ts { + export function getOriginalNodeId(node: Node) { + node = getOriginalNode(node); + return node ? getNodeId(node) : 0; + } + export interface ExternalModuleInfo { externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers exportSpecifiers: ESMap; // file-local export specifiers by name (no reexports) - exportedBindings: MultiMap; // exported names of local declarations + exportedBindings: Identifier[][]; // exported names of local declarations exportedNames: Identifier[] | undefined; // all exported names in the module, both local and reexported exportEquals: ExportAssignment | undefined; // an export= declaration if one was present hasExportStarsToExportValues: boolean; // whether this module contains export* @@ -63,7 +68,7 @@ namespace ts { export function collectExternalModuleInfo(context: TransformationContext, sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; const exportSpecifiers = createMultiMap(); - const exportedBindings = createMultiMap(); + const exportedBindings: Identifier[][] = []; const uniqueExports = new Map(); let exportedNames: Identifier[] | undefined; let hasExportDefault = false; @@ -113,7 +118,7 @@ namespace ts { else { const name = ((node as ExportDeclaration).exportClause as NamespaceExport).name; if (!uniqueExports.get(idText(name))) { - exportedBindings.add(getOriginalNode(node), name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); uniqueExports.set(idText(name), true); exportedNames = append(exportedNames, name); } @@ -148,7 +153,7 @@ namespace ts { if (hasSyntacticModifier(node, ModifierFlags.Default)) { // export default function() { } if (!hasExportDefault) { - exportedBindings.add(getOriginalNode(node), context.factory.getDeclarationName(node as FunctionDeclaration)); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), context.factory.getDeclarationName(node as FunctionDeclaration)); hasExportDefault = true; } } @@ -156,7 +161,7 @@ namespace ts { // export function x() { } const name = (node as FunctionDeclaration).name!; if (!uniqueExports.get(idText(name))) { - exportedBindings.add(getOriginalNode(node), name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); uniqueExports.set(idText(name), true); exportedNames = append(exportedNames, name); } @@ -169,7 +174,7 @@ namespace ts { if (hasSyntacticModifier(node, ModifierFlags.Default)) { // export default class { } if (!hasExportDefault) { - exportedBindings.add(getOriginalNode(node), context.factory.getDeclarationName(node as ClassDeclaration)); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), context.factory.getDeclarationName(node as ClassDeclaration)); hasExportDefault = true; } } @@ -177,7 +182,7 @@ namespace ts { // export class x { } const name = (node as ClassDeclaration).name; if (name && !uniqueExports.get(idText(name))) { - exportedBindings.add(getOriginalNode(node), name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); uniqueExports.set(idText(name), true); exportedNames = append(exportedNames, name); } @@ -206,7 +211,7 @@ namespace ts { || resolver.getReferencedValueDeclaration(name); if (decl) { - exportedBindings.add(getOriginalNode(decl), specifier.name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); } uniqueExports.set(idText(specifier.name), true); @@ -234,6 +239,18 @@ namespace ts { return exportedNames; } + /** Use a sparse array as a multi-map. */ + function multiMapSparseArrayAdd(map: V[][], key: number, value: V): V[] { + let values = map[key]; + if (values) { + values.push(value); + } + else { + map[key] = values = [value]; + } + return values; + } + /** * Used in the module transformer to check if an expression is reasonably without sideeffect, * and thus better to copy into multiple places rather than to cache in a temporary variable diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 35e5d68d9b843..170c65b1271f0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4908,7 +4908,7 @@ namespace ts { /* @internal */ isReferenced?: SymbolFlags; // True if the symbol is referenced elsewhere. Keeps track of the meaning of a reference in case a symbol is both a type parameter and parameter. /* @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? /* @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments - /* @internal */ assignmentDeclarationMembers?: Set; // detected late-bound assignment declarations associated with the symbol + /* @internal */ assignmentDeclarationMembers?: ESMap; // detected late-bound assignment declarations associated with the symbol } /* @internal */ @@ -4946,7 +4946,7 @@ namespace ts { lateSymbol?: Symbol; // Late-bound symbol for a computed property specifierCache?: ESMap; // For symbols corresponding to external modules, a cache of incoming path -> module specifier name mappings extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in - extendedContainersByFile?: ESMap; // Containers (other than the parent) which this symbol is aliased in + extendedContainersByFile?: ESMap; // Containers (other than the parent) which this symbol is aliased in variances?: VarianceFlags[]; // Alias symbol type argument variance cache deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type deferralParent?: Type; // Source union/intersection of a deferred type @@ -5106,7 +5106,7 @@ namespace ts { jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node jsxImplicitImportContainer?: Symbol | false; // Resolved module symbol the implicit jsx import of this file should refer to contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive - deferredNodes?: Set; // Set of nodes whose checking has been deferred + deferredNodes?: ESMap; // Set of nodes whose checking has been deferred capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type isExhaustive?: boolean; // Is node an exhaustive switch statement From e23710aae5c011e6e35b3c0f89089399174e9170 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 9 Nov 2021 01:32:07 +0000 Subject: [PATCH 14/15] Redo checker changes, see if these are wins. --- src/compiler/checker.ts | 12 +++++------- src/compiler/types.ts | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f74d36a2392dd..4ce54bbcabec7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3887,10 +3887,9 @@ namespace ts { function getAlternativeContainingModules(symbol: Symbol, enclosingDeclaration: Node): Symbol[] { const containingFile = getSourceFileOfNode(enclosingDeclaration); - const id = getNodeId(containingFile); const links = getSymbolLinks(symbol); let results: Symbol[] | undefined; - if (links.extendedContainersByFile && (results = links.extendedContainersByFile.get(id))) { + if (links.extendedContainersByFile && (results = links.extendedContainersByFile.get(containingFile))) { return results; } if (containingFile && containingFile.imports) { @@ -3904,7 +3903,7 @@ namespace ts { results = append(results, resolvedModule); } if (length(results)) { - (links.extendedContainersByFile || (links.extendedContainersByFile = new Map())).set(id, results!); + (links.extendedContainersByFile ||= new Map()).set(containingFile, results!); return results!; } } @@ -33796,7 +33795,7 @@ namespace ts { const type = checkExpression(node); // If control flow analysis was required to determine the type, it is worth caching. if (flowInvocationCount !== startInvocationCount) { - const cache = flowTypeCache || (flowTypeCache = []); + const cache = (flowTypeCache ||= []); cache[getNodeId(node)] = type; setNodeFlags(node, node.flags | NodeFlags.TypeCached); } @@ -40318,9 +40317,8 @@ namespace ts { const enclosingFile = getSourceFileOfNode(node); const links = getNodeLinks(enclosingFile); if (!(links.flags & NodeCheckFlags.TypeChecked)) { - links.deferredNodes = links.deferredNodes || new Map(); - const id = getNodeId(node); - links.deferredNodes.set(id, node); + links.deferredNodes ||= new Set(); + links.deferredNodes.add(node); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 170c65b1271f0..951c3b8a012aa 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4946,7 +4946,7 @@ namespace ts { lateSymbol?: Symbol; // Late-bound symbol for a computed property specifierCache?: ESMap; // For symbols corresponding to external modules, a cache of incoming path -> module specifier name mappings extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in - extendedContainersByFile?: ESMap; // Containers (other than the parent) which this symbol is aliased in + extendedContainersByFile?: ESMap; // Containers (other than the parent) which this symbol is aliased in variances?: VarianceFlags[]; // Alias symbol type argument variance cache deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type deferralParent?: Type; // Source union/intersection of a deferred type @@ -5106,7 +5106,7 @@ namespace ts { jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node jsxImplicitImportContainer?: Symbol | false; // Resolved module symbol the implicit jsx import of this file should refer to contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive - deferredNodes?: ESMap; // Set of nodes whose checking has been deferred + deferredNodes?: Set; // Set of nodes whose checking has been deferred capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type isExhaustive?: boolean; // Is node an exhaustive switch statement From b7d31bcd69d88ffab78b82757d05b81451b44986 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 9 Nov 2021 02:23:49 +0000 Subject: [PATCH 15/15] Switch to sparse array for deferred nodes. --- src/compiler/checker.ts | 4 ++-- src/compiler/types.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4ce54bbcabec7..305852103def4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -40317,8 +40317,8 @@ namespace ts { const enclosingFile = getSourceFileOfNode(node); const links = getNodeLinks(enclosingFile); if (!(links.flags & NodeCheckFlags.TypeChecked)) { - links.deferredNodes ||= new Set(); - links.deferredNodes.add(node); + links.deferredNodes ||= []; + links.deferredNodes[getNodeId(node)] = node; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 951c3b8a012aa..6660f9849a92f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5106,7 +5106,7 @@ namespace ts { jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node jsxImplicitImportContainer?: Symbol | false; // Resolved module symbol the implicit jsx import of this file should refer to contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive - deferredNodes?: Set; // Set of nodes whose checking has been deferred + deferredNodes?: Node[]; // Sparse array of nodes whose checking has been deferred capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type isExhaustive?: boolean; // Is node an exhaustive switch statement