| Index: pkg/compiler/lib/src/resolution/members.dart
|
| diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart
|
| deleted file mode 100644
|
| index 937d308a4afbc195fa6be76d78d86e5b2586eeb7..0000000000000000000000000000000000000000
|
| --- a/pkg/compiler/lib/src/resolution/members.dart
|
| +++ /dev/null
|
| @@ -1,5054 +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.
|
| -
|
| -part of resolution;
|
| -
|
| -abstract class TreeElements {
|
| - AnalyzableElement get analyzedElement;
|
| - Iterable<Node> get superUses;
|
| -
|
| - /// Iterables of the dependencies that this [TreeElement] records of
|
| - /// [analyzedElement].
|
| - Iterable<Element> get allElements;
|
| - void forEachConstantNode(f(Node n, ConstantExpression c));
|
| -
|
| - /// A set of additional dependencies. See [registerDependency] below.
|
| - Iterable<Element> get otherDependencies;
|
| -
|
| - Element operator[](Node node);
|
| -
|
| - // TODO(johnniwinther): Investigate whether [Node] could be a [Send].
|
| - Selector getSelector(Node node);
|
| - Selector getGetterSelectorInComplexSendSet(SendSet node);
|
| - Selector getOperatorSelectorInComplexSendSet(SendSet node);
|
| - DartType getType(Node node);
|
| - void setSelector(Node node, Selector selector);
|
| - void setGetterSelectorInComplexSendSet(SendSet node, Selector selector);
|
| - void setOperatorSelectorInComplexSendSet(SendSet node, Selector selector);
|
| -
|
| - /// Returns the for-in loop variable for [node].
|
| - Element getForInVariable(ForIn node);
|
| - Selector getIteratorSelector(ForIn node);
|
| - Selector getMoveNextSelector(ForIn node);
|
| - Selector getCurrentSelector(ForIn node);
|
| - void setIteratorSelector(ForIn node, Selector selector);
|
| - void setMoveNextSelector(ForIn node, Selector selector);
|
| - void setCurrentSelector(ForIn node, Selector selector);
|
| - void setConstant(Node node, ConstantExpression constant);
|
| - ConstantExpression getConstant(Node node);
|
| - bool isAssert(Send send);
|
| -
|
| - /// Returns the [FunctionElement] defined by [node].
|
| - FunctionElement getFunctionDefinition(FunctionExpression node);
|
| -
|
| - /// Returns target constructor for the redirecting factory body [node].
|
| - ConstructorElement getRedirectingTargetConstructor(
|
| - RedirectingFactoryBody node);
|
| -
|
| - /**
|
| - * Returns [:true:] if [node] is a type literal.
|
| - *
|
| - * Resolution marks this by setting the type on the node to be the
|
| - * type that the literal refers to.
|
| - */
|
| - bool isTypeLiteral(Send node);
|
| -
|
| - /// Returns the type that the type literal [node] refers to.
|
| - DartType getTypeLiteralType(Send node);
|
| -
|
| - /// Register additional dependencies required by [analyzedElement].
|
| - /// For example, elements that are used by a backend.
|
| - void registerDependency(Element element);
|
| -
|
| - /// Returns a list of nodes that potentially mutate [element] anywhere in its
|
| - /// scope.
|
| - List<Node> getPotentialMutations(VariableElement element);
|
| -
|
| - /// Returns a list of nodes that potentially mutate [element] in [node].
|
| - List<Node> getPotentialMutationsIn(Node node, VariableElement element);
|
| -
|
| - /// Returns a list of nodes that potentially mutate [element] in a closure.
|
| - List<Node> getPotentialMutationsInClosure(VariableElement element);
|
| -
|
| - /// Returns a list of nodes that access [element] within a closure in [node].
|
| - List<Node> getAccessesByClosureIn(Node node, VariableElement element);
|
| -
|
| - /// Returns the jump target defined by [node].
|
| - JumpTarget getTargetDefinition(Node node);
|
| -
|
| - /// Returns the jump target of the [node].
|
| - JumpTarget getTargetOf(GotoStatement node);
|
| -
|
| - /// Returns the label defined by [node].
|
| - LabelDefinition getLabelDefinition(Label node);
|
| -
|
| - /// Returns the label that [node] targets.
|
| - LabelDefinition getTargetLabel(GotoStatement node);
|
| -}
|
| -
|
| -class TreeElementMapping implements TreeElements {
|
| - final AnalyzableElement analyzedElement;
|
| - Map<Spannable, Selector> _selectors;
|
| - Map<Node, DartType> _types;
|
| - Setlet<Node> _superUses;
|
| - Setlet<Element> _otherDependencies;
|
| - Map<Node, ConstantExpression> _constants;
|
| - Map<VariableElement, List<Node>> _potentiallyMutated;
|
| - Map<Node, Map<VariableElement, List<Node>>> _potentiallyMutatedIn;
|
| - Map<VariableElement, List<Node>> _potentiallyMutatedInClosure;
|
| - Map<Node, Map<VariableElement, List<Node>>> _accessedByClosureIn;
|
| - Setlet<Element> _elements;
|
| - Setlet<Send> _asserts;
|
| -
|
| - /// Map from nodes to the targets they define.
|
| - Map<Node, JumpTarget> _definedTargets;
|
| -
|
| - /// Map from goto statements to their targets.
|
| - Map<GotoStatement, JumpTarget> _usedTargets;
|
| -
|
| - /// Map from labels to their label definition.
|
| - Map<Label, LabelDefinition> _definedLabels;
|
| -
|
| - /// Map from labeled goto statements to the labels they target.
|
| - Map<GotoStatement, LabelDefinition> _targetLabels;
|
| -
|
| - final int hashCode = ++_hashCodeCounter;
|
| - static int _hashCodeCounter = 0;
|
| -
|
| - TreeElementMapping(this.analyzedElement);
|
| -
|
| - operator []=(Node node, Element element) {
|
| - assert(invariant(node, () {
|
| - FunctionExpression functionExpression = node.asFunctionExpression();
|
| - if (functionExpression != null) {
|
| - return !functionExpression.modifiers.isExternal;
|
| - }
|
| - return true;
|
| - }));
|
| - // TODO(johnniwinther): Simplify this invariant to use only declarations in
|
| - // [TreeElements].
|
| - assert(invariant(node, () {
|
| - if (!element.isErroneous && analyzedElement != null && element.isPatch) {
|
| - return analyzedElement.implementationLibrary.isPatch;
|
| - }
|
| - return true;
|
| - }));
|
| - // TODO(ahe): Investigate why the invariant below doesn't hold.
|
| - // assert(invariant(node,
|
| - // getTreeElement(node) == element ||
|
| - // getTreeElement(node) == null,
|
| - // message: '${getTreeElement(node)}; $element'));
|
| -
|
| - if (_elements == null) {
|
| - _elements = new Setlet<Element>();
|
| - }
|
| - _elements.add(element);
|
| - setTreeElement(node, element);
|
| - }
|
| -
|
| - operator [](Node node) => getTreeElement(node);
|
| -
|
| - void setType(Node node, DartType type) {
|
| - if (_types == null) {
|
| - _types = new Maplet<Node, DartType>();
|
| - }
|
| - _types[node] = type;
|
| - }
|
| -
|
| - DartType getType(Node node) => _types != null ? _types[node] : null;
|
| -
|
| - Iterable<Node> get superUses {
|
| - return _superUses != null ? _superUses : const <Node>[];
|
| - }
|
| -
|
| - void addSuperUse(Node node) {
|
| - if (_superUses == null) {
|
| - _superUses = new Setlet<Node>();
|
| - }
|
| - _superUses.add(node);
|
| - }
|
| -
|
| - Selector _getSelector(Spannable node) {
|
| - return _selectors != null ? _selectors[node] : null;
|
| - }
|
| -
|
| - void _setSelector(Spannable node, Selector selector) {
|
| - if (_selectors == null) {
|
| - _selectors = new Maplet<Spannable, Selector>();
|
| - }
|
| - _selectors[node] = selector;
|
| - }
|
| -
|
| - void setSelector(Node node, Selector selector) {
|
| - _setSelector(node, selector);
|
| - }
|
| -
|
| - Selector getSelector(Node node) => _getSelector(node);
|
| -
|
| - int getSelectorCount() => _selectors == null ? 0 : _selectors.length;
|
| -
|
| - void setGetterSelectorInComplexSendSet(SendSet node, Selector selector) {
|
| - _setSelector(node.selector, selector);
|
| - }
|
| -
|
| - Selector getGetterSelectorInComplexSendSet(SendSet node) {
|
| - return _getSelector(node.selector);
|
| - }
|
| -
|
| - void setOperatorSelectorInComplexSendSet(SendSet node, Selector selector) {
|
| - _setSelector(node.assignmentOperator, selector);
|
| - }
|
| -
|
| - Selector getOperatorSelectorInComplexSendSet(SendSet node) {
|
| - return _getSelector(node.assignmentOperator);
|
| - }
|
| -
|
| - // The following methods set selectors on the "for in" node. Since
|
| - // we're using three selectors, we need to use children of the node,
|
| - // and we arbitrarily choose which ones.
|
| -
|
| - void setIteratorSelector(ForIn node, Selector selector) {
|
| - _setSelector(node, selector);
|
| - }
|
| -
|
| - Selector getIteratorSelector(ForIn node) {
|
| - return _getSelector(node);
|
| - }
|
| -
|
| - void setMoveNextSelector(ForIn node, Selector selector) {
|
| - _setSelector(node.forToken, selector);
|
| - }
|
| -
|
| - Selector getMoveNextSelector(ForIn node) {
|
| - return _getSelector(node.forToken);
|
| - }
|
| -
|
| - void setCurrentSelector(ForIn node, Selector selector) {
|
| - _setSelector(node.inToken, selector);
|
| - }
|
| -
|
| - Selector getCurrentSelector(ForIn node) {
|
| - return _getSelector(node.inToken);
|
| - }
|
| -
|
| - Element getForInVariable(ForIn node) {
|
| - return this[node];
|
| - }
|
| -
|
| - void setConstant(Node node, ConstantExpression constant) {
|
| - if (_constants == null) {
|
| - _constants = new Maplet<Node, ConstantExpression>();
|
| - }
|
| - _constants[node] = constant;
|
| - }
|
| -
|
| - ConstantExpression getConstant(Node node) {
|
| - return _constants != null ? _constants[node] : null;
|
| - }
|
| -
|
| - bool isTypeLiteral(Send node) {
|
| - return getType(node) != null;
|
| - }
|
| -
|
| - DartType getTypeLiteralType(Send node) {
|
| - return getType(node);
|
| - }
|
| -
|
| - void registerDependency(Element element) {
|
| - if (element == null) return;
|
| - if (_otherDependencies == null) {
|
| - _otherDependencies = new Setlet<Element>();
|
| - }
|
| - _otherDependencies.add(element.implementation);
|
| - }
|
| -
|
| - Iterable<Element> get otherDependencies {
|
| - return _otherDependencies != null ? _otherDependencies : const <Element>[];
|
| - }
|
| -
|
| - List<Node> getPotentialMutations(VariableElement element) {
|
| - if (_potentiallyMutated == null) return const <Node>[];
|
| - List<Node> mutations = _potentiallyMutated[element];
|
| - if (mutations == null) return const <Node>[];
|
| - return mutations;
|
| - }
|
| -
|
| - void registerPotentialMutation(VariableElement element, Node mutationNode) {
|
| - if (_potentiallyMutated == null) {
|
| - _potentiallyMutated = new Maplet<VariableElement, List<Node>>();
|
| - }
|
| - _potentiallyMutated.putIfAbsent(element, () => <Node>[]).add(mutationNode);
|
| - }
|
| -
|
| - List<Node> getPotentialMutationsIn(Node node, VariableElement element) {
|
| - if (_potentiallyMutatedIn == null) return const <Node>[];
|
| - Map<VariableElement, List<Node>> mutationsIn = _potentiallyMutatedIn[node];
|
| - if (mutationsIn == null) return const <Node>[];
|
| - List<Node> mutations = mutationsIn[element];
|
| - if (mutations == null) return const <Node>[];
|
| - return mutations;
|
| - }
|
| -
|
| - void registerPotentialMutationIn(Node contextNode, VariableElement element,
|
| - Node mutationNode) {
|
| - if (_potentiallyMutatedIn == null) {
|
| - _potentiallyMutatedIn =
|
| - new Maplet<Node, Map<VariableElement, List<Node>>>();
|
| - }
|
| - Map<VariableElement, List<Node>> mutationMap =
|
| - _potentiallyMutatedIn.putIfAbsent(contextNode,
|
| - () => new Maplet<VariableElement, List<Node>>());
|
| - mutationMap.putIfAbsent(element, () => <Node>[]).add(mutationNode);
|
| - }
|
| -
|
| - List<Node> getPotentialMutationsInClosure(VariableElement element) {
|
| - if (_potentiallyMutatedInClosure == null) return const <Node>[];
|
| - List<Node> mutations = _potentiallyMutatedInClosure[element];
|
| - if (mutations == null) return const <Node>[];
|
| - return mutations;
|
| - }
|
| -
|
| - void registerPotentialMutationInClosure(VariableElement element,
|
| - Node mutationNode) {
|
| - if (_potentiallyMutatedInClosure == null) {
|
| - _potentiallyMutatedInClosure = new Maplet<VariableElement, List<Node>>();
|
| - }
|
| - _potentiallyMutatedInClosure.putIfAbsent(
|
| - element, () => <Node>[]).add(mutationNode);
|
| - }
|
| -
|
| - List<Node> getAccessesByClosureIn(Node node, VariableElement element) {
|
| - if (_accessedByClosureIn == null) return const <Node>[];
|
| - Map<VariableElement, List<Node>> accessesIn = _accessedByClosureIn[node];
|
| - if (accessesIn == null) return const <Node>[];
|
| - List<Node> accesses = accessesIn[element];
|
| - if (accesses == null) return const <Node>[];
|
| - return accesses;
|
| - }
|
| -
|
| - void setAccessedByClosureIn(Node contextNode, VariableElement element,
|
| - Node accessNode) {
|
| - if (_accessedByClosureIn == null) {
|
| - _accessedByClosureIn = new Map<Node, Map<VariableElement, List<Node>>>();
|
| - }
|
| - Map<VariableElement, List<Node>> accessMap =
|
| - _accessedByClosureIn.putIfAbsent(contextNode,
|
| - () => new Maplet<VariableElement, List<Node>>());
|
| - accessMap.putIfAbsent(element, () => <Node>[]).add(accessNode);
|
| - }
|
| -
|
| - String toString() => 'TreeElementMapping($analyzedElement)';
|
| -
|
| - Iterable<Element> get allElements {
|
| - return _elements != null ? _elements : const <Element>[];
|
| - }
|
| -
|
| - void forEachConstantNode(f(Node n, ConstantExpression c)) {
|
| - if (_constants != null) {
|
| - _constants.forEach(f);
|
| - }
|
| - }
|
| -
|
| - void setAssert(Send node) {
|
| - if (_asserts == null) {
|
| - _asserts = new Setlet<Send>();
|
| - }
|
| - _asserts.add(node);
|
| - }
|
| -
|
| - bool isAssert(Send node) {
|
| - return _asserts != null && _asserts.contains(node);
|
| - }
|
| -
|
| - FunctionElement getFunctionDefinition(FunctionExpression node) {
|
| - return this[node];
|
| - }
|
| -
|
| - ConstructorElement getRedirectingTargetConstructor(
|
| - RedirectingFactoryBody node) {
|
| - return this[node];
|
| - }
|
| -
|
| - void defineTarget(Node node, JumpTarget target) {
|
| - if (_definedTargets == null) {
|
| - _definedTargets = new Maplet<Node, JumpTarget>();
|
| - }
|
| - _definedTargets[node] = target;
|
| - }
|
| -
|
| - void undefineTarget(Node node) {
|
| - if (_definedTargets != null) {
|
| - _definedTargets.remove(node);
|
| - if (_definedTargets.isEmpty) {
|
| - _definedTargets = null;
|
| - }
|
| - }
|
| - }
|
| -
|
| - JumpTarget getTargetDefinition(Node node) {
|
| - return _definedTargets != null ? _definedTargets[node] : null;
|
| - }
|
| -
|
| - void registerTargetOf(GotoStatement node, JumpTarget target) {
|
| - if (_usedTargets == null) {
|
| - _usedTargets = new Maplet<GotoStatement, JumpTarget>();
|
| - }
|
| - _usedTargets[node] = target;
|
| - }
|
| -
|
| - JumpTarget getTargetOf(GotoStatement node) {
|
| - return _usedTargets != null ? _usedTargets[node] : null;
|
| - }
|
| -
|
| - void defineLabel(Label label, LabelDefinition target) {
|
| - if (_definedLabels == null) {
|
| - _definedLabels = new Maplet<Label, LabelDefinition>();
|
| - }
|
| - _definedLabels[label] = target;
|
| - }
|
| -
|
| - void undefineLabel(Label label) {
|
| - if (_definedLabels != null) {
|
| - _definedLabels.remove(label);
|
| - if (_definedLabels.isEmpty) {
|
| - _definedLabels = null;
|
| - }
|
| - }
|
| - }
|
| -
|
| - LabelDefinition getLabelDefinition(Label label) {
|
| - return _definedLabels != null ? _definedLabels[label] : null;
|
| - }
|
| -
|
| - void registerTargetLabel(GotoStatement node, LabelDefinition label) {
|
| - assert(node.target != null);
|
| - if (_targetLabels == null) {
|
| - _targetLabels = new Maplet<GotoStatement, LabelDefinition>();
|
| - }
|
| - _targetLabels[node] = label;
|
| - }
|
| -
|
| - LabelDefinition getTargetLabel(GotoStatement node) {
|
| - assert(node.target != null);
|
| - return _targetLabels != null ? _targetLabels[node] : null;
|
| - }
|
| -}
|
| -
|
| -class ResolverTask extends CompilerTask {
|
| - final ConstantCompiler constantCompiler;
|
| -
|
| - ResolverTask(Compiler compiler, this.constantCompiler) : super(compiler);
|
| -
|
| - String get name => 'Resolver';
|
| -
|
| - TreeElements resolve(Element element) {
|
| - return measure(() {
|
| - if (Elements.isErroneousElement(element)) return null;
|
| -
|
| - processMetadata([result]) {
|
| - for (MetadataAnnotation metadata in element.metadata) {
|
| - metadata.ensureResolved(compiler);
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - ElementKind kind = element.kind;
|
| - if (identical(kind, ElementKind.GENERATIVE_CONSTRUCTOR) ||
|
| - identical(kind, ElementKind.FUNCTION) ||
|
| - identical(kind, ElementKind.GETTER) ||
|
| - identical(kind, ElementKind.SETTER)) {
|
| - return processMetadata(resolveMethodElement(element));
|
| - }
|
| -
|
| - if (identical(kind, ElementKind.FIELD)) {
|
| - return processMetadata(resolveField(element));
|
| - }
|
| - if (element.isClass) {
|
| - ClassElement cls = element;
|
| - cls.ensureResolved(compiler);
|
| - return processMetadata();
|
| - } else if (element.isTypedef) {
|
| - TypedefElement typdef = element;
|
| - return processMetadata(resolveTypedef(typdef));
|
| - }
|
| -
|
| - compiler.unimplemented(element, "resolve($element)");
|
| - });
|
| - }
|
| -
|
| - void resolveRedirectingConstructor(InitializerResolver resolver,
|
| - Node node,
|
| - FunctionElement constructor,
|
| - FunctionElement redirection) {
|
| - assert(invariant(node, constructor.isImplementation,
|
| - message: 'Redirecting constructors must be resolved on implementation '
|
| - 'elements.'));
|
| - Setlet<FunctionElement> seen = new Setlet<FunctionElement>();
|
| - seen.add(constructor);
|
| - while (redirection != null) {
|
| - // Ensure that we follow redirections through implementation elements.
|
| - redirection = redirection.implementation;
|
| - if (seen.contains(redirection)) {
|
| - resolver.visitor.error(node, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE);
|
| - return;
|
| - }
|
| - seen.add(redirection);
|
| - redirection = resolver.visitor.resolveConstructorRedirection(redirection);
|
| - }
|
| - }
|
| -
|
| - void checkMatchingPatchParameters(FunctionElement origin,
|
| - Link<Element> originParameters,
|
| - Link<Element> patchParameters) {
|
| - while (!originParameters.isEmpty) {
|
| - ParameterElementX originParameter = originParameters.head;
|
| - ParameterElementX patchParameter = patchParameters.head;
|
| - // TODO(johnniwinther): Remove the conditional patching when we never
|
| - // resolve the same method twice.
|
| - if (!originParameter.isPatched) {
|
| - originParameter.applyPatch(patchParameter);
|
| - } else {
|
| - assert(invariant(origin, originParameter.patch == patchParameter,
|
| - message: "Inconsistent repatch of $originParameter."));
|
| - }
|
| - DartType originParameterType = originParameter.computeType(compiler);
|
| - DartType patchParameterType = patchParameter.computeType(compiler);
|
| - if (originParameterType != patchParameterType) {
|
| - compiler.reportError(
|
| - originParameter.parseNode(compiler),
|
| - MessageKind.PATCH_PARAMETER_TYPE_MISMATCH,
|
| - {'methodName': origin.name,
|
| - 'parameterName': originParameter.name,
|
| - 'originParameterType': originParameterType,
|
| - 'patchParameterType': patchParameterType});
|
| - compiler.reportInfo(patchParameter,
|
| - MessageKind.PATCH_POINT_TO_PARAMETER,
|
| - {'parameterName': patchParameter.name});
|
| - } else {
|
| - // Hack: Use unparser to test parameter equality. This only works
|
| - // because we are restricting patch uses and the approach cannot be used
|
| - // elsewhere.
|
| -
|
| - // The node contains the type, so there is a potential overlap.
|
| - // Therefore we only check the text if the types are identical.
|
| - String originParameterText =
|
| - originParameter.parseNode(compiler).toString();
|
| - String patchParameterText =
|
| - patchParameter.parseNode(compiler).toString();
|
| - if (originParameterText != patchParameterText
|
| - // We special case the list constructor because of the
|
| - // optional parameter.
|
| - && origin != compiler.unnamedListConstructor) {
|
| - compiler.reportError(
|
| - originParameter.parseNode(compiler),
|
| - MessageKind.PATCH_PARAMETER_MISMATCH,
|
| - {'methodName': origin.name,
|
| - 'originParameter': originParameterText,
|
| - 'patchParameter': patchParameterText});
|
| - compiler.reportInfo(patchParameter,
|
| - MessageKind.PATCH_POINT_TO_PARAMETER,
|
| - {'parameterName': patchParameter.name});
|
| - }
|
| - }
|
| -
|
| - originParameters = originParameters.tail;
|
| - patchParameters = patchParameters.tail;
|
| - }
|
| - }
|
| -
|
| - void checkMatchingPatchSignatures(FunctionElement origin,
|
| - FunctionElement patch) {
|
| - // TODO(johnniwinther): Show both origin and patch locations on errors.
|
| - FunctionExpression originTree = origin.node;
|
| - FunctionSignature originSignature = origin.functionSignature;
|
| - FunctionExpression patchTree = patch.node;
|
| - FunctionSignature patchSignature = patch.functionSignature;
|
| -
|
| - if (originSignature.type.returnType != patchSignature.type.returnType) {
|
| - compiler.withCurrentElement(patch, () {
|
| - Node errorNode =
|
| - patchTree.returnType != null ? patchTree.returnType : patchTree;
|
| - error(errorNode, MessageKind.PATCH_RETURN_TYPE_MISMATCH,
|
| - {'methodName': origin.name,
|
| - 'originReturnType': originSignature.type.returnType,
|
| - 'patchReturnType': patchSignature.type.returnType});
|
| - });
|
| - }
|
| - if (originSignature.requiredParameterCount !=
|
| - patchSignature.requiredParameterCount) {
|
| - compiler.withCurrentElement(patch, () {
|
| - error(patchTree,
|
| - MessageKind.PATCH_REQUIRED_PARAMETER_COUNT_MISMATCH,
|
| - {'methodName': origin.name,
|
| - 'originParameterCount': originSignature.requiredParameterCount,
|
| - 'patchParameterCount': patchSignature.requiredParameterCount});
|
| - });
|
| - } else {
|
| - checkMatchingPatchParameters(origin,
|
| - originSignature.requiredParameters,
|
| - patchSignature.requiredParameters);
|
| - }
|
| - if (originSignature.optionalParameterCount != 0 &&
|
| - patchSignature.optionalParameterCount != 0) {
|
| - if (originSignature.optionalParametersAreNamed !=
|
| - patchSignature.optionalParametersAreNamed) {
|
| - compiler.withCurrentElement(patch, () {
|
| - error(patchTree,
|
| - MessageKind.PATCH_OPTIONAL_PARAMETER_NAMED_MISMATCH,
|
| - {'methodName': origin.name});
|
| - });
|
| - }
|
| - }
|
| - if (originSignature.optionalParameterCount !=
|
| - patchSignature.optionalParameterCount) {
|
| - compiler.withCurrentElement(patch, () {
|
| - error(patchTree,
|
| - MessageKind.PATCH_OPTIONAL_PARAMETER_COUNT_MISMATCH,
|
| - {'methodName': origin.name,
|
| - 'originParameterCount': originSignature.optionalParameterCount,
|
| - 'patchParameterCount': patchSignature.optionalParameterCount});
|
| - });
|
| - } else {
|
| - checkMatchingPatchParameters(origin,
|
| - originSignature.optionalParameters,
|
| - patchSignature.optionalParameters);
|
| - }
|
| - }
|
| -
|
| - static void processAsyncMarker(Compiler compiler,
|
| - BaseFunctionElementX element) {
|
| - FunctionExpression functionExpression = element.node;
|
| - AsyncModifier asyncModifier = functionExpression.asyncModifier;
|
| - if (asyncModifier != null) {
|
| - if (!compiler.enableAsyncAwait) {
|
| - compiler.reportError(asyncModifier,
|
| - MessageKind.EXPERIMENTAL_ASYNC_AWAIT,
|
| - {'modifier': element.asyncMarker});
|
| - } else if (!compiler.analyzeOnly) {
|
| - compiler.reportError(asyncModifier,
|
| - MessageKind.EXPERIMENTAL_ASYNC_AWAIT,
|
| - {'modifier': element.asyncMarker});
|
| - }
|
| -
|
| - if (asyncModifier.isAsynchronous) {
|
| - element.asyncMarker = asyncModifier.isYielding
|
| - ? AsyncMarker.ASYNC_STAR : AsyncMarker.ASYNC;
|
| - } else {
|
| - element.asyncMarker = AsyncMarker.SYNC_STAR;
|
| - }
|
| - if (element.isAbstract) {
|
| - compiler.reportError(asyncModifier,
|
| - MessageKind.ASYNC_MODIFIER_ON_ABSTRACT_METHOD,
|
| - {'modifier': element.asyncMarker});
|
| - } else if (element.isConstructor) {
|
| - compiler.reportError(asyncModifier,
|
| - MessageKind.ASYNC_MODIFIER_ON_CONSTRUCTOR,
|
| - {'modifier': element.asyncMarker});
|
| - } else if (functionExpression.body.asReturn() != null &&
|
| - element.asyncMarker.isYielding) {
|
| - compiler.reportError(asyncModifier,
|
| - MessageKind.YIELDING_MODIFIER_ON_ARROW_BODY,
|
| - {'modifier': element.asyncMarker});
|
| - }
|
| - }
|
| - }
|
| -
|
| - TreeElements resolveMethodElement(FunctionElementX element) {
|
| - assert(invariant(element, element.isDeclaration));
|
| - return compiler.withCurrentElement(element, () {
|
| - bool isConstructor =
|
| - identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR);
|
| - if (compiler.enqueuer.resolution.hasBeenResolved(element)) {
|
| - // TODO(karlklose): Remove the check for [isConstructor]. [elememts]
|
| - // should never be non-null, not even for constructors.
|
| - assert(invariant(element, element.isConstructor,
|
| - message: 'Non-constructor element $element '
|
| - 'has already been analyzed.'));
|
| - return element.resolvedAst.elements;
|
| - }
|
| - if (element.isSynthesized) {
|
| - if (isConstructor) {
|
| - ResolutionRegistry registry =
|
| - new ResolutionRegistry(compiler, element);
|
| - ConstructorElement constructor = element.asFunctionElement();
|
| - ConstructorElement target = constructor.definingConstructor;
|
| - // Ensure the signature of the synthesized element is
|
| - // resolved. This is the only place where the resolver is
|
| - // seeing this element.
|
| - element.computeSignature(compiler);
|
| - if (!target.isErroneous) {
|
| - registry.registerStaticUse(target);
|
| - registry.registerImplicitSuperCall(target);
|
| - }
|
| - return registry.mapping;
|
| - } else {
|
| - assert(element.isDeferredLoaderGetter);
|
| - return _ensureTreeElements(element);
|
| - }
|
| - }
|
| - element.parseNode(compiler);
|
| - element.computeType(compiler);
|
| - processAsyncMarker(compiler, element);
|
| - if (element.isPatched) {
|
| - FunctionElementX patch = element.patch;
|
| - compiler.withCurrentElement(patch, () {
|
| - patch.parseNode(compiler);
|
| - patch.computeType(compiler);
|
| - });
|
| - checkMatchingPatchSignatures(element, patch);
|
| - element = patch;
|
| - processAsyncMarker(compiler, element);
|
| - }
|
| - return compiler.withCurrentElement(element, () {
|
| - FunctionExpression tree = element.node;
|
| - if (tree.modifiers.isExternal) {
|
| - error(tree, MessageKind.PATCH_EXTERNAL_WITHOUT_IMPLEMENTATION);
|
| - return null;
|
| - }
|
| - if (isConstructor || element.isFactoryConstructor) {
|
| - if (tree.returnType != null) {
|
| - error(tree, MessageKind.CONSTRUCTOR_WITH_RETURN_TYPE);
|
| - }
|
| - if (element.modifiers.isConst &&
|
| - tree.hasBody() &&
|
| - !tree.isRedirectingFactory) {
|
| - compiler.reportError(tree, MessageKind.CONST_CONSTRUCTOR_HAS_BODY);
|
| - }
|
| - }
|
| -
|
| - ResolverVisitor visitor = visitorFor(element);
|
| - ResolutionRegistry registry = visitor.registry;
|
| - registry.defineFunction(tree, element);
|
| - visitor.setupFunction(tree, element);
|
| -
|
| - if (isConstructor && !element.isForwardingConstructor) {
|
| - // Even if there is no initializer list we still have to do the
|
| - // resolution in case there is an implicit super constructor call.
|
| - InitializerResolver resolver = new InitializerResolver(visitor);
|
| - FunctionElement redirection =
|
| - resolver.resolveInitializers(element, tree);
|
| - if (redirection != null) {
|
| - resolveRedirectingConstructor(resolver, tree, element, redirection);
|
| - }
|
| - } else if (element.isForwardingConstructor) {
|
| - // Initializers will be checked on the original constructor.
|
| - } else if (tree.initializers != null) {
|
| - error(tree, MessageKind.FUNCTION_WITH_INITIALIZER);
|
| - }
|
| -
|
| - if (!compiler.analyzeSignaturesOnly || tree.isRedirectingFactory) {
|
| - // We need to analyze the redirecting factory bodies to ensure that
|
| - // we can analyze compile-time constants.
|
| - visitor.visit(tree.body);
|
| - }
|
| -
|
| - // Get the resolution tree and check that the resolved
|
| - // function doesn't use 'super' if it is mixed into another
|
| - // class. This is the part of the 'super' mixin check that
|
| - // happens when a function is resolved after the mixin
|
| - // application has been performed.
|
| - TreeElements resolutionTree = registry.mapping;
|
| - ClassElement enclosingClass = element.enclosingClass;
|
| - if (enclosingClass != null) {
|
| - // TODO(johnniwinther): Find another way to obtain mixin uses.
|
| - Iterable<MixinApplicationElement> mixinUses =
|
| - compiler.world.allMixinUsesOf(enclosingClass);
|
| - ClassElement mixin = enclosingClass;
|
| - for (MixinApplicationElement mixinApplication in mixinUses) {
|
| - checkMixinSuperUses(resolutionTree, mixinApplication, mixin);
|
| - }
|
| - }
|
| - return resolutionTree;
|
| - });
|
| - });
|
| - }
|
| -
|
| - /// Creates a [ResolverVisitor] for resolving an AST in context of [element].
|
| - /// If [useEnclosingScope] is `true` then the initial scope of the visitor
|
| - /// does not include inner scope of [element].
|
| - ///
|
| - /// This method should only be used by this library (or tests of
|
| - /// this library).
|
| - ResolverVisitor visitorFor(Element element, {bool useEnclosingScope: false}) {
|
| - return new ResolverVisitor(compiler, element,
|
| - new ResolutionRegistry(compiler, element),
|
| - useEnclosingScope: useEnclosingScope);
|
| - }
|
| -
|
| - TreeElements resolveField(FieldElementX element) {
|
| - VariableDefinitions tree = element.parseNode(compiler);
|
| - if(element.modifiers.isStatic && element.isTopLevel) {
|
| - error(element.modifiers.getStatic(),
|
| - MessageKind.TOP_LEVEL_VARIABLE_DECLARED_STATIC);
|
| - }
|
| - ResolverVisitor visitor = visitorFor(element);
|
| - ResolutionRegistry registry = visitor.registry;
|
| - // TODO(johnniwinther): Maybe remove this when placeholderCollector migrates
|
| - // to the backend ast.
|
| - registry.defineElement(tree.definitions.nodes.head, element);
|
| - // TODO(johnniwinther): Share the resolved type between all variables
|
| - // declared in the same declaration.
|
| - if (tree.type != null) {
|
| - element.variables.type = visitor.resolveTypeAnnotation(tree.type);
|
| - } else {
|
| - element.variables.type = const DynamicType();
|
| - }
|
| -
|
| - Expression initializer = element.initializer;
|
| - Modifiers modifiers = element.modifiers;
|
| - if (initializer != null) {
|
| - // TODO(johnniwinther): Avoid analyzing initializers if
|
| - // [Compiler.analyzeSignaturesOnly] is set.
|
| - visitor.visit(initializer);
|
| - } else if (modifiers.isConst) {
|
| - compiler.reportError(element, MessageKind.CONST_WITHOUT_INITIALIZER);
|
| - } else if (modifiers.isFinal && !element.isInstanceMember) {
|
| - compiler.reportError(element, MessageKind.FINAL_WITHOUT_INITIALIZER);
|
| - } else {
|
| - registry.registerInstantiatedClass(compiler.nullClass);
|
| - }
|
| -
|
| - if (Elements.isStaticOrTopLevelField(element)) {
|
| - visitor.addDeferredAction(element, () {
|
| - if (element.modifiers.isConst) {
|
| - constantCompiler.compileConstant(element);
|
| - } else {
|
| - constantCompiler.compileVariable(element);
|
| - }
|
| - });
|
| - if (initializer != null) {
|
| - if (!element.modifiers.isConst) {
|
| - // TODO(johnniwinther): Determine the const-ness eagerly to avoid
|
| - // unnecessary registrations.
|
| - registry.registerLazyField();
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Perform various checks as side effect of "computing" the type.
|
| - element.computeType(compiler);
|
| -
|
| - return registry.mapping;
|
| - }
|
| -
|
| - DartType resolveTypeAnnotation(Element element, TypeAnnotation annotation) {
|
| - DartType type = resolveReturnType(element, annotation);
|
| - if (type.isVoid) {
|
| - error(annotation, MessageKind.VOID_NOT_ALLOWED);
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - DartType resolveReturnType(Element element, TypeAnnotation annotation) {
|
| - if (annotation == null) return const DynamicType();
|
| - DartType result = visitorFor(element).resolveTypeAnnotation(annotation);
|
| - if (result == null) {
|
| - // TODO(karklose): warning.
|
| - return const DynamicType();
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - void resolveRedirectionChain(ConstructorElementX constructor,
|
| - Spannable node) {
|
| - ConstructorElementX target = constructor;
|
| - InterfaceType targetType;
|
| - List<Element> seen = new List<Element>();
|
| - // Follow the chain of redirections and check for cycles.
|
| - while (target.isRedirectingFactory) {
|
| - if (target.internalEffectiveTarget != null) {
|
| - // We found a constructor that already has been processed.
|
| - targetType = target.effectiveTargetType;
|
| - assert(invariant(target, targetType != null,
|
| - message: 'Redirection target type has not been computed for '
|
| - '$target'));
|
| - target = target.internalEffectiveTarget;
|
| - break;
|
| - }
|
| -
|
| - Element nextTarget = target.immediateRedirectionTarget;
|
| - if (seen.contains(nextTarget)) {
|
| - error(node, MessageKind.CYCLIC_REDIRECTING_FACTORY);
|
| - break;
|
| - }
|
| - seen.add(target);
|
| - target = nextTarget;
|
| - }
|
| -
|
| - if (targetType == null) {
|
| - assert(!target.isRedirectingFactory);
|
| - targetType = target.enclosingClass.thisType;
|
| - }
|
| -
|
| - // [target] is now the actual target of the redirections. Run through
|
| - // the constructors again and set their [redirectionTarget], so that we
|
| - // do not have to run the loop for these constructors again. Furthermore,
|
| - // compute [redirectionTargetType] for each factory by computing the
|
| - // substitution of the target type with respect to the factory type.
|
| - while (!seen.isEmpty) {
|
| - ConstructorElementX factory = seen.removeLast();
|
| -
|
| - // [factory] must already be analyzed but the [TreeElements] might not
|
| - // have been stored in the enqueuer cache yet.
|
| - // TODO(johnniwinther): Store [TreeElements] in the cache before
|
| - // resolution of the element.
|
| - TreeElements treeElements = factory.treeElements;
|
| - assert(invariant(node, treeElements != null,
|
| - message: 'No TreeElements cached for $factory.'));
|
| - FunctionExpression functionNode = factory.parseNode(compiler);
|
| - RedirectingFactoryBody redirectionNode = functionNode.body;
|
| - InterfaceType factoryType = treeElements.getType(redirectionNode);
|
| -
|
| - targetType = targetType.substByContext(factoryType);
|
| - factory.effectiveTarget = target;
|
| - factory.effectiveTargetType = targetType;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Load and resolve the supertypes of [cls].
|
| - *
|
| - * Warning: do not call this method directly. It should only be
|
| - * called by [resolveClass] and [ClassSupertypeResolver].
|
| - */
|
| - void loadSupertypes(BaseClassElementX cls, Spannable from) {
|
| - compiler.withCurrentElement(cls, () => measure(() {
|
| - if (cls.supertypeLoadState == STATE_DONE) return;
|
| - if (cls.supertypeLoadState == STATE_STARTED) {
|
| - compiler.reportError(from, MessageKind.CYCLIC_CLASS_HIERARCHY,
|
| - {'className': cls.name});
|
| - cls.supertypeLoadState = STATE_DONE;
|
| - cls.hasIncompleteHierarchy = true;
|
| - cls.allSupertypesAndSelf =
|
| - compiler.objectClass.allSupertypesAndSelf.extendClass(
|
| - cls.computeType(compiler));
|
| - cls.supertype = cls.allSupertypes.head;
|
| - assert(invariant(from, cls.supertype != null,
|
| - message: 'Missing supertype on cyclic class $cls.'));
|
| - cls.interfaces = const Link<DartType>();
|
| - return;
|
| - }
|
| - cls.supertypeLoadState = STATE_STARTED;
|
| - compiler.withCurrentElement(cls, () {
|
| - // TODO(ahe): Cache the node in cls.
|
| - cls.parseNode(compiler).accept(
|
| - new ClassSupertypeResolver(compiler, cls));
|
| - if (cls.supertypeLoadState != STATE_DONE) {
|
| - cls.supertypeLoadState = STATE_DONE;
|
| - }
|
| - });
|
| - }));
|
| - }
|
| -
|
| - // TODO(johnniwinther): Remove this queue when resolution has been split into
|
| - // syntax and semantic resolution.
|
| - TypeDeclarationElement currentlyResolvedTypeDeclaration;
|
| - Queue<ClassElement> pendingClassesToBeResolved = new Queue<ClassElement>();
|
| - Queue<ClassElement> pendingClassesToBePostProcessed =
|
| - new Queue<ClassElement>();
|
| -
|
| - /// Resolve [element] using [resolveTypeDeclaration].
|
| - ///
|
| - /// This methods ensure that class declarations encountered through type
|
| - /// annotations during the resolution of [element] are resolved after
|
| - /// [element] has been resolved.
|
| - // TODO(johnniwinther): Encapsulate this functionality in a
|
| - // 'TypeDeclarationResolver'.
|
| - _resolveTypeDeclaration(TypeDeclarationElement element,
|
| - resolveTypeDeclaration()) {
|
| - return compiler.withCurrentElement(element, () {
|
| - return measure(() {
|
| - TypeDeclarationElement previousResolvedTypeDeclaration =
|
| - currentlyResolvedTypeDeclaration;
|
| - currentlyResolvedTypeDeclaration = element;
|
| - var result = resolveTypeDeclaration();
|
| - if (previousResolvedTypeDeclaration == null) {
|
| - do {
|
| - while (!pendingClassesToBeResolved.isEmpty) {
|
| - pendingClassesToBeResolved.removeFirst().ensureResolved(compiler);
|
| - }
|
| - while (!pendingClassesToBePostProcessed.isEmpty) {
|
| - _postProcessClassElement(
|
| - pendingClassesToBePostProcessed.removeFirst());
|
| - }
|
| - } while (!pendingClassesToBeResolved.isEmpty);
|
| - assert(pendingClassesToBeResolved.isEmpty);
|
| - assert(pendingClassesToBePostProcessed.isEmpty);
|
| - }
|
| - currentlyResolvedTypeDeclaration = previousResolvedTypeDeclaration;
|
| - return result;
|
| - });
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Resolve the class [element].
|
| - *
|
| - * Before calling this method, [element] was constructed by the
|
| - * scanner and most fields are null or empty. This method fills in
|
| - * these fields and also ensure that the supertypes of [element] are
|
| - * resolved.
|
| - *
|
| - * Warning: Do not call this method directly. Instead use
|
| - * [:element.ensureResolved(compiler):].
|
| - */
|
| - TreeElements resolveClass(BaseClassElementX element) {
|
| - return _resolveTypeDeclaration(element, () {
|
| - // TODO(johnniwinther): Store the mapping in the resolution enqueuer.
|
| - ResolutionRegistry registry = new ResolutionRegistry(compiler, element);
|
| - resolveClassInternal(element, registry);
|
| - return element.treeElements;
|
| - });
|
| - }
|
| -
|
| - void _ensureClassWillBeResolved(ClassElement element) {
|
| - if (currentlyResolvedTypeDeclaration == null) {
|
| - element.ensureResolved(compiler);
|
| - } else {
|
| - pendingClassesToBeResolved.add(element);
|
| - }
|
| - }
|
| -
|
| - void resolveClassInternal(BaseClassElementX element,
|
| - ResolutionRegistry registry) {
|
| - if (!element.isPatch) {
|
| - compiler.withCurrentElement(element, () => measure(() {
|
| - assert(element.resolutionState == STATE_NOT_STARTED);
|
| - element.resolutionState = STATE_STARTED;
|
| - Node tree = element.parseNode(compiler);
|
| - loadSupertypes(element, tree);
|
| -
|
| - ClassResolverVisitor visitor =
|
| - new ClassResolverVisitor(compiler, element, registry);
|
| - visitor.visit(tree);
|
| - element.resolutionState = STATE_DONE;
|
| - compiler.onClassResolved(element);
|
| - pendingClassesToBePostProcessed.add(element);
|
| - }));
|
| - if (element.isPatched) {
|
| - // Ensure handling patch after origin.
|
| - element.patch.ensureResolved(compiler);
|
| - }
|
| - } else { // Handle patch classes:
|
| - element.resolutionState = STATE_STARTED;
|
| - // Ensure handling origin before patch.
|
| - element.origin.ensureResolved(compiler);
|
| - // Ensure that the type is computed.
|
| - element.computeType(compiler);
|
| - // Copy class hierarchy from origin.
|
| - element.supertype = element.origin.supertype;
|
| - element.interfaces = element.origin.interfaces;
|
| - element.allSupertypesAndSelf = element.origin.allSupertypesAndSelf;
|
| - // Stepwise assignment to ensure invariant.
|
| - element.supertypeLoadState = STATE_STARTED;
|
| - element.supertypeLoadState = STATE_DONE;
|
| - element.resolutionState = STATE_DONE;
|
| - // TODO(johnniwinther): Check matching type variables and
|
| - // empty extends/implements clauses.
|
| - }
|
| - }
|
| -
|
| - void _postProcessClassElement(BaseClassElementX element) {
|
| - for (MetadataAnnotation metadata in element.metadata) {
|
| - metadata.ensureResolved(compiler);
|
| - if (!element.isProxy &&
|
| - metadata.constant.value == compiler.proxyConstant) {
|
| - element.isProxy = true;
|
| - }
|
| - }
|
| -
|
| - // Force resolution of metadata on non-instance members since they may be
|
| - // inspected by the backend while emitting. Metadata on instance members is
|
| - // handled as a result of processing instantiated class members in the
|
| - // enqueuer.
|
| - // TODO(ahe): Avoid this eager resolution.
|
| - element.forEachMember((_, Element member) {
|
| - if (!member.isInstanceMember) {
|
| - compiler.withCurrentElement(member, () {
|
| - for (MetadataAnnotation metadata in member.metadata) {
|
| - metadata.ensureResolved(compiler);
|
| - }
|
| - });
|
| - }
|
| - });
|
| -
|
| - computeClassMember(element, Compiler.CALL_OPERATOR_NAME);
|
| - }
|
| -
|
| - void computeClassMembers(ClassElement element) {
|
| - MembersCreator.computeAllClassMembers(compiler, element);
|
| - }
|
| -
|
| - void computeClassMember(ClassElement element, String name) {
|
| - MembersCreator.computeClassMembersByName(compiler, element, name);
|
| - }
|
| -
|
| - void checkClass(ClassElement element) {
|
| - computeClassMembers(element);
|
| - if (element.isMixinApplication) {
|
| - checkMixinApplication(element);
|
| - } else {
|
| - checkClassMembers(element);
|
| - }
|
| - }
|
| -
|
| - void checkMixinApplication(MixinApplicationElementX mixinApplication) {
|
| - Modifiers modifiers = mixinApplication.modifiers;
|
| - int illegalFlags = modifiers.flags & ~Modifiers.FLAG_ABSTRACT;
|
| - if (illegalFlags != 0) {
|
| - Modifiers illegalModifiers = new Modifiers.withFlags(null, illegalFlags);
|
| - compiler.reportError(
|
| - modifiers,
|
| - MessageKind.ILLEGAL_MIXIN_APPLICATION_MODIFIERS,
|
| - {'modifiers': illegalModifiers});
|
| - }
|
| -
|
| - // In case of cyclic mixin applications, the mixin chain will have
|
| - // been cut. If so, we have already reported the error to the
|
| - // user so we just return from here.
|
| - ClassElement mixin = mixinApplication.mixin;
|
| - if (mixin == null) return;
|
| -
|
| - // Check that we're not trying to use Object as a mixin.
|
| - if (mixin.superclass == null) {
|
| - compiler.reportError(mixinApplication,
|
| - MessageKind.ILLEGAL_MIXIN_OBJECT);
|
| - // Avoid reporting additional errors for the Object class.
|
| - return;
|
| - }
|
| -
|
| - // Check that the mixed in class has Object as its superclass.
|
| - if (!mixin.superclass.isObject) {
|
| - compiler.reportError(mixin, MessageKind.ILLEGAL_MIXIN_SUPERCLASS);
|
| - }
|
| -
|
| - // Check that the mixed in class doesn't have any constructors and
|
| - // make sure we aren't mixing in methods that use 'super'.
|
| - mixin.forEachLocalMember((AstElement member) {
|
| - if (member.isGenerativeConstructor && !member.isSynthesized) {
|
| - compiler.reportError(member, MessageKind.ILLEGAL_MIXIN_CONSTRUCTOR);
|
| - } else {
|
| - // Get the resolution tree and check that the resolved member
|
| - // doesn't use 'super'. This is the part of the 'super' mixin
|
| - // check that happens when a function is resolved before the
|
| - // mixin application has been performed.
|
| - // TODO(johnniwinther): Obtain the [TreeElements] for [member]
|
| - // differently.
|
| - if (compiler.enqueuer.resolution.hasBeenResolved(member)) {
|
| - checkMixinSuperUses(
|
| - member.resolvedAst.elements,
|
| - mixinApplication,
|
| - mixin);
|
| - }
|
| - }
|
| - });
|
| - }
|
| -
|
| - void checkMixinSuperUses(TreeElements resolutionTree,
|
| - MixinApplicationElement mixinApplication,
|
| - ClassElement mixin) {
|
| - // TODO(johnniwinther): Avoid the use of [TreeElements] here.
|
| - if (resolutionTree == null) return;
|
| - Iterable<Node> superUses = resolutionTree.superUses;
|
| - if (superUses.isEmpty) return;
|
| - compiler.reportError(mixinApplication,
|
| - MessageKind.ILLEGAL_MIXIN_WITH_SUPER,
|
| - {'className': mixin.name});
|
| - // Show the user the problematic uses of 'super' in the mixin.
|
| - for (Node use in superUses) {
|
| - compiler.reportInfo(
|
| - use,
|
| - MessageKind.ILLEGAL_MIXIN_SUPER_USE);
|
| - }
|
| - }
|
| -
|
| - void checkClassMembers(ClassElement cls) {
|
| - assert(invariant(cls, cls.isDeclaration));
|
| - if (cls.isObject) return;
|
| - // TODO(johnniwinther): Should this be done on the implementation element as
|
| - // well?
|
| - List<Element> constConstructors = <Element>[];
|
| - List<Element> nonFinalInstanceFields = <Element>[];
|
| - cls.forEachMember((holder, member) {
|
| - compiler.withCurrentElement(member, () {
|
| - // Perform various checks as side effect of "computing" the type.
|
| - member.computeType(compiler);
|
| -
|
| - // Check modifiers.
|
| - if (member.isFunction && member.modifiers.isFinal) {
|
| - compiler.reportError(
|
| - member, MessageKind.ILLEGAL_FINAL_METHOD_MODIFIER);
|
| - }
|
| - if (member.isConstructor) {
|
| - final mismatchedFlagsBits =
|
| - member.modifiers.flags &
|
| - (Modifiers.FLAG_STATIC | Modifiers.FLAG_ABSTRACT);
|
| - if (mismatchedFlagsBits != 0) {
|
| - final mismatchedFlags =
|
| - new Modifiers.withFlags(null, mismatchedFlagsBits);
|
| - compiler.reportError(
|
| - member,
|
| - MessageKind.ILLEGAL_CONSTRUCTOR_MODIFIERS,
|
| - {'modifiers': mismatchedFlags});
|
| - }
|
| - if (member.modifiers.isConst) {
|
| - constConstructors.add(member);
|
| - }
|
| - }
|
| - if (member.isField) {
|
| - if (member.modifiers.isConst && !member.modifiers.isStatic) {
|
| - compiler.reportError(
|
| - member, MessageKind.ILLEGAL_CONST_FIELD_MODIFIER);
|
| - }
|
| - if (!member.modifiers.isStatic && !member.modifiers.isFinal) {
|
| - nonFinalInstanceFields.add(member);
|
| - }
|
| - }
|
| - checkAbstractField(member);
|
| - checkUserDefinableOperator(member);
|
| - });
|
| - });
|
| - if (!constConstructors.isEmpty && !nonFinalInstanceFields.isEmpty) {
|
| - Spannable span = constConstructors.length > 1
|
| - ? cls : constConstructors[0];
|
| - compiler.reportError(span,
|
| - MessageKind.CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS,
|
| - {'className': cls.name});
|
| - if (constConstructors.length > 1) {
|
| - for (Element constructor in constConstructors) {
|
| - compiler.reportInfo(constructor,
|
| - MessageKind.CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_CONSTRUCTOR);
|
| - }
|
| - }
|
| - for (Element field in nonFinalInstanceFields) {
|
| - compiler.reportInfo(field,
|
| - MessageKind.CONST_CONSTRUCTOR_WITH_NONFINAL_FIELDS_FIELD);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void checkAbstractField(Element member) {
|
| - // Only check for getters. The test can only fail if there is both a setter
|
| - // and a getter with the same name, and we only need to check each abstract
|
| - // field once, so we just ignore setters.
|
| - if (!member.isGetter) return;
|
| -
|
| - // Find the associated abstract field.
|
| - ClassElement classElement = member.enclosingClass;
|
| - Element lookupElement = classElement.lookupLocalMember(member.name);
|
| - if (lookupElement == null) {
|
| - compiler.internalError(member,
|
| - "No abstract field for accessor");
|
| - } else if (!identical(lookupElement.kind, ElementKind.ABSTRACT_FIELD)) {
|
| - compiler.internalError(member,
|
| - "Inaccessible abstract field for accessor");
|
| - }
|
| - AbstractFieldElement field = lookupElement;
|
| -
|
| - FunctionElementX getter = field.getter;
|
| - if (getter == null) return;
|
| - FunctionElementX setter = field.setter;
|
| - if (setter == null) return;
|
| - int getterFlags = getter.modifiers.flags | Modifiers.FLAG_ABSTRACT;
|
| - int setterFlags = setter.modifiers.flags | Modifiers.FLAG_ABSTRACT;
|
| - if (!identical(getterFlags, setterFlags)) {
|
| - final mismatchedFlags =
|
| - new Modifiers.withFlags(null, getterFlags ^ setterFlags);
|
| - compiler.reportError(
|
| - field.getter,
|
| - MessageKind.GETTER_MISMATCH,
|
| - {'modifiers': mismatchedFlags});
|
| - compiler.reportError(
|
| - field.setter,
|
| - MessageKind.SETTER_MISMATCH,
|
| - {'modifiers': mismatchedFlags});
|
| - }
|
| - }
|
| -
|
| - void checkUserDefinableOperator(Element member) {
|
| - FunctionElement function = member.asFunctionElement();
|
| - if (function == null) return;
|
| - String value = member.name;
|
| - if (value == null) return;
|
| - if (!(isUserDefinableOperator(value) || identical(value, 'unary-'))) return;
|
| -
|
| - bool isMinus = false;
|
| - int requiredParameterCount;
|
| - MessageKind messageKind;
|
| - if (identical(value, 'unary-')) {
|
| - isMinus = true;
|
| - messageKind = MessageKind.MINUS_OPERATOR_BAD_ARITY;
|
| - requiredParameterCount = 0;
|
| - } else if (isMinusOperator(value)) {
|
| - isMinus = true;
|
| - messageKind = MessageKind.MINUS_OPERATOR_BAD_ARITY;
|
| - requiredParameterCount = 1;
|
| - } else if (isUnaryOperator(value)) {
|
| - messageKind = MessageKind.UNARY_OPERATOR_BAD_ARITY;
|
| - requiredParameterCount = 0;
|
| - } else if (isBinaryOperator(value)) {
|
| - messageKind = MessageKind.BINARY_OPERATOR_BAD_ARITY;
|
| - requiredParameterCount = 1;
|
| - if (identical(value, '==')) checkOverrideHashCode(member);
|
| - } else if (isTernaryOperator(value)) {
|
| - messageKind = MessageKind.TERNARY_OPERATOR_BAD_ARITY;
|
| - requiredParameterCount = 2;
|
| - } else {
|
| - compiler.internalError(function,
|
| - 'Unexpected user defined operator $value');
|
| - }
|
| - checkArity(function, requiredParameterCount, messageKind, isMinus);
|
| - }
|
| -
|
| - void checkOverrideHashCode(FunctionElement operatorEquals) {
|
| - if (operatorEquals.isAbstract) return;
|
| - ClassElement cls = operatorEquals.enclosingClass;
|
| - Element hashCodeImplementation =
|
| - cls.lookupLocalMember('hashCode');
|
| - if (hashCodeImplementation != null) return;
|
| - compiler.reportHint(
|
| - operatorEquals, MessageKind.OVERRIDE_EQUALS_NOT_HASH_CODE,
|
| - {'class': cls.name});
|
| - }
|
| -
|
| - void checkArity(FunctionElement function,
|
| - int requiredParameterCount, MessageKind messageKind,
|
| - bool isMinus) {
|
| - FunctionExpression node = function.node;
|
| - FunctionSignature signature = function.functionSignature;
|
| - if (signature.requiredParameterCount != requiredParameterCount) {
|
| - Node errorNode = node;
|
| - if (node.parameters != null) {
|
| - if (isMinus ||
|
| - signature.requiredParameterCount < requiredParameterCount) {
|
| - // If there are too few parameters, point to the whole parameter list.
|
| - // For instance
|
| - //
|
| - // int operator +() {}
|
| - // ^^
|
| - //
|
| - // int operator []=(value) {}
|
| - // ^^^^^^^
|
| - //
|
| - // For operator -, always point the whole parameter list, like
|
| - //
|
| - // int operator -(a, b) {}
|
| - // ^^^^^^
|
| - //
|
| - // instead of
|
| - //
|
| - // int operator -(a, b) {}
|
| - // ^
|
| - //
|
| - // since the correction might not be to remove 'b' but instead to
|
| - // remove 'a, b'.
|
| - errorNode = node.parameters;
|
| - } else {
|
| - errorNode = node.parameters.nodes.skip(requiredParameterCount).head;
|
| - }
|
| - }
|
| - compiler.reportError(
|
| - errorNode, messageKind, {'operatorName': function.name});
|
| - }
|
| - if (signature.optionalParameterCount != 0) {
|
| - Node errorNode =
|
| - node.parameters.nodes.skip(signature.requiredParameterCount).head;
|
| - if (signature.optionalParametersAreNamed) {
|
| - compiler.reportError(
|
| - errorNode,
|
| - MessageKind.OPERATOR_NAMED_PARAMETERS,
|
| - {'operatorName': function.name});
|
| - } else {
|
| - compiler.reportError(
|
| - errorNode,
|
| - MessageKind.OPERATOR_OPTIONAL_PARAMETERS,
|
| - {'operatorName': function.name});
|
| - }
|
| - }
|
| - }
|
| -
|
| - reportErrorWithContext(Element errorneousElement,
|
| - MessageKind errorMessage,
|
| - Element contextElement,
|
| - MessageKind contextMessage) {
|
| - compiler.reportError(
|
| - errorneousElement,
|
| - errorMessage,
|
| - {'memberName': contextElement.name,
|
| - 'className': contextElement.enclosingClass.name});
|
| - compiler.reportInfo(contextElement, contextMessage);
|
| - }
|
| -
|
| -
|
| - FunctionSignature resolveSignature(FunctionElementX element) {
|
| - MessageKind defaultValuesError = null;
|
| - if (element.isFactoryConstructor) {
|
| - FunctionExpression body = element.parseNode(compiler);
|
| - if (body.isRedirectingFactory) {
|
| - defaultValuesError = MessageKind.REDIRECTING_FACTORY_WITH_DEFAULT;
|
| - }
|
| - }
|
| - return compiler.withCurrentElement(element, () {
|
| - FunctionExpression node =
|
| - compiler.parser.measure(() => element.parseNode(compiler));
|
| - return measure(() => SignatureResolver.analyze(
|
| - compiler, node.parameters, node.returnType, element,
|
| - new ResolutionRegistry(compiler, element),
|
| - defaultValuesError: defaultValuesError,
|
| - createRealParameters: true));
|
| - });
|
| - }
|
| -
|
| - TreeElements resolveTypedef(TypedefElementX element) {
|
| - if (element.isResolved) return element.treeElements;
|
| - compiler.world.allTypedefs.add(element);
|
| - return _resolveTypeDeclaration(element, () {
|
| - ResolutionRegistry registry = new ResolutionRegistry(compiler, element);
|
| - return compiler.withCurrentElement(element, () {
|
| - return measure(() {
|
| - assert(element.resolutionState == STATE_NOT_STARTED);
|
| - element.resolutionState = STATE_STARTED;
|
| - Typedef node =
|
| - compiler.parser.measure(() => element.parseNode(compiler));
|
| - TypedefResolverVisitor visitor =
|
| - new TypedefResolverVisitor(compiler, element, registry);
|
| - visitor.visit(node);
|
| - element.resolutionState = STATE_DONE;
|
| - return registry.mapping;
|
| - });
|
| - });
|
| - });
|
| - }
|
| -
|
| - void resolveMetadataAnnotation(MetadataAnnotationX annotation) {
|
| - compiler.withCurrentElement(annotation.annotatedElement, () => measure(() {
|
| - assert(annotation.resolutionState == STATE_NOT_STARTED);
|
| - annotation.resolutionState = STATE_STARTED;
|
| -
|
| - Node node = annotation.parseNode(compiler);
|
| - Element annotatedElement = annotation.annotatedElement;
|
| - AnalyzableElement context = annotatedElement.analyzableElement;
|
| - ClassElement classElement = annotatedElement.enclosingClass;
|
| - if (classElement != null) {
|
| - // The annotation is resolved in the scope of [classElement].
|
| - classElement.ensureResolved(compiler);
|
| - }
|
| - assert(invariant(node, context != null,
|
| - message: "No context found for metadata annotation "
|
| - "on $annotatedElement."));
|
| - ResolverVisitor visitor = visitorFor(context, useEnclosingScope: true);
|
| - ResolutionRegistry registry = visitor.registry;
|
| - node.accept(visitor);
|
| - // TODO(johnniwinther): Avoid passing the [TreeElements] to
|
| - // [compileMetadata].
|
| - annotation.constant =
|
| - constantCompiler.compileMetadata(annotation, node, registry.mapping);
|
| - // TODO(johnniwinther): Register the relation between the annotation
|
| - // and the annotated element instead. This will allow the backend to
|
| - // retrieve the backend constant and only register metadata on the
|
| - // elements for which it is needed. (Issue 17732).
|
| - registry.registerMetadataConstant(annotation, annotatedElement);
|
| - annotation.resolutionState = STATE_DONE;
|
| - }));
|
| - }
|
| -
|
| - error(Spannable node, MessageKind kind, [arguments = const {}]) {
|
| - // TODO(ahe): Make non-fatal.
|
| - compiler.reportFatalError(node, kind, arguments);
|
| - }
|
| -
|
| - Link<MetadataAnnotation> resolveMetadata(Element element,
|
| - VariableDefinitions node) {
|
| - LinkBuilder<MetadataAnnotation> metadata =
|
| - new LinkBuilder<MetadataAnnotation>();
|
| - for (Metadata annotation in node.metadata.nodes) {
|
| - ParameterMetadataAnnotation metadataAnnotation =
|
| - new ParameterMetadataAnnotation(annotation);
|
| - metadataAnnotation.annotatedElement = element;
|
| - metadata.addLast(metadataAnnotation.ensureResolved(compiler));
|
| - }
|
| - return metadata.toLink();
|
| - }
|
| -}
|
| -
|
| -class InitializerResolver {
|
| - final ResolverVisitor visitor;
|
| - final Map<Element, Node> initialized;
|
| - Link<Node> initializers;
|
| - bool hasSuper;
|
| -
|
| - InitializerResolver(this.visitor)
|
| - : initialized = new Map<Element, Node>(), hasSuper = false;
|
| -
|
| - ResolutionRegistry get registry => visitor.registry;
|
| -
|
| - 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();
|
| - }
|
| -
|
| - reportDuplicateInitializerError(Element field, Node init, Node existing) {
|
| - visitor.compiler.reportError(
|
| - init,
|
| - MessageKind.DUPLICATE_INITIALIZER, {'fieldName': field.name});
|
| - visitor.compiler.reportInfo(
|
| - existing,
|
| - MessageKind.ALREADY_INITIALIZED, {'fieldName': field.name});
|
| - }
|
| -
|
| - void checkForDuplicateInitializers(FieldElementX field, Node init) {
|
| - // [field] can be null if it could not be resolved.
|
| - if (field == null) return;
|
| - String name = field.name;
|
| - if (initialized.containsKey(field)) {
|
| - reportDuplicateInitializerError(field, init, initialized[field]);
|
| - } else if (field.isFinal) {
|
| - field.parseNode(visitor.compiler);
|
| - Expression initializer = field.initializer;
|
| - if (initializer != null) {
|
| - reportDuplicateInitializerError(field, init, initializer);
|
| - }
|
| - }
|
| - initialized[field] = init;
|
| - }
|
| -
|
| - void resolveFieldInitializer(FunctionElement constructor, SendSet init) {
|
| - // init is of the form [this.]field = value.
|
| - final Node selector = init.selector;
|
| - final String name = selector.asIdentifier().source;
|
| - // Lookup target field.
|
| - Element target;
|
| - if (isFieldInitializer(init)) {
|
| - target = constructor.enclosingClass.lookupLocalMember(name);
|
| - if (target == null) {
|
| - error(selector, MessageKind.CANNOT_RESOLVE, {'name': name});
|
| - } else if (target.kind != ElementKind.FIELD) {
|
| - error(selector, MessageKind.NOT_A_FIELD, {'fieldName': name});
|
| - } else if (!target.isInstanceMember) {
|
| - error(selector, MessageKind.INIT_STATIC_FIELD, {'fieldName': name});
|
| - }
|
| - } else {
|
| - error(init, MessageKind.INVALID_RECEIVER_IN_INITIALIZER);
|
| - }
|
| - registry.useElement(init, target);
|
| - registry.registerStaticUse(target);
|
| - checkForDuplicateInitializers(target, init);
|
| - // Resolve initializing value.
|
| - visitor.visitInStaticContext(init.arguments.head);
|
| - }
|
| -
|
| - ClassElement getSuperOrThisLookupTarget(FunctionElement constructor,
|
| - bool isSuperCall,
|
| - Node diagnosticNode) {
|
| - ClassElement lookupTarget = constructor.enclosingClass;
|
| - if (isSuperCall) {
|
| - // Calculate correct lookup target and constructor name.
|
| - if (identical(lookupTarget, visitor.compiler.objectClass)) {
|
| - error(diagnosticNode, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
|
| - } else {
|
| - return lookupTarget.supertype.element;
|
| - }
|
| - }
|
| - return lookupTarget;
|
| - }
|
| -
|
| - Element resolveSuperOrThisForSend(FunctionElement constructor,
|
| - FunctionExpression functionNode,
|
| - Send call) {
|
| - // Resolve the selector and the arguments.
|
| - ResolverTask resolver = visitor.compiler.resolver;
|
| - visitor.inStaticContext(() {
|
| - visitor.resolveSelector(call, null);
|
| - visitor.resolveArguments(call.argumentsNode);
|
| - });
|
| - Selector selector = registry.getSelector(call);
|
| - bool isSuperCall = Initializers.isSuperConstructorCall(call);
|
| -
|
| - ClassElement lookupTarget = getSuperOrThisLookupTarget(constructor,
|
| - isSuperCall,
|
| - call);
|
| - Selector constructorSelector =
|
| - visitor.getRedirectingThisOrSuperConstructorSelector(call);
|
| - FunctionElement calledConstructor =
|
| - lookupTarget.lookupConstructor(constructorSelector);
|
| -
|
| - final bool isImplicitSuperCall = false;
|
| - final String className = lookupTarget.name;
|
| - verifyThatConstructorMatchesCall(constructor,
|
| - calledConstructor,
|
| - selector,
|
| - isImplicitSuperCall,
|
| - call,
|
| - className,
|
| - constructorSelector);
|
| -
|
| - registry.useElement(call, calledConstructor);
|
| - registry.registerStaticUse(calledConstructor);
|
| - return calledConstructor;
|
| - }
|
| -
|
| - void resolveImplicitSuperConstructorSend(FunctionElement constructor,
|
| - FunctionExpression functionNode) {
|
| - // If the class has a super resolve the implicit super call.
|
| - ClassElement classElement = constructor.enclosingClass;
|
| - ClassElement superClass = classElement.superclass;
|
| - if (classElement != visitor.compiler.objectClass) {
|
| - assert(superClass != null);
|
| - assert(superClass.resolutionState == STATE_DONE);
|
| - String constructorName = '';
|
| - Selector callToMatch = new Selector.call(
|
| - constructorName,
|
| - classElement.library,
|
| - 0);
|
| -
|
| - final bool isSuperCall = true;
|
| - ClassElement lookupTarget = getSuperOrThisLookupTarget(constructor,
|
| - isSuperCall,
|
| - functionNode);
|
| - Selector constructorSelector = new Selector.callDefaultConstructor(
|
| - visitor.enclosingElement.library);
|
| - Element calledConstructor = lookupTarget.lookupConstructor(
|
| - constructorSelector);
|
| -
|
| - final String className = lookupTarget.name;
|
| - final bool isImplicitSuperCall = true;
|
| - verifyThatConstructorMatchesCall(constructor,
|
| - calledConstructor,
|
| - callToMatch,
|
| - isImplicitSuperCall,
|
| - functionNode,
|
| - className,
|
| - constructorSelector);
|
| - registry.registerImplicitSuperCall(calledConstructor);
|
| - registry.registerStaticUse(calledConstructor);
|
| - }
|
| - }
|
| -
|
| - void verifyThatConstructorMatchesCall(
|
| - FunctionElement caller,
|
| - FunctionElement lookedupConstructor,
|
| - Selector call,
|
| - bool isImplicitSuperCall,
|
| - Node diagnosticNode,
|
| - String className,
|
| - Selector constructorSelector) {
|
| - if (lookedupConstructor == null
|
| - || !lookedupConstructor.isGenerativeConstructor) {
|
| - String fullConstructorName = Elements.constructorNameForDiagnostics(
|
| - className,
|
| - constructorSelector.name);
|
| - MessageKind kind = isImplicitSuperCall
|
| - ? MessageKind.CANNOT_RESOLVE_CONSTRUCTOR_FOR_IMPLICIT
|
| - : MessageKind.CANNOT_RESOLVE_CONSTRUCTOR;
|
| - visitor.compiler.reportError(
|
| - diagnosticNode, kind, {'constructorName': fullConstructorName});
|
| - } else {
|
| - lookedupConstructor.computeSignature(visitor.compiler);
|
| - if (!call.applies(lookedupConstructor, visitor.compiler.world)) {
|
| - MessageKind kind = isImplicitSuperCall
|
| - ? MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT
|
| - : MessageKind.NO_MATCHING_CONSTRUCTOR;
|
| - visitor.compiler.reportError(diagnosticNode, kind);
|
| - } else if (caller.isConst
|
| - && !lookedupConstructor.isConst) {
|
| - visitor.compiler.reportError(
|
| - diagnosticNode, MessageKind.CONST_CALLS_NON_CONST);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * 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) {
|
| - // Keep track of all "this.param" parameters specified for constructor so
|
| - // that we can ensure that fields are initialized only once.
|
| - FunctionSignature functionParameters = constructor.functionSignature;
|
| - functionParameters.forEachParameter((ParameterElement element) {
|
| - if (element.isInitializingFormal) {
|
| - InitializingFormalElement initializingFormal = element;
|
| - checkForDuplicateInitializers(initializingFormal.fieldElement,
|
| - element.initializer);
|
| - }
|
| - });
|
| -
|
| - if (functionNode.initializers == null) {
|
| - initializers = const Link<Node>();
|
| - } else {
|
| - initializers = functionNode.initializers.nodes;
|
| - }
|
| - FunctionElement result;
|
| - bool resolvedSuper = false;
|
| - 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();
|
| - if (call.argumentsNode == null) {
|
| - error(link.head, MessageKind.INVALID_INITIALIZER);
|
| - continue;
|
| - }
|
| - if (Initializers.isSuperConstructorCall(call)) {
|
| - if (resolvedSuper) {
|
| - error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER);
|
| - }
|
| - resolveSuperOrThisForSend(constructor, functionNode, call);
|
| - resolvedSuper = true;
|
| - } else if (Initializers.isConstructorRedirect(call)) {
|
| - // Check that there is no body (Language specification 7.5.1). If the
|
| - // constructor is also const, we already reported an error in
|
| - // [resolveMethodElement].
|
| - if (functionNode.hasBody() && !constructor.isConst) {
|
| - 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);
|
| - }
|
| - // Check that there are no field initializing parameters.
|
| - Compiler compiler = visitor.compiler;
|
| - FunctionSignature signature = constructor.functionSignature;
|
| - signature.forEachParameter((ParameterElement parameter) {
|
| - if (parameter.isInitializingFormal) {
|
| - Node node = parameter.node;
|
| - error(node, MessageKind.INITIALIZING_FORMAL_NOT_ALLOWED);
|
| - }
|
| - });
|
| - return resolveSuperOrThisForSend(constructor, functionNode, call);
|
| - } else {
|
| - visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED);
|
| - return null;
|
| - }
|
| - } else {
|
| - error(link.head, MessageKind.INVALID_INITIALIZER);
|
| - }
|
| - }
|
| - if (!resolvedSuper) {
|
| - resolveImplicitSuperConstructorSend(constructor, functionNode);
|
| - }
|
| - return null; // If there was no redirection always return null.
|
| - }
|
| -}
|
| -
|
| -class CommonResolverVisitor<R> extends Visitor<R> {
|
| - final Compiler compiler;
|
| -
|
| - CommonResolverVisitor(Compiler this.compiler);
|
| -
|
| - R visitNode(Node node) {
|
| - internalError(node,
|
| - 'internal error: Unhandled node: ${node.getObjectDescription()}');
|
| - return null;
|
| - }
|
| -
|
| - 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(Spannable node, MessageKind kind, [Map arguments = const {}]) {
|
| - compiler.reportFatalError(node, kind, arguments);
|
| - }
|
| -
|
| - void warning(Spannable node, MessageKind kind, [Map arguments = const {}]) {
|
| - compiler.reportWarning(node, kind, arguments);
|
| - }
|
| -
|
| - void internalError(Spannable node, message) {
|
| - compiler.internalError(node, message);
|
| - }
|
| -
|
| - void addDeferredAction(Element element, DeferredAction action) {
|
| - compiler.enqueuer.resolution.addDeferredAction(element, action);
|
| - }
|
| -}
|
| -
|
| -abstract class LabelScope {
|
| - LabelScope get outer;
|
| - LabelDefinition lookup(String label);
|
| -}
|
| -
|
| -class LabeledStatementLabelScope implements LabelScope {
|
| - final LabelScope outer;
|
| - final Map<String, LabelDefinition> labels;
|
| - LabeledStatementLabelScope(this.outer, this.labels);
|
| - LabelDefinition lookup(String labelName) {
|
| - LabelDefinition label = labels[labelName];
|
| - if (label != null) return label;
|
| - return outer.lookup(labelName);
|
| - }
|
| -}
|
| -
|
| -class SwitchLabelScope implements LabelScope {
|
| - final LabelScope outer;
|
| - final Map<String, LabelDefinition> caseLabels;
|
| -
|
| - SwitchLabelScope(this.outer, this.caseLabels);
|
| -
|
| - LabelDefinition lookup(String labelName) {
|
| - LabelDefinition result = caseLabels[labelName];
|
| - if (result != null) return result;
|
| - return outer.lookup(labelName);
|
| - }
|
| -}
|
| -
|
| -class EmptyLabelScope implements LabelScope {
|
| - const EmptyLabelScope();
|
| - LabelDefinition lookup(String label) => null;
|
| - LabelScope get outer {
|
| - throw 'internal error: empty label scope has no outer';
|
| - }
|
| -}
|
| -
|
| -class StatementScope {
|
| - LabelScope labels;
|
| - Link<JumpTarget> breakTargetStack;
|
| - Link<JumpTarget> 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 Link<JumpTarget>(),
|
| - continueTargetStack = const Link<JumpTarget>();
|
| -
|
| - LabelDefinition lookupLabel(String label) {
|
| - return labels.lookup(label);
|
| - }
|
| -
|
| - JumpTarget currentBreakTarget() =>
|
| - breakTargetStack.isEmpty ? null : breakTargetStack.head;
|
| -
|
| - JumpTarget currentContinueTarget() =>
|
| - continueTargetStack.isEmpty ? null : continueTargetStack.head;
|
| -
|
| - void enterLabelScope(Map<String, LabelDefinition> elements) {
|
| - labels = new LabeledStatementLabelScope(labels, elements);
|
| - nestingLevel++;
|
| - }
|
| -
|
| - void exitLabelScope() {
|
| - nestingLevel--;
|
| - labels = labels.outer;
|
| - }
|
| -
|
| - void enterLoop(JumpTarget element) {
|
| - breakTargetStack = breakTargetStack.prepend(element);
|
| - continueTargetStack = continueTargetStack.prepend(element);
|
| - nestingLevel++;
|
| - }
|
| -
|
| - void exitLoop() {
|
| - nestingLevel--;
|
| - breakTargetStack = breakTargetStack.tail;
|
| - continueTargetStack = continueTargetStack.tail;
|
| - }
|
| -
|
| - void enterSwitch(JumpTarget breakElement,
|
| - Map<String, LabelDefinition> continueElements) {
|
| - breakTargetStack = breakTargetStack.prepend(breakElement);
|
| - labels = new SwitchLabelScope(labels, continueElements);
|
| - nestingLevel++;
|
| - }
|
| -
|
| - void exitSwitch() {
|
| - nestingLevel--;
|
| - breakTargetStack = breakTargetStack.tail;
|
| - labels = labels.outer;
|
| - }
|
| -}
|
| -
|
| -class TypeResolver {
|
| - final Compiler compiler;
|
| -
|
| - TypeResolver(this.compiler);
|
| -
|
| - /// Tries to resolve the type name as an element.
|
| - Element resolveTypeName(Identifier prefixName,
|
| - Identifier typeName,
|
| - Scope scope,
|
| - {bool deferredIsMalformed: true}) {
|
| - Element element;
|
| - bool deferredTypeAnnotation = false;
|
| - if (prefixName != null) {
|
| - Element prefixElement =
|
| - lookupInScope(compiler, prefixName, scope, prefixName.source);
|
| - if (prefixElement != null && prefixElement.isPrefix) {
|
| - // The receiver is a prefix. Lookup in the imported members.
|
| - PrefixElement prefix = prefixElement;
|
| - element = prefix.lookupLocalMember(typeName.source);
|
| - // TODO(17260, sigurdm): The test for DartBackend is there because
|
| - // dart2dart outputs malformed types with prefix.
|
| - if (element != null &&
|
| - prefix.isDeferred &&
|
| - deferredIsMalformed &&
|
| - compiler.backend is! DartBackend) {
|
| - element = new ErroneousElementX(MessageKind.DEFERRED_TYPE_ANNOTATION,
|
| - {'node': typeName},
|
| - element.name,
|
| - element);
|
| - }
|
| - } else {
|
| - // The caller of this method will create the ErroneousElement for
|
| - // the MalformedType.
|
| - element = null;
|
| - }
|
| - } else {
|
| - String stringValue = typeName.source;
|
| - element = lookupInScope(compiler, typeName, scope, typeName.source);
|
| - }
|
| - return element;
|
| - }
|
| -
|
| - DartType resolveTypeAnnotation(MappingVisitor visitor, TypeAnnotation node,
|
| - {bool malformedIsError: false,
|
| - bool deferredIsMalformed: true}) {
|
| - ResolutionRegistry registry = visitor.registry;
|
| -
|
| - Identifier typeName;
|
| - DartType type;
|
| -
|
| - DartType checkNoTypeArguments(DartType type) {
|
| - List<DartType> arguments = new List<DartType>();
|
| - bool hasTypeArgumentMismatch = resolveTypeArguments(
|
| - visitor, node, const <DartType>[], arguments);
|
| - if (hasTypeArgumentMismatch) {
|
| - return new MalformedType(
|
| - new ErroneousElementX(MessageKind.TYPE_ARGUMENT_COUNT_MISMATCH,
|
| - {'type': node}, typeName.source, visitor.enclosingElement),
|
| - type, arguments);
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - Identifier prefixName;
|
| - Send send = node.typeName.asSend();
|
| - if (send != null) {
|
| - // The type name is of the form [: prefix . identifier :].
|
| - prefixName = send.receiver.asIdentifier();
|
| - typeName = send.selector.asIdentifier();
|
| - } else {
|
| - typeName = node.typeName.asIdentifier();
|
| - if (identical(typeName.source, 'void')) {
|
| - type = const VoidType();
|
| - checkNoTypeArguments(type);
|
| - registry.useType(node, type);
|
| - return type;
|
| - } else if (identical(typeName.source, 'dynamic')) {
|
| - type = const DynamicType();
|
| - checkNoTypeArguments(type);
|
| - registry.useType(node, type);
|
| - return type;
|
| - }
|
| - }
|
| -
|
| - Element element = resolveTypeName(prefixName, typeName, visitor.scope,
|
| - deferredIsMalformed: deferredIsMalformed);
|
| -
|
| - DartType reportFailureAndCreateType(MessageKind messageKind,
|
| - Map messageArguments,
|
| - {DartType userProvidedBadType,
|
| - Element erroneousElement}) {
|
| - if (malformedIsError) {
|
| - visitor.error(node, messageKind, messageArguments);
|
| - } else {
|
| - registry.registerThrowRuntimeError();
|
| - visitor.warning(node, messageKind, messageArguments);
|
| - }
|
| - if (erroneousElement == null) {
|
| - erroneousElement = new ErroneousElementX(
|
| - messageKind, messageArguments, typeName.source,
|
| - visitor.enclosingElement);
|
| - }
|
| - List<DartType> arguments = <DartType>[];
|
| - resolveTypeArguments(visitor, node, const <DartType>[], arguments);
|
| - return new MalformedType(erroneousElement,
|
| - userProvidedBadType, arguments);
|
| - }
|
| -
|
| - // Try to construct the type from the element.
|
| - if (element == null) {
|
| - type = reportFailureAndCreateType(
|
| - MessageKind.CANNOT_RESOLVE_TYPE, {'typeName': node.typeName});
|
| - } else if (element.isAmbiguous) {
|
| - AmbiguousElement ambiguous = element;
|
| - type = reportFailureAndCreateType(
|
| - ambiguous.messageKind, ambiguous.messageArguments);
|
| - ambiguous.diagnose(registry.mapping.analyzedElement, compiler);
|
| - } else if (element.isErroneous) {
|
| - ErroneousElement erroneousElement = element;
|
| - type = reportFailureAndCreateType(
|
| - erroneousElement.messageKind, erroneousElement.messageArguments,
|
| - erroneousElement: erroneousElement);
|
| - } else if (!element.impliesType) {
|
| - type = reportFailureAndCreateType(
|
| - MessageKind.NOT_A_TYPE, {'node': node.typeName});
|
| - } else {
|
| - bool addTypeVariableBoundsCheck = false;
|
| - if (element.isClass) {
|
| - ClassElement cls = element;
|
| - // TODO(johnniwinther): [_ensureClassWillBeResolved] should imply
|
| - // [computeType].
|
| - compiler.resolver._ensureClassWillBeResolved(cls);
|
| - element.computeType(compiler);
|
| - List<DartType> arguments = <DartType>[];
|
| - bool hasTypeArgumentMismatch = resolveTypeArguments(
|
| - visitor, node, cls.typeVariables, arguments);
|
| - if (hasTypeArgumentMismatch) {
|
| - type = new BadInterfaceType(cls.declaration,
|
| - new InterfaceType.forUserProvidedBadType(cls.declaration,
|
| - arguments));
|
| - } else {
|
| - if (arguments.isEmpty) {
|
| - type = cls.rawType;
|
| - } else {
|
| - type = new InterfaceType(cls.declaration, arguments.toList(growable: false));
|
| - addTypeVariableBoundsCheck = true;
|
| - }
|
| - }
|
| - } else if (element.isTypedef) {
|
| - TypedefElement typdef = element;
|
| - // TODO(johnniwinther): [ensureResolved] should imply [computeType].
|
| - typdef.ensureResolved(compiler);
|
| - element.computeType(compiler);
|
| - List<DartType> arguments = <DartType>[];
|
| - bool hasTypeArgumentMismatch = resolveTypeArguments(
|
| - visitor, node, typdef.typeVariables, arguments);
|
| - if (hasTypeArgumentMismatch) {
|
| - type = new BadTypedefType(typdef,
|
| - new TypedefType.forUserProvidedBadType(typdef, arguments));
|
| - } else {
|
| - if (arguments.isEmpty) {
|
| - type = typdef.rawType;
|
| - } else {
|
| - type = new TypedefType(typdef, arguments.toList(growable: false));
|
| - addTypeVariableBoundsCheck = true;
|
| - }
|
| - }
|
| - } else if (element.isTypeVariable) {
|
| - Element outer =
|
| - visitor.enclosingElement.outermostEnclosingMemberOrTopLevel;
|
| - bool isInFactoryConstructor =
|
| - outer != null && outer.isFactoryConstructor;
|
| - if (!outer.isClass &&
|
| - !outer.isTypedef &&
|
| - !isInFactoryConstructor &&
|
| - Elements.isInStaticContext(visitor.enclosingElement)) {
|
| - registry.registerThrowRuntimeError();
|
| - type = reportFailureAndCreateType(
|
| - MessageKind.TYPE_VARIABLE_WITHIN_STATIC_MEMBER,
|
| - {'typeVariableName': node},
|
| - userProvidedBadType: element.computeType(compiler));
|
| - } else {
|
| - type = element.computeType(compiler);
|
| - }
|
| - type = checkNoTypeArguments(type);
|
| - } else {
|
| - compiler.internalError(node,
|
| - "Unexpected element kind ${element.kind}.");
|
| - }
|
| - if (addTypeVariableBoundsCheck) {
|
| - registry.registerTypeVariableBoundCheck();
|
| - visitor.addDeferredAction(
|
| - visitor.enclosingElement,
|
| - () => checkTypeVariableBounds(node, type));
|
| - }
|
| - }
|
| - registry.useType(node, type);
|
| - return type;
|
| - }
|
| -
|
| - /// Checks the type arguments of [type] against the type variable bounds.
|
| - void checkTypeVariableBounds(TypeAnnotation node, GenericType type) {
|
| - void checkTypeVariableBound(_, DartType typeArgument,
|
| - TypeVariableType typeVariable,
|
| - DartType bound) {
|
| - if (!compiler.types.isSubtype(typeArgument, bound)) {
|
| - compiler.reportWarning(node,
|
| - MessageKind.INVALID_TYPE_VARIABLE_BOUND,
|
| - {'typeVariable': typeVariable,
|
| - 'bound': bound,
|
| - 'typeArgument': typeArgument,
|
| - 'thisType': type.element.thisType});
|
| - }
|
| - };
|
| -
|
| - compiler.types.checkTypeVariableBounds(type, checkTypeVariableBound);
|
| - }
|
| -
|
| - /**
|
| - * Resolves the type arguments of [node] and adds these to [arguments].
|
| - *
|
| - * Returns [: true :] if the number of type arguments did not match the
|
| - * number of type variables.
|
| - */
|
| - bool resolveTypeArguments(MappingVisitor visitor,
|
| - TypeAnnotation node,
|
| - List<DartType> typeVariables,
|
| - List<DartType> arguments) {
|
| - if (node.typeArguments == null) {
|
| - return false;
|
| - }
|
| - int expectedVariables = typeVariables.length;
|
| - int index = 0;
|
| - bool typeArgumentCountMismatch = false;
|
| - for (Link<Node> typeArguments = node.typeArguments.nodes;
|
| - !typeArguments.isEmpty;
|
| - typeArguments = typeArguments.tail, index++) {
|
| - if (index > expectedVariables - 1) {
|
| - visitor.warning(
|
| - typeArguments.head, MessageKind.ADDITIONAL_TYPE_ARGUMENT);
|
| - typeArgumentCountMismatch = true;
|
| - }
|
| - DartType argType = resolveTypeAnnotation(visitor, typeArguments.head);
|
| - // TODO(karlklose): rewrite to not modify [arguments].
|
| - arguments.add(argType);
|
| - }
|
| - if (index < expectedVariables) {
|
| - visitor.warning(node.typeArguments,
|
| - MessageKind.MISSING_TYPE_ARGUMENT);
|
| - typeArgumentCountMismatch = true;
|
| - }
|
| - return typeArgumentCountMismatch;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Common supertype for resolver visitors that record resolutions in a
|
| - * [ResolutionRegistry].
|
| - */
|
| -abstract class MappingVisitor<T> extends CommonResolverVisitor<T> {
|
| - final ResolutionRegistry registry;
|
| - final TypeResolver typeResolver;
|
| - /// The current enclosing element for the visited AST nodes.
|
| - Element get enclosingElement;
|
| - /// The current scope of the visitor.
|
| - Scope get scope;
|
| -
|
| - MappingVisitor(Compiler compiler, ResolutionRegistry this.registry)
|
| - : typeResolver = new TypeResolver(compiler),
|
| - super(compiler);
|
| -
|
| - AsyncMarker get currentAsyncMarker => AsyncMarker.SYNC;
|
| -
|
| - /// Add [element] to the current scope and check for duplicate definitions.
|
| - void addToScope(Element element) {
|
| - Element existing = scope.add(element);
|
| - if (existing != element) {
|
| - reportDuplicateDefinition(element.name, element, existing);
|
| - }
|
| - }
|
| -
|
| - void checkLocalDefinitionName(Node node, Element element) {
|
| - if (currentAsyncMarker != AsyncMarker.SYNC) {
|
| - if (element.name == 'yield' ||
|
| - element.name == 'async' ||
|
| - element.name == 'await') {
|
| - compiler.reportError(
|
| - node, MessageKind.ASYNC_KEYWORD_AS_IDENTIFIER,
|
| - {'keyword': element.name,
|
| - 'modifier': currentAsyncMarker});
|
| - }
|
| - }
|
| - }
|
| -
|
| - /// Register [node] as the definition of [element].
|
| - void defineLocalVariable(Node node, LocalVariableElement element) {
|
| - invariant(node, element != null);
|
| - checkLocalDefinitionName(node, element);
|
| - registry.defineElement(node, element);
|
| - }
|
| -
|
| - void reportDuplicateDefinition(String name,
|
| - Spannable definition,
|
| - Spannable existing) {
|
| - compiler.reportError(definition,
|
| - MessageKind.DUPLICATE_DEFINITION, {'name': name});
|
| - compiler.reportInfo(existing,
|
| - MessageKind.EXISTING_DEFINITION, {'name': name});
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Core implementation of resolution.
|
| - *
|
| - * Do not subclass or instantiate this class outside this library
|
| - * except for testing.
|
| - */
|
| -class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| - /**
|
| - * The current enclosing element for the visited AST nodes.
|
| - *
|
| - * This field is updated when nested closures are visited.
|
| - */
|
| - Element enclosingElement;
|
| - bool inInstanceContext;
|
| - bool inCheckContext;
|
| - bool inCatchBlock;
|
| -
|
| - Scope scope;
|
| - ClassElement currentClass;
|
| - ExpressionStatement currentExpressionStatement;
|
| - bool sendIsMemberAccess = false;
|
| - StatementScope statementScope;
|
| - int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION
|
| - | ElementCategory.IMPLIES_TYPE;
|
| -
|
| - /**
|
| - * Record of argument nodes to JS_INTERCEPTOR_CONSTANT for deferred
|
| - * processing.
|
| - */
|
| - Set<Node> argumentsToJsInterceptorConstant = null;
|
| -
|
| - /// When visiting the type declaration of the variable in a [ForIn] loop,
|
| - /// the initializer of the variable is implicit and we should not emit an
|
| - /// error when verifying that all final variables are initialized.
|
| - bool allowFinalWithoutInitializer = false;
|
| -
|
| - /// The nodes for which variable access and mutation must be registered in
|
| - /// order to determine when the static type of variables types is promoted.
|
| - Link<Node> promotionScope = const Link<Node>();
|
| -
|
| - bool isPotentiallyMutableTarget(Element target) {
|
| - if (target == null) return false;
|
| - return (target.isVariable || target.isParameter) &&
|
| - !(target.isFinal || target.isConst);
|
| - }
|
| -
|
| - // TODO(ahe): Find a way to share this with runtime implementation.
|
| - static final RegExp symbolValidationPattern =
|
| - new RegExp(r'^(?:[a-zA-Z$][a-zA-Z$0-9_]*\.)*(?:[a-zA-Z$][a-zA-Z$0-9_]*=?|'
|
| - r'-|'
|
| - r'unary-|'
|
| - r'\[\]=|'
|
| - r'~|'
|
| - r'==|'
|
| - r'\[\]|'
|
| - r'\*|'
|
| - r'/|'
|
| - r'%|'
|
| - r'~/|'
|
| - r'\+|'
|
| - r'<<|'
|
| - r'>>|'
|
| - r'>=|'
|
| - r'>|'
|
| - r'<=|'
|
| - r'<|'
|
| - r'&|'
|
| - r'\^|'
|
| - r'\|'
|
| - r')$');
|
| -
|
| - ResolverVisitor(Compiler compiler,
|
| - Element element,
|
| - ResolutionRegistry registry,
|
| - {bool useEnclosingScope: false})
|
| - : this.enclosingElement = element,
|
| - // When the element is a field, we are actually resolving its
|
| - // initial value, which should not have access to instance
|
| - // fields.
|
| - inInstanceContext = (element.isInstanceMember && !element.isField)
|
| - || element.isGenerativeConstructor,
|
| - this.currentClass = element.isClassMember ? element.enclosingClass
|
| - : null,
|
| - this.statementScope = new StatementScope(),
|
| - scope = useEnclosingScope
|
| - ? Scope.buildEnclosingScope(element) : element.buildScope(),
|
| - // The type annotations on a typedef do not imply type checks.
|
| - // TODO(karlklose): clean this up (dartbug.com/8870).
|
| - inCheckContext = compiler.enableTypeAssertions &&
|
| - !element.isLibrary &&
|
| - !element.isTypedef &&
|
| - !element.enclosingElement.isTypedef,
|
| - inCatchBlock = false,
|
| - super(compiler, registry);
|
| -
|
| - AsyncMarker get currentAsyncMarker {
|
| - if (enclosingElement is FunctionElement) {
|
| - FunctionElement function = enclosingElement;
|
| - return function.asyncMarker;
|
| - }
|
| - return AsyncMarker.SYNC;
|
| - }
|
| -
|
| - Element reportLookupErrorIfAny(Element result, Node node, String name) {
|
| - if (!Elements.isUnresolved(result)) {
|
| - if (!inInstanceContext && result.isInstanceMember) {
|
| - compiler.reportError(
|
| - node, MessageKind.NO_INSTANCE_AVAILABLE, {'name': name});
|
| - return new ErroneousElementX(MessageKind.NO_INSTANCE_AVAILABLE,
|
| - {'name': name},
|
| - name, enclosingElement);
|
| - } else if (result.isAmbiguous) {
|
| - AmbiguousElement ambiguous = result;
|
| - compiler.reportError(
|
| - node, ambiguous.messageKind, ambiguous.messageArguments);
|
| - ambiguous.diagnose(enclosingElement, compiler);
|
| - return new ErroneousElementX(ambiguous.messageKind,
|
| - ambiguous.messageArguments,
|
| - name, enclosingElement);
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - // Create, or reuse an already created, target element for a statement.
|
| - JumpTarget getOrDefineTarget(Node statement) {
|
| - JumpTarget element = registry.getTargetDefinition(statement);
|
| - if (element == null) {
|
| - element = new JumpTargetX(statement,
|
| - statementScope.nestingLevel,
|
| - enclosingElement);
|
| - registry.defineTarget(statement, element);
|
| - }
|
| - return element;
|
| - }
|
| -
|
| - doInCheckContext(action()) {
|
| - bool wasInCheckContext = inCheckContext;
|
| - inCheckContext = true;
|
| - var result = action();
|
| - inCheckContext = wasInCheckContext;
|
| - return result;
|
| - }
|
| -
|
| - inStaticContext(action()) {
|
| - bool wasInstanceContext = inInstanceContext;
|
| - inInstanceContext = false;
|
| - var result = action();
|
| - inInstanceContext = wasInstanceContext;
|
| - return result;
|
| - }
|
| -
|
| - doInPromotionScope(Node node, action()) {
|
| - promotionScope = promotionScope.prepend(node);
|
| - var result = action();
|
| - promotionScope = promotionScope.tail;
|
| - return result;
|
| - }
|
| -
|
| - visitInStaticContext(Node node) {
|
| - inStaticContext(() => visit(node));
|
| - }
|
| -
|
| - ErroneousElement warnAndCreateErroneousElement(Node node,
|
| - String name,
|
| - MessageKind kind,
|
| - [Map arguments = const {}]) {
|
| - compiler.reportWarning(node, kind, arguments);
|
| - return new ErroneousElementX(kind, arguments, name, enclosingElement);
|
| - }
|
| -
|
| - ResolutionResult visitIdentifier(Identifier node) {
|
| - if (node.isThis()) {
|
| - if (!inInstanceContext) {
|
| - error(node, MessageKind.NO_INSTANCE_AVAILABLE, {'name': 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 {
|
| - String name = node.source;
|
| - Element element = lookupInScope(compiler, node, scope, name);
|
| - if (Elements.isUnresolved(element) && name == 'dynamic') {
|
| - // TODO(johnniwinther): Remove this hack when we can return more complex
|
| - // objects than [Element] from this method.
|
| - element = compiler.typeClass;
|
| - // Set the type to be `dynamic` to mark that this is a type literal.
|
| - registry.setType(node, const DynamicType());
|
| - }
|
| - element = reportLookupErrorIfAny(element, node, node.source);
|
| - if (element == null) {
|
| - if (!inInstanceContext) {
|
| - element = warnAndCreateErroneousElement(
|
| - node, node.source, MessageKind.CANNOT_RESOLVE,
|
| - {'name': node});
|
| - registry.registerThrowNoSuchMethod();
|
| - }
|
| - } else if (element.isErroneous) {
|
| - // Use the erroneous element.
|
| - } else {
|
| - if ((element.kind.category & allowedCategory) == 0) {
|
| - // TODO(ahe): Improve error message. Need UX input.
|
| - error(node, MessageKind.GENERIC,
|
| - {'text': "is not an expression $element"});
|
| - }
|
| - }
|
| - if (!Elements.isUnresolved(element) && element.isClass) {
|
| - ClassElement classElement = element;
|
| - classElement.ensureResolved(compiler);
|
| - }
|
| - return new ElementResult(registry.useElement(node, element));
|
| - }
|
| - }
|
| -
|
| - ResolutionResult visitTypeAnnotation(TypeAnnotation node) {
|
| - DartType type = resolveTypeAnnotation(node);
|
| - if (inCheckContext) {
|
| - registry.registerIsCheck(type);
|
| - }
|
| - return new TypeResult(type);
|
| - }
|
| -
|
| - bool isNamedConstructor(Send node) => node.receiver != null;
|
| -
|
| - Selector getRedirectingThisOrSuperConstructorSelector(Send node) {
|
| - if (isNamedConstructor(node)) {
|
| - String constructorName = node.selector.asIdentifier().source;
|
| - return new Selector.callConstructor(
|
| - constructorName,
|
| - enclosingElement.library);
|
| - } else {
|
| - return new Selector.callDefaultConstructor(
|
| - enclosingElement.library);
|
| - }
|
| - }
|
| -
|
| - FunctionElement resolveConstructorRedirection(FunctionElementX 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)) {
|
| - Selector selector =
|
| - getRedirectingThisOrSuperConstructorSelector(initializers.head);
|
| - final ClassElement classElement = constructor.enclosingClass;
|
| - return classElement.lookupConstructor(selector);
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - void setupFunction(FunctionExpression node, FunctionElement function) {
|
| - Element enclosingElement = function.enclosingElement;
|
| - if (node.modifiers.isStatic &&
|
| - enclosingElement.kind != ElementKind.CLASS) {
|
| - compiler.reportError(node, MessageKind.ILLEGAL_STATIC);
|
| - }
|
| -
|
| - scope = new MethodScope(scope, function);
|
| - // Put the parameters in scope.
|
| - FunctionSignature functionParameters = function.functionSignature;
|
| - Link<Node> parameterNodes = (node.parameters == null)
|
| - ? const Link<Node>() : node.parameters.nodes;
|
| - functionParameters.forEachParameter((ParameterElement element) {
|
| - // TODO(karlklose): should be a list of [FormalElement]s, but the actual
|
| - // implementation uses [Element].
|
| - Link<Element> optionals = functionParameters.optionalParameters;
|
| - if (!optionals.isEmpty && element == optionals.head) {
|
| - NodeList nodes = parameterNodes.head;
|
| - parameterNodes = nodes.nodes;
|
| - }
|
| - visit(element.initializer);
|
| - VariableDefinitions variableDefinitions = parameterNodes.head;
|
| - Node parameterNode = variableDefinitions.definitions.nodes.head;
|
| - // Field parameters (this.x) are not visible inside the constructor. The
|
| - // fields they reference are visible, but must be resolved independently.
|
| - if (element.isInitializingFormal) {
|
| - registry.useElement(parameterNode, element);
|
| - } else {
|
| - LocalParameterElement parameterElement = element;
|
| - defineLocalVariable(parameterNode, parameterElement);
|
| - addToScope(parameterElement);
|
| - }
|
| - parameterNodes = parameterNodes.tail;
|
| - });
|
| - addDeferredAction(enclosingElement, () {
|
| - functionParameters.forEachOptionalParameter((Element parameter) {
|
| - compiler.resolver.constantCompiler.compileConstant(parameter);
|
| - });
|
| - });
|
| - if (inCheckContext) {
|
| - functionParameters.forEachParameter((ParameterElement element) {
|
| - registry.registerIsCheck(element.type);
|
| - });
|
| - }
|
| - }
|
| -
|
| - visitCascade(Cascade node) {
|
| - visit(node.expression);
|
| - }
|
| -
|
| - visitCascadeReceiver(CascadeReceiver node) {
|
| - visit(node.expression);
|
| - }
|
| -
|
| - visitClassNode(ClassNode node) {
|
| - internalError(node, "shouldn't be called");
|
| - }
|
| -
|
| - visitIn(Node node, Scope nestedScope) {
|
| - Scope oldScope = scope;
|
| - scope = nestedScope;
|
| - ResolutionResult result = visit(node);
|
| - scope = oldScope;
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Introduces new default targets for break and continue
|
| - * before visiting the body of the loop
|
| - */
|
| - visitLoopBodyIn(Loop loop, Node body, Scope bodyScope) {
|
| - JumpTarget element = getOrDefineTarget(loop);
|
| - statementScope.enterLoop(element);
|
| - visitIn(body, bodyScope);
|
| - statementScope.exitLoop();
|
| - if (!element.isTarget) {
|
| - registry.undefineTarget(loop);
|
| - }
|
| - }
|
| -
|
| - visitBlock(Block node) {
|
| - visitIn(node.statements, new BlockScope(scope));
|
| - }
|
| -
|
| - visitDoWhile(DoWhile node) {
|
| - visitLoopBodyIn(node, node.body, new BlockScope(scope));
|
| - visit(node.condition);
|
| - }
|
| -
|
| - visitEmptyStatement(EmptyStatement node) { }
|
| -
|
| - visitExpressionStatement(ExpressionStatement node) {
|
| - ExpressionStatement oldExpressionStatement = currentExpressionStatement;
|
| - currentExpressionStatement = node;
|
| - visit(node.expression);
|
| - currentExpressionStatement = oldExpressionStatement;
|
| - }
|
| -
|
| - visitFor(For node) {
|
| - Scope blockScope = new BlockScope(scope);
|
| - visitIn(node.initializer, blockScope);
|
| - visitIn(node.condition, blockScope);
|
| - visitIn(node.update, blockScope);
|
| - visitLoopBodyIn(node, node.body, blockScope);
|
| - }
|
| -
|
| - visitFunctionDeclaration(FunctionDeclaration node) {
|
| - assert(node.function.name != null);
|
| - visitFunctionExpression(node.function, inFunctionDeclaration: true);
|
| - }
|
| -
|
| -
|
| - /// Process a local function declaration or an anonymous function expression.
|
| - ///
|
| - /// [inFunctionDeclaration] is `true` when the current node is the immediate
|
| - /// child of a function declaration.
|
| - ///
|
| - /// This is used to distinguish local function declarations from anonymous
|
| - /// function expressions.
|
| - visitFunctionExpression(FunctionExpression node,
|
| - {bool inFunctionDeclaration: false}) {
|
| - bool doAddToScope = inFunctionDeclaration;
|
| - if (!inFunctionDeclaration && node.name != null) {
|
| - compiler.reportError(
|
| - node.name,
|
| - MessageKind.NAMED_FUNCTION_EXPRESSION,
|
| - {'name': node.name});
|
| - }
|
| - visit(node.returnType);
|
| - String name;
|
| - if (node.name == null) {
|
| - name = "";
|
| - } else {
|
| - name = node.name.asIdentifier().source;
|
| - }
|
| - LocalFunctionElementX function = new LocalFunctionElementX(
|
| - name, node, ElementKind.FUNCTION, Modifiers.EMPTY,
|
| - enclosingElement);
|
| - function.functionSignatureCache =
|
| - SignatureResolver.analyze(compiler, node.parameters, node.returnType,
|
| - function, registry, createRealParameters: true);
|
| - ResolverTask.processAsyncMarker(compiler, function);
|
| - checkLocalDefinitionName(node, function);
|
| - registry.defineFunction(node, function);
|
| - if (doAddToScope) {
|
| - addToScope(function);
|
| - }
|
| - Scope oldScope = scope; // The scope is modified by [setupFunction].
|
| - setupFunction(node, function);
|
| -
|
| - Element previousEnclosingElement = enclosingElement;
|
| - enclosingElement = function;
|
| - // Run the body in a fresh statement scope.
|
| - StatementScope oldStatementScope = statementScope;
|
| - statementScope = new StatementScope();
|
| - visit(node.body);
|
| - statementScope = oldStatementScope;
|
| -
|
| - scope = oldScope;
|
| - enclosingElement = previousEnclosingElement;
|
| -
|
| - registry.registerClosure(function);
|
| - registry.registerInstantiatedClass(compiler.functionClass);
|
| - }
|
| -
|
| - visitIf(If node) {
|
| - doInPromotionScope(node.condition.expression, () => visit(node.condition));
|
| - doInPromotionScope(node.thenPart,
|
| - () => visitIn(node.thenPart, new BlockScope(scope)));
|
| - visitIn(node.elsePart, new BlockScope(scope));
|
| - }
|
| -
|
| - ResolutionResult resolveSend(Send node) {
|
| - Selector selector = resolveSelector(node, null);
|
| - if (node.isSuperCall) registry.registerSuperUse(node);
|
| -
|
| - if (node.receiver == null) {
|
| - // If this send is of the form "assert(expr);", then
|
| - // this is an assertion.
|
| - if (selector.isAssert) {
|
| - if (selector.argumentCount != 1) {
|
| - error(node.selector,
|
| - MessageKind.WRONG_NUMBER_OF_ARGUMENTS_FOR_ASSERT,
|
| - {'argumentCount': selector.argumentCount});
|
| - } else if (selector.namedArgumentCount != 0) {
|
| - error(node.selector,
|
| - MessageKind.ASSERT_IS_GIVEN_NAMED_ARGUMENTS,
|
| - {'argumentCount': selector.namedArgumentCount});
|
| - }
|
| - registry.registerAssert(node);
|
| - return const AssertResult();
|
| - }
|
| -
|
| - return node.selector.accept(this);
|
| - }
|
| -
|
| - var oldCategory = allowedCategory;
|
| - allowedCategory |= ElementCategory.PREFIX | ElementCategory.SUPER;
|
| - ResolutionResult resolvedReceiver = visit(node.receiver);
|
| - allowedCategory = oldCategory;
|
| -
|
| - Element target;
|
| - String name = node.selector.asIdentifier().source;
|
| - if (identical(name, 'this')) {
|
| - // TODO(ahe): Why is this using GENERIC?
|
| - error(node.selector, MessageKind.GENERIC,
|
| - {'text': "expected an identifier"});
|
| - } else if (node.isSuperCall) {
|
| - if (node.isOperator) {
|
| - if (isUserDefinableOperator(name)) {
|
| - name = selector.name;
|
| - } else {
|
| - error(node.selector, MessageKind.ILLEGAL_SUPER_SEND, {'name': name});
|
| - }
|
| - }
|
| - if (!inInstanceContext) {
|
| - error(node.receiver, MessageKind.NO_INSTANCE_AVAILABLE, {'name': 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,
|
| - {'text': "Object has no superclass"});
|
| - }
|
| - // TODO(johnniwinther): Ensure correct behavior if currentClass is a
|
| - // patch.
|
| - target = currentClass.lookupSuperSelector(selector);
|
| - // [target] may be null which means invoking noSuchMethod on
|
| - // super.
|
| - if (target == null) {
|
| - target = warnAndCreateErroneousElement(
|
| - node, name, MessageKind.NO_SUCH_SUPER_MEMBER,
|
| - {'className': currentClass, 'memberName': name});
|
| - // We still need to register the invocation, because we might
|
| - // call [:super.noSuchMethod:] which calls
|
| - // [JSInvocationMirror._invokeOn].
|
| - registry.registerDynamicInvocation(selector);
|
| - registry.registerSuperNoSuchMethod();
|
| - }
|
| - } else if (resolvedReceiver == null ||
|
| - Elements.isUnresolved(resolvedReceiver.element)) {
|
| - return null;
|
| - } else if (resolvedReceiver.element.isClass) {
|
| - ClassElement receiverClass = resolvedReceiver.element;
|
| - receiverClass.ensureResolved(compiler);
|
| - if (node.isOperator) {
|
| - // When the resolved receiver is a class, we can have two cases:
|
| - // 1) a static send: C.foo, or
|
| - // 2) an operator send, where the receiver is a class literal: 'C + 1'.
|
| - // The following code that looks up the selector on the resolved
|
| - // receiver will treat the second as the invocation of a static operator
|
| - // if the resolved receiver is not null.
|
| - return null;
|
| - }
|
| - MembersCreator.computeClassMembersByName(
|
| - compiler, receiverClass.declaration, name);
|
| - target = receiverClass.lookupLocalMember(name);
|
| - if (target == null || target.isInstanceMember) {
|
| - registry.registerThrowNoSuchMethod();
|
| - // TODO(johnniwinther): With the simplified [TreeElements] invariant,
|
| - // try to resolve injected elements if [currentClass] is in the patch
|
| - // library of [receiverClass].
|
| -
|
| - // TODO(karlklose): this should be reported by the caller of
|
| - // [resolveSend] to select better warning messages for getters and
|
| - // setters.
|
| - MessageKind kind = (target == null)
|
| - ? MessageKind.MEMBER_NOT_FOUND
|
| - : MessageKind.MEMBER_NOT_STATIC;
|
| - return new ElementResult(warnAndCreateErroneousElement(
|
| - node, name, kind,
|
| - {'className': receiverClass.name, 'memberName': name}));
|
| - } else if (isPrivateName(name) &&
|
| - target.library != enclosingElement.library) {
|
| - registry.registerThrowNoSuchMethod();
|
| - return new ElementResult(warnAndCreateErroneousElement(
|
| - node, name, MessageKind.PRIVATE_ACCESS,
|
| - {'libraryName': target.library.getLibraryOrScriptName(),
|
| - 'name': name}));
|
| - }
|
| - } else if (resolvedReceiver.element.isPrefix) {
|
| - PrefixElement prefix = resolvedReceiver.element;
|
| - target = prefix.lookupLocalMember(name);
|
| - if (Elements.isUnresolved(target)) {
|
| - registry.registerThrowNoSuchMethod();
|
| - return new ElementResult(warnAndCreateErroneousElement(
|
| - node, name, MessageKind.NO_SUCH_LIBRARY_MEMBER,
|
| - {'libraryName': prefix.name, 'memberName': name}));
|
| - } else if (target.isAmbiguous) {
|
| - registry.registerThrowNoSuchMethod();
|
| - AmbiguousElement ambiguous = target;
|
| - target = warnAndCreateErroneousElement(node, name,
|
| - ambiguous.messageKind,
|
| - ambiguous.messageArguments);
|
| - ambiguous.diagnose(enclosingElement, compiler);
|
| - return new ElementResult(target);
|
| - } else if (target.kind == ElementKind.CLASS) {
|
| - ClassElement classElement = target;
|
| - classElement.ensureResolved(compiler);
|
| - }
|
| - }
|
| - return new ElementResult(target);
|
| - }
|
| -
|
| - static Selector computeSendSelector(Send node,
|
| - LibraryElement library,
|
| - Element element) {
|
| - // First determine if this is part of an assignment.
|
| - bool isSet = node.asSendSet() != null;
|
| -
|
| - if (node.isIndex) {
|
| - return isSet ? new Selector.indexSet() : new Selector.index();
|
| - }
|
| -
|
| - if (node.isOperator) {
|
| - String source = node.selector.asOperator().source;
|
| - String string = source;
|
| - if (identical(string, '!') ||
|
| - identical(string, '&&') || identical(string, '||') ||
|
| - identical(string, 'is') || identical(string, 'as') ||
|
| - identical(string, '?') ||
|
| - identical(string, '>>>')) {
|
| - return null;
|
| - }
|
| - String op = source;
|
| - if (!isUserDefinableOperator(source)) {
|
| - op = Elements.mapToUserOperatorOrNull(source);
|
| - }
|
| - if (op == null) {
|
| - // Unsupported operator. An error has been reported during parsing.
|
| - return new Selector.call(
|
| - source, library, node.argumentsNode.slowLength(), []);
|
| - }
|
| - return node.arguments.isEmpty
|
| - ? new Selector.unaryOperator(op)
|
| - : new Selector.binaryOperator(op);
|
| - }
|
| -
|
| - Identifier identifier = node.selector.asIdentifier();
|
| - if (node.isPropertyAccess) {
|
| - assert(!isSet);
|
| - return new Selector.getter(identifier.source, library);
|
| - } else if (isSet) {
|
| - return new Selector.setter(identifier.source, library);
|
| - }
|
| -
|
| - // Compute the arity and the list of named arguments.
|
| - int arity = 0;
|
| - List<String> named = <String>[];
|
| - for (Link<Node> link = node.argumentsNode.nodes;
|
| - !link.isEmpty;
|
| - link = link.tail) {
|
| - Expression argument = link.head;
|
| - NamedArgument namedArgument = argument.asNamedArgument();
|
| - if (namedArgument != null) {
|
| - named.add(namedArgument.name.source);
|
| - }
|
| - arity++;
|
| - }
|
| -
|
| - if (element != null && element.isConstructor) {
|
| - return new Selector.callConstructor(
|
| - element.name, library, arity, named);
|
| - }
|
| -
|
| - // If we're invoking a closure, we do not have an identifier.
|
| - return (identifier == null)
|
| - ? new Selector.callClosure(arity, named)
|
| - : new Selector.call(identifier.source, library, arity, named);
|
| - }
|
| -
|
| - Selector resolveSelector(Send node, Element element) {
|
| - LibraryElement library = enclosingElement.library;
|
| - Selector selector = computeSendSelector(node, library, element);
|
| - if (selector != null) registry.setSelector(node, selector);
|
| - return selector;
|
| - }
|
| -
|
| - void resolveArguments(NodeList list) {
|
| - if (list == null) return;
|
| - bool oldSendIsMemberAccess = sendIsMemberAccess;
|
| - sendIsMemberAccess = false;
|
| - Map<String, Node> seenNamedArguments = new Map<String, Node>();
|
| - for (Link<Node> link = list.nodes; !link.isEmpty; link = link.tail) {
|
| - Expression argument = link.head;
|
| - visit(argument);
|
| - NamedArgument namedArgument = argument.asNamedArgument();
|
| - if (namedArgument != null) {
|
| - String source = namedArgument.name.source;
|
| - if (seenNamedArguments.containsKey(source)) {
|
| - reportDuplicateDefinition(
|
| - source,
|
| - argument,
|
| - seenNamedArguments[source]);
|
| - } else {
|
| - seenNamedArguments[source] = namedArgument;
|
| - }
|
| - } else if (!seenNamedArguments.isEmpty) {
|
| - error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED);
|
| - }
|
| - }
|
| - sendIsMemberAccess = oldSendIsMemberAccess;
|
| - }
|
| -
|
| - ResolutionResult visitSend(Send node) {
|
| - bool oldSendIsMemberAccess = sendIsMemberAccess;
|
| - sendIsMemberAccess = node.isPropertyAccess || node.isCall;
|
| - ResolutionResult result;
|
| - if (node.isLogicalAnd) {
|
| - result = doInPromotionScope(node.receiver, () => resolveSend(node));
|
| - } else {
|
| - result = resolveSend(node);
|
| - }
|
| - sendIsMemberAccess = oldSendIsMemberAccess;
|
| -
|
| - Element target = result != null ? result.element : null;
|
| -
|
| - if (target != null
|
| - && target == compiler.mirrorSystemGetNameFunction
|
| - && !compiler.mirrorUsageAnalyzerTask.hasMirrorUsage(enclosingElement)) {
|
| - compiler.reportHint(
|
| - node.selector, MessageKind.STATIC_FUNCTION_BLOAT,
|
| - {'class': compiler.mirrorSystemClass.name,
|
| - 'name': compiler.mirrorSystemGetNameFunction.name});
|
| - }
|
| -
|
| - if (!Elements.isUnresolved(target)) {
|
| - if (target.isAbstractField) {
|
| - AbstractFieldElement field = target;
|
| - target = field.getter;
|
| - if (target == null && !inInstanceContext) {
|
| - registry.registerThrowNoSuchMethod();
|
| - target =
|
| - warnAndCreateErroneousElement(node.selector, field.name,
|
| - MessageKind.CANNOT_RESOLVE_GETTER);
|
| - }
|
| - } else if (target.isTypeVariable) {
|
| - ClassElement cls = target.enclosingClass;
|
| - assert(enclosingElement.enclosingClass == cls);
|
| - registry.registerClassUsingVariableExpression(cls);
|
| - registry.registerTypeVariableExpression();
|
| - // Set the type of the node to [Type] to mark this send as a
|
| - // type variable expression.
|
| - registry.registerTypeLiteral(node, target.computeType(compiler));
|
| - } else if (target.impliesType && (!sendIsMemberAccess || node.isCall)) {
|
| - // Set the type of the node to [Type] to mark this send as a
|
| - // type literal.
|
| - DartType type;
|
| -
|
| - // TODO(johnniwinther): Remove this hack when we can pass more complex
|
| - // information between methods than resolved elements.
|
| - if (target == compiler.typeClass && node.receiver == null) {
|
| - // Potentially a 'dynamic' type literal.
|
| - type = registry.getType(node.selector);
|
| - }
|
| - if (type == null) {
|
| - type = target.computeType(compiler);
|
| - }
|
| - registry.registerTypeLiteral(node, type);
|
| -
|
| - // Don't try to make constants of calls to type literals.
|
| - if (!node.isCall) {
|
| - analyzeConstant(node);
|
| - } else {
|
| - // The node itself is not a constant but we register the selector (the
|
| - // identifier that refers to the class/typedef) as a constant.
|
| - analyzeConstant(node.selector);
|
| - }
|
| - }
|
| - if (isPotentiallyMutableTarget(target)) {
|
| - if (enclosingElement != target.enclosingElement) {
|
| - for (Node scope in promotionScope) {
|
| - registry.setAccessedByClosureIn(scope, target, node);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - bool resolvedArguments = false;
|
| - if (node.isOperator) {
|
| - String operatorString = node.selector.asOperator().source;
|
| - if (identical(operatorString, 'is')) {
|
| - // TODO(johnniwinther): Use seen type tests to avoid registration of
|
| - // mutation/access to unpromoted variables.
|
| - DartType type =
|
| - resolveTypeAnnotation(node.typeAnnotationFromIsCheckOrCast);
|
| - if (type != null) {
|
| - registry.registerIsCheck(type);
|
| - }
|
| - resolvedArguments = true;
|
| - } else if (identical(operatorString, 'as')) {
|
| - DartType type = resolveTypeAnnotation(node.arguments.head);
|
| - if (type != null) {
|
| - registry.registerAsCheck(type);
|
| - }
|
| - resolvedArguments = true;
|
| - } else if (identical(operatorString, '&&')) {
|
| - doInPromotionScope(node.arguments.head,
|
| - () => resolveArguments(node.argumentsNode));
|
| - resolvedArguments = true;
|
| - }
|
| - }
|
| -
|
| - if (!resolvedArguments) {
|
| - resolveArguments(node.argumentsNode);
|
| - }
|
| -
|
| - // If the selector is null, it means that we will not be generating
|
| - // code for this as a send.
|
| - Selector selector = registry.getSelector(node);
|
| - if (selector == null) return null;
|
| -
|
| - if (node.isCall) {
|
| - if (Elements.isUnresolved(target) ||
|
| - target.isGetter ||
|
| - target.isField ||
|
| - Elements.isClosureSend(node, target)) {
|
| - // If we don't know what we're calling or if we are calling a getter,
|
| - // we need to register that fact that we may be calling a closure
|
| - // with the same arguments.
|
| - Selector call = new Selector.callClosureFrom(selector);
|
| - registry.registerDynamicInvocation(call);
|
| - } else if (target.impliesType) {
|
| - // We call 'call()' on a Type instance returned from the reference to a
|
| - // class or typedef literal. We do not need to register this call as a
|
| - // dynamic invocation, because we statically know what the target is.
|
| - } else {
|
| - if (target is FunctionElement) {
|
| - FunctionElement function = target;
|
| - function.computeSignature(compiler);
|
| - }
|
| - if (!selector.applies(target, compiler.world)) {
|
| - registry.registerThrowNoSuchMethod();
|
| - if (node.isSuperCall) {
|
| - // Similar to what we do when we can't find super via selector
|
| - // in [resolveSend] above, we still need to register the invocation,
|
| - // because we might call [:super.noSuchMethod:] which calls
|
| - // [JSInvocationMirror._invokeOn].
|
| - registry.registerDynamicInvocation(selector);
|
| - registry.registerSuperNoSuchMethod();
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (target != null && target.isForeign(compiler.backend)) {
|
| - if (selector.name == 'JS') {
|
| - registry.registerJsCall(node, this);
|
| - } else if (selector.name == 'JS_EMBEDDED_GLOBAL') {
|
| - registry.registerJsEmbeddedGlobalCall(node, this);
|
| - } else if (selector.name == 'JS_INTERCEPTOR_CONSTANT') {
|
| - if (!node.argumentsNode.isEmpty) {
|
| - Node argument = node.argumentsNode.nodes.head;
|
| - if (argumentsToJsInterceptorConstant == null) {
|
| - argumentsToJsInterceptorConstant = new Set<Node>();
|
| - }
|
| - argumentsToJsInterceptorConstant.add(argument);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - registry.useElement(node, target);
|
| - registerSend(selector, target);
|
| - if (node.isPropertyAccess && Elements.isStaticOrTopLevelFunction(target)) {
|
| - registry.registerGetOfStaticFunction(target.declaration);
|
| - }
|
| - return node.isPropertyAccess ? new ElementResult(target) : null;
|
| - }
|
| -
|
| - /// Callback for native enqueuer to parse a type. Returns [:null:] on error.
|
| - DartType resolveTypeFromString(Node node, String typeName) {
|
| - Element element = lookupInScope(compiler, node,
|
| - scope, typeName);
|
| - if (element == null) return null;
|
| - if (element is! ClassElement) return null;
|
| - ClassElement cls = element;
|
| - cls.ensureResolved(compiler);
|
| - return cls.computeType(compiler);
|
| - }
|
| -
|
| - ResolutionResult visitSendSet(SendSet node) {
|
| - bool oldSendIsMemberAccess = sendIsMemberAccess;
|
| - sendIsMemberAccess = node.isPropertyAccess || node.isCall;
|
| - ResolutionResult result = resolveSend(node);
|
| - sendIsMemberAccess = oldSendIsMemberAccess;
|
| - Element target = result != null ? result.element : null;
|
| - Element setter = target;
|
| - Element getter = target;
|
| - String operatorName = node.assignmentOperator.source;
|
| - String source = operatorName;
|
| - bool isComplex = !identical(source, '=');
|
| - if (!(result is AssertResult || Elements.isUnresolved(target))) {
|
| - if (target.isAbstractField) {
|
| - AbstractFieldElement field = target;
|
| - setter = field.setter;
|
| - getter = field.getter;
|
| - if (setter == null && !inInstanceContext) {
|
| - setter = warnAndCreateErroneousElement(
|
| - node.selector, field.name, MessageKind.CANNOT_RESOLVE_SETTER);
|
| - registry.registerThrowNoSuchMethod();
|
| - }
|
| - if (isComplex && getter == null && !inInstanceContext) {
|
| - getter = warnAndCreateErroneousElement(
|
| - node.selector, field.name, MessageKind.CANNOT_RESOLVE_GETTER);
|
| - registry.registerThrowNoSuchMethod();
|
| - }
|
| - } else if (target.impliesType) {
|
| - setter = warnAndCreateErroneousElement(
|
| - node.selector, target.name, MessageKind.ASSIGNING_TYPE);
|
| - registry.registerThrowNoSuchMethod();
|
| - } else if (target.isFinal ||
|
| - target.isConst ||
|
| - (target.isFunction &&
|
| - Elements.isStaticOrTopLevelFunction(target) &&
|
| - !target.isSetter)) {
|
| - if (target.isFunction) {
|
| - setter = warnAndCreateErroneousElement(
|
| - node.selector, target.name, MessageKind.ASSIGNING_METHOD);
|
| - } else {
|
| - setter = warnAndCreateErroneousElement(
|
| - node.selector, target.name, MessageKind.CANNOT_RESOLVE_SETTER);
|
| - }
|
| - registry.registerThrowNoSuchMethod();
|
| - }
|
| - if (isPotentiallyMutableTarget(target)) {
|
| - registry.registerPotentialMutation(target, node);
|
| - if (enclosingElement != target.enclosingElement) {
|
| - registry.registerPotentialMutationInClosure(target, node);
|
| - }
|
| - for (Node scope in promotionScope) {
|
| - registry.registerPotentialMutationIn(scope, target, node);
|
| - }
|
| - }
|
| - }
|
| -
|
| - resolveArguments(node.argumentsNode);
|
| -
|
| - Selector selector = registry.getSelector(node);
|
| - if (isComplex) {
|
| - Selector getterSelector;
|
| - if (selector.isSetter) {
|
| - getterSelector = new Selector.getterFrom(selector);
|
| - } else {
|
| - assert(selector.isIndexSet);
|
| - getterSelector = new Selector.index();
|
| - }
|
| - registerSend(getterSelector, getter);
|
| - registry.setGetterSelectorInComplexSendSet(node, getterSelector);
|
| - if (node.isSuperCall) {
|
| - getter = currentClass.lookupSuperSelector(getterSelector);
|
| - if (getter == null) {
|
| - target = warnAndCreateErroneousElement(
|
| - node, selector.name, MessageKind.NO_SUCH_SUPER_MEMBER,
|
| - {'className': currentClass, 'memberName': selector.name});
|
| - registry.registerSuperNoSuchMethod();
|
| - }
|
| - }
|
| - registry.useElement(node.selector, getter);
|
| -
|
| - // Make sure we include the + and - operators if we are using
|
| - // the ++ and -- ones. Also, if op= form is used, include op itself.
|
| - void registerBinaryOperator(String name) {
|
| - Selector binop = new Selector.binaryOperator(name);
|
| - registry.registerDynamicInvocation(binop);
|
| - registry.setOperatorSelectorInComplexSendSet(node, binop);
|
| - }
|
| - if (identical(source, '++')) {
|
| - registerBinaryOperator('+');
|
| - registry.registerInstantiatedClass(compiler.intClass);
|
| - } else if (identical(source, '--')) {
|
| - registerBinaryOperator('-');
|
| - registry.registerInstantiatedClass(compiler.intClass);
|
| - } else if (source.endsWith('=')) {
|
| - registerBinaryOperator(Elements.mapToUserOperator(operatorName));
|
| - }
|
| - }
|
| -
|
| - registerSend(selector, setter);
|
| - return new ElementResult(registry.useElement(node, setter));
|
| - }
|
| -
|
| - void registerSend(Selector selector, Element target) {
|
| - if (target == null || target.isInstanceMember) {
|
| - if (selector.isGetter) {
|
| - registry.registerDynamicGetter(selector);
|
| - } else if (selector.isSetter) {
|
| - registry.registerDynamicSetter(selector);
|
| - } else {
|
| - registry.registerDynamicInvocation(selector);
|
| - }
|
| - } else if (Elements.isStaticOrTopLevel(target)) {
|
| - // Avoid registration of type variables since they are not analyzable but
|
| - // instead resolved through their enclosing type declaration.
|
| - if (!target.isTypeVariable) {
|
| - // [target] might be the implementation element and only declaration
|
| - // elements may be registered.
|
| - registry.registerStaticUse(target.declaration);
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitLiteralInt(LiteralInt node) {
|
| - registry.registerInstantiatedClass(compiler.intClass);
|
| - }
|
| -
|
| - visitLiteralDouble(LiteralDouble node) {
|
| - registry.registerInstantiatedClass(compiler.doubleClass);
|
| - }
|
| -
|
| - visitLiteralBool(LiteralBool node) {
|
| - registry.registerInstantiatedClass(compiler.boolClass);
|
| - }
|
| -
|
| - visitLiteralString(LiteralString node) {
|
| - registry.registerInstantiatedClass(compiler.stringClass);
|
| - }
|
| -
|
| - visitLiteralNull(LiteralNull node) {
|
| - registry.registerInstantiatedClass(compiler.nullClass);
|
| - }
|
| -
|
| - visitLiteralSymbol(LiteralSymbol node) {
|
| - registry.registerInstantiatedClass(compiler.symbolClass);
|
| - registry.registerStaticUse(compiler.symbolConstructor.declaration);
|
| - registry.registerConstSymbol(node.slowNameString);
|
| - if (!validateSymbol(node, node.slowNameString, reportError: false)) {
|
| - compiler.reportError(node,
|
| - MessageKind.UNSUPPORTED_LITERAL_SYMBOL,
|
| - {'value': node.slowNameString});
|
| - }
|
| - analyzeConstant(node);
|
| - }
|
| -
|
| - visitStringJuxtaposition(StringJuxtaposition node) {
|
| - registry.registerInstantiatedClass(compiler.stringClass);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - visitNodeList(NodeList node) {
|
| - for (Link<Node> link = node.nodes; !link.isEmpty; link = link.tail) {
|
| - visit(link.head);
|
| - }
|
| - }
|
| -
|
| - visitOperator(Operator node) {
|
| - internalError(node, 'operator');
|
| - }
|
| -
|
| - visitRethrow(Rethrow node) {
|
| - if (!inCatchBlock) {
|
| - error(node, MessageKind.THROW_WITHOUT_EXPRESSION);
|
| - }
|
| - }
|
| -
|
| - visitReturn(Return node) {
|
| - Node expression = node.expression;
|
| - if (expression != null &&
|
| - enclosingElement.isGenerativeConstructor) {
|
| - // It is a compile-time error if a return statement of the form
|
| - // `return e;` appears in a generative constructor. (Dart Language
|
| - // Specification 13.12.)
|
| - compiler.reportError(expression,
|
| - MessageKind.CANNOT_RETURN_FROM_CONSTRUCTOR);
|
| - }
|
| - visit(node.expression);
|
| - }
|
| -
|
| - visitYield(Yield node) {
|
| - compiler.streamClass.ensureResolved(compiler);
|
| - compiler.iterableClass.ensureResolved(compiler);
|
| - visit(node.expression);
|
| - }
|
| -
|
| - visitRedirectingFactoryBody(RedirectingFactoryBody node) {
|
| - final isSymbolConstructor = enclosingElement == compiler.symbolConstructor;
|
| - if (!enclosingElement.isFactoryConstructor) {
|
| - compiler.reportError(
|
| - node, MessageKind.FACTORY_REDIRECTION_IN_NON_FACTORY);
|
| - compiler.reportHint(
|
| - enclosingElement, MessageKind.MISSING_FACTORY_KEYWORD);
|
| - }
|
| - ConstructorElementX constructor = enclosingElement;
|
| - bool isConstConstructor = constructor.isConst;
|
| - ConstructorElement redirectionTarget = resolveRedirectingFactory(
|
| - node, inConstContext: isConstConstructor);
|
| - constructor.immediateRedirectionTarget = redirectionTarget;
|
| - registry.setRedirectingTargetConstructor(node, redirectionTarget);
|
| - if (Elements.isUnresolved(redirectionTarget)) {
|
| - registry.registerThrowNoSuchMethod();
|
| - return;
|
| - } else {
|
| - if (isConstConstructor &&
|
| - !redirectionTarget.isConst) {
|
| - compiler.reportError(node, MessageKind.CONSTRUCTOR_IS_NOT_CONST);
|
| - }
|
| - if (redirectionTarget == constructor) {
|
| - compiler.reportError(node, MessageKind.CYCLIC_REDIRECTING_FACTORY);
|
| - return;
|
| - }
|
| - }
|
| -
|
| - // Check that the target constructor is type compatible with the
|
| - // redirecting constructor.
|
| - ClassElement targetClass = redirectionTarget.enclosingClass;
|
| - InterfaceType type = registry.getType(node);
|
| - FunctionType targetType = redirectionTarget.computeType(compiler)
|
| - .subst(type.typeArguments, targetClass.typeVariables);
|
| - FunctionType constructorType = constructor.computeType(compiler);
|
| - bool isSubtype = compiler.types.isSubtype(targetType, constructorType);
|
| - if (!isSubtype) {
|
| - warning(node, MessageKind.NOT_ASSIGNABLE,
|
| - {'fromType': targetType, 'toType': constructorType});
|
| - }
|
| -
|
| - FunctionSignature targetSignature =
|
| - redirectionTarget.computeSignature(compiler);
|
| - FunctionSignature constructorSignature =
|
| - constructor.computeSignature(compiler);
|
| - if (!targetSignature.isCompatibleWith(constructorSignature)) {
|
| - assert(!isSubtype);
|
| - registry.registerThrowNoSuchMethod();
|
| - }
|
| -
|
| - // Register a post process to check for cycles in the redirection chain and
|
| - // set the actual generative constructor at the end of the chain.
|
| - addDeferredAction(constructor, () {
|
| - compiler.resolver.resolveRedirectionChain(constructor, node);
|
| - });
|
| -
|
| - registry.registerStaticUse(redirectionTarget);
|
| - // TODO(johnniwinther): Register the effective target type instead.
|
| - registry.registerInstantiatedClass(
|
| - redirectionTarget.enclosingClass.declaration);
|
| - if (isSymbolConstructor) {
|
| - registry.registerSymbolConstructor();
|
| - }
|
| - }
|
| -
|
| - visitThrow(Throw node) {
|
| - registry.registerThrowExpression();
|
| - visit(node.expression);
|
| - }
|
| -
|
| - visitAwait(Await node) {
|
| - compiler.futureClass.ensureResolved(compiler);
|
| - visit(node.expression);
|
| - }
|
| -
|
| - visitVariableDefinitions(VariableDefinitions node) {
|
| - DartType type;
|
| - if (node.type != null) {
|
| - type = resolveTypeAnnotation(node.type);
|
| - } else {
|
| - type = const DynamicType();
|
| - }
|
| - VariableList variables = new VariableList.node(node, type);
|
| - VariableDefinitionsVisitor visitor =
|
| - new VariableDefinitionsVisitor(compiler, node, this, variables);
|
| -
|
| - Modifiers modifiers = node.modifiers;
|
| - void reportExtraModifier(String modifier) {
|
| - Node modifierNode;
|
| - for (Link<Node> nodes = modifiers.nodes.nodes;
|
| - !nodes.isEmpty;
|
| - nodes = nodes.tail) {
|
| - if (modifier == nodes.head.asIdentifier().source) {
|
| - modifierNode = nodes.head;
|
| - break;
|
| - }
|
| - }
|
| - assert(modifierNode != null);
|
| - compiler.reportError(modifierNode, MessageKind.EXTRANEOUS_MODIFIER,
|
| - {'modifier': modifier});
|
| - }
|
| - if (modifiers.isFinal && (modifiers.isConst || modifiers.isVar)) {
|
| - reportExtraModifier('final');
|
| - }
|
| - if (modifiers.isVar && (modifiers.isConst || node.type != null)) {
|
| - reportExtraModifier('var');
|
| - }
|
| - if (enclosingElement.isFunction) {
|
| - if (modifiers.isAbstract) {
|
| - reportExtraModifier('abstract');
|
| - }
|
| - if (modifiers.isStatic) {
|
| - reportExtraModifier('static');
|
| - }
|
| - }
|
| - if (node.metadata != null) {
|
| - variables.metadata =
|
| - compiler.resolver.resolveMetadata(enclosingElement, node);
|
| - }
|
| - visitor.visit(node.definitions);
|
| - }
|
| -
|
| - visitWhile(While node) {
|
| - visit(node.condition);
|
| - visitLoopBodyIn(node, node.body, new BlockScope(scope));
|
| - }
|
| -
|
| - visitParenthesizedExpression(ParenthesizedExpression node) {
|
| - bool oldSendIsMemberAccess = sendIsMemberAccess;
|
| - sendIsMemberAccess = false;
|
| - visit(node.expression);
|
| - sendIsMemberAccess = oldSendIsMemberAccess;
|
| - }
|
| -
|
| - ResolutionResult visitNewExpression(NewExpression node) {
|
| - Node selector = node.send.selector;
|
| - FunctionElement constructor = resolveConstructor(node);
|
| - final bool isSymbolConstructor = constructor == compiler.symbolConstructor;
|
| - final bool isMirrorsUsedConstant =
|
| - node.isConst && (constructor == compiler.mirrorsUsedConstructor);
|
| - Selector callSelector = resolveSelector(node.send, constructor);
|
| - resolveArguments(node.send.argumentsNode);
|
| - registry.useElement(node.send, constructor);
|
| - if (Elements.isUnresolved(constructor)) {
|
| - return new ElementResult(constructor);
|
| - }
|
| - constructor.computeSignature(compiler);
|
| - if (!callSelector.applies(constructor, compiler.world)) {
|
| - registry.registerThrowNoSuchMethod();
|
| - }
|
| -
|
| - // [constructor] might be the implementation element
|
| - // and only declaration elements may be registered.
|
| - registry.registerStaticUse(constructor.declaration);
|
| - ClassElement cls = constructor.enclosingClass;
|
| - InterfaceType type = registry.getType(node);
|
| - if (node.isConst && type.containsTypeVariables) {
|
| - compiler.reportError(node.send.selector,
|
| - MessageKind.TYPE_VARIABLE_IN_CONSTANT);
|
| - }
|
| - // TODO(johniwinther): Avoid registration of `type` in face of redirecting
|
| - // factory constructors.
|
| - registry.registerInstantiatedType(type);
|
| - if (constructor.isFactoryConstructor && !type.typeArguments.isEmpty) {
|
| - registry.registerFactoryWithTypeArguments();
|
| - }
|
| - if (constructor.isGenerativeConstructor && cls.isAbstract) {
|
| - warning(node, MessageKind.ABSTRACT_CLASS_INSTANTIATION);
|
| - registry.registerAbstractClassInstantiation();
|
| - }
|
| -
|
| - if (isSymbolConstructor) {
|
| - if (node.isConst) {
|
| - Node argumentNode = node.send.arguments.head;
|
| - ConstantExpression constant =
|
| - compiler.resolver.constantCompiler.compileNode(
|
| - argumentNode, registry.mapping);
|
| - ConstantValue name = constant.value;
|
| - if (!name.isString) {
|
| - DartType type = name.computeType(compiler);
|
| - compiler.reportError(argumentNode, MessageKind.STRING_EXPECTED,
|
| - {'type': type});
|
| - } else {
|
| - StringConstantValue stringConstant = name;
|
| - String nameString = stringConstant.toDartString().slowToString();
|
| - if (validateSymbol(argumentNode, nameString)) {
|
| - registry.registerConstSymbol(nameString);
|
| - }
|
| - }
|
| - } else {
|
| - if (!compiler.mirrorUsageAnalyzerTask.hasMirrorUsage(
|
| - enclosingElement)) {
|
| - compiler.reportHint(
|
| - node.newToken, MessageKind.NON_CONST_BLOAT,
|
| - {'name': compiler.symbolClass.name});
|
| - }
|
| - registry.registerNewSymbol();
|
| - }
|
| - } else if (isMirrorsUsedConstant) {
|
| - compiler.mirrorUsageAnalyzerTask.validate(node, registry.mapping);
|
| - }
|
| - if (node.isConst) {
|
| - analyzeConstant(node);
|
| - }
|
| -
|
| - return null;
|
| - }
|
| -
|
| - void checkConstMapKeysDontOverrideEquals(Spannable spannable,
|
| - MapConstantValue map) {
|
| - for (ConstantValue key in map.keys) {
|
| - if (!key.isObject) continue;
|
| - ObjectConstantValue objectConstant = key;
|
| - DartType keyType = objectConstant.type;
|
| - ClassElement cls = keyType.element;
|
| - if (cls == compiler.stringClass) continue;
|
| - Element equals = cls.lookupMember('==');
|
| - if (equals.enclosingClass != compiler.objectClass) {
|
| - compiler.reportError(spannable,
|
| - MessageKind.CONST_MAP_KEY_OVERRIDES_EQUALS,
|
| - {'type': keyType});
|
| - }
|
| - }
|
| - }
|
| -
|
| - void analyzeConstant(Node node) {
|
| - addDeferredAction(enclosingElement, () {
|
| - ConstantExpression constant =
|
| - compiler.resolver.constantCompiler.compileNode(
|
| - node, registry.mapping);
|
| -
|
| - ConstantValue value = constant.value;
|
| - if (value.isMap) {
|
| - checkConstMapKeysDontOverrideEquals(node, value);
|
| - }
|
| -
|
| - // The type constant that is an argument to JS_INTERCEPTOR_CONSTANT names
|
| - // a class that will be instantiated outside the program by attaching a
|
| - // native class dispatch record referencing the interceptor.
|
| - if (argumentsToJsInterceptorConstant != null &&
|
| - argumentsToJsInterceptorConstant.contains(node)) {
|
| - if (value.isType) {
|
| - TypeConstantValue typeConstant = value;
|
| - if (typeConstant.representedType is InterfaceType) {
|
| - registry.registerInstantiatedType(typeConstant.representedType);
|
| - } else {
|
| - compiler.reportError(node,
|
| - MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT);
|
| - }
|
| - } else {
|
| - compiler.reportError(node,
|
| - MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT);
|
| - }
|
| - }
|
| - });
|
| - }
|
| -
|
| - bool validateSymbol(Node node, String name, {bool reportError: true}) {
|
| - if (name.isEmpty) return true;
|
| - if (name.startsWith('_')) {
|
| - if (reportError) {
|
| - compiler.reportError(node, MessageKind.PRIVATE_IDENTIFIER,
|
| - {'value': name});
|
| - }
|
| - return false;
|
| - }
|
| - if (!symbolValidationPattern.hasMatch(name)) {
|
| - if (reportError) {
|
| - compiler.reportError(node, MessageKind.INVALID_SYMBOL,
|
| - {'value': name});
|
| - }
|
| - return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - /**
|
| - * Try to resolve the constructor that is referred to by [node].
|
| - * Note: this function may return an ErroneousFunctionElement instead of
|
| - * [:null:], if there is no corresponding constructor, class or library.
|
| - */
|
| - ConstructorElement resolveConstructor(NewExpression node) {
|
| - return node.accept(new ConstructorResolver(compiler, this));
|
| - }
|
| -
|
| - ConstructorElement resolveRedirectingFactory(RedirectingFactoryBody node,
|
| - {bool inConstContext: false}) {
|
| - return node.accept(new ConstructorResolver(compiler, this,
|
| - inConstContext: inConstContext));
|
| - }
|
| -
|
| - DartType resolveTypeAnnotation(TypeAnnotation node,
|
| - {bool malformedIsError: false,
|
| - bool deferredIsMalformed: true}) {
|
| - DartType type = typeResolver.resolveTypeAnnotation(
|
| - this, node, malformedIsError: malformedIsError,
|
| - deferredIsMalformed: deferredIsMalformed);
|
| - if (inCheckContext) {
|
| - registry.registerIsCheck(type);
|
| - registry.registerRequiredType(type, enclosingElement);
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - visitModifiers(Modifiers node) {
|
| - internalError(node, 'modifiers');
|
| - }
|
| -
|
| - visitLiteralList(LiteralList node) {
|
| - bool oldSendIsMemberAccess = sendIsMemberAccess;
|
| - sendIsMemberAccess = false;
|
| -
|
| - NodeList arguments = node.typeArguments;
|
| - DartType typeArgument;
|
| - if (arguments != null) {
|
| - Link<Node> nodes = arguments.nodes;
|
| - if (nodes.isEmpty) {
|
| - // The syntax [: <>[] :] is not allowed.
|
| - error(arguments, MessageKind.MISSING_TYPE_ARGUMENT);
|
| - } else {
|
| - typeArgument = resolveTypeAnnotation(nodes.head);
|
| - for (nodes = nodes.tail; !nodes.isEmpty; nodes = nodes.tail) {
|
| - warning(nodes.head, MessageKind.ADDITIONAL_TYPE_ARGUMENT);
|
| - resolveTypeAnnotation(nodes.head);
|
| - }
|
| - }
|
| - }
|
| - DartType listType;
|
| - if (typeArgument != null) {
|
| - if (node.isConst && typeArgument.containsTypeVariables) {
|
| - compiler.reportError(arguments.nodes.head,
|
| - MessageKind.TYPE_VARIABLE_IN_CONSTANT);
|
| - }
|
| - listType = new InterfaceType(compiler.listClass, [typeArgument]);
|
| - } else {
|
| - compiler.listClass.computeType(compiler);
|
| - listType = compiler.listClass.rawType;
|
| - }
|
| - registry.setType(node, listType);
|
| - registry.registerInstantiatedType(listType);
|
| - registry.registerRequiredType(listType, enclosingElement);
|
| - visit(node.elements);
|
| - if (node.isConst) {
|
| - analyzeConstant(node);
|
| - }
|
| -
|
| - sendIsMemberAccess = false;
|
| - }
|
| -
|
| - visitConditional(Conditional node) {
|
| - doInPromotionScope(node.condition, () => visit(node.condition));
|
| - doInPromotionScope(node.thenExpression, () => visit(node.thenExpression));
|
| - visit(node.elseExpression);
|
| - }
|
| -
|
| - visitStringInterpolation(StringInterpolation node) {
|
| - registry.registerInstantiatedClass(compiler.stringClass);
|
| - registry.registerStringInterpolation();
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - visitStringInterpolationPart(StringInterpolationPart node) {
|
| - registerImplicitInvocation('toString', 0);
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - visitBreakStatement(BreakStatement node) {
|
| - JumpTarget 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;
|
| - LabelDefinition label = statementScope.lookupLabel(labelName);
|
| - if (label == null) {
|
| - error(node.target, MessageKind.UNBOUND_LABEL, {'labelName': labelName});
|
| - return;
|
| - }
|
| - target = label.target;
|
| - if (!target.statement.isValidBreakTarget()) {
|
| - error(node.target, MessageKind.INVALID_BREAK);
|
| - return;
|
| - }
|
| - label.setBreakTarget();
|
| - registry.useLabel(node, label);
|
| - }
|
| - registry.registerTargetOf(node, target);
|
| - }
|
| -
|
| - visitContinueStatement(ContinueStatement node) {
|
| - JumpTarget 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;
|
| - LabelDefinition label = statementScope.lookupLabel(labelName);
|
| - if (label == null) {
|
| - error(node.target, MessageKind.UNBOUND_LABEL, {'labelName': labelName});
|
| - return;
|
| - }
|
| - target = label.target;
|
| - if (!target.statement.isValidContinueTarget()) {
|
| - error(node.target, MessageKind.INVALID_CONTINUE);
|
| - }
|
| - label.setContinueTarget();
|
| - registry.useLabel(node, label);
|
| - }
|
| - registry.registerTargetOf(node, target);
|
| - }
|
| -
|
| - registerImplicitInvocation(String name, int arity) {
|
| - Selector selector = new Selector.call(name, null, arity);
|
| - registry.registerDynamicInvocation(selector);
|
| - }
|
| -
|
| - visitForIn(ForIn node) {
|
| - LibraryElement library = enclosingElement.library;
|
| - registry.setIteratorSelector(node, compiler.iteratorSelector);
|
| - registry.registerDynamicGetter(compiler.iteratorSelector);
|
| - registry.setCurrentSelector(node, compiler.currentSelector);
|
| - registry.registerDynamicGetter(compiler.currentSelector);
|
| - registry.setMoveNextSelector(node, compiler.moveNextSelector);
|
| - registry.registerDynamicInvocation(compiler.moveNextSelector);
|
| -
|
| - visit(node.expression);
|
| - Scope blockScope = new BlockScope(scope);
|
| - Node declaration = node.declaredIdentifier;
|
| -
|
| - bool oldAllowFinalWithoutInitializer = allowFinalWithoutInitializer;
|
| - allowFinalWithoutInitializer = true;
|
| - visitIn(declaration, blockScope);
|
| - allowFinalWithoutInitializer = oldAllowFinalWithoutInitializer;
|
| -
|
| - Send send = declaration.asSend();
|
| - VariableDefinitions variableDefinitions =
|
| - declaration.asVariableDefinitions();
|
| - Element loopVariable;
|
| - Selector loopVariableSelector;
|
| - if (send != null) {
|
| - loopVariable = registry.getDefinition(send);
|
| - Identifier identifier = send.selector.asIdentifier();
|
| - if (identifier == null) {
|
| - compiler.reportError(send.selector, MessageKind.INVALID_FOR_IN);
|
| - } else {
|
| - loopVariableSelector = new Selector.setter(identifier.source, library);
|
| - }
|
| - if (send.receiver != null) {
|
| - compiler.reportError(send.receiver, MessageKind.INVALID_FOR_IN);
|
| - }
|
| - } else if (variableDefinitions != null) {
|
| - Link<Node> nodes = variableDefinitions.definitions.nodes;
|
| - if (!nodes.tail.isEmpty) {
|
| - compiler.reportError(nodes.tail.head, MessageKind.INVALID_FOR_IN);
|
| - }
|
| - Node first = nodes.head;
|
| - Identifier identifier = first.asIdentifier();
|
| - if (identifier == null) {
|
| - compiler.reportError(first, MessageKind.INVALID_FOR_IN);
|
| - } else {
|
| - loopVariableSelector = new Selector.setter(identifier.source, library);
|
| - loopVariable = registry.getDefinition(identifier);
|
| - }
|
| - } else {
|
| - compiler.reportError(declaration, MessageKind.INVALID_FOR_IN);
|
| - }
|
| - if (loopVariableSelector != null) {
|
| - registry.setSelector(declaration, loopVariableSelector);
|
| - registerSend(loopVariableSelector, loopVariable);
|
| - } else {
|
| - // The selector may only be null if we reported an error.
|
| - assert(invariant(declaration, compiler.compilationFailed));
|
| - }
|
| - if (loopVariable != null) {
|
| - // loopVariable may be null if it could not be resolved.
|
| - registry.setForInVariable(node, loopVariable);
|
| - }
|
| - visitLoopBodyIn(node, node.body, blockScope);
|
| - }
|
| -
|
| - visitLabel(Label node) {
|
| - // Labels are handled by their containing statements/cases.
|
| - }
|
| -
|
| - visitLabeledStatement(LabeledStatement node) {
|
| - Statement body = node.statement;
|
| - JumpTarget targetElement = getOrDefineTarget(body);
|
| - Map<String, LabelDefinition> labelElements = <String, LabelDefinition>{};
|
| - for (Label label in node.labels) {
|
| - String labelName = label.labelName;
|
| - if (labelElements.containsKey(labelName)) continue;
|
| - LabelDefinition element = targetElement.addLabel(label, labelName);
|
| - labelElements[labelName] = element;
|
| - }
|
| - statementScope.enterLabelScope(labelElements);
|
| - visit(node.statement);
|
| - statementScope.exitLabelScope();
|
| - labelElements.forEach((String labelName, LabelDefinition element) {
|
| - if (element.isTarget) {
|
| - registry.defineLabel(element.label, element);
|
| - } else {
|
| - warning(element.label, MessageKind.UNUSED_LABEL,
|
| - {'labelName': labelName});
|
| - }
|
| - });
|
| - if (!targetElement.isTarget) {
|
| - registry.undefineTarget(body);
|
| - }
|
| - }
|
| -
|
| - visitLiteralMap(LiteralMap node) {
|
| - bool oldSendIsMemberAccess = sendIsMemberAccess;
|
| - sendIsMemberAccess = false;
|
| -
|
| - NodeList arguments = node.typeArguments;
|
| - DartType keyTypeArgument;
|
| - DartType valueTypeArgument;
|
| - if (arguments != null) {
|
| - Link<Node> nodes = arguments.nodes;
|
| - if (nodes.isEmpty) {
|
| - // The syntax [: <>{} :] is not allowed.
|
| - error(arguments, MessageKind.MISSING_TYPE_ARGUMENT);
|
| - } else {
|
| - keyTypeArgument = resolveTypeAnnotation(nodes.head);
|
| - nodes = nodes.tail;
|
| - if (nodes.isEmpty) {
|
| - warning(arguments, MessageKind.MISSING_TYPE_ARGUMENT);
|
| - } else {
|
| - valueTypeArgument = resolveTypeAnnotation(nodes.head);
|
| - for (nodes = nodes.tail; !nodes.isEmpty; nodes = nodes.tail) {
|
| - warning(nodes.head, MessageKind.ADDITIONAL_TYPE_ARGUMENT);
|
| - resolveTypeAnnotation(nodes.head);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - DartType mapType;
|
| - if (valueTypeArgument != null) {
|
| - mapType = new InterfaceType(compiler.mapClass,
|
| - [keyTypeArgument, valueTypeArgument]);
|
| - } else {
|
| - compiler.mapClass.computeType(compiler);
|
| - mapType = compiler.mapClass.rawType;
|
| - }
|
| - if (node.isConst && mapType.containsTypeVariables) {
|
| - compiler.reportError(arguments,
|
| - MessageKind.TYPE_VARIABLE_IN_CONSTANT);
|
| - }
|
| - registry.setType(node, mapType);
|
| - registry.registerInstantiatedType(mapType);
|
| - if (node.isConst) {
|
| - registry.registerConstantMap();
|
| - }
|
| - registry.registerRequiredType(mapType, enclosingElement);
|
| - node.visitChildren(this);
|
| - if (node.isConst) {
|
| - analyzeConstant(node);
|
| - }
|
| -
|
| - sendIsMemberAccess = false;
|
| - }
|
| -
|
| - visitLiteralMapEntry(LiteralMapEntry node) {
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - visitNamedArgument(NamedArgument node) {
|
| - visit(node.expression);
|
| - }
|
| -
|
| - DartType typeOfConstant(ConstantValue constant) {
|
| - if (constant.isInt) return compiler.intClass.rawType;
|
| - if (constant.isBool) return compiler.boolClass.rawType;
|
| - if (constant.isDouble) return compiler.doubleClass.rawType;
|
| - if (constant.isString) return compiler.stringClass.rawType;
|
| - if (constant.isNull) return compiler.nullClass.rawType;
|
| - if (constant.isFunction) return compiler.functionClass.rawType;
|
| - assert(constant.isObject);
|
| - ObjectConstantValue objectConstant = constant;
|
| - return objectConstant.type;
|
| - }
|
| -
|
| - bool overridesEquals(DartType type) {
|
| - ClassElement cls = type.element;
|
| - Element equals = cls.lookupMember('==');
|
| - return equals.enclosingClass != compiler.objectClass;
|
| - }
|
| -
|
| - void checkCaseExpressions(SwitchStatement node) {
|
| - JumpTarget breakElement = getOrDefineTarget(node);
|
| - Map<String, LabelDefinition> continueLabels = <String, LabelDefinition>{};
|
| -
|
| - Link<Node> cases = node.cases.nodes;
|
| - CaseMatch firstCase = null;
|
| - DartType firstCaseType = null;
|
| - bool hasReportedProblem = false;
|
| -
|
| - for (Link<Node> cases = node.cases.nodes;
|
| - !cases.isEmpty;
|
| - cases = cases.tail) {
|
| - SwitchCase switchCase = cases.head;
|
| -
|
| - for (Node labelOrCase in switchCase.labelsAndCases) {
|
| - CaseMatch caseMatch = labelOrCase.asCaseMatch();
|
| - if (caseMatch == null) continue;
|
| -
|
| - // Analyze the constant.
|
| - ConstantExpression constant =
|
| - registry.getConstant(caseMatch.expression);
|
| - assert(invariant(node, constant != null,
|
| - message: 'No constant computed for $node'));
|
| -
|
| - DartType caseType = typeOfConstant(constant.value);
|
| -
|
| - if (firstCaseType == null) {
|
| - firstCase = caseMatch;
|
| - firstCaseType = caseType;
|
| -
|
| - // We only report the bad type on the first class element. All others
|
| - // get a "type differs" error.
|
| - if (caseType.element == compiler.doubleClass) {
|
| - compiler.reportError(node,
|
| - MessageKind.SWITCH_CASE_VALUE_OVERRIDES_EQUALS,
|
| - {'type': "double"});
|
| - } else if (caseType.element == compiler.functionClass) {
|
| - compiler.reportError(node, MessageKind.SWITCH_CASE_FORBIDDEN,
|
| - {'type': "Function"});
|
| - } else if (constant.value.isObject && overridesEquals(caseType)) {
|
| - compiler.reportError(firstCase.expression,
|
| - MessageKind.SWITCH_CASE_VALUE_OVERRIDES_EQUALS,
|
| - {'type': caseType});
|
| - }
|
| - } else {
|
| - if (caseType != firstCaseType) {
|
| - if (!hasReportedProblem) {
|
| - compiler.reportError(
|
| - node,
|
| - MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL,
|
| - {'type': firstCaseType});
|
| - compiler.reportInfo(
|
| - firstCase.expression,
|
| - MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL_CASE,
|
| - {'type': firstCaseType});
|
| - hasReportedProblem = true;
|
| - }
|
| - compiler.reportInfo(
|
| - caseMatch.expression,
|
| - MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL_CASE,
|
| - {'type': caseType});
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - visitSwitchStatement(SwitchStatement node) {
|
| - node.expression.accept(this);
|
| -
|
| - JumpTarget breakElement = getOrDefineTarget(node);
|
| - Map<String, LabelDefinition> continueLabels = <String, LabelDefinition>{};
|
| - Link<Node> cases = node.cases.nodes;
|
| - while (!cases.isEmpty) {
|
| - SwitchCase switchCase = cases.head;
|
| - for (Node labelOrCase in switchCase.labelsAndCases) {
|
| - CaseMatch caseMatch = labelOrCase.asCaseMatch();
|
| - if (caseMatch != null) {
|
| - analyzeConstant(caseMatch.expression);
|
| - continue;
|
| - }
|
| - Label label = labelOrCase;
|
| - String labelName = label.labelName;
|
| -
|
| - LabelDefinition existingElement = continueLabels[labelName];
|
| - if (existingElement != null) {
|
| - // It's an error if the same label occurs twice in the same switch.
|
| - compiler.reportError(
|
| - label,
|
| - MessageKind.DUPLICATE_LABEL, {'labelName': labelName});
|
| - compiler.reportInfo(
|
| - existingElement.label,
|
| - MessageKind.EXISTING_LABEL, {'labelName': labelName});
|
| - } else {
|
| - // It's only a warning if it shadows another label.
|
| - existingElement = statementScope.lookupLabel(labelName);
|
| - if (existingElement != null) {
|
| - compiler.reportWarning(
|
| - label,
|
| - MessageKind.DUPLICATE_LABEL, {'labelName': labelName});
|
| - compiler.reportInfo(
|
| - existingElement.label,
|
| - MessageKind.EXISTING_LABEL, {'labelName': labelName});
|
| - }
|
| - }
|
| -
|
| - JumpTarget targetElement = getOrDefineTarget(switchCase);
|
| - LabelDefinition labelElement = targetElement.addLabel(label, labelName);
|
| - registry.defineLabel(label, labelElement);
|
| - continueLabels[labelName] = labelElement;
|
| - }
|
| - cases = cases.tail;
|
| - // Test that only the last case, if any, is a default case.
|
| - if (switchCase.defaultKeyword != null && !cases.isEmpty) {
|
| - error(switchCase, MessageKind.INVALID_CASE_DEFAULT);
|
| - }
|
| - }
|
| -
|
| - addDeferredAction(enclosingElement, () {
|
| - checkCaseExpressions(node);
|
| - });
|
| -
|
| - statementScope.enterSwitch(breakElement, continueLabels);
|
| - node.cases.accept(this);
|
| - statementScope.exitSwitch();
|
| -
|
| - // Clean-up unused labels.
|
| - continueLabels.forEach((String key, LabelDefinition label) {
|
| - if (!label.isContinueTarget) {
|
| - JumpTarget targetElement = label.target;
|
| - SwitchCase switchCase = targetElement.statement;
|
| - registry.undefineTarget(switchCase);
|
| - registry.undefineLabel(label.label);
|
| - }
|
| - });
|
| - // TODO(15575): We should warn if we can detect a fall through
|
| - // error.
|
| - registry.registerFallThroughError();
|
| - }
|
| -
|
| - visitSwitchCase(SwitchCase node) {
|
| - node.labelsAndCases.accept(this);
|
| - visitIn(node.statements, new BlockScope(scope));
|
| - }
|
| -
|
| - visitCaseMatch(CaseMatch node) {
|
| - visit(node.expression);
|
| - }
|
| -
|
| - visitTryStatement(TryStatement node) {
|
| - visit(node.tryBlock);
|
| - if (node.catchBlocks.isEmpty && node.finallyBlock == null) {
|
| - error(node.getEndToken().next, MessageKind.NO_CATCH_NOR_FINALLY);
|
| - }
|
| - visit(node.catchBlocks);
|
| - visit(node.finallyBlock);
|
| - }
|
| -
|
| - visitCatchBlock(CatchBlock node) {
|
| - registry.registerCatchStatement();
|
| - // Check that if catch part is present, then
|
| - // it has one or two formal parameters.
|
| - VariableDefinitions exceptionDefinition;
|
| - VariableDefinitions stackTraceDefinition;
|
| - if (node.formals != null) {
|
| - Link<Node> formalsToProcess = node.formals.nodes;
|
| - if (formalsToProcess.isEmpty) {
|
| - error(node, MessageKind.EMPTY_CATCH_DECLARATION);
|
| - } else {
|
| - exceptionDefinition = formalsToProcess.head.asVariableDefinitions();
|
| - formalsToProcess = formalsToProcess.tail;
|
| - if (!formalsToProcess.isEmpty) {
|
| - stackTraceDefinition = formalsToProcess.head.asVariableDefinitions();
|
| - formalsToProcess = formalsToProcess.tail;
|
| - if (!formalsToProcess.isEmpty) {
|
| - for (Node extra in formalsToProcess) {
|
| - error(extra, MessageKind.EXTRA_CATCH_DECLARATION);
|
| - }
|
| - }
|
| - registry.registerStackTraceInCatch();
|
| - }
|
| - }
|
| -
|
| - // Check that the formals aren't optional and that they have no
|
| - // modifiers or type.
|
| - for (Link<Node> link = node.formals.nodes;
|
| - !link.isEmpty;
|
| - link = link.tail) {
|
| - // If the formal parameter is a node list, it means that it is a
|
| - // sequence of optional parameters.
|
| - NodeList nodeList = link.head.asNodeList();
|
| - if (nodeList != null) {
|
| - error(nodeList, MessageKind.OPTIONAL_PARAMETER_IN_CATCH);
|
| - } else {
|
| - VariableDefinitions declaration = link.head;
|
| - for (Node modifier in declaration.modifiers.nodes) {
|
| - error(modifier, MessageKind.PARAMETER_WITH_MODIFIER_IN_CATCH);
|
| - }
|
| - TypeAnnotation type = declaration.type;
|
| - if (type != null) {
|
| - error(type, MessageKind.PARAMETER_WITH_TYPE_IN_CATCH);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - Scope blockScope = new BlockScope(scope);
|
| - doInCheckContext(() => visitIn(node.type, blockScope));
|
| - visitIn(node.formals, blockScope);
|
| - var oldInCatchBlock = inCatchBlock;
|
| - inCatchBlock = true;
|
| - visitIn(node.block, blockScope);
|
| - inCatchBlock = oldInCatchBlock;
|
| -
|
| - if (node.type != null && exceptionDefinition != null) {
|
| - DartType exceptionType = registry.getType(node.type);
|
| - Node exceptionVariable = exceptionDefinition.definitions.nodes.head;
|
| - VariableElementX exceptionElement =
|
| - registry.getDefinition(exceptionVariable);
|
| - exceptionElement.variables.type = exceptionType;
|
| - }
|
| - if (stackTraceDefinition != null) {
|
| - Node stackTraceVariable = stackTraceDefinition.definitions.nodes.head;
|
| - VariableElementX stackTraceElement =
|
| - registry.getDefinition(stackTraceVariable);
|
| - registry.registerInstantiatedClass(compiler.stackTraceClass);
|
| - stackTraceElement.variables.type = compiler.stackTraceClass.rawType;
|
| - }
|
| - }
|
| -
|
| - visitTypedef(Typedef node) {
|
| - internalError(node, 'typedef');
|
| - }
|
| -}
|
| -
|
| -class TypeDefinitionVisitor extends MappingVisitor<DartType> {
|
| - Scope scope;
|
| - final TypeDeclarationElement enclosingElement;
|
| - TypeDeclarationElement get element => enclosingElement;
|
| -
|
| - TypeDefinitionVisitor(Compiler compiler,
|
| - TypeDeclarationElement element,
|
| - ResolutionRegistry registry)
|
| - : this.enclosingElement = element,
|
| - scope = Scope.buildEnclosingScope(element),
|
| - super(compiler, registry);
|
| -
|
| - DartType get objectType => compiler.objectClass.rawType;
|
| -
|
| - void resolveTypeVariableBounds(NodeList node) {
|
| - if (node == null) return;
|
| -
|
| - Setlet<String> nameSet = new Setlet<String>();
|
| - // Resolve the bounds of type variables.
|
| - Iterator<DartType> types = element.typeVariables.iterator;
|
| - Link<Node> nodeLink = node.nodes;
|
| - while (!nodeLink.isEmpty) {
|
| - types.moveNext();
|
| - TypeVariableType typeVariable = types.current;
|
| - String typeName = typeVariable.name;
|
| - TypeVariable typeNode = nodeLink.head;
|
| - registry.useType(typeNode, typeVariable);
|
| - if (nameSet.contains(typeName)) {
|
| - error(typeNode, MessageKind.DUPLICATE_TYPE_VARIABLE_NAME,
|
| - {'typeVariableName': typeName});
|
| - }
|
| - nameSet.add(typeName);
|
| -
|
| - TypeVariableElementX variableElement = typeVariable.element;
|
| - if (typeNode.bound != null) {
|
| - DartType boundType = typeResolver.resolveTypeAnnotation(
|
| - this, typeNode.bound);
|
| - variableElement.boundCache = boundType;
|
| -
|
| - void checkTypeVariableBound() {
|
| - Link<TypeVariableElement> seenTypeVariables =
|
| - const Link<TypeVariableElement>();
|
| - seenTypeVariables = seenTypeVariables.prepend(variableElement);
|
| - DartType bound = boundType;
|
| - while (bound.isTypeVariable) {
|
| - TypeVariableElement element = bound.element;
|
| - if (seenTypeVariables.contains(element)) {
|
| - if (identical(element, variableElement)) {
|
| - // Only report an error on the checked type variable to avoid
|
| - // generating multiple errors for the same cyclicity.
|
| - warning(typeNode.name, MessageKind.CYCLIC_TYPE_VARIABLE,
|
| - {'typeVariableName': variableElement.name});
|
| - }
|
| - break;
|
| - }
|
| - seenTypeVariables = seenTypeVariables.prepend(element);
|
| - bound = element.bound;
|
| - }
|
| - }
|
| - addDeferredAction(element, checkTypeVariableBound);
|
| - } else {
|
| - variableElement.boundCache = objectType;
|
| - }
|
| - nodeLink = nodeLink.tail;
|
| - }
|
| - assert(!types.moveNext());
|
| - }
|
| -}
|
| -
|
| -class TypedefResolverVisitor extends TypeDefinitionVisitor {
|
| - TypedefElementX get element => enclosingElement;
|
| -
|
| - TypedefResolverVisitor(Compiler compiler,
|
| - TypedefElement typedefElement,
|
| - ResolutionRegistry registry)
|
| - : super(compiler, typedefElement, registry);
|
| -
|
| - visitTypedef(Typedef node) {
|
| - TypedefType type = element.computeType(compiler);
|
| - scope = new TypeDeclarationScope(scope, element);
|
| - resolveTypeVariableBounds(node.typeParameters);
|
| -
|
| - FunctionSignature signature = SignatureResolver.analyze(
|
| - compiler, node.formals, node.returnType, element, registry,
|
| - defaultValuesError: MessageKind.TYPEDEF_FORMAL_WITH_DEFAULT);
|
| - element.functionSignature = signature;
|
| -
|
| - scope = new MethodScope(scope, element);
|
| - signature.forEachParameter(addToScope);
|
| -
|
| - element.alias = signature.type;
|
| -
|
| - void checkCyclicReference() {
|
| - element.checkCyclicReference(compiler);
|
| - }
|
| - addDeferredAction(element, checkCyclicReference);
|
| - }
|
| -}
|
| -
|
| -// TODO(johnniwinther): Replace with a traversal on the AST when the type
|
| -// annotations in typedef alias are stored in a [TreeElements] mapping.
|
| -class TypedefCyclicVisitor extends DartTypeVisitor {
|
| - final Compiler compiler;
|
| - final TypedefElementX element;
|
| - bool hasCyclicReference = false;
|
| -
|
| - Link<TypedefElement> seenTypedefs = const Link<TypedefElement>();
|
| -
|
| - int seenTypedefsCount = 0;
|
| -
|
| - Link<TypeVariableElement> seenTypeVariables =
|
| - const Link<TypeVariableElement>();
|
| -
|
| - TypedefCyclicVisitor(Compiler this.compiler, TypedefElement this.element);
|
| -
|
| - visitType(DartType type, _) {
|
| - // Do nothing.
|
| - }
|
| -
|
| - visitTypedefType(TypedefType type, _) {
|
| - TypedefElementX typedefElement = type.element;
|
| - if (seenTypedefs.contains(typedefElement)) {
|
| - if (!hasCyclicReference && identical(element, typedefElement)) {
|
| - // Only report an error on the checked typedef to avoid generating
|
| - // multiple errors for the same cyclicity.
|
| - hasCyclicReference = true;
|
| - if (seenTypedefsCount == 1) {
|
| - // Direct cyclicity.
|
| - compiler.reportError(element,
|
| - MessageKind.CYCLIC_TYPEDEF,
|
| - {'typedefName': element.name});
|
| - } else if (seenTypedefsCount == 2) {
|
| - // Cyclicity through one other typedef.
|
| - compiler.reportError(element,
|
| - MessageKind.CYCLIC_TYPEDEF_ONE,
|
| - {'typedefName': element.name,
|
| - 'otherTypedefName': seenTypedefs.head.name});
|
| - } else {
|
| - // Cyclicity through more than one other typedef.
|
| - for (TypedefElement cycle in seenTypedefs) {
|
| - if (!identical(typedefElement, cycle)) {
|
| - compiler.reportError(element,
|
| - MessageKind.CYCLIC_TYPEDEF_ONE,
|
| - {'typedefName': element.name,
|
| - 'otherTypedefName': cycle.name});
|
| - }
|
| - }
|
| - }
|
| - ErroneousElementX erroneousElement = new ErroneousElementX(
|
| - MessageKind.CYCLIC_TYPEDEF,
|
| - {'typedefName': element.name},
|
| - element.name, element);
|
| - element.alias =
|
| - new MalformedType(erroneousElement, typedefElement.alias);
|
| - element.hasBeenCheckedForCycles = true;
|
| - }
|
| - } else {
|
| - seenTypedefs = seenTypedefs.prepend(typedefElement);
|
| - seenTypedefsCount++;
|
| - type.visitChildren(this, null);
|
| - typedefElement.alias.accept(this, null);
|
| - seenTypedefs = seenTypedefs.tail;
|
| - seenTypedefsCount--;
|
| - }
|
| - }
|
| -
|
| - visitFunctionType(FunctionType type, _) {
|
| - type.visitChildren(this, null);
|
| - }
|
| -
|
| - visitInterfaceType(InterfaceType type, _) {
|
| - type.visitChildren(this, null);
|
| - }
|
| -
|
| - visitTypeVariableType(TypeVariableType type, _) {
|
| - TypeVariableElement typeVariableElement = type.element;
|
| - if (seenTypeVariables.contains(typeVariableElement)) {
|
| - // Avoid running in cycles on cyclic type variable bounds.
|
| - // Cyclicity is reported elsewhere.
|
| - return;
|
| - }
|
| - seenTypeVariables = seenTypeVariables.prepend(typeVariableElement);
|
| - typeVariableElement.bound.accept(this, null);
|
| - seenTypeVariables = seenTypeVariables.tail;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * The implementation of [ResolverTask.resolveClass].
|
| - *
|
| - * This visitor has to be extra careful as it is building the basic
|
| - * element information, and cannot safely look at other elements as
|
| - * this may lead to cycles.
|
| - *
|
| - * This visitor can assume that the supertypes have already been
|
| - * resolved, but it cannot call [ResolverTask.resolveClass] directly
|
| - * or indirectly (through [ClassElement.ensureResolved]) for any other
|
| - * types.
|
| - */
|
| -class ClassResolverVisitor extends TypeDefinitionVisitor {
|
| - BaseClassElementX get element => enclosingElement;
|
| -
|
| - ClassResolverVisitor(Compiler compiler,
|
| - ClassElement classElement,
|
| - ResolutionRegistry registry)
|
| - : super(compiler, classElement, registry);
|
| -
|
| - DartType visitClassNode(ClassNode node) {
|
| - invariant(node, element != null);
|
| - invariant(element, element.resolutionState == STATE_STARTED,
|
| - message: () => 'cyclic resolution of class $element');
|
| -
|
| - InterfaceType type = element.computeType(compiler);
|
| - scope = new TypeDeclarationScope(scope, element);
|
| - // TODO(ahe): It is not safe to call resolveTypeVariableBounds yet.
|
| - // As a side-effect, this may get us back here trying to
|
| - // resolve this class again.
|
| - resolveTypeVariableBounds(node.typeParameters);
|
| -
|
| - // Setup the supertype for the element (if there is a cycle in the
|
| - // class hierarchy, it has already been set to Object).
|
| - if (element.supertype == null && node.superclass != null) {
|
| - MixinApplication superMixin = node.superclass.asMixinApplication();
|
| - if (superMixin != null) {
|
| - DartType supertype = resolveSupertype(element, superMixin.superclass);
|
| - Link<Node> link = superMixin.mixins.nodes;
|
| - while (!link.isEmpty) {
|
| - supertype = applyMixin(supertype,
|
| - checkMixinType(link.head), link.head);
|
| - link = link.tail;
|
| - }
|
| - element.supertype = supertype;
|
| - } else {
|
| - element.supertype = resolveSupertype(element, node.superclass);
|
| - }
|
| - }
|
| - // If the super type isn't specified, we provide a default. The language
|
| - // specifies [Object] but the backend can pick a specific 'implementation'
|
| - // of Object - the JavaScript backend chooses between Object and
|
| - // Interceptor.
|
| - if (element.supertype == null) {
|
| - ClassElement superElement = registry.defaultSuperclass(element);
|
| - // Avoid making the superclass (usually Object) extend itself.
|
| - if (element != superElement) {
|
| - if (superElement == null) {
|
| - compiler.internalError(node,
|
| - "Cannot resolve default superclass for $element.");
|
| - } else {
|
| - superElement.ensureResolved(compiler);
|
| - }
|
| - element.supertype = superElement.computeType(compiler);
|
| - }
|
| - }
|
| -
|
| - if (element.interfaces == null) {
|
| - element.interfaces = resolveInterfaces(node.interfaces, node.superclass);
|
| - } else {
|
| - assert(invariant(element, element.hasIncompleteHierarchy));
|
| - }
|
| - calculateAllSupertypes(element);
|
| -
|
| - if (!element.hasConstructor) {
|
| - Element superMember = element.superclass.localLookup('');
|
| - if (superMember == null || !superMember.isGenerativeConstructor) {
|
| - MessageKind kind = MessageKind.CANNOT_FIND_CONSTRUCTOR;
|
| - Map arguments = {'constructorName': ''};
|
| - // TODO(ahe): Why is this a compile-time error? Or if it is an error,
|
| - // why do we bother to registerThrowNoSuchMethod below?
|
| - compiler.reportError(node, kind, arguments);
|
| - superMember = new ErroneousElementX(
|
| - kind, arguments, '', element);
|
| - registry.registerThrowNoSuchMethod();
|
| - } else {
|
| - ConstructorElement superConstructor = superMember;
|
| - Selector callToMatch = new Selector.call("", element.library, 0);
|
| - superConstructor.computeSignature(compiler);
|
| - if (!callToMatch.applies(superConstructor, compiler.world)) {
|
| - MessageKind kind = MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT;
|
| - compiler.reportError(node, kind);
|
| - superMember = new ErroneousElementX(kind, {}, '', element);
|
| - }
|
| - }
|
| - FunctionElement constructor =
|
| - new SynthesizedConstructorElementX.forDefault(superMember, element);
|
| - element.setDefaultConstructor(constructor, compiler);
|
| - }
|
| - return element.computeType(compiler);
|
| - }
|
| -
|
| - /// Resolves the mixed type for [mixinNode] and checks that the the mixin type
|
| - /// is a valid, non-blacklisted interface type. The mixin type is returned.
|
| - DartType checkMixinType(TypeAnnotation mixinNode) {
|
| - DartType mixinType = resolveType(mixinNode);
|
| - if (isBlackListed(mixinType)) {
|
| - compiler.reportError(mixinNode,
|
| - MessageKind.CANNOT_MIXIN, {'type': mixinType});
|
| - } else if (mixinType.isTypeVariable) {
|
| - compiler.reportError(mixinNode, MessageKind.CLASS_NAME_EXPECTED);
|
| - } else if (mixinType.isMalformed) {
|
| - compiler.reportError(mixinNode, MessageKind.CANNOT_MIXIN_MALFORMED,
|
| - {'className': element.name, 'malformedType': mixinType});
|
| - }
|
| - return mixinType;
|
| - }
|
| -
|
| - DartType visitNamedMixinApplication(NamedMixinApplication node) {
|
| - invariant(node, element != null);
|
| - invariant(element, element.resolutionState == STATE_STARTED,
|
| - message: () => 'cyclic resolution of class $element');
|
| -
|
| - if (identical(node.classKeyword.stringValue, 'typedef')) {
|
| - // TODO(aprelev@gmail.com): Remove this deprecation diagnostic
|
| - // together with corresponding TODO in parser.dart.
|
| - compiler.reportWarning(node.classKeyword,
|
| - MessageKind.DEPRECATED_TYPEDEF_MIXIN_SYNTAX);
|
| - }
|
| -
|
| - InterfaceType type = element.computeType(compiler);
|
| - scope = new TypeDeclarationScope(scope, element);
|
| - resolveTypeVariableBounds(node.typeParameters);
|
| -
|
| - // Generate anonymous mixin application elements for the
|
| - // intermediate mixin applications (excluding the last).
|
| - DartType supertype = resolveSupertype(element, node.superclass);
|
| - Link<Node> link = node.mixins.nodes;
|
| - while (!link.tail.isEmpty) {
|
| - supertype = applyMixin(supertype, checkMixinType(link.head), link.head);
|
| - link = link.tail;
|
| - }
|
| - doApplyMixinTo(element, supertype, checkMixinType(link.head));
|
| - return element.computeType(compiler);
|
| - }
|
| -
|
| - DartType applyMixin(DartType supertype, DartType mixinType, Node node) {
|
| - String superName = supertype.name;
|
| - String mixinName = mixinType.name;
|
| - MixinApplicationElementX mixinApplication = new MixinApplicationElementX(
|
| - "${superName}+${mixinName}",
|
| - element.compilationUnit,
|
| - compiler.getNextFreeClassId(),
|
| - node,
|
| - new Modifiers.withFlags(new NodeList.empty(), Modifiers.FLAG_ABSTRACT));
|
| - // Create synthetic type variables for the mixin application.
|
| - List<DartType> typeVariables = <DartType>[];
|
| - element.typeVariables.forEach((TypeVariableType type) {
|
| - TypeVariableElementX typeVariableElement = new TypeVariableElementX(
|
| - type.name, mixinApplication, type.element.node);
|
| - TypeVariableType typeVariable = new TypeVariableType(typeVariableElement);
|
| - typeVariables.add(typeVariable);
|
| - });
|
| - // Setup bounds on the synthetic type variables.
|
| - List<DartType> link = typeVariables;
|
| - int index = 0;
|
| - element.typeVariables.forEach((TypeVariableType type) {
|
| - TypeVariableType typeVariable = typeVariables[index++];
|
| - TypeVariableElementX typeVariableElement = typeVariable.element;
|
| - typeVariableElement.typeCache = typeVariable;
|
| - typeVariableElement.boundCache =
|
| - type.element.bound.subst(typeVariables, element.typeVariables);
|
| - });
|
| - // Setup this and raw type for the mixin application.
|
| - mixinApplication.computeThisAndRawType(compiler, typeVariables);
|
| - // Substitute in synthetic type variables in super and mixin types.
|
| - supertype = supertype.subst(typeVariables, element.typeVariables);
|
| - mixinType = mixinType.subst(typeVariables, element.typeVariables);
|
| -
|
| - doApplyMixinTo(mixinApplication, supertype, mixinType);
|
| - mixinApplication.resolutionState = STATE_DONE;
|
| - mixinApplication.supertypeLoadState = STATE_DONE;
|
| - // Replace the synthetic type variables by the original type variables in
|
| - // the returned type (which should be the type actually extended).
|
| - InterfaceType mixinThisType = mixinApplication.computeType(compiler);
|
| - return mixinThisType.subst(element.typeVariables,
|
| - mixinThisType.typeArguments);
|
| - }
|
| -
|
| - bool isDefaultConstructor(FunctionElement constructor) {
|
| - return constructor.name == '' &&
|
| - constructor.computeSignature(compiler).parameterCount == 0;
|
| - }
|
| -
|
| - FunctionElement createForwardingConstructor(ConstructorElement target,
|
| - ClassElement enclosing) {
|
| - return new SynthesizedConstructorElementX(
|
| - target.name, target, enclosing, false);
|
| - }
|
| -
|
| - void doApplyMixinTo(MixinApplicationElementX mixinApplication,
|
| - DartType supertype,
|
| - DartType mixinType) {
|
| - Node node = mixinApplication.parseNode(compiler);
|
| -
|
| - if (mixinApplication.supertype != null) {
|
| - // [supertype] is not null if there was a cycle.
|
| - assert(invariant(node, compiler.compilationFailed));
|
| - supertype = mixinApplication.supertype;
|
| - assert(invariant(node, supertype.element == compiler.objectClass));
|
| - } else {
|
| - mixinApplication.supertype = supertype;
|
| - }
|
| -
|
| - // Named mixin application may have an 'implements' clause.
|
| - NamedMixinApplication namedMixinApplication =
|
| - node.asNamedMixinApplication();
|
| - Link<DartType> interfaces = (namedMixinApplication != null)
|
| - ? resolveInterfaces(namedMixinApplication.interfaces,
|
| - namedMixinApplication.superclass)
|
| - : const Link<DartType>();
|
| -
|
| - // The class that is the result of a mixin application implements
|
| - // the interface of the class that was mixed in so always prepend
|
| - // that to the interface list.
|
| - if (mixinApplication.interfaces == null) {
|
| - if (mixinType.isInterfaceType) {
|
| - // Avoid malformed types in the interfaces.
|
| - interfaces = interfaces.prepend(mixinType);
|
| - }
|
| - mixinApplication.interfaces = interfaces;
|
| - } else {
|
| - assert(invariant(mixinApplication,
|
| - mixinApplication.hasIncompleteHierarchy));
|
| - }
|
| -
|
| - ClassElement superclass = supertype.element;
|
| - if (mixinType.kind != TypeKind.INTERFACE) {
|
| - mixinApplication.hasIncompleteHierarchy = true;
|
| - mixinApplication.allSupertypesAndSelf = superclass.allSupertypesAndSelf;
|
| - return;
|
| - }
|
| -
|
| - assert(mixinApplication.mixinType == null);
|
| - mixinApplication.mixinType = resolveMixinFor(mixinApplication, mixinType);
|
| -
|
| - // Create forwarding constructors for constructor defined in the superclass
|
| - // because they are now hidden by the mixin application.
|
| - superclass.forEachLocalMember((Element member) {
|
| - if (!member.isGenerativeConstructor) return;
|
| - FunctionElement forwarder =
|
| - createForwardingConstructor(member, mixinApplication);
|
| - if (isPrivateName(member.name) &&
|
| - mixinApplication.library != superclass.library) {
|
| - // Do not create a forwarder to the super constructor, because the mixin
|
| - // application is in a different library than the constructor in the
|
| - // super class and it is not possible to call that constructor from the
|
| - // library using the mixin application.
|
| - return;
|
| - }
|
| - mixinApplication.addConstructor(forwarder);
|
| - });
|
| - calculateAllSupertypes(mixinApplication);
|
| - }
|
| -
|
| - InterfaceType resolveMixinFor(MixinApplicationElement mixinApplication,
|
| - DartType mixinType) {
|
| - ClassElement mixin = mixinType.element;
|
| - mixin.ensureResolved(compiler);
|
| -
|
| - // Check for cycles in the mixin chain.
|
| - ClassElement previous = mixinApplication; // For better error messages.
|
| - ClassElement current = mixin;
|
| - while (current != null && current.isMixinApplication) {
|
| - MixinApplicationElement currentMixinApplication = current;
|
| - if (currentMixinApplication == mixinApplication) {
|
| - compiler.reportError(
|
| - mixinApplication, MessageKind.ILLEGAL_MIXIN_CYCLE,
|
| - {'mixinName1': current.name, 'mixinName2': previous.name});
|
| - // We have found a cycle in the mixin chain. Return null as
|
| - // the mixin for this application to avoid getting into
|
| - // infinite recursion when traversing members.
|
| - return null;
|
| - }
|
| - previous = current;
|
| - current = currentMixinApplication.mixin;
|
| - }
|
| - registry.registerMixinUse(mixinApplication, mixin);
|
| - return mixinType;
|
| - }
|
| -
|
| - DartType resolveType(TypeAnnotation node) {
|
| - return typeResolver.resolveTypeAnnotation(this, node);
|
| - }
|
| -
|
| - DartType resolveSupertype(ClassElement cls, TypeAnnotation superclass) {
|
| - DartType supertype = resolveType(superclass);
|
| - if (supertype != null) {
|
| - if (identical(supertype.kind, TypeKind.MALFORMED_TYPE)) {
|
| - compiler.reportError(superclass, MessageKind.CANNOT_EXTEND_MALFORMED,
|
| - {'className': element.name, 'malformedType': supertype});
|
| - return objectType;
|
| - } else if (!identical(supertype.kind, TypeKind.INTERFACE)) {
|
| - compiler.reportError(superclass.typeName,
|
| - MessageKind.CLASS_NAME_EXPECTED);
|
| - return objectType;
|
| - } else if (isBlackListed(supertype)) {
|
| - compiler.reportError(superclass, MessageKind.CANNOT_EXTEND,
|
| - {'type': supertype});
|
| - return objectType;
|
| - }
|
| - }
|
| - return supertype;
|
| - }
|
| -
|
| - Link<DartType> resolveInterfaces(NodeList interfaces, Node superclass) {
|
| - Link<DartType> result = const Link<DartType>();
|
| - if (interfaces == null) return result;
|
| - for (Link<Node> link = interfaces.nodes; !link.isEmpty; link = link.tail) {
|
| - DartType interfaceType = resolveType(link.head);
|
| - if (interfaceType != null) {
|
| - if (identical(interfaceType.kind, TypeKind.MALFORMED_TYPE)) {
|
| - compiler.reportError(superclass,
|
| - MessageKind.CANNOT_IMPLEMENT_MALFORMED,
|
| - {'className': element.name, 'malformedType': interfaceType});
|
| - } else if (!identical(interfaceType.kind, TypeKind.INTERFACE)) {
|
| - // TODO(johnniwinther): Handle dynamic.
|
| - TypeAnnotation typeAnnotation = link.head;
|
| - error(typeAnnotation.typeName, MessageKind.CLASS_NAME_EXPECTED);
|
| - } else {
|
| - if (interfaceType == element.supertype) {
|
| - compiler.reportError(
|
| - superclass,
|
| - MessageKind.DUPLICATE_EXTENDS_IMPLEMENTS,
|
| - {'type': interfaceType});
|
| - compiler.reportError(
|
| - link.head,
|
| - MessageKind.DUPLICATE_EXTENDS_IMPLEMENTS,
|
| - {'type': interfaceType});
|
| - }
|
| - if (result.contains(interfaceType)) {
|
| - compiler.reportError(
|
| - link.head,
|
| - MessageKind.DUPLICATE_IMPLEMENTS,
|
| - {'type': interfaceType});
|
| - }
|
| - result = result.prepend(interfaceType);
|
| - if (isBlackListed(interfaceType)) {
|
| - error(link.head, MessageKind.CANNOT_IMPLEMENT,
|
| - {'type': interfaceType});
|
| - }
|
| - }
|
| - }
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Compute the list of all supertypes.
|
| - *
|
| - * The elements of this list are ordered as follows: first the supertype that
|
| - * the class extends, then the implemented interfaces, and then the supertypes
|
| - * of these. The class [Object] appears only once, at the end of the list.
|
| - *
|
| - * For example, for a class `class C extends S implements I1, I2`, we compute
|
| - * supertypes(C) = [S, I1, I2] ++ supertypes(S) ++ supertypes(I1)
|
| - * ++ supertypes(I2),
|
| - * where ++ stands for list concatenation.
|
| - *
|
| - * This order makes sure that if a class implements an interface twice with
|
| - * different type arguments, the type used in the most specific class comes
|
| - * first.
|
| - */
|
| - void calculateAllSupertypes(BaseClassElementX cls) {
|
| - if (cls.allSupertypesAndSelf != null) return;
|
| - final DartType supertype = cls.supertype;
|
| - if (supertype != null) {
|
| - OrderedTypeSetBuilder allSupertypes = new OrderedTypeSetBuilder(cls);
|
| - // TODO(15296): Collapse these iterations to one when the order is not
|
| - // needed.
|
| - allSupertypes.add(compiler, supertype);
|
| - for (Link<DartType> interfaces = cls.interfaces;
|
| - !interfaces.isEmpty;
|
| - interfaces = interfaces.tail) {
|
| - allSupertypes.add(compiler, interfaces.head);
|
| - }
|
| -
|
| - addAllSupertypes(allSupertypes, supertype);
|
| - for (Link<DartType> interfaces = cls.interfaces;
|
| - !interfaces.isEmpty;
|
| - interfaces = interfaces.tail) {
|
| - addAllSupertypes(allSupertypes, interfaces.head);
|
| - }
|
| - allSupertypes.add(compiler, cls.computeType(compiler));
|
| - cls.allSupertypesAndSelf = allSupertypes.toTypeSet();
|
| - } else {
|
| - assert(identical(cls, compiler.objectClass));
|
| - cls.allSupertypesAndSelf =
|
| - new OrderedTypeSet.singleton(cls.computeType(compiler));
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Adds [type] and all supertypes of [type] to [allSupertypes] while
|
| - * substituting type variables.
|
| - */
|
| - void addAllSupertypes(OrderedTypeSetBuilder allSupertypes,
|
| - InterfaceType type) {
|
| - ClassElement classElement = type.element;
|
| - Link<DartType> supertypes = classElement.allSupertypes;
|
| - assert(invariant(element, supertypes != null,
|
| - message: "Supertypes not computed on $classElement "
|
| - "during resolution of $element"));
|
| - while (!supertypes.isEmpty) {
|
| - DartType supertype = supertypes.head;
|
| - allSupertypes.add(compiler, supertype.substByContext(type));
|
| - supertypes = supertypes.tail;
|
| - }
|
| - }
|
| -
|
| - isBlackListed(DartType type) {
|
| - LibraryElement lib = element.library;
|
| - return
|
| - !identical(lib, compiler.coreLibrary) &&
|
| - !compiler.backend.isBackendLibrary(lib) &&
|
| - (type.isDynamic ||
|
| - identical(type.element, compiler.boolClass) ||
|
| - identical(type.element, compiler.numClass) ||
|
| - identical(type.element, compiler.intClass) ||
|
| - identical(type.element, compiler.doubleClass) ||
|
| - identical(type.element, compiler.stringClass) ||
|
| - identical(type.element, compiler.nullClass));
|
| - }
|
| -}
|
| -
|
| -class ClassSupertypeResolver extends CommonResolverVisitor {
|
| - Scope context;
|
| - ClassElement classElement;
|
| -
|
| - ClassSupertypeResolver(Compiler compiler, ClassElement cls)
|
| - : context = Scope.buildEnclosingScope(cls),
|
| - this.classElement = cls,
|
| - super(compiler);
|
| -
|
| - void loadSupertype(ClassElement element, Node from) {
|
| - compiler.resolver.loadSupertypes(element, from);
|
| - element.ensureResolved(compiler);
|
| - }
|
| -
|
| - void visitNodeList(NodeList node) {
|
| - if (node != null) {
|
| - for (Link<Node> link = node.nodes; !link.isEmpty; link = link.tail) {
|
| - link.head.accept(this);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void visitClassNode(ClassNode node) {
|
| - if (node.superclass == null) {
|
| - if (!identical(classElement, compiler.objectClass)) {
|
| - loadSupertype(compiler.objectClass, node);
|
| - }
|
| - } else {
|
| - node.superclass.accept(this);
|
| - }
|
| - visitNodeList(node.interfaces);
|
| - }
|
| -
|
| - void visitMixinApplication(MixinApplication node) {
|
| - node.superclass.accept(this);
|
| - visitNodeList(node.mixins);
|
| - }
|
| -
|
| - void visitNamedMixinApplication(NamedMixinApplication node) {
|
| - node.superclass.accept(this);
|
| - visitNodeList(node.mixins);
|
| - visitNodeList(node.interfaces);
|
| - }
|
| -
|
| - void visitTypeAnnotation(TypeAnnotation node) {
|
| - node.typeName.accept(this);
|
| - }
|
| -
|
| - void visitIdentifier(Identifier node) {
|
| - Element element = lookupInScope(compiler, node, context, node.source);
|
| - if (element != null && element.isClass) {
|
| - loadSupertype(element, node);
|
| - }
|
| - }
|
| -
|
| - void visitSend(Send node) {
|
| - Identifier prefix = node.receiver.asIdentifier();
|
| - if (prefix == null) {
|
| - error(node.receiver, MessageKind.NOT_A_PREFIX, {'node': node.receiver});
|
| - return;
|
| - }
|
| - Element element = lookupInScope(compiler, prefix, context, prefix.source);
|
| - if (element == null || !identical(element.kind, ElementKind.PREFIX)) {
|
| - error(node.receiver, MessageKind.NOT_A_PREFIX, {'node': node.receiver});
|
| - return;
|
| - }
|
| - 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,
|
| - {'typeName': node.selector});
|
| - return;
|
| - }
|
| - loadSupertype(e, node);
|
| - }
|
| -}
|
| -
|
| -class VariableDefinitionsVisitor extends CommonResolverVisitor<Identifier> {
|
| - VariableDefinitions definitions;
|
| - ResolverVisitor resolver;
|
| - VariableList variables;
|
| -
|
| - VariableDefinitionsVisitor(Compiler compiler,
|
| - this.definitions,
|
| - this.resolver,
|
| - this.variables)
|
| - : super(compiler) {
|
| - }
|
| -
|
| - ResolutionRegistry get registry => resolver.registry;
|
| -
|
| - Identifier visitSendSet(SendSet node) {
|
| - assert(node.arguments.tail.isEmpty); // Sanity check
|
| - Identifier identifier = node.selector;
|
| - String name = identifier.source;
|
| - VariableDefinitionScope scope =
|
| - new VariableDefinitionScope(resolver.scope, name);
|
| - resolver.visitIn(node.arguments.head, scope);
|
| - if (scope.variableReferencedInInitializer) {
|
| - compiler.reportError(
|
| - identifier, MessageKind.REFERENCE_IN_INITIALIZATION,
|
| - {'variableName': name});
|
| - }
|
| - return identifier;
|
| - }
|
| -
|
| - Identifier visitIdentifier(Identifier node) {
|
| - // The variable is initialized to null.
|
| - registry.registerInstantiatedClass(compiler.nullClass);
|
| - if (definitions.modifiers.isConst) {
|
| - compiler.reportError(node, MessageKind.CONST_WITHOUT_INITIALIZER);
|
| - }
|
| - if (definitions.modifiers.isFinal &&
|
| - !resolver.allowFinalWithoutInitializer) {
|
| - compiler.reportError(node, MessageKind.FINAL_WITHOUT_INITIALIZER);
|
| - }
|
| - return node;
|
| - }
|
| -
|
| - visitNodeList(NodeList node) {
|
| - for (Link<Node> link = node.nodes; !link.isEmpty; link = link.tail) {
|
| - Identifier name = visit(link.head);
|
| - LocalVariableElement element = new LocalVariableElementX(
|
| - name.source, resolver.enclosingElement,
|
| - variables, name.token);
|
| - resolver.defineLocalVariable(link.head, element);
|
| - resolver.addToScope(element);
|
| - if (definitions.modifiers.isConst) {
|
| - compiler.enqueuer.resolution.addDeferredAction(element, () {
|
| - compiler.resolver.constantCompiler.compileConstant(element);
|
| - });
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -class ConstructorResolver extends CommonResolverVisitor<Element> {
|
| - final ResolverVisitor resolver;
|
| - bool inConstContext;
|
| - DartType type;
|
| -
|
| - ConstructorResolver(Compiler compiler, this.resolver,
|
| - {bool this.inConstContext: false})
|
| - : super(compiler);
|
| -
|
| - ResolutionRegistry get registry => resolver.registry;
|
| -
|
| - visitNode(Node node) {
|
| - throw 'not supported';
|
| - }
|
| -
|
| - failOrReturnErroneousElement(Element enclosing, Node diagnosticNode,
|
| - String targetName, MessageKind kind,
|
| - Map arguments) {
|
| - if (kind == MessageKind.CANNOT_FIND_CONSTRUCTOR) {
|
| - registry.registerThrowNoSuchMethod();
|
| - } else {
|
| - registry.registerThrowRuntimeError();
|
| - }
|
| - if (inConstContext) {
|
| - compiler.reportError(diagnosticNode, kind, arguments);
|
| - } else {
|
| - compiler.reportWarning(diagnosticNode, kind, arguments);
|
| - }
|
| - return new ErroneousElementX(kind, arguments, targetName, enclosing);
|
| - }
|
| -
|
| - Selector createConstructorSelector(String constructorName) {
|
| - return constructorName == ''
|
| - ? new Selector.callDefaultConstructor(
|
| - resolver.enclosingElement.library)
|
| - : new Selector.callConstructor(
|
| - constructorName,
|
| - resolver.enclosingElement.library);
|
| - }
|
| -
|
| - FunctionElement resolveConstructor(ClassElement cls,
|
| - Node diagnosticNode,
|
| - String constructorName) {
|
| - cls.ensureResolved(compiler);
|
| - Selector selector = createConstructorSelector(constructorName);
|
| - Element result = cls.lookupConstructor(selector);
|
| - if (result == null) {
|
| - String fullConstructorName = Elements.constructorNameForDiagnostics(
|
| - cls.name,
|
| - constructorName);
|
| - return failOrReturnErroneousElement(
|
| - cls,
|
| - diagnosticNode,
|
| - fullConstructorName,
|
| - MessageKind.CANNOT_FIND_CONSTRUCTOR,
|
| - {'constructorName': fullConstructorName});
|
| - } else if (inConstContext && !result.isConst) {
|
| - error(diagnosticNode, MessageKind.CONSTRUCTOR_IS_NOT_CONST);
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - Element visitNewExpression(NewExpression node) {
|
| - inConstContext = node.isConst;
|
| - Node selector = node.send.selector;
|
| - Element element = visit(selector);
|
| - assert(invariant(selector, element != null,
|
| - message: 'No element return for $selector.'));
|
| - return finishConstructorReference(element, node.send.selector, node);
|
| - }
|
| -
|
| - /// Finishes resolution of a constructor reference and records the
|
| - /// type of the constructed instance on [expression].
|
| - FunctionElement finishConstructorReference(Element element,
|
| - Node diagnosticNode,
|
| - Node expression) {
|
| - assert(invariant(diagnosticNode, element != null,
|
| - message: 'No element return for $diagnosticNode.'));
|
| - // Find the unnamed constructor if the reference resolved to a
|
| - // class.
|
| - if (!Elements.isUnresolved(element) && !element.isConstructor) {
|
| - if (element.isClass) {
|
| - ClassElement cls = element;
|
| - cls.ensureResolved(compiler);
|
| - // The unnamed constructor may not exist, so [e] may become unresolved.
|
| - element = resolveConstructor(cls, diagnosticNode, '');
|
| - } else {
|
| - element = failOrReturnErroneousElement(
|
| - element, diagnosticNode, element.name, MessageKind.NOT_A_TYPE,
|
| - {'node': diagnosticNode});
|
| - }
|
| - }
|
| - if (type == null) {
|
| - if (Elements.isUnresolved(element)) {
|
| - type = const DynamicType();
|
| - } else {
|
| - type = element.enclosingClass.rawType;
|
| - }
|
| - }
|
| - resolver.registry.setType(expression, type);
|
| - return element;
|
| - }
|
| -
|
| - Element visitTypeAnnotation(TypeAnnotation node) {
|
| - assert(invariant(node, type == null));
|
| - // This is not really resolving a type-annotation, but the name of the
|
| - // constructor. Therefore we allow deferred types.
|
| - type = resolver.resolveTypeAnnotation(node,
|
| - malformedIsError: inConstContext,
|
| - deferredIsMalformed: false);
|
| - registry.registerRequiredType(type, resolver.enclosingElement);
|
| - return type.element;
|
| - }
|
| -
|
| - Element visitSend(Send node) {
|
| - Element element = visit(node.receiver);
|
| - assert(invariant(node.receiver, element != null,
|
| - message: 'No element return for $node.receiver.'));
|
| - if (Elements.isUnresolved(element)) return element;
|
| - Identifier name = node.selector.asIdentifier();
|
| - if (name == null) internalError(node.selector, 'unexpected node');
|
| -
|
| - if (element.isClass) {
|
| - ClassElement cls = element;
|
| - cls.ensureResolved(compiler);
|
| - return resolveConstructor(cls, name, name.source);
|
| - } else if (element.isPrefix) {
|
| - PrefixElement prefix = element;
|
| - element = prefix.lookupLocalMember(name.source);
|
| - element = Elements.unwrap(element, compiler, node);
|
| - if (element == null) {
|
| - return failOrReturnErroneousElement(
|
| - resolver.enclosingElement, name,
|
| - name.source,
|
| - MessageKind.CANNOT_RESOLVE,
|
| - {'name': name});
|
| - } else if (!element.isClass) {
|
| - error(node, MessageKind.NOT_A_TYPE, {'node': name});
|
| - }
|
| - } else {
|
| - internalError(node.receiver, 'unexpected element $element');
|
| - }
|
| - return element;
|
| - }
|
| -
|
| - Element visitIdentifier(Identifier node) {
|
| - String name = node.source;
|
| - Element element = resolver.reportLookupErrorIfAny(
|
| - lookupInScope(compiler, node, resolver.scope, name), node, name);
|
| - registry.useElement(node, element);
|
| - // TODO(johnniwinther): Change errors to warnings, cf. 11.11.1.
|
| - if (element == null) {
|
| - return failOrReturnErroneousElement(resolver.enclosingElement, node, name,
|
| - MessageKind.CANNOT_RESOLVE,
|
| - {'name': name});
|
| - } else if (element.isErroneous) {
|
| - return element;
|
| - } else if (element.isTypedef) {
|
| - error(node, MessageKind.CANNOT_INSTANTIATE_TYPEDEF,
|
| - {'typedefName': name});
|
| - } else if (element.isTypeVariable) {
|
| - error(node, MessageKind.CANNOT_INSTANTIATE_TYPE_VARIABLE,
|
| - {'typeVariableName': name});
|
| - } else if (!element.isClass && !element.isPrefix) {
|
| - error(node, MessageKind.NOT_A_TYPE, {'node': name});
|
| - }
|
| - return element;
|
| - }
|
| -
|
| - /// Assumed to be called by [resolveRedirectingFactory].
|
| - Element visitRedirectingFactoryBody(RedirectingFactoryBody node) {
|
| - Node constructorReference = node.constructorReference;
|
| - return finishConstructorReference(visit(constructorReference),
|
| - constructorReference, node);
|
| - }
|
| -}
|
| -
|
| -/// Looks up [name] in [scope] and unwraps the result.
|
| -Element lookupInScope(Compiler compiler, Node node,
|
| - Scope scope, String name) {
|
| - return Elements.unwrap(scope.lookup(name), compiler, node);
|
| -}
|
| -
|
| -TreeElements _ensureTreeElements(AnalyzableElementX element) {
|
| - if (element._treeElements == null) {
|
| - element._treeElements = new TreeElementMapping(element);
|
| - }
|
| - return element._treeElements;
|
| -}
|
| -
|
| -abstract class AnalyzableElementX implements AnalyzableElement {
|
| - TreeElements _treeElements;
|
| -
|
| - bool get hasTreeElements => _treeElements != null;
|
| -
|
| - TreeElements get treeElements {
|
| - assert(invariant(this, _treeElements !=null,
|
| - message: "TreeElements have not been computed for $this."));
|
| - return _treeElements;
|
| - }
|
| -
|
| - void reuseElement() {
|
| - _treeElements = null;
|
| - }
|
| -}
|
| -
|
| -/// The result of resolving a node.
|
| -abstract class ResolutionResult {
|
| - Element get element;
|
| -}
|
| -
|
| -/// The result for the resolution of a node that points to an [Element].
|
| -class ElementResult implements ResolutionResult {
|
| - final Element element;
|
| -
|
| - // TODO(johnniwinther): Remove this factory constructor when `null` is never
|
| - // passed as an element result.
|
| - factory ElementResult(Element element) {
|
| - return element != null ? new ElementResult.internal(element) : null;
|
| - }
|
| -
|
| - ElementResult.internal(this.element);
|
| -
|
| - String toString() => 'ElementResult($element)';
|
| -}
|
| -
|
| -/// The result for the resolution of a node that points to an [DartType].
|
| -class TypeResult implements ResolutionResult {
|
| - final DartType type;
|
| -
|
| - TypeResult(this.type) {
|
| - assert(type != null);
|
| - }
|
| -
|
| - Element get element => type.element;
|
| -
|
| - String toString() => 'TypeResult($type)';
|
| -}
|
| -
|
| -/// The result for the resolution of the `assert` method.
|
| -class AssertResult implements ResolutionResult {
|
| - const AssertResult();
|
| -
|
| - Element get element => null;
|
| -
|
| - String toString() => 'AssertResult()';
|
| -}
|
|
|