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 Continuation { 8 public exceptionBlock = -1; 9 constructor (public normalBlock: number) { } 10 } 11 12 function getBaseTypeLinks(bases: ASTList, baseTypeLinks: TypeLink[]) { 13 if (bases) { 14 var len = bases.members.length; 15 if (baseTypeLinks == null) { 16 baseTypeLinks = new TypeLink[]; 17 } 18 for (var i = 0; i < len; i++) { 19 var baseExpr = bases.members[i]; 20 var name = baseExpr; 21 var typeLink = new TypeLink(); 22 typeLink.ast = name; 23 baseTypeLinks[baseTypeLinks.length] = typeLink; 24 } 25 } 26 return baseTypeLinks; 27 } 28 29 function getBases(type: Type, typeDecl: TypeDeclaration) { 30 type.extendsTypeLinks = getBaseTypeLinks(typeDecl.extendsList, type.extendsTypeLinks); 31 type.implementsTypeLinks = getBaseTypeLinks(typeDecl.implementsList, type.implementsTypeLinks); 32 } 33 34 function addPrototypeField(classType: Type, ast: AST, context: TypeCollectionContext) { 35 var field = new ValueLocation(); 36 field.typeLink = new TypeLink(); 37 field.typeLink.ast = ast; 38 field.typeLink.type = classType.instanceType; 39 40 var fieldSymbol = 41 new FieldSymbol("prototype", ast.minChar, 42 context.checker.locationInfo.unitIndex, true, field); 43 fieldSymbol.flags |= (SymbolFlags.Property | SymbolFlags.BuiltIn); 44 field.symbol = fieldSymbol; 45 fieldSymbol.declAST = ast; 46 classType.members.addPublicMember("prototype", fieldSymbol); 47 } 48 49 export function createNewConstructGroupForType(type: Type) { 50 var signature = new Signature(); 51 signature.returnType = new TypeLink(); 52 signature.returnType.type = type.instanceType; 53 signature.parameters = []; 54 55 type.construct = new SignatureGroup(); 56 type.construct.addSignature(signature); 57 } 58 59 export function cloneParentConstructGroupForChildType(child: Type, parent: Type) { 60 child.construct = new SignatureGroup(); 61 var sig: Signature = null; 62 63 if (!parent.construct) { 64 createNewConstructGroupForType(parent); 65 } 66 67 for (var i = 0; i < parent.construct.signatures.length; i++) { 68 sig = new Signature(); 69 sig.parameters = parent.construct.signatures[i].parameters; 70 sig.nonOptionalParameterCount = parent.construct.signatures[i].nonOptionalParameterCount; 71 sig.typeCheckStatus = parent.construct.signatures[i].typeCheckStatus; 72 sig.declAST = parent.construct.signatures[i].declAST; 73 sig.returnType = new TypeLink(); 74 sig.returnType.type = child.instanceType; 75 child.construct.addSignature(sig); 76 } 77 78 } 79 80 export var globalId = "__GLO"; 81 82 export interface IAliasScopeContext { 83 topLevelScope: ScopeChain; 84 members: IHashTable; 85 tcContext: TypeCollectionContext; 86 } 87 88 function findTypeSymbolInScopeChain(name: string, scopeChain: ScopeChain): Symbol { 89 var symbol = scopeChain.scope.find(name, false, true); 90 91 if (symbol == null && scopeChain.previous) { 92 symbol = findTypeSymbolInScopeChain(name, scopeChain.previous); 93 } 94 95 return symbol; 96 } 97 98 function findSymbolFromAlias(alias: AST, context: IAliasScopeContext): Symbol { 99 var symbol: Symbol = null; 100 switch (alias.nodeType) { 101 case NodeType.Name: 102 var name = (<Identifier>alias).text; 103 var isDynamic = isQuoted(name); 104 105 var findSym = (id: string) => { 106 if (context.members) { 107 return context.members.lookup(name); 108 } 109 else { 110 return findTypeSymbolInScopeChain(name, context.topLevelScope); 111 } 112 } 113 114 if (isDynamic) { 115 symbol = context.tcContext.checker.findSymbolForDynamicModule(name, context.tcContext.script.locationInfo.filename, findSym); 116 } 117 else { 118 symbol = findSym(name); 119 } 120 121 break; 122 123 case NodeType.Dot: 124 var dottedExpr = <BinaryExpression>alias; 125 var op1Sym = findSymbolFromAlias(dottedExpr.operand1, context); 126 127 if (op1Sym && op1Sym.getType()) { 128 symbol = findSymbolFromAlias(dottedExpr.operand2, context); 129 } 130 131 break; 132 133 default: 134 break; 135 } 136 137 if (symbol) { 138 var symType = symbol.getType(); 139 if (symType) { 140 var members = symType.members; 141 if (members) { 142 context.members = members.publicMembers; 143 } 144 } 145 } 146 147 return symbol; 148 } 149 150 export function preCollectImportTypes(ast: AST, parent: AST, context: TypeCollectionContext) { 151 var scopeChain = context.scopeChain; 152 var typeSymbol: TypeSymbol = null; 153 var modType: ModuleType = null; 154 var importDecl = <ImportDeclaration>ast; 155 var isExported = hasFlag(importDecl.varFlags, VarFlags.Exported); 156 157 // REVIEW: technically, this call isn't strictly necessary, since we'll find the type during the call to resolveTypeMembers 158 var aliasedModSymbol = findSymbolFromAlias(importDecl.alias, { topLevelScope: scopeChain, members: null, tcContext: context }); 159 var isGlobal = context.scopeChain.container == context.checker.gloMod; 160 161 if (aliasedModSymbol) { 162 var aliasedModType = aliasedModSymbol.getType(); 163 164 if (aliasedModType) { 165 modType = <ModuleType>aliasedModType; 166 } 167 } 168 169 typeSymbol = new TypeSymbol(importDecl.id.text, importDecl.minChar, 170 context.checker.locationInfo.unitIndex, modType); 171 172 typeSymbol.aliasLink = importDecl; 173 174 if (context.scopeChain.moduleDecl) { 175 typeSymbol.declModule = context.scopeChain.moduleDecl; 176 } 177 typeSymbol.declAST = importDecl; 178 importDecl.id.sym = typeSymbol; 179 scopeChain.scope.enter(scopeChain.container, ast, typeSymbol, 180 context.checker.errorReporter, isExported || isGlobal, true, false); 181 scopeChain.scope.enter(scopeChain.container, ast, typeSymbol, 182 context.checker.errorReporter, isExported || isGlobal, false, false); 183 return true; 184 } 185 186 export function preCollectModuleTypes(ast: AST, parent: AST, context: TypeCollectionContext) { 187 var scopeChain = context.scopeChain; 188 189 var moduleDecl: ModuleDeclaration = <ModuleDeclaration>ast; 190 191 var isAmbient = hasFlag(moduleDecl.modFlags, ModuleFlags.Ambient); 192 var isEnum = hasFlag(moduleDecl.modFlags, ModuleFlags.IsEnum); 193 var isGlobal = context.scopeChain.container == context.checker.gloMod; 194 var isExported = hasFlag(moduleDecl.modFlags, ModuleFlags.Exported); 195 var modName = (<Identifier>moduleDecl.name).text; 196 197 var isDynamic = isQuoted(modName); 198 199 var symbol = scopeChain.scope.findLocal(modName, false, false); 200 var typeSymbol: TypeSymbol = null; 201 var modType: ModuleType = null; 202 if ((symbol == null) || (symbol.kind() != SymbolKind.Type)) { 203 204 if (modType == null) { 205 var enclosedTypes = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 206 var ambientEnclosedTypes = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 207 modType = new ModuleType(enclosedTypes, ambientEnclosedTypes); 208 if (isEnum) { 209 modType.typeFlags |= TypeFlags.IsEnum; 210 } 211 modType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 212 modType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 213 modType.setHasImplementation(); 214 } 215 216 typeSymbol = new TypeSymbol(modName, moduleDecl.minChar, 217 context.checker.locationInfo.unitIndex, modType); 218 219 if (context.scopeChain.moduleDecl) { 220 typeSymbol.declModule = context.scopeChain.moduleDecl; 221 } 222 typeSymbol.declAST = moduleDecl; 223 typeSymbol.prettyName = moduleDecl.prettyName; 224 scopeChain.scope.enter(scopeChain.container, ast, typeSymbol, 225 context.checker.errorReporter, isExported || isGlobal, true, isAmbient); 226 scopeChain.scope.enter(scopeChain.container, ast, typeSymbol, 227 context.checker.errorReporter, isExported || isGlobal, false, isAmbient); 228 modType.symbol = typeSymbol; 229 } 230 else { 231 if (symbol && symbol.declAST && symbol.declAST.nodeType != NodeType.ModuleDeclaration) { 232 context.checker.errorReporter.simpleError(moduleDecl, "Conflicting symbol name for module '" + modName + "'"); 233 } 234 typeSymbol = <TypeSymbol>symbol; 235 236 // initialize new private scope for the type 237 var publicEnclosedTypes = typeSymbol.type.getAllEnclosedTypes().publicMembers; 238 var publicEnclosedTypesTable = (publicEnclosedTypes == null) ? new StringHashTable() : publicEnclosedTypes; 239 var enclosedTypes = new ScopedMembers(new DualStringHashTable(publicEnclosedTypesTable, new StringHashTable())); 240 241 var publicEnclosedAmbientTypes = typeSymbol.type.getAllAmbientEnclosedTypes().publicMembers; 242 var publicAmbientEnclosedTypesTable = (publicEnclosedAmbientTypes == null) ? new StringHashTable() : publicEnclosedAmbientTypes; 243 var ambientEnclosedTypes = new ScopedMembers(new DualStringHashTable(publicAmbientEnclosedTypesTable, new StringHashTable())); 244 245 var publicMembers = typeSymbol.type.members.publicMembers; 246 var publicMembersTable = (publicMembers == null) ? new StringHashTable() : publicMembers; 247 var members = new ScopedMembers(new DualStringHashTable(publicMembersTable, new StringHashTable())); 248 249 var publicAmbientMembers = typeSymbol.type.ambientMembers.publicMembers; 250 var publicAmbientMembersTable = (publicAmbientMembers == null) ? new StringHashTable() : publicAmbientMembers; 251 var ambientMembers = new ScopedMembers(new DualStringHashTable(publicAmbientMembersTable, new StringHashTable())); 252 253 modType = new ModuleType(enclosedTypes, ambientEnclosedTypes); 254 if (isEnum) { 255 modType.typeFlags |= TypeFlags.IsEnum; 256 } 257 modType.members = members; 258 modType.ambientMembers = ambientMembers; 259 modType.setHasImplementation(); 260 modType.symbol = typeSymbol; 261 262 typeSymbol.addLocation(moduleDecl.minChar); 263 typeSymbol.expansions.push(modType); 264 265 } 266 if (context.scopeChain.moduleDecl) { 267 context.scopeChain.moduleDecl.recordNonInterface(); 268 } 269 // REVIEW: If multiple disparate module decls for the same module don't agree 270 // in export privileges, how should we handle it? 271 if (isExported) { 272 typeSymbol.flags |= SymbolFlags.Exported; 273 } 274 if ((context.scopeChain.moduleDecl) || 275 (context.scopeChain.container == context.checker.gloMod)) { 276 typeSymbol.flags |= SymbolFlags.ModuleMember; 277 } 278 279 moduleDecl.mod = modType; 280 pushTypeCollectionScope(typeSymbol, modType.members, 281 modType.ambientMembers, 282 modType.enclosedTypes, 283 modType.ambientEnclosedTypes, 284 context, null, null, moduleDecl); 285 286 return true; 287 } 288 289 export function preCollectClassTypes(ast: AST, parent: AST, context: TypeCollectionContext) { 290 var scopeChain = context.scopeChain; 291 var classDecl = <ClassDeclaration>ast; 292 293 var classType: Type; 294 var instanceType: Type; 295 var typeSymbol: TypeSymbol = null; 296 var className = (<Identifier>classDecl.name).text; 297 var alreadyInScope = false; 298 var isAmbient = hasFlag(classDecl.varFlags, VarFlags.Ambient); 299 var isExported = hasFlag(classDecl.varFlags, VarFlags.Exported); 300 var isGlobal = context.scopeChain.container == context.checker.gloMod; 301 var containerMod = <TypeSymbol>scopeChain.container; 302 var foundValSymbol = false; 303 304 typeSymbol = <TypeSymbol>scopeChain.scope.findLocal(className, false, true); 305 306 // check the value space, since an override may have been declared with the type's name 307 // REVIEW-CLASSES 308 if (!typeSymbol) { 309 var valTypeSymbol = scopeChain.scope.findLocal(className, false, false); 310 311 if (valTypeSymbol && 312 valTypeSymbol.isType() && 313 valTypeSymbol.declAST && 314 valTypeSymbol.declAST.nodeType == NodeType.FuncDecl && 315 (<FuncDecl>valTypeSymbol.declAST).isSignature()) { 316 317 typeSymbol = <TypeSymbol>valTypeSymbol; 318 foundValSymbol = true; 319 320 if (isExported) { 321 typeSymbol.flags |= SymbolFlags.Exported; 322 } 323 324 if (isAmbient) { 325 typeSymbol.flags |= SymbolFlags.Ambient; 326 } 327 328 // the class was never entered into type space, so add it 329 context.scopeChain.scope.enter(context.scopeChain.container, ast, typeSymbol, 330 context.checker.errorReporter, isExported || isGlobal, true, isAmbient); 331 } 332 } 333 334 if (typeSymbol && !foundValSymbol && (typeSymbol.declAST != classDecl)) { 335 typeSymbol = null; 336 } 337 338 if (typeSymbol == null) { 339 var valueSymbol = scopeChain.scope.findLocal(className, false, false); 340 classType = new Type(); 341 classType.setHasImplementation(); 342 instanceType = new Type(); 343 instanceType.setHasImplementation(); 344 classType.instanceType = instanceType; 345 classType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 346 classType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 347 addPrototypeField(classType, classDecl, context); 348 instanceType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 349 instanceType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 350 typeSymbol = new TypeSymbol(className, classDecl.minChar, 351 context.checker.locationInfo.unitIndex, classType); 352 typeSymbol.declAST = classDecl; 353 typeSymbol.instanceType = instanceType; 354 classType.symbol = typeSymbol; 355 instanceType.symbol = typeSymbol; 356 357 if (context.scopeChain.moduleDecl) { 358 context.scopeChain.moduleDecl.recordNonInterface(); 359 typeSymbol.declModule = context.scopeChain.moduleDecl; 360 typeSymbol.flags |= SymbolFlags.ModuleMember; 361 } 362 363 if (isExported) { 364 typeSymbol.flags |= SymbolFlags.Exported; 365 } 366 367 if (isAmbient) { 368 typeSymbol.flags |= SymbolFlags.Ambient; 369 } 370 371 ast.type = classType; 372 373 // class in both name spaces (type for instance type; constructor representative in value space) 374 context.scopeChain.scope.enter(context.scopeChain.container, ast, typeSymbol, 375 context.checker.errorReporter, isExported || isGlobal, true, isAmbient); 376 377 if (valueSymbol == null) { 378 context.scopeChain.scope.enter(context.scopeChain.container, ast, typeSymbol, 379 context.checker.errorReporter, isExported || isGlobal, false, isAmbient); 380 } 381 } 382 else { 383 classType = typeSymbol.type; 384 385 // If the instance type is null, a call overload was likely declared before the class constructor 386 if (classType.instanceType == null) { 387 classType.instanceType = new Type(); 388 classType.instanceType.setHasImplementation(); 389 classType.instanceType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 390 classType.instanceType.symbol = classType.symbol; 391 classType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 392 classType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 393 } 394 395 instanceType = classType.instanceType; 396 ast.type = classType; 397 } 398 399 // if the class has no declared constructor, either create a default signature or adapt 400 // it's base class's signature group 401 if (!classDecl.constructorDecl) { 402 403 if (typeSymbol && typeSymbol.declAST && typeSymbol.declAST.type && typeSymbol.declAST.type.call && !(<FuncDecl>typeSymbol.declAST).isOverload) { 404 context.checker.errorReporter.duplicateIdentifier(typeSymbol.declAST, typeSymbol.name); 405 } 406 407 createNewConstructGroupForType(classDecl.type); 408 } 409 410 classType.typeFlags |= TypeFlags.IsClass; 411 instanceType.typeFlags |= TypeFlags.IsClass; 412 413 getBases(instanceType, classDecl); 414 pushTypeCollectionScope(typeSymbol, instanceType.members, instanceType.ambientMembers, null, null, 415 context, instanceType, classType, null); 416 return true; 417 } 418 419 export function preCollectInterfaceTypes(ast: AST, parent: AST, context: TypeCollectionContext) { 420 var scopeChain = context.scopeChain; 421 var interfaceDecl = <InterfaceDeclaration>ast; 422 var interfaceSymbol: TypeSymbol = null; 423 var interfaceType: Type = null; 424 var isExported = hasFlag(interfaceDecl.varFlags, VarFlags.Exported); 425 var isGlobal = context.scopeChain.container == context.checker.gloMod; 426 var alreadyInScope = true; 427 428 alreadyInScope = false; 429 var interfaceName = (<Identifier>interfaceDecl.name).text; 430 interfaceSymbol = <TypeSymbol>scopeChain.scope.findLocal(interfaceName, false, true); 431 if (interfaceSymbol == null) { 432 interfaceType = new Type(); 433 interfaceSymbol = new TypeSymbol(interfaceName, 434 ast.minChar, 435 context.checker.locationInfo.unitIndex, 436 interfaceType); 437 interfaceType.symbol = interfaceSymbol; 438 // REVIEW: Shouldn't allocate another table for interface privates 439 interfaceType.members = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 440 interfaceType.ambientMembers = new ScopedMembers(new DualStringHashTable(new StringHashTable(), new StringHashTable())); 441 interfaceSymbol.declAST = interfaceDecl; 442 interfaceSymbol.declModule = context.scopeChain.moduleDecl; 443 } 444 else { 445 alreadyInScope = true; 446 interfaceType = interfaceSymbol.type; 447 } 448 449 if (!interfaceType) { 450 interfaceType = context.checker.anyType; 451 } 452 453 ast.type = interfaceType; 454 getBases(interfaceType, interfaceDecl); 455 456 if (isExported) { 457 interfaceSymbol.flags |= SymbolFlags.Exported; 458 } 459 460 if (context.scopeChain.moduleDecl) { 461 interfaceSymbol.flags |= SymbolFlags.ModuleMember; 462 } 463 464 if (!alreadyInScope) { 465 context.scopeChain.scope.enter(context.scopeChain.container, ast, 466 interfaceSymbol, context.checker.errorReporter, isGlobal || isExported, true, false); // REVIEW: Technically, interfaces should be ambient 467 } 468 pushTypeCollectionScope(interfaceSymbol, interfaceType.members, interfaceType.ambientMembers, null, null, 469 context, interfaceType, null, null); 470 return true; 471 } 472 473 export function preCollectArgDeclTypes(ast: AST, parent: AST, context: TypeCollectionContext) { 474 var scopeChain = context.scopeChain; 475 var argDecl = <ArgDecl>ast; 476 if (hasFlag(argDecl.varFlags, VarFlags.Public | VarFlags.Private)) { 477 var field = new ValueLocation(); 478 var isPrivate = hasFlag(argDecl.varFlags, VarFlags.Private); 479 var fieldSymbol = 480 new FieldSymbol(argDecl.id.text, argDecl.minChar, 481 context.checker.locationInfo.unitIndex, 482 !hasFlag(argDecl.varFlags, VarFlags.Readonly), 483 field); 484 fieldSymbol.transferVarFlags(argDecl.varFlags); 485 field.symbol = fieldSymbol; 486 fieldSymbol.declAST = ast; 487 argDecl.parameterPropertySym = fieldSymbol; 488 489 context.scopeChain.scope.enter(context.scopeChain.container, ast, 490 fieldSymbol, context.checker.errorReporter, !isPrivate, false, false); 491 492 field.typeLink = getTypeLink(argDecl.typeExpr, context.checker, argDecl.init == null); 493 argDecl.sym = fieldSymbol; 494 } 495 return false; 496 } 497 498 export function preCollectVarDeclTypes(ast: AST, parent: AST, context: TypeCollectionContext) { 499 var scopeChain = context.scopeChain; 500 var varDecl = <VarDecl>ast; 501 var isAmbient = hasFlag(varDecl.varFlags, VarFlags.Ambient); 502 var isExported = hasFlag(varDecl.varFlags, VarFlags.Exported); 503 var isGlobal = context.scopeChain.container == context.checker.gloMod; 504 var isProperty = hasFlag(varDecl.varFlags, VarFlags.Property); 505 var isStatic = hasFlag(varDecl.varFlags, VarFlags.Static); 506 var isPrivate = hasFlag(varDecl.varFlags, VarFlags.Private); 507 var isOptional = hasFlag(varDecl.id.flags, ASTFlags.OptionalName); 508 509 if (context.scopeChain.moduleDecl) { 510 context.scopeChain.moduleDecl.recordNonInterface(); 511 } 512 if (isProperty || 513 isExported || 514 (context.scopeChain.container == context.checker.gloMod) || 515 context.scopeChain.moduleDecl) { 516 if (isAmbient) { 517 var existingSym = 518 <FieldSymbol>scopeChain.scope.findLocal(varDecl.id.text, false, false); 519 if (existingSym) { 520 varDecl.sym = existingSym; 521 return false; 522 } 523 } 524 525 // Defensive error detection... 526 if (varDecl.id == null) { 527 context.checker.errorReporter.simpleError(varDecl, "Expected variable identifier at this location"); 528 return false; 529 } 530 531 var field = new ValueLocation(); 532 var fieldSymbol = 533 new FieldSymbol(varDecl.id.text, varDecl.minChar, 534 context.checker.locationInfo.unitIndex, 535 (varDecl.varFlags & VarFlags.Readonly) == VarFlags.None, 536 field); 537 fieldSymbol.transferVarFlags(varDecl.varFlags); 538 if (isOptional) { 539 fieldSymbol.flags |= SymbolFlags.Optional; 540 } 541 field.symbol = fieldSymbol; 542 fieldSymbol.declAST = ast; 543 if ((context.scopeChain.moduleDecl) || 544 (context.scopeChain.container == context.checker.gloMod)) { 545 fieldSymbol.flags |= SymbolFlags.ModuleMember; 546 fieldSymbol.declModule = context.scopeChain.moduleDecl; 547 } 548 549 // if it's static, enter it into the class's member list directly 550 if (hasFlag(varDecl.varFlags, VarFlags.Property) && isStatic && context.scopeChain.classType) { 551 if (!context.scopeChain.classType.members.publicMembers.add(varDecl.id.text, fieldSymbol)) { 552 context.checker.errorReporter.duplicateIdentifier(ast, fieldSymbol.name); 553 } 554 fieldSymbol.container = context.scopeChain.classType.symbol; 555 } 556 else { 557 context.scopeChain.scope.enter(context.scopeChain.container, 558 ast, 559 fieldSymbol, 560 context.checker.errorReporter, 561 !isPrivate && (isProperty || isExported || isGlobal || isStatic), 562 false, 563 isAmbient); 564 } 565 566 if (hasFlag(varDecl.varFlags, VarFlags.Exported)) { 567 fieldSymbol.flags |= SymbolFlags.Exported; 568 } 569 570 field.typeLink = getTypeLink(varDecl.typeExpr, context.checker, 571 varDecl.init == null); 572 varDecl.sym = fieldSymbol; 573 } 574 return false; 575 } 576 577 export function preCollectFuncDeclTypes(ast: AST, parent: AST, context: TypeCollectionContext) { 578 var scopeChain = context.scopeChain; 579 580 // REVIEW: This will have to change when we move to "export" 581 if (context.scopeChain.moduleDecl) { 582 context.scopeChain.moduleDecl.recordNonInterface(); 583 } 584 585 var funcDecl = <FuncDecl>ast; 586 var fgSym: TypeSymbol = null; 587 var nameText = funcDecl.getNameText(); 588 var isExported = hasFlag(funcDecl.fncFlags, FncFlags.Exported | FncFlags.ClassPropertyMethodExported); 589 var isStatic = hasFlag(funcDecl.fncFlags, FncFlags.Static); 590 var isPrivate = hasFlag(funcDecl.fncFlags, FncFlags.Private); 591 var isConstructor = funcDecl.isConstructMember() || funcDecl.isConstructor; 592 var containerSym:TypeSymbol = <TypeSymbol> (((funcDecl.isMethod() && isStatic) || funcDecl.isAccessor()) && context.scopeChain.classType ? context.scopeChain.classType.symbol : context.scopeChain.container); 593 var containerScope: SymbolScope = context.scopeChain.scope; 594 var isGlobal = containerSym == context.checker.gloMod; 595 var isOptional = funcDecl.name && hasFlag(funcDecl.name.flags, ASTFlags.OptionalName); 596 var go = false; 597 var foundSymbol = false; 598 599 // If this is a class constructor, the "container" is actually the class declaration 600 if (isConstructor && hasFlag(funcDecl.fncFlags, FncFlags.ClassMethod)) { 601 containerSym = <TypeSymbol>containerSym.container; 602 containerScope = scopeChain.previous.scope; 603 } 604 605 funcDecl.unitIndex = context.checker.locationInfo.unitIndex; 606 607 // If the parent is the constructor, and this isn't an instance method, skip it. 608 // That way, we'll set the type during scope assignment, and can be sure that the 609 // function will be placed in the constructor-local scope 610 if (!funcDecl.isConstructor && 611 containerSym && 612 containerSym.declAST && 613 containerSym.declAST.nodeType == NodeType.FuncDecl && 614 (<FuncDecl>containerSym.declAST).isConstructor && 615 !funcDecl.isMethod()) { 616 return go; 617 } 618 619 // Interfaces and overloads 620 if (hasFlag(funcDecl.fncFlags, FncFlags.Signature)) { 621 var instType = context.scopeChain.thisType; 622 623 // If the function is static, search in the class type's 624 if (nameText && nameText != "__missing") { 625 if (isStatic) { 626 fgSym = containerSym.type.members.allMembers.lookup(nameText); 627 } 628 else { 629 // REVIEW: This logic should be symmetric with preCollectClassTypes 630 fgSym = <TypeSymbol>containerScope.findLocal(nameText, false, false); 631 632 // If we could not find the function symbol in the value context, look 633 // in the type context. 634 // This would be the case, for example, if a class constructor override 635 // were declared before a call override for a given class 636 if (fgSym == null) { 637 fgSym = <TypeSymbol>containerScope.findLocal(nameText, false, true); 638 } 639 } 640 641 if (fgSym) { 642 foundSymbol = true; 643 644 // We'll combine ambient and non-ambient funcdecls during typecheck (for contextual typing)., 645 // So, if they don't agree, don't use the symbol we've found 646 if (!funcDecl.isSignature() && (hasFlag(funcDecl.fncFlags, FncFlags.Ambient) != hasFlag(fgSym.flags, SymbolFlags.Ambient))) { 647 fgSym = null; 648 } 649 } 650 } 651 652 // a function with this symbol has not yet been declared in this scope 653 // REVIEW: In the code below, we need to ensure that only function overloads are considered 654 // (E.g., if a vardecl has the same id as a function or class, we may use the vardecl symbol 655 // as the overload.) Defensively, however, the vardecl won't have a type yet, so it should 656 // suffice to just check for a null type when considering the overload symbol in 657 // createFunctionSignature 658 if (fgSym == null) { 659 if (!(funcDecl.isSpecialFn())) { 660 fgSym = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, null, !foundSymbol).declAST.type.symbol; 661 } 662 else { 663 fgSym = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, containerSym, false).declAST.type.symbol; 664 } 665 666 // set the symbol's declAST, which will point back to the first declaration (symbol or otherwise) 667 // related to this symbol 668 if (fgSym.declAST == null || !funcDecl.isSpecialFn()) { 669 fgSym.declAST = ast; 670 } 671 } 672 else { // there exists a symbol with this name 673 674 if ((fgSym.kind() == SymbolKind.Type)) { 675 676 fgSym = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, fgSym, false).declAST.type.symbol; 677 } 678 else { 679 context.checker.errorReporter.simpleError(funcDecl, "Function or method '" + funcDecl.name.actualText + "' already declared as a property"); 680 } 681 } 682 683 if (funcDecl.isSpecialFn() && !isStatic) { 684 funcDecl.type = instType ? instType : fgSym.type; 685 } 686 else { 687 funcDecl.type = fgSym.type; 688 } 689 } 690 else { 691 // declarations 692 693 if (nameText) { 694 if (isStatic) { 695 fgSym = containerSym.type.members.allMembers.lookup(nameText); 696 } 697 else { 698 // in the constructor case, we want to check the parent scope for overloads 699 if (funcDecl.isConstructor && context.scopeChain.previous) { 700 fgSym = <TypeSymbol>context.scopeChain.previous.scope.findLocal(nameText, false, false); 701 } 702 703 if (fgSym == null) { 704 fgSym = <TypeSymbol>containerScope.findLocal(nameText, false, false); 705 } 706 } 707 if (fgSym) { 708 foundSymbol = true; 709 710 if (!isConstructor && fgSym.declAST.nodeType == NodeType.FuncDecl && !(<FuncDecl>fgSym.declAST).isAccessor() && !(<FuncDecl>fgSym.declAST).isSignature()) { 711 fgSym = null; 712 foundSymbol = false; 713 } 714 } 715 } 716 717 // REVIEW: Move this check into the typecheck phase? It's only being run over properties... 718 if (fgSym && 719 !fgSym.isAccessor() && 720 fgSym.type && 721 fgSym.type.construct && 722 fgSym.type.construct.signatures != [] && 723 (fgSym.type.construct.signatures[0].declAST == null || 724 !hasFlag(fgSym.type.construct.signatures[0].declAST.fncFlags, FncFlags.Ambient)) && 725 !funcDecl.isConstructor) { 726 context.checker.errorReporter.simpleError(funcDecl, "Functions may not have class overloads"); 727 } 728 729 if (fgSym && !(fgSym.kind() == SymbolKind.Type) && funcDecl.isMethod() && !funcDecl.isAccessor() && !funcDecl.isConstructor) { 730 context.checker.errorReporter.simpleError(funcDecl, "Function or method '" + funcDecl.name.actualText + "' already declared as a property"); 731 fgSym.type = context.checker.anyType; 732 } 733 var sig = context.checker.createFunctionSignature(funcDecl, containerSym, containerScope, fgSym, !foundSymbol); 734 735 // it's a getter or setter function 736 if (((!fgSym || fgSym.declAST.nodeType != NodeType.FuncDecl) && funcDecl.isAccessor()) || (fgSym && fgSym.isAccessor())) { 737 funcDecl.accessorSymbol = context.checker.createAccessorSymbol(funcDecl, fgSym, containerSym.type, (funcDecl.isMethod() && isStatic), true, containerScope, containerSym); 738 } 739 740 funcDecl.type.symbol.declAST = ast; 741 if (funcDecl.isConstructor) { // REVIEW: Remove when classes completely replace oldclass 742 go = true; 743 }; 744 } 745 if (isExported) { 746 if (funcDecl.type.call) { 747 funcDecl.type.symbol.flags |= SymbolFlags.Exported; 748 } 749 750 // Accessors are set to 'exported' above 751 if (fgSym && !fgSym.isAccessor() && fgSym.kind() == SymbolKind.Type && fgSym.type.call) { 752 fgSym.flags |= SymbolFlags.Exported; 753 } 754 } 755 if (context.scopeChain.moduleDecl && !funcDecl.isSpecialFn()) { 756 funcDecl.type.symbol.flags |= SymbolFlags.ModuleMember; 757 funcDecl.type.symbol.declModule = context.scopeChain.moduleDecl; 758 } 759 760 if (fgSym && isOptional) { 761 fgSym.flags |= SymbolFlags.Optional; 762 } 763 764 return go; 765 } 766 767 export function preCollectTypes(ast: AST, parent: AST, walker: IAstWalker) { 768 var context: TypeCollectionContext = walker.state; 769 var go = false; 770 var scopeChain = context.scopeChain; 771 772 if (ast.nodeType == NodeType.Script) { 773 var script: Script = <Script>ast; 774 context.script = script; 775 go = true; 776 } 777 else if (ast.nodeType == NodeType.List) { 778 go = true; 779 } 780 else if (ast.nodeType == NodeType.ImportDeclaration) { 781 go = preCollectImportTypes(ast, parent, context); 782 } 783 else if (ast.nodeType == NodeType.With) { 784 go = false; 785 } 786 else if (ast.nodeType == NodeType.ModuleDeclaration) { 787 go = preCollectModuleTypes(ast, parent, context); 788 } 789 else if (ast.nodeType == NodeType.ClassDeclaration) { 790 go = preCollectClassTypes(ast, parent, context); 791 } 792 else if (ast.nodeType == NodeType.Block) { 793 go = true; 794 } 795 else if (ast.nodeType == NodeType.InterfaceDeclaration) { 796 go = preCollectInterfaceTypes(ast, parent, context); 797 } 798 // This will be a constructor arg because this pass only traverses 799 // constructor arg lists 800 else if (ast.nodeType == NodeType.ArgDecl) { 801 go = preCollectArgDeclTypes(ast, parent, context); 802 } 803 else if (ast.nodeType == NodeType.VarDecl) { 804 go = preCollectVarDeclTypes(ast, parent, context); 805 } 806 else if (ast.nodeType == NodeType.FuncDecl) { 807 go = preCollectFuncDeclTypes(ast, parent, context); 808 } 809 else { 810 if (ast.isStatementOrExpression() && context.scopeChain.moduleDecl) { 811 context.scopeChain.moduleDecl.recordNonInterface(); 812 } 813 } 814 walker.options.goChildren = go; 815 return ast; 816 } 817 818 export function postCollectTypes(ast: AST, parent: AST, walker: IAstWalker) { 819 var context: TypeCollectionContext = walker.state; 820 821 if (ast.nodeType == NodeType.ModuleDeclaration) { 822 popTypeCollectionScope(context); 823 } 824 else if (ast.nodeType == NodeType.ClassDeclaration) { 825 popTypeCollectionScope(context); 826 } 827 else if (ast.nodeType == NodeType.InterfaceDeclaration) { 828 popTypeCollectionScope(context); 829 } 830 return ast; 831 } 832 833}