Chromium Code Reviews| Index: pkg/compiler/lib/src/resolution/resolution.dart |
| diff --git a/pkg/compiler/lib/src/resolution/resolution.dart b/pkg/compiler/lib/src/resolution/resolution.dart |
| index 73efaf40a4b46ac0202338c2fb2842f7a9fba676..3209c07cbbcdbff3f0ad97d4ffc656bf73075bf2 100644 |
| --- a/pkg/compiler/lib/src/resolution/resolution.dart |
| +++ b/pkg/compiler/lib/src/resolution/resolution.dart |
| @@ -2,95 +2,1044 @@ |
| // 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. |
| -library resolution; |
| +library dart2js.resolution; |
| import 'dart:collection' show Queue; |
| -import '../common/backend_api.dart' show |
| - Backend; |
| -import '../common/registry.dart' show |
| - Registry; |
| import '../common/tasks.dart' show |
| CompilerTask, |
| DeferredAction; |
| import '../compiler.dart' show |
| - Compiler, |
| - isPrivateName; |
| -import '../compile_time_constants.dart'; |
| -import '../constants/constructors.dart'; |
| -import '../constants/expressions.dart'; |
| -import '../constants/values.dart'; |
| -import '../core_types.dart'; |
| -import '../dart_backend/dart_backend.dart' show DartBackend; |
| + Compiler; |
| +import '../compile_time_constants.dart' show |
| + ConstantCompiler; |
| +import '../constants/values.dart' show |
| + ConstantValue; |
| import '../dart_types.dart'; |
| import '../diagnostics/invariant.dart' show |
| invariant; |
| -import '../diagnostics/messages.dart' show MessageKind; |
| +import '../diagnostics/messages.dart' show |
| + MessageKind; |
| import '../diagnostics/spannable.dart' show |
| Spannable; |
| -import '../enqueue.dart' show |
| - ResolutionEnqueuer, |
| - WorldImpact; |
| -import '../tree/tree.dart'; |
| -import '../scanner/scannerlib.dart'; |
| import '../elements/elements.dart'; |
| import '../elements/modelx.dart' show |
| BaseClassElementX, |
| BaseFunctionElementX, |
| ConstructorElementX, |
| - ErroneousConstructorElementX, |
| - ErroneousElementX, |
| - ErroneousFieldElementX, |
| - ErroneousInitializingFormalElementX, |
| FieldElementX, |
| - FormalElementX, |
| FunctionElementX, |
| - FunctionSignatureX, |
| GetterElementX, |
| - InitializingFormalElementX, |
| - JumpTargetX, |
| - LabelDefinitionX, |
| - LocalFunctionElementX, |
| - LocalParameterElementX, |
| - LocalVariableElementX, |
| MetadataAnnotationX, |
| - MethodElementX, |
| MixinApplicationElementX, |
| - ParameterElementX, |
| ParameterMetadataAnnotation, |
| SetterElementX, |
| - SynthesizedConstructorElementX, |
| - TypeVariableElementX, |
| - TypedefElementX, |
| - VariableElementX, |
| - VariableList; |
| -import '../ordered_typeset.dart' show OrderedTypeSet, OrderedTypeSetBuilder; |
| -import '../types/types.dart' show TypeMask; |
| -import '../util/util.dart'; |
| -import '../universe/universe.dart' show |
| - CallStructure, |
| - Selector, |
| - SelectorKind, |
| - UniverseSelector; |
| -import '../world.dart' show World; |
| - |
| -import 'access_semantics.dart'; |
| + TypedefElementX; |
| +import '../enqueue.dart' show |
| + WorldImpact; |
| +import '../scanner/scannerlib.dart' show |
| + isBinaryOperator, |
| + isMinusOperator, |
| + isTernaryOperator, |
| + isUnaryOperator, |
| + isUserDefinableOperator; |
| +import '../tree/tree.dart'; |
| +import '../util/util.dart' show |
| + Link, |
| + LinkBuilder, |
| + Setlet; |
| + |
| +import 'class_hierarchy.dart'; |
| import 'class_members.dart' show MembersCreator; |
| -import 'enum_creator.dart'; |
| -import 'operators.dart'; |
| -import 'secret_tree_element.dart' show getTreeElement, setTreeElement; |
| -import 'send_structure.dart'; |
| - |
| -part 'class_hierarchy.dart'; |
| -part 'constructors.dart'; |
| -part 'label_scope.dart'; |
| -part 'members.dart'; |
| -part 'registry.dart'; |
| -part 'resolution_common.dart'; |
| -part 'resolution_result.dart'; |
| -part 'scope.dart'; |
| -part 'signatures.dart'; |
| -part 'tree_elements.dart'; |
| -part 'typedefs.dart'; |
| -part 'type_resolver.dart'; |
| -part 'variables.dart'; |
| +import 'constructors.dart'; |
| +import 'members.dart'; |
| +import 'registry.dart'; |
| +import 'signatures.dart'; |
| +import 'tree_elements.dart'; |
| +import 'typedefs.dart'; |
| + |
|
Johnni Winther
2015/08/17 08:01:34
The rest of the file is moved here from resolver_c
|
| +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, _ensureTreeElements(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, _ensureTreeElements(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, _ensureTreeElements(element)); |
| + resolveClassInternal(element, registry); |
| + return element.treeElements; |
| + }); |
| + } |
| + |
| + void ensureClassWillBeResolvedInternal(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, _ensureTreeElements(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, _ensureTreeElements(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(); |
| + } |
| +} |
| + |
| +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; |
| + } |
| +} |