1// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. 2// See LICENSE.txt in the project root for complete license information. 3 4///<reference path='typescript.ts' /> 5 6module TypeScript { 7 export class Binder { 8 constructor (public checker: TypeChecker) { } 9 public resolveBaseTypeLinks(typeLinks: TypeLink[], scope: SymbolScope) { 10 var extendsList: Type[] = null; 11 if (typeLinks) { 12 extendsList = new Type[]; 13 for (var i = 0, len = typeLinks.length; i < len; i++) { 14 var typeLink = typeLinks[i]; 15 this.checker.resolvingBases = true; 16 this.checker.resolveTypeLink(scope, typeLink, true); 17 this.checker.resolvingBases = false; 18 if (typeLink.type.isClass()) { 19 extendsList[i] = typeLink.type.instanceType; 20 } 21 else { 22 extendsList[i] = typeLink.type; 23 } 24 } 25 } 26 return extendsList; 27 } 28 29 public resolveBases(scope: SymbolScope, type: Type) { 30 type.extendsList = this.resolveBaseTypeLinks(type.extendsTypeLinks, scope); 31 32 var i = 0, len = type.extendsList.length; 33 var derivedIsClass = type.isClassInstance(); 34 for (; i < len; i++) { 35 var baseIsClass = type.extendsList[i].isClassInstance(); 36 if (type.extendsList[i] != this.checker.anyType) { 37 if (derivedIsClass) { 38 if (!baseIsClass) { 39 this.checker.errorReporter.simpleErrorFromSym(type.symbol, 40 "A export class may only extend other classes, " + type.extendsList[i].symbol.fullName() + " is an interface."); 41 } 42 } 43 else { 44 if (baseIsClass) { 45 this.checker.errorReporter.simpleErrorFromSym(type.symbol, 46 "An interface may only extend other interfaces, " + type.extendsList[i].symbol.fullName() + " is a class."); 47 } 48 } 49 } 50 } 51 52 type.implementsList = this.resolveBaseTypeLinks(type.implementsTypeLinks, scope); 53 54 if (type.implementsList) { 55 for (i = 0, len = type.implementsList.length; i < len; i++) { 56 var iface = type.implementsList[i]; 57 if (iface.isClassInstance()) { 58 if (derivedIsClass) { 59 this.checker.errorReporter.simpleErrorFromSym(type.symbol, 60 "A class may only implement an interface; " + iface.symbol.fullName() + " is a class."); 61 } 62 } 63 } 64 } 65 } 66 67 public resolveSignatureGroup(signatureGroup: SignatureGroup, scope: SymbolScope, instanceType: Type) { 68 var supplyVar = !(signatureGroup.hasImplementation); 69 for (var i = 0, len = signatureGroup.signatures.length; i < len; i++) { 70 var signature = signatureGroup.signatures[i]; 71 if (instanceType) { 72 signature.returnType.type = instanceType; 73 } 74 else { 75 this.checker.resolveTypeLink(scope, signature.returnType, supplyVar); 76 } 77 var paramLen = signature.parameters.length; 78 for (var j = 0; j < paramLen; j++) { 79 this.bindSymbol(scope, signature.parameters[j]); 80 } 81 if (signature.hasVariableArgList) { 82 // check that last parameter has an array type 83 var lastParam = <ParameterSymbol>signature.parameters[paramLen - 1]; 84 lastParam.argsOffset = paramLen - 1; 85 if (!lastParam.getType().isArray()) { 86 this.checker.errorReporter.simpleErrorFromSym(lastParam, 87 "... parameter must have array type"); 88 lastParam.parameter.typeLink.type = this.checker.makeArrayType(lastParam.parameter.typeLink.type); 89 } 90 } 91 } 92 } 93 94 public bindType(scope: SymbolScope, type: Type, instanceType: Type): void { 95 if (instanceType) { 96 this.bindType(scope, instanceType, null); 97 } 98 if (type.hasMembers()) { 99 var members = type.members; 100 var ambientMembers = type.ambientMembers; 101 var typeMembers = type.getAllEnclosedTypes(); // REVIEW: Should only be getting exported types? 102 var ambientTypeMembers = type.getAllAmbientEnclosedTypes(); // REVIEW: Should only be getting exported types? 103 var memberScope = new SymbolTableScope(members, ambientMembers, typeMembers, ambientTypeMembers, type.symbol); 104 var agg = new SymbolAggregateScope(type.symbol); 105 var prevCurrentModDecl = this.checker.currentModDecl; 106 var prevBindStatus = this.checker.inBind; 107 agg.addParentScope(memberScope); 108 agg.addParentScope(scope); 109 if (type.isModuleType()) { 110 this.checker.currentModDecl = <ModuleDeclaration>type.symbol.declAST; 111 this.checker.inBind = true; 112 } 113 if (members) { 114 this.bind(agg, type.members.allMembers); // REVIEW: Should only be getting exported types? 115 } 116 if (typeMembers) { 117 this.bind(agg, typeMembers.allMembers); 118 } 119 if (ambientMembers) { 120 this.bind(agg, ambientMembers.allMembers); 121 } 122 if (ambientTypeMembers) { 123 this.bind(agg, ambientTypeMembers.allMembers); 124 } 125 this.checker.currentModDecl = prevCurrentModDecl; 126 this.checker.inBind = prevBindStatus; 127 } 128 if (type.extendsTypeLinks) { 129 this.resolveBases(scope, type); 130 } 131 if (type.construct) { 132 this.resolveSignatureGroup(type.construct, scope, instanceType); 133 } 134 if (type.call) { 135 this.resolveSignatureGroup(type.call, scope, null); 136 } 137 if (type.index) { 138 this.resolveSignatureGroup(type.index, scope, null); 139 } 140 if (type.elementType) { 141 this.bindType(scope, type.elementType, null); 142 } 143 } 144 145 public bindSymbol(scope: SymbolScope, symbol: Symbol) { 146 if (!symbol.bound) { 147 var prevLocationInfo = this.checker.locationInfo; 148 if ((this.checker.units) && (symbol.unitIndex >= 0) && (symbol.unitIndex < this.checker.units.length)) { 149 this.checker.locationInfo = this.checker.units[symbol.unitIndex]; 150 } 151 switch (symbol.kind()) { 152 case SymbolKind.Type: 153 154 if (symbol.flags & SymbolFlags.Bound) { 155 break; 156 } 157 158 var typeSymbol = <TypeSymbol>symbol; 159 typeSymbol.flags |= SymbolFlags.Bound; 160 161 // Since type collection happens out of order, a dynamic module referenced by an import statement 162 // may not yet be in scope when the import symbol is created. In that case, we need to search 163 // out the module symbol now 164 // Note that we'll also want to do this in resolveTypeMembers, in case the symbol is set outside the 165 // context of a given module (E.g., an outer import statement) 166 if (typeSymbol.aliasLink && !typeSymbol.type && typeSymbol.aliasLink.alias.nodeType == NodeType.Name) { 167 var modPath = (<Identifier>typeSymbol.aliasLink.alias).text; 168 var modSym = this.checker.findSymbolForDynamicModule(modPath, this.checker.locationInfo.filename, (id) => scope.find(id, false, true)); 169 if (modSym) { 170 typeSymbol.type = modSym.getType(); 171 } 172 } 173 174 if (typeSymbol.type && typeSymbol.type != this.checker.gloModType) { 175 this.bindType(scope, typeSymbol.type, typeSymbol.instanceType); 176 177 // bind expansions on the parent type symbol 178 if (typeSymbol.type.isModuleType()) { 179 for (var i = 0; i < typeSymbol.expansions.length; i++) { 180 this.bindType(scope, typeSymbol.expansions[i], typeSymbol.instanceType); 181 } 182 } 183 } 184 break; 185 case SymbolKind.Field: 186 this.checker.resolveTypeLink(scope, (<FieldSymbol>symbol).field.typeLink, 187 false); 188 break; 189 case SymbolKind.Parameter: 190 this.checker.resolveTypeLink(scope, 191 (<ParameterSymbol>symbol).parameter.typeLink, 192 true); 193 break; 194 } 195 this.checker.locationInfo = prevLocationInfo; 196 } 197 symbol.bound = true; 198 } 199 200 public bind(scope: SymbolScope, table: IHashTable) { 201 table.map( 202 (key, sym, binder) => { 203 binder.bindSymbol(scope, sym); 204 }, 205 this); 206 } 207 } 208 209}