From c5d920e912585084dbe04d7993890a43ed6a511d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 16:23:56 -0700 Subject: [PATCH] Simplify recursion in the binder. We now only recurse in a single place in the binder. The rest of the binding code is only concerned with how to bind a single node to a symbol and add that symbol to a symbol table. Recursion is handled as a separate concern, greatly simplifying binder flow. --- src/compiler/binder.ts | 193 +++++++++++++++++++++-------------------- 1 file changed, 100 insertions(+), 93 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f5364f71d13..cee2ff08a78 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -276,7 +276,7 @@ module ts { blockScopeContainer = savedBlockScopeContainer; } - function declareSymbolForDeclarationAndBindChildren(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { + function declareSymbolForDeclarationAndBindChildren(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): SymbolFlags { // First we declare a symbol for the provided node. The symbol will be added to an // appropriate symbol table. Possible symbol tables include: // @@ -288,7 +288,7 @@ module ts { // to bind. declareSymbolAndAddToAppropriateContainer(node, symbolFlags, symbolExcludes); - bindChildren(node, symbolFlags); + return symbolFlags; } function declareSymbolAndAddToAppropriateContainer(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { @@ -401,16 +401,25 @@ module ts { function bindModuleDeclaration(node: ModuleDeclaration) { setExportContextFlag(node); if (node.name.kind === SyntaxKind.StringLiteral) { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); } else { let state = getModuleInstanceState(node); if (state === ModuleInstanceState.NonInstantiated) { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); } else { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + } + } + } + + function postBindModuleDeclarationChildren(node: ModuleDeclaration) { + if (node.name.kind !== SyntaxKind.StringLiteral) { + let state = getModuleInstanceState(node); + if (state !== ModuleInstanceState.NonInstantiated) { let currentModuleIsConstEnumOnly = state === ModuleInstanceState.ConstEnumOnly; + if (node.symbol.constEnumOnlyModule === undefined) { // non-merged case - use the current state node.symbol.constEnumOnlyModule = currentModuleIsConstEnumOnly; @@ -423,7 +432,7 @@ module ts { } } - function bindFunctionOrConstructorType(node: SignatureDeclaration) { + function bindFunctionOrConstructorType(node: SignatureDeclaration): SymbolFlags { // For a given function symbol "<...>(...) => T" we want to generate a symbol identical // to the one we would get for: { <...>(...): T } // @@ -433,24 +442,24 @@ module ts { let name = getDeclarationName(node); let symbol = createSymbol(SymbolFlags.Signature, name); addDeclarationToSymbol(symbol, node, SymbolFlags.Signature); - bindChildren(node, SymbolFlags.Signature); + return SymbolFlags.Signature; + } + function postBindFunctionOrConstructorTypeChildren(node: SignatureDeclaration): void { + let symbol = node.symbol; + let name = symbol.name; let typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); typeLiteralSymbol.members = { [name]: symbol }; } - function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string) { + function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string): SymbolFlags { let symbol = createSymbol(symbolFlags, name); addDeclarationToSymbol(symbol, node, symbolFlags); - bindChildren(node, symbolFlags); + return symbolFlags; } - function bindCatchVariableDeclaration(node: CatchClause) { - bindChildren(node, SymbolFlags.BlockScopedContainer); - } - - function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { + function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): SymbolFlags { switch (blockScopeContainer.kind) { case SyntaxKind.ModuleDeclaration: declareModuleMember(node, symbolFlags, symbolExcludes); @@ -467,11 +476,12 @@ module ts { } declareSymbol(blockScopeContainer.locals, undefined, node, symbolFlags, symbolExcludes); } - bindChildren(node, symbolFlags); + + return symbolFlags; } - function bindBlockScopedVariableDeclaration(node: Declaration) { - bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); + function bindBlockScopedVariableDeclaration(node: Declaration): SymbolFlags { + return bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); } function getDestructuringParameterName(node: Declaration) { @@ -479,126 +489,112 @@ module ts { } function bind(node: Node): void { + // First we bind declaration nodes to a symbol if possible. We'll both create a symbol + // and add the symbol to an appropriate symbol table. The symbolFlags that are retuerned + // from this help inform how we recurse into the children of this node. + var symbolFlags = bindWorker(node); + + // Then we recurse into the children of the node to bind them as well. For certain + // symbols we do specialized work when we recurse. For example, we'll keep track of + // the current 'container' node when it changes. This helps us know which symbol table + // a local should go into for example. + bindChildren(node, symbolFlags); + + // Allow certain nodes to do specialized work after their children have been bound. + postBindChildren(node); + } + + function bindWorker(node: Node): SymbolFlags { node.parent = parent; switch (node.kind) { case SyntaxKind.TypeParameter: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); - break; + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); case SyntaxKind.Parameter: - bindParameter(node); - break; + return bindParameter(node); case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: if (isBindingPattern((node).name)) { - bindChildren(node, 0); + return SymbolFlags.None; } else if (isBlockOrCatchScoped(node)) { - bindBlockScopedVariableDeclaration(node); + return bindBlockScopedVariableDeclaration(node); } else { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); } - break; case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : 0), SymbolFlags.PropertyExcludes); - break; + return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : 0), SymbolFlags.PropertyExcludes); case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); - break; + return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); case SyntaxKind.EnumMember: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); - break; + return bindPropertyOrMethodOrAccessor(node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Signature, 0); - break; + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Signature, 0); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: // If this is an ObjectLiteralExpression method, then it sits in the same space // as other properties in the object literal. So we use SymbolFlags.PropertyExcludes // so that it will conflict with any other object literal members with the same // name. - bindPropertyOrMethodOrAccessor(node, SymbolFlags.Method | ((node).questionToken ? SymbolFlags.Optional : 0), + return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Method | ((node).questionToken ? SymbolFlags.Optional : 0), isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes); - break; case SyntaxKind.FunctionDeclaration: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); - break; + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); case SyntaxKind.Constructor: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Constructor, /*symbolExcludes:*/ 0); - break; + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Constructor, /*symbolExcludes:*/ 0); case SyntaxKind.GetAccessor: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes); - break; + return bindPropertyOrMethodOrAccessor(node, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes); case SyntaxKind.SetAccessor: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes); - break; - + return bindPropertyOrMethodOrAccessor(node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: - bindFunctionOrConstructorType(node); - break; - + return bindFunctionOrConstructorType(node); case SyntaxKind.TypeLiteral: - bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type"); - break; + return bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type"); case SyntaxKind.ObjectLiteralExpression: - bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object"); - break; + return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object"); case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - bindAnonymousDeclaration(node, SymbolFlags.Function, "__function"); - break; + return bindAnonymousDeclaration(node, SymbolFlags.Function, "__function"); case SyntaxKind.ClassExpression: - bindAnonymousDeclaration(node, SymbolFlags.Class, "__class"); - break; - case SyntaxKind.CatchClause: - bindCatchVariableDeclaration(node); - break; + return bindAnonymousDeclaration(node, SymbolFlags.Class, "__class"); case SyntaxKind.ClassDeclaration: - bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); - break; + return bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); case SyntaxKind.InterfaceDeclaration: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); - break; + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); case SyntaxKind.TypeAliasDeclaration: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); - break; + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); case SyntaxKind.EnumDeclaration: if (isConst(node)) { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes); + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes); } else { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); } - break; case SyntaxKind.ModuleDeclaration: - bindModuleDeclaration(node); - break; + return bindModuleDeclaration(node); case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); - break; + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); case SyntaxKind.ImportClause: if ((node).name) { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); } else { - bindChildren(node, 0); + return SymbolFlags.None; } - break; case SyntaxKind.ExportDeclaration: if (!(node).exportClause) { // All export * declarations are collected in an __export symbol declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, 0); } - bindChildren(node, 0); - break; + return SymbolFlags.None; case SyntaxKind.ExportAssignment: if ((node).expression.kind === SyntaxKind.Identifier) { // An export default clause with an identifier exports all meanings of that identifier @@ -608,14 +604,14 @@ module ts { // An export default clause with an expression exports a value declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); } - bindChildren(node, 0); - break; + return SymbolFlags.None; case SyntaxKind.SourceFile: setExportContextFlag(node); if (isExternalModule(node)) { - bindAnonymousDeclaration(node, SymbolFlags.ValueModule, '"' + removeFileExtension((node).fileName) + '"'); - break; + return bindAnonymousDeclaration(node, SymbolFlags.ValueModule, '"' + removeFileExtension((node).fileName) + '"'); } + // fall through. + case SyntaxKind.Block: // do not treat function block a block-scope container // all block-scope locals that reside in this block should go to the function locals. @@ -625,29 +621,40 @@ module ts { // let x; // } // 'let x' will be placed into the function locals and 'let x' - into the locals of the block - bindChildren(node, isFunctionLike(node.parent) ? 0 : SymbolFlags.BlockScopedContainer); - break; + return isFunctionLike(node.parent) ? 0 : SymbolFlags.BlockScopedContainer; case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: case SyntaxKind.CaseBlock: - bindChildren(node, SymbolFlags.BlockScopedContainer); - break; - default: - bindChildren(node, 0); - break; + return SymbolFlags.BlockScopedContainer; + } + + return SymbolFlags.None; + } + + function postBindChildren(node: Node) { + switch (node.kind) { + case SyntaxKind.Parameter: + return postParameterChildren(node); + case SyntaxKind.ModuleDeclaration: + return postBindModuleDeclarationChildren(node); + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + return postBindFunctionOrConstructorTypeChildren(node); } } - function bindParameter(node: ParameterDeclaration) { + function bindParameter(node: ParameterDeclaration): SymbolFlags { if (isBindingPattern(node.name)) { - bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); + return bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); } else { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); } + } + function postParameterChildren(node: ParameterDeclaration): void { // If this is a property-parameter, then also declare the property symbol into the // containing class. if (node.flags & NodeFlags.AccessibilityModifier && @@ -659,12 +666,12 @@ module ts { } } - function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { + function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): SymbolFlags { if (hasDynamicName(node)) { - bindAnonymousDeclaration(node, symbolFlags, "__computed"); + return bindAnonymousDeclaration(node, symbolFlags, "__computed"); } else { - declareSymbolForDeclarationAndBindChildren(node, symbolFlags, symbolExcludes); + return declareSymbolForDeclarationAndBindChildren(node, symbolFlags, symbolExcludes); } } }