| Index: pkg/compiler/lib/src/resolution/resolution_common.dart
|
| diff --git a/pkg/compiler/lib/src/resolution/resolution_common.dart b/pkg/compiler/lib/src/resolution/resolution_common.dart
|
| index 0f1a7784a4b65d976028c1acf95e1ae3e6e7fef5..67974330fcc28a1527e749345202ae0f364aeda5 100644
|
| --- a/pkg/compiler/lib/src/resolution/resolution_common.dart
|
| +++ b/pkg/compiler/lib/src/resolution/resolution_common.dart
|
| @@ -2,968 +2,25 @@
|
| // 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;
|
| -
|
| -class ResolverTask extends CompilerTask {
|
| - final ConstantCompiler constantCompiler;
|
| -
|
| - ResolverTask(Compiler compiler, this.constantCompiler) : super(compiler);
|
| -
|
| - String get name => 'Resolver';
|
| -
|
| - WorldImpact resolve(Element element) {
|
| - return measure(() {
|
| - if (Elements.isErroneous(element)) {
|
| - // TODO(johnniwinther): Add a predicate for this.
|
| - assert(invariant(element, element is! ErroneousElement,
|
| - message: "Element $element expected to have parse errors."));
|
| - _ensureTreeElements(element);
|
| - return const WorldImpact();
|
| - }
|
| -
|
| - WorldImpact processMetadata([WorldImpact 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(const WorldImpact());
|
| - } 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);
|
| - }
|
| - }
|
| -
|
| - static void processAsyncMarker(Compiler compiler,
|
| - BaseFunctionElementX element,
|
| - ResolutionRegistry registry) {
|
| - FunctionExpression functionExpression = element.node;
|
| - AsyncModifier asyncModifier = functionExpression.asyncModifier;
|
| - if (asyncModifier != null) {
|
| -
|
| - 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 (element.isSetter) {
|
| - compiler.reportError(asyncModifier,
|
| - MessageKind.ASYNC_MODIFIER_ON_SETTER,
|
| - {'modifier': element.asyncMarker});
|
| -
|
| - }
|
| - if (functionExpression.body.asReturn() != null &&
|
| - element.asyncMarker.isYielding) {
|
| - compiler.reportError(asyncModifier,
|
| - MessageKind.YIELDING_MODIFIER_ON_ARROW_BODY,
|
| - {'modifier': element.asyncMarker});
|
| - }
|
| - }
|
| - registry.registerAsyncMarker(element);
|
| - switch (element.asyncMarker) {
|
| - case AsyncMarker.ASYNC:
|
| - compiler.futureClass.ensureResolved(compiler);
|
| - break;
|
| - case AsyncMarker.ASYNC_STAR:
|
| - compiler.streamClass.ensureResolved(compiler);
|
| - break;
|
| - case AsyncMarker.SYNC_STAR:
|
| - compiler.iterableClass.ensureResolved(compiler);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - bool _isNativeClassOrExtendsNativeClass(ClassElement classElement) {
|
| - assert(classElement != null);
|
| - while (classElement != null) {
|
| - if (classElement.isNative) return true;
|
| - classElement = classElement.superclass;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - WorldImpact resolveMethodElementImplementation(
|
| - FunctionElement element, FunctionExpression tree) {
|
| - return compiler.withCurrentElement(element, () {
|
| - if (element.isExternal && tree.hasBody()) {
|
| - error(element,
|
| - MessageKind.EXTERNAL_WITH_BODY,
|
| - {'functionName': element.name});
|
| - }
|
| - if (element.isConstructor) {
|
| - if (tree.returnType != null) {
|
| - error(tree, MessageKind.CONSTRUCTOR_WITH_RETURN_TYPE);
|
| - }
|
| - if (element.isConst &&
|
| - tree.hasBody() &&
|
| - !tree.isRedirectingFactory) {
|
| - error(tree, MessageKind.CONST_CONSTRUCTOR_HAS_BODY);
|
| - }
|
| - }
|
| -
|
| - ResolverVisitor visitor = visitorFor(element);
|
| - ResolutionRegistry registry = visitor.registry;
|
| - registry.defineFunction(tree, element);
|
| - visitor.setupFunction(tree, element);
|
| - processAsyncMarker(compiler, element, registry);
|
| -
|
| - if (element.isGenerativeConstructor) {
|
| - // 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, element, tree);
|
| - FunctionElement redirection = resolver.resolveInitializers();
|
| - if (redirection != null) {
|
| - resolveRedirectingConstructor(resolver, tree, element, redirection);
|
| - }
|
| - } 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);
|
| - }
|
| - }
|
| -
|
| - // TODO(9631): support noSuchMethod on native classes.
|
| - if (Elements.isInstanceMethod(element) &&
|
| - element.name == Compiler.NO_SUCH_METHOD &&
|
| - _isNativeClassOrExtendsNativeClass(enclosingClass)) {
|
| - error(tree, MessageKind.NO_SUCH_METHOD_IN_NATIVE);
|
| - }
|
| -
|
| - return registry.worldImpact;
|
| - });
|
| -
|
| - }
|
| -
|
| - WorldImpact resolveMethodElement(FunctionElementX element) {
|
| - assert(invariant(element, element.isDeclaration));
|
| - return compiler.withCurrentElement(element, () {
|
| - 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 const WorldImpact();
|
| - }
|
| - if (element.isSynthesized) {
|
| - if (element.isGenerativeConstructor) {
|
| - 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.worldImpact;
|
| - } else {
|
| - assert(element.isDeferredLoaderGetter || element.isErroneous);
|
| - _ensureTreeElements(element);
|
| - return const WorldImpact();
|
| - }
|
| - } else {
|
| - element.parseNode(compiler);
|
| - element.computeType(compiler);
|
| - FunctionElementX implementation = element;
|
| - if (element.isExternal) {
|
| - implementation = compiler.backend.resolveExternalFunction(element);
|
| - }
|
| - return resolveMethodElementImplementation(
|
| - implementation, implementation.node);
|
| - }
|
| - });
|
| - }
|
| -
|
| - /// 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);
|
| - }
|
| -
|
| - WorldImpact 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) {
|
| - element.constant = 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.worldImpact;
|
| - }
|
| -
|
| - 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);
|
| - targetType = target.enclosingClass.thisType;
|
| - 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;
|
| - DartType factoryType = treeElements.getType(redirectionNode);
|
| - if (!factoryType.isDynamic) {
|
| - 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);
|
| - ConstantValue value =
|
| - compiler.constants.getConstantValue(metadata.constant);
|
| - if (!element.isProxy && 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;
|
| - }
|
| -
|
| - if (mixin.isEnumClass) {
|
| - // Mixing in an enum has already caused a compile-time error.
|
| - 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)) {
|
| - if (lookupElement.isErroneous || lookupElement.isAmbiguous) return;
|
| - compiler.internalError(member,
|
| - "Inaccessible abstract field for accessor");
|
| - }
|
| - AbstractFieldElement field = lookupElement;
|
| -
|
| - GetterElementX getter = field.getter;
|
| - if (getter == null) return;
|
| - SetterElementX 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));
|
| - });
|
| - }
|
| -
|
| - WorldImpact resolveTypedef(TypedefElementX element) {
|
| - if (element.isResolved) return const WorldImpact();
|
| - 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.worldImpact;
|
| - });
|
| - });
|
| - });
|
| - }
|
| -
|
| - 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 {}]) {
|
| - compiler.reportError(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();
|
| - }
|
| -}
|
| +library dart2js.resolution.common;
|
| +
|
| +import '../common/tasks.dart' show
|
| + DeferredAction;
|
| +import '../compiler.dart' show
|
| + Compiler;
|
| +import '../diagnostics/messages.dart' show
|
| + MessageKind;
|
| +import '../diagnostics/spannable.dart' show
|
| + Spannable;
|
| +import '../elements/elements.dart';
|
| +import '../tree/tree.dart';
|
| +
|
| +import 'registry.dart' show
|
| + ResolutionRegistry;
|
| +import 'scope.dart' show
|
| + Scope;
|
| +import 'type_resolver.dart' show
|
| + TypeResolver;
|
|
|
| class CommonResolverVisitor<R> extends Visitor<R> {
|
| final Compiler compiler;
|
|
|