8000 Reduce polymorphism resulting from unstable Node shapes by rbuckton · Pull Request #51682 · microsoft/TypeScript · GitHub
[go: up one dir, main page]

Skip to content

Reduce polymorphism resulting from unstable Node shapes #51682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 13, 2022
Merged
Prev Previous commit
Next Next commit
Move 'locals' and 'nextContainer' out of Node
  • Loading branch information
rbuckton committed Dec 13, 2022
commit 4c94861b0786a2052a3577b3c42b630ec86e99a3
27 changes: 18 additions & 9 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
BreakOrContinueStatement,
CallChain,
CallExpression,
canHaveLocals,
canHaveSymbol,
CaseBlock,
CaseClause,
Expand Down Expand Up @@ -495,7 +496,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
let container: IsContainer | EntityNameExpression;
let thisParentContainer: IsContainer | EntityNameExpression; // Container one level up
let blockScopeContainer: IsBlockScopedContainer;
let lastContainer: IsBlockScopedContainer;
let lastContainer: HasLocals;
let delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag)[];
let seenThisKeyword: boolean;

Expand Down Expand Up @@ -858,6 +859,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes);
}
else {
Debug.assertNode(container, canHaveLocals);
return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
}
}
Expand All @@ -879,7 +881,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
// and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
if (isJSDocTypeAlias(node)) Debug.assert(isInJSFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file.
if (!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) {
if (!container.locals || (hasSyntacticModifier(node, ModifierFlags.Default) && !getDeclarationName(node))) {
if (!canHaveLocals(container) || !container.locals || (hasSyntacticModifier(node, ModifierFlags.Default) && !getDeclarationName(node))) {
return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); // No local symbol for an unnamed default!
}
const exportKind = symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0;
Expand All @@ -889,6 +891,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
return local;
}
else {
Debug.assertNode(container, canHaveLocals);
return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
}
}
Expand Down Expand Up @@ -946,13 +949,15 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
}
container = blockScopeContainer = node as IsContainer;
if (containerFlags & ContainerFlags.HasLocals) {
container.locals = createSymbolTable();
(container as HasLocals).locals = createSymbolTable();
addToContainerChain(container as HasLocals);
}
addToContainerChain(blockScopeContainer);
}
else if (containerFlags & ContainerFlags.IsBlockScopedContainer) {
blockScopeContainer = node as IsBlockScopedContainer;
blockScopeContainer.locals = undefined;
if (containerFlags & ContainerFlags.HasLocals) {
(blockScopeContainer as HasLocals).locals = undefined;
}
}
if (containerFlags & ContainerFlags.IsControlFlowContainer) {
const saveCurrentFlow = currentFlow;
Expand Down Expand Up @@ -2127,7 +2132,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
}
}

function addToContainerChain(next: IsBlockScopedContainer) {
function addToContainerChain(next: HasLocals) {
if (lastContainer) {
lastContainer.nextContainer = next;
}
Expand Down Expand Up @@ -2190,6 +2195,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
// their container in the tree). To accomplish this, we simply add their declared
// symbol to the 'locals' of the container. These symbols can then be found as
// the type checker walks up the containers, checking them for matching names.
if (container.locals) Debug.assertNode(container, canHaveLocals);
return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
}
}
Expand Down Expand Up @@ -2317,6 +2323,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
}
// falls through
default:
Debug.assertNode(blockScopeContainer, canHaveLocals);
if (!blockScopeContainer.locals) {
blockScopeContainer.locals = createSymbolTable();
addToContainerChain(blockScopeContainer);
Expand Down Expand Up @@ -3612,6 +3619,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
if (isJSDocTemplateTag(node.parent)) {
const container: HasLocals | undefined = getEffectiveContainerForJSDocTemplateTag(node.parent);
if (container) {
Debug.assertNode(container, canHaveLocals);
container.locals ??= createSymbolTable();
declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
}
Expand All @@ -3622,6 +3630,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
else if (node.parent.kind === SyntaxKind.InferType) {
const container: HasLocals | undefined = getInferTypeContainer(node.parent);
if (container) {
Debug.assertNode(container, canHaveLocals);
container.locals ??= createSymbolTable();
declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
}
Expand Down Expand Up @@ -3798,7 +3807,7 @@ function getContainerFlags(node: Node): ContainerFlags {
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
case SyntaxKind.CaseBlock:
return ContainerFlags.IsBlockScopedContainer;
return ContainerFlags.IsBlockScopedContainer | ContainerFlags.HasLocals;

case SyntaxKind.Block:
// do not treat blocks directly inside a function as a block-scoped-container.
Expand All @@ -3817,14 +3826,14 @@ functio D7AE n getContainerFlags(node: Node): ContainerFlags {
// By not creating a new block-scoped-container here, we ensure that both 'var x'
// and 'let x' go into the Function-container's locals, and we do get a collision
// conflict.
return isFunctionLike(node.parent) || isClassStaticBlockDeclaration(node.parent) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer;
return isFunctionLike(node.parent) || isClassStaticBlockDeclaration(node.parent) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer | ContainerFlags.HasLocals;
}

return ContainerFlags.None;
}

function lookupSymbolForName(container: Node, name: __String): Symbol | undefined {
const local = container.locals?.get(name);
const local = tryCast(container, canHaveLocals)?.locals?.get(name);
if (local) {
return local.exportSymbol ?? local;
}
Expand Down
12 changes: 7 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
canHaveIllegalDecorators,
canHaveIllegalModifiers,
canHaveJSDoc,
canHaveLocals,
canHaveModifiers,
canUsePropertyAccess,
canHaveSymbol,
Expand Down Expand Up @@ -359,6 +360,7 @@ import {
hasJSDocNodes,
hasJSDocParameterTags,
hasJsonModuleEmitEnabled,
HasLocals,
HasModifiers,
hasOnlyExpressionInitializer,
hasOverrideModifier,
Expand Down Expand Up @@ -2928,7 +2930,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return undefined;
}
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if (location.locals && !isGlobalSourceFile(location)) {
if (canHaveLocals(location) && location.locals && !isGlobalSourceFile(location)) {
if (result = lookup(location.locals, name, meaning)) {
let useResult = true;
if (isFunctionLike(location) && lastLocation && lastLocation !== (location as FunctionLikeDeclaration).body) {
Expand Down Expand Up @@ -4100,7 +4102,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function reportNonExportedMember(node: Node, name: Identifier, declarationName: string, moduleSymbol: Symbol, moduleName: string): void {
const localSymbol = moduleSymbol.valueDeclaration?.locals?.get(name.escapedText);
const localSymbol = tryCast(moduleSymbol.valueDeclaration, canHaveLocals)?.locals?.get(name.escapedText);
const exports = moduleSymbol.exports;
if (localSymbol) {
const exportedEqualsSymbol = exports?.get(InternalSymbolName.ExportEquals);
Expand Down Expand Up @@ -5497,7 +5499,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
let result: T;
for (let location = enclosingDeclaration; location; location = location.parent) {
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if (location.locals && !isGlobalSourceFile(location)) {
if (canHaveLocals(location) && location.locals && !isGlobalSourceFile(location)) {
if (result = callback(location.locals, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true, location)) {
return result;
}
Expand Down Expand Up @@ -39012,7 +39014,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
(isVariableDeclaration(declaration) && isForInOrOfStatement(declaration.parent.parent) || isImportedDeclaration(declaration)) && isIdentifierThatStartsWithUnderscore(declaration.name!);
}

function checkUnusedLocalsAndParameters(nodeWithLocals: Node, addDiagnostic: AddUnusedDiagnostic): void {
function checkUnusedLocalsAndParameters(nodeWithLocals: HasLocals, addDiagnostic: AddUnusedDiagnostic): void {
// Ideally we could use the ImportClause directly as a key, but must wait until we have full ES6 maps. So must store key along with value.
const unusedImports = new Map<string, [ImportClause, ImportedDeclaration[]]>();
const unusedDestructures = new Map<string, [BindingPattern, BindingElement[]]>();
Expand Down Expand Up @@ -43593,7 +43595,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {

function populateSymbols() {
while (location) {
if (location.locals && !isGlobalSourceFile(location)) {
if (canHaveLocals(location) && location.locals && !isGlobalSourceFile(location)) {
copySymbols(location.locals, meaning);
}

Expand Down
14 changes: 7 additions & 7 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
BundleFileTextLikeKind,
CallExpression,
CallSignatureDeclaration,
CaseBlock,
canHaveLocals, CaseBlock,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no lint rule for this, but these should be columns.

Hopefully one day we can get formatting automated...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One day...
futuristic city

🤣 (sorry, I couldn't resist)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no lint rule for this, but these should be columns.

Hopefully one day we can get formatting automated...

I've also noticed that Organize Imports seems to be sorting __String to the end rather than the start.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's: #51733

CaseClause,
CaseOrDefaultClause,
cast,
Expand Down Expand Up @@ -181,7 +181,7 @@ import {
getTransformers,
getTypeNode,
guessIndentation,
hasRecordedExternalHelpers,
HasLocals, hasRecordedExternalHelpers,
HeritageClause,
Identifier,
idText,
Expand Down Expand Up @@ -412,7 +412,7 @@ import {
tracing,
TransformationResult,
transformNodes,
tryParseRawSourceMap,
tryCast, tryParseRawSourceMap,
TryStatement,
TupleTypeNode,
TypeAliasDeclaration,
Expand Down Expand Up @@ -5655,9 +5655,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
/**
* Returns a value indicating whether a name is unique within a container.
*/
function isUniqueLocalName(name: string, container: Node): boolean {
for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer!) {
if (node.locals) {
function isUniqueLocalName(name: string, container: HasLocals | undefined): boolean {
for (let node = container; node && isNodeDescendantOf(node, container); node = node.nextContainer) {
if (canHaveLocals(node) && node.locals) {
const local = node.locals.get(escapeLeadingUnderscores(name));
// We conservatively include alias symbols to cover cases where they're emitted as locals
if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
Expand Down Expand Up @@ -5797,7 +5797,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) {
const name = getTextOfNode(node.name);
// Use module/enum name itself if it is unique, otherwise make a unique variation
return isUniqueLocalName(name, node) ? name : makeUniqueName(name, isUniqueName, /*optimistic*/ false, /*scoped*/ false, /*privateName*/ false, /*prefix*/ "", /*suffix*/ "");
return isUniqueLocalName(name, tryCast(node, canHaveLocals)) ? name : makeUniqueName(name, isUniqueName, /*optimistic*/ false, /*scoped*/ false, /*privateName*/ false, /*prefix*/ "", /*suffix*/ "");
}

/**
Expand Down
Loading
0