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; |
+ } |
+} |