From 33a74101b8804c10f5a5b915e9598983fd0132ef Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:28:16 -0700 Subject: [PATCH] Split out the concerns of the binder even more. Don't use SymbolFlags to direct how we handle containers in the binder. Instead, Just determine what we should do based on the .kind of the node itself, and nothing more. --- src/compiler/binder.ts | 200 +++++++++++++++++++++++------------------ src/compiler/types.ts | 4 - 2 files changed, 111 insertions(+), 93 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e27851c7806..612b957cf4a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -52,6 +52,15 @@ module ts { } } + const enum ContainerFlags { + None = 0, + IsContainer = 0x01, + IsBlockScopedContainer = 0x02, + HasLocals = 0x04, + + IsContainerWithLocals = IsContainer | HasLocals + } + export function bindSourceFile(file: SourceFile): void { let start = new Date().getTime(); bindSourceFileWorker(file); @@ -67,8 +76,6 @@ module ts { let Symbol = objectAllocator.getSymbolConstructor(); if (!file.locals) { - file.locals = {}; - container = blockScopeContainer = file; bind(file); file.symbolCount = symbolCount; } @@ -82,9 +89,6 @@ module ts { symbol.flags |= symbolFlags; node.symbol = symbol; - if (symbolFlags & SymbolFlags.HasLocals) { - node.locals = {}; - } if (!symbol.declarations) { symbol.declarations = []; @@ -238,34 +242,29 @@ module ts { } } - // All container nodes are kept on a linked list in declaration order. This list is used by the getLocalNameOfContainer function - // in the type checker to validate that the local name used for a container is unique. - function bindChildren(node: Node, symbolFlags: SymbolFlags) { + // All container nodes are kept on a linked list in declaration order. This list is used by + // the getLocalNameOfContainer function in the type checker to validate that the local name + // used for a container is unique. + function bindChildren(node: Node): void { let saveParent = parent; let saveContainer = container; let savedBlockScopeContainer = blockScopeContainer; parent = node; - if (symbolFlags & SymbolFlags.IsContainer) { - container = node; - if (lastContainer) { - lastContainer.nextContainer = container; + let containerFlags = getContainerFlags(node); + if (containerFlags & ContainerFlags.IsContainer) { + container = blockScopeContainer = node; + + // If this is a container that also has locals, initialize them now. + if (containerFlags & ContainerFlags.HasLocals) { + container.locals = {}; } - lastContainer = container; + addContainerToEndOfChain(); } - - if (symbolFlags & SymbolFlags.IsBlockScopedContainer) { - // in incremental scenarios we might reuse nodes that already have locals being allocated - // during the bind step these locals should be dropped to prevent using stale data. - // locals should always be dropped unless they were previously initialized by the binder - // these cases are: - // - node has locals (symbolKind & HasLocals) !== 0 - // - node is a source file + else if (containerFlags & ContainerFlags.IsBlockScopedContainer) { blockScopeContainer = node; - if ((symbolFlags & SymbolFlags.HasLocals) === 0 && node.kind !== SyntaxKind.SourceFile) { - blockScopeContainer.locals = undefined; - } + blockScopeContainer.locals = undefined; } forEachChild(node, bind); @@ -275,9 +274,64 @@ module ts { blockScopeContainer = savedBlockScopeContainer; } - function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): SymbolFlags { + function getContainerFlags(node: Node): ContainerFlags { + switch (node.kind) { + case SyntaxKind.ClassExpression: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.TypeLiteral: + case SyntaxKind.ObjectLiteralExpression: + return ContainerFlags.IsContainer; + + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.IndexSignature: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.Constructor: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.SourceFile: + return ContainerFlags.IsContainerWithLocals; + + case SyntaxKind.CatchClause: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.CaseBlock: + return ContainerFlags.IsBlockScopedContainer; + + 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. + // Otherwise this won't be considered as redeclaration of a block scoped local: + // function foo(x) { + // let x; + // } + // 'x' will be placed into the function locals and 'let x' - into the locals of the block + return isFunctionLike(node.parent) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer; + } + + return ContainerFlags.None; + } + + function addContainerToEndOfChain() { + if (lastContainer) { + lastContainer.nextContainer = container; + } + + lastContainer = container; + } + + function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); - return symbolFlags; } function declareSymbolAndAddToSymbolTableWorker(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { @@ -381,7 +435,7 @@ module ts { } } - function bindModuleDeclaration(node: ModuleDeclaration) { + function bindModuleDeclaration(node: ModuleDeclaration): void { setExportContextFlag(node); if (node.name.kind === SyntaxKind.StringLiteral) { return declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); @@ -392,7 +446,7 @@ module ts { return declareSymbolAndAddToSymbolTable(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); } else { - let result = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); let currentModuleIsConstEnumOnly = state === ModuleInstanceState.ConstEnumOnly; if (node.symbol.constEnumOnlyModule === undefined) { @@ -403,13 +457,11 @@ module ts { // merged case: module is const enum only if all its pieces are non-instantiated or const enum node.symbol.constEnumOnlyModule = node.symbol.constEnumOnlyModule && currentModuleIsConstEnumOnly; } - - return result; } } } - function bindFunctionOrConstructorType(node: SignatureDeclaration): SymbolFlags { + function bindFunctionOrConstructorType(node: SignatureDeclaration): void { // For a given function symbol "<...>(...) => T" we want to generate a symbol identical // to the one we would get for: { <...>(...): T } // @@ -423,17 +475,14 @@ module ts { let typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); typeLiteralSymbol.members = { [name]: symbol }; - - return SymbolFlags.Signature; } - function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string): SymbolFlags { + function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string): void { let symbol = createSymbol(symbolFlags, name); addDeclarationToSymbol(symbol, node, symbolFlags); - return symbolFlags; } - function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): SymbolFlags { + function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { switch (blockScopeContainer.kind) { case SyntaxKind.ModuleDeclaration: declareModuleMember(node, symbolFlags, symbolExcludes); @@ -450,11 +499,9 @@ module ts { } declareSymbol(blockScopeContainer.locals, undefined, node, symbolFlags, symbolExcludes); } - - return symbolFlags; } - function bindBlockScopedVariableDeclaration(node: Declaration): SymbolFlags { + function bindBlockScopedVariableDeclaration(node: Declaration): void { return bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); } @@ -477,16 +524,16 @@ module ts { // // However, not all symbols will end up in any of these tables. 'Anonymous' symbols // (like TypeLiterals for example) will not be put in any table. - var symbolFlags = bindWorker(node); + 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); + bindChildren(node); } - function bindWorker(node: Node): SymbolFlags { + function bindWorker(node: Node): void { switch (node.kind) { case SyntaxKind.TypeParameter: return declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); @@ -557,34 +604,17 @@ module ts { return bindExportAssignment(node); case SyntaxKind.SourceFile: return bindSourceFileIfExternalModule(); - 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. - // Otherwise this won't be considered as redeclaration of a block scoped local: - // function foo(x) { - // let x; - // } - // 'x' will be placed into the function locals and 'let x' - into the locals of the block - return isFunctionLike(node.parent) ? SymbolFlags.None : SymbolFlags.BlockScopedContainer; - case SyntaxKind.CatchClause: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.CaseBlock: - return SymbolFlags.BlockScopedContainer; } - - return SymbolFlags.None; } - function bindSourceFileIfExternalModule() { + function bindSourceFileIfExternalModule(): void { setExportContextFlag(file); - return isExternalModule(file) - ? bindAnonymousDeclaration(file, SymbolFlags.ValueModule, '"' + removeFileExtension(file.fileName) + '"') - : SymbolFlags.BlockScopedContainer; + if (isExternalModule(file)) { + bindAnonymousDeclaration(file, SymbolFlags.ValueModule, '"' + removeFileExtension(file.fileName) + '"'); + } } - function bindExportAssignment(node: ExportAssignment) { + function bindExportAssignment(node: ExportAssignment): void { if (node.expression.kind === SyntaxKind.Identifier) { // An export default clause with an identifier exports all meanings of that identifier declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Alias, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); @@ -593,25 +623,22 @@ 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); } - - return SymbolFlags.None; } - function bindExportDeclaration(node: ExportDeclaration) { + function bindExportDeclaration(node: ExportDeclaration): void { if (!node.exportClause) { // All export * declarations are collected in an __export symbol declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, SymbolFlags.None); } - return SymbolFlags.None; } - function bindImportClause(node: ImportClause) { - return node.name - ? declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes) - : SymbolFlags.None; + function bindImportClause(node: ImportClause): void { + if (node.name) { + declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); + } } - function bindClassLikeDeclaration(node: ClassLikeDeclaration) { + function bindClassLikeDeclaration(node: ClassLikeDeclaration): void { if (node.kind === SyntaxKind.ClassDeclaration) { bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); } @@ -640,29 +667,26 @@ module ts { } symbol.exports[prototypeSymbol.name] = prototypeSymbol; prototypeSymbol.parent = symbol; - - return SymbolFlags.Class; } - function bindEnumDeclaration(node: EnumDeclaration) { + function bindEnumDeclaration(node: EnumDeclaration): void { return isConst(node) ? declareSymbolAndAddToSymbolTable(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) : declareSymbolAndAddToSymbolTable(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); } - function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { - if (isBindingPattern(node.name)) { - return SymbolFlags.None; - } - else if (isBlockOrCatchScoped(node)) { - return bindBlockScopedVariableDeclaration(node); - } - else { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); + function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement): void { + if (!isBindingPattern(node.name)) { + if (isBlockOrCatchScoped(node)) { + return bindBlockScopedVariableDeclaration(node); + } + else { + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); + } } } - function bindParameter(node: ParameterDeclaration): SymbolFlags { + function bindParameter(node: ParameterDeclaration): void { if (isBindingPattern(node.name)) { bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); } @@ -679,11 +703,9 @@ module ts { let classDeclaration = node.parent.parent; declareSymbol(classDeclaration.symbol.members, classDeclaration.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); } - - return SymbolFlags.FunctionScopedVariable; } - function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): SymbolFlags { + function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { return hasDynamicName(node) ? bindAnonymousDeclaration(node, symbolFlags, "__computed") : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 010b6308348..c55f016704d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1311,7 +1311,6 @@ module ts { UnionProperty = 0x10000000, // Property in union type Optional = 0x20000000, // Optional property ExportStar = 0x40000000, // Export * declaration - BlockScopedContainer = 0x80000000, Enum = RegularEnum | ConstEnum, Variable = FunctionScopedVariable | BlockScopedVariable, @@ -1350,12 +1349,9 @@ module ts { ExportHasLocal = Function | Class | Enum | ValueModule, - HasLocals = Function | Module | Method | Constructor | Accessor | Signature, HasExports = Class | Enum | Module, HasMembers = Class | Interface | TypeLiteral | ObjectLiteral, - IsBlockScopedContainer = BlockScopedContainer | Module | Function | Accessor | Method | Constructor, - IsContainer = HasLocals | HasExports | HasMembers, PropertyOrAccessor = Property | Accessor, Export = ExportNamespace | ExportType | ExportValue, }