OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 interface TreeElements { | |
6 Element operator[](Node node); | |
7 Selector getSelector(Send send); | |
8 } | |
9 | |
10 class TreeElementMapping implements TreeElements { | |
11 Map<Node, Element> map; | |
12 Map<Send, Selector> selectors; | |
13 TreeElementMapping() | |
14 : map = new LinkedHashMap<Node, Element>(), | |
15 selectors = new LinkedHashMap<Send, Selector>(); | |
16 | |
17 operator []=(Node node, Element element) => map[node] = element; | |
18 operator [](Node node) => map[node]; | |
19 void remove(Node node) { map.remove(node); } | |
20 | |
21 void setSelector(Send send, Selector selector) { | |
22 selectors[send] = selector; | |
23 } | |
24 | |
25 Selector getSelector(Send send) => selectors[send]; | |
26 } | |
27 | |
28 class ResolverTask extends CompilerTask { | |
29 Queue<ClassElement> toResolve; | |
30 | |
31 // Caches the elements of analyzed constructors to make them available | |
32 // for inlining in later tasks. | |
33 Map<FunctionElement, TreeElements> constructorElements; | |
34 | |
35 ResolverTask(Compiler compiler) | |
36 : super(compiler), toResolve = new Queue<ClassElement>(), | |
37 constructorElements = new Map<FunctionElement, TreeElements>(); | |
38 | |
39 String get name() => 'Resolver'; | |
40 | |
41 TreeElements resolve(Element element) { | |
42 return measure(() { | |
43 switch (element.kind) { | |
44 case ElementKind.GENERATIVE_CONSTRUCTOR: | |
45 case ElementKind.FUNCTION: | |
46 case ElementKind.GETTER: | |
47 case ElementKind.SETTER: | |
48 return resolveMethodElement(element); | |
49 | |
50 case ElementKind.FIELD: | |
51 return resolveField(element); | |
52 | |
53 case ElementKind.PARAMETER: | |
54 case ElementKind.FIELD_PARAMETER: | |
55 return resolveParameter(element); | |
56 | |
57 default: | |
58 compiler.unimplemented( | |
59 "resolver", node: element.parseNode(compiler)); | |
60 } | |
61 }); | |
62 } | |
63 | |
64 SourceString getConstructorName(Send node) { | |
65 if (node.receiver !== null) { | |
66 return node.selector.asIdentifier().source; | |
67 } else { | |
68 return const SourceString(''); | |
69 } | |
70 } | |
71 | |
72 FunctionElement lookupConstructor(ClassElement classElement, Send send, | |
73 [noConstructor(Element)]) { | |
74 final SourceString constructorName = getConstructorName(send); | |
75 final SourceString className = classElement.name; | |
76 return classElement.lookupConstructor(className, | |
77 constructorName, | |
78 noConstructor); | |
79 } | |
80 | |
81 FunctionElement resolveConstructorRedirection(FunctionElement constructor) { | |
82 FunctionExpression node = constructor.parseNode(compiler); | |
83 // A synthetic constructor does not have a node. | |
84 if (node === null) return null; | |
85 if (node.initializers === null) return null; | |
86 Link<Node> initializers = node.initializers.nodes; | |
87 if (!initializers.isEmpty() && | |
88 Initializers.isConstructorRedirect(initializers.head)) { | |
89 return lookupConstructor(constructor.enclosingElement, initializers.head); | |
90 } | |
91 return null; | |
92 } | |
93 | |
94 void resolveRedirectingConstructor(InitializerResolver resolver, | |
95 Node node, | |
96 FunctionElement constructor, | |
97 FunctionElement redirection) { | |
98 Set<FunctionElement> seen = new Set<FunctionElement>(); | |
99 seen.add(constructor); | |
100 while (redirection !== null) { | |
101 if (seen.contains(redirection)) { | |
102 resolver.visitor.error(node, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE); | |
103 return; | |
104 } | |
105 seen.add(redirection); | |
106 redirection = resolveConstructorRedirection(redirection); | |
107 } | |
108 } | |
109 | |
110 TreeElements resolveMethodElement(FunctionElement element) { | |
111 return compiler.withCurrentElement(element, () { | |
112 bool isConstructor = element.kind === ElementKind.GENERATIVE_CONSTRUCTOR; | |
113 if (constructorElements.containsKey(element)) { | |
114 assert(isConstructor); | |
115 TreeElements elements = constructorElements[element]; | |
116 if (elements !== null) return elements; | |
117 } | |
118 FunctionExpression tree = element.parseNode(compiler); | |
119 if (isConstructor) { | |
120 resolveConstructorImplementation(element, tree); | |
121 } | |
122 ResolverVisitor visitor = new ResolverVisitor(compiler, element); | |
123 visitor.useElement(tree, element); | |
124 visitor.setupFunction(tree, element); | |
125 | |
126 if (tree.initializers != null) { | |
127 if (!isConstructor) { | |
128 error(tree, MessageKind.FUNCTION_WITH_INITIALIZER); | |
129 } | |
130 InitializerResolver resolver = new InitializerResolver(visitor); | |
131 FunctionElement redirection = | |
132 resolver.resolveInitializers(element, tree); | |
133 if (redirection !== null) { | |
134 resolveRedirectingConstructor(resolver, tree, element, redirection); | |
135 } | |
136 } | |
137 visitor.visit(tree.body); | |
138 | |
139 // Resolve the type annotations encountered in the method. | |
140 while (!toResolve.isEmpty()) { | |
141 ClassElement classElement = toResolve.removeFirst(); | |
142 classElement.ensureResolved(compiler); | |
143 } | |
144 if (isConstructor) { | |
145 constructorElements[element] = visitor.mapping; | |
146 } | |
147 return visitor.mapping; | |
148 }); | |
149 } | |
150 | |
151 void resolveConstructorImplementation(FunctionElement constructor, | |
152 FunctionExpression node) { | |
153 assert(constructor.defaultImplementation === constructor); | |
154 ClassElement intrface = constructor.enclosingElement; | |
155 if (!intrface.isInterface()) return; | |
156 Type defaultType = intrface.defaultClass; | |
157 if (defaultType === null) { | |
158 error(node, MessageKind.NO_DEFAULT_CLASS, [intrface.name]); | |
159 } | |
160 ClassElement defaultClass = defaultType.element; | |
161 defaultClass.ensureResolved(compiler); | |
162 if (defaultClass.isInterface()) { | |
163 error(node, MessageKind.CANNOT_INSTANTIATE_INTERFACE, | |
164 [defaultClass.name]); | |
165 } | |
166 // We have now established the following: | |
167 // [intrface] is an interface, let's say "MyInterface". | |
168 // [defaultClass] is a class, let's say "MyClass". | |
169 | |
170 // First look up the constructor named "MyInterface.name". | |
171 constructor.defaultImplementation = | |
172 defaultClass.lookupConstructor(constructor.name); | |
173 | |
174 // If that fails, try looking up "MyClass.name". | |
175 if (constructor.defaultImplementation === null) { | |
176 SourceString name = | |
177 new SourceString(constructor.name.slowToString().replaceFirst( | |
178 intrface.name.slowToString(), | |
179 defaultClass.name.slowToString())); | |
180 constructor.defaultImplementation = defaultClass.lookupConstructor(name); | |
181 | |
182 if (constructor.defaultImplementation === null) { | |
183 // We failed find a constrcutor named either | |
184 // "MyInterface.name" or "MyClass.name". | |
185 error(node, MessageKind.CANNOT_FIND_CONSTRUCTOR2, | |
186 [constructor.name, name]); | |
187 } | |
188 } | |
189 } | |
190 | |
191 TreeElements resolveField(Element element) { | |
192 Node tree = element.parseNode(compiler); | |
193 ResolverVisitor visitor = new ResolverVisitor(compiler, element); | |
194 initializerDo(tree, visitor.visit); | |
195 return visitor.mapping; | |
196 } | |
197 | |
198 TreeElements resolveParameter(Element element) { | |
199 Node tree = element.parseNode(compiler); | |
200 ResolverVisitor visitor = | |
201 new ResolverVisitor(compiler, element.enclosingElement); | |
202 initializerDo(tree, visitor.visit); | |
203 return visitor.mapping; | |
204 } | |
205 | |
206 Type resolveType(ClassElement element) { | |
207 if (element.isResolved) return element.type; | |
208 return measure(() { | |
209 ClassNode tree = element.parseNode(compiler); | |
210 ClassResolverVisitor visitor = | |
211 new ClassResolverVisitor(compiler, element.getLibrary(), element); | |
212 visitor.visit(tree); | |
213 element.isResolved = true; | |
214 return element.type; | |
215 }); | |
216 } | |
217 | |
218 FunctionParameters resolveSignature(FunctionElement element) { | |
219 return measure(() => SignatureResolver.analyze(compiler, element)); | |
220 } | |
221 | |
222 error(Node node, MessageKind kind, [arguments = const []]) { | |
223 ResolutionError message = new ResolutionError(kind, arguments); | |
224 compiler.reportError(node, message); | |
225 } | |
226 } | |
227 | |
228 class InitializerResolver { | |
229 final ResolverVisitor visitor; | |
230 final Map<SourceString, Node> initialized; | |
231 Link<Node> initializers; | |
232 bool hasSuper; | |
233 | |
234 InitializerResolver(this.visitor) | |
235 : initialized = new Map<SourceString, Node>(), hasSuper = false; | |
236 | |
237 error(Node node, MessageKind kind, [arguments = const []]) { | |
238 visitor.error(node, kind, arguments); | |
239 } | |
240 | |
241 warning(Node node, MessageKind kind, [arguments = const []]) { | |
242 visitor.warning(node, kind, arguments); | |
243 } | |
244 | |
245 bool isFieldInitializer(SendSet node) { | |
246 if (node.selector.asIdentifier() == null) return false; | |
247 if (node.receiver == null) return true; | |
248 if (node.receiver.asIdentifier() == null) return false; | |
249 return node.receiver.asIdentifier().isThis(); | |
250 } | |
251 | |
252 void resolveFieldInitializer(FunctionElement constructor, SendSet init) { | |
253 // init is of the form [this.]field = value. | |
254 final Node selector = init.selector; | |
255 final SourceString name = selector.asIdentifier().source; | |
256 // Lookup target field. | |
257 Element target; | |
258 if (isFieldInitializer(init)) { | |
259 final ClassElement classElement = constructor.enclosingElement; | |
260 target = classElement.lookupLocalMember(name); | |
261 if (target === null) { | |
262 error(selector, MessageKind.CANNOT_RESOLVE, [name]); | |
263 } else if (target.kind != ElementKind.FIELD) { | |
264 error(selector, MessageKind.NOT_A_FIELD, [name]); | |
265 } else if (!target.isInstanceMember()) { | |
266 error(selector, MessageKind.INIT_STATIC_FIELD, [name]); | |
267 } | |
268 } else { | |
269 error(init, MessageKind.INVALID_RECEIVER_IN_INITIALIZER); | |
270 } | |
271 visitor.useElement(init, target); | |
272 // Check for duplicate initializers. | |
273 if (initialized.containsKey(name)) { | |
274 error(init, MessageKind.DUPLICATE_INITIALIZER, [name]); | |
275 warning(initialized[name], MessageKind.ALREADY_INITIALIZED, [name]); | |
276 } | |
277 initialized[name] = init; | |
278 // Resolve initializing value. | |
279 visitor.visitInStaticContext(init.arguments.head); | |
280 } | |
281 | |
282 Element resolveSuperOrThis(FunctionElement constructor, | |
283 FunctionExpression functionNode, | |
284 Send call) { | |
285 noConstructor(e) { | |
286 if (e !== null) error(call, MessageKind.NO_CONSTRUCTOR, [e.name, e.kind]); | |
287 } | |
288 | |
289 ClassElement lookupTarget = constructor.enclosingElement; | |
290 bool validTarget = true; | |
291 FunctionElement result; | |
292 if (Initializers.isSuperConstructorCall(call)) { | |
293 // Check for invalid initializers. | |
294 if (hasSuper) { | |
295 error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER); | |
296 } | |
297 hasSuper = true; | |
298 // Calculate correct lookup target and constructor name. | |
299 if (lookupTarget.name == Types.OBJECT) { | |
300 error(call, MessageKind.SUPER_INITIALIZER_IN_OBJECT); | |
301 } else { | |
302 lookupTarget = lookupTarget.supertype.element; | |
303 } | |
304 } else if (Initializers.isConstructorRedirect(call)) { | |
305 // Check that there is no body (Language specification 7.5.1). | |
306 if (functionNode.hasBody()) { | |
307 error(functionNode, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_BODY); | |
308 } | |
309 // Check that there are no other initializers. | |
310 if (!initializers.tail.isEmpty()) { | |
311 error(call, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER); | |
312 } | |
313 } else { | |
314 visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED); | |
315 validTarget = false; | |
316 } | |
317 | |
318 if (validTarget) { | |
319 // Resolve the arguments, and make sure the call gets a selector | |
320 // by calling handleArguments. | |
321 visitor.inStaticContext( () => visitor.handleArguments(call) ); | |
322 // Lookup constructor and try to match it to the selector. | |
323 ResolverTask resolver = visitor.compiler.resolver; | |
324 result = resolver.lookupConstructor(lookupTarget, call); | |
325 if (result === null) { | |
326 SourceString constructorName = resolver.getConstructorName(call); | |
327 String className = lookupTarget.name.slowToString(); | |
328 String name = (constructorName === const SourceString('')) | |
329 ? className | |
330 : "$className.${constructorName.slowToString()}"; | |
331 error(call, MessageKind.CANNOT_RESOLVE_CONSTRUCTOR, [name]); | |
332 } else { | |
333 final Compiler compiler = visitor.compiler; | |
334 Selector selector = visitor.mapping.getSelector(call); | |
335 FunctionParameters parameters = result.computeParameters(compiler); | |
336 // TODO(karlklose): support optional arguments. | |
337 if (!selector.applies(parameters)) { | |
338 error(call, MessageKind.NO_MATCHING_CONSTRUCTOR); | |
339 } | |
340 } | |
341 visitor.useElement(call, result); | |
342 } | |
343 return result; | |
344 } | |
345 | |
346 FunctionElement resolveRedirection(FunctionElement constructor, | |
347 FunctionExpression functionNode) { | |
348 if (functionNode.initializers === null) return null; | |
349 Link<Node> link = functionNode.initializers.nodes; | |
350 if (!link.isEmpty() && Initializers.isConstructorRedirect(link.head)) { | |
351 return resolveSuperOrThis(constructor, functionNode, link.head); | |
352 } | |
353 return null; | |
354 } | |
355 | |
356 /** | |
357 * Resolve all initializers of this constructor. In the case of a redirecting | |
358 * constructor, the resolved constructor's function element is returned. | |
359 */ | |
360 FunctionElement resolveInitializers(FunctionElement constructor, | |
361 FunctionExpression functionNode) { | |
362 if (functionNode.initializers === null) return null; | |
363 initializers = functionNode.initializers.nodes; | |
364 FunctionElement result; | |
365 for (Link<Node> link = initializers; | |
366 !link.isEmpty(); | |
367 link = link.tail) { | |
368 if (link.head.asSendSet() != null) { | |
369 final SendSet init = link.head.asSendSet(); | |
370 resolveFieldInitializer(constructor, init); | |
371 } else if (link.head.asSend() !== null) { | |
372 final Send call = link.head.asSend(); | |
373 result = resolveSuperOrThis(constructor, functionNode, call); | |
374 } else { | |
375 error(link.head, MessageKind.INVALID_INITIALIZER); | |
376 } | |
377 } | |
378 return result; | |
379 } | |
380 } | |
381 | |
382 class CommonResolverVisitor<R> extends AbstractVisitor<R> { | |
383 final Compiler compiler; | |
384 | |
385 CommonResolverVisitor(Compiler this.compiler); | |
386 | |
387 R visitNode(Node node) { | |
388 cancel(node, 'internal error'); | |
389 } | |
390 | |
391 R visitEmptyStatement(Node node) => null; | |
392 | |
393 /** Convenience method for visiting nodes that may be null. */ | |
394 R visit(Node node) => (node == null) ? null : node.accept(this); | |
395 | |
396 void error(Node node, MessageKind kind, [arguments = const []]) { | |
397 ResolutionError message = new ResolutionError(kind, arguments); | |
398 compiler.reportError(node, message); | |
399 } | |
400 | |
401 void warning(Node node, MessageKind kind, [arguments = const []]) { | |
402 ResolutionWarning message = new ResolutionWarning(kind, arguments); | |
403 compiler.reportWarning(node, message); | |
404 } | |
405 | |
406 void cancel(Node node, String message) { | |
407 compiler.cancel(message, node: node); | |
408 } | |
409 | |
410 void internalError(Node node, String message) { | |
411 compiler.internalError(message, node: node); | |
412 } | |
413 | |
414 void unimplemented(Node node, String message) { | |
415 compiler.unimplemented(message, node: node); | |
416 } | |
417 } | |
418 | |
419 interface LabelScope { | |
420 LabelScope get outer(); | |
421 LabelElement lookup(String label); | |
422 } | |
423 | |
424 class LabeledStatementLabelScope implements LabelScope { | |
425 final LabelScope outer; | |
426 final LabelElement label; | |
427 LabeledStatementLabelScope(this.outer, this.label); | |
428 LabelElement lookup(String labelName) { | |
429 if (this.label.labelName == labelName) return label; | |
430 return outer.lookup(labelName); | |
431 } | |
432 } | |
433 | |
434 class SwitchLabelScope implements LabelScope { | |
435 final LabelScope outer; | |
436 final Map<String, LabelElement> caseLabels; | |
437 | |
438 SwitchLabelScope(this.outer, this.caseLabels); | |
439 | |
440 LabelElement lookup(String labelName) { | |
441 LabelElement result = caseLabels[labelName]; | |
442 if (result !== null) return result; | |
443 return outer.lookup(labelName); | |
444 } | |
445 } | |
446 | |
447 class EmptyLabelScope implements LabelScope { | |
448 const EmptyLabelScope(); | |
449 LabelElement lookup(String label) => null; | |
450 LabelScope get outer() { | |
451 throw 'internal error: empty label scope has no outer'; | |
452 } | |
453 } | |
454 | |
455 class StatementScope { | |
456 LabelScope labels; | |
457 Link<TargetElement> breakTargetStack; | |
458 Link<TargetElement> continueTargetStack; | |
459 // Used to provide different numbers to statements if one is inside the other. | |
460 // Can be used to make otherwise duplicate labels unique. | |
461 int nestingLevel = 0; | |
462 | |
463 StatementScope() | |
464 : labels = const EmptyLabelScope(), | |
465 breakTargetStack = const EmptyLink<TargetElement>(), | |
466 continueTargetStack = const EmptyLink<TargetElement>(); | |
467 | |
468 LabelElement lookupLabel(String label) { | |
469 return labels.lookup(label); | |
470 } | |
471 TargetElement currentBreakTarget() => | |
472 breakTargetStack.isEmpty() ? null : breakTargetStack.head; | |
473 | |
474 TargetElement currentContinueTarget() => | |
475 continueTargetStack.isEmpty() ? null : continueTargetStack.head; | |
476 | |
477 void enterLabelScope(LabelElement element) { | |
478 labels = new LabeledStatementLabelScope(labels, element); | |
479 nestingLevel++; | |
480 } | |
481 | |
482 void exitLabelScope() { | |
483 nestingLevel--; | |
484 labels = labels.outer; | |
485 } | |
486 | |
487 void enterLoop(TargetElement element) { | |
488 breakTargetStack = breakTargetStack.prepend(element); | |
489 continueTargetStack = continueTargetStack.prepend(element); | |
490 nestingLevel++; | |
491 } | |
492 | |
493 void exitLoop() { | |
494 nestingLevel--; | |
495 breakTargetStack = breakTargetStack.tail; | |
496 continueTargetStack = continueTargetStack.tail; | |
497 } | |
498 | |
499 void enterSwitch(TargetElement breakElement, | |
500 Map<String, LabelElement> continueElements) { | |
501 breakTargetStack = breakTargetStack.prepend(breakElement); | |
502 labels = new SwitchLabelScope(labels, continueElements); | |
503 nestingLevel++; | |
504 } | |
505 | |
506 void exitSwitch() { | |
507 nestingLevel--; | |
508 breakTargetStack = breakTargetStack.tail; | |
509 labels = labels.outer; | |
510 } | |
511 } | |
512 | |
513 class ResolverVisitor extends CommonResolverVisitor<Element> { | |
514 final TreeElementMapping mapping; | |
515 final Element enclosingElement; | |
516 bool inInstanceContext; | |
517 Scope context; | |
518 ClassElement currentClass; | |
519 bool typeRequired = false; | |
520 StatementScope statementScope; | |
521 int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION; | |
522 | |
523 ResolverVisitor(Compiler compiler, Element element) | |
524 : this.mapping = new TreeElementMapping(), | |
525 this.enclosingElement = element, | |
526 inInstanceContext = element.isInstanceMember() | |
527 || element.isGenerativeConstructor(), | |
528 this.context = element.isMember() | |
529 ? new ClassScope(element.enclosingElement, element.getLibrary()) | |
530 : new TopScope(element.getLibrary()), | |
531 this.currentClass = element.isMember() ? element.enclosingElement : null, | |
532 this.statementScope = new StatementScope(), | |
533 super(compiler); | |
534 | |
535 Element lookup(Node node, SourceString name) { | |
536 Element result = context.lookup(name); | |
537 if (!inInstanceContext && result != null && result.isInstanceMember()) { | |
538 error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]); | |
539 } | |
540 return result; | |
541 } | |
542 | |
543 // Create, or reuse an already created, statement element for a statement. | |
544 TargetElement getOrCreateTargetElement(Node statement) { | |
545 TargetElement element = mapping[statement]; | |
546 if (element === null) { | |
547 element = new TargetElement(statement, | |
548 statementScope.nestingLevel, | |
549 enclosingElement); | |
550 mapping[statement] = element; | |
551 } | |
552 return element; | |
553 } | |
554 | |
555 inStaticContext(action()) { | |
556 bool wasInstanceContext = inInstanceContext; | |
557 inInstanceContext = false; | |
558 var result = action(); | |
559 inInstanceContext = wasInstanceContext; | |
560 return result; | |
561 } | |
562 | |
563 visitInStaticContext(Node node) { | |
564 inStaticContext(() => visit(node)); | |
565 } | |
566 | |
567 Element visitIdentifier(Identifier node) { | |
568 if (node.isThis()) { | |
569 if (!inInstanceContext) { | |
570 error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]); | |
571 } | |
572 return null; | |
573 } else if (node.isSuper()) { | |
574 if (!inInstanceContext) error(node, MessageKind.NO_SUPER_IN_STATIC); | |
575 if ((ElementCategory.SUPER & allowedCategory) == 0) { | |
576 error(node, MessageKind.INVALID_USE_OF_SUPER); | |
577 } | |
578 return null; | |
579 } else { | |
580 Element element = lookup(node, node.source); | |
581 if (element === null) { | |
582 if (!inInstanceContext) error(node, MessageKind.CANNOT_RESOLVE, [node]); | |
583 } else { | |
584 if ((element.kind.category & allowedCategory) == 0) { | |
585 // TODO(ahe): Improve error message. Need UX input. | |
586 error(node, MessageKind.GENERIC, ["is not an expression $element"]); | |
587 } | |
588 } | |
589 return useElement(node, element); | |
590 } | |
591 } | |
592 | |
593 visitTypeAnnotation(TypeAnnotation node) { | |
594 Send send = node.typeName.asSend(); | |
595 Element element; | |
596 if (send !== null) { | |
597 if (typeRequired) { | |
598 element = resolveSend(send); | |
599 } else { | |
600 // Not calling resolveSend as it will emit an error instead of | |
601 // a warning if the type is bogus. | |
602 // TODO(ahe): Change resolveSend so it can emit a warning when needed. | |
603 return null; | |
604 } | |
605 } else { | |
606 element = context.lookup(node.typeName.asIdentifier().source); | |
607 } | |
608 if (element === null) { | |
609 if (typeRequired) { | |
610 error(node, MessageKind.CANNOT_RESOLVE_TYPE, [node.typeName]); | |
611 } else { | |
612 warning(node, MessageKind.CANNOT_RESOLVE_TYPE, [node.typeName]); | |
613 } | |
614 } else if (!element.impliesType()) { | |
615 if (typeRequired) { | |
616 error(node, MessageKind.NOT_A_TYPE, [node.typeName]); | |
617 } else { | |
618 warning(node, MessageKind.NOT_A_TYPE, [node.typeName]); | |
619 } | |
620 } else { | |
621 if (element.isClass()) { | |
622 // TODO(ngeoffray): Should we also resolve typedef? | |
623 ClassElement cls = element; | |
624 compiler.resolver.toResolve.add(element); | |
625 } | |
626 // TODO(ahe): This should be a Type. | |
627 useElement(node, element); | |
628 } | |
629 return element; | |
630 } | |
631 | |
632 Element defineElement(Node node, Element element, | |
633 [bool doAddToScope = true]) { | |
634 compiler.ensure(element !== null); | |
635 mapping[node] = element; | |
636 if (doAddToScope) { | |
637 Element existing = context.add(element); | |
638 if (existing != element) { | |
639 error(node, MessageKind.DUPLICATE_DEFINITION, [node]); | |
640 } | |
641 } | |
642 return element; | |
643 } | |
644 | |
645 Element useElement(Node node, Element element) { | |
646 if (element === null) return null; | |
647 return mapping[node] = element; | |
648 } | |
649 | |
650 void setupFunction(FunctionExpression node, FunctionElement function) { | |
651 context = new MethodScope(context, function); | |
652 // Put the parameters in scope. | |
653 FunctionParameters functionParameters = | |
654 function.computeParameters(compiler); | |
655 Link<Node> parameterNodes = node.parameters.nodes; | |
656 functionParameters.forEachParameter((Element element) { | |
657 if (element == functionParameters.optionalParameters.head) { | |
658 NodeList nodes = parameterNodes.head; | |
659 parameterNodes = nodes.nodes; | |
660 } | |
661 VariableDefinitions variableDefinitions = parameterNodes.head; | |
662 Node parameterNode = variableDefinitions.definitions.nodes.head; | |
663 initializerDo(parameterNode, (n) => n.accept(this)); | |
664 // Field parameters (this.x) are not visible inside the constructor. The | |
665 // fields they reference are visible, but must be resolved independently. | |
666 if (element.kind == ElementKind.FIELD_PARAMETER) { | |
667 useElement(parameterNode, element); | |
668 } else { | |
669 defineElement(variableDefinitions.definitions.nodes.head, element); | |
670 } | |
671 parameterNodes = parameterNodes.tail; | |
672 }); | |
673 } | |
674 | |
675 Element visitClassNode(ClassNode node) { | |
676 cancel(node, "shouldn't be called"); | |
677 } | |
678 | |
679 visitIn(Node node, Scope scope) { | |
680 context = scope; | |
681 Element element = visit(node); | |
682 context = context.parent; | |
683 return element; | |
684 } | |
685 | |
686 /** | |
687 * Introduces new default targets for break and continue | |
688 * before visiting the body of the loop | |
689 */ | |
690 visitLoopBodyIn(Node loop, Node body, Scope scope) { | |
691 TargetElement element = getOrCreateTargetElement(loop); | |
692 statementScope.enterLoop(element); | |
693 visitIn(body, scope); | |
694 statementScope.exitLoop(); | |
695 if (!element.isTarget) { | |
696 mapping.remove(loop); | |
697 } | |
698 } | |
699 | |
700 visitBlock(Block node) { | |
701 visitIn(node.statements, new BlockScope(context)); | |
702 } | |
703 | |
704 visitDoWhile(DoWhile node) { | |
705 visitLoopBodyIn(node, node.body, new BlockScope(context)); | |
706 visit(node.condition); | |
707 } | |
708 | |
709 visitEmptyStatement(EmptyStatement node) { } | |
710 | |
711 visitExpressionStatement(ExpressionStatement node) { | |
712 visit(node.expression); | |
713 } | |
714 | |
715 visitFor(For node) { | |
716 Scope scope = new BlockScope(context); | |
717 visitIn(node.initializer, scope); | |
718 visitIn(node.condition, scope); | |
719 visitIn(node.update, scope); | |
720 visitLoopBodyIn(node, node.body, scope); | |
721 } | |
722 | |
723 visitFunctionDeclaration(FunctionDeclaration node) { | |
724 assert(node.function.name !== null); | |
725 visit(node.function); | |
726 FunctionElement functionElement = mapping[node.function]; | |
727 // TODO(floitsch): this might lead to two errors complaining about | |
728 // shadowing. | |
729 defineElement(node, functionElement); | |
730 } | |
731 | |
732 visitFunctionExpression(FunctionExpression node) { | |
733 visit(node.returnType); | |
734 SourceString name; | |
735 if (node.name === null) { | |
736 name = const SourceString(""); | |
737 } else { | |
738 name = node.name.asIdentifier().source; | |
739 } | |
740 FunctionElement enclosing = new FunctionElement.node( | |
741 name, node, ElementKind.FUNCTION, new Modifiers.empty(), | |
742 context.element); | |
743 setupFunction(node, enclosing); | |
744 defineElement(node, enclosing, doAddToScope: node.name !== null); | |
745 | |
746 // Run the body in a fresh statement scope. | |
747 StatementScope oldScope = statementScope; | |
748 statementScope = new StatementScope(); | |
749 visit(node.body); | |
750 statementScope = oldScope; | |
751 | |
752 context = context.parent; | |
753 } | |
754 | |
755 visitIf(If node) { | |
756 visit(node.condition); | |
757 visit(node.thenPart); | |
758 visit(node.elsePart); | |
759 } | |
760 | |
761 static bool isLogicalOperator(Identifier op) { | |
762 String str = op.source.stringValue; | |
763 return (str === '&&' || str == '||' || str == '!'); | |
764 } | |
765 | |
766 Element resolveSend(Send node) { | |
767 if (node.receiver === null) { | |
768 return node.selector.accept(this); | |
769 } | |
770 var oldCategory = allowedCategory; | |
771 allowedCategory |= | |
772 ElementCategory.CLASS | ElementCategory.PREFIX | ElementCategory.SUPER; | |
773 Element resolvedReceiver = visit(node.receiver); | |
774 allowedCategory = oldCategory; | |
775 | |
776 Element target; | |
777 SourceString name = node.selector.asIdentifier().source; | |
778 if (name.stringValue === 'this') { | |
779 error(node.selector, MessageKind.GENERIC, ["expected an identifier"]); | |
780 } else if (node.isSuperCall) { | |
781 if (node.isOperator) { | |
782 if (isUserDefinableOperator(name.stringValue)) { | |
783 name = Elements.constructOperatorName(const SourceString('operator'), | |
784 name); | |
785 } else { | |
786 error(node.selector, MessageKind.ILLEGAL_SUPER_SEND, [name]); | |
787 } | |
788 } | |
789 if (!inInstanceContext) { | |
790 error(node.receiver, MessageKind.NO_INSTANCE_AVAILABLE, [name]); | |
791 return null; | |
792 } | |
793 if (currentClass.supertype === null) { | |
794 // This is just to guard against internal errors, so no need | |
795 // for a real error message. | |
796 error(node.receiver, MessageKind.GENERIC, ["Object has no superclass"]); | |
797 } | |
798 target = currentClass.lookupSuperMember(name); | |
799 // [target] may be null which means invoking noSuchMethod on | |
800 // super. | |
801 } else if (resolvedReceiver === null) { | |
802 return null; | |
803 } else if (resolvedReceiver.kind === ElementKind.CLASS) { | |
804 ClassElement receiverClass = resolvedReceiver; | |
805 target = receiverClass.ensureResolved(compiler).lookupLocalMember(name); | |
806 if (target === null) { | |
807 error(node, MessageKind.METHOD_NOT_FOUND, [receiverClass.name, name]); | |
808 } else if (target.isInstanceMember()) { | |
809 error(node, MessageKind.MEMBER_NOT_STATIC, [receiverClass.name, name]); | |
810 } | |
811 } else if (resolvedReceiver.kind === ElementKind.PREFIX) { | |
812 PrefixElement prefix = resolvedReceiver; | |
813 target = prefix.lookupLocalMember(name); | |
814 if (target == null) { | |
815 error(node, MessageKind.NO_SUCH_LIBRARY_MEMBER, [prefix.name, name]); | |
816 } | |
817 } | |
818 return target; | |
819 } | |
820 | |
821 resolveTypeTest(Node argument) { | |
822 TypeAnnotation node = argument.asTypeAnnotation(); | |
823 if (node == null) { | |
824 node = argument.asSend().receiver; | |
825 } | |
826 resolveTypeRequired(node); | |
827 } | |
828 | |
829 void handleArguments(Send node) { | |
830 int count = 0; | |
831 List<SourceString> namedArguments = <SourceString>[]; | |
832 bool seenNamedArgument = false; | |
833 for (Link<Node> link = node.argumentsNode.nodes; | |
834 !link.isEmpty(); | |
835 link = link.tail) { | |
836 count++; | |
837 Expression argument = link.head; | |
838 visit(argument); | |
839 if (argument.asNamedArgument() != null) { | |
840 seenNamedArgument = true; | |
841 NamedArgument named = argument; | |
842 namedArguments.add(named.name.source); | |
843 } else if (seenNamedArgument) { | |
844 error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED); | |
845 } | |
846 } | |
847 mapping.setSelector(node, new Invocation(count, namedArguments)); | |
848 } | |
849 | |
850 visitSend(Send node) { | |
851 Element target = resolveSend(node); | |
852 if (node.isOperator) { | |
853 Operator op = node.selector.asOperator(); | |
854 if (op.source.stringValue === 'is') { | |
855 resolveTypeTest(node.arguments.head); | |
856 assert(node.arguments.tail.isEmpty()); | |
857 mapping.setSelector(node, Selector.BINARY_OPERATOR); | |
858 } else if (node.arguments.isEmpty()) { | |
859 assert(op.token.kind !== PLUS_TOKEN); | |
860 mapping.setSelector(node, Selector.UNARY_OPERATOR); | |
861 } else { | |
862 visit(node.argumentsNode); | |
863 mapping.setSelector(node, Selector.BINARY_OPERATOR); | |
864 } | |
865 } else if (node.isIndex) { | |
866 visit(node.argumentsNode); | |
867 assert(node.arguments.tail.isEmpty()); | |
868 mapping.setSelector(node, Selector.INDEX); | |
869 } else if (node.isPropertyAccess) { | |
870 mapping.setSelector(node, Selector.GETTER); | |
871 } else { | |
872 handleArguments(node); | |
873 } | |
874 if (target != null && target.kind == ElementKind.ABSTRACT_FIELD) { | |
875 AbstractFieldElement field = target; | |
876 target = field.getter; | |
877 } | |
878 // TODO(ngeoffray): Warn if target is null and the send is | |
879 // unqualified. | |
880 useElement(node, target); | |
881 if (node.isPropertyAccess) return target; | |
882 } | |
883 | |
884 visitSendSet(SendSet node) { | |
885 Element target = resolveSend(node); | |
886 Element setter = null; | |
887 Element getter = null; | |
888 if (target != null && target.kind == ElementKind.ABSTRACT_FIELD) { | |
889 AbstractFieldElement field = target; | |
890 setter = field.setter; | |
891 getter = field.getter; | |
892 } else { | |
893 setter = target; | |
894 getter = target; | |
895 } | |
896 // TODO(ngeoffray): Check if the target can be assigned. | |
897 Identifier op = node.assignmentOperator; | |
898 bool needsGetter = op.source.stringValue !== '='; | |
899 Selector selector; | |
900 if (needsGetter) { | |
901 if (node.isIndex) { | |
902 selector = Selector.INDEX_AND_INDEX_SET; | |
903 } else { | |
904 selector = Selector.GETTER_AND_SETTER; | |
905 } | |
906 useElement(node.selector, getter); | |
907 } else if (node.isIndex) { | |
908 selector = Selector.INDEX_SET; | |
909 } else { | |
910 selector = Selector.SETTER; | |
911 } | |
912 visit(node.argumentsNode); | |
913 mapping.setSelector(node, selector); | |
914 // TODO(ngeoffray): Warn if target is null and the send is | |
915 // unqualified. | |
916 return useElement(node, setter); | |
917 } | |
918 | |
919 visitLiteralInt(LiteralInt node) { | |
920 } | |
921 | |
922 visitLiteralDouble(LiteralDouble node) { | |
923 } | |
924 | |
925 visitLiteralBool(LiteralBool node) { | |
926 } | |
927 | |
928 visitLiteralString(LiteralString node) { | |
929 } | |
930 | |
931 visitLiteralNull(LiteralNull node) { | |
932 } | |
933 | |
934 visitStringJuxtaposition(StringJuxtaposition node) { | |
935 node.visitChildren(this); | |
936 } | |
937 | |
938 visitNodeList(NodeList node) { | |
939 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { | |
940 visit(link.head); | |
941 } | |
942 } | |
943 | |
944 visitOperator(Operator node) { | |
945 unimplemented(node, 'operator'); | |
946 } | |
947 | |
948 visitReturn(Return node) { | |
949 visit(node.expression); | |
950 } | |
951 | |
952 visitThrow(Throw node) { | |
953 visit(node.expression); | |
954 } | |
955 | |
956 visitVariableDefinitions(VariableDefinitions node) { | |
957 visit(node.type); | |
958 VariableDefinitionsVisitor visitor = | |
959 new VariableDefinitionsVisitor(compiler, node, this, | |
960 ElementKind.VARIABLE); | |
961 visitor.visit(node.definitions); | |
962 } | |
963 | |
964 visitWhile(While node) { | |
965 visit(node.condition); | |
966 visitLoopBodyIn(node, node.body, new BlockScope(context)); | |
967 } | |
968 | |
969 visitParenthesizedExpression(ParenthesizedExpression node) { | |
970 visit(node.expression); | |
971 } | |
972 | |
973 visitNewExpression(NewExpression node) { | |
974 Node selector = node.send.selector; | |
975 | |
976 FunctionElement constructor = resolveConstructor(node); | |
977 handleArguments(node.send); | |
978 if (constructor === null) return null; | |
979 // TODO(karlklose): handle optional arguments. | |
980 if (node.send.argumentCount() != constructor.parameterCount(compiler)) { | |
981 // TODO(ngeoffray): resolution error with wrong number of | |
982 // parameters. We cannot do this rigth now because of the | |
983 // List constructor. | |
984 } | |
985 useElement(node.send, constructor); | |
986 return null; | |
987 } | |
988 | |
989 FunctionElement resolveConstructor(NewExpression node) { | |
990 FunctionElement constructor = | |
991 node.accept(new ConstructorResolver(compiler, this)); | |
992 if (constructor === null) { | |
993 Element resolved = resolveTypeRequired(node.send.selector); | |
994 if (resolved !== null && resolved.kind === ElementKind.TYPE_VARIABLE) { | |
995 error(node, WarningKind.TYPE_VARIABLE_AS_CONSTRUCTOR); | |
996 return null; | |
997 } else { | |
998 error(node.send, MessageKind.CANNOT_FIND_CONSTRUCTOR, [node.send]); | |
999 } | |
1000 } | |
1001 return constructor; | |
1002 } | |
1003 | |
1004 Element resolveTypeRequired(Node node) { | |
1005 bool old = typeRequired; | |
1006 typeRequired = true; | |
1007 Element element = visit(node); | |
1008 typeRequired = old; | |
1009 return element; | |
1010 } | |
1011 | |
1012 visitModifiers(Modifiers node) { | |
1013 // TODO(ngeoffray): Implement this. | |
1014 unimplemented(node, 'modifiers'); | |
1015 } | |
1016 | |
1017 visitLiteralList(LiteralList node) { | |
1018 visit(node.elements); | |
1019 } | |
1020 | |
1021 visitConditional(Conditional node) { | |
1022 node.visitChildren(this); | |
1023 } | |
1024 | |
1025 visitStringInterpolation(StringInterpolation node) { | |
1026 node.visitChildren(this); | |
1027 } | |
1028 | |
1029 visitStringInterpolationPart(StringInterpolationPart node) { | |
1030 node.visitChildren(this); | |
1031 } | |
1032 | |
1033 visitBreakStatement(BreakStatement node) { | |
1034 TargetElement target; | |
1035 if (node.target === null) { | |
1036 target = statementScope.currentBreakTarget(); | |
1037 if (target === null) { | |
1038 error(node, MessageKind.NO_BREAK_TARGET); | |
1039 return; | |
1040 } | |
1041 target.isBreakTarget = true; | |
1042 } else { | |
1043 String labelName = node.target.source.slowToString(); | |
1044 LabelElement label = statementScope.lookupLabel(labelName); | |
1045 if (label === null) { | |
1046 error(node.target, MessageKind.UNBOUND_LABEL, [labelName]); | |
1047 return; | |
1048 } | |
1049 target = label.target; | |
1050 if (!target.statement.isValidBreakTarget()) { | |
1051 error(node.target, MessageKind.INVALID_BREAK, [labelName]); | |
1052 return; | |
1053 } | |
1054 label.setBreakTarget(); | |
1055 mapping[node.target] = label; | |
1056 } | |
1057 mapping[node] = target; | |
1058 } | |
1059 | |
1060 visitContinueStatement(ContinueStatement node) { | |
1061 TargetElement target; | |
1062 if (node.target === null) { | |
1063 target = statementScope.currentContinueTarget(); | |
1064 if (target === null) { | |
1065 error(node, MessageKind.NO_CONTINUE_TARGET); | |
1066 return; | |
1067 } | |
1068 target.isContinueTarget = true; | |
1069 } else { | |
1070 String labelName = node.target.source.slowToString(); | |
1071 LabelElement label = statementScope.lookupLabel(labelName); | |
1072 if (label === null) { | |
1073 error(node.target, MessageKind.UNBOUND_LABEL, [labelName]); | |
1074 return; | |
1075 } | |
1076 target = label.target; | |
1077 if (!target.statement.isValidContinueTarget()) { | |
1078 error(node.target, MessageKind.INVALID_CONTINUE, [labelName]); | |
1079 } | |
1080 label.setContinueTarget(); | |
1081 } | |
1082 mapping[node] = target; | |
1083 } | |
1084 | |
1085 visitForInStatement(ForInStatement node) { | |
1086 visit(node.expression); | |
1087 Scope scope = new BlockScope(context); | |
1088 Node declaration = node.declaredIdentifier; | |
1089 visitIn(declaration, scope); | |
1090 visitLoopBodyIn(node, node.body, scope); | |
1091 | |
1092 // TODO(lrn): Also allow a single identifier. | |
1093 if ((declaration is !Send || declaration.asSend().selector is !Identifier) | |
1094 && (declaration is !VariableDefinitions || | |
1095 !declaration.asVariableDefinitions().definitions.nodes.tail.isEmpty())) | |
1096 { | |
1097 // The variable declaration is either not an identifier, not a | |
1098 // declaration, or it's declaring more than one variable. | |
1099 error(node.declaredIdentifier, MessageKind.INVALID_FOR_IN, []); | |
1100 } | |
1101 } | |
1102 | |
1103 visitLabeledStatement(LabeledStatement node) { | |
1104 String labelName = node.label.source.slowToString(); | |
1105 LabelElement existingElement = statementScope.lookupLabel(labelName); | |
1106 if (existingElement !== null) { | |
1107 warning(node.label, MessageKind.DUPLICATE_LABEL, [labelName]); | |
1108 warning(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]); | |
1109 } | |
1110 Node body = node.getBody(); | |
1111 TargetElement targetElement = getOrCreateTargetElement(body); | |
1112 | |
1113 LabelElement element = targetElement.addLabel(node.label, labelName); | |
1114 statementScope.enterLabelScope(element); | |
1115 visit(node.statement); | |
1116 statementScope.exitLabelScope(); | |
1117 if (element.isTarget) { | |
1118 mapping[node.label] = element; | |
1119 } else { | |
1120 warning(node.label, MessageKind.UNUSED_LABEL, [labelName]); | |
1121 } | |
1122 if (!targetElement.isTarget && mapping[body] === targetElement) { | |
1123 // If the body is itself a break or continue for another target, it | |
1124 // might have updated its mapping to the target it actually does target. | |
1125 mapping.remove(body); | |
1126 } | |
1127 } | |
1128 | |
1129 visitLiteralMap(LiteralMap node) { | |
1130 node.visitChildren(this); | |
1131 } | |
1132 | |
1133 visitLiteralMapEntry(LiteralMapEntry node) { | |
1134 node.visitChildren(this); | |
1135 } | |
1136 | |
1137 visitNamedArgument(NamedArgument node) { | |
1138 visit(node.expression); | |
1139 } | |
1140 | |
1141 visitSwitchStatement(SwitchStatement node) { | |
1142 node.expression.accept(this); | |
1143 | |
1144 TargetElement breakElement = getOrCreateTargetElement(node); | |
1145 Map<String, LabelElement> continueLabels = <LabelElement>{}; | |
1146 Link<Node> cases = node.cases.nodes; | |
1147 while (!cases.isEmpty()) { | |
1148 SwitchCase switchCase = cases.head; | |
1149 if (switchCase.label !== null) { | |
1150 Identifier labelIdentifier = switchCase.label; | |
1151 String labelName = labelIdentifier.source.slowToString(); | |
1152 | |
1153 LabelElement existingElement = continueLabels[labelName]; | |
1154 if (existingElement !== null) { | |
1155 // It's an error if the same label occurs twice in the same switch. | |
1156 warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]); | |
1157 error(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]); | |
1158 } else { | |
1159 // It's only a warning if it shadows another label. | |
1160 existingElement = statementScope.lookupLabel(labelName); | |
1161 if (existingElement !== null) { | |
1162 warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]); | |
1163 warning(existingElement.label, | |
1164 MessageKind.EXISTING_LABEL, [labelName]); | |
1165 } | |
1166 } | |
1167 | |
1168 TargetElement TargetElement = | |
1169 new TargetElement(switchCase, | |
1170 statementScope.nestingLevel, | |
1171 enclosingElement); | |
1172 mapping[switchCase] = TargetElement; | |
1173 | |
1174 LabelElement label = | |
1175 new LabelElement(labelIdentifier, labelName, | |
1176 TargetElement, enclosingElement); | |
1177 mapping[labelIdentifier] = label; | |
1178 continueLabels[labelName] = label; | |
1179 } | |
1180 cases = cases.tail; | |
1181 if (switchCase.defaultKeyword !== null && !cases.isEmpty()) { | |
1182 error(switchCase, MessageKind.INVALID_CASE_DEFAULT); | |
1183 } | |
1184 } | |
1185 statementScope.enterSwitch(breakElement, continueLabels); | |
1186 node.cases.accept(this); | |
1187 statementScope.exitSwitch(); | |
1188 | |
1189 // Clean-up unused labels | |
1190 continueLabels.forEach((String key, LabelElement label) { | |
1191 TargetElement TargetElement = label.target; | |
1192 SwitchCase switchCase = TargetElement.statement; | |
1193 if (!label.isContinueTarget) { | |
1194 mapping.remove(switchCase); | |
1195 mapping.remove(label.label); | |
1196 } | |
1197 }); | |
1198 } | |
1199 | |
1200 visitSwitchCase(SwitchCase node) { | |
1201 // The label was handled in [visitSwitchStatement(SwitchStatement)]. | |
1202 node.expressions.accept(this); | |
1203 visitIn(node.statements, new BlockScope(context)); | |
1204 } | |
1205 | |
1206 visitTryStatement(TryStatement node) { | |
1207 visit(node.tryBlock); | |
1208 if (node.catchBlocks.isEmpty() && node.finallyBlock == null) { | |
1209 // TODO(ngeoffray): The precise location is | |
1210 // node.getEndtoken.next. Adjust when issue #1581 is fixed. | |
1211 error(node, MessageKind.NO_CATCH_NOR_FINALLY); | |
1212 } | |
1213 visit(node.catchBlocks); | |
1214 visit(node.finallyBlock); | |
1215 } | |
1216 | |
1217 visitCatchBlock(CatchBlock node) { | |
1218 Scope scope = new BlockScope(context); | |
1219 if (node.formals.isEmpty()) { | |
1220 error(node, MessageKind.EMPTY_CATCH_DECLARATION); | |
1221 } else if (!node.formals.nodes.tail.isEmpty() | |
1222 && !node.formals.nodes.tail.tail.isEmpty()) { | |
1223 for (Node extra in node.formals.nodes.tail.tail) { | |
1224 error(extra, MessageKind.EXTRA_CATCH_DECLARATION); | |
1225 } | |
1226 } | |
1227 visitIn(node.formals, scope); | |
1228 visitIn(node.block, scope); | |
1229 } | |
1230 | |
1231 visitTypedef(Typedef node) { | |
1232 unimplemented(node, 'typedef'); | |
1233 } | |
1234 } | |
1235 | |
1236 class ClassResolverVisitor extends CommonResolverVisitor<Type> { | |
1237 Scope context; | |
1238 ClassElement classElement; | |
1239 | |
1240 ClassResolverVisitor(Compiler compiler, LibraryElement library, | |
1241 ClassElement this.classElement) | |
1242 : context = new TopScope(library), | |
1243 super(compiler); | |
1244 | |
1245 Type visitClassNode(ClassNode node) { | |
1246 compiler.ensure(classElement !== null); | |
1247 compiler.ensure(!classElement.isResolved); | |
1248 final Link<Node> parameters = | |
1249 node.typeParameters !== null ? node.typeParameters.nodes | |
1250 : const EmptyLink<TypeVariable>(); | |
1251 // Create types and elements for type variable. | |
1252 for (Link<Node> link = parameters; !link.isEmpty(); link = link.tail) { | |
1253 TypeVariable typeNode = link.head; | |
1254 SourceString variableName = typeNode.name.source; | |
1255 TypeVariableType variableType = new TypeVariableType(variableName); | |
1256 TypeVariableElement variableElement = | |
1257 new TypeVariableElement(variableName, classElement, node, | |
1258 variableType); | |
1259 variableType.element = variableElement; | |
1260 classElement.typeParameters[variableName] = variableElement; | |
1261 context = new TypeVariablesScope(context, classElement); | |
1262 } | |
1263 // Resolve the bounds of type variables. | |
1264 for (Link<Node> link = parameters; !link.isEmpty(); link = link.tail) { | |
1265 TypeVariable typeNode = link.head; | |
1266 SourceString variableName = typeNode.name.source; | |
1267 TypeVariableElement variableElement = | |
1268 classElement.typeParameters[variableName]; | |
1269 if (typeNode.bound !== null) { | |
1270 Type boundType = visit(typeNode.bound); | |
1271 if (boundType !== null && boundType.element == variableElement) { | |
1272 warning(node, MessageKind.CYCLIC_TYPE_VARIABLE, | |
1273 [variableElement.name]); | |
1274 } else if (boundType !== null) { | |
1275 variableElement.bound = boundType; | |
1276 } else { | |
1277 variableElement.bound = compiler.objectClass.computeType(compiler); | |
1278 } | |
1279 } | |
1280 } | |
1281 // Find super type. | |
1282 Type supertype = visit(node.superclass); | |
1283 if (supertype !== null && supertype.element.isExtendable()) { | |
1284 classElement.supertype = supertype; | |
1285 if (isBlackListed(supertype)) { | |
1286 error(node.superclass, MessageKind.CANNOT_EXTEND, [supertype]); | |
1287 } | |
1288 } else if (supertype !== null) { | |
1289 error(node.superclass, MessageKind.TYPE_NAME_EXPECTED); | |
1290 } | |
1291 if (classElement.name != Types.OBJECT && classElement.supertype === null) { | |
1292 ClassElement objectElement = context.lookup(Types.OBJECT); | |
1293 if (objectElement !== null && !objectElement.isResolved) { | |
1294 compiler.resolver.toResolve.add(objectElement); | |
1295 } else if (objectElement === null){ | |
1296 error(node, MessageKind.CANNOT_RESOLVE_TYPE, [Types.OBJECT]); | |
1297 } | |
1298 classElement.supertype = new SimpleType(Types.OBJECT, objectElement); | |
1299 } | |
1300 if (node.defaultClause !== null) { | |
1301 classElement.defaultClass = visit(node.defaultClause); | |
1302 } | |
1303 for (Link<Node> link = node.interfaces.nodes; | |
1304 !link.isEmpty(); | |
1305 link = link.tail) { | |
1306 Type interfaceType = visit(link.head); | |
1307 if (interfaceType !== null && interfaceType.element.isExtendable()) { | |
1308 classElement.interfaces = | |
1309 classElement.interfaces.prepend(interfaceType); | |
1310 if (isBlackListed(interfaceType)) { | |
1311 error(link.head, MessageKind.CANNOT_IMPLEMENT, [interfaceType]); | |
1312 } | |
1313 } else { | |
1314 error(link.head, MessageKind.TYPE_NAME_EXPECTED); | |
1315 } | |
1316 } | |
1317 calculateAllSupertypes(classElement, new Set<ClassElement>()); | |
1318 addDefaultConstructorIfNeeded(classElement); | |
1319 return classElement.computeType(compiler); | |
1320 } | |
1321 | |
1322 Type visitTypeAnnotation(TypeAnnotation node) { | |
1323 return visit(node.typeName); | |
1324 } | |
1325 | |
1326 Type visitIdentifier(Identifier node) { | |
1327 Element element = context.lookup(node.source); | |
1328 if (element === null) { | |
1329 error(node, MessageKind.CANNOT_RESOLVE_TYPE, [node]); | |
1330 return null; | |
1331 } else if (!element.impliesType() && !element.isTypeVariable()) { | |
1332 error(node, MessageKind.NOT_A_TYPE, [node]); | |
1333 return null; | |
1334 } else { | |
1335 if (element.isClass()) { | |
1336 compiler.resolver.toResolve.add(element); | |
1337 } | |
1338 if (element.isTypeVariable()) { | |
1339 TypeVariableElement variableElement = element; | |
1340 return variableElement.type; | |
1341 } else if (element.isTypedef()) { | |
1342 compiler.unimplemented('visitIdentifier for typedefs'); | |
1343 } else { | |
1344 // TODO(ngeoffray): Use type variables. | |
1345 return element.computeType(compiler); | |
1346 } | |
1347 } | |
1348 return null; | |
1349 } | |
1350 | |
1351 Type visitSend(Send node) { | |
1352 Identifier prefix = node.receiver.asIdentifier(); | |
1353 if (prefix === null) { | |
1354 error(node.receiver, MessageKind.NOT_A_PREFIX, [node.receiver]); | |
1355 return null; | |
1356 } | |
1357 Element element = context.lookup(prefix.source); | |
1358 if (element === null || element.kind !== ElementKind.PREFIX) { | |
1359 error(node.receiver, MessageKind.NOT_A_PREFIX, [node.receiver]); | |
1360 return null; | |
1361 } | |
1362 PrefixElement prefixElement = element; | |
1363 Identifier selector = node.selector.asIdentifier(); | |
1364 var e = prefixElement.lookupLocalMember(selector.source); | |
1365 if (e === null || !e.impliesType()) { | |
1366 error(node.selector, MessageKind.CANNOT_RESOLVE_TYPE, [node.selector]); | |
1367 return null; | |
1368 } | |
1369 return e.computeType(compiler); | |
1370 } | |
1371 | |
1372 Link<Type> getOrCalculateAllSupertypes(ClassElement classElement, | |
1373 [Set<ClassElement> seen]) { | |
1374 Link<Type> allSupertypes = classElement.allSupertypes; | |
1375 if (allSupertypes !== null) return allSupertypes; | |
1376 if (seen === null) { | |
1377 seen = new Set<ClassElement>(); | |
1378 } | |
1379 if (seen.contains(classElement)) { | |
1380 error(classElement.parseNode(compiler), | |
1381 MessageKind.CYCLIC_CLASS_HIERARCHY, | |
1382 [classElement.name]); | |
1383 classElement.allSupertypes = const EmptyLink<Type>(); | |
1384 } else { | |
1385 classElement.ensureResolved(compiler); | |
1386 calculateAllSupertypes(classElement, seen); | |
1387 } | |
1388 return classElement.allSupertypes; | |
1389 } | |
1390 | |
1391 void calculateAllSupertypes(ClassElement classElement, | |
1392 Set<ClassElement> seen) { | |
1393 // TODO(karlklose): substitute type variables. | |
1394 // TODO(karlklose): check if type arguments match, if a classelement occurs | |
1395 // more than once in the supertypes. | |
1396 if (classElement.allSupertypes !== null) return; | |
1397 final Type supertype = classElement.supertype; | |
1398 if (seen.contains(classElement)) { | |
1399 error(classElement.parseNode(compiler), | |
1400 MessageKind.CYCLIC_CLASS_HIERARCHY, | |
1401 [classElement.name]); | |
1402 classElement.allSupertypes = const EmptyLink<Type>(); | |
1403 } else if (supertype != null) { | |
1404 seen.add(classElement); | |
1405 Link<Type> superSupertypes = | |
1406 getOrCalculateAllSupertypes(supertype.element, seen); | |
1407 Link<Type> supertypes = new Link<Type>(supertype, superSupertypes); | |
1408 for (Link<Type> interfaces = classElement.interfaces; | |
1409 !interfaces.isEmpty(); | |
1410 interfaces = interfaces.tail) { | |
1411 Element element = interfaces.head.element; | |
1412 Link<Type> interfaceSupertypes = | |
1413 getOrCalculateAllSupertypes(element, seen); | |
1414 supertypes = supertypes.reversePrependAll(interfaceSupertypes); | |
1415 supertypes = supertypes.prepend(interfaces.head); | |
1416 } | |
1417 seen.remove(classElement); | |
1418 classElement.allSupertypes = supertypes; | |
1419 } else { | |
1420 classElement.allSupertypes = const EmptyLink<Type>(); | |
1421 } | |
1422 } | |
1423 | |
1424 /** | |
1425 * Add a synthetic nullary constructor if there are no other | |
1426 * constructors. | |
1427 */ | |
1428 void addDefaultConstructorIfNeeded(ClassElement element) { | |
1429 if (element.constructors.length != 0) return; | |
1430 SynthesizedConstructorElement constructor = | |
1431 new SynthesizedConstructorElement(element); | |
1432 element.constructors[element.name] = constructor; | |
1433 Type returnType = compiler.types.voidType; | |
1434 constructor.type = new FunctionType(returnType, const EmptyLink<Type>(), | |
1435 constructor); | |
1436 constructor.cachedNode = | |
1437 new FunctionExpression(new Identifier(element.position()), | |
1438 new NodeList.empty(), | |
1439 new Block(new NodeList.empty()), | |
1440 null, null, null, null); | |
1441 } | |
1442 | |
1443 isBlackListed(Type type) { | |
1444 LibraryElement lib = classElement.getLibrary(); | |
1445 return | |
1446 lib !== compiler.coreLibrary && | |
1447 lib !== compiler.coreImplLibrary && | |
1448 lib !== compiler.jsHelperLibrary && | |
1449 (type.element === compiler.dynamicClass || | |
1450 type.element === compiler.boolClass || | |
1451 type.element === compiler.numClass || | |
1452 type.element === compiler.intClass || | |
1453 type.element === compiler.doubleClass || | |
1454 type.element === compiler.stringClass || | |
1455 type.element === compiler.nullClass || | |
1456 type.element === compiler.functionClass); | |
1457 } | |
1458 } | |
1459 | |
1460 class VariableDefinitionsVisitor extends CommonResolverVisitor<SourceString> { | |
1461 VariableDefinitions definitions; | |
1462 ResolverVisitor resolver; | |
1463 ElementKind kind; | |
1464 VariableListElement variables; | |
1465 | |
1466 VariableDefinitionsVisitor(Compiler compiler, | |
1467 this.definitions, this.resolver, this.kind) | |
1468 : super(compiler) | |
1469 { | |
1470 variables = new VariableListElement.node( | |
1471 definitions, ElementKind.VARIABLE_LIST, resolver.context.element); | |
1472 } | |
1473 | |
1474 SourceString visitSendSet(SendSet node) { | |
1475 assert(node.arguments.tail.isEmpty()); // Sanity check | |
1476 resolver.visit(node.arguments.head); | |
1477 return visit(node.selector); | |
1478 } | |
1479 | |
1480 SourceString visitIdentifier(Identifier node) => node.source; | |
1481 | |
1482 visitNodeList(NodeList node) { | |
1483 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { | |
1484 SourceString name = visit(link.head); | |
1485 VariableElement element = new VariableElement( | |
1486 name, variables, kind, resolver.context.element, node: link.head); | |
1487 resolver.defineElement(link.head, element); | |
1488 } | |
1489 } | |
1490 } | |
1491 | |
1492 class SignatureResolver extends CommonResolverVisitor<Element> { | |
1493 final Element enclosingElement; | |
1494 Link<Element> optionalParameters = const EmptyLink<Element>(); | |
1495 int optionalParameterCount = 0; | |
1496 Node currentDefinitions; | |
1497 | |
1498 SignatureResolver(Compiler compiler, this.enclosingElement) : super(compiler); | |
1499 | |
1500 Element visitNodeList(NodeList node) { | |
1501 // This must be a list of optional arguments. | |
1502 if (node.beginToken.stringValue !== '[') { | |
1503 internalError(node, "expected optional parameters"); | |
1504 } | |
1505 LinkBuilder<Element> elements = analyzeNodes(node.nodes); | |
1506 optionalParameterCount = elements.length; | |
1507 optionalParameters = elements.toLink(); | |
1508 return null; | |
1509 } | |
1510 | |
1511 Element visitVariableDefinitions(VariableDefinitions node) { | |
1512 resolveType(node.type); | |
1513 | |
1514 Link<Node> definitions = node.definitions.nodes; | |
1515 if (definitions.isEmpty()) { | |
1516 cancel(node, 'internal error: no parameter definition'); | |
1517 return null; | |
1518 } | |
1519 if (!definitions.tail.isEmpty()) { | |
1520 cancel(definitions.tail.head, 'internal error: extra definition'); | |
1521 return null; | |
1522 } | |
1523 Node definition = definitions.head; | |
1524 if (definition is NodeList) { | |
1525 cancel(node, 'optional parameters are not implemented'); | |
1526 } | |
1527 | |
1528 if (currentDefinitions != null) { | |
1529 cancel(node, 'function type parameters not supported'); | |
1530 } | |
1531 currentDefinitions = node; | |
1532 Element element = definition.accept(this); | |
1533 currentDefinitions = null; | |
1534 return element; | |
1535 } | |
1536 | |
1537 Element visitIdentifier(Identifier node) { | |
1538 Element variables = new VariableListElement.node(currentDefinitions, | |
1539 ElementKind.VARIABLE_LIST, enclosingElement); | |
1540 return new VariableElement(node.source, variables, | |
1541 ElementKind.PARAMETER, enclosingElement, node: node); | |
1542 } | |
1543 | |
1544 // The only valid [Send] can be in constructors and must be of the form | |
1545 // [:this.x:] (where [:x:] represents an instance field). | |
1546 FieldParameterElement visitSend(Send node) { | |
1547 FieldParameterElement element; | |
1548 if (node.receiver.asIdentifier() === null || | |
1549 !node.receiver.asIdentifier().isThis()) { | |
1550 error(node, MessageKind.INVALID_PARAMETER, []); | |
1551 } else if (enclosingElement.kind !== ElementKind.GENERATIVE_CONSTRUCTOR) { | |
1552 error(node, MessageKind.FIELD_PARAMETER_NOT_ALLOWED, []); | |
1553 } else { | |
1554 if (node.selector.asIdentifier() == null) { | |
1555 cancel(node, | |
1556 'internal error: unimplemented receiver on parameter send'); | |
1557 } | |
1558 SourceString name = node.selector.asIdentifier().source; | |
1559 Element fieldElement = currentClass.lookupLocalMember(name); | |
1560 if (fieldElement === null || fieldElement.kind !== ElementKind.FIELD) { | |
1561 error(node, MessageKind.NOT_A_FIELD, [name]); | |
1562 } else if (!fieldElement.isInstanceMember()) { | |
1563 error(node, MessageKind.NOT_INSTANCE_FIELD, [name]); | |
1564 } | |
1565 Element variables = new VariableListElement.node(currentDefinitions, | |
1566 ElementKind.VARIABLE_LIST, enclosingElement); | |
1567 element = new FieldParameterElement(node.selector.asIdentifier().source, | |
1568 fieldElement, variables, enclosingElement, node); | |
1569 } | |
1570 return element; | |
1571 } | |
1572 | |
1573 Element visitSendSet(SendSet node) { | |
1574 Element element; | |
1575 if (node.receiver != null) { | |
1576 element = visitSend(node); | |
1577 } else if (node.selector.asIdentifier() != null) { | |
1578 Element variables = new VariableListElement.node(currentDefinitions, | |
1579 ElementKind.VARIABLE_LIST, enclosingElement); | |
1580 element = new VariableElement(node.selector.asIdentifier().source, | |
1581 variables, ElementKind.PARAMETER, enclosingElement, node: node); | |
1582 } | |
1583 // Visit the value. The compile time constant handler will | |
1584 // make sure it's a compile time constant. | |
1585 resolveExpression(node.arguments.head); | |
1586 compiler.enqueue(new WorkItem.toCompile(element)); | |
1587 return element; | |
1588 } | |
1589 | |
1590 Element visitFunctionExpression(FunctionExpression node) { | |
1591 // This is a function typed parameter. | |
1592 // TODO(ahe): Resolve the function type. | |
1593 return visit(node.name); | |
1594 } | |
1595 | |
1596 LinkBuilder<Element> analyzeNodes(Link<Node> link) { | |
1597 LinkBuilder<Element> elements = new LinkBuilder<Element>(); | |
1598 for (; !link.isEmpty(); link = link.tail) { | |
1599 Element element = link.head.accept(this); | |
1600 if (element != null) { | |
1601 elements.addLast(element); | |
1602 } else { | |
1603 // If parameter is null, the current node should be the last, | |
1604 // and a list of optional named parameters. | |
1605 if (!link.tail.isEmpty() || (link.head is !NodeList)) { | |
1606 internalError(link.head, "expected optional parameters"); | |
1607 } | |
1608 } | |
1609 } | |
1610 return elements; | |
1611 } | |
1612 | |
1613 static FunctionParameters analyze(Compiler compiler, | |
1614 FunctionElement element) { | |
1615 FunctionExpression node = element.parseNode(compiler); | |
1616 SignatureResolver visitor = new SignatureResolver(compiler, element); | |
1617 Link<Node> nodes = node.parameters.nodes; | |
1618 LinkBuilder<Element> parameters = visitor.analyzeNodes(nodes); | |
1619 return new FunctionParameters(parameters.toLink(), | |
1620 visitor.optionalParameters, | |
1621 parameters.length, | |
1622 visitor.optionalParameterCount); | |
1623 } | |
1624 | |
1625 // TODO(ahe): This is temporary. | |
1626 void resolveExpression(Node node) { | |
1627 if (node == null) return; | |
1628 node.accept(new ResolverVisitor(compiler, enclosingElement)); | |
1629 } | |
1630 | |
1631 // TODO(ahe): This is temporary. | |
1632 void resolveType(Node node) { | |
1633 if (node == null) return; | |
1634 node.accept(new ResolverVisitor(compiler, enclosingElement)); | |
1635 } | |
1636 | |
1637 // TODO(ahe): This is temporary. | |
1638 ClassElement get currentClass() { | |
1639 return enclosingElement.isMember() | |
1640 ? enclosingElement.enclosingElement : null; | |
1641 } | |
1642 } | |
1643 | |
1644 class ConstructorResolver extends CommonResolverVisitor<Element> { | |
1645 final ResolverVisitor resolver; | |
1646 ConstructorResolver(Compiler compiler, this.resolver) : super(compiler); | |
1647 | |
1648 visitNode(Node node) { | |
1649 throw 'not supported'; | |
1650 } | |
1651 | |
1652 visitNewExpression(NewExpression node) { | |
1653 Node selector = node.send.selector; | |
1654 Element e = visit(selector); | |
1655 if (e !== null && e.kind === ElementKind.CLASS) { | |
1656 ClassElement cls = e; | |
1657 cls.ensureResolved(compiler); | |
1658 compiler.resolver.toResolve.add(cls); | |
1659 if (cls.isInterface() && (cls.defaultClass === null)) { | |
1660 error(selector, MessageKind.CANNOT_INSTANTIATE_INTERFACE, [cls.name]); | |
1661 } | |
1662 e = cls.lookupConstructor(cls.name); | |
1663 } | |
1664 return e; | |
1665 } | |
1666 | |
1667 visitTypeAnnotation(TypeAnnotation node) { | |
1668 // TODO(ahe): Do not ignore type arguments. | |
1669 return visit(node.typeName); | |
1670 } | |
1671 | |
1672 visitSend(Send node) { | |
1673 Element e = visit(node.receiver); | |
1674 if (e === null) return null; // TODO(ahe): Return erroneous element. | |
1675 | |
1676 Identifier name = node.selector.asIdentifier(); | |
1677 if (name === null) internalError(node.selector, 'unexpected node'); | |
1678 | |
1679 if (e.kind === ElementKind.CLASS) { | |
1680 ClassElement cls = e; | |
1681 cls.ensureResolved(compiler); | |
1682 compiler.resolver.toResolve.add(cls); | |
1683 if (cls.isInterface() && (cls.defaultClass === null)) { | |
1684 error(node.receiver, MessageKind.CANNOT_INSTANTIATE_INTERFACE, | |
1685 [cls.name]); | |
1686 } | |
1687 SourceString constructorName = | |
1688 Elements.constructConstructorName(cls.name, name.source); | |
1689 FunctionElement constructor = cls.lookupConstructor(constructorName); | |
1690 if (constructor === null) { | |
1691 error(name, MessageKind.CANNOT_FIND_CONSTRUCTOR, [name]); | |
1692 } | |
1693 e = constructor; | |
1694 } else if (e.kind === ElementKind.PREFIX) { | |
1695 PrefixElement prefix = e; | |
1696 e = prefix.lookupLocalMember(name.source); | |
1697 if (e === null) { | |
1698 error(name, MessageKind.CANNOT_RESOLVE, [name]); | |
1699 // TODO(ahe): Return erroneous element. | |
1700 } else if (e.kind !== ElementKind.CLASS) { | |
1701 error(node, MessageKind.NOT_A_TYPE, [name]); | |
1702 } | |
1703 } else { | |
1704 internalError(node.receiver, 'unexpected element $e'); | |
1705 } | |
1706 return e; | |
1707 } | |
1708 | |
1709 Element visitIdentifier(Identifier node) { | |
1710 SourceString name = node.source; | |
1711 Element e = resolver.lookup(node, name); | |
1712 if (e === null) { | |
1713 error(node, MessageKind.CANNOT_RESOLVE, [name]); | |
1714 // TODO(ahe): Return erroneous element. | |
1715 } else if (e.kind === ElementKind.TYPEDEF) { | |
1716 error(node, MessageKind.CANNOT_INSTANTIATE_TYPEDEF, [name]); | |
1717 } else if (e.kind !== ElementKind.CLASS && e.kind !== ElementKind.PREFIX) { | |
1718 error(node, MessageKind.NOT_A_TYPE, [name]); | |
1719 } | |
1720 return e; | |
1721 } | |
1722 } | |
1723 | |
1724 class Scope { | |
1725 final Element element; | |
1726 final Scope parent; | |
1727 | |
1728 Scope(this.parent, this.element); | |
1729 abstract Element add(Element element); | |
1730 abstract Element lookup(SourceString name); | |
1731 } | |
1732 | |
1733 class TypeVariablesScope extends Scope { | |
1734 TypeVariablesScope(parent, ClassElement element) : super(parent, element); | |
1735 Element add(Element element) { | |
1736 throw "Cannot add element to TypeVariableScope"; | |
1737 } | |
1738 Element lookup(SourceString name) { | |
1739 ClassElement cls = element; | |
1740 Element result = cls.lookupTypeParameter(name); | |
1741 if (result !== null) return result; | |
1742 if (parent !== null) return parent.lookup(name); | |
1743 } | |
1744 } | |
1745 | |
1746 class MethodScope extends Scope { | |
1747 final Map<SourceString, Element> elements; | |
1748 | |
1749 MethodScope(Scope parent, Element element) | |
1750 : super(parent, element), this.elements = new Map<SourceString, Element>(); | |
1751 | |
1752 Element lookup(SourceString name) { | |
1753 Element element = elements[name]; | |
1754 if (element !== null) return element; | |
1755 return parent.lookup(name); | |
1756 } | |
1757 | |
1758 Element add(Element element) { | |
1759 if (elements.containsKey(element.name)) return elements[element.name]; | |
1760 elements[element.name] = element; | |
1761 return element; | |
1762 } | |
1763 } | |
1764 | |
1765 class BlockScope extends MethodScope { | |
1766 BlockScope(Scope parent) : super(parent, parent.element); | |
1767 } | |
1768 | |
1769 class ClassScope extends Scope { | |
1770 ClassScope(ClassElement element, LibraryElement library) | |
1771 : super(new TopScope(library), element); | |
1772 | |
1773 Element lookup(SourceString name) { | |
1774 ClassElement cls = element; | |
1775 Element result = cls.lookupLocalMember(name); | |
1776 if (result !== null) return result; | |
1777 result = cls.lookupTypeParameter(name); | |
1778 if (result !== null) return result; | |
1779 result = parent.lookup(name); | |
1780 if (result != null) return result; | |
1781 return cls.lookupSuperMember(name); | |
1782 } | |
1783 | |
1784 Element add(Element element) { | |
1785 throw "Cannot add an element in a class scope"; | |
1786 } | |
1787 } | |
1788 | |
1789 class TopScope extends Scope { | |
1790 LibraryElement get library() => element; | |
1791 | |
1792 TopScope(LibraryElement library) : super(null, library); | |
1793 Element lookup(SourceString name) { | |
1794 return library.find(name); | |
1795 } | |
1796 | |
1797 Element add(Element element) { | |
1798 throw "Cannot add an element in the top scope"; | |
1799 } | |
1800 } | |
OLD | NEW |