Index: frog/leg/resolver.dart |
=================================================================== |
--- frog/leg/resolver.dart (revision 5925) |
+++ frog/leg/resolver.dart (working copy) |
@@ -1,1800 +0,0 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-interface TreeElements { |
- Element operator[](Node node); |
- Selector getSelector(Send send); |
-} |
- |
-class TreeElementMapping implements TreeElements { |
- Map<Node, Element> map; |
- Map<Send, Selector> selectors; |
- TreeElementMapping() |
- : map = new LinkedHashMap<Node, Element>(), |
- selectors = new LinkedHashMap<Send, Selector>(); |
- |
- operator []=(Node node, Element element) => map[node] = element; |
- operator [](Node node) => map[node]; |
- void remove(Node node) { map.remove(node); } |
- |
- void setSelector(Send send, Selector selector) { |
- selectors[send] = selector; |
- } |
- |
- Selector getSelector(Send send) => selectors[send]; |
-} |
- |
-class ResolverTask extends CompilerTask { |
- Queue<ClassElement> toResolve; |
- |
- // Caches the elements of analyzed constructors to make them available |
- // for inlining in later tasks. |
- Map<FunctionElement, TreeElements> constructorElements; |
- |
- ResolverTask(Compiler compiler) |
- : super(compiler), toResolve = new Queue<ClassElement>(), |
- constructorElements = new Map<FunctionElement, TreeElements>(); |
- |
- String get name() => 'Resolver'; |
- |
- TreeElements resolve(Element element) { |
- return measure(() { |
- switch (element.kind) { |
- case ElementKind.GENERATIVE_CONSTRUCTOR: |
- case ElementKind.FUNCTION: |
- case ElementKind.GETTER: |
- case ElementKind.SETTER: |
- return resolveMethodElement(element); |
- |
- case ElementKind.FIELD: |
- return resolveField(element); |
- |
- case ElementKind.PARAMETER: |
- case ElementKind.FIELD_PARAMETER: |
- return resolveParameter(element); |
- |
- default: |
- compiler.unimplemented( |
- "resolver", node: element.parseNode(compiler)); |
- } |
- }); |
- } |
- |
- SourceString getConstructorName(Send node) { |
- if (node.receiver !== null) { |
- return node.selector.asIdentifier().source; |
- } else { |
- return const SourceString(''); |
- } |
- } |
- |
- FunctionElement lookupConstructor(ClassElement classElement, Send send, |
- [noConstructor(Element)]) { |
- final SourceString constructorName = getConstructorName(send); |
- final SourceString className = classElement.name; |
- return classElement.lookupConstructor(className, |
- constructorName, |
- noConstructor); |
- } |
- |
- FunctionElement resolveConstructorRedirection(FunctionElement constructor) { |
- FunctionExpression node = constructor.parseNode(compiler); |
- // A synthetic constructor does not have a node. |
- if (node === null) return null; |
- if (node.initializers === null) return null; |
- Link<Node> initializers = node.initializers.nodes; |
- if (!initializers.isEmpty() && |
- Initializers.isConstructorRedirect(initializers.head)) { |
- return lookupConstructor(constructor.enclosingElement, initializers.head); |
- } |
- return null; |
- } |
- |
- void resolveRedirectingConstructor(InitializerResolver resolver, |
- Node node, |
- FunctionElement constructor, |
- FunctionElement redirection) { |
- Set<FunctionElement> seen = new Set<FunctionElement>(); |
- seen.add(constructor); |
- while (redirection !== null) { |
- if (seen.contains(redirection)) { |
- resolver.visitor.error(node, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE); |
- return; |
- } |
- seen.add(redirection); |
- redirection = resolveConstructorRedirection(redirection); |
- } |
- } |
- |
- TreeElements resolveMethodElement(FunctionElement element) { |
- return compiler.withCurrentElement(element, () { |
- bool isConstructor = element.kind === ElementKind.GENERATIVE_CONSTRUCTOR; |
- if (constructorElements.containsKey(element)) { |
- assert(isConstructor); |
- TreeElements elements = constructorElements[element]; |
- if (elements !== null) return elements; |
- } |
- FunctionExpression tree = element.parseNode(compiler); |
- if (isConstructor) { |
- resolveConstructorImplementation(element, tree); |
- } |
- ResolverVisitor visitor = new ResolverVisitor(compiler, element); |
- visitor.useElement(tree, element); |
- visitor.setupFunction(tree, element); |
- |
- if (tree.initializers != null) { |
- if (!isConstructor) { |
- error(tree, MessageKind.FUNCTION_WITH_INITIALIZER); |
- } |
- InitializerResolver resolver = new InitializerResolver(visitor); |
- FunctionElement redirection = |
- resolver.resolveInitializers(element, tree); |
- if (redirection !== null) { |
- resolveRedirectingConstructor(resolver, tree, element, redirection); |
- } |
- } |
- visitor.visit(tree.body); |
- |
- // Resolve the type annotations encountered in the method. |
- while (!toResolve.isEmpty()) { |
- ClassElement classElement = toResolve.removeFirst(); |
- classElement.ensureResolved(compiler); |
- } |
- if (isConstructor) { |
- constructorElements[element] = visitor.mapping; |
- } |
- return visitor.mapping; |
- }); |
- } |
- |
- void resolveConstructorImplementation(FunctionElement constructor, |
- FunctionExpression node) { |
- assert(constructor.defaultImplementation === constructor); |
- ClassElement intrface = constructor.enclosingElement; |
- if (!intrface.isInterface()) return; |
- Type defaultType = intrface.defaultClass; |
- if (defaultType === null) { |
- error(node, MessageKind.NO_DEFAULT_CLASS, [intrface.name]); |
- } |
- ClassElement defaultClass = defaultType.element; |
- defaultClass.ensureResolved(compiler); |
- if (defaultClass.isInterface()) { |
- error(node, MessageKind.CANNOT_INSTANTIATE_INTERFACE, |
- [defaultClass.name]); |
- } |
- // We have now established the following: |
- // [intrface] is an interface, let's say "MyInterface". |
- // [defaultClass] is a class, let's say "MyClass". |
- |
- // First look up the constructor named "MyInterface.name". |
- constructor.defaultImplementation = |
- defaultClass.lookupConstructor(constructor.name); |
- |
- // If that fails, try looking up "MyClass.name". |
- if (constructor.defaultImplementation === null) { |
- SourceString name = |
- new SourceString(constructor.name.slowToString().replaceFirst( |
- intrface.name.slowToString(), |
- defaultClass.name.slowToString())); |
- constructor.defaultImplementation = defaultClass.lookupConstructor(name); |
- |
- if (constructor.defaultImplementation === null) { |
- // We failed find a constrcutor named either |
- // "MyInterface.name" or "MyClass.name". |
- error(node, MessageKind.CANNOT_FIND_CONSTRUCTOR2, |
- [constructor.name, name]); |
- } |
- } |
- } |
- |
- TreeElements resolveField(Element element) { |
- Node tree = element.parseNode(compiler); |
- ResolverVisitor visitor = new ResolverVisitor(compiler, element); |
- initializerDo(tree, visitor.visit); |
- return visitor.mapping; |
- } |
- |
- TreeElements resolveParameter(Element element) { |
- Node tree = element.parseNode(compiler); |
- ResolverVisitor visitor = |
- new ResolverVisitor(compiler, element.enclosingElement); |
- initializerDo(tree, visitor.visit); |
- return visitor.mapping; |
- } |
- |
- Type resolveType(ClassElement element) { |
- if (element.isResolved) return element.type; |
- return measure(() { |
- ClassNode tree = element.parseNode(compiler); |
- ClassResolverVisitor visitor = |
- new ClassResolverVisitor(compiler, element.getLibrary(), element); |
- visitor.visit(tree); |
- element.isResolved = true; |
- return element.type; |
- }); |
- } |
- |
- FunctionParameters resolveSignature(FunctionElement element) { |
- return measure(() => SignatureResolver.analyze(compiler, element)); |
- } |
- |
- error(Node node, MessageKind kind, [arguments = const []]) { |
- ResolutionError message = new ResolutionError(kind, arguments); |
- compiler.reportError(node, message); |
- } |
-} |
- |
-class InitializerResolver { |
- final ResolverVisitor visitor; |
- final Map<SourceString, Node> initialized; |
- Link<Node> initializers; |
- bool hasSuper; |
- |
- InitializerResolver(this.visitor) |
- : initialized = new Map<SourceString, Node>(), hasSuper = false; |
- |
- error(Node node, MessageKind kind, [arguments = const []]) { |
- visitor.error(node, kind, arguments); |
- } |
- |
- warning(Node node, MessageKind kind, [arguments = const []]) { |
- visitor.warning(node, kind, arguments); |
- } |
- |
- bool isFieldInitializer(SendSet node) { |
- if (node.selector.asIdentifier() == null) return false; |
- if (node.receiver == null) return true; |
- if (node.receiver.asIdentifier() == null) return false; |
- return node.receiver.asIdentifier().isThis(); |
- } |
- |
- void resolveFieldInitializer(FunctionElement constructor, SendSet init) { |
- // init is of the form [this.]field = value. |
- final Node selector = init.selector; |
- final SourceString name = selector.asIdentifier().source; |
- // Lookup target field. |
- Element target; |
- if (isFieldInitializer(init)) { |
- final ClassElement classElement = constructor.enclosingElement; |
- target = classElement.lookupLocalMember(name); |
- if (target === null) { |
- error(selector, MessageKind.CANNOT_RESOLVE, [name]); |
- } else if (target.kind != ElementKind.FIELD) { |
- error(selector, MessageKind.NOT_A_FIELD, [name]); |
- } else if (!target.isInstanceMember()) { |
- error(selector, MessageKind.INIT_STATIC_FIELD, [name]); |
- } |
- } else { |
- error(init, MessageKind.INVALID_RECEIVER_IN_INITIALIZER); |
- } |
- visitor.useElement(init, target); |
- // Check for duplicate initializers. |
- if (initialized.containsKey(name)) { |
- error(init, MessageKind.DUPLICATE_INITIALIZER, [name]); |
- warning(initialized[name], MessageKind.ALREADY_INITIALIZED, [name]); |
- } |
- initialized[name] = init; |
- // Resolve initializing value. |
- visitor.visitInStaticContext(init.arguments.head); |
- } |
- |
- Element resolveSuperOrThis(FunctionElement constructor, |
- FunctionExpression functionNode, |
- Send call) { |
- noConstructor(e) { |
- if (e !== null) error(call, MessageKind.NO_CONSTRUCTOR, [e.name, e.kind]); |
- } |
- |
- ClassElement lookupTarget = constructor.enclosingElement; |
- bool validTarget = true; |
- FunctionElement result; |
- if (Initializers.isSuperConstructorCall(call)) { |
- // Check for invalid initializers. |
- if (hasSuper) { |
- error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER); |
- } |
- hasSuper = true; |
- // Calculate correct lookup target and constructor name. |
- if (lookupTarget.name == Types.OBJECT) { |
- error(call, MessageKind.SUPER_INITIALIZER_IN_OBJECT); |
- } else { |
- lookupTarget = lookupTarget.supertype.element; |
- } |
- } else if (Initializers.isConstructorRedirect(call)) { |
- // Check that there is no body (Language specification 7.5.1). |
- if (functionNode.hasBody()) { |
- error(functionNode, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_BODY); |
- } |
- // Check that there are no other initializers. |
- if (!initializers.tail.isEmpty()) { |
- error(call, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER); |
- } |
- } else { |
- visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED); |
- validTarget = false; |
- } |
- |
- if (validTarget) { |
- // Resolve the arguments, and make sure the call gets a selector |
- // by calling handleArguments. |
- visitor.inStaticContext( () => visitor.handleArguments(call) ); |
- // Lookup constructor and try to match it to the selector. |
- ResolverTask resolver = visitor.compiler.resolver; |
- result = resolver.lookupConstructor(lookupTarget, call); |
- if (result === null) { |
- SourceString constructorName = resolver.getConstructorName(call); |
- String className = lookupTarget.name.slowToString(); |
- String name = (constructorName === const SourceString('')) |
- ? className |
- : "$className.${constructorName.slowToString()}"; |
- error(call, MessageKind.CANNOT_RESOLVE_CONSTRUCTOR, [name]); |
- } else { |
- final Compiler compiler = visitor.compiler; |
- Selector selector = visitor.mapping.getSelector(call); |
- FunctionParameters parameters = result.computeParameters(compiler); |
- // TODO(karlklose): support optional arguments. |
- if (!selector.applies(parameters)) { |
- error(call, MessageKind.NO_MATCHING_CONSTRUCTOR); |
- } |
- } |
- visitor.useElement(call, result); |
- } |
- return result; |
- } |
- |
- FunctionElement resolveRedirection(FunctionElement constructor, |
- FunctionExpression functionNode) { |
- if (functionNode.initializers === null) return null; |
- Link<Node> link = functionNode.initializers.nodes; |
- if (!link.isEmpty() && Initializers.isConstructorRedirect(link.head)) { |
- return resolveSuperOrThis(constructor, functionNode, link.head); |
- } |
- return null; |
- } |
- |
- /** |
- * Resolve all initializers of this constructor. In the case of a redirecting |
- * constructor, the resolved constructor's function element is returned. |
- */ |
- FunctionElement resolveInitializers(FunctionElement constructor, |
- FunctionExpression functionNode) { |
- if (functionNode.initializers === null) return null; |
- initializers = functionNode.initializers.nodes; |
- FunctionElement result; |
- for (Link<Node> link = initializers; |
- !link.isEmpty(); |
- link = link.tail) { |
- if (link.head.asSendSet() != null) { |
- final SendSet init = link.head.asSendSet(); |
- resolveFieldInitializer(constructor, init); |
- } else if (link.head.asSend() !== null) { |
- final Send call = link.head.asSend(); |
- result = resolveSuperOrThis(constructor, functionNode, call); |
- } else { |
- error(link.head, MessageKind.INVALID_INITIALIZER); |
- } |
- } |
- return result; |
- } |
-} |
- |
-class CommonResolverVisitor<R> extends AbstractVisitor<R> { |
- final Compiler compiler; |
- |
- CommonResolverVisitor(Compiler this.compiler); |
- |
- R visitNode(Node node) { |
- cancel(node, 'internal error'); |
- } |
- |
- R visitEmptyStatement(Node node) => null; |
- |
- /** Convenience method for visiting nodes that may be null. */ |
- R visit(Node node) => (node == null) ? null : node.accept(this); |
- |
- void error(Node node, MessageKind kind, [arguments = const []]) { |
- ResolutionError message = new ResolutionError(kind, arguments); |
- compiler.reportError(node, message); |
- } |
- |
- void warning(Node node, MessageKind kind, [arguments = const []]) { |
- ResolutionWarning message = new ResolutionWarning(kind, arguments); |
- compiler.reportWarning(node, message); |
- } |
- |
- void cancel(Node node, String message) { |
- compiler.cancel(message, node: node); |
- } |
- |
- void internalError(Node node, String message) { |
- compiler.internalError(message, node: node); |
- } |
- |
- void unimplemented(Node node, String message) { |
- compiler.unimplemented(message, node: node); |
- } |
-} |
- |
-interface LabelScope { |
- LabelScope get outer(); |
- LabelElement lookup(String label); |
-} |
- |
-class LabeledStatementLabelScope implements LabelScope { |
- final LabelScope outer; |
- final LabelElement label; |
- LabeledStatementLabelScope(this.outer, this.label); |
- LabelElement lookup(String labelName) { |
- if (this.label.labelName == labelName) return label; |
- return outer.lookup(labelName); |
- } |
-} |
- |
-class SwitchLabelScope implements LabelScope { |
- final LabelScope outer; |
- final Map<String, LabelElement> caseLabels; |
- |
- SwitchLabelScope(this.outer, this.caseLabels); |
- |
- LabelElement lookup(String labelName) { |
- LabelElement result = caseLabels[labelName]; |
- if (result !== null) return result; |
- return outer.lookup(labelName); |
- } |
-} |
- |
-class EmptyLabelScope implements LabelScope { |
- const EmptyLabelScope(); |
- LabelElement lookup(String label) => null; |
- LabelScope get outer() { |
- throw 'internal error: empty label scope has no outer'; |
- } |
-} |
- |
-class StatementScope { |
- LabelScope labels; |
- Link<TargetElement> breakTargetStack; |
- Link<TargetElement> continueTargetStack; |
- // Used to provide different numbers to statements if one is inside the other. |
- // Can be used to make otherwise duplicate labels unique. |
- int nestingLevel = 0; |
- |
- StatementScope() |
- : labels = const EmptyLabelScope(), |
- breakTargetStack = const EmptyLink<TargetElement>(), |
- continueTargetStack = const EmptyLink<TargetElement>(); |
- |
- LabelElement lookupLabel(String label) { |
- return labels.lookup(label); |
- } |
- TargetElement currentBreakTarget() => |
- breakTargetStack.isEmpty() ? null : breakTargetStack.head; |
- |
- TargetElement currentContinueTarget() => |
- continueTargetStack.isEmpty() ? null : continueTargetStack.head; |
- |
- void enterLabelScope(LabelElement element) { |
- labels = new LabeledStatementLabelScope(labels, element); |
- nestingLevel++; |
- } |
- |
- void exitLabelScope() { |
- nestingLevel--; |
- labels = labels.outer; |
- } |
- |
- void enterLoop(TargetElement element) { |
- breakTargetStack = breakTargetStack.prepend(element); |
- continueTargetStack = continueTargetStack.prepend(element); |
- nestingLevel++; |
- } |
- |
- void exitLoop() { |
- nestingLevel--; |
- breakTargetStack = breakTargetStack.tail; |
- continueTargetStack = continueTargetStack.tail; |
- } |
- |
- void enterSwitch(TargetElement breakElement, |
- Map<String, LabelElement> continueElements) { |
- breakTargetStack = breakTargetStack.prepend(breakElement); |
- labels = new SwitchLabelScope(labels, continueElements); |
- nestingLevel++; |
- } |
- |
- void exitSwitch() { |
- nestingLevel--; |
- breakTargetStack = breakTargetStack.tail; |
- labels = labels.outer; |
- } |
-} |
- |
-class ResolverVisitor extends CommonResolverVisitor<Element> { |
- final TreeElementMapping mapping; |
- final Element enclosingElement; |
- bool inInstanceContext; |
- Scope context; |
- ClassElement currentClass; |
- bool typeRequired = false; |
- StatementScope statementScope; |
- int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION; |
- |
- ResolverVisitor(Compiler compiler, Element element) |
- : this.mapping = new TreeElementMapping(), |
- this.enclosingElement = element, |
- inInstanceContext = element.isInstanceMember() |
- || element.isGenerativeConstructor(), |
- this.context = element.isMember() |
- ? new ClassScope(element.enclosingElement, element.getLibrary()) |
- : new TopScope(element.getLibrary()), |
- this.currentClass = element.isMember() ? element.enclosingElement : null, |
- this.statementScope = new StatementScope(), |
- super(compiler); |
- |
- Element lookup(Node node, SourceString name) { |
- Element result = context.lookup(name); |
- if (!inInstanceContext && result != null && result.isInstanceMember()) { |
- error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]); |
- } |
- return result; |
- } |
- |
- // Create, or reuse an already created, statement element for a statement. |
- TargetElement getOrCreateTargetElement(Node statement) { |
- TargetElement element = mapping[statement]; |
- if (element === null) { |
- element = new TargetElement(statement, |
- statementScope.nestingLevel, |
- enclosingElement); |
- mapping[statement] = element; |
- } |
- return element; |
- } |
- |
- inStaticContext(action()) { |
- bool wasInstanceContext = inInstanceContext; |
- inInstanceContext = false; |
- var result = action(); |
- inInstanceContext = wasInstanceContext; |
- return result; |
- } |
- |
- visitInStaticContext(Node node) { |
- inStaticContext(() => visit(node)); |
- } |
- |
- Element visitIdentifier(Identifier node) { |
- if (node.isThis()) { |
- if (!inInstanceContext) { |
- error(node, MessageKind.NO_INSTANCE_AVAILABLE, [node]); |
- } |
- return null; |
- } else if (node.isSuper()) { |
- if (!inInstanceContext) error(node, MessageKind.NO_SUPER_IN_STATIC); |
- if ((ElementCategory.SUPER & allowedCategory) == 0) { |
- error(node, MessageKind.INVALID_USE_OF_SUPER); |
- } |
- return null; |
- } else { |
- Element element = lookup(node, node.source); |
- if (element === null) { |
- if (!inInstanceContext) error(node, MessageKind.CANNOT_RESOLVE, [node]); |
- } else { |
- if ((element.kind.category & allowedCategory) == 0) { |
- // TODO(ahe): Improve error message. Need UX input. |
- error(node, MessageKind.GENERIC, ["is not an expression $element"]); |
- } |
- } |
- return useElement(node, element); |
- } |
- } |
- |
- visitTypeAnnotation(TypeAnnotation node) { |
- Send send = node.typeName.asSend(); |
- Element element; |
- if (send !== null) { |
- if (typeRequired) { |
- element = resolveSend(send); |
- } else { |
- // Not calling resolveSend as it will emit an error instead of |
- // a warning if the type is bogus. |
- // TODO(ahe): Change resolveSend so it can emit a warning when needed. |
- return null; |
- } |
- } else { |
- element = context.lookup(node.typeName.asIdentifier().source); |
- } |
- if (element === null) { |
- if (typeRequired) { |
- error(node, MessageKind.CANNOT_RESOLVE_TYPE, [node.typeName]); |
- } else { |
- warning(node, MessageKind.CANNOT_RESOLVE_TYPE, [node.typeName]); |
- } |
- } else if (!element.impliesType()) { |
- if (typeRequired) { |
- error(node, MessageKind.NOT_A_TYPE, [node.typeName]); |
- } else { |
- warning(node, MessageKind.NOT_A_TYPE, [node.typeName]); |
- } |
- } else { |
- if (element.isClass()) { |
- // TODO(ngeoffray): Should we also resolve typedef? |
- ClassElement cls = element; |
- compiler.resolver.toResolve.add(element); |
- } |
- // TODO(ahe): This should be a Type. |
- useElement(node, element); |
- } |
- return element; |
- } |
- |
- Element defineElement(Node node, Element element, |
- [bool doAddToScope = true]) { |
- compiler.ensure(element !== null); |
- mapping[node] = element; |
- if (doAddToScope) { |
- Element existing = context.add(element); |
- if (existing != element) { |
- error(node, MessageKind.DUPLICATE_DEFINITION, [node]); |
- } |
- } |
- return element; |
- } |
- |
- Element useElement(Node node, Element element) { |
- if (element === null) return null; |
- return mapping[node] = element; |
- } |
- |
- void setupFunction(FunctionExpression node, FunctionElement function) { |
- context = new MethodScope(context, function); |
- // Put the parameters in scope. |
- FunctionParameters functionParameters = |
- function.computeParameters(compiler); |
- Link<Node> parameterNodes = node.parameters.nodes; |
- functionParameters.forEachParameter((Element element) { |
- if (element == functionParameters.optionalParameters.head) { |
- NodeList nodes = parameterNodes.head; |
- parameterNodes = nodes.nodes; |
- } |
- VariableDefinitions variableDefinitions = parameterNodes.head; |
- Node parameterNode = variableDefinitions.definitions.nodes.head; |
- initializerDo(parameterNode, (n) => n.accept(this)); |
- // Field parameters (this.x) are not visible inside the constructor. The |
- // fields they reference are visible, but must be resolved independently. |
- if (element.kind == ElementKind.FIELD_PARAMETER) { |
- useElement(parameterNode, element); |
- } else { |
- defineElement(variableDefinitions.definitions.nodes.head, element); |
- } |
- parameterNodes = parameterNodes.tail; |
- }); |
- } |
- |
- Element visitClassNode(ClassNode node) { |
- cancel(node, "shouldn't be called"); |
- } |
- |
- visitIn(Node node, Scope scope) { |
- context = scope; |
- Element element = visit(node); |
- context = context.parent; |
- return element; |
- } |
- |
- /** |
- * Introduces new default targets for break and continue |
- * before visiting the body of the loop |
- */ |
- visitLoopBodyIn(Node loop, Node body, Scope scope) { |
- TargetElement element = getOrCreateTargetElement(loop); |
- statementScope.enterLoop(element); |
- visitIn(body, scope); |
- statementScope.exitLoop(); |
- if (!element.isTarget) { |
- mapping.remove(loop); |
- } |
- } |
- |
- visitBlock(Block node) { |
- visitIn(node.statements, new BlockScope(context)); |
- } |
- |
- visitDoWhile(DoWhile node) { |
- visitLoopBodyIn(node, node.body, new BlockScope(context)); |
- visit(node.condition); |
- } |
- |
- visitEmptyStatement(EmptyStatement node) { } |
- |
- visitExpressionStatement(ExpressionStatement node) { |
- visit(node.expression); |
- } |
- |
- visitFor(For node) { |
- Scope scope = new BlockScope(context); |
- visitIn(node.initializer, scope); |
- visitIn(node.condition, scope); |
- visitIn(node.update, scope); |
- visitLoopBodyIn(node, node.body, scope); |
- } |
- |
- visitFunctionDeclaration(FunctionDeclaration node) { |
- assert(node.function.name !== null); |
- visit(node.function); |
- FunctionElement functionElement = mapping[node.function]; |
- // TODO(floitsch): this might lead to two errors complaining about |
- // shadowing. |
- defineElement(node, functionElement); |
- } |
- |
- visitFunctionExpression(FunctionExpression node) { |
- visit(node.returnType); |
- SourceString name; |
- if (node.name === null) { |
- name = const SourceString(""); |
- } else { |
- name = node.name.asIdentifier().source; |
- } |
- FunctionElement enclosing = new FunctionElement.node( |
- name, node, ElementKind.FUNCTION, new Modifiers.empty(), |
- context.element); |
- setupFunction(node, enclosing); |
- defineElement(node, enclosing, doAddToScope: node.name !== null); |
- |
- // Run the body in a fresh statement scope. |
- StatementScope oldScope = statementScope; |
- statementScope = new StatementScope(); |
- visit(node.body); |
- statementScope = oldScope; |
- |
- context = context.parent; |
- } |
- |
- visitIf(If node) { |
- visit(node.condition); |
- visit(node.thenPart); |
- visit(node.elsePart); |
- } |
- |
- static bool isLogicalOperator(Identifier op) { |
- String str = op.source.stringValue; |
- return (str === '&&' || str == '||' || str == '!'); |
- } |
- |
- Element resolveSend(Send node) { |
- if (node.receiver === null) { |
- return node.selector.accept(this); |
- } |
- var oldCategory = allowedCategory; |
- allowedCategory |= |
- ElementCategory.CLASS | ElementCategory.PREFIX | ElementCategory.SUPER; |
- Element resolvedReceiver = visit(node.receiver); |
- allowedCategory = oldCategory; |
- |
- Element target; |
- SourceString name = node.selector.asIdentifier().source; |
- if (name.stringValue === 'this') { |
- error(node.selector, MessageKind.GENERIC, ["expected an identifier"]); |
- } else if (node.isSuperCall) { |
- if (node.isOperator) { |
- if (isUserDefinableOperator(name.stringValue)) { |
- name = Elements.constructOperatorName(const SourceString('operator'), |
- name); |
- } else { |
- error(node.selector, MessageKind.ILLEGAL_SUPER_SEND, [name]); |
- } |
- } |
- if (!inInstanceContext) { |
- error(node.receiver, MessageKind.NO_INSTANCE_AVAILABLE, [name]); |
- return null; |
- } |
- if (currentClass.supertype === null) { |
- // This is just to guard against internal errors, so no need |
- // for a real error message. |
- error(node.receiver, MessageKind.GENERIC, ["Object has no superclass"]); |
- } |
- target = currentClass.lookupSuperMember(name); |
- // [target] may be null which means invoking noSuchMethod on |
- // super. |
- } else if (resolvedReceiver === null) { |
- return null; |
- } else if (resolvedReceiver.kind === ElementKind.CLASS) { |
- ClassElement receiverClass = resolvedReceiver; |
- target = receiverClass.ensureResolved(compiler).lookupLocalMember(name); |
- if (target === null) { |
- error(node, MessageKind.METHOD_NOT_FOUND, [receiverClass.name, name]); |
- } else if (target.isInstanceMember()) { |
- error(node, MessageKind.MEMBER_NOT_STATIC, [receiverClass.name, name]); |
- } |
- } else if (resolvedReceiver.kind === ElementKind.PREFIX) { |
- PrefixElement prefix = resolvedReceiver; |
- target = prefix.lookupLocalMember(name); |
- if (target == null) { |
- error(node, MessageKind.NO_SUCH_LIBRARY_MEMBER, [prefix.name, name]); |
- } |
- } |
- return target; |
- } |
- |
- resolveTypeTest(Node argument) { |
- TypeAnnotation node = argument.asTypeAnnotation(); |
- if (node == null) { |
- node = argument.asSend().receiver; |
- } |
- resolveTypeRequired(node); |
- } |
- |
- void handleArguments(Send node) { |
- int count = 0; |
- List<SourceString> namedArguments = <SourceString>[]; |
- bool seenNamedArgument = false; |
- for (Link<Node> link = node.argumentsNode.nodes; |
- !link.isEmpty(); |
- link = link.tail) { |
- count++; |
- Expression argument = link.head; |
- visit(argument); |
- if (argument.asNamedArgument() != null) { |
- seenNamedArgument = true; |
- NamedArgument named = argument; |
- namedArguments.add(named.name.source); |
- } else if (seenNamedArgument) { |
- error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED); |
- } |
- } |
- mapping.setSelector(node, new Invocation(count, namedArguments)); |
- } |
- |
- visitSend(Send node) { |
- Element target = resolveSend(node); |
- if (node.isOperator) { |
- Operator op = node.selector.asOperator(); |
- if (op.source.stringValue === 'is') { |
- resolveTypeTest(node.arguments.head); |
- assert(node.arguments.tail.isEmpty()); |
- mapping.setSelector(node, Selector.BINARY_OPERATOR); |
- } else if (node.arguments.isEmpty()) { |
- assert(op.token.kind !== PLUS_TOKEN); |
- mapping.setSelector(node, Selector.UNARY_OPERATOR); |
- } else { |
- visit(node.argumentsNode); |
- mapping.setSelector(node, Selector.BINARY_OPERATOR); |
- } |
- } else if (node.isIndex) { |
- visit(node.argumentsNode); |
- assert(node.arguments.tail.isEmpty()); |
- mapping.setSelector(node, Selector.INDEX); |
- } else if (node.isPropertyAccess) { |
- mapping.setSelector(node, Selector.GETTER); |
- } else { |
- handleArguments(node); |
- } |
- if (target != null && target.kind == ElementKind.ABSTRACT_FIELD) { |
- AbstractFieldElement field = target; |
- target = field.getter; |
- } |
- // TODO(ngeoffray): Warn if target is null and the send is |
- // unqualified. |
- useElement(node, target); |
- if (node.isPropertyAccess) return target; |
- } |
- |
- visitSendSet(SendSet node) { |
- Element target = resolveSend(node); |
- Element setter = null; |
- Element getter = null; |
- if (target != null && target.kind == ElementKind.ABSTRACT_FIELD) { |
- AbstractFieldElement field = target; |
- setter = field.setter; |
- getter = field.getter; |
- } else { |
- setter = target; |
- getter = target; |
- } |
- // TODO(ngeoffray): Check if the target can be assigned. |
- Identifier op = node.assignmentOperator; |
- bool needsGetter = op.source.stringValue !== '='; |
- Selector selector; |
- if (needsGetter) { |
- if (node.isIndex) { |
- selector = Selector.INDEX_AND_INDEX_SET; |
- } else { |
- selector = Selector.GETTER_AND_SETTER; |
- } |
- useElement(node.selector, getter); |
- } else if (node.isIndex) { |
- selector = Selector.INDEX_SET; |
- } else { |
- selector = Selector.SETTER; |
- } |
- visit(node.argumentsNode); |
- mapping.setSelector(node, selector); |
- // TODO(ngeoffray): Warn if target is null and the send is |
- // unqualified. |
- return useElement(node, setter); |
- } |
- |
- visitLiteralInt(LiteralInt node) { |
- } |
- |
- visitLiteralDouble(LiteralDouble node) { |
- } |
- |
- visitLiteralBool(LiteralBool node) { |
- } |
- |
- visitLiteralString(LiteralString node) { |
- } |
- |
- visitLiteralNull(LiteralNull node) { |
- } |
- |
- visitStringJuxtaposition(StringJuxtaposition node) { |
- node.visitChildren(this); |
- } |
- |
- visitNodeList(NodeList node) { |
- for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { |
- visit(link.head); |
- } |
- } |
- |
- visitOperator(Operator node) { |
- unimplemented(node, 'operator'); |
- } |
- |
- visitReturn(Return node) { |
- visit(node.expression); |
- } |
- |
- visitThrow(Throw node) { |
- visit(node.expression); |
- } |
- |
- visitVariableDefinitions(VariableDefinitions node) { |
- visit(node.type); |
- VariableDefinitionsVisitor visitor = |
- new VariableDefinitionsVisitor(compiler, node, this, |
- ElementKind.VARIABLE); |
- visitor.visit(node.definitions); |
- } |
- |
- visitWhile(While node) { |
- visit(node.condition); |
- visitLoopBodyIn(node, node.body, new BlockScope(context)); |
- } |
- |
- visitParenthesizedExpression(ParenthesizedExpression node) { |
- visit(node.expression); |
- } |
- |
- visitNewExpression(NewExpression node) { |
- Node selector = node.send.selector; |
- |
- FunctionElement constructor = resolveConstructor(node); |
- handleArguments(node.send); |
- if (constructor === null) return null; |
- // TODO(karlklose): handle optional arguments. |
- if (node.send.argumentCount() != constructor.parameterCount(compiler)) { |
- // TODO(ngeoffray): resolution error with wrong number of |
- // parameters. We cannot do this rigth now because of the |
- // List constructor. |
- } |
- useElement(node.send, constructor); |
- return null; |
- } |
- |
- FunctionElement resolveConstructor(NewExpression node) { |
- FunctionElement constructor = |
- node.accept(new ConstructorResolver(compiler, this)); |
- if (constructor === null) { |
- Element resolved = resolveTypeRequired(node.send.selector); |
- if (resolved !== null && resolved.kind === ElementKind.TYPE_VARIABLE) { |
- error(node, WarningKind.TYPE_VARIABLE_AS_CONSTRUCTOR); |
- return null; |
- } else { |
- error(node.send, MessageKind.CANNOT_FIND_CONSTRUCTOR, [node.send]); |
- } |
- } |
- return constructor; |
- } |
- |
- Element resolveTypeRequired(Node node) { |
- bool old = typeRequired; |
- typeRequired = true; |
- Element element = visit(node); |
- typeRequired = old; |
- return element; |
- } |
- |
- visitModifiers(Modifiers node) { |
- // TODO(ngeoffray): Implement this. |
- unimplemented(node, 'modifiers'); |
- } |
- |
- visitLiteralList(LiteralList node) { |
- visit(node.elements); |
- } |
- |
- visitConditional(Conditional node) { |
- node.visitChildren(this); |
- } |
- |
- visitStringInterpolation(StringInterpolation node) { |
- node.visitChildren(this); |
- } |
- |
- visitStringInterpolationPart(StringInterpolationPart node) { |
- node.visitChildren(this); |
- } |
- |
- visitBreakStatement(BreakStatement node) { |
- TargetElement target; |
- if (node.target === null) { |
- target = statementScope.currentBreakTarget(); |
- if (target === null) { |
- error(node, MessageKind.NO_BREAK_TARGET); |
- return; |
- } |
- target.isBreakTarget = true; |
- } else { |
- String labelName = node.target.source.slowToString(); |
- LabelElement label = statementScope.lookupLabel(labelName); |
- if (label === null) { |
- error(node.target, MessageKind.UNBOUND_LABEL, [labelName]); |
- return; |
- } |
- target = label.target; |
- if (!target.statement.isValidBreakTarget()) { |
- error(node.target, MessageKind.INVALID_BREAK, [labelName]); |
- return; |
- } |
- label.setBreakTarget(); |
- mapping[node.target] = label; |
- } |
- mapping[node] = target; |
- } |
- |
- visitContinueStatement(ContinueStatement node) { |
- TargetElement target; |
- if (node.target === null) { |
- target = statementScope.currentContinueTarget(); |
- if (target === null) { |
- error(node, MessageKind.NO_CONTINUE_TARGET); |
- return; |
- } |
- target.isContinueTarget = true; |
- } else { |
- String labelName = node.target.source.slowToString(); |
- LabelElement label = statementScope.lookupLabel(labelName); |
- if (label === null) { |
- error(node.target, MessageKind.UNBOUND_LABEL, [labelName]); |
- return; |
- } |
- target = label.target; |
- if (!target.statement.isValidContinueTarget()) { |
- error(node.target, MessageKind.INVALID_CONTINUE, [labelName]); |
- } |
- label.setContinueTarget(); |
- } |
- mapping[node] = target; |
- } |
- |
- visitForInStatement(ForInStatement node) { |
- visit(node.expression); |
- Scope scope = new BlockScope(context); |
- Node declaration = node.declaredIdentifier; |
- visitIn(declaration, scope); |
- visitLoopBodyIn(node, node.body, scope); |
- |
- // TODO(lrn): Also allow a single identifier. |
- if ((declaration is !Send || declaration.asSend().selector is !Identifier) |
- && (declaration is !VariableDefinitions || |
- !declaration.asVariableDefinitions().definitions.nodes.tail.isEmpty())) |
- { |
- // The variable declaration is either not an identifier, not a |
- // declaration, or it's declaring more than one variable. |
- error(node.declaredIdentifier, MessageKind.INVALID_FOR_IN, []); |
- } |
- } |
- |
- visitLabeledStatement(LabeledStatement node) { |
- String labelName = node.label.source.slowToString(); |
- LabelElement existingElement = statementScope.lookupLabel(labelName); |
- if (existingElement !== null) { |
- warning(node.label, MessageKind.DUPLICATE_LABEL, [labelName]); |
- warning(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]); |
- } |
- Node body = node.getBody(); |
- TargetElement targetElement = getOrCreateTargetElement(body); |
- |
- LabelElement element = targetElement.addLabel(node.label, labelName); |
- statementScope.enterLabelScope(element); |
- visit(node.statement); |
- statementScope.exitLabelScope(); |
- if (element.isTarget) { |
- mapping[node.label] = element; |
- } else { |
- warning(node.label, MessageKind.UNUSED_LABEL, [labelName]); |
- } |
- if (!targetElement.isTarget && mapping[body] === targetElement) { |
- // If the body is itself a break or continue for another target, it |
- // might have updated its mapping to the target it actually does target. |
- mapping.remove(body); |
- } |
- } |
- |
- visitLiteralMap(LiteralMap node) { |
- node.visitChildren(this); |
- } |
- |
- visitLiteralMapEntry(LiteralMapEntry node) { |
- node.visitChildren(this); |
- } |
- |
- visitNamedArgument(NamedArgument node) { |
- visit(node.expression); |
- } |
- |
- visitSwitchStatement(SwitchStatement node) { |
- node.expression.accept(this); |
- |
- TargetElement breakElement = getOrCreateTargetElement(node); |
- Map<String, LabelElement> continueLabels = <LabelElement>{}; |
- Link<Node> cases = node.cases.nodes; |
- while (!cases.isEmpty()) { |
- SwitchCase switchCase = cases.head; |
- if (switchCase.label !== null) { |
- Identifier labelIdentifier = switchCase.label; |
- String labelName = labelIdentifier.source.slowToString(); |
- |
- LabelElement existingElement = continueLabels[labelName]; |
- if (existingElement !== null) { |
- // It's an error if the same label occurs twice in the same switch. |
- warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]); |
- error(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]); |
- } else { |
- // It's only a warning if it shadows another label. |
- existingElement = statementScope.lookupLabel(labelName); |
- if (existingElement !== null) { |
- warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]); |
- warning(existingElement.label, |
- MessageKind.EXISTING_LABEL, [labelName]); |
- } |
- } |
- |
- TargetElement TargetElement = |
- new TargetElement(switchCase, |
- statementScope.nestingLevel, |
- enclosingElement); |
- mapping[switchCase] = TargetElement; |
- |
- LabelElement label = |
- new LabelElement(labelIdentifier, labelName, |
- TargetElement, enclosingElement); |
- mapping[labelIdentifier] = label; |
- continueLabels[labelName] = label; |
- } |
- cases = cases.tail; |
- if (switchCase.defaultKeyword !== null && !cases.isEmpty()) { |
- error(switchCase, MessageKind.INVALID_CASE_DEFAULT); |
- } |
- } |
- statementScope.enterSwitch(breakElement, continueLabels); |
- node.cases.accept(this); |
- statementScope.exitSwitch(); |
- |
- // Clean-up unused labels |
- continueLabels.forEach((String key, LabelElement label) { |
- TargetElement TargetElement = label.target; |
- SwitchCase switchCase = TargetElement.statement; |
- if (!label.isContinueTarget) { |
- mapping.remove(switchCase); |
- mapping.remove(label.label); |
- } |
- }); |
- } |
- |
- visitSwitchCase(SwitchCase node) { |
- // The label was handled in [visitSwitchStatement(SwitchStatement)]. |
- node.expressions.accept(this); |
- visitIn(node.statements, new BlockScope(context)); |
- } |
- |
- visitTryStatement(TryStatement node) { |
- visit(node.tryBlock); |
- if (node.catchBlocks.isEmpty() && node.finallyBlock == null) { |
- // TODO(ngeoffray): The precise location is |
- // node.getEndtoken.next. Adjust when issue #1581 is fixed. |
- error(node, MessageKind.NO_CATCH_NOR_FINALLY); |
- } |
- visit(node.catchBlocks); |
- visit(node.finallyBlock); |
- } |
- |
- visitCatchBlock(CatchBlock node) { |
- Scope scope = new BlockScope(context); |
- if (node.formals.isEmpty()) { |
- error(node, MessageKind.EMPTY_CATCH_DECLARATION); |
- } else if (!node.formals.nodes.tail.isEmpty() |
- && !node.formals.nodes.tail.tail.isEmpty()) { |
- for (Node extra in node.formals.nodes.tail.tail) { |
- error(extra, MessageKind.EXTRA_CATCH_DECLARATION); |
- } |
- } |
- visitIn(node.formals, scope); |
- visitIn(node.block, scope); |
- } |
- |
- visitTypedef(Typedef node) { |
- unimplemented(node, 'typedef'); |
- } |
-} |
- |
-class ClassResolverVisitor extends CommonResolverVisitor<Type> { |
- Scope context; |
- ClassElement classElement; |
- |
- ClassResolverVisitor(Compiler compiler, LibraryElement library, |
- ClassElement this.classElement) |
- : context = new TopScope(library), |
- super(compiler); |
- |
- Type visitClassNode(ClassNode node) { |
- compiler.ensure(classElement !== null); |
- compiler.ensure(!classElement.isResolved); |
- final Link<Node> parameters = |
- node.typeParameters !== null ? node.typeParameters.nodes |
- : const EmptyLink<TypeVariable>(); |
- // Create types and elements for type variable. |
- for (Link<Node> link = parameters; !link.isEmpty(); link = link.tail) { |
- TypeVariable typeNode = link.head; |
- SourceString variableName = typeNode.name.source; |
- TypeVariableType variableType = new TypeVariableType(variableName); |
- TypeVariableElement variableElement = |
- new TypeVariableElement(variableName, classElement, node, |
- variableType); |
- variableType.element = variableElement; |
- classElement.typeParameters[variableName] = variableElement; |
- context = new TypeVariablesScope(context, classElement); |
- } |
- // Resolve the bounds of type variables. |
- for (Link<Node> link = parameters; !link.isEmpty(); link = link.tail) { |
- TypeVariable typeNode = link.head; |
- SourceString variableName = typeNode.name.source; |
- TypeVariableElement variableElement = |
- classElement.typeParameters[variableName]; |
- if (typeNode.bound !== null) { |
- Type boundType = visit(typeNode.bound); |
- if (boundType !== null && boundType.element == variableElement) { |
- warning(node, MessageKind.CYCLIC_TYPE_VARIABLE, |
- [variableElement.name]); |
- } else if (boundType !== null) { |
- variableElement.bound = boundType; |
- } else { |
- variableElement.bound = compiler.objectClass.computeType(compiler); |
- } |
- } |
- } |
- // Find super type. |
- Type supertype = visit(node.superclass); |
- if (supertype !== null && supertype.element.isExtendable()) { |
- classElement.supertype = supertype; |
- if (isBlackListed(supertype)) { |
- error(node.superclass, MessageKind.CANNOT_EXTEND, [supertype]); |
- } |
- } else if (supertype !== null) { |
- error(node.superclass, MessageKind.TYPE_NAME_EXPECTED); |
- } |
- if (classElement.name != Types.OBJECT && classElement.supertype === null) { |
- ClassElement objectElement = context.lookup(Types.OBJECT); |
- if (objectElement !== null && !objectElement.isResolved) { |
- compiler.resolver.toResolve.add(objectElement); |
- } else if (objectElement === null){ |
- error(node, MessageKind.CANNOT_RESOLVE_TYPE, [Types.OBJECT]); |
- } |
- classElement.supertype = new SimpleType(Types.OBJECT, objectElement); |
- } |
- if (node.defaultClause !== null) { |
- classElement.defaultClass = visit(node.defaultClause); |
- } |
- for (Link<Node> link = node.interfaces.nodes; |
- !link.isEmpty(); |
- link = link.tail) { |
- Type interfaceType = visit(link.head); |
- if (interfaceType !== null && interfaceType.element.isExtendable()) { |
- classElement.interfaces = |
- classElement.interfaces.prepend(interfaceType); |
- if (isBlackListed(interfaceType)) { |
- error(link.head, MessageKind.CANNOT_IMPLEMENT, [interfaceType]); |
- } |
- } else { |
- error(link.head, MessageKind.TYPE_NAME_EXPECTED); |
- } |
- } |
- calculateAllSupertypes(classElement, new Set<ClassElement>()); |
- addDefaultConstructorIfNeeded(classElement); |
- return classElement.computeType(compiler); |
- } |
- |
- Type visitTypeAnnotation(TypeAnnotation node) { |
- return visit(node.typeName); |
- } |
- |
- Type visitIdentifier(Identifier node) { |
- Element element = context.lookup(node.source); |
- if (element === null) { |
- error(node, MessageKind.CANNOT_RESOLVE_TYPE, [node]); |
- return null; |
- } else if (!element.impliesType() && !element.isTypeVariable()) { |
- error(node, MessageKind.NOT_A_TYPE, [node]); |
- return null; |
- } else { |
- if (element.isClass()) { |
- compiler.resolver.toResolve.add(element); |
- } |
- if (element.isTypeVariable()) { |
- TypeVariableElement variableElement = element; |
- return variableElement.type; |
- } else if (element.isTypedef()) { |
- compiler.unimplemented('visitIdentifier for typedefs'); |
- } else { |
- // TODO(ngeoffray): Use type variables. |
- return element.computeType(compiler); |
- } |
- } |
- return null; |
- } |
- |
- Type visitSend(Send node) { |
- Identifier prefix = node.receiver.asIdentifier(); |
- if (prefix === null) { |
- error(node.receiver, MessageKind.NOT_A_PREFIX, [node.receiver]); |
- return null; |
- } |
- Element element = context.lookup(prefix.source); |
- if (element === null || element.kind !== ElementKind.PREFIX) { |
- error(node.receiver, MessageKind.NOT_A_PREFIX, [node.receiver]); |
- return null; |
- } |
- PrefixElement prefixElement = element; |
- Identifier selector = node.selector.asIdentifier(); |
- var e = prefixElement.lookupLocalMember(selector.source); |
- if (e === null || !e.impliesType()) { |
- error(node.selector, MessageKind.CANNOT_RESOLVE_TYPE, [node.selector]); |
- return null; |
- } |
- return e.computeType(compiler); |
- } |
- |
- Link<Type> getOrCalculateAllSupertypes(ClassElement classElement, |
- [Set<ClassElement> seen]) { |
- Link<Type> allSupertypes = classElement.allSupertypes; |
- if (allSupertypes !== null) return allSupertypes; |
- if (seen === null) { |
- seen = new Set<ClassElement>(); |
- } |
- if (seen.contains(classElement)) { |
- error(classElement.parseNode(compiler), |
- MessageKind.CYCLIC_CLASS_HIERARCHY, |
- [classElement.name]); |
- classElement.allSupertypes = const EmptyLink<Type>(); |
- } else { |
- classElement.ensureResolved(compiler); |
- calculateAllSupertypes(classElement, seen); |
- } |
- return classElement.allSupertypes; |
- } |
- |
- void calculateAllSupertypes(ClassElement classElement, |
- Set<ClassElement> seen) { |
- // TODO(karlklose): substitute type variables. |
- // TODO(karlklose): check if type arguments match, if a classelement occurs |
- // more than once in the supertypes. |
- if (classElement.allSupertypes !== null) return; |
- final Type supertype = classElement.supertype; |
- if (seen.contains(classElement)) { |
- error(classElement.parseNode(compiler), |
- MessageKind.CYCLIC_CLASS_HIERARCHY, |
- [classElement.name]); |
- classElement.allSupertypes = const EmptyLink<Type>(); |
- } else if (supertype != null) { |
- seen.add(classElement); |
- Link<Type> superSupertypes = |
- getOrCalculateAllSupertypes(supertype.element, seen); |
- Link<Type> supertypes = new Link<Type>(supertype, superSupertypes); |
- for (Link<Type> interfaces = classElement.interfaces; |
- !interfaces.isEmpty(); |
- interfaces = interfaces.tail) { |
- Element element = interfaces.head.element; |
- Link<Type> interfaceSupertypes = |
- getOrCalculateAllSupertypes(element, seen); |
- supertypes = supertypes.reversePrependAll(interfaceSupertypes); |
- supertypes = supertypes.prepend(interfaces.head); |
- } |
- seen.remove(classElement); |
- classElement.allSupertypes = supertypes; |
- } else { |
- classElement.allSupertypes = const EmptyLink<Type>(); |
- } |
- } |
- |
- /** |
- * Add a synthetic nullary constructor if there are no other |
- * constructors. |
- */ |
- void addDefaultConstructorIfNeeded(ClassElement element) { |
- if (element.constructors.length != 0) return; |
- SynthesizedConstructorElement constructor = |
- new SynthesizedConstructorElement(element); |
- element.constructors[element.name] = constructor; |
- Type returnType = compiler.types.voidType; |
- constructor.type = new FunctionType(returnType, const EmptyLink<Type>(), |
- constructor); |
- constructor.cachedNode = |
- new FunctionExpression(new Identifier(element.position()), |
- new NodeList.empty(), |
- new Block(new NodeList.empty()), |
- null, null, null, null); |
- } |
- |
- isBlackListed(Type type) { |
- LibraryElement lib = classElement.getLibrary(); |
- return |
- lib !== compiler.coreLibrary && |
- lib !== compiler.coreImplLibrary && |
- lib !== compiler.jsHelperLibrary && |
- (type.element === compiler.dynamicClass || |
- type.element === compiler.boolClass || |
- type.element === compiler.numClass || |
- type.element === compiler.intClass || |
- type.element === compiler.doubleClass || |
- type.element === compiler.stringClass || |
- type.element === compiler.nullClass || |
- type.element === compiler.functionClass); |
- } |
-} |
- |
-class VariableDefinitionsVisitor extends CommonResolverVisitor<SourceString> { |
- VariableDefinitions definitions; |
- ResolverVisitor resolver; |
- ElementKind kind; |
- VariableListElement variables; |
- |
- VariableDefinitionsVisitor(Compiler compiler, |
- this.definitions, this.resolver, this.kind) |
- : super(compiler) |
- { |
- variables = new VariableListElement.node( |
- definitions, ElementKind.VARIABLE_LIST, resolver.context.element); |
- } |
- |
- SourceString visitSendSet(SendSet node) { |
- assert(node.arguments.tail.isEmpty()); // Sanity check |
- resolver.visit(node.arguments.head); |
- return visit(node.selector); |
- } |
- |
- SourceString visitIdentifier(Identifier node) => node.source; |
- |
- visitNodeList(NodeList node) { |
- for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { |
- SourceString name = visit(link.head); |
- VariableElement element = new VariableElement( |
- name, variables, kind, resolver.context.element, node: link.head); |
- resolver.defineElement(link.head, element); |
- } |
- } |
-} |
- |
-class SignatureResolver extends CommonResolverVisitor<Element> { |
- final Element enclosingElement; |
- Link<Element> optionalParameters = const EmptyLink<Element>(); |
- int optionalParameterCount = 0; |
- Node currentDefinitions; |
- |
- SignatureResolver(Compiler compiler, this.enclosingElement) : super(compiler); |
- |
- Element visitNodeList(NodeList node) { |
- // This must be a list of optional arguments. |
- if (node.beginToken.stringValue !== '[') { |
- internalError(node, "expected optional parameters"); |
- } |
- LinkBuilder<Element> elements = analyzeNodes(node.nodes); |
- optionalParameterCount = elements.length; |
- optionalParameters = elements.toLink(); |
- return null; |
- } |
- |
- Element visitVariableDefinitions(VariableDefinitions node) { |
- resolveType(node.type); |
- |
- Link<Node> definitions = node.definitions.nodes; |
- if (definitions.isEmpty()) { |
- cancel(node, 'internal error: no parameter definition'); |
- return null; |
- } |
- if (!definitions.tail.isEmpty()) { |
- cancel(definitions.tail.head, 'internal error: extra definition'); |
- return null; |
- } |
- Node definition = definitions.head; |
- if (definition is NodeList) { |
- cancel(node, 'optional parameters are not implemented'); |
- } |
- |
- if (currentDefinitions != null) { |
- cancel(node, 'function type parameters not supported'); |
- } |
- currentDefinitions = node; |
- Element element = definition.accept(this); |
- currentDefinitions = null; |
- return element; |
- } |
- |
- Element visitIdentifier(Identifier node) { |
- Element variables = new VariableListElement.node(currentDefinitions, |
- ElementKind.VARIABLE_LIST, enclosingElement); |
- return new VariableElement(node.source, variables, |
- ElementKind.PARAMETER, enclosingElement, node: node); |
- } |
- |
- // The only valid [Send] can be in constructors and must be of the form |
- // [:this.x:] (where [:x:] represents an instance field). |
- FieldParameterElement visitSend(Send node) { |
- FieldParameterElement element; |
- if (node.receiver.asIdentifier() === null || |
- !node.receiver.asIdentifier().isThis()) { |
- error(node, MessageKind.INVALID_PARAMETER, []); |
- } else if (enclosingElement.kind !== ElementKind.GENERATIVE_CONSTRUCTOR) { |
- error(node, MessageKind.FIELD_PARAMETER_NOT_ALLOWED, []); |
- } else { |
- if (node.selector.asIdentifier() == null) { |
- cancel(node, |
- 'internal error: unimplemented receiver on parameter send'); |
- } |
- SourceString name = node.selector.asIdentifier().source; |
- Element fieldElement = currentClass.lookupLocalMember(name); |
- if (fieldElement === null || fieldElement.kind !== ElementKind.FIELD) { |
- error(node, MessageKind.NOT_A_FIELD, [name]); |
- } else if (!fieldElement.isInstanceMember()) { |
- error(node, MessageKind.NOT_INSTANCE_FIELD, [name]); |
- } |
- Element variables = new VariableListElement.node(currentDefinitions, |
- ElementKind.VARIABLE_LIST, enclosingElement); |
- element = new FieldParameterElement(node.selector.asIdentifier().source, |
- fieldElement, variables, enclosingElement, node); |
- } |
- return element; |
- } |
- |
- Element visitSendSet(SendSet node) { |
- Element element; |
- if (node.receiver != null) { |
- element = visitSend(node); |
- } else if (node.selector.asIdentifier() != null) { |
- Element variables = new VariableListElement.node(currentDefinitions, |
- ElementKind.VARIABLE_LIST, enclosingElement); |
- element = new VariableElement(node.selector.asIdentifier().source, |
- variables, ElementKind.PARAMETER, enclosingElement, node: node); |
- } |
- // Visit the value. The compile time constant handler will |
- // make sure it's a compile time constant. |
- resolveExpression(node.arguments.head); |
- compiler.enqueue(new WorkItem.toCompile(element)); |
- return element; |
- } |
- |
- Element visitFunctionExpression(FunctionExpression node) { |
- // This is a function typed parameter. |
- // TODO(ahe): Resolve the function type. |
- return visit(node.name); |
- } |
- |
- LinkBuilder<Element> analyzeNodes(Link<Node> link) { |
- LinkBuilder<Element> elements = new LinkBuilder<Element>(); |
- for (; !link.isEmpty(); link = link.tail) { |
- Element element = link.head.accept(this); |
- if (element != null) { |
- elements.addLast(element); |
- } else { |
- // If parameter is null, the current node should be the last, |
- // and a list of optional named parameters. |
- if (!link.tail.isEmpty() || (link.head is !NodeList)) { |
- internalError(link.head, "expected optional parameters"); |
- } |
- } |
- } |
- return elements; |
- } |
- |
- static FunctionParameters analyze(Compiler compiler, |
- FunctionElement element) { |
- FunctionExpression node = element.parseNode(compiler); |
- SignatureResolver visitor = new SignatureResolver(compiler, element); |
- Link<Node> nodes = node.parameters.nodes; |
- LinkBuilder<Element> parameters = visitor.analyzeNodes(nodes); |
- return new FunctionParameters(parameters.toLink(), |
- visitor.optionalParameters, |
- parameters.length, |
- visitor.optionalParameterCount); |
- } |
- |
- // TODO(ahe): This is temporary. |
- void resolveExpression(Node node) { |
- if (node == null) return; |
- node.accept(new ResolverVisitor(compiler, enclosingElement)); |
- } |
- |
- // TODO(ahe): This is temporary. |
- void resolveType(Node node) { |
- if (node == null) return; |
- node.accept(new ResolverVisitor(compiler, enclosingElement)); |
- } |
- |
- // TODO(ahe): This is temporary. |
- ClassElement get currentClass() { |
- return enclosingElement.isMember() |
- ? enclosingElement.enclosingElement : null; |
- } |
-} |
- |
-class ConstructorResolver extends CommonResolverVisitor<Element> { |
- final ResolverVisitor resolver; |
- ConstructorResolver(Compiler compiler, this.resolver) : super(compiler); |
- |
- visitNode(Node node) { |
- throw 'not supported'; |
- } |
- |
- visitNewExpression(NewExpression node) { |
- Node selector = node.send.selector; |
- Element e = visit(selector); |
- if (e !== null && e.kind === ElementKind.CLASS) { |
- ClassElement cls = e; |
- cls.ensureResolved(compiler); |
- compiler.resolver.toResolve.add(cls); |
- if (cls.isInterface() && (cls.defaultClass === null)) { |
- error(selector, MessageKind.CANNOT_INSTANTIATE_INTERFACE, [cls.name]); |
- } |
- e = cls.lookupConstructor(cls.name); |
- } |
- return e; |
- } |
- |
- visitTypeAnnotation(TypeAnnotation node) { |
- // TODO(ahe): Do not ignore type arguments. |
- return visit(node.typeName); |
- } |
- |
- visitSend(Send node) { |
- Element e = visit(node.receiver); |
- if (e === null) return null; // TODO(ahe): Return erroneous element. |
- |
- Identifier name = node.selector.asIdentifier(); |
- if (name === null) internalError(node.selector, 'unexpected node'); |
- |
- if (e.kind === ElementKind.CLASS) { |
- ClassElement cls = e; |
- cls.ensureResolved(compiler); |
- compiler.resolver.toResolve.add(cls); |
- if (cls.isInterface() && (cls.defaultClass === null)) { |
- error(node.receiver, MessageKind.CANNOT_INSTANTIATE_INTERFACE, |
- [cls.name]); |
- } |
- SourceString constructorName = |
- Elements.constructConstructorName(cls.name, name.source); |
- FunctionElement constructor = cls.lookupConstructor(constructorName); |
- if (constructor === null) { |
- error(name, MessageKind.CANNOT_FIND_CONSTRUCTOR, [name]); |
- } |
- e = constructor; |
- } else if (e.kind === ElementKind.PREFIX) { |
- PrefixElement prefix = e; |
- e = prefix.lookupLocalMember(name.source); |
- if (e === null) { |
- error(name, MessageKind.CANNOT_RESOLVE, [name]); |
- // TODO(ahe): Return erroneous element. |
- } else if (e.kind !== ElementKind.CLASS) { |
- error(node, MessageKind.NOT_A_TYPE, [name]); |
- } |
- } else { |
- internalError(node.receiver, 'unexpected element $e'); |
- } |
- return e; |
- } |
- |
- Element visitIdentifier(Identifier node) { |
- SourceString name = node.source; |
- Element e = resolver.lookup(node, name); |
- if (e === null) { |
- error(node, MessageKind.CANNOT_RESOLVE, [name]); |
- // TODO(ahe): Return erroneous element. |
- } else if (e.kind === ElementKind.TYPEDEF) { |
- error(node, MessageKind.CANNOT_INSTANTIATE_TYPEDEF, [name]); |
- } else if (e.kind !== ElementKind.CLASS && e.kind !== ElementKind.PREFIX) { |
- error(node, MessageKind.NOT_A_TYPE, [name]); |
- } |
- return e; |
- } |
-} |
- |
-class Scope { |
- final Element element; |
- final Scope parent; |
- |
- Scope(this.parent, this.element); |
- abstract Element add(Element element); |
- abstract Element lookup(SourceString name); |
-} |
- |
-class TypeVariablesScope extends Scope { |
- TypeVariablesScope(parent, ClassElement element) : super(parent, element); |
- Element add(Element element) { |
- throw "Cannot add element to TypeVariableScope"; |
- } |
- Element lookup(SourceString name) { |
- ClassElement cls = element; |
- Element result = cls.lookupTypeParameter(name); |
- if (result !== null) return result; |
- if (parent !== null) return parent.lookup(name); |
- } |
-} |
- |
-class MethodScope extends Scope { |
- final Map<SourceString, Element> elements; |
- |
- MethodScope(Scope parent, Element element) |
- : super(parent, element), this.elements = new Map<SourceString, Element>(); |
- |
- Element lookup(SourceString name) { |
- Element element = elements[name]; |
- if (element !== null) return element; |
- return parent.lookup(name); |
- } |
- |
- Element add(Element element) { |
- if (elements.containsKey(element.name)) return elements[element.name]; |
- elements[element.name] = element; |
- return element; |
- } |
-} |
- |
-class BlockScope extends MethodScope { |
- BlockScope(Scope parent) : super(parent, parent.element); |
-} |
- |
-class ClassScope extends Scope { |
- ClassScope(ClassElement element, LibraryElement library) |
- : super(new TopScope(library), element); |
- |
- Element lookup(SourceString name) { |
- ClassElement cls = element; |
- Element result = cls.lookupLocalMember(name); |
- if (result !== null) return result; |
- result = cls.lookupTypeParameter(name); |
- if (result !== null) return result; |
- result = parent.lookup(name); |
- if (result != null) return result; |
- return cls.lookupSuperMember(name); |
- } |
- |
- Element add(Element element) { |
- throw "Cannot add an element in a class scope"; |
- } |
-} |
- |
-class TopScope extends Scope { |
- LibraryElement get library() => element; |
- |
- TopScope(LibraryElement library) : super(null, library); |
- Element lookup(SourceString name) { |
- return library.find(name); |
- } |
- |
- Element add(Element element) { |
- throw "Cannot add an element in the top scope"; |
- } |
-} |