• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}