//// [parserRealSource7.ts]
// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. 
// See LICENSE.txt in the project root for complete license information.

///<reference path='typescript.ts' />

module TypeScript {
    export class Continuation {
        public exceptionBlock = -1;
        constructor (public normalBlock: number) { }
    }

    function getBaseTypeLinks(bases: ASTList, baseTypeLinks: TypeLink[]) {
        if (bases) {
            var len = bases.members.length;
            if (baseTypeLinks == null) {
                baseTypeLinks = new TypeLink[];
            }
            for (var i = 0; i < len; i++) {
                var baseExpr = bases.members[i];
                var name = baseExpr;
                var typeLink = new TypeLink();
                typeLink.ast = name;
                baseTypeLinks[baseTypeLinks.length] = typeLink;
            }
        }
        return baseTypeLinks;
    }

    function getBases(type: Type, typeDecl: TypeDeclaration) {
        type.extendsTypeLinks = getBaseTypeLinks(typeDecl.extendsList, type.extendsTypeLinks);
        type.implementsTypeLinks = getBaseTypeLinks(typeDecl.implementsList, type.implementsTypeLinks);
    }

    function addPrototypeField(classType: Type, ast: AST, context: TypeCollectionContext) {
        var field = new ValueLocation();
        field.typeLink = new TypeLink();
        field.typeLink.ast = ast;
        field.typeLink.type = classType.instanceType;

        var fieldSymbol =
            new FieldSymbol("prototype", ast.minChar,
                            context.checker.locationInfo.unitIndex, true, field);
        fieldSymbol.flags |= (SymbolFlags.Property | SymbolFlags.BuiltIn);
        field.symbol = fieldSymbol;
        fieldSymbol.declAST = ast;
        classType.members.addPublicMember("prototype", fieldSymbol);
    }

    export function createNewConstructGroupForType(type: Type) {
        var signature = new Signature();
        signature.returnType = new TypeLink();
        signature.returnType.type = type.instanceType;
        signature.parameters = [];

        type.construct = new SignatureGroup();
        type.construct.addSignature(signature);     
    }

    export function cloneParentConstructGroupForChildType(child: Type, parent: Type) {
        child.construct = new SignatureGroup();
        var sig: Signature = null;

        if (!parent.construct) {
            createNewConstructGroupForType(parent);
        }

        for (var i = 0; i < parent.construct.signatures.length; i++) { 
            sig = new Signature();
            sig.parameters = parent.construct.signatures[i].parameters;
            sig.nonOptionalParameterCount = parent.construct.signatures[i].nonOptionalParameterCount;
            sig.typeCheckStatus = parent.construct.signatures[i].typeCheckStatus;
            sig.declAST = parent.construct.signatures[i].declAST;
            sig.returnType = new TypeLink();
            sig.returnType.type = child.instanceType;
            child.construct.addSignature(sig);
        }

    }

    export var globalId = "__GLO";

    export interface IAliasScopeContext {
        topLevelScope: ScopeChain;
        members: IHashTable;
        tcContext: TypeCollectionContext;
    }

    function findTypeSymbolInScopeChain(name: string, scopeChain: ScopeChain): Symbol {
        var symbol = scopeChain.scope.find(name, false, true);

        if (symbol == null && scopeChain.previous) {
            symbol = findTypeSymbolInScopeChain(name, scopeChain.previous);
        }

        return symbol;
    }

    function findSymbolFromAlias(alias: AST, context: IAliasScopeContext): Symbol {
        var symbol: Symbol = null;
        switch (alias.nodeType) {
            case NodeType.Name:
                var name = (<Identifier>alias).text;
                var isDynamic = isQuoted(name);

                var findSym = (id: string) => {
                    if (context.members) {
                        return context.members.lookup(name);
                    }
                    else {
                        return findTypeSymbolInScopeChain(name, context.topLevelScope);
                    }
                }

                if (isDynamic) {
                    symbol = context.tcContext.checker.findSymbolForDynamicModule(name, context.tcContext.script.locationInfo.filename, findSym);
                }
                else {
                    symbol = findSym(name);
                }

                break;

            case NodeType.Dot:
                var dottedExpr = <BinaryExpression>alias;
                var op1Sym = findSymbolFromAlias(dottedExpr.operand1, context);

                if (op1Sym && op1Sym.getType()) {
                    symbol = findSymbolFromAlias(dottedExpr.operand2, context);
                }

                break;

            default:
                break;
        }

        if (symbol) {
            var symType = symbol.getType();
            if (symType) {
                var members = symType.members;
                if (members) {
                    context.members = members.publicMembers;
                }
            }
        }

        return symbol;
    }

    export function preCollectImportTypes(ast: AST, parent: AST, context: TypeCollectionContext) {
        var scopeChain = context.scopeChain;
        var typeSymbol: TypeSymbol = null;
        var modType: ModuleType = null;
        var importDecl = <ImportDeclaration>ast;
        var isExported = hasFlag(importDecl.varFlags, VarFlags.Exported);

        // REVIEW: technically, this call isn't strictly necessary, since we'll find the type during the call to resolveTypeMembers
        var aliasedModSymbol = findSymbolFromAlias(importDecl.alias, { topLevelScope: scopeChain, members: null, tcContext: context });
        var isGlobal = context.scopeChain.container == context.checker.gloMod;

        if (aliasedModSymbol) {
            var aliasedModType = aliasedModSymbol.getType();

            if (aliasedModType) {
                modType = <ModuleType>aliasedModType;
            }
        }

        typeSymbol = new TypeSymbol(importDecl.id.text, importDecl.minChar,
                                    context.checker.locationInfo.unitIndex, modType);

        typeSymbol.aliasLink = importDecl;

        if (context.scopeChain.moduleDecl) {
            typeSymbol.declModule = context.scopeChain.moduleDecl;
        }
        typeSymbol.declAST = importDecl;
        importDecl.id.sym = typeSymbol;
        scopeChain.scope.enter(scopeChain.container, ast, typeSymbol,
                                context.checker.errorReporter, isExported || isGlobal, true, false);
        scopeChain.scope.enter(scopeChain.container, ast, typeSymbol,
                                context.checker.errorReporter, isExported || isGlobal, false, false);
        return true;
    }

    export function preCollectModuleTypes(ast: AST, parent: AST, context: TypeCollectionContext) {
        var scopeChain = context.scopeChain;

        var moduleDecl: ModuleDeclaration = <ModuleDeclaration>ast;

        var isAmbient = hasFlag(moduleDecl.modFlags, ModuleFlags.Ambient);
        var isEnum = hasFlag(moduleDecl.modFlags, ModuleFlags.IsEnum);
        var isGlobal = context.scopeChain.container == context.checker.gloMod;
        var isExported = hasFlag(moduleDecl.modFlags, ModuleFlags.Exported);
        var modName = (<Identifier>moduleDecl.name).text;

        var isDynamic = isQuoted(modName);

        var symbol = scopeChain.scope.findLocal(modName, false, false);
        var typeSymbol: TypeSymbol = null;
        var modType: ModuleType = null;
        if ((symbol == null) || (symbol.kind() != SymbolKind.Type)) {

            if (modType == null) {
                var enclosedTypes = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                var ambientEnclosedTypes = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                modType = new ModuleType(enclosedTypes, ambientEnclosedTypes);
                if (isEnum) {
                    modType.typeFlags |= TypeFlags.IsEnum;
                }
                modType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                modType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                modType.setHasImplementation();
            }

            typeSymbol = new TypeSymbol(modName, moduleDecl.minChar,
                                        context.checker.locationInfo.unitIndex, modType);

            if (context.scopeChain.moduleDecl) {
                typeSymbol.declModule = context.scopeChain.moduleDecl;
            }
            typeSymbol.declAST = moduleDecl;
            typeSymbol.prettyName = moduleDecl.prettyName;
            scopeChain.scope.enter(scopeChain.container, ast, typeSymbol,
                                    context.checker.errorReporter, isExported || isGlobal, true, isAmbient);
            scopeChain.scope.enter(scopeChain.container, ast, typeSymbol,
                                    context.checker.errorReporter, isExported || isGlobal, false, isAmbient);
            modType.symbol = typeSymbol;
        }
        else {
            if (symbol && symbol.declAST && symbol.declAST.nodeType != NodeType.ModuleDeclaration) {
                context.checker.errorReporter.simpleError(moduleDecl, "Conflicting symbol name for module '" + modName + "'");
            }
            typeSymbol = <TypeSymbol>symbol;

            // initialize new private scope for the type
            var publicEnclosedTypes = typeSymbol.type.getAllEnclosedTypes().publicMembers;
            var publicEnclosedTypesTable = (publicEnclosedTypes == null) ? new StringHashTable() : publicEnclosedTypes;
            var enclosedTypes = new ScopedMembers(new DualStringHashTable(publicEnclosedTypesTable, new StringHashTable()));

            var publicEnclosedAmbientTypes = typeSymbol.type.getAllAmbientEnclosedTypes().publicMembers;
            var publicAmbientEnclosedTypesTable = (publicEnclosedAmbientTypes == null) ? new StringHashTable() : publicEnclosedAmbientTypes;
            var ambientEnclosedTypes = new ScopedMembers(new DualStringHashTable(publicAmbientEnclosedTypesTable, new StringHashTable()));

            var publicMembers = typeSymbol.type.members.publicMembers;
            var publicMembersTable = (publicMembers == null) ? new StringHashTable() : publicMembers;
            var members = new ScopedMembers(new DualStringHashTable(publicMembersTable, new StringHashTable()));

            var publicAmbientMembers = typeSymbol.type.ambientMembers.publicMembers;
            var publicAmbientMembersTable = (publicAmbientMembers == null) ? new StringHashTable() : publicAmbientMembers;
            var ambientMembers = new ScopedMembers(new DualStringHashTable(publicAmbientMembersTable, new StringHashTable()));

            modType = new ModuleType(enclosedTypes, ambientEnclosedTypes);
            if (isEnum) {
                modType.typeFlags |= TypeFlags.IsEnum;
            }
            modType.members = members;
            modType.ambientMembers = ambientMembers;
            modType.setHasImplementation();
            modType.symbol = typeSymbol;

            typeSymbol.addLocation(moduleDecl.minChar);
            typeSymbol.expansions.push(modType);

        }
        if (context.scopeChain.moduleDecl) {
            context.scopeChain.moduleDecl.recordNonInterface();
        }
        // REVIEW: If multiple disparate module decls for the same module don't agree
        // in export privileges, how should we handle it?
        if (isExported) {
            typeSymbol.flags |= SymbolFlags.Exported;
        }
        if ((context.scopeChain.moduleDecl) ||
            (context.scopeChain.container == context.checker.gloMod)) {
            typeSymbol.flags |= SymbolFlags.ModuleMember;
        }

        moduleDecl.mod = modType;
        pushTypeCollectionScope(typeSymbol, modType.members,
                                modType.ambientMembers,
                                modType.enclosedTypes,
                                modType.ambientEnclosedTypes,
                                context, null, null, moduleDecl);

        return true;
    }

    export function preCollectClassTypes(ast: AST, parent: AST, context: TypeCollectionContext) {
        var scopeChain = context.scopeChain;
        var classDecl = <ClassDeclaration>ast;

        var classType: Type;
        var instanceType: Type;
        var typeSymbol: TypeSymbol = null;
        var className = (<Identifier>classDecl.name).text;
        var alreadyInScope = false;
        var isAmbient = hasFlag(classDecl.varFlags, VarFlags.Ambient);
        var isExported = hasFlag(classDecl.varFlags, VarFlags.Exported);
        var isGlobal = context.scopeChain.container == context.checker.gloMod;
        var containerMod = <TypeSymbol>scopeChain.container;
        var foundValSymbol = false;

        typeSymbol = <TypeSymbol>scopeChain.scope.findLocal(className, false, true);
        
        // check the value space, since an override may have been declared with the type's name
        // REVIEW-CLASSES
        if (!typeSymbol) {
            var valTypeSymbol = scopeChain.scope.findLocal(className, false, false);
            
            if (valTypeSymbol &&
                valTypeSymbol.isType() &&
                valTypeSymbol.declAST &&
                valTypeSymbol.declAST.nodeType == NodeType.FuncDecl &&
                (<FuncDecl>valTypeSymbol.declAST).isSignature()) {
                
                typeSymbol = <TypeSymbol>valTypeSymbol;
                foundValSymbol = true;
                
                if (isExported) {
                    typeSymbol.flags |= SymbolFlags.Exported;
                }
            
                if (isAmbient) {
                    typeSymbol.flags |= SymbolFlags.Ambient;
                }                
                
                // the class was never entered into type space, so add it
                context.scopeChain.scope.enter(context.scopeChain.container, ast, typeSymbol,
                                            context.checker.errorReporter, isExported || isGlobal, true, isAmbient);                
            }
        }
        
        if (typeSymbol && !foundValSymbol && (typeSymbol.declAST != classDecl)) {
            typeSymbol = null;
        }

        if (typeSymbol == null) {
            var valueSymbol = scopeChain.scope.findLocal(className, false, false);
            classType = new Type();
            classType.setHasImplementation();
            instanceType = new Type();
            instanceType.setHasImplementation();
            classType.instanceType = instanceType;
            classType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            classType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            addPrototypeField(classType, classDecl, context);
            instanceType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            instanceType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            typeSymbol = new TypeSymbol(className, classDecl.minChar,
                                        context.checker.locationInfo.unitIndex, classType);
            typeSymbol.declAST = classDecl;
            typeSymbol.instanceType = instanceType;
            classType.symbol = typeSymbol;
            instanceType.symbol = typeSymbol;

            if (context.scopeChain.moduleDecl) {
                context.scopeChain.moduleDecl.recordNonInterface();
                typeSymbol.declModule = context.scopeChain.moduleDecl;
                typeSymbol.flags |= SymbolFlags.ModuleMember;
            }

            if (isExported) {
                typeSymbol.flags |= SymbolFlags.Exported;
            }
            
            if (isAmbient) {
                typeSymbol.flags |= SymbolFlags.Ambient;
            }

            ast.type = classType;

            // class in both name spaces (type for instance type; constructor representative in value space)
            context.scopeChain.scope.enter(context.scopeChain.container, ast, typeSymbol,
                                            context.checker.errorReporter, isExported || isGlobal, true, isAmbient);

            if (valueSymbol == null) {
                context.scopeChain.scope.enter(context.scopeChain.container, ast, typeSymbol,
                                            context.checker.errorReporter, isExported || isGlobal, false, isAmbient);
            }
        }
        else {                            
            classType = typeSymbol.type;
            
            // If the instance type is null, a call overload was likely declared before the class constructor
            if (classType.instanceType == null) {
                classType.instanceType = new Type();
                classType.instanceType.setHasImplementation();
                classType.instanceType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                classType.instanceType.symbol = classType.symbol;
                classType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                classType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            }
            
            instanceType = classType.instanceType;
            ast.type = classType;
        }
        
        // if the class has no declared constructor, either create a default signature or adapt 
        // it's base class's signature group
        if (!classDecl.constructorDecl) {

            if (typeSymbol && typeSymbol.declAST && typeSymbol.declAST.type && typeSymbol.declAST.type.call && !(<FuncDecl>typeSymbol.declAST).isOverload) {
                context.checker.errorReporter.duplicateIdentifier(typeSymbol.declAST, typeSymbol.name);
            }

            createNewConstructGroupForType(classDecl.type);
        }

        classType.typeFlags |= TypeFlags.IsClass;
        instanceType.typeFlags |= TypeFlags.IsClass;

        getBases(instanceType, classDecl);
        pushTypeCollectionScope(typeSymbol, instanceType.members, instanceType.ambientMembers, null, null,
                                context, instanceType, classType, null);
        return true;
    }

    export function preCollectInterfaceTypes(ast: AST, parent: AST, context: TypeCollectionContext) {
        var scopeChain = context.scopeChain;
        var interfaceDecl = <InterfaceDeclaration>ast;
        var interfaceSymbol: TypeSymbol = null;
        var interfaceType: Type = null;
        var isExported = hasFlag(interfaceDecl.varFlags, VarFlags.Exported);
        var isGlobal = context.scopeChain.container == context.checker.gloMod;
        var alreadyInScope = true;

        alreadyInScope = false;
        var interfaceName = (<Identifier>interfaceDecl.name).text;
        interfaceSymbol = <TypeSymbol>scopeChain.scope.findLocal(interfaceName, false, true);
        if (interfaceSymbol == null) {
            interfaceType = new Type();
            interfaceSymbol = new TypeSymbol(interfaceName,
                                        ast.minChar,
                                        context.checker.locationInfo.unitIndex,
                                        interfaceType);
            interfaceType.symbol = interfaceSymbol;
            // REVIEW: Shouldn't allocate another table for interface privates
            interfaceType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            interfaceType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            interfaceSymbol.declAST = interfaceDecl;
            interfaceSymbol.declModule = context.scopeChain.moduleDecl;
        }
        else {
            alreadyInScope = true;
            interfaceType = interfaceSymbol.type;
        }

        if (!interfaceType) {
            interfaceType = context.checker.anyType;
        }

        ast.type = interfaceType;
        getBases(interfaceType, interfaceDecl);

        if (isExported) {
            interfaceSymbol.flags |= SymbolFlags.Exported;
        }

        if (context.scopeChain.moduleDecl) {
            interfaceSymbol.flags |= SymbolFlags.ModuleMember;
        }

        if (!alreadyInScope) {
            context.scopeChain.scope.enter(context.scopeChain.container, ast,
                                            interfaceSymbol, context.checker.errorReporter, isGlobal || isExported, true, false); // REVIEW: Technically, interfaces should be ambient
        }
        pushTypeCollectionScope(interfaceSymbol, interfaceType.members, interfaceType.ambientMembers, null, null,
                                context, interfaceType, null, null);
        return true;
    }

    export function preCollectArgDeclTypes(ast: AST, parent: AST, context: TypeCollectionContext) {
        var scopeChain = context.scopeChain;
        var argDecl = <ArgDecl>ast;
        if (hasFlag(argDecl.varFlags, VarFlags.Public | VarFlags.Private)) {
            var field = new ValueLocation();
            var isPrivate = hasFlag(argDecl.varFlags, VarFlags.Private);
            var fieldSymbol =
                new FieldSymbol(argDecl.id.text, argDecl.minChar,
                                context.checker.locationInfo.unitIndex,
                                !hasFlag(argDecl.varFlags, VarFlags.Readonly),
                                field);
            fieldSymbol.transferVarFlags(argDecl.varFlags);
            field.symbol = fieldSymbol;
            fieldSymbol.declAST = ast;
            argDecl.parameterPropertySym = fieldSymbol;

            context.scopeChain.scope.enter(context.scopeChain.container, ast,
                                            fieldSymbol, context.checker.errorReporter, !isPrivate, false, false);

            field.typeLink = getTypeLink(argDecl.typeExpr, context.checker, argDecl.init == null);
            argDecl.sym = fieldSymbol;
        }
        return false;
    }

    export function preCollectVarDeclTypes(ast: AST, parent: AST, context: TypeCollectionContext) {
        var scopeChain = context.scopeChain;
        var varDecl = <VarDecl>ast;
        var isAmbient = hasFlag(varDecl.varFlags, VarFlags.Ambient);
        var isExported = hasFlag(varDecl.varFlags, VarFlags.Exported);
        var isGlobal = context.scopeChain.container == context.checker.gloMod;
        var isProperty = hasFlag(varDecl.varFlags, VarFlags.Property);
        var isStatic = hasFlag(varDecl.varFlags, VarFlags.Static);
        var isPrivate = hasFlag(varDecl.varFlags, VarFlags.Private);
        var isOptional = hasFlag(varDecl.id.flags, ASTFlags.OptionalName);

        if (context.scopeChain.moduleDecl) {
            context.scopeChain.moduleDecl.recordNonInterface();
        }
        if (isProperty ||
            isExported ||
            (context.scopeChain.container == context.checker.gloMod) ||
            context.scopeChain.moduleDecl) {
            if (isAmbient) {
                var existingSym =
                    <FieldSymbol>scopeChain.scope.findLocal(varDecl.id.text, false, false);
                if (existingSym) {
                    varDecl.sym = existingSym;
                    return false;
                }
            }

            // Defensive error detection...
            if (varDecl.id == null) {
                context.checker.errorReporter.simpleError(varDecl, "Expected variable identifier at this location");
                return false;
            }

            var field = new ValueLocation();
            var fieldSymbol =
                new FieldSymbol(varDecl.id.text, varDecl.minChar,
                                context.checker.locationInfo.unitIndex,
                                (varDecl.varFlags & VarFlags.Readonly) == VarFlags.None,
                                field);
            fieldSymbol.transferVarFlags(varDecl.varFlags);
            if (isOptional) {
                fieldSymbol.flags |= SymbolFlags.Optional;
            }
            field.symbol = fieldSymbol;
            fieldSymbol.declAST = ast;
            if ((context.scopeChain.moduleDecl) ||
                (context.scopeChain.container == context.checker.gloMod)) {
                fieldSymbol.flags |= SymbolFlags.ModuleMember;
                fieldSymbol.declModule = context.scopeChain.moduleDecl;
            }

            // if it's static, enter it into the class's member list directly
            if (hasFlag(varDecl.varFlags, VarFlags.Property) && isStatic && context.scopeChain.classType) {
                if (!context.scopeChain.classType.members.publicMembers.add(varDecl.id.text, fieldSymbol)) {
                    context.checker.errorReporter.duplicateIdentifier(ast, fieldSymbol.name);
                }
                fieldSymbol.container = context.scopeChain.classType.symbol;
            }
            else {
                context.scopeChain.scope.enter(context.scopeChain.container,
                                                ast,
                                                fieldSymbol,
                                                context.checker.errorReporter,
                                                !isPrivate && (isProperty || isExported || isGlobal || isStatic),
                                                false,
                                                isAmbient);
            }

            if (hasFlag(varDecl.varFlags, VarFlags.Exported)) {
                fieldSymbol.flags |= SymbolFlags.Exported;
            }

            field.typeLink = getTypeLink(varDecl.typeExpr, context.checker,
                                        varDecl.init == null);
            varDecl.sym = fieldSymbol;
        }
        return false;
    }

    export function preCollectFuncDeclTypes(ast: AST, parent: AST, context: TypeCollectionContext) {
        var scopeChain = context.scopeChain;

        // REVIEW: This will have to change when we move to "export"
        if (context.scopeChain.moduleDecl) {
            context.scopeChain.moduleDecl.recordNonInterface();
        }

        var funcDecl = <FuncDecl>ast;
        var fgSym: TypeSymbol = null;
        var nameText = funcDecl.getNameText();
        var isExported = hasFlag(funcDecl.fncFlags, FncFlags.Exported | FncFlags.ClassPropertyMethodExported);
        var isStatic = hasFlag(funcDecl.fncFlags, FncFlags.Static);
        var isPrivate = hasFlag(funcDecl.fncFlags, FncFlags.Private);
        var isConstructor = funcDecl.isConstructMember() || funcDecl.isConstructor;
        var containerSym:TypeSymbol = <TypeSymbol> (((funcDecl.isMethod() && isStatic) || funcDecl.isAccessor()) && context.scopeChain.classType ? context.scopeChain.classType.symbol : context.scopeChain.container);
        var containerScope: SymbolScope = context.scopeChain.scope;
        var isGlobal = containerSym == context.checker.gloMod;
        var isOptional = funcDecl.name && hasFlag(funcDecl.name.flags, ASTFlags.OptionalName);
        var go = false;
        var foundSymbol = false; 

        // If this is a class constructor, the "container" is actually the class declaration
        if (isConstructor && hasFlag(funcDecl.fncFlags, FncFlags.ClassMethod)) {
            containerSym = <TypeSymbol>containerSym.container;
            containerScope = scopeChain.previous.scope;
        }

        funcDecl.unitIndex = context.checker.locationInfo.unitIndex;
        
        // If the parent is the constructor, and this isn't an instance method, skip it.
        // That way, we'll set the type during scope assignment, and can be sure that the
        // function will be placed in the constructor-local scope
        if (!funcDecl.isConstructor &&
            containerSym &&
            containerSym.declAST &&
            containerSym.declAST.nodeType == NodeType.FuncDecl &&
            (<FuncDecl>containerSym.declAST).isConstructor &&
            !funcDecl.isMethod()) {
            return go;
        }        

        // Interfaces and overloads
        if (hasFlag(funcDecl.fncFlags, FncFlags.Signature)) {
            var instType = context.scopeChain.thisType;                       

            // If the function is static, search in the class type's
            if (nameText && nameText != "__missing") {
                if (isStatic) {
                    fgSym = containerSym.type.members.allMembers.lookup(nameText);
                }
                else {
                    // REVIEW: This logic should be symmetric with preCollectClassTypes
                    fgSym = <TypeSymbol>containerScope.findLocal(nameText, false, false);
                    
                    // If we could not find the function symbol in the value context, look
                    // in the type context.
                    // This would be the case, for example, if a class constructor override
                    // were declared before a call override for a given class
                    if (fgSym == null) {
                        fgSym = <TypeSymbol>containerScope.findLocal(nameText, false, true);
                    }
                }
                
                if (fgSym) {
                    foundSymbol = true;
                    
                    // We'll combine ambient and non-ambient funcdecls during typecheck (for contextual typing).,
                    // So, if they don't agree, don't use the symbol we've found                    
                    if (!funcDecl.isSignature() && (hasFlag(funcDecl.fncFlags, FncFlags.Ambient) != hasFlag(fgSym.flags, SymbolFlags.Ambient))) {
                       fgSym = null;
                    }
                }                
            }
            
            // a function with this symbol has not yet been declared in this scope
            // REVIEW: In the code below, we need to ensure that only function overloads are considered
            //  (E.g., if a vardecl has the same id as a function or class, we may use the vardecl symbol
            //  as the overload.)  Defensively, however, the vardecl won't have a type yet, so it should
            //  suffice to just check for a null type when considering the overload symbol in
            //  createFunctionSignature
            if (fgSym == null) {
                if (!(funcDecl.isSpecialFn())) {                    
                    fgSym = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, null, !foundSymbol).declAST.type.symbol;
                }
                else {
                    fgSym = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, containerSym, false).declAST.type.symbol;                                                                         
                }
                
                // set the symbol's declAST, which will point back to the first declaration (symbol or otherwise)
                // related to this symbol
                if (fgSym.declAST == null || !funcDecl.isSpecialFn()) {
                    fgSym.declAST = ast;
                }
            }
            else { // there exists a symbol with this name
                
                if ((fgSym.kind() == SymbolKind.Type)) {

                    fgSym = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, fgSym, false).declAST.type.symbol;
                }
                else {
                    context.checker.errorReporter.simpleError(funcDecl, "Function or method '" + funcDecl.name.actualText + "' already declared as a property");
                }
            }
         
            if (funcDecl.isSpecialFn() && !isStatic) {
                funcDecl.type = instType ? instType : fgSym.type; 
            }
            else {
                funcDecl.type = fgSym.type;
            }            
        }
        else {
            // declarations
            
            if (nameText) {
                if (isStatic) {
                    fgSym = containerSym.type.members.allMembers.lookup(nameText);
                }
                else {
                    // in the constructor case, we want to check the parent scope for overloads
                    if (funcDecl.isConstructor && context.scopeChain.previous) {
                        fgSym = <TypeSymbol>context.scopeChain.previous.scope.findLocal(nameText, false, false);
                    }
                    
                    if (fgSym == null) {
                        fgSym = <TypeSymbol>containerScope.findLocal(nameText, false, false);
                    }
                }
                if (fgSym) {
                    foundSymbol = true;
                    
                    if (!isConstructor && fgSym.declAST.nodeType == NodeType.FuncDecl && !(<FuncDecl>fgSym.declAST).isAccessor() && !(<FuncDecl>fgSym.declAST).isSignature()) {
                        fgSym = null;
                        foundSymbol = false;
                    }
                }                
            }

            // REVIEW: Move this check into the typecheck phase?  It's only being run over properties...
            if (fgSym &&
                !fgSym.isAccessor() &&
                fgSym.type &&
                fgSym.type.construct &&
                fgSym.type.construct.signatures != [] &&
                (fgSym.type.construct.signatures[0].declAST == null ||
                    !hasFlag(fgSym.type.construct.signatures[0].declAST.fncFlags, FncFlags.Ambient)) &&
                !funcDecl.isConstructor) {
                context.checker.errorReporter.simpleError(funcDecl, "Functions may not have class overloads");
            }

            if (fgSym && !(fgSym.kind() == SymbolKind.Type) && funcDecl.isMethod() && !funcDecl.isAccessor() && !funcDecl.isConstructor) {
                context.checker.errorReporter.simpleError(funcDecl, "Function or method '" + funcDecl.name.actualText + "' already declared as a property");
                fgSym.type = context.checker.anyType;
            }
            var sig = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, fgSym, !foundSymbol);

            // it's a getter or setter function                                   
            if (((!fgSym || fgSym.declAST.nodeType != NodeType.FuncDecl) && funcDecl.isAccessor()) || (fgSym && fgSym.isAccessor())) {
                funcDecl.accessorSymbol = context.checker.createAccessorSymbol(funcDecl, fgSym, containerSym.type, (funcDecl.isMethod() && isStatic), true, containerScope, containerSym);
            }

            funcDecl.type.symbol.declAST = ast;
            if (funcDecl.isConstructor) { // REVIEW: Remove when classes completely replace oldclass
                go = true;
            };
        }
        if (isExported) {
            if (funcDecl.type.call) {
                funcDecl.type.symbol.flags |= SymbolFlags.Exported;
            }
            
            // Accessors are set to 'exported' above
            if (fgSym && !fgSym.isAccessor() && fgSym.kind() == SymbolKind.Type && fgSym.type.call) {
                fgSym.flags |= SymbolFlags.Exported;
            }
        }
        if (context.scopeChain.moduleDecl && !funcDecl.isSpecialFn()) {
            funcDecl.type.symbol.flags |= SymbolFlags.ModuleMember;
            funcDecl.type.symbol.declModule = context.scopeChain.moduleDecl;
        }

        if (fgSym && isOptional) {
            fgSym.flags |= SymbolFlags.Optional;
        }

        return go;
    }

    export function preCollectTypes(ast: AST, parent: AST, walker: IAstWalker) {
        var context: TypeCollectionContext = walker.state;
        var go = false;
        var scopeChain = context.scopeChain;

        if (ast.nodeType == NodeType.Script) {
            var script: Script = <Script>ast;
            context.script = script;
            go = true;
        }
        else if (ast.nodeType == NodeType.List) {
            go = true;
        }
        else if (ast.nodeType == NodeType.ImportDeclaration) {
            go = preCollectImportTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.With) {
            go = false;
        }
        else if (ast.nodeType == NodeType.ModuleDeclaration) {
            go = preCollectModuleTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.ClassDeclaration) {
            go = preCollectClassTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.Block) {
            go = true;
        }
        else if (ast.nodeType == NodeType.InterfaceDeclaration) {
            go = preCollectInterfaceTypes(ast, parent, context);
        }
        // This will be a constructor arg because this pass only traverses
        // constructor arg lists
        else if (ast.nodeType == NodeType.ArgDecl) {
            go = preCollectArgDeclTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.VarDecl) {
            go = preCollectVarDeclTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.FuncDecl) {
            go = preCollectFuncDeclTypes(ast, parent, context);
        }
        else {
            if (ast.isStatementOrExpression() && context.scopeChain.moduleDecl) {
                context.scopeChain.moduleDecl.recordNonInterface();
            }
        }
        walker.options.goChildren = go;
        return ast;
    }

    export function postCollectTypes(ast: AST, parent: AST, walker: IAstWalker) {
        var context: TypeCollectionContext = walker.state;

        if (ast.nodeType == NodeType.ModuleDeclaration) {
            popTypeCollectionScope(context);
        }
        else if (ast.nodeType == NodeType.ClassDeclaration) {
            popTypeCollectionScope(context);
        }
        else if (ast.nodeType == NodeType.InterfaceDeclaration) {
            popTypeCollectionScope(context);
        }
        return ast;
    }

}

//// [parserRealSource7.js]
// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. 
// See LICENSE.txt in the project root for complete license information.
///<reference path='typescript.ts' />
var TypeScript;
(function (TypeScript) {
    var Continuation = (function () {
        function Continuation(normalBlock) {
            this.normalBlock = normalBlock;
            this.exceptionBlock = -1;
        }
        return Continuation;
    })();
    TypeScript.Continuation = Continuation;
    function getBaseTypeLinks(bases, baseTypeLinks) {
        if (bases) {
            var len = bases.members.length;
            if (baseTypeLinks == null) {
                baseTypeLinks = new TypeLink[];
            }
            for (var i = 0; i < len; i++) {
                var baseExpr = bases.members[i];
                var name = baseExpr;
                var typeLink = new TypeLink();
                typeLink.ast = name;
                baseTypeLinks[baseTypeLinks.length] = typeLink;
            }
        }
        return baseTypeLinks;
    }
    function getBases(type, typeDecl) {
        type.extendsTypeLinks = getBaseTypeLinks(typeDecl.extendsList, type.extendsTypeLinks);
        type.implementsTypeLinks = getBaseTypeLinks(typeDecl.implementsList, type.implementsTypeLinks);
    }
    function addPrototypeField(classType, ast, context) {
        var field = new ValueLocation();
        field.typeLink = new TypeLink();
        field.typeLink.ast = ast;
        field.typeLink.type = classType.instanceType;
        var fieldSymbol = new FieldSymbol("prototype", ast.minChar, context.checker.locationInfo.unitIndex, true, field);
        fieldSymbol.flags |= (SymbolFlags.Property | SymbolFlags.BuiltIn);
        field.symbol = fieldSymbol;
        fieldSymbol.declAST = ast;
        classType.members.addPublicMember("prototype", fieldSymbol);
    }
    function createNewConstructGroupForType(type) {
        var signature = new Signature();
        signature.returnType = new TypeLink();
        signature.returnType.type = type.instanceType;
        signature.parameters = [];
        type.construct = new SignatureGroup();
        type.construct.addSignature(signature);
    }
    TypeScript.createNewConstructGroupForType = createNewConstructGroupForType;
    function cloneParentConstructGroupForChildType(child, parent) {
        child.construct = new SignatureGroup();
        var sig = null;
        if (!parent.construct) {
            createNewConstructGroupForType(parent);
        }
        for (var i = 0; i < parent.construct.signatures.length; i++) {
            sig = new Signature();
            sig.parameters = parent.construct.signatures[i].parameters;
            sig.nonOptionalParameterCount = parent.construct.signatures[i].nonOptionalParameterCount;
            sig.typeCheckStatus = parent.construct.signatures[i].typeCheckStatus;
            sig.declAST = parent.construct.signatures[i].declAST;
            sig.returnType = new TypeLink();
            sig.returnType.type = child.instanceType;
            child.construct.addSignature(sig);
        }
    }
    TypeScript.cloneParentConstructGroupForChildType = cloneParentConstructGroupForChildType;
    TypeScript.globalId = "__GLO";
    function findTypeSymbolInScopeChain(name, scopeChain) {
        var symbol = scopeChain.scope.find(name, false, true);
        if (symbol == null && scopeChain.previous) {
            symbol = findTypeSymbolInScopeChain(name, scopeChain.previous);
        }
        return symbol;
    }
    function findSymbolFromAlias(alias, context) {
        var symbol = null;
        switch (alias.nodeType) {
            case NodeType.Name:
                var name = alias.text;
                var isDynamic = isQuoted(name);
                var findSym = function (id) {
                    if (context.members) {
                        return context.members.lookup(name);
                    }
                    else {
                        return findTypeSymbolInScopeChain(name, context.topLevelScope);
                    }
                };
                if (isDynamic) {
                    symbol = context.tcContext.checker.findSymbolForDynamicModule(name, context.tcContext.script.locationInfo.filename, findSym);
                }
                else {
                    symbol = findSym(name);
                }
                break;
            case NodeType.Dot:
                var dottedExpr = alias;
                var op1Sym = findSymbolFromAlias(dottedExpr.operand1, context);
                if (op1Sym && op1Sym.getType()) {
                    symbol = findSymbolFromAlias(dottedExpr.operand2, context);
                }
                break;
            default:
                break;
        }
        if (symbol) {
            var symType = symbol.getType();
            if (symType) {
                var members = symType.members;
                if (members) {
                    context.members = members.publicMembers;
                }
            }
        }
        return symbol;
    }
    function preCollectImportTypes(ast, parent, context) {
        var scopeChain = context.scopeChain;
        var typeSymbol = null;
        var modType = null;
        var importDecl = ast;
        var isExported = hasFlag(importDecl.varFlags, VarFlags.Exported);
        // REVIEW: technically, this call isn't strictly necessary, since we'll find the type during the call to resolveTypeMembers
        var aliasedModSymbol = findSymbolFromAlias(importDecl.alias, { topLevelScope: scopeChain, members: null, tcContext: context });
        var isGlobal = context.scopeChain.container == context.checker.gloMod;
        if (aliasedModSymbol) {
            var aliasedModType = aliasedModSymbol.getType();
            if (aliasedModType) {
                modType = aliasedModType;
            }
        }
        typeSymbol = new TypeSymbol(importDecl.id.text, importDecl.minChar, context.checker.locationInfo.unitIndex, modType);
        typeSymbol.aliasLink = importDecl;
        if (context.scopeChain.moduleDecl) {
            typeSymbol.declModule = context.scopeChain.moduleDecl;
        }
        typeSymbol.declAST = importDecl;
        importDecl.id.sym = typeSymbol;
        scopeChain.scope.enter(scopeChain.container, ast, typeSymbol, context.checker.errorReporter, isExported || isGlobal, true, false);
        scopeChain.scope.enter(scopeChain.container, ast, typeSymbol, context.checker.errorReporter, isExported || isGlobal, false, false);
        return true;
    }
    TypeScript.preCollectImportTypes = preCollectImportTypes;
    function preCollectModuleTypes(ast, parent, context) {
        var scopeChain = context.scopeChain;
        var moduleDecl = ast;
        var isAmbient = hasFlag(moduleDecl.modFlags, ModuleFlags.Ambient);
        var isEnum = hasFlag(moduleDecl.modFlags, ModuleFlags.IsEnum);
        var isGlobal = context.scopeChain.container == context.checker.gloMod;
        var isExported = hasFlag(moduleDecl.modFlags, ModuleFlags.Exported);
        var modName = moduleDecl.name.text;
        var isDynamic = isQuoted(modName);
        var symbol = scopeChain.scope.findLocal(modName, false, false);
        var typeSymbol = null;
        var modType = null;
        if ((symbol == null) || (symbol.kind() != SymbolKind.Type)) {
            if (modType == null) {
                var enclosedTypes = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                var ambientEnclosedTypes = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                modType = new ModuleType(enclosedTypes, ambientEnclosedTypes);
                if (isEnum) {
                    modType.typeFlags |= TypeFlags.IsEnum;
                }
                modType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                modType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                modType.setHasImplementation();
            }
            typeSymbol = new TypeSymbol(modName, moduleDecl.minChar, context.checker.locationInfo.unitIndex, modType);
            if (context.scopeChain.moduleDecl) {
                typeSymbol.declModule = context.scopeChain.moduleDecl;
            }
            typeSymbol.declAST = moduleDecl;
            typeSymbol.prettyName = moduleDecl.prettyName;
            scopeChain.scope.enter(scopeChain.container, ast, typeSymbol, context.checker.errorReporter, isExported || isGlobal, true, isAmbient);
            scopeChain.scope.enter(scopeChain.container, ast, typeSymbol, context.checker.errorReporter, isExported || isGlobal, false, isAmbient);
            modType.symbol = typeSymbol;
        }
        else {
            if (symbol && symbol.declAST && symbol.declAST.nodeType != NodeType.ModuleDeclaration) {
                context.checker.errorReporter.simpleError(moduleDecl, "Conflicting symbol name for module '" + modName + "'");
            }
            typeSymbol = symbol;
            // initialize new private scope for the type
            var publicEnclosedTypes = typeSymbol.type.getAllEnclosedTypes().publicMembers;
            var publicEnclosedTypesTable = (publicEnclosedTypes == null) ? new StringHashTable() : publicEnclosedTypes;
            var enclosedTypes = new ScopedMembers(new DualStringHashTable(publicEnclosedTypesTable, new StringHashTable()));
            var publicEnclosedAmbientTypes = typeSymbol.type.getAllAmbientEnclosedTypes().publicMembers;
            var publicAmbientEnclosedTypesTable = (publicEnclosedAmbientTypes == null) ? new StringHashTable() : publicEnclosedAmbientTypes;
            var ambientEnclosedTypes = new ScopedMembers(new DualStringHashTable(publicAmbientEnclosedTypesTable, new StringHashTable()));
            var publicMembers = typeSymbol.type.members.publicMembers;
            var publicMembersTable = (publicMembers == null) ? new StringHashTable() : publicMembers;
            var members = new ScopedMembers(new DualStringHashTable(publicMembersTable, new StringHashTable()));
            var publicAmbientMembers = typeSymbol.type.ambientMembers.publicMembers;
            var publicAmbientMembersTable = (publicAmbientMembers == null) ? new StringHashTable() : publicAmbientMembers;
            var ambientMembers = new ScopedMembers(new DualStringHashTable(publicAmbientMembersTable, new StringHashTable()));
            modType = new ModuleType(enclosedTypes, ambientEnclosedTypes);
            if (isEnum) {
                modType.typeFlags |= TypeFlags.IsEnum;
            }
            modType.members = members;
            modType.ambientMembers = ambientMembers;
            modType.setHasImplementation();
            modType.symbol = typeSymbol;
            typeSymbol.addLocation(moduleDecl.minChar);
            typeSymbol.expansions.push(modType);
        }
        if (context.scopeChain.moduleDecl) {
            context.scopeChain.moduleDecl.recordNonInterface();
        }
        // REVIEW: If multiple disparate module decls for the same module don't agree
        // in export privileges, how should we handle it?
        if (isExported) {
            typeSymbol.flags |= SymbolFlags.Exported;
        }
        if ((context.scopeChain.moduleDecl) ||
            (context.scopeChain.container == context.checker.gloMod)) {
            typeSymbol.flags |= SymbolFlags.ModuleMember;
        }
        moduleDecl.mod = modType;
        pushTypeCollectionScope(typeSymbol, modType.members, modType.ambientMembers, modType.enclosedTypes, modType.ambientEnclosedTypes, context, null, null, moduleDecl);
        return true;
    }
    TypeScript.preCollectModuleTypes = preCollectModuleTypes;
    function preCollectClassTypes(ast, parent, context) {
        var scopeChain = context.scopeChain;
        var classDecl = ast;
        var classType;
        var instanceType;
        var typeSymbol = null;
        var className = classDecl.name.text;
        var alreadyInScope = false;
        var isAmbient = hasFlag(classDecl.varFlags, VarFlags.Ambient);
        var isExported = hasFlag(classDecl.varFlags, VarFlags.Exported);
        var isGlobal = context.scopeChain.container == context.checker.gloMod;
        var containerMod = scopeChain.container;
        var foundValSymbol = false;
        typeSymbol = scopeChain.scope.findLocal(className, false, true);
        // check the value space, since an override may have been declared with the type's name
        // REVIEW-CLASSES
        if (!typeSymbol) {
            var valTypeSymbol = scopeChain.scope.findLocal(className, false, false);
            if (valTypeSymbol &&
                valTypeSymbol.isType() &&
                valTypeSymbol.declAST &&
                valTypeSymbol.declAST.nodeType == NodeType.FuncDecl &&
                valTypeSymbol.declAST.isSignature()) {
                typeSymbol = valTypeSymbol;
                foundValSymbol = true;
                if (isExported) {
                    typeSymbol.flags |= SymbolFlags.Exported;
                }
                if (isAmbient) {
                    typeSymbol.flags |= SymbolFlags.Ambient;
                }
                // the class was never entered into type space, so add it
                context.scopeChain.scope.enter(context.scopeChain.container, ast, typeSymbol, context.checker.errorReporter, isExported || isGlobal, true, isAmbient);
            }
        }
        if (typeSymbol && !foundValSymbol && (typeSymbol.declAST != classDecl)) {
            typeSymbol = null;
        }
        if (typeSymbol == null) {
            var valueSymbol = scopeChain.scope.findLocal(className, false, false);
            classType = new Type();
            classType.setHasImplementation();
            instanceType = new Type();
            instanceType.setHasImplementation();
            classType.instanceType = instanceType;
            classType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            classType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            addPrototypeField(classType, classDecl, context);
            instanceType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            instanceType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            typeSymbol = new TypeSymbol(className, classDecl.minChar, context.checker.locationInfo.unitIndex, classType);
            typeSymbol.declAST = classDecl;
            typeSymbol.instanceType = instanceType;
            classType.symbol = typeSymbol;
            instanceType.symbol = typeSymbol;
            if (context.scopeChain.moduleDecl) {
                context.scopeChain.moduleDecl.recordNonInterface();
                typeSymbol.declModule = context.scopeChain.moduleDecl;
                typeSymbol.flags |= SymbolFlags.ModuleMember;
            }
            if (isExported) {
                typeSymbol.flags |= SymbolFlags.Exported;
            }
            if (isAmbient) {
                typeSymbol.flags |= SymbolFlags.Ambient;
            }
            ast.type = classType;
            // class in both name spaces (type for instance type; constructor representative in value space)
            context.scopeChain.scope.enter(context.scopeChain.container, ast, typeSymbol, context.checker.errorReporter, isExported || isGlobal, true, isAmbient);
            if (valueSymbol == null) {
                context.scopeChain.scope.enter(context.scopeChain.container, ast, typeSymbol, context.checker.errorReporter, isExported || isGlobal, false, isAmbient);
            }
        }
        else {
            classType = typeSymbol.type;
            // If the instance type is null, a call overload was likely declared before the class constructor
            if (classType.instanceType == null) {
                classType.instanceType = new Type();
                classType.instanceType.setHasImplementation();
                classType.instanceType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                classType.instanceType.symbol = classType.symbol;
                classType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
                classType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            }
            instanceType = classType.instanceType;
            ast.type = classType;
        }
        // if the class has no declared constructor, either create a default signature or adapt 
        // it's base class's signature group
        if (!classDecl.constructorDecl) {
            if (typeSymbol && typeSymbol.declAST && typeSymbol.declAST.type && typeSymbol.declAST.type.call && !typeSymbol.declAST.isOverload) {
                context.checker.errorReporter.duplicateIdentifier(typeSymbol.declAST, typeSymbol.name);
            }
            createNewConstructGroupForType(classDecl.type);
        }
        classType.typeFlags |= TypeFlags.IsClass;
        instanceType.typeFlags |= TypeFlags.IsClass;
        getBases(instanceType, classDecl);
        pushTypeCollectionScope(typeSymbol, instanceType.members, instanceType.ambientMembers, null, null, context, instanceType, classType, null);
        return true;
    }
    TypeScript.preCollectClassTypes = preCollectClassTypes;
    function preCollectInterfaceTypes(ast, parent, context) {
        var scopeChain = context.scopeChain;
        var interfaceDecl = ast;
        var interfaceSymbol = null;
        var interfaceType = null;
        var isExported = hasFlag(interfaceDecl.varFlags, VarFlags.Exported);
        var isGlobal = context.scopeChain.container == context.checker.gloMod;
        var alreadyInScope = true;
        alreadyInScope = false;
        var interfaceName = interfaceDecl.name.text;
        interfaceSymbol = scopeChain.scope.findLocal(interfaceName, false, true);
        if (interfaceSymbol == null) {
            interfaceType = new Type();
            interfaceSymbol = new TypeSymbol(interfaceName, ast.minChar, context.checker.locationInfo.unitIndex, interfaceType);
            interfaceType.symbol = interfaceSymbol;
            // REVIEW: Shouldn't allocate another table for interface privates
            interfaceType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            interfaceType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable()));
            interfaceSymbol.declAST = interfaceDecl;
            interfaceSymbol.declModule = context.scopeChain.moduleDecl;
        }
        else {
            alreadyInScope = true;
            interfaceType = interfaceSymbol.type;
        }
        if (!interfaceType) {
            interfaceType = context.checker.anyType;
        }
        ast.type = interfaceType;
        getBases(interfaceType, interfaceDecl);
        if (isExported) {
            interfaceSymbol.flags |= SymbolFlags.Exported;
        }
        if (context.scopeChain.moduleDecl) {
            interfaceSymbol.flags |= SymbolFlags.ModuleMember;
        }
        if (!alreadyInScope) {
            context.scopeChain.scope.enter(context.scopeChain.container, ast, interfaceSymbol, context.checker.errorReporter, isGlobal || isExported, true, false); // REVIEW: Technically, interfaces should be ambient
        }
        pushTypeCollectionScope(interfaceSymbol, interfaceType.members, interfaceType.ambientMembers, null, null, context, interfaceType, null, null);
        return true;
    }
    TypeScript.preCollectInterfaceTypes = preCollectInterfaceTypes;
    function preCollectArgDeclTypes(ast, parent, context) {
        var scopeChain = context.scopeChain;
        var argDecl = ast;
        if (hasFlag(argDecl.varFlags, VarFlags.Public | VarFlags.Private)) {
            var field = new ValueLocation();
            var isPrivate = hasFlag(argDecl.varFlags, VarFlags.Private);
            var fieldSymbol = new FieldSymbol(argDecl.id.text, argDecl.minChar, context.checker.locationInfo.unitIndex, !hasFlag(argDecl.varFlags, VarFlags.Readonly), field);
            fieldSymbol.transferVarFlags(argDecl.varFlags);
            field.symbol = fieldSymbol;
            fieldSymbol.declAST = ast;
            argDecl.parameterPropertySym = fieldSymbol;
            context.scopeChain.scope.enter(context.scopeChain.container, ast, fieldSymbol, context.checker.errorReporter, !isPrivate, false, false);
            field.typeLink = getTypeLink(argDecl.typeExpr, context.checker, argDecl.init == null);
            argDecl.sym = fieldSymbol;
        }
        return false;
    }
    TypeScript.preCollectArgDeclTypes = preCollectArgDeclTypes;
    function preCollectVarDeclTypes(ast, parent, context) {
        var scopeChain = context.scopeChain;
        var varDecl = ast;
        var isAmbient = hasFlag(varDecl.varFlags, VarFlags.Ambient);
        var isExported = hasFlag(varDecl.varFlags, VarFlags.Exported);
        var isGlobal = context.scopeChain.container == context.checker.gloMod;
        var isProperty = hasFlag(varDecl.varFlags, VarFlags.Property);
        var isStatic = hasFlag(varDecl.varFlags, VarFlags.Static);
        var isPrivate = hasFlag(varDecl.varFlags, VarFlags.Private);
        var isOptional = hasFlag(varDecl.id.flags, ASTFlags.OptionalName);
        if (context.scopeChain.moduleDecl) {
            context.scopeChain.moduleDecl.recordNonInterface();
        }
        if (isProperty ||
            isExported ||
            (context.scopeChain.container == context.checker.gloMod) ||
            context.scopeChain.moduleDecl) {
            if (isAmbient) {
                var existingSym = scopeChain.scope.findLocal(varDecl.id.text, false, false);
                if (existingSym) {
                    varDecl.sym = existingSym;
                    return false;
                }
            }
            // Defensive error detection...
            if (varDecl.id == null) {
                context.checker.errorReporter.simpleError(varDecl, "Expected variable identifier at this location");
                return false;
            }
            var field = new ValueLocation();
            var fieldSymbol = new FieldSymbol(varDecl.id.text, varDecl.minChar, context.checker.locationInfo.unitIndex, (varDecl.varFlags & VarFlags.Readonly) == VarFlags.None, field);
            fieldSymbol.transferVarFlags(varDecl.varFlags);
            if (isOptional) {
                fieldSymbol.flags |= SymbolFlags.Optional;
            }
            field.symbol = fieldSymbol;
            fieldSymbol.declAST = ast;
            if ((context.scopeChain.moduleDecl) ||
                (context.scopeChain.container == context.checker.gloMod)) {
                fieldSymbol.flags |= SymbolFlags.ModuleMember;
                fieldSymbol.declModule = context.scopeChain.moduleDecl;
            }
            // if it's static, enter it into the class's member list directly
            if (hasFlag(varDecl.varFlags, VarFlags.Property) && isStatic && context.scopeChain.classType) {
                if (!context.scopeChain.classType.members.publicMembers.add(varDecl.id.text, fieldSymbol)) {
                    context.checker.errorReporter.duplicateIdentifier(ast, fieldSymbol.name);
                }
                fieldSymbol.container = context.scopeChain.classType.symbol;
            }
            else {
                context.scopeChain.scope.enter(context.scopeChain.container, ast, fieldSymbol, context.checker.errorReporter, !isPrivate && (isProperty || isExported || isGlobal || isStatic), false, isAmbient);
            }
            if (hasFlag(varDecl.varFlags, VarFlags.Exported)) {
                fieldSymbol.flags |= SymbolFlags.Exported;
            }
            field.typeLink = getTypeLink(varDecl.typeExpr, context.checker, varDecl.init == null);
            varDecl.sym = fieldSymbol;
        }
        return false;
    }
    TypeScript.preCollectVarDeclTypes = preCollectVarDeclTypes;
    function preCollectFuncDeclTypes(ast, parent, context) {
        var scopeChain = context.scopeChain;
        // REVIEW: This will have to change when we move to "export"
        if (context.scopeChain.moduleDecl) {
            context.scopeChain.moduleDecl.recordNonInterface();
        }
        var funcDecl = ast;
        var fgSym = null;
        var nameText = funcDecl.getNameText();
        var isExported = hasFlag(funcDecl.fncFlags, FncFlags.Exported | FncFlags.ClassPropertyMethodExported);
        var isStatic = hasFlag(funcDecl.fncFlags, FncFlags.Static);
        var isPrivate = hasFlag(funcDecl.fncFlags, FncFlags.Private);
        var isConstructor = funcDecl.isConstructMember() || funcDecl.isConstructor;
        var containerSym = (((funcDecl.isMethod() && isStatic) || funcDecl.isAccessor()) && context.scopeChain.classType ? context.scopeChain.classType.symbol : context.scopeChain.container);
        var containerScope = context.scopeChain.scope;
        var isGlobal = containerSym == context.checker.gloMod;
        var isOptional = funcDecl.name && hasFlag(funcDecl.name.flags, ASTFlags.OptionalName);
        var go = false;
        var foundSymbol = false;
        // If this is a class constructor, the "container" is actually the class declaration
        if (isConstructor && hasFlag(funcDecl.fncFlags, FncFlags.ClassMethod)) {
            containerSym = containerSym.container;
            containerScope = scopeChain.previous.scope;
        }
        funcDecl.unitIndex = context.checker.locationInfo.unitIndex;
        // If the parent is the constructor, and this isn't an instance method, skip it.
        // That way, we'll set the type during scope assignment, and can be sure that the
        // function will be placed in the constructor-local scope
        if (!funcDecl.isConstructor &&
            containerSym &&
            containerSym.declAST &&
            containerSym.declAST.nodeType == NodeType.FuncDecl &&
            containerSym.declAST.isConstructor &&
            !funcDecl.isMethod()) {
            return go;
        }
        // Interfaces and overloads
        if (hasFlag(funcDecl.fncFlags, FncFlags.Signature)) {
            var instType = context.scopeChain.thisType;
            // If the function is static, search in the class type's
            if (nameText && nameText != "__missing") {
                if (isStatic) {
                    fgSym = containerSym.type.members.allMembers.lookup(nameText);
                }
                else {
                    // REVIEW: This logic should be symmetric with preCollectClassTypes
                    fgSym = containerScope.findLocal(nameText, false, false);
                    // If we could not find the function symbol in the value context, look
                    // in the type context.
                    // This would be the case, for example, if a class constructor override
                    // were declared before a call override for a given class
                    if (fgSym == null) {
                        fgSym = containerScope.findLocal(nameText, false, true);
                    }
                }
                if (fgSym) {
                    foundSymbol = true;
                    // We'll combine ambient and non-ambient funcdecls during typecheck (for contextual typing).,
                    // So, if they don't agree, don't use the symbol we've found                    
                    if (!funcDecl.isSignature() && (hasFlag(funcDecl.fncFlags, FncFlags.Ambient) != hasFlag(fgSym.flags, SymbolFlags.Ambient))) {
                        fgSym = null;
                    }
                }
            }
            // a function with this symbol has not yet been declared in this scope
            // REVIEW: In the code below, we need to ensure that only function overloads are considered
            //  (E.g., if a vardecl has the same id as a function or class, we may use the vardecl symbol
            //  as the overload.)  Defensively, however, the vardecl won't have a type yet, so it should
            //  suffice to just check for a null type when considering the overload symbol in
            //  createFunctionSignature
            if (fgSym == null) {
                if (!(funcDecl.isSpecialFn())) {
                    fgSym = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, null, !foundSymbol).declAST.type.symbol;
                }
                else {
                    fgSym = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, containerSym, false).declAST.type.symbol;
                }
                // set the symbol's declAST, which will point back to the first declaration (symbol or otherwise)
                // related to this symbol
                if (fgSym.declAST == null || !funcDecl.isSpecialFn()) {
                    fgSym.declAST = ast;
                }
            }
            else {
                if ((fgSym.kind() == SymbolKind.Type)) {
                    fgSym = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, fgSym, false).declAST.type.symbol;
                }
                else {
                    context.checker.errorReporter.simpleError(funcDecl, "Function or method '" + funcDecl.name.actualText + "' already declared as a property");
                }
            }
            if (funcDecl.isSpecialFn() && !isStatic) {
                funcDecl.type = instType ? instType : fgSym.type;
            }
            else {
                funcDecl.type = fgSym.type;
            }
        }
        else {
            // declarations
            if (nameText) {
                if (isStatic) {
                    fgSym = containerSym.type.members.allMembers.lookup(nameText);
                }
                else {
                    // in the constructor case, we want to check the parent scope for overloads
                    if (funcDecl.isConstructor && context.scopeChain.previous) {
                        fgSym = context.scopeChain.previous.scope.findLocal(nameText, false, false);
                    }
                    if (fgSym == null) {
                        fgSym = containerScope.findLocal(nameText, false, false);
                    }
                }
                if (fgSym) {
                    foundSymbol = true;
                    if (!isConstructor && fgSym.declAST.nodeType == NodeType.FuncDecl && !fgSym.declAST.isAccessor() && !fgSym.declAST.isSignature()) {
                        fgSym = null;
                        foundSymbol = false;
                    }
                }
            }
            // REVIEW: Move this check into the typecheck phase?  It's only being run over properties...
            if (fgSym &&
                !fgSym.isAccessor() &&
                fgSym.type &&
                fgSym.type.construct &&
                fgSym.type.construct.signatures != [] &&
                (fgSym.type.construct.signatures[0].declAST == null ||
                    !hasFlag(fgSym.type.construct.signatures[0].declAST.fncFlags, FncFlags.Ambient)) &&
                !funcDecl.isConstructor) {
                context.checker.errorReporter.simpleError(funcDecl, "Functions may not have class overloads");
            }
            if (fgSym && !(fgSym.kind() == SymbolKind.Type) && funcDecl.isMethod() && !funcDecl.isAccessor() && !funcDecl.isConstructor) {
                context.checker.errorReporter.simpleError(funcDecl, "Function or method '" + funcDecl.name.actualText + "' already declared as a property");
                fgSym.type = context.checker.anyType;
            }
            var sig = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, fgSym, !foundSymbol);
            // it's a getter or setter function                                   
            if (((!fgSym || fgSym.declAST.nodeType != NodeType.FuncDecl) && funcDecl.isAccessor()) || (fgSym && fgSym.isAccessor())) {
                funcDecl.accessorSymbol = context.checker.createAccessorSymbol(funcDecl, fgSym, containerSym.type, (funcDecl.isMethod() && isStatic), true, containerScope, containerSym);
            }
            funcDecl.type.symbol.declAST = ast;
            if (funcDecl.isConstructor) {
                go = true;
            }
            ;
        }
        if (isExported) {
            if (funcDecl.type.call) {
                funcDecl.type.symbol.flags |= SymbolFlags.Exported;
            }
            // Accessors are set to 'exported' above
            if (fgSym && !fgSym.isAccessor() && fgSym.kind() == SymbolKind.Type && fgSym.type.call) {
                fgSym.flags |= SymbolFlags.Exported;
            }
        }
        if (context.scopeChain.moduleDecl && !funcDecl.isSpecialFn()) {
            funcDecl.type.symbol.flags |= SymbolFlags.ModuleMember;
            funcDecl.type.symbol.declModule = context.scopeChain.moduleDecl;
        }
        if (fgSym && isOptional) {
            fgSym.flags |= SymbolFlags.Optional;
        }
        return go;
    }
    TypeScript.preCollectFuncDeclTypes = preCollectFuncDeclTypes;
    function preCollectTypes(ast, parent, walker) {
        var context = walker.state;
        var go = false;
        var scopeChain = context.scopeChain;
        if (ast.nodeType == NodeType.Script) {
            var script = ast;
            context.script = script;
            go = true;
        }
        else if (ast.nodeType == NodeType.List) {
            go = true;
        }
        else if (ast.nodeType == NodeType.ImportDeclaration) {
            go = preCollectImportTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.With) {
            go = false;
        }
        else if (ast.nodeType == NodeType.ModuleDeclaration) {
            go = preCollectModuleTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.ClassDeclaration) {
            go = preCollectClassTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.Block) {
            go = true;
        }
        else if (ast.nodeType == NodeType.InterfaceDeclaration) {
            go = preCollectInterfaceTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.ArgDecl) {
            go = preCollectArgDeclTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.VarDecl) {
            go = preCollectVarDeclTypes(ast, parent, context);
        }
        else if (ast.nodeType == NodeType.FuncDecl) {
            go = preCollectFuncDeclTypes(ast, parent, context);
        }
        else {
            if (ast.isStatementOrExpression() && context.scopeChain.moduleDecl) {
                context.scopeChain.moduleDecl.recordNonInterface();
            }
        }
        walker.options.goChildren = go;
        return ast;
    }
    TypeScript.preCollectTypes = preCollectTypes;
    function postCollectTypes(ast, parent, walker) {
        var context = walker.state;
        if (ast.nodeType == NodeType.ModuleDeclaration) {
            popTypeCollectionScope(context);
        }
        else if (ast.nodeType == NodeType.ClassDeclaration) {
            popTypeCollectionScope(context);
        }
        else if (ast.nodeType == NodeType.InterfaceDeclaration) {
            popTypeCollectionScope(context);
        }
        return ast;
    }
    TypeScript.postCollectTypes = postCollectTypes;
})(TypeScript || (TypeScript = {}));
