From e5910af2c835ffbc8b67469ad1fff6bbec4e112b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 14:15:49 -0700 Subject: [PATCH 01/38] Always recurse into children in the binder in a uniform manner. --- src/compiler/binder.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 9d6d9fab35b..1682cbdcd44 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -575,10 +575,8 @@ module ts { bindChildren(node, 0, /*isBlockScopeContainer*/ true); break; default: - let saveParent = parent; - parent = node; - forEachChild(node, bind); - parent = saveParent; + bindChildren(node, 0, /*isBlockScopeContainer*/ false); + break; } } From 14e925beb980790b271aec0ee8d6b54da4cfa669 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 14:21:52 -0700 Subject: [PATCH 02/38] ConstructorType's name should be __call not __constructor. --- src/compiler/binder.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1682cbdcd44..4bc79e35c33 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -111,12 +111,12 @@ module ts { return (node.name).text; } switch (node.kind) { - case SyntaxKind.ConstructorType: case SyntaxKind.Constructor: return "__constructor"; case SyntaxKind.FunctionType: case SyntaxKind.CallSignature: return "__call"; + case SyntaxKind.ConstructorType: case SyntaxKind.ConstructSignature: return "__new"; case SyntaxKind.IndexSignature: @@ -368,15 +368,14 @@ module ts { // We do that by making an anonymous type literal symbol, and then setting the function // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable // from an actual type literal symbol you would have gotten had you used the long form. - - let symbol = createSymbol(SymbolFlags.Signature, getDeclarationName(node)); + let name = getDeclarationName(node); + let symbol = createSymbol(SymbolFlags.Signature, name); addDeclarationToSymbol(symbol, node, SymbolFlags.Signature); bindChildren(node, SymbolFlags.Signature, /*isBlockScopeContainer:*/ false); let typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); - typeLiteralSymbol.members = {}; - typeLiteralSymbol.members[node.kind === SyntaxKind.FunctionType ? "__call" : "__new"] = symbol + typeLiteralSymbol.members = { [name]: symbol }; } function bindAnonymousDeclaration(node: Declaration, symbolKind: SymbolFlags, name: string, isBlockScopeContainer: boolean) { From 6478155aac000a15c95d6e0bb60bad1d72dd6626 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 14:30:35 -0700 Subject: [PATCH 03/38] Rename locals to more clearly indicate they are flags and not kinds. --- src/compiler/binder.ts | 98 ++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4bc79e35c33..b11a7b5b8c0 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -86,14 +86,27 @@ module ts { } } - function addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolKind: SymbolFlags) { - symbol.flags |= symbolKind; - if (!symbol.declarations) symbol.declarations = []; - symbol.declarations.push(node); - if (symbolKind & SymbolFlags.HasExports && !symbol.exports) symbol.exports = {}; - if (symbolKind & SymbolFlags.HasMembers && !symbol.members) symbol.members = {}; + function addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolFlags: SymbolFlags) { + symbol.flags |= symbolFlags; + node.symbol = symbol; - if (symbolKind & SymbolFlags.Value && !symbol.valueDeclaration) symbol.valueDeclaration = node; + + if (!symbol.declarations) { + symbol.declarations = []; + } + symbol.declarations.push(node); + + if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) { + symbol.exports = {}; + } + + if (symbolFlags & SymbolFlags.HasMembers && !symbol.members) { + symbol.members = {}; + } + + if (symbolFlags & SymbolFlags.Value && !symbol.valueDeclaration) { + symbol.valueDeclaration = node; + } } // Should not be called on a declaration with a computed property name, @@ -189,14 +202,14 @@ module ts { return symbol; } - function declareModuleMember(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags) { + function declareModuleMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { let hasExportModifier = getCombinedNodeFlags(node) & NodeFlags.Export; - if (symbolKind & SymbolFlags.Alias) { + if (symbolFlags & SymbolFlags.Alias) { if (node.kind === SyntaxKind.ExportSpecifier || (node.kind === SyntaxKind.ImportEqualsDeclaration && hasExportModifier)) { - declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); + declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); } else { - declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes); + declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); } } else { @@ -212,23 +225,23 @@ module ts { // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. if (hasExportModifier || container.flags & NodeFlags.ExportContext) { - let exportKind = (symbolKind & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) | - (symbolKind & SymbolFlags.Type ? SymbolFlags.ExportType : 0) | - (symbolKind & SymbolFlags.Namespace ? SymbolFlags.ExportNamespace : 0); + let exportKind = (symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) | + (symbolFlags & SymbolFlags.Type ? SymbolFlags.ExportType : 0) | + (symbolFlags & SymbolFlags.Namespace ? SymbolFlags.ExportNamespace : 0); let local = declareSymbol(container.locals, undefined, node, exportKind, symbolExcludes); - local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); + local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); node.localSymbol = local; } else { - declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes); + declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); } } } // 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, symbolKind: SymbolFlags, isBlockScopeContainer: boolean) { - if (symbolKind & SymbolFlags.HasLocals) { + function bindChildren(node: Node, symbolFlags: SymbolFlags, isBlockScopeContainer: boolean) { + if (symbolFlags & SymbolFlags.HasLocals) { node.locals = {}; } @@ -236,7 +249,7 @@ module ts { let saveContainer = container; let savedBlockScopeContainer = blockScopeContainer; parent = node; - if (symbolKind & SymbolFlags.IsContainer) { + if (symbolFlags & SymbolFlags.IsContainer) { container = node; if (lastContainer) { @@ -253,7 +266,7 @@ module ts { // these cases are: // - node has locals (symbolKind & HasLocals) !== 0 // - node is a source file - setBlockScopeContainer(node, /*cleanLocals*/ (symbolKind & SymbolFlags.HasLocals) === 0 && node.kind !== SyntaxKind.SourceFile); + setBlockScopeContainer(node, /*cleanLocals*/ (symbolFlags & SymbolFlags.HasLocals) === 0 && node.kind !== SyntaxKind.SourceFile); } forEachChild(node, bind); @@ -262,14 +275,14 @@ module ts { blockScopeContainer = savedBlockScopeContainer; } - function bindDeclaration(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean) { + function bindDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean) { switch (container.kind) { case SyntaxKind.ModuleDeclaration: - declareModuleMember(node, symbolKind, symbolExcludes); + declareModuleMember(node, symbolFlags, symbolExcludes); break; case SyntaxKind.SourceFile: if (isExternalModule(container)) { - declareModuleMember(node, symbolKind, symbolExcludes); + declareModuleMember(node, symbolFlags, symbolExcludes); break; } case SyntaxKind.FunctionType: @@ -285,29 +298,32 @@ module ts { case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes); + declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); break; case SyntaxKind.ClassExpression: case SyntaxKind.ClassDeclaration: if (node.flags & NodeFlags.Static) { - declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); + declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); break; } case SyntaxKind.TypeLiteral: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.InterfaceDeclaration: - declareSymbol(container.symbol.members, container.symbol, node, symbolKind, symbolExcludes); + declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); break; case SyntaxKind.EnumDeclaration: - declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); + declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); break; } - bindChildren(node, symbolKind, isBlockScopeContainer); + bindChildren(node, symbolFlags, isBlockScopeContainer); } function isAmbientContext(node: Node): boolean { while (node) { - if (node.flags & NodeFlags.Ambient) return true; + if (node.flags & NodeFlags.Ambient) { + return true; + } + node = node.parent; } return false; @@ -378,24 +394,24 @@ module ts { typeLiteralSymbol.members = { [name]: symbol }; } - function bindAnonymousDeclaration(node: Declaration, symbolKind: SymbolFlags, name: string, isBlockScopeContainer: boolean) { - let symbol = createSymbol(symbolKind, name); - addDeclarationToSymbol(symbol, node, symbolKind); - bindChildren(node, symbolKind, isBlockScopeContainer); + function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string, isBlockScopeContainer: boolean) { + let symbol = createSymbol(symbolFlags, name); + addDeclarationToSymbol(symbol, node, symbolFlags); + bindChildren(node, symbolFlags, isBlockScopeContainer); } function bindCatchVariableDeclaration(node: CatchClause) { bindChildren(node, /*symbolKind:*/ 0, /*isBlockScopeContainer:*/ true); } - function bindBlockScopedDeclaration(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags) { + function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { switch (blockScopeContainer.kind) { case SyntaxKind.ModuleDeclaration: - declareModuleMember(node, symbolKind, symbolExcludes); + declareModuleMember(node, symbolFlags, symbolExcludes); break; case SyntaxKind.SourceFile: if (isExternalModule(container)) { - declareModuleMember(node, symbolKind, symbolExcludes); + declareModuleMember(node, symbolFlags, symbolExcludes); break; } // fall through. @@ -403,9 +419,9 @@ module ts { if (!blockScopeContainer.locals) { blockScopeContainer.locals = {}; } - declareSymbol(blockScopeContainer.locals, undefined, node, symbolKind, symbolExcludes); + declareSymbol(blockScopeContainer.locals, undefined, node, symbolFlags, symbolExcludes); } - bindChildren(node, symbolKind, /*isBlockScopeContainer*/ false); + bindChildren(node, symbolFlags, /*isBlockScopeContainer*/ false); } function bindBlockScopedVariableDeclaration(node: Declaration) { @@ -598,12 +614,12 @@ module ts { } } - function bindPropertyOrMethodOrAccessor(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean) { + function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean) { if (hasDynamicName(node)) { - bindAnonymousDeclaration(node, symbolKind, "__computed", isBlockScopeContainer); + bindAnonymousDeclaration(node, symbolFlags, "__computed", isBlockScopeContainer); } else { - bindDeclaration(node, symbolKind, symbolExcludes, isBlockScopeContainer); + bindDeclaration(node, symbolFlags, symbolExcludes, isBlockScopeContainer); } } } From 71d0f7affe4d8c5ec9b1841fca9a403ffe14b8a2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 14:46:01 -0700 Subject: [PATCH 04/38] Simplify concerns in the binder. --- src/compiler/binder.ts | 89 ++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b11a7b5b8c0..af7226eee2d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -202,14 +202,14 @@ module ts { return symbol; } - function declareModuleMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { + function declareModuleMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { let hasExportModifier = getCombinedNodeFlags(node) & NodeFlags.Export; if (symbolFlags & SymbolFlags.Alias) { if (node.kind === SyntaxKind.ExportSpecifier || (node.kind === SyntaxKind.ImportEqualsDeclaration && hasExportModifier)) { - declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); + return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); } else { - declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); + return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); } } else { @@ -231,9 +231,10 @@ module ts { let local = declareSymbol(container.locals, undefined, node, exportKind, symbolExcludes); local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); node.localSymbol = local; + return local; } else { - declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); + return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); } } } @@ -275,16 +276,17 @@ module ts { blockScopeContainer = savedBlockScopeContainer; } - function bindDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean) { + function declareSymbolForDeclarationAndBindChildren(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean): void { + declareSymbolAndAddToAppropriateContainer(node, symbolFlags, symbolExcludes, isBlockScopeContainer); + bindChildren(node, symbolFlags, isBlockScopeContainer); + } + + function declareSymbolAndAddToAppropriateContainer(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean): Symbol { switch (container.kind) { case SyntaxKind.ModuleDeclaration: - declareModuleMember(node, symbolFlags, symbolExcludes); - break; + return declareModuleMember(node, symbolFlags, symbolExcludes); case SyntaxKind.SourceFile: - if (isExternalModule(container)) { - declareModuleMember(node, symbolFlags, symbolExcludes); - break; - } + return declareSourceFileMember(container, node, symbolFlags, symbolExcludes); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: @@ -298,24 +300,35 @@ module ts { case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); - break; + return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); case SyntaxKind.ClassExpression: case SyntaxKind.ClassDeclaration: - if (node.flags & NodeFlags.Static) { - declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); - break; - } + return declareClassMember(node, symbolFlags, symbolExcludes); case SyntaxKind.TypeLiteral: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.InterfaceDeclaration: - declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); - break; + return declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); case SyntaxKind.EnumDeclaration: - declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); - break; + return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); + } + } + + function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { + if (node.flags & NodeFlags.Static) { + return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); + } + else { + return declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); + } + } + + function declareSourceFileMember(container: SourceFile, node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { + if (isExternalModule(container)) { + return declareModuleMember(node, symbolFlags, symbolExcludes); + } + else { + return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); } - bindChildren(node, symbolFlags, isBlockScopeContainer); } function isAmbientContext(node: Node): boolean { @@ -355,15 +368,15 @@ module ts { function bindModuleDeclaration(node: ModuleDeclaration) { setExportContextFlag(node); if (node.name.kind === SyntaxKind.StringLiteral) { - bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); } else { let state = getModuleInstanceState(node); if (state === ModuleInstanceState.NonInstantiated) { - bindDeclaration(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes, /*isBlockScopeContainer*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes, /*isBlockScopeContainer*/ true); } else { - bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); let currentModuleIsConstEnumOnly = state === ModuleInstanceState.ConstEnumOnly; if (node.symbol.constEnumOnlyModule === undefined) { // non-merged case - use the current state @@ -437,7 +450,7 @@ module ts { switch (node.kind) { case SyntaxKind.TypeParameter: - bindDeclaration(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.Parameter: bindParameter(node); @@ -451,7 +464,7 @@ module ts { bindBlockScopedVariableDeclaration(node); } else { - bindDeclaration(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes, /*isBlockScopeContainer*/ false); } break; case SyntaxKind.PropertyDeclaration: @@ -468,7 +481,7 @@ module ts { case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - bindDeclaration(node, SymbolFlags.Signature, 0, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Signature, 0, /*isBlockScopeContainer*/ false); break; case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: @@ -480,10 +493,10 @@ module ts { isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes, /*isBlockScopeContainer*/ true); break; case SyntaxKind.FunctionDeclaration: - bindDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes, /*isBlockScopeContainer*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes, /*isBlockScopeContainer*/ true); break; case SyntaxKind.Constructor: - bindDeclaration(node, SymbolFlags.Constructor, /*symbolExcludes:*/ 0, /*isBlockScopeContainer:*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Constructor, /*symbolExcludes:*/ 0, /*isBlockScopeContainer:*/ true); break; case SyntaxKind.GetAccessor: bindPropertyOrMethodOrAccessor(node, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes, /*isBlockScopeContainer*/ true); @@ -517,17 +530,17 @@ module ts { bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); break; case SyntaxKind.InterfaceDeclaration: - bindDeclaration(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.TypeAliasDeclaration: - bindDeclaration(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.EnumDeclaration: if (isConst(node)) { - bindDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes, /*isBlockScopeContainer*/ false); } else { - bindDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes, /*isBlockScopeContainer*/ false); } break; case SyntaxKind.ModuleDeclaration: @@ -537,11 +550,11 @@ module ts { case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - bindDeclaration(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.ImportClause: if ((node).name) { - bindDeclaration(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes, /*isBlockScopeContainer*/ false); } else { bindChildren(node, 0, /*isBlockScopeContainer*/ false); @@ -600,7 +613,7 @@ module ts { bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node), /*isBlockScopeContainer*/ false); } else { - bindDeclaration(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes, /*isBlockScopeContainer*/ false); } // If this is a property-parameter, then also declare the property symbol into the @@ -619,7 +632,7 @@ module ts { bindAnonymousDeclaration(node, symbolFlags, "__computed", isBlockScopeContainer); } else { - bindDeclaration(node, symbolFlags, symbolExcludes, isBlockScopeContainer); + declareSymbolForDeclarationAndBindChildren(node, symbolFlags, symbolExcludes, isBlockScopeContainer); } } } From 9e64b4500118a662c9087f42997d9a1e242d81f0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 14:57:35 -0700 Subject: [PATCH 05/38] Add explanatory comments to the binder. --- src/compiler/binder.ts | 59 ++++++++++++++++++++++++++++++++---------- src/compiler/types.ts | 9 ++++--- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index af7226eee2d..0beed9b1152 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -277,16 +277,51 @@ module ts { } function declareSymbolForDeclarationAndBindChildren(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean): void { + // First we declare a symbol for the provided node. The symbol will be added to an + // appropriate symbol table. Possible symbol tables include: + // + // 1) The 'exports' table of the current container's symbol. + // 2) The 'members' table of the current container's symbol. + // 3) The 'locals' table of the current container. + // + // Then, we recurse down the children of this declaration, seeking more declarations + // to bind. + declareSymbolAndAddToAppropriateContainer(node, symbolFlags, symbolExcludes, isBlockScopeContainer); bindChildren(node, symbolFlags, isBlockScopeContainer); } function declareSymbolAndAddToAppropriateContainer(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean): Symbol { switch (container.kind) { + // Modules, source files, and classes need specialized handling for how their + // members are declared (for example, a member of a class will go into a specific + // symbol table depending on if it is static or not). As such, we defer to + // specialized handlers to take care of declaring these child members. case SyntaxKind.ModuleDeclaration: return declareModuleMember(node, symbolFlags, symbolExcludes); + case SyntaxKind.SourceFile: - return declareSourceFileMember(container, node, symbolFlags, symbolExcludes); + return declareSourceFileMember(node, symbolFlags, symbolExcludes); + + case SyntaxKind.ClassExpression: + case SyntaxKind.ClassDeclaration: + return declareClassMember(node, symbolFlags, symbolExcludes); + + case SyntaxKind.EnumDeclaration: + // Enum members are always put int the 'exports' of the containing enum. + // They are only accessibly through their container, and are never in + // scope otherwise (even inside the body of the enum declaring them.). + return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); + + case SyntaxKind.TypeLiteral: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.InterfaceDeclaration: + // Interface/Object-types always have their children added to the 'members' of + // their container. They are only accessible through an instance of their + // container, and are never in scope otherwise (even inside the body of the + // object / type / interface declaring them). + return declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); + case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: @@ -300,16 +335,14 @@ module ts { case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: + // All the children of these container types are never visible through another + // symbol (i.e. through another symbol's 'exports' or 'members'). Instead, + // more or less, they're only accessed 'lexically' (i.e. from code that exists + // underneath 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. return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); - case SyntaxKind.ClassExpression: - case SyntaxKind.ClassDeclaration: - return declareClassMember(node, symbolFlags, symbolExcludes); - case SyntaxKind.TypeLiteral: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.InterfaceDeclaration: - return declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); - case SyntaxKind.EnumDeclaration: - return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); } } @@ -322,12 +355,12 @@ module ts { } } - function declareSourceFileMember(container: SourceFile, node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { - if (isExternalModule(container)) { + function declareSourceFileMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { + if (isExternalModule(file)) { return declareModuleMember(node, symbolFlags, symbolExcludes); } else { - return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); + return declareSymbol(file.locals, undefined, node, symbolFlags, symbolExcludes); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 078f3d060a6..6b1191f4a5e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1360,14 +1360,15 @@ module ts { export interface Symbol { flags: SymbolFlags; // Symbol flags name: string; // Name of symbol - /* @internal */ id?: number; // Unique id (used to look up SymbolLinks) - /* @internal */ mergeId?: number; // Merge id (used to look up merged symbol) declarations?: Declaration[]; // Declarations associated with this symbol - /* @internal */ parent?: Symbol; // Parent symbol + valueDeclaration?: Declaration; // First value declaration of the symbol + members?: SymbolTable; // Class, interface or literal instance members exports?: SymbolTable; // Module exports + /* @internal */ id?: number; // 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 - valueDeclaration?: Declaration; // First value declaration of the symbol /* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums } From e7ddba508ab30e4961467cdeb1e2c9e06e877e2c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 15:50:02 -0700 Subject: [PATCH 06/38] Merge block container logic in the binder to use the same mechanism as SymbolFlags --- src/compiler/binder.ts | 96 +++++++++++++++++++++--------------------- src/compiler/types.ts | 2 + 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 0beed9b1152..9670b44d80e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -241,7 +241,7 @@ 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, isBlockScopeContainer: boolean) { + function bindChildren(node: Node, symbolFlags: SymbolFlags) { if (symbolFlags & SymbolFlags.HasLocals) { node.locals = {}; } @@ -260,7 +260,7 @@ module ts { lastContainer = container; } - if (isBlockScopeContainer) { + 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 @@ -276,7 +276,7 @@ module ts { blockScopeContainer = savedBlockScopeContainer; } - function declareSymbolForDeclarationAndBindChildren(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean): void { + function declareSymbolForDeclarationAndBindChildren(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { // First we declare a symbol for the provided node. The symbol will be added to an // appropriate symbol table. Possible symbol tables include: // @@ -287,11 +287,11 @@ module ts { // Then, we recurse down the children of this declaration, seeking more declarations // to bind. - declareSymbolAndAddToAppropriateContainer(node, symbolFlags, symbolExcludes, isBlockScopeContainer); - bindChildren(node, symbolFlags, isBlockScopeContainer); + declareSymbolAndAddToAppropriateContainer(node, symbolFlags, symbolExcludes); + bindChildren(node, symbolFlags); } - function declareSymbolAndAddToAppropriateContainer(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean): Symbol { + function declareSymbolAndAddToAppropriateContainer(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { switch (container.kind) { // Modules, source files, and classes need specialized handling for how their // members are declared (for example, a member of a class will go into a specific @@ -401,15 +401,15 @@ module ts { function bindModuleDeclaration(node: ModuleDeclaration) { setExportContextFlag(node); if (node.name.kind === SyntaxKind.StringLiteral) { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); } else { let state = getModuleInstanceState(node); if (state === ModuleInstanceState.NonInstantiated) { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes, /*isBlockScopeContainer*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); } else { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); let currentModuleIsConstEnumOnly = state === ModuleInstanceState.ConstEnumOnly; if (node.symbol.constEnumOnlyModule === undefined) { // non-merged case - use the current state @@ -433,21 +433,21 @@ module ts { let name = getDeclarationName(node); let symbol = createSymbol(SymbolFlags.Signature, name); addDeclarationToSymbol(symbol, node, SymbolFlags.Signature); - bindChildren(node, SymbolFlags.Signature, /*isBlockScopeContainer:*/ false); + bindChildren(node, SymbolFlags.Signature); let typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); typeLiteralSymbol.members = { [name]: symbol }; } - function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string, isBlockScopeContainer: boolean) { + function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string) { let symbol = createSymbol(symbolFlags, name); addDeclarationToSymbol(symbol, node, symbolFlags); - bindChildren(node, symbolFlags, isBlockScopeContainer); + bindChildren(node, symbolFlags); } function bindCatchVariableDeclaration(node: CatchClause) { - bindChildren(node, /*symbolKind:*/ 0, /*isBlockScopeContainer:*/ true); + bindChildren(node, SymbolFlags.BlockScopedContainer); } function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { @@ -467,7 +467,7 @@ module ts { } declareSymbol(blockScopeContainer.locals, undefined, node, symbolFlags, symbolExcludes); } - bindChildren(node, symbolFlags, /*isBlockScopeContainer*/ false); + bindChildren(node, symbolFlags); } function bindBlockScopedVariableDeclaration(node: Declaration) { @@ -483,7 +483,7 @@ module ts { switch (node.kind) { case SyntaxKind.TypeParameter: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); break; case SyntaxKind.Parameter: bindParameter(node); @@ -491,30 +491,30 @@ module ts { case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: if (isBindingPattern((node).name)) { - bindChildren(node, 0, /*isBlockScopeContainer*/ false); + bindChildren(node, 0); } else if (isBlockOrCatchScoped(node)) { bindBlockScopedVariableDeclaration(node); } else { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); } break; case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : 0), SymbolFlags.PropertyExcludes, /*isBlockScopeContainer*/ false); + bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : 0), SymbolFlags.PropertyExcludes); break; case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes, /*isBlockScopeContainer*/ false); + bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); break; case SyntaxKind.EnumMember: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes, /*isBlockScopeContainer*/ false); + bindPropertyOrMethodOrAccessor(node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); break; case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Signature, 0, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Signature, 0); break; case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: @@ -523,19 +523,19 @@ module ts { // so that it will conflict with any other object literal members with the same // name. bindPropertyOrMethodOrAccessor(node, SymbolFlags.Method | ((node).questionToken ? SymbolFlags.Optional : 0), - isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes, /*isBlockScopeContainer*/ true); + isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes); break; case SyntaxKind.FunctionDeclaration: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes, /*isBlockScopeContainer*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); break; case SyntaxKind.Constructor: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Constructor, /*symbolExcludes:*/ 0, /*isBlockScopeContainer:*/ true); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Constructor, /*symbolExcludes:*/ 0); break; case SyntaxKind.GetAccessor: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes, /*isBlockScopeContainer*/ true); + bindPropertyOrMethodOrAccessor(node, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes); break; case SyntaxKind.SetAccessor: - bindPropertyOrMethodOrAccessor(node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes, /*isBlockScopeContainer*/ true); + bindPropertyOrMethodOrAccessor(node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes); break; case SyntaxKind.FunctionType: @@ -544,17 +544,17 @@ module ts { break; case SyntaxKind.TypeLiteral: - bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type", /*isBlockScopeContainer*/ false); + bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type"); break; case SyntaxKind.ObjectLiteralExpression: - bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object", /*isBlockScopeContainer*/ false); + bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object"); break; case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - bindAnonymousDeclaration(node, SymbolFlags.Function, "__function", /*isBlockScopeContainer*/ true); + bindAnonymousDeclaration(node, SymbolFlags.Function, "__function"); break; case SyntaxKind.ClassExpression: - bindAnonymousDeclaration(node, SymbolFlags.Class, "__class", /*isBlockScopeContainer*/ false); + bindAnonymousDeclaration(node, SymbolFlags.Class, "__class"); break; case SyntaxKind.CatchClause: bindCatchVariableDeclaration(node); @@ -563,17 +563,17 @@ module ts { bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); break; case SyntaxKind.InterfaceDeclaration: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); break; case SyntaxKind.TypeAliasDeclaration: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); break; case SyntaxKind.EnumDeclaration: if (isConst(node)) { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes); } else { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); } break; case SyntaxKind.ModuleDeclaration: @@ -583,14 +583,14 @@ module ts { case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); break; case SyntaxKind.ImportClause: if ((node).name) { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); } else { - bindChildren(node, 0, /*isBlockScopeContainer*/ false); + bindChildren(node, 0); } break; case SyntaxKind.ExportDeclaration: @@ -598,7 +598,7 @@ module ts { // All export * declarations are collected in an __export symbol declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, 0); } - bindChildren(node, 0, /*isBlockScopeContainer*/ false); + bindChildren(node, 0); break; case SyntaxKind.ExportAssignment: if ((node).expression.kind === SyntaxKind.Identifier) { @@ -609,12 +609,12 @@ 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, /*isBlockScopeContainer*/ false); + bindChildren(node, 0); break; case SyntaxKind.SourceFile: setExportContextFlag(node); if (isExternalModule(node)) { - bindAnonymousDeclaration(node, SymbolFlags.ValueModule, '"' + removeFileExtension((node).fileName) + '"', /*isBlockScopeContainer*/ true); + bindAnonymousDeclaration(node, SymbolFlags.ValueModule, '"' + removeFileExtension((node).fileName) + '"'); break; } case SyntaxKind.Block: @@ -626,27 +626,27 @@ module ts { // let x; // } // 'let x' will be placed into the function locals and 'let x' - into the locals of the block - bindChildren(node, 0, /*isBlockScopeContainer*/ !isFunctionLike(node.parent)); + bindChildren(node, isFunctionLike(node.parent) ? 0 : SymbolFlags.BlockScopedContainer); break; case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: case SyntaxKind.CaseBlock: - bindChildren(node, 0, /*isBlockScopeContainer*/ true); + bindChildren(node, SymbolFlags.BlockScopedContainer); break; default: - bindChildren(node, 0, /*isBlockScopeContainer*/ false); + bindChildren(node, 0); break; } } function bindParameter(node: ParameterDeclaration) { if (isBindingPattern(node.name)) { - bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node), /*isBlockScopeContainer*/ false); + bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); } else { - declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes, /*isBlockScopeContainer*/ false); + declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); } // If this is a property-parameter, then also declare the property symbol into the @@ -660,12 +660,12 @@ module ts { } } - function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean) { + function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { if (hasDynamicName(node)) { - bindAnonymousDeclaration(node, symbolFlags, "__computed", isBlockScopeContainer); + bindAnonymousDeclaration(node, symbolFlags, "__computed"); } else { - declareSymbolForDeclarationAndBindChildren(node, symbolFlags, symbolExcludes, isBlockScopeContainer); + declareSymbolForDeclarationAndBindChildren(node, symbolFlags, symbolExcludes); } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6b1191f4a5e..5b5150ab132 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1310,6 +1310,7 @@ module ts { UnionProperty = 0x10000000, // Property in union type Optional = 0x20000000, // Optional property ExportStar = 0x40000000, // Export * declaration + BlockScopedContainer = 0x80000000, Enum = RegularEnum | ConstEnum, Variable = FunctionScopedVariable | BlockScopedVariable, @@ -1352,6 +1353,7 @@ module ts { 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, From b75fda1052f637853802962fa8793c2fe1b2d4ad Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 15:55:21 -0700 Subject: [PATCH 07/38] Explicitly type 'bind' as being a void function. --- src/compiler/binder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 9670b44d80e..1665da1431f 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -478,7 +478,7 @@ module ts { return "__" + indexOf((node.parent).parameters, node); } - function bind(node: Node) { + function bind(node: Node): void { node.parent = parent; switch (node.kind) { From 9043121188f6d6544bdb599bb2660d8da9231a89 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 16:02:07 -0700 Subject: [PATCH 08/38] Add a 'None' member to SymbolFlags enum. --- src/compiler/binder.ts | 1 - src/compiler/types.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1665da1431f..f5364f71d13 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -480,7 +480,6 @@ module ts { function bind(node: Node): void { node.parent = parent; - switch (node.kind) { case SyntaxKind.TypeParameter: declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5b5150ab132..010b6308348 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1279,6 +1279,7 @@ module ts { } export const enum SymbolFlags { + None = 0, FunctionScopedVariable = 0x00000001, // Variable (var) or parameter BlockScopedVariable = 0x00000002, // A block-scoped variable (let or const) Property = 0x00000004, // Property or enum member From c5d920e912585084dbe04d7993890a43ed6a511d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 16:23:56 -0700 Subject: [PATCH 09/38] 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); } } } From 02640a397f8af4b7d33e9cd2b6e4c62a6dd85d34 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 16:37:24 -0700 Subject: [PATCH 10/38] Extract any complicated code in top level bind function to individual helpers. --- src/compiler/binder.ts | 102 ++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index cee2ff08a78..fdff9d2e7ea 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -513,15 +513,7 @@ module ts { return bindParameter(node); case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: - if (isBindingPattern((node).name)) { - return SymbolFlags.None; - } - else if (isBlockOrCatchScoped(node)) { - return bindBlockScopedVariableDeclaration(node); - } - else { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); - } + return bindVariableDeclarationOrBindingElement(node); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : 0), SymbolFlags.PropertyExcludes); @@ -569,12 +561,7 @@ module ts { case SyntaxKind.TypeAliasDeclaration: return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); case SyntaxKind.EnumDeclaration: - if (isConst(node)) { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes); - } - else { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); - } + return bindEnumDeclaration(node); case SyntaxKind.ModuleDeclaration: return bindModuleDeclaration(node); case SyntaxKind.ImportEqualsDeclaration: @@ -583,35 +570,13 @@ module ts { case SyntaxKind.ExportSpecifier: return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); case SyntaxKind.ImportClause: - if ((node).name) { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); - } - else { - return SymbolFlags.None; - } + return bindImportClause(node); 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); - } - return SymbolFlags.None; + return bindExportDeclaration(node); case SyntaxKind.ExportAssignment: - 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); - } - else { - // 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; + return bindExportAssignment(node); case SyntaxKind.SourceFile: - setExportContextFlag(node); - if (isExternalModule(node)) { - return bindAnonymousDeclaration(node, SymbolFlags.ValueModule, '"' + removeFileExtension((node).fileName) + '"'); - } - // fall through. - + 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. @@ -645,6 +610,61 @@ module ts { } } + function bindSourceFileIfExternalModule() { + setExportContextFlag(file); + return isExternalModule(file) + ? bindAnonymousDeclaration(file, SymbolFlags.ValueModule, '"' + removeFileExtension(file.fileName) + '"') + : SymbolFlags.BlockScopedContainer; + } + + function bindExportAssignment(node: ExportAssignment) { + 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); + } + else { + // 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) { + if (!node.exportClause) { + // All export * declarations are collected in an __export symbol + declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, 0); + } + return SymbolFlags.None; + } + + function bindImportClause(node: ImportClause) { + if (node.name) { + return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); + } + else { + return SymbolFlags.None; + } + } + + function bindEnumDeclaration(node: EnumDeclaration) { + return isConst(node) + ? declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) + : declareSymbolForDeclarationAndBindChildren(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 declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); + } + } + function bindParameter(node: ParameterDeclaration): SymbolFlags { if (isBindingPattern(node.name)) { return bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); From fb925ee4d136792fc1ed4283b5840187cdc43759 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 16:39:09 -0700 Subject: [PATCH 11/38] Rename methods. --- src/compiler/binder.ts | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index fdff9d2e7ea..b994f3ea7bc 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): SymbolFlags { + function declareSymbolAndAddToSymbolTable(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: // @@ -287,11 +287,11 @@ module ts { // Then, we recurse down the children of this declaration, seeking more declarations // to bind. - declareSymbolAndAddToAppropriateContainer(node, symbolFlags, symbolExcludes); + declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); return symbolFlags; } - function declareSymbolAndAddToAppropriateContainer(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { + function declareSymbolAndAddToSymbolTableWorker(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { switch (container.kind) { // Modules, source files, and classes need specialized handling for how their // members are declared (for example, a member of a class will go into a specific @@ -401,15 +401,15 @@ module ts { function bindModuleDeclaration(node: ModuleDeclaration) { setExportContextFlag(node); if (node.name.kind === SyntaxKind.StringLiteral) { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); } else { let state = getModuleInstanceState(node); if (state === ModuleInstanceState.NonInstantiated) { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); } else { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); } } } @@ -508,7 +508,7 @@ module ts { node.parent = parent; switch (node.kind) { case SyntaxKind.TypeParameter: - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); case SyntaxKind.Parameter: return bindParameter(node); case SyntaxKind.VariableDeclaration: @@ -525,7 +525,7 @@ module ts { case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Signature, 0); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Signature, 0); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: // If this is an ObjectLiteralExpression method, then it sits in the same space @@ -535,9 +535,9 @@ module ts { return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Method | ((node).questionToken ? SymbolFlags.Optional : 0), isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes); case SyntaxKind.FunctionDeclaration: - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); case SyntaxKind.Constructor: - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Constructor, /*symbolExcludes:*/ 0); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Constructor, /*symbolExcludes:*/ 0); case SyntaxKind.GetAccessor: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes); case SyntaxKind.SetAccessor: @@ -557,9 +557,9 @@ module ts { case SyntaxKind.ClassDeclaration: return bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); case SyntaxKind.InterfaceDeclaration: - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); case SyntaxKind.TypeAliasDeclaration: - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); case SyntaxKind.EnumDeclaration: return bindEnumDeclaration(node); case SyntaxKind.ModuleDeclaration: @@ -568,7 +568,7 @@ module ts { case SyntaxKind.NamespaceImport: case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); case SyntaxKind.ImportClause: return bindImportClause(node); case SyntaxKind.ExportDeclaration: @@ -640,7 +640,7 @@ module ts { function bindImportClause(node: ImportClause) { if (node.name) { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); } else { return SymbolFlags.None; @@ -649,8 +649,8 @@ module ts { function bindEnumDeclaration(node: EnumDeclaration) { return isConst(node) - ? declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) - : declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); + ? declareSymbolAndAddToSymbolTable(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) + : declareSymbolAndAddToSymbolTable(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); } function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { @@ -661,7 +661,7 @@ module ts { return bindBlockScopedVariableDeclaration(node); } else { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); } } @@ -670,7 +670,7 @@ module ts { return bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); } else { - return declareSymbolForDeclarationAndBindChildren(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); } } @@ -691,7 +691,7 @@ module ts { return bindAnonymousDeclaration(node, symbolFlags, "__computed"); } else { - return declareSymbolForDeclarationAndBindChildren(node, symbolFlags, symbolExcludes); + return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); } } } From eb29eb9acd5074c1e8ca698e4d34013492a988c4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 16:43:42 -0700 Subject: [PATCH 12/38] Remove code to post bind parameters. --- src/compiler/binder.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b994f3ea7bc..3d9204c81a9 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -600,8 +600,6 @@ module ts { function postBindChildren(node: Node) { switch (node.kind) { - case SyntaxKind.Parameter: - return postParameterChildren(node); case SyntaxKind.ModuleDeclaration: return postBindModuleDeclarationChildren(node); case SyntaxKind.FunctionType: @@ -667,14 +665,12 @@ module ts { function bindParameter(node: ParameterDeclaration): SymbolFlags { if (isBindingPattern(node.name)) { - return bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); + bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); } else { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); + declareSymbolAndAddToSymbolTable(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 && @@ -684,6 +680,8 @@ 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 { From fab6fca5b4af8685ace4496a02db8eb312662605 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 16:48:28 -0700 Subject: [PATCH 13/38] Remove post bind step for modules. --- src/compiler/binder.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3d9204c81a9..d214cba6140 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -409,17 +409,9 @@ module ts { return declareSymbolAndAddToSymbolTable(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); } else { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); - } - } - } + let result = declareSymbolAndAddToSymbolTable(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; @@ -428,6 +420,8 @@ 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; } } } @@ -600,8 +594,6 @@ module ts { function postBindChildren(node: Node) { switch (node.kind) { - case SyntaxKind.ModuleDeclaration: - return postBindModuleDeclarationChildren(node); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: return postBindFunctionOrConstructorTypeChildren(node); From ea7bafa9fb0e4a95e879fc5ca024726d3aedae06 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 16:54:09 -0700 Subject: [PATCH 14/38] Remove unncessary postbind for function/constructor types. --- src/compiler/binder.ts | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d214cba6140..381e2750fd5 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -436,15 +436,12 @@ module ts { let name = getDeclarationName(node); let symbol = createSymbol(SymbolFlags.Signature, name); addDeclarationToSymbol(symbol, 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 }; + + return SymbolFlags.Signature; } function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string): SymbolFlags { @@ -483,6 +480,8 @@ module ts { } function bind(node: Node): void { + node.parent = parent; + // 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. @@ -493,13 +492,9 @@ module ts { // 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: return declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); @@ -592,14 +587,6 @@ module ts { return SymbolFlags.None; } - function postBindChildren(node: Node) { - switch (node.kind) { - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - return postBindFunctionOrConstructorTypeChildren(node); - } - } - function bindSourceFileIfExternalModule() { setExportContextFlag(file); return isExternalModule(file) From db128252b0b59ca0fe0ef8fa437a64e35439f077 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 16:57:44 -0700 Subject: [PATCH 15/38] Simplify code in the binder. --- src/compiler/binder.ts | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 381e2750fd5..56022bc61d4 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -347,21 +347,15 @@ module ts { } function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { - if (node.flags & NodeFlags.Static) { - return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); - } - else { - return declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); - } + return node.flags & NodeFlags.Static + ? declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes) + : declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); } function declareSourceFileMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { - if (isExternalModule(file)) { - return declareModuleMember(node, symbolFlags, symbolExcludes); - } - else { - return declareSymbol(file.locals, undefined, node, symbolFlags, symbolExcludes); - } + return isExternalModule(file) + ? declareModuleMember(node, symbolFlags, symbolExcludes) + : declareSymbol(file.locals, undefined, node, symbolFlags, symbolExcludes); } function isAmbientContext(node: Node): boolean { @@ -616,12 +610,9 @@ module ts { } function bindImportClause(node: ImportClause) { - if (node.name) { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); - } - else { - return SymbolFlags.None; - } + return node.name + ? declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes) + : SymbolFlags.None; } function bindEnumDeclaration(node: EnumDeclaration) { @@ -664,12 +655,9 @@ module ts { } function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): SymbolFlags { - if (hasDynamicName(node)) { - return bindAnonymousDeclaration(node, symbolFlags, "__computed"); - } - else { - return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); - } + return hasDynamicName(node) + ? bindAnonymousDeclaration(node, symbolFlags, "__computed") + : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); } } } From 941128ba79483a73a3ba3800f1201c6d89a3e962 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 17:02:49 -0700 Subject: [PATCH 16/38] Clean up comments. --- src/compiler/binder.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 56022bc61d4..4fca9a65508 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -277,16 +277,6 @@ module ts { } function declareSymbolAndAddToSymbolTable(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: - // - // 1) The 'exports' table of the current container's symbol. - // 2) The 'members' table of the current container's symbol. - // 3) The 'locals' table of the current container. - // - // Then, we recurse down the children of this declaration, seeking more declarations - // to bind. - declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); return symbolFlags; } @@ -477,8 +467,17 @@ module ts { node.parent = parent; // 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. + // and add the symbol to an appropriate symbol table (if appropriate). The symbolFlags + // that are returned from this help inform how we recurse into the children of this node. + // + // Possible destination symbol tables are: + // + // 1) The 'exports' table of the current container's symbol. + // 2) The 'members' table of the current container's symbol. + // 3) The 'locals' table of the current container. + // + // 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); // Then we recurse into the children of the node to bind them as well. For certain From 44559954772fe222c4025644a9e1488b48c33e4f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 17:09:17 -0700 Subject: [PATCH 17/38] Move all symbol table initialization into the same method. --- src/compiler/binder.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4fca9a65508..5d449b8b4b1 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -90,6 +90,9 @@ module ts { symbol.flags |= symbolFlags; node.symbol = symbol; + if (symbolFlags & SymbolFlags.HasLocals) { + node.locals = {}; + } if (!symbol.declarations) { symbol.declarations = []; @@ -242,10 +245,6 @@ 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) { - if (symbolFlags & SymbolFlags.HasLocals) { - node.locals = {}; - } - let saveParent = parent; let saveContainer = container; let savedBlockScopeContainer = blockScopeContainer; @@ -271,6 +270,7 @@ module ts { } forEachChild(node, bind); + container = saveContainer; parent = saveParent; blockScopeContainer = savedBlockScopeContainer; From 221262314c8e1c6d0ae425018e99175ad7c583b8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 17:14:31 -0700 Subject: [PATCH 18/38] Inline binder method. --- src/compiler/binder.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5d449b8b4b1..fa9cc95128e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -68,8 +68,7 @@ module ts { if (!file.locals) { file.locals = {}; - container = file; - setBlockScopeContainer(file, /*cleanLocals*/ false); + container = blockScopeContainer = file; bind(file); file.symbolCount = symbolCount; } @@ -79,13 +78,6 @@ module ts { return new Symbol(flags, name); } - function setBlockScopeContainer(node: Node, cleanLocals: boolean) { - blockScopeContainer = node; - if (cleanLocals) { - blockScopeContainer.locals = undefined; - } - } - function addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolFlags: SymbolFlags) { symbol.flags |= symbolFlags; @@ -266,7 +258,10 @@ module ts { // these cases are: // - node has locals (symbolKind & HasLocals) !== 0 // - node is a source file - setBlockScopeContainer(node, /*cleanLocals*/ (symbolFlags & SymbolFlags.HasLocals) === 0 && node.kind !== SyntaxKind.SourceFile); + blockScopeContainer = node; + if ((symbolFlags & SymbolFlags.HasLocals) === 0 && node.kind !== SyntaxKind.SourceFile) { + blockScopeContainer.locals = undefined; + } } forEachChild(node, bind); From 4f8d68bb23a44f6f8603e8e64de009f83a5bbf92 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 17:31:52 -0700 Subject: [PATCH 19/38] Use SymbolFlags.None in the binder. --- src/compiler/binder.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index fa9cc95128e..7abf9d27cc5 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -151,7 +151,7 @@ module ts { let symbol: Symbol; if (name !== undefined) { - symbol = hasProperty(symbols, name) ? symbols[name] : (symbols[name] = createSymbol(0, name)); + symbol = hasProperty(symbols, name) ? symbols[name] : (symbols[name] = createSymbol(SymbolFlags.None, name)); if (symbol.flags & excludes) { if (node.name) { node.name.parent = node; @@ -168,11 +168,11 @@ module ts { }); file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node))); - symbol = createSymbol(0, name); + symbol = createSymbol(SymbolFlags.None, name); } } else { - symbol = createSymbol(0, "__missing"); + symbol = createSymbol(SymbolFlags.None, "__missing"); } addDeclarationToSymbol(symbol, node, includes); symbol.parent = parent; @@ -493,7 +493,7 @@ module ts { return bindVariableDeclarationOrBindingElement(node); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: - return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : 0), SymbolFlags.PropertyExcludes); + return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes); case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); @@ -502,19 +502,19 @@ module ts { case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Signature, 0); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Signature, SymbolFlags.None); 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. - return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Method | ((node).questionToken ? SymbolFlags.Optional : 0), + return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Method | ((node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes); case SyntaxKind.FunctionDeclaration: return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); case SyntaxKind.Constructor: - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Constructor, /*symbolExcludes:*/ 0); + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Constructor, /*symbolExcludes:*/ SymbolFlags.None); case SyntaxKind.GetAccessor: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes); case SyntaxKind.SetAccessor: @@ -563,7 +563,7 @@ module ts { // let x; // } // 'let x' will be placed into the function locals and 'let x' - into the locals of the block - return isFunctionLike(node.parent) ? 0 : SymbolFlags.BlockScopedContainer; + return isFunctionLike(node.parent) ? SymbolFlags.None : SymbolFlags.BlockScopedContainer; case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: @@ -598,7 +598,7 @@ module ts { function bindExportDeclaration(node: ExportDeclaration) { if (!node.exportClause) { // All export * declarations are collected in an __export symbol - declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, 0); + declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, SymbolFlags.None); } return SymbolFlags.None; } From 38bae987284d4561299ac5074947094a80c40dd1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 23:06:42 -0700 Subject: [PATCH 20/38] CR feedback. --- src/compiler/binder.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 7abf9d27cc5..4f65c81bd53 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -293,8 +293,8 @@ module ts { return declareClassMember(node, symbolFlags, symbolExcludes); case SyntaxKind.EnumDeclaration: - // Enum members are always put int the 'exports' of the containing enum. - // They are only accessibly through their container, and are never in + // Enum members are always put in the 'exports' of the containing enum. + // They are only accessible through their container, and are never in // scope otherwise (even inside the body of the enum declaring them.). return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); @@ -462,7 +462,7 @@ module ts { node.parent = parent; // 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 (if appropriate). The symbolFlags + // and then potentially add the symbol to an appropriate symbol table. The symbolFlags // that are returned from this help inform how we recurse into the children of this node. // // Possible destination symbol tables are: From e9a73e0cf866f36db72478d275018d7755b4a34f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 23:20:04 -0700 Subject: [PATCH 21/38] Move code for creating a prototype out of the common declareSymbol codepath. --- src/compiler/binder.ts | 50 ++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4f65c81bd53..a4bf77b22c1 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -174,26 +174,10 @@ module ts { else { symbol = createSymbol(SymbolFlags.None, "__missing"); } + addDeclarationToSymbol(symbol, node, includes); symbol.parent = parent; - if ((node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && symbol.exports) { - // TypeScript 1.0 spec (April 2014): 8.4 - // Every class automatically contains a static property member named 'prototype', - // the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter. - // It is an error to explicitly declare a static property member with the name 'prototype'. - let prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype"); - if (hasProperty(symbol.exports, prototypeSymbol.name)) { - if (node.name) { - node.name.parent = node; - } - file.bindDiagnostics.push(createDiagnosticForNode(symbol.exports[prototypeSymbol.name].declarations[0], - Diagnostics.Duplicate_identifier_0, prototypeSymbol.name)); - } - symbol.exports[prototypeSymbol.name] = prototypeSymbol; - prototypeSymbol.parent = symbol; - } - return symbol; } @@ -530,9 +514,8 @@ module ts { case SyntaxKind.ArrowFunction: return bindAnonymousDeclaration(node, SymbolFlags.Function, "__function"); case SyntaxKind.ClassExpression: - return bindAnonymousDeclaration(node, SymbolFlags.Class, "__class"); case SyntaxKind.ClassDeclaration: - return bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); + return bindClassLikeDeclaration(node); case SyntaxKind.InterfaceDeclaration: return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); case SyntaxKind.TypeAliasDeclaration: @@ -609,6 +592,35 @@ module ts { : SymbolFlags.None; } + function bindClassLikeDeclaration(node: ClassLikeDeclaration) { + if (node.kind === SyntaxKind.ClassDeclaration) { + bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); + } + else { + bindAnonymousDeclaration(node, SymbolFlags.Class, "__class"); + } + + let symbol = node.symbol; + if (symbol.exports) { + // TypeScript 1.0 spec (April 2014): 8.4 + // Every class automatically contains a static property member named 'prototype', + // the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter. + // It is an error to explicitly declare a static property member with the name 'prototype'. + let prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype"); + if (hasProperty(symbol.exports, prototypeSymbol.name)) { + if (node.name) { + node.name.parent = node; + } + file.bindDiagnostics.push(createDiagnosticForNode(symbol.exports[prototypeSymbol.name].declarations[0], + Diagnostics.Duplicate_identifier_0, prototypeSymbol.name)); + } + symbol.exports[prototypeSymbol.name] = prototypeSymbol; + prototypeSymbol.parent = symbol; + } + + return SymbolFlags.Class; + } + function bindEnumDeclaration(node: EnumDeclaration) { return isConst(node) ? declareSymbolAndAddToSymbolTable(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) From ee6c7dc0e480f149b967f0432896807466ab0f11 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 23:23:44 -0700 Subject: [PATCH 22/38] Remove unnecessary check. Classes always have exports. --- src/compiler/binder.ts | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index a4bf77b22c1..2100936c775 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -601,22 +601,21 @@ module ts { } let symbol = node.symbol; - if (symbol.exports) { - // TypeScript 1.0 spec (April 2014): 8.4 - // Every class automatically contains a static property member named 'prototype', - // the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter. - // It is an error to explicitly declare a static property member with the name 'prototype'. - let prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype"); - if (hasProperty(symbol.exports, prototypeSymbol.name)) { - if (node.name) { - node.name.parent = node; - } - file.bindDiagnostics.push(createDiagnosticForNode(symbol.exports[prototypeSymbol.name].declarations[0], - Diagnostics.Duplicate_identifier_0, prototypeSymbol.name)); + + // TypeScript 1.0 spec (April 2014): 8.4 + // Every class automatically contains a static property member named 'prototype', + // the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter. + // It is an error to explicitly declare a static property member with the name 'prototype'. + let prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype"); + if (hasProperty(symbol.exports, prototypeSymbol.name)) { + if (node.name) { + node.name.parent = node; } - symbol.exports[prototypeSymbol.name] = prototypeSymbol; - prototypeSymbol.parent = symbol; + file.bindDiagnostics.push(createDiagnosticForNode(symbol.exports[prototypeSymbol.name].declarations[0], + Diagnostics.Duplicate_identifier_0, prototypeSymbol.name)); } + symbol.exports[prototypeSymbol.name] = prototypeSymbol; + prototypeSymbol.parent = symbol; return SymbolFlags.Class; } From aaf9371357faa9b38c2adcfef8dd372d22f537d2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 19 Apr 2015 23:30:06 -0700 Subject: [PATCH 23/38] Add clarifying comments to binder. --- src/compiler/binder.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 2100936c775..6f8140a983a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -603,9 +603,14 @@ module ts { let symbol = node.symbol; // TypeScript 1.0 spec (April 2014): 8.4 - // Every class automatically contains a static property member named 'prototype', - // the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter. - // It is an error to explicitly declare a static property member with the name 'prototype'. + // Every class automatically contains a static property member named 'prototype', the + // type of which is an instantiation of the class type with type Any supplied as a type + // argument for each type parameter. It is an error to explicitly declare a static + // property member with the name 'prototype'. + // + // Note: we check for this here because this class may be merging into a module. The + // module might have an exported variable called 'prototype'. We can't allow that as + // that would clash with the built-in 'prototype' for the class. let prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype"); if (hasProperty(symbol.exports, prototypeSymbol.name)) { if (node.name) { From 2e8e4a1f5c22cc5514a0a2137217b6037cde4779 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 02:24:47 -0700 Subject: [PATCH 24/38] Add clarifying comments to the binder. --- src/compiler/binder.ts | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6f8140a983a..e27851c7806 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -143,7 +143,7 @@ module ts { return node.name ? declarationNameToString(node.name) : getDeclarationName(node); } - function declareSymbol(symbols: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { + function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); // The exported symbol for an export default function/class node is always named "default" @@ -151,7 +151,27 @@ module ts { let symbol: Symbol; if (name !== undefined) { - symbol = hasProperty(symbols, name) ? symbols[name] : (symbols[name] = createSymbol(SymbolFlags.None, name)); + // Check and see if the symbol table already has a symbol with this name. If not, + // create a new symbol with this name and add it to the table. Note that we don't + // give the new symbol any flags *yet*. This ensures that it will not conflict + // witht he 'excludes' flags we pass in. + // + // If we do get an existing symbol, see if it conflicts with the new symbol we're + // creating. For example, a 'var' symbol and a 'class' symbol will conflict within + // the same symbol table. If we have a conflict, report the issue on each + // declaration we have for this symbol, and then create a new symbol for this + // declaration. + // + // If we created a new symbol, either because we didn't have a symbol with this name + // in the symbol table, or we conflicted with an existing symbol, then just add this + // node as the sole declaration of the new symbol. + // + // Otherwise, we'll be merging into a compatible existing symbol (for example when + // you have multiple 'vars' with the same name in the same container). In this case + // just add this node into the declarations list of the symbol. + symbol = hasProperty(symbolTable, name) + ? symbolTable[name] + : (symbolTable[name] = createSymbol(SymbolFlags.None, name)); if (symbol.flags & excludes) { if (node.name) { node.name.parent = node; @@ -160,9 +180,8 @@ module ts { // Report errors every position with duplicate declaration // Report errors on previous encountered declarations let message = symbol.flags & SymbolFlags.BlockScopedVariable - ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 + ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0; - forEach(symbol.declarations, declaration => { file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration))); }); @@ -204,7 +223,8 @@ module ts { // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. if (hasExportModifier || container.flags & NodeFlags.ExportContext) { - let exportKind = (symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) | + let exportKind = + (symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) | (symbolFlags & SymbolFlags.Type ? SymbolFlags.ExportType : 0) | (symbolFlags & SymbolFlags.Namespace ? SymbolFlags.ExportNamespace : 0); let local = declareSymbol(container.locals, undefined, node, exportKind, symbolExcludes); @@ -541,11 +561,10 @@ module ts { // 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() { - // let x; + // function foo(x) { // let x; // } - // 'let x' will be placed into the function locals and 'let x' - into the locals of the block + // '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: From 33a74101b8804c10f5a5b915e9598983fd0132ef Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:28:16 -0700 Subject: [PATCH 25/38] 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, } From 602c5c8fa93d08d6fe8957bdc104e9296aa0c651 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:33:13 -0700 Subject: [PATCH 26/38] Add explanatory comments. --- src/compiler/binder.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 612b957cf4a..dbe705fbe35 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -246,16 +246,37 @@ module ts { // the getLocalNameOfContainer function in the type checker to validate that the local name // used for a container is unique. function bindChildren(node: Node): void { + // Before we recurse into a node's chilren, we first save the existing parent, container + // and block-container. Then after we pop out of processing the children, we restore + // these saved values. let saveParent = parent; let saveContainer = container; let savedBlockScopeContainer = blockScopeContainer; - parent = node; + // This node will now be set as the parent of all of its children as we recurse into them. + parent = node; + + // Depending on what kind of node this is, we may have to adjust the current container + // and block-container. If the current node is a container, then it is automatically + // considered the current block-container as well. Also, for containers that we know + // may contain locals, we proactively initialize the .locals field. We do this because + // it's highly likely that the .locals will be need to place some child in (for example, + // a parameter, or variable declaration). + // + // However, we do not proactively create the locals for block-containers because it's + // totally normal and common for block-containers to never actually have a block-scoped + // variable in them. We don't want to end up allocating an object for every 'block' we + // run into when most of them won't be necessary. + // + // Finally, if this is a block-container, then we clear out any existing locals object + // it may contain within it. This happens in incremental scenarios. Because we can be + // reusing a node from a previous compilation, that node may have had 'locals' created + // for it. We must clear this so we don't accidently move any stale data forward from + // a previous compilation. 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 = {}; } From 60d0b1444d16c18befe2acf3192121f926eb176e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:34:19 -0700 Subject: [PATCH 27/38] CR feedback. --- src/compiler/binder.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index dbe705fbe35..0ffd60b81c7 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -372,9 +372,6 @@ module ts { return declareClassMember(node, symbolFlags, symbolExcludes); case SyntaxKind.EnumDeclaration: - // Enum members are always put in the 'exports' of the containing enum. - // They are only accessible through their container, and are never in - // scope otherwise (even inside the body of the enum declaring them.). return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); case SyntaxKind.TypeLiteral: @@ -401,11 +398,10 @@ module ts { case SyntaxKind.ArrowFunction: // All the children of these container types are never visible through another // symbol (i.e. through another symbol's 'exports' or 'members'). Instead, - // more or less, they're only accessed 'lexically' (i.e. from code that exists - // underneath 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. + // they're only accessed 'lexically' (i.e. from code that exists underneath + // 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. return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); } } From d220b7ebb4827d76ec3c4f79a19760a0bac77c82 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:43:54 -0700 Subject: [PATCH 28/38] Add explanatory comments. --- src/compiler/binder.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 0ffd60b81c7..eeee76c3761 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -53,11 +53,27 @@ module ts { } const enum ContainerFlags { - None = 0, - IsContainer = 0x01, - IsBlockScopedContainer = 0x02, + // The current node is not a container, and no container manipulation should happen before + // recursing into it. + None = 0, + + // The current node is a container. It should be set as the current container (and block- + // container) before recursing into it. The current node does not have locals. Examples: + // + // Classes, ObjectLiterals, TypeLiterals, Interfaces... + IsContainer = 0x01, + + // The current node is a block-scoped-container. It should be set as the current block- + // container before recursing into it. Examples: + // + // Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements... + IsBlockScopedContainer = 0x02, + HasLocals = 0x04, + // If the current node is a container that also container that also contains locals. Examples: + // + // Functions, Methods, Modules, Source-files. IsContainerWithLocals = IsContainer | HasLocals } From 0a144e1806087bcd2ffbd81f6d44a5345bfc85a6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:44:33 -0700 Subject: [PATCH 29/38] Add explicit return to indicate the end of flow of a method.' --- src/compiler/binder.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index eeee76c3761..e20b7aca0e8 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -96,6 +96,8 @@ module ts { file.symbolCount = symbolCount; } + return; + function createSymbol(flags: SymbolFlags, name: string): Symbol { symbolCount++; return new Symbol(flags, name); From b1417d408dce2cd820ec937953b61298d0fcb481 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:47:42 -0700 Subject: [PATCH 30/38] Clean up comment. --- src/compiler/binder.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e20b7aca0e8..b4a993a805e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -322,7 +322,7 @@ module ts { case SyntaxKind.TypeLiteral: case SyntaxKind.ObjectLiteralExpression: return ContainerFlags.IsContainer; - + case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: @@ -348,13 +348,19 @@ module ts { 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 + // 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 + // wouldn't be considered as redeclaration of a block scoped local: + // + // function foo() { + // var x; + // let x; + // } + // + // If we placed 'var x' into the function locals and 'let x' - into the locals of + // the block, then there would be no collision. By doing this, 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) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer; } From b81a2c00ba4c3d5604c1305f1f129ca37a7eec7c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:52:46 -0700 Subject: [PATCH 31/38] Clean up comment. --- src/compiler/binder.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b4a993a805e..ad5ca83585e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -348,19 +348,19 @@ module ts { return ContainerFlags.IsBlockScopedContainer; case SyntaxKind.Block: - // do not treat function block a block-scope container. All block-scope locals + // do not treat blocks directly inside a function as a block-scoped-container. // that reside in this block should go to the function locals. Otherwise this // wouldn't be considered as redeclaration of a block scoped local: // - // function foo() { - // var x; - // let x; - // } + // function foo() { + // var x; + // let x; + // } // // If we placed 'var x' into the function locals and 'let x' - into the locals of - // the block, then there would be no collision. By doing this, we ensure that both - // 'var x' and 'let x' go into the Function-container's locals, and we do get a - // collision conflict. + // the block, then there would be no collision. 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) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer; } From 93b7c33347b171382c19c90cb3ae9787a198fa91 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:54:05 -0700 Subject: [PATCH 32/38] Remove unnecessary returns. --- src/compiler/binder.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ad5ca83585e..6a3b410df30 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -479,12 +479,12 @@ module ts { function bindModuleDeclaration(node: ModuleDeclaration): void { setExportContextFlag(node); if (node.name.kind === SyntaxKind.StringLiteral) { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); } else { let state = getModuleInstanceState(node); if (state === ModuleInstanceState.NonInstantiated) { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); + declareSymbolAndAddToSymbolTable(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); } else { declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); @@ -543,7 +543,7 @@ module ts { } function bindBlockScopedVariableDeclaration(node: Declaration): void { - return bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); + bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); } function getDestructuringParameterName(node: Declaration) { @@ -719,10 +719,10 @@ module ts { function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement): void { if (!isBindingPattern(node.name)) { if (isBlockOrCatchScoped(node)) { - return bindBlockScopedVariableDeclaration(node); + bindBlockScopedVariableDeclaration(node); } else { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); + declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); } } } From fac9ab2508d299d8a873692c4f71f5ccf2adba5a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 20 Apr 2015 23:56:38 -0700 Subject: [PATCH 33/38] Clean up comment more. --- src/compiler/binder.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6a3b410df30..ed318e1804e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -349,18 +349,21 @@ module ts { case SyntaxKind.Block: // do not treat blocks directly inside a function as a block-scoped-container. - // that reside in this block should go to the function locals. Otherwise this - // wouldn't be considered as redeclaration of a block scoped local: + // that reside in this block should go to the function locals. Othewise 'x' + // would not appear to be a redeclaration of a block scoped local in the following + // example: // // function foo() { // var x; // let x; // } // - // If we placed 'var x' into the function locals and 'let x' - into the locals of - // the block, then there would be no collision. 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. + // If we placed 'var x' into the function locals and 'let x' into the locals of + // the block, then there would be no collision. + // + // 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) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer; } From a93bf1048da11678b6cde0c40f0dd445b611e0f8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 21 Apr 2015 00:01:06 -0700 Subject: [PATCH 34/38] Clean up comment. --- src/compiler/binder.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ed318e1804e..40168900a6d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -278,15 +278,15 @@ module ts { // and block-container. If the current node is a container, then it is automatically // considered the current block-container as well. Also, for containers that we know // may contain locals, we proactively initialize the .locals field. We do this because - // it's highly likely that the .locals will be need to place some child in (for example, + // it's highly likely that the .locals will be needed to place some child in (for example, // a parameter, or variable declaration). // - // However, we do not proactively create the locals for block-containers because it's + // However, we do not proactively create the .locals for block-containers because it's // totally normal and common for block-containers to never actually have a block-scoped // variable in them. We don't want to end up allocating an object for every 'block' we // run into when most of them won't be necessary. // - // Finally, if this is a block-container, then we clear out any existing locals object + // Finally, if this is a block-container, then we clear out any existing .locals object // it may contain within it. This happens in incremental scenarios. Because we can be // reusing a node from a previous compilation, that node may have had 'locals' created // for it. We must clear this so we don't accidently move any stale data forward from From cf00a2bec859eaffa8d82698e57350a149bb2aa4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 21 Apr 2015 00:05:08 -0700 Subject: [PATCH 35/38] Clean up comment. --- src/compiler/binder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 40168900a6d..d0bb5edd489 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -349,7 +349,7 @@ module ts { case SyntaxKind.Block: // do not treat blocks directly inside a function as a block-scoped-container. - // that reside in this block should go to the function locals. Othewise 'x' + // Locals that reside in this block should go to the function locals. Othewise 'x' // would not appear to be a redeclaration of a block scoped local in the following // example: // From ca63d6429297d450df796573be4c724a50cbfb65 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 21 Apr 2015 12:59:22 -0700 Subject: [PATCH 36/38] CR feedback. --- src/compiler/binder.ts | 48 ++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d0bb5edd489..daec6fee7df 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -77,13 +77,13 @@ module ts { IsContainerWithLocals = IsContainer | HasLocals } - export function bindSourceFile(file: SourceFile): void { + export function bindSourceFile(file: SourceFile) { let start = new Date().getTime(); bindSourceFileWorker(file); bindTime += new Date().getTime() - start; } - function bindSourceFileWorker(file: SourceFile): void { + function bindSourceFileWorker(file: SourceFile) { let parent: Node; let container: Node; let blockScopeContainer: Node; @@ -263,7 +263,7 @@ 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): void { + function bindChildren(node: Node) { // Before we recurse into a node's chilren, we first save the existing parent, container // and block-container. Then after we pop out of processing the children, we restore // these saved values. @@ -378,11 +378,11 @@ module ts { lastContainer = container; } - function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { + function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); } - function declareSymbolAndAddToSymbolTableWorker(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { + function declareSymbolAndAddToSymbolTableWorker(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { switch (container.kind) { // Modules, source files, and classes need specialized handling for how their // members are declared (for example, a member of a class will go into a specific @@ -479,7 +479,7 @@ module ts { } } - function bindModuleDeclaration(node: ModuleDeclaration): void { + function bindModuleDeclaration(node: ModuleDeclaration) { setExportContextFlag(node); if (node.name.kind === SyntaxKind.StringLiteral) { declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); @@ -505,7 +505,7 @@ module ts { } } - function bindFunctionOrConstructorType(node: SignatureDeclaration): void { + function bindFunctionOrConstructorType(node: SignatureDeclaration) { // For a given function symbol "<...>(...) => T" we want to generate a symbol identical // to the one we would get for: { <...>(...): T } // @@ -521,12 +521,12 @@ module ts { typeLiteralSymbol.members = { [name]: symbol }; } - function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string): void { + function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string) { let symbol = createSymbol(symbolFlags, name); addDeclarationToSymbol(symbol, node, symbolFlags); } - function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { + function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { switch (blockScopeContainer.kind) { case SyntaxKind.ModuleDeclaration: declareModuleMember(node, symbolFlags, symbolExcludes); @@ -545,7 +545,7 @@ module ts { } } - function bindBlockScopedVariableDeclaration(node: Declaration): void { + function bindBlockScopedVariableDeclaration(node: Declaration) { bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); } @@ -553,14 +553,12 @@ module ts { return "__" + indexOf((node.parent).parameters, node); } - function bind(node: Node): void { + function bind(node: Node) { node.parent = parent; // First we bind declaration nodes to a symbol if possible. We'll both create a symbol - // and then potentially add the symbol to an appropriate symbol table. The symbolFlags - // that are returned from this help inform how we recurse into the children of this node. - // - // Possible destination symbol tables are: + // and then potentially add the symbol to an appropriate symbol table. Possible + // destination symbol tables are: // // 1) The 'exports' table of the current container's symbol. // 2) The 'members' table of the current container's symbol. @@ -577,7 +575,7 @@ module ts { bindChildren(node); } - function bindWorker(node: Node): void { + function bindWorker(node: Node) { switch (node.kind) { case SyntaxKind.TypeParameter: return declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); @@ -651,14 +649,14 @@ module ts { } } - function bindSourceFileIfExternalModule(): void { + function bindSourceFileIfExternalModule() { setExportContextFlag(file); if (isExternalModule(file)) { bindAnonymousDeclaration(file, SymbolFlags.ValueModule, '"' + removeFileExtension(file.fileName) + '"'); } } - function bindExportAssignment(node: ExportAssignment): void { + function bindExportAssignment(node: ExportAssignment) { 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); @@ -669,20 +667,20 @@ module ts { } } - function bindExportDeclaration(node: ExportDeclaration): void { + function bindExportDeclaration(node: ExportDeclaration) { if (!node.exportClause) { // All export * declarations are collected in an __export symbol declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, SymbolFlags.None); } } - function bindImportClause(node: ImportClause): void { + function bindImportClause(node: ImportClause) { if (node.name) { declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); } } - function bindClassLikeDeclaration(node: ClassLikeDeclaration): void { + function bindClassLikeDeclaration(node: ClassLikeDeclaration) { if (node.kind === SyntaxKind.ClassDeclaration) { bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); } @@ -713,13 +711,13 @@ module ts { prototypeSymbol.parent = symbol; } - function bindEnumDeclaration(node: EnumDeclaration): void { + function bindEnumDeclaration(node: EnumDeclaration) { return isConst(node) ? declareSymbolAndAddToSymbolTable(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) : declareSymbolAndAddToSymbolTable(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); } - function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement): void { + function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { if (!isBindingPattern(node.name)) { if (isBlockOrCatchScoped(node)) { bindBlockScopedVariableDeclaration(node); @@ -730,7 +728,7 @@ module ts { } } - function bindParameter(node: ParameterDeclaration): void { + function bindParameter(node: ParameterDeclaration) { if (isBindingPattern(node.name)) { bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node)); } @@ -749,7 +747,7 @@ module ts { } } - function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { + function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { return hasDynamicName(node) ? bindAnonymousDeclaration(node, symbolFlags, "__computed") : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); From a54d4154aaad8bce527916a99ce2c4d5fbe3ca54 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 21 Apr 2015 13:06:40 -0700 Subject: [PATCH 37/38] CR feedback. --- src/compiler/binder.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index daec6fee7df..96cd2fdfdc7 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -61,15 +61,15 @@ module ts { // container) before recursing into it. The current node does not have locals. Examples: // // Classes, ObjectLiterals, TypeLiterals, Interfaces... - IsContainer = 0x01, + IsContainer = 1 << 0, // The current node is a block-scoped-container. It should be set as the current block- // container before recursing into it. Examples: // // Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements... - IsBlockScopedContainer = 0x02, + IsBlockScopedContainer = 1 << 1, - HasLocals = 0x04, + HasLocals = 1 << 2, // If the current node is a container that also container that also contains locals. Examples: // From 4c18b2bcf814b8d91c655eedcb1e5926634eb2dc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 2 Jun 2015 17:41:34 -0700 Subject: [PATCH 38/38] Simplify code. --- src/compiler/binder.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1b65ccd63ec..ea589891241 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -513,13 +513,12 @@ module ts { // We do that by making an anonymous type literal symbol, and then setting the function // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable // from an actual type literal symbol you would have gotten had you used the long form. - let name = getDeclarationName(node); - let symbol = createSymbol(SymbolFlags.Signature, name); + let symbol = createSymbol(SymbolFlags.Signature, getDeclarationName(node)); addDeclarationToSymbol(symbol, node, SymbolFlags.Signature); let typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); - typeLiteralSymbol.members = { [name]: symbol }; + typeLiteralSymbol.members = { [symbol.name]: symbol }; } function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string) {