Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Unified Diff: pkg/compiler/lib/src/resolution/resolution.dart

Issue 1286993004: Split resolution into several libraries. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
+ }
+}
« no previous file with comments | « pkg/compiler/lib/src/resolution/registry.dart ('k') | pkg/compiler/lib/src/resolution/resolution_common.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698