Index: pkg/compiler/lib/src/resolution/send_resolver.dart |
diff --git a/pkg/compiler/lib/src/resolution/send_resolver.dart b/pkg/compiler/lib/src/resolution/send_resolver.dart |
index 49369ccf692c40b15d17d5115797cf1e32f5cc9e..efd92cf2724e4eba6f5e8ac476dc0e7f5cff6d8a 100644 |
--- a/pkg/compiler/lib/src/resolution/send_resolver.dart |
+++ b/pkg/compiler/lib/src/resolution/send_resolver.dart |
@@ -6,6 +6,8 @@ library dart2js.semantics_visitor.resolver; |
import '../constants/expressions.dart'; |
import '../dart_types.dart'; |
+import '../diagnostics/invariant.dart' show |
+ invariant; |
import '../diagnostics/messages.dart' show |
MessageKind; |
import '../diagnostics/spannable.dart' show |
@@ -16,15 +18,548 @@ import '../tree/tree.dart'; |
import '../universe/universe.dart'; |
import 'access_semantics.dart'; |
+import 'operators.dart'; |
import 'semantic_visitor.dart'; |
import 'send_structure.dart'; |
import 'tree_elements.dart'; |
+enum SendStructureKind { |
+ GET, |
+ SET, |
+ INVOKE, |
+ UNARY, |
+ NOT, |
+ BINARY, |
+ EQ, |
+ NOT_EQ, |
+ COMPOUND, |
+ INDEX, |
+ INDEX_SET, |
+ COMPOUND_INDEX_SET, |
+ PREFIX, |
+ POSTFIX, |
+ INDEX_PREFIX, |
+ INDEX_POSTFIX, |
+} |
+ |
abstract class SendResolverMixin { |
TreeElements get elements; |
internalError(Spannable spannable, String message); |
+ AccessSemantics handleCompoundErroneousSetterAccess( |
+ Send node, |
+ Element setter, |
+ Element getter) { |
+ assert(invariant(node, Elements.isUnresolved(setter), |
+ message: "Unexpected erreneous compound setter: $setter.")); |
+ if (getter.isStatic) { |
+ if (getter.isGetter) { |
+ return new CompoundAccessSemantics( |
+ CompoundAccessKind.UNRESOLVED_STATIC_SETTER, getter, setter); |
+ } else if (getter.isField) { |
+ // TODO(johnniwinther): Handle const field separately. |
+ assert(invariant(node, getter.isFinal || getter.isConst, |
+ message: "Field expected to be final or const.")); |
+ return new StaticAccess.finalStaticField(getter); |
+ } else if (getter.isFunction) { |
+ return new StaticAccess.staticMethod(getter); |
+ } else { |
+ return internalError(node, |
+ "Unexpected erroneous static compound: getter=$getter"); |
+ } |
+ } else if (getter.isTopLevel) { |
+ if (getter.isGetter) { |
+ return new CompoundAccessSemantics( |
+ CompoundAccessKind.UNRESOLVED_TOPLEVEL_SETTER, getter, setter); |
+ } else if (getter.isField) { |
+ // TODO(johnniwinther): Handle const field separately. |
+ assert(invariant(node, getter.isFinal || getter.isConst, |
+ message: "Field expected to be final or const.")); |
+ return new StaticAccess.finalTopLevelField(getter); |
+ } else if (getter.isFunction) { |
+ return new StaticAccess.topLevelMethod(getter); |
+ } else { |
+ return internalError(node, |
+ "Unexpected erroneous top level compound: getter=$getter"); |
+ } |
+ } else if (getter.isParameter) { |
+ assert(invariant(node, getter.isFinal, |
+ message: "Parameter expected to be final.")); |
+ return new StaticAccess.finalParameter(getter); |
+ } else if (getter.isLocal) { |
+ if (getter.isVariable) { |
+ // TODO(johnniwinther): Handle const variable separately. |
+ assert(invariant(node, getter.isFinal || getter.isConst, |
+ message: "Variable expected to be final or const.")); |
+ return new StaticAccess.finalLocalVariable(getter); |
+ } else if (getter.isFunction) { |
+ return new StaticAccess.localFunction(getter); |
+ } else { |
+ return internalError(node, |
+ "Unexpected erroneous local compound: getter=$getter"); |
+ } |
+ } else if (getter.isErroneous) { |
+ return new StaticAccess.unresolved(getter); |
+ } else { |
+ return internalError(node, |
+ "Unexpected erroneous compound: getter=$getter"); |
+ } |
+ } |
+ |
+ AccessSemantics handleStaticallyResolvedAccess( |
+ Send node, |
+ Element element, |
+ Element getter, |
+ {bool isCompound}) { |
+ if (element == null) { |
+ assert(invariant(node, isCompound, message: |
+ "Non-compound static access without element.")); |
+ assert(invariant(node, getter != null, message: |
+ "Compound static access without element.")); |
+ return handleCompoundErroneousSetterAccess(node, element, getter); |
+ } |
+ if (element.isErroneous) { |
+ if (isCompound) { |
+ return handleCompoundErroneousSetterAccess(node, element, getter); |
+ } |
+ return new StaticAccess.unresolved(element); |
+ } else if (element.isParameter) { |
+ if (element.isFinal) { |
+ return new StaticAccess.finalParameter(element); |
+ } else { |
+ return new StaticAccess.parameter(element); |
+ } |
+ } else if (element.isLocal) { |
+ if (element.isFunction) { |
+ return new StaticAccess.localFunction(element); |
+ } else if (element.isFinal || element.isConst) { |
+ return new StaticAccess.finalLocalVariable(element); |
+ } else { |
+ return new StaticAccess.localVariable(element); |
+ } |
+ } else if (element.isStatic) { |
+ if (element.isField) { |
+ if (element.isFinal || element.isConst) { |
+ // TODO(johnniwinther): Handle const field separately. |
+ return new StaticAccess.finalStaticField(element); |
+ } |
+ return new StaticAccess.staticField(element); |
+ } else if (element.isGetter) { |
+ if (isCompound) { |
+ return new CompoundAccessSemantics( |
+ CompoundAccessKind.UNRESOLVED_STATIC_SETTER, element, null); |
+ } |
+ return new StaticAccess.staticGetter(element); |
+ } else if (element.isSetter) { |
+ if (getter != null) { |
+ CompoundAccessKind accessKind; |
+ if (getter.isErroneous) { |
+ accessKind = CompoundAccessKind.UNRESOLVED_STATIC_GETTER; |
+ } else if (getter.isAbstractField) { |
+ AbstractFieldElement abstractField = getter; |
+ if (abstractField.getter == null) { |
+ accessKind = CompoundAccessKind.UNRESOLVED_STATIC_GETTER; |
+ } else { |
+ // TODO(johnniwinther): This might be dead code. |
+ getter = abstractField.getter; |
+ accessKind = CompoundAccessKind.STATIC_GETTER_SETTER; |
+ } |
+ } else if (getter.isGetter) { |
+ accessKind = CompoundAccessKind.STATIC_GETTER_SETTER; |
+ } else { |
+ accessKind = CompoundAccessKind.STATIC_METHOD_SETTER; |
+ } |
+ return new CompoundAccessSemantics( |
+ accessKind, getter, element); |
+ } else { |
+ return new StaticAccess.staticSetter(element); |
+ } |
+ } else { |
+ return new StaticAccess.staticMethod(element); |
+ } |
+ } else if (element.isTopLevel) { |
+ if (element.isField) { |
+ if (element.isFinal || element.isConst) { |
+ // TODO(johnniwinther): Handle const field separately. |
+ return new StaticAccess.finalTopLevelField(element); |
+ } |
+ return new StaticAccess.topLevelField(element); |
+ } else if (element.isGetter) { |
+ return new StaticAccess.topLevelGetter(element); |
+ } else if (element.isSetter) { |
+ if (getter != null) { |
+ CompoundAccessKind accessKind; |
+ if (getter.isErroneous) { |
+ accessKind = CompoundAccessKind.UNRESOLVED_TOPLEVEL_GETTER; |
+ } else if (getter.isAbstractField) { |
+ AbstractFieldElement abstractField = getter; |
+ if (abstractField.getter == null) { |
+ accessKind = CompoundAccessKind.UNRESOLVED_TOPLEVEL_GETTER; |
+ } else { |
+ // TODO(johnniwinther): This might be dead code. |
+ getter = abstractField.getter; |
+ accessKind = CompoundAccessKind.TOPLEVEL_GETTER_SETTER; |
+ } |
+ } else if (getter.isGetter) { |
+ accessKind = CompoundAccessKind.TOPLEVEL_GETTER_SETTER; |
+ } else { |
+ accessKind = CompoundAccessKind.TOPLEVEL_METHOD_SETTER; |
+ } |
+ return new CompoundAccessSemantics( |
+ accessKind, getter, element); |
+ } else { |
+ return new StaticAccess.topLevelSetter(element); |
+ } |
+ } else { |
+ return new StaticAccess.topLevelMethod(element); |
+ } |
+ } else { |
+ return internalError( |
+ node, "Unhandled resolved property access: $element"); |
+ } |
+ } |
+ |
+ SendStructure computeSendStructure(Send node) { |
+ SendStructure sendStructure = elements.getSendStructure(node); |
+ if (sendStructure != null) { |
+ return sendStructure; |
+ } |
+ |
+ if (elements.isAssert(node)) { |
+ return internalError(node, "Unexpected assert."); |
+ } |
+ |
+ AssignmentOperator assignmentOperator; |
+ BinaryOperator binaryOperator; |
+ IncDecOperator incDecOperator; |
+ |
+ if (node.isOperator) { |
+ String operatorText = node.selector.asOperator().source; |
+ if (operatorText == 'is') { |
+ return internalError(node, "Unexpected is test."); |
+ } else if (operatorText == 'as') { |
+ return internalError(node, "Unexpected as cast."); |
+ } else if (operatorText == '&&') { |
+ return internalError(node, "Unexpected logical and."); |
+ } else if (operatorText == '||') { |
+ return internalError(node, "Unexpected logical or."); |
+ } else if (operatorText == '??') { |
+ return internalError(node, "Unexpected if-null."); |
+ } |
+ } |
+ |
+ SendStructureKind kind; |
+ |
+ if (node.asSendSet() != null) { |
+ SendSet sendSet = node.asSendSet(); |
+ String operatorText = sendSet.assignmentOperator.source; |
+ if (sendSet.isPrefix || sendSet.isPostfix) { |
+ kind = sendSet.isPrefix |
+ ? SendStructureKind.PREFIX |
+ : SendStructureKind.POSTFIX; |
+ incDecOperator = IncDecOperator.parse(operatorText); |
+ if (incDecOperator == null) { |
+ return internalError( |
+ node, "No inc/dec operator for '$operatorText'."); |
+ } |
+ } else { |
+ assignmentOperator = AssignmentOperator.parse(operatorText); |
+ if (assignmentOperator != null) { |
+ switch (assignmentOperator.kind) { |
+ case AssignmentOperatorKind.ASSIGN: |
+ kind = SendStructureKind.SET; |
+ break; |
+ default: |
+ kind = SendStructureKind.COMPOUND; |
+ } |
+ } else { |
+ return internalError( |
+ node, "No assignment operator for '$operatorText'."); |
+ } |
+ } |
+ } else if (!node.isPropertyAccess) { |
+ kind = SendStructureKind.INVOKE; |
+ } else { |
+ kind = SendStructureKind.GET; |
+ } |
+ |
+ if (node.isOperator) { |
+ String operatorText = node.selector.asOperator().source; |
+ if (node.arguments.isEmpty) { |
+ return internalError(node, "Unexpected unary $operatorText."); |
+ } else { |
+ binaryOperator = BinaryOperator.parse(operatorText); |
+ if (binaryOperator != null) { |
+ switch (binaryOperator.kind) { |
+ case BinaryOperatorKind.EQ: |
+ kind = SendStructureKind.EQ; |
+ return internalError(node, "Unexpected binary $kind."); |
+ case BinaryOperatorKind.NOT_EQ: |
+ kind = SendStructureKind.NOT_EQ; |
+ return internalError(node, "Unexpected binary $kind."); |
+ case BinaryOperatorKind.INDEX: |
+ if (node.isPrefix) { |
+ kind = SendStructureKind.INDEX_PREFIX; |
+ } else if (node.isPostfix) { |
+ kind = SendStructureKind.INDEX_POSTFIX; |
+ } else if (node.arguments.tail.isEmpty) { |
+ // a[b] |
+ kind = SendStructureKind.INDEX; |
+ return internalError(node, "Unexpected binary $kind."); |
+ } else { |
+ if (kind == SendStructureKind.COMPOUND) { |
+ // a[b] += c |
+ kind = SendStructureKind.COMPOUND_INDEX_SET; |
+ } else { |
+ // a[b] = c |
+ kind = SendStructureKind.INDEX_SET; |
+ } |
+ } |
+ break; |
+ default: |
+ kind = SendStructureKind.BINARY; |
+ return internalError(node, "Unexpected binary $kind."); |
+ } |
+ } else { |
+ return internalError( |
+ node, "Unexpected invalid binary $operatorText."); |
+ } |
+ } |
+ } |
+ AccessSemantics semantics = computeAccessSemantics( |
+ node, |
+ isSet: kind == SendStructureKind.SET, |
+ isInvoke: kind == SendStructureKind.INVOKE, |
+ isCompound: kind == SendStructureKind.COMPOUND || |
+ kind == SendStructureKind.COMPOUND_INDEX_SET || |
+ kind == SendStructureKind.PREFIX || |
+ kind == SendStructureKind.POSTFIX || |
+ kind == SendStructureKind.INDEX_PREFIX || |
+ kind == SendStructureKind.INDEX_POSTFIX); |
+ if (semantics == null) { |
+ return internalError(node, 'No semantics for $node'); |
+ } |
+ Selector selector = elements.getSelector(node); |
+ switch (kind) { |
+ case SendStructureKind.GET: |
+ return new GetStructure(semantics, selector); |
+ case SendStructureKind.SET: |
+ return new SetStructure(semantics, selector); |
+ case SendStructureKind.INVOKE: |
+ switch (semantics.kind) { |
+ case AccessKind.STATIC_METHOD: |
+ case AccessKind.SUPER_METHOD: |
+ case AccessKind.TOPLEVEL_METHOD: |
+ // TODO(johnniwinther): Should local function also be handled here? |
+ FunctionElement function = semantics.element; |
+ FunctionSignature signature = function.functionSignature; |
+ if (!selector.callStructure.signatureApplies(signature)) { |
+ return new IncompatibleInvokeStructure(semantics, selector); |
+ } |
+ break; |
+ default: |
+ break; |
+ } |
+ return new InvokeStructure(semantics, selector); |
+ case SendStructureKind.UNARY: |
+ return internalError(node, "Unexpected unary."); |
+ case SendStructureKind.NOT: |
+ return internalError(node, "Unexpected not."); |
+ case SendStructureKind.BINARY: |
+ return internalError(node, "Unexpected binary."); |
+ case SendStructureKind.INDEX: |
+ return internalError(node, "Unexpected index."); |
+ case SendStructureKind.EQ: |
+ return internalError(node, "Unexpected equals."); |
+ case SendStructureKind.NOT_EQ: |
+ return internalError(node, "Unexpected not equals."); |
+ case SendStructureKind.COMPOUND: |
+ Selector getterSelector = |
+ elements.getGetterSelectorInComplexSendSet(node); |
+ return new CompoundStructure( |
+ semantics, |
+ assignmentOperator, |
+ getterSelector, |
+ selector); |
+ case SendStructureKind.INDEX_SET: |
+ return new IndexSetStructure(semantics, selector); |
+ case SendStructureKind.COMPOUND_INDEX_SET: |
+ Selector getterSelector = |
+ elements.getGetterSelectorInComplexSendSet(node); |
+ return new CompoundIndexSetStructure( |
+ semantics, |
+ assignmentOperator, |
+ getterSelector, |
+ selector); |
+ case SendStructureKind.INDEX_PREFIX: |
+ Selector getterSelector = |
+ elements.getGetterSelectorInComplexSendSet(node); |
+ return new IndexPrefixStructure( |
+ semantics, |
+ incDecOperator, |
+ getterSelector, |
+ selector); |
+ case SendStructureKind.INDEX_POSTFIX: |
+ Selector getterSelector = |
+ elements.getGetterSelectorInComplexSendSet(node); |
+ return new IndexPostfixStructure( |
+ semantics, |
+ incDecOperator, |
+ getterSelector, |
+ selector); |
+ case SendStructureKind.PREFIX: |
+ Selector getterSelector = |
+ elements.getGetterSelectorInComplexSendSet(node); |
+ return new PrefixStructure( |
+ semantics, |
+ incDecOperator, |
+ getterSelector, |
+ selector); |
+ case SendStructureKind.POSTFIX: |
+ Selector getterSelector = |
+ elements.getGetterSelectorInComplexSendSet(node); |
+ return new PostfixStructure( |
+ semantics, |
+ incDecOperator, |
+ getterSelector, |
+ selector); |
+ } |
+ } |
+ |
+ AccessSemantics computeAccessSemantics(Send node, |
+ {bool isSet: false, |
+ bool isInvoke: false, |
+ bool isCompound: false}) { |
+ Element element = elements[node]; |
+ Element getter = isCompound ? elements[node.selector] : null; |
+ if (elements.isTypeLiteral(node)) { |
+ DartType dartType = elements.getTypeLiteralType(node); |
+ // TODO(johnniwinther): Handle deferred constants. There are runtime |
+ // but not compile-time constants and should have their own |
+ // [DeferredConstantExpression] class. |
+ ConstantExpression constant = elements.getConstant( |
+ isInvoke || isSet || isCompound ? node.selector : node); |
+ switch (dartType.kind) { |
+ case TypeKind.INTERFACE: |
+ return new ConstantAccess.classTypeLiteral(constant); |
+ case TypeKind.TYPEDEF: |
+ return new ConstantAccess.typedefTypeLiteral(constant); |
+ case TypeKind.TYPE_VARIABLE: |
+ return new StaticAccess.typeParameterTypeLiteral(dartType.element); |
+ case TypeKind.DYNAMIC: |
+ return new ConstantAccess.dynamicTypeLiteral(constant); |
+ default: |
+ return internalError(node, "Unexpected type literal type: $dartType"); |
+ } |
+ } else if (node.isSuperCall) { |
+ if (Elements.isUnresolved(element)) { |
+ if (isCompound) { |
+ if (Elements.isUnresolved(getter)) { |
+ // TODO(johnniwinther): Ensure that [getter] is not null. This |
+ // happens in the case of missing super getter. |
+ return new StaticAccess.unresolvedSuper(element); |
+ } else if (getter.isField) { |
+ assert(invariant(node, getter.isFinal, |
+ message: "Super field expected to be final.")); |
+ return new StaticAccess.superFinalField(getter); |
+ } else if (getter.isFunction) { |
+ if (node.isIndex) { |
+ return new CompoundAccessSemantics( |
+ CompoundAccessKind.UNRESOLVED_SUPER_SETTER, getter, element); |
+ } else { |
+ return new StaticAccess.superMethod(getter); |
+ } |
+ } else { |
+ return new CompoundAccessSemantics( |
+ CompoundAccessKind.UNRESOLVED_SUPER_SETTER, getter, element); |
+ } |
+ } else { |
+ return new StaticAccess.unresolvedSuper(element); |
+ } |
+ } else if (isCompound && Elements.isUnresolved(getter)) { |
+ // TODO(johnniwinther): Ensure that [getter] is not null. This happens |
+ // in the case of missing super getter. |
+ return new CompoundAccessSemantics( |
+ CompoundAccessKind.UNRESOLVED_SUPER_GETTER, getter, element); |
+ } else if (element.isField) { |
+ if (getter != null && getter != element) { |
+ CompoundAccessKind accessKind; |
+ if (getter.isField) { |
+ accessKind = CompoundAccessKind.SUPER_FIELD_FIELD; |
+ } else if (getter.isGetter) { |
+ accessKind = CompoundAccessKind.SUPER_GETTER_FIELD; |
+ } else { |
+ return internalError(node, |
+ "Unsupported super call: $node : $element/$getter."); |
+ } |
+ return new CompoundAccessSemantics(accessKind, getter, element); |
+ } else if (element.isFinal) { |
+ return new StaticAccess.superFinalField(element); |
+ } |
+ return new StaticAccess.superField(element); |
+ } else if (element.isGetter) { |
+ return new StaticAccess.superGetter(element); |
+ } else if (element.isSetter) { |
+ if (getter != null) { |
+ CompoundAccessKind accessKind; |
+ if (getter.isField) { |
+ accessKind = CompoundAccessKind.SUPER_FIELD_SETTER; |
+ } else if (getter.isGetter) { |
+ accessKind = CompoundAccessKind.SUPER_GETTER_SETTER; |
+ } else { |
+ accessKind = CompoundAccessKind.SUPER_METHOD_SETTER; |
+ } |
+ return new CompoundAccessSemantics(accessKind, getter, element); |
+ } |
+ return new StaticAccess.superSetter(element); |
+ } else if (isCompound) { |
+ return new CompoundAccessSemantics( |
+ CompoundAccessKind.SUPER_GETTER_SETTER, getter, element); |
+ } else { |
+ return new StaticAccess.superMethod(element); |
+ } |
+ } else if (node.isConditional) { |
+ // Conditional sends (e?.x) are treated as dynamic property reads because |
+ // they are equivalent to do ((a) => a == null ? null : a.x)(e). If `e` is |
+ // a type `A`, this is equivalent to write `(A).x`. |
+ return const DynamicAccess.ifNotNullProperty(); |
+ } else if (node.isOperator) { |
+ return const DynamicAccess.dynamicProperty(); |
+ } else if (Elements.isClosureSend(node, element)) { |
+ if (element == null) { |
+ if (node.selector.isThis()) { |
+ return new DynamicAccess.thisAccess(); |
+ } else { |
+ return new DynamicAccess.expression(); |
+ } |
+ } else if (Elements.isErroneous(element)) { |
+ return new StaticAccess.unresolved(element); |
+ } else { |
+ return handleStaticallyResolvedAccess( |
+ node, element, getter, isCompound: isCompound); |
+ } |
+ } else { |
+ bool isDynamicAccess(Element e) => e == null || e.isInstanceMember; |
+ |
+ if (isDynamicAccess(element) && |
+ (!isCompound || isDynamicAccess(getter))) { |
+ if (node.receiver == null || node.receiver.isThis()) { |
+ return const DynamicAccess.thisProperty(); |
+ } else { |
+ return const DynamicAccess.dynamicProperty(); |
+ } |
+ } else if (element != null && element.impliesType) { |
+ // TODO(johnniwinther): Provide an [ErroneousElement]. |
+ // This happens for code like `C.this`. |
+ return new StaticAccess.unresolved(null); |
+ } else { |
+ return handleStaticallyResolvedAccess( |
+ node, element, getter, isCompound: isCompound); |
+ } |
+ } |
+ } |
+ |
ConstructorAccessSemantics computeConstructorAccessSemantics( |
ConstructorElement constructor, |
CallStructure callStructure, |