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 8 export class AssignScopeContext { 9 constructor (public scopeChain: ScopeChain, 10 public typeFlow: TypeFlow, 11 public modDeclChain: ModuleDeclaration[]) { 12 } 13 } 14 15 export function pushAssignScope(scope: SymbolScope, 16 context: AssignScopeContext, 17 type: Type, 18 classType: Type, 19 fnc: FuncDecl) { 20 21 var chain = new ScopeChain(null, context.scopeChain, scope); 22 chain.thisType = type; 23 chain.classType = classType; 24 chain.fnc = fnc; 25 context.scopeChain = chain; 26 } 27 28 export function popAssignScope(context: AssignScopeContext) { 29 context.scopeChain = context.scopeChain.previous; 30 } 31 32 export function instanceCompare(a: Symbol, b: Symbol) { 33 if (((a == null) || (!a.isInstanceProperty()))) { 34 return b; 35 } 36 else { 37 return a; 38 } 39 } 40 41 export function instanceFilterStop(s: Symbol) { 42 return s.isInstanceProperty(); 43 } 44 45 export class ScopeSearchFilter { 46 47 constructor (public select: (a: Symbol, b: Symbol) =>Symbol, 48 public stop: (s: Symbol) =>boolean) { } 49 50 public result: Symbol = null; 51 52 public reset() { 53 this.result = null; 54 } 55 56 public update(b: Symbol): boolean { 57 this.result = this.select(this.result, b); 58 if (this.result) { 59 return this.stop(this.result); 60 } 61 else { 62 return false; 63 } 64 } 65 } 66 67 export var instanceFilter = new ScopeSearchFilter(instanceCompare, instanceFilterStop); 68 69 export function preAssignModuleScopes(ast: AST, context: AssignScopeContext) { 70 var moduleDecl = <ModuleDeclaration>ast; 71 var memberScope: SymbolTableScope = null; 72 var aggScope: SymbolAggregateScope = null; 73 74 if (moduleDecl.name && moduleDecl.mod) { 75 moduleDecl.name.sym = moduleDecl.mod.symbol; 76 } 77 78 var mod = moduleDecl.mod; 79 80 // We're likely here because of error recovery 81 if (!mod) { 82 return; 83 } 84 85 memberScope = new SymbolTableScope(mod.members, mod.ambientMembers, mod.enclosedTypes, mod.ambientEnclosedTypes, mod.symbol); 86 mod.memberScope = memberScope; 87 context.modDeclChain.push(moduleDecl); 88 context.typeFlow.checker.currentModDecl = moduleDecl; 89 aggScope = new SymbolAggregateScope(mod.symbol); 90 aggScope.addParentScope(memberScope); 91 aggScope.addParentScope(context.scopeChain.scope); 92 pushAssignScope(aggScope, context, null, null, null); 93 mod.containedScope = aggScope; 94 if (mod.symbol) { 95 context.typeFlow.addLocalsFromScope(mod.containedScope, mod.symbol, moduleDecl.vars, mod.members.privateMembers, true); 96 } 97 } 98 99 export function preAssignClassScopes(ast: AST, context: AssignScopeContext) { 100 var classDecl = <InterfaceDeclaration>ast; 101 var memberScope: SymbolTableScope = null; 102 var aggScope: SymbolAggregateScope = null; 103 104 if (classDecl.name && classDecl.type) { 105 classDecl.name.sym = classDecl.type.symbol; 106 } 107 108 var classType = ast.type; 109 110 if (classType) { 111 var classSym = classType.symbol; 112 memberScope = <SymbolTableScope>context.typeFlow.checker.scopeOf(classType); 113 114 aggScope = new SymbolAggregateScope(classType.symbol); 115 aggScope.addParentScope(memberScope); 116 aggScope.addParentScope(context.scopeChain.scope); 117 118 classType.containedScope = aggScope; 119 classType.memberScope = memberScope; 120 121 var instanceType = classType.instanceType; 122 memberScope = <SymbolTableScope>context.typeFlow.checker.scopeOf(instanceType); 123 instanceType.memberScope = memberScope; 124 125 aggScope = new SymbolAggregateScope(instanceType.symbol); 126 aggScope.addParentScope(context.scopeChain.scope); 127 128 pushAssignScope(aggScope, context, instanceType, classType, null); 129 instanceType.containedScope = aggScope; 130 } 131 else { 132 ast.type = context.typeFlow.anyType; 133 } 134 } 135 136 export function preAssignInterfaceScopes(ast: AST, context: AssignScopeContext) { 137 var interfaceDecl = <InterfaceDeclaration>ast; 138 var memberScope: SymbolTableScope = null; 139 var aggScope: SymbolAggregateScope = null; 140 141 if (interfaceDecl.name && interfaceDecl.type) { 142 interfaceDecl.name.sym = interfaceDecl.type.symbol; 143 } 144 145 var interfaceType = ast.type; 146 memberScope = <SymbolTableScope>context.typeFlow.checker.scopeOf(interfaceType); 147 interfaceType.memberScope = memberScope; 148 aggScope = new SymbolAggregateScope(interfaceType.symbol); 149 aggScope.addParentScope(memberScope); 150 aggScope.addParentScope(context.scopeChain.scope); 151 pushAssignScope(aggScope, context, null, null, null); 152 interfaceType.containedScope = aggScope; 153 } 154 155 export function preAssignWithScopes(ast: AST, context: AssignScopeContext) { 156 var withStmt = <WithStatement>ast; 157 var withType = withStmt.type; 158 159 var members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 160 var ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 161 162 var withType = new Type(); 163 var withSymbol = new WithSymbol(withStmt.minChar, context.typeFlow.checker.locationInfo.unitIndex, withType); 164 withType.members = members; 165 withType.ambientMembers = ambientMembers; 166 withType.symbol = withSymbol; 167 withType.setHasImplementation(); 168 withStmt.type = withType; 169 170 var withScope = new TypeScript.SymbolScopeBuilder(withType.members, withType.ambientMembers, null, null, context.scopeChain.scope, withType.symbol); 171 172 pushAssignScope(withScope, context, null, null, null); 173 withType.containedScope = withScope; 174 } 175 176 export function preAssignFuncDeclScopes(ast: AST, context: AssignScopeContext) { 177 var funcDecl = <FuncDecl>ast; 178 179 var container: Symbol = null; 180 var localContainer: Symbol = null; 181 if (funcDecl.type) { 182 localContainer = ast.type.symbol; 183 } 184 185 var isStatic = hasFlag(funcDecl.fncFlags, FncFlags.Static); 186 var isInnerStatic = isStatic && context.scopeChain.fnc != null; 187 // for inner static functions, use the parent's member scope, so local vars cannot be captured 188 var parentScope = isInnerStatic ? context.scopeChain.fnc.type.memberScope : context.scopeChain.scope; 189 190 // if this is not a method, but enclosed by class, use constructor as 191 // the enclosing scope 192 // REVIEW: Some twisted logic here - this needs to be cleaned up once old classes are removed 193 // - if it's a new class, always use the contained scope, since we initialize the constructor scope below 194 if (context.scopeChain.thisType && 195 (!funcDecl.isConstructor || hasFlag(funcDecl.fncFlags, FncFlags.ClassMethod))) { 196 var instType = context.scopeChain.thisType; 197 198 if (!(instType.typeFlags & TypeFlags.IsClass) && !hasFlag(funcDecl.fncFlags, FncFlags.ClassMethod)) { 199 if (!funcDecl.isMethod() || isStatic) { 200 parentScope = instType.constructorScope; 201 } 202 else { 203 // use constructor scope if a method as well 204 parentScope = instType.containedScope; 205 } 206 } 207 else { 208 if (context.scopeChain.previous.scope.container && 209 context.scopeChain.previous.scope.container.declAST && 210 context.scopeChain.previous.scope.container.declAST.nodeType == NodeType.FuncDecl && 211 (<FuncDecl>context.scopeChain.previous.scope.container.declAST).isConstructor) { 212 213 // if the parent is the class constructor, use the constructor scope 214 parentScope = instType.constructorScope; 215 } 216 else if (isStatic && context.scopeChain.classType) { 217 parentScope = context.scopeChain.classType.containedScope; 218 } 219 else { 220 // else, use the contained scope 221 parentScope = instType.containedScope; 222 } 223 } 224 container = instType.symbol; 225 } 226 else if (funcDecl.isConstructor && context.scopeChain.thisType) { 227 // sets the container to the class type's symbol (which is shared by the instance type) 228 container = context.scopeChain.thisType.symbol; 229 } 230 231 if (funcDecl.type == null || hasFlag(funcDecl.type.symbol.flags, SymbolFlags.TypeSetDuringScopeAssignment)) { 232 if (context.scopeChain.fnc && context.scopeChain.fnc.type) { 233 container = context.scopeChain.fnc.type.symbol; 234 } 235 236 var funcScope = null; 237 var outerFnc: FuncDecl = context.scopeChain.fnc; 238 var nameText = funcDecl.name ? funcDecl.name.actualText : null; 239 var fgSym: TypeSymbol = null; 240 241 if (isStatic) { 242 // In the case of function-nested statics, no member list will have bee initialized for the function, so we need 243 // to copy it over. We don't set this by default because having a non-null member list will throw off assignment 244 // compatibility tests 245 if (outerFnc.type.members == null && container.getType().memberScope) { 246 outerFnc.type.members = (<SymbolScopeBuilder>(<TypeSymbol>container).type.memberScope).valueMembers; 247 } 248 funcScope = context.scopeChain.fnc.type.memberScope; 249 outerFnc.innerStaticFuncs[outerFnc.innerStaticFuncs.length] = funcDecl; 250 } 251 else { 252 253 if (!funcDecl.isConstructor && 254 container && 255 container.declAST && 256 container.declAST.nodeType == NodeType.FuncDecl && 257 (<FuncDecl>container.declAST).isConstructor && 258 !funcDecl.isMethod()) { 259 funcScope = context.scopeChain.thisType.constructorScope;//locals; 260 } 261 else { 262 funcScope = context.scopeChain.scope; 263 } 264 } 265 266 // REVIEW: We don't search for another sym for accessors to prevent us from 267 // accidentally coalescing function signatures with the same name (E.g., a function 268 // 'f' the outer scope and a setter 'f' in an object literal within that scope) 269 if (nameText && nameText != "__missing" && !funcDecl.isAccessor()) { 270 if (isStatic) { 271 fgSym = funcScope.findLocal(nameText, false, false); 272 } 273 else { 274 // REVIEW: This logic should be symmetric with preCollectClassTypes 275 fgSym = funcScope.findLocal(nameText, false, false); 276 } 277 } 278 279 context.typeFlow.checker.createFunctionSignature(funcDecl, container, 280 funcScope, fgSym, fgSym == null); 281 282 // it's a getter or setter for a class property 283 if (!funcDecl.accessorSymbol && 284 (funcDecl.fncFlags & FncFlags.ClassMethod) && 285 container && 286 ((!fgSym || fgSym.declAST.nodeType != NodeType.FuncDecl) && funcDecl.isAccessor()) || 287 (fgSym && fgSym.isAccessor())) 288 { 289 funcDecl.accessorSymbol = context.typeFlow.checker.createAccessorSymbol(funcDecl, fgSym, container.getType(), (funcDecl.isMethod() && isStatic), true, funcScope, container); 290 } 291 292 funcDecl.type.symbol.flags |= SymbolFlags.TypeSetDuringScopeAssignment; 293 } 294 295 // Set the symbol for functions and their overloads 296 if (funcDecl.name && funcDecl.type) { 297 funcDecl.name.sym = funcDecl.type.symbol; 298 } 299 300 // Keep track of the original scope type, because target typing might override 301 // the "type" member. We need the original "Scope type" for completion list, etc. 302 funcDecl.scopeType = funcDecl.type; 303 304 // Overloads have no scope, so bail here 305 if (funcDecl.isOverload) { 306 return; 307 } 308 309 var funcTable = new StringHashTable(); 310 var funcMembers = new ScopedMembers(new DualStringHashTable(funcTable, new StringHashTable())); 311 var ambientFuncTable = new StringHashTable(); 312 var ambientFuncMembers = new ScopedMembers(new DualStringHashTable(ambientFuncTable, new StringHashTable())); 313 var funcStaticTable = new StringHashTable(); 314 var funcStaticMembers = new ScopedMembers(new DualStringHashTable(funcStaticTable, new StringHashTable())); 315 var ambientFuncStaticTable = new StringHashTable(); 316 var ambientFuncStaticMembers = new ScopedMembers(new DualStringHashTable(ambientFuncStaticTable, new StringHashTable())); 317 318 // REVIEW: Is it a problem that this is being set twice for properties and constructors? 319 funcDecl.unitIndex = context.typeFlow.checker.locationInfo.unitIndex; 320 321 var locals = new SymbolScopeBuilder(funcMembers, ambientFuncMembers, null, null, parentScope, localContainer); 322 var statics = new SymbolScopeBuilder(funcStaticMembers, ambientFuncStaticMembers, null, null, parentScope, null); 323 324 if (funcDecl.isConstructor && context.scopeChain.thisType) { 325 context.scopeChain.thisType.constructorScope = locals; 326 } 327 328 // basically, there are two problems 329 // - Above, for new classes, we were overwriting the constructor scope with the containing scope. This caused constructor params to be 330 // in scope everywhere 331 // - Below, we're setting the contained scope table to the same table we were overwriting the constructor scope with, which we need to 332 // fish lambda params, etc, out (see funcTable below) 333 // 334 // A good first approach to solving this would be to change addLocalsFromScope to take a scope instead of a table, and add to the 335 // constructor scope as appropriate 336 337 funcDecl.symbols = funcTable; 338 339 if (!funcDecl.isSpecialFn()) { 340 var group = funcDecl.type; 341 var signature = funcDecl.signature; 342 343 if (!funcDecl.isConstructor) { 344 group.containedScope = locals; 345 locals.container = group.symbol; 346 347 group.memberScope = statics; 348 statics.container = group.symbol; 349 } 350 funcDecl.enclosingFnc = context.scopeChain.fnc; 351 group.enclosingType = isStatic ? context.scopeChain.classType : context.scopeChain.thisType; 352 // for mapping when type checking 353 var fgSym = <TypeSymbol>ast.type.symbol; 354 if (((funcDecl.fncFlags & FncFlags.Signature) == FncFlags.None) && funcDecl.vars) { 355 context.typeFlow.addLocalsFromScope(locals, fgSym, funcDecl.vars, 356 funcTable, false); 357 context.typeFlow.addLocalsFromScope(statics, fgSym, funcDecl.statics, 358 funcStaticTable, false); 359 } 360 if (signature.parameters) { 361 var len = signature.parameters.length; 362 for (var i = 0; i < len; i++) { 363 var paramSym: ParameterSymbol = signature.parameters[i]; 364 context.typeFlow.checker.resolveTypeLink(locals, 365 paramSym.parameter.typeLink, true); 366 } 367 } 368 context.typeFlow.checker.resolveTypeLink(locals, signature.returnType, 369 funcDecl.isSignature()); 370 } 371 372 if (!funcDecl.isConstructor || hasFlag(funcDecl.fncFlags, FncFlags.ClassMethod)) { 373 var thisType = (funcDecl.isConstructor && hasFlag(funcDecl.fncFlags, FncFlags.ClassMethod)) ? context.scopeChain.thisType : null; 374 pushAssignScope(locals, context, thisType, null, funcDecl); 375 } 376 } 377 378 export function preAssignCatchScopes(ast: AST, context: AssignScopeContext) { 379 var catchBlock = <Catch>ast; 380 if (catchBlock.param) { 381 var catchTable = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); // REVIEW: Should we be allocating a public table instead of a private one? 382 var catchLocals = new SymbolScopeBuilder(catchTable, null, null, null, context.scopeChain.scope, 383 context.scopeChain.scope.container); 384 catchBlock.containedScope = catchLocals; 385 pushAssignScope(catchLocals, context, context.scopeChain.thisType, context.scopeChain.classType, context.scopeChain.fnc); 386 } 387 } 388 389 export function preAssignScopes(ast: AST, parent: AST, walker: IAstWalker) { 390 var context:AssignScopeContext = walker.state; 391 var go = true; 392 393 if (ast) { 394 if (ast.nodeType == NodeType.List) { 395 var list = <ASTList>ast; 396 list.enclosingScope = context.scopeChain.scope; 397 } 398 else if (ast.nodeType == NodeType.ModuleDeclaration) { 399 preAssignModuleScopes(ast, context); 400 } 401 else if (ast.nodeType == NodeType.ClassDeclaration) { 402 preAssignClassScopes(ast, context); 403 } 404 else if (ast.nodeType == NodeType.InterfaceDeclaration) { 405 preAssignInterfaceScopes(ast, context); 406 } 407 else if (ast.nodeType == NodeType.With) { 408 preAssignWithScopes(ast, context); 409 } 410 else if (ast.nodeType == NodeType.FuncDecl) { 411 preAssignFuncDeclScopes(ast, context); 412 } 413 else if (ast.nodeType == NodeType.Catch) { 414 preAssignCatchScopes(ast, context); 415 } 416 else if (ast.nodeType == NodeType.TypeRef) { 417 go = false; 418 } 419 } 420 walker.options.goChildren = go; 421 return ast; 422 } 423 424 export function postAssignScopes(ast: AST, parent: AST, walker: IAstWalker) { 425 var context:AssignScopeContext = walker.state; 426 var go = true; 427 if (ast) { 428 if (ast.nodeType == NodeType.ModuleDeclaration) { 429 var prevModDecl = <ModuleDeclaration>ast; 430 431 popAssignScope(context); 432 433 context.modDeclChain.pop(); 434 if (context.modDeclChain.length >= 1) { 435 context.typeFlow.checker.currentModDecl = context.modDeclChain[context.modDeclChain.length - 1]; 436 } 437 } 438 else if (ast.nodeType == NodeType.ClassDeclaration) { 439 popAssignScope(context); 440 } 441 else if (ast.nodeType == NodeType.InterfaceDeclaration) { 442 popAssignScope(context); 443 } 444 else if (ast.nodeType == NodeType.With) { 445 popAssignScope(context); 446 } 447 else if (ast.nodeType == NodeType.FuncDecl) { 448 var funcDecl = <FuncDecl>ast; 449 if ((!funcDecl.isConstructor || hasFlag(funcDecl.fncFlags, FncFlags.ClassMethod)) && !funcDecl.isOverload) { 450 popAssignScope(context); 451 } 452 } 453 else if (ast.nodeType == NodeType.Catch) { 454 var catchBlock = <Catch>ast; 455 if (catchBlock.param) { 456 popAssignScope(context); 457 } 458 } 459 else { 460 go = false; 461 } 462 } 463 walker.options.goChildren = go; 464 return ast; 465 } 466}