Chromium Code Reviews| Index: pkg/compiler/lib/src/resolution/members.dart |
| diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart |
| index dd9b62be3692cb532aad6294aab77d590c6ecad7..8631460d87717980cd2b1d556a6116f0a17c3968 100644 |
| --- a/pkg/compiler/lib/src/resolution/members.dart |
| +++ b/pkg/compiler/lib/src/resolution/members.dart |
| @@ -874,26 +874,36 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } |
| } |
| - /// Check that access to `super` is currently allowed. |
| - bool checkSuperAccess(Send node) { |
| + /// Check that access to `super` is currently allowed. Returns an |
| + /// [AccessSemantics] in case of an error, `null` otherwise. |
| + AccessSemantics checkSuperAccess(Send node) { |
| if (!inInstanceContext) { |
| - compiler.reportError(node, MessageKind.NO_SUPER_IN_STATIC); |
| - return false; |
| + return new StaticAccess.invalid( |
| + reportAndCreateErroneousElement( |
| + node, 'super', |
| + MessageKind.NO_SUPER_IN_STATIC, {}, |
| + isError: true)); |
| } |
| if (node.isConditional) { |
| // `super?.foo` is not allowed. |
| - compiler.reportError(node, MessageKind.INVALID_USE_OF_SUPER); |
| - return false; |
| + return new StaticAccess.invalid( |
| + reportAndCreateErroneousElement( |
| + node, 'super', |
| + MessageKind.INVALID_USE_OF_SUPER, {}, |
| + isError: true)); |
| } |
| if (currentClass.supertype == null) { |
| // This is just to guard against internal errors, so no need |
| // for a real error message. |
| - compiler.reportError(node, MessageKind.GENERIC, |
| - {'text': "Object has no superclass"}); |
| - return false; |
| + return new StaticAccess.invalid( |
| + reportAndCreateErroneousElement( |
| + node, 'super', |
| + MessageKind.GENERIC, |
| + {'text': "Object has no superclass"}, |
| + isError: true)); |
| } |
| registry.registerSuperUse(node); |
| - return true; |
| + return null; |
| } |
| /// Check that access to `this` is currently allowed. |
| @@ -926,6 +936,75 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } |
| } |
| + /// Compute the [AccessSemantics] corresponding to a compound super access |
| + /// reading from [getter] and writing to [setter]. |
| + AccessSemantics computeCompoundSuperAccessSemantics( |
| + Spannable node, |
| + Element getter, |
| + Element setter) { |
| + if (getter.isErroneous) { |
| + if (setter.isErroneous) { |
| + return new StaticAccess.unresolvedSuper(getter); |
| + } else if (setter.isFunction) { |
| + assert(invariant(node, setter.name == '[]=', |
| + message: "Unexpected super setter '$setter'.")); |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.UNRESOLVED_SUPER_GETTER, getter, setter); |
| + } else { |
| + assert(invariant(node, setter.isSetter, |
| + message: "Unexpected super setter '$setter'.")); |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.UNRESOLVED_SUPER_GETTER, getter, setter); |
| + } |
| + } else if (getter.isField) { |
| + if (setter.isField) { |
| + if (getter == setter) { |
| + return new StaticAccess.superField(getter); |
| + } else { |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.SUPER_FIELD_FIELD, getter, setter); |
| + } |
| + } else { |
|
floitsch
2015/07/31 12:30:37
Maybe add a short comment, explaining why it's not
Johnni Winther
2015/07/31 13:38:29
Done.
|
| + assert(invariant(node, setter.isSetter, |
| + message: "Unexpected super setter '$setter'.")); |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.SUPER_FIELD_SETTER, getter, setter); |
| + } |
| + } else if (getter.isGetter) { |
| + if (setter.isErroneous) { |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.UNRESOLVED_SUPER_SETTER, getter, setter); |
| + } else if (setter.isField) { |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.SUPER_GETTER_FIELD, getter, setter); |
| + } else { |
| + assert(invariant(node, setter.isSetter, |
| + message: "Unexpected super setter '$setter'.")); |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.SUPER_GETTER_SETTER, getter, setter); |
| + } |
| + } else { |
| + assert(invariant(node, getter.isFunction, |
| + message: "Unexpected super getter '$getter'.")); |
| + if (setter.isErroneous) { |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.UNRESOLVED_SUPER_SETTER, getter, setter); |
| + } else if (setter.isFunction) { |
| + assert(invariant(node, getter.name == '[]', |
| + message: "Unexpected super getter '$getter'.")); |
| + assert(invariant(node, setter.name == '[]=', |
| + message: "Unexpected super setter '$setter'.")); |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.SUPER_GETTER_SETTER, getter, setter); |
| + } else { |
| + assert(invariant(node, setter.isSetter, |
| + message: "Unexpected super setter '$setter'.")); |
| + return new CompoundAccessSemantics( |
| + CompoundAccessKind.SUPER_METHOD_SETTER, getter, setter); |
| + } |
| + } |
| + } |
| + |
| /// Compute the [AccessSemantics] corresponding to a local access of [target]. |
| AccessSemantics computeLocalAccessSemantics(Spannable node, |
| LocalElement target) { |
| @@ -1043,6 +1122,55 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| return computeSuperAccessSemantics(node, target); |
| } |
| + /// Compute the [AccessSemantics] for accessing the name of [selector] on the |
| + /// super class. |
| + /// |
| + /// If no matching super member is found and error is reported and |
| + /// `noSuchMethod` on `super` is registered. Furthermore, if [alternateName] |
| + /// is provided, the [AccessSemantics] corresponding to the alternate name is |
| + /// returned. For instance, the access of a super setter for an unresolved |
| + /// getter: |
| + /// |
| + /// class Super { |
| + /// set name(_) {} |
| + /// } |
| + /// class Sub extends Super { |
| + /// foo => super.name; // Access to the setter. |
| + /// } |
| + /// |
| + AccessSemantics computeSuperAccessSemanticsForSelectors( |
| + Spannable node, |
| + Selector getterSelector, Selector setterSelector) { |
| + |
| + // TODO(johnniwinther): Ensure correct behavior if currentClass is a |
| + // patch. |
| + Element getter = currentClass.lookupSuperByName(getterSelector.memberName); |
| + // [target] may be null which means invoking noSuchMethod on super. |
| + if (getter == null) { |
| + getter = reportAndCreateErroneousElement( |
| + node, getterSelector.name, MessageKind.NO_SUCH_SUPER_MEMBER, |
| + {'className': currentClass.name, 'memberName': getterSelector.name}); |
| + // We still need to register the invocation, because we might |
| + // call [:super.noSuchMethod:] which calls [JSInvocationMirror._invokeOn]. |
|
floitsch
2015/07/31 12:30:37
nit. I prefer `super.noSuchMethod`.
Johnni Winther
2015/07/31 13:38:29
Done.
|
| + registry.registerDynamicInvocation( |
| + new UniverseSelector(getterSelector, null)); |
| + registry.registerSuperNoSuchMethod(); |
| + } |
| + Element setter = currentClass.lookupSuperByName(setterSelector.memberName); |
| + // [target] may be null which means invoking noSuchMethod on super. |
| + if (setter == null) { |
| + setter = reportAndCreateErroneousElement( |
| + node, setterSelector.name, MessageKind.NO_SUCH_SUPER_MEMBER, |
| + {'className': currentClass.name, 'memberName': setterSelector.name}); |
| + // We still need to register the invocation, because we might |
| + // call [:super.noSuchMethod:] which calls [JSInvocationMirror._invokeOn]. |
| + registry.registerDynamicInvocation( |
| + new UniverseSelector(setterSelector, null)); |
| + registry.registerSuperNoSuchMethod(); |
| + } |
| + return computeCompoundSuperAccessSemantics(node, getter, setter); |
| + } |
| + |
| /// Resolve [node] as a subexpression that is _not_ the prefix of a member |
| /// access. For instance `a` in `a + b`, as opposed to `a` in `a.b`. |
| ResolutionResult visitExpression(Node node) { |
| @@ -1151,7 +1279,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| AccessSemantics semantics; |
| if (node.isSuperCall) { |
| - if (checkSuperAccess(node)) { |
| + semantics = checkSuperAccess(node); |
| + if (semantics == null) { |
| semantics = computeSuperAccessSemanticsForSelector(node, selector); |
| // TODO(johnniwinther): Add information to [AccessSemantics] about |
| // whether it is erroneous. |
| @@ -1197,8 +1326,6 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } |
| } |
| if (semantics != null) { |
| - // TODO(johnniwinther): Support invalid super access as an |
| - // [AccessSemantics]. |
| registry.registerSendStructure(node, |
| new UnaryStructure(semantics, operator)); |
| } |
| @@ -1325,7 +1452,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| registry.setSelector(node, selector); |
| if (node.isSuperCall) { |
| - if (checkSuperAccess(node)) { |
| + semantics = checkSuperAccess(node); |
| + if (semantics == null) { |
| semantics = computeSuperAccessSemanticsForSelector(node, selector); |
| // TODO(johnniwinther): Add information to [AccessSemantics] about |
| // whether it is erroneous. |
| @@ -1552,71 +1680,78 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } else { |
| selector = new Selector(SelectorKind.GETTER, name, callStructure); |
| } |
| - if (checkSuperAccess(node)) { |
| - AccessSemantics semantics = computeSuperAccessSemanticsForSelector( |
| + AccessSemantics semantics = checkSuperAccess(node); |
| + if (semantics == null) { |
| + semantics = computeSuperAccessSemanticsForSelector( |
| node, selector, alternateName: name.setter); |
| - if (node.isCall) { |
| - bool isIncompatibleInvoke = false; |
| - switch (semantics.kind) { |
| - case AccessKind.SUPER_METHOD: |
| - MethodElementX superMethod = semantics.element; |
| - superMethod.computeSignature(compiler); |
| - if (!callStructure.signatureApplies( |
| - superMethod.functionSignature)) { |
| - registry.registerThrowNoSuchMethod(); |
| - registry.registerDynamicInvocation( |
| - new UniverseSelector(selector, null)); |
| - registry.registerSuperNoSuchMethod(); |
| - isIncompatibleInvoke = true; |
| - } else { |
| - registry.registerStaticInvocation(semantics.element); |
| - } |
| - break; |
| - case AccessKind.SUPER_FIELD: |
| - case AccessKind.SUPER_FINAL_FIELD: |
| - case AccessKind.SUPER_GETTER: |
| - registry.registerStaticUse(semantics.element); |
| - selector = callStructure.callSelector; |
| + } |
| + if (node.isCall) { |
| + bool isIncompatibleInvoke = false; |
| + switch (semantics.kind) { |
| + case AccessKind.SUPER_METHOD: |
| + MethodElementX superMethod = semantics.element; |
| + superMethod.computeSignature(compiler); |
| + if (!callStructure.signatureApplies( |
| + superMethod.functionSignature)) { |
| + registry.registerThrowNoSuchMethod(); |
| registry.registerDynamicInvocation( |
| new UniverseSelector(selector, null)); |
| - break; |
| - case AccessKind.SUPER_SETTER: |
| - case AccessKind.UNRESOLVED_SUPER: |
| - // NoSuchMethod registered in [computeSuperSemantics]. |
| - break; |
| - default: |
| - internalError(node, "Unexpected super property access $semantics."); |
| - break; |
| - } |
| - registry.registerSendStructure(node, |
| - isIncompatibleInvoke |
| - ? new IncompatibleInvokeStructure(semantics, selector) |
| - : new InvokeStructure(semantics, selector)); |
| - } else { |
| - switch (semantics.kind) { |
| - case AccessKind.SUPER_METHOD: |
| - // TODO(johnniwinther): Method this should be registered as a |
| - // closurization. |
| - registry.registerStaticUse(semantics.element); |
| - break; |
| - case AccessKind.SUPER_FIELD: |
| - case AccessKind.SUPER_FINAL_FIELD: |
| - case AccessKind.SUPER_GETTER: |
| - registry.registerStaticUse(semantics.element); |
| - break; |
| - case AccessKind.SUPER_SETTER: |
| - case AccessKind.UNRESOLVED_SUPER: |
| - // NoSuchMethod registered in [computeSuperSemantics]. |
| - break; |
| - default: |
| - internalError(node, "Unexpected super property access $semantics."); |
| - break; |
| - } |
| - registry.registerSendStructure(node, |
| - new GetStructure(semantics, selector)); |
| + registry.registerSuperNoSuchMethod(); |
| + isIncompatibleInvoke = true; |
| + } else { |
| + registry.registerStaticInvocation(semantics.element); |
| + } |
| + break; |
| + case AccessKind.SUPER_FIELD: |
| + case AccessKind.SUPER_FINAL_FIELD: |
| + case AccessKind.SUPER_GETTER: |
| + registry.registerStaticUse(semantics.element); |
| + selector = callStructure.callSelector; |
| + registry.registerDynamicInvocation( |
| + new UniverseSelector(selector, null)); |
| + break; |
| + case AccessKind.SUPER_SETTER: |
| + case AccessKind.UNRESOLVED_SUPER: |
| + // NoSuchMethod registered in [computeSuperSemantics]. |
| + break; |
| + case AccessKind.INVALID: |
| + // 'super' is not allowed. |
| + break; |
| + default: |
| + internalError(node, "Unexpected super property access $semantics."); |
| + break; |
| } |
| - target = semantics.element; |
| + registry.registerSendStructure(node, |
| + isIncompatibleInvoke |
| + ? new IncompatibleInvokeStructure(semantics, selector) |
| + : new InvokeStructure(semantics, selector)); |
| + } else { |
| + switch (semantics.kind) { |
| + case AccessKind.SUPER_METHOD: |
| + // TODO(johnniwinther): Method this should be registered as a |
| + // closurization. |
| + registry.registerStaticUse(semantics.element); |
| + break; |
| + case AccessKind.SUPER_FIELD: |
| + case AccessKind.SUPER_FINAL_FIELD: |
| + case AccessKind.SUPER_GETTER: |
| + registry.registerStaticUse(semantics.element); |
| + break; |
| + case AccessKind.SUPER_SETTER: |
| + case AccessKind.UNRESOLVED_SUPER: |
| + // NoSuchMethod registered in [computeSuperSemantics]. |
| + break; |
| + case AccessKind.INVALID: |
| + // 'super' is not allowed. |
| + break; |
| + default: |
| + internalError(node, "Unexpected super property access $semantics."); |
| + break; |
| + } |
| + registry.registerSendStructure(node, |
| + new GetStructure(semantics, selector)); |
| } |
| + target = semantics.element; |
| // TODO(johnniwinther): Remove these when all information goes through |
| // the [SendStructure]. |
| @@ -2621,10 +2756,116 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } |
| } |
| + /// Handle super index operations like `super[a] = b`, `super[a] += b`, and |
| + /// `super[a]++`. |
| + // TODO(johnniwinther): Share code with [handleIndexSendSet]. |
| + ResolutionResult handleSuperIndexSendSet(SendSet node) { |
| + String operatorText = node.assignmentOperator.source; |
| + Node index = node.arguments.head; |
| + visitExpression(index); |
| + AccessSemantics semantics = checkSuperAccess(node); |
| + if (node.isPrefix || node.isPostfix) { |
| + // `super[a]++` or `++super[a]`. |
| + IncDecOperator operator = IncDecOperator.parse(operatorText); |
| + Selector getterSelector = new Selector.index(); |
| + Selector setterSelector = new Selector.indexSet(); |
| + Selector operatorSelector = |
| + new Selector.binaryOperator(operator.selectorName); |
| + |
| + // TODO(johnniwinther): Remove these when selectors are only accessed |
| + // through the send structure. |
| + registry.setGetterSelectorInComplexSendSet(node, getterSelector); |
| + registry.setSelector(node, setterSelector); |
| + registry.setOperatorSelectorInComplexSendSet(node, operatorSelector); |
| + |
| + if (semantics == null) { |
| + semantics = computeSuperAccessSemanticsForSelectors( |
| + node, getterSelector, setterSelector); |
| + |
| + registry.registerStaticInvocation(semantics.getter); |
| + registry.registerStaticInvocation(semantics.setter); |
| + |
| + // TODO(johnniwinther): Remove these when elements are only accessed |
| + // through the send structure. |
| + registry.useElement(node, semantics.setter); |
| + registry.useElement(node.selector, semantics.getter); |
| + } |
| + registry.registerDynamicInvocation( |
| + new UniverseSelector(operatorSelector, null)); |
| + |
| + SendStructure sendStructure = node.isPrefix |
| + ? new IndexPrefixStructure( |
| + semantics, operator, getterSelector, setterSelector) |
| + : new IndexPostfixStructure( |
| + semantics, operator, getterSelector, setterSelector); |
| + registry.registerSendStructure(node, sendStructure); |
| + return const NoneResult(); |
| + } else { |
| + Node rhs = node.arguments.tail.head; |
| + visitExpression(rhs); |
| + |
| + AssignmentOperator operator = AssignmentOperator.parse(operatorText); |
| + if (operator.kind == AssignmentOperatorKind.ASSIGN) { |
| + // `super[a] = b`. |
| + Selector setterSelector = new Selector.indexSet(); |
| + if (semantics == null) { |
| + semantics = |
| + computeSuperAccessSemanticsForSelector(node, setterSelector); |
| + |
| + // TODO(johnniwinther): Remove these when elements are only accessed |
| + // through the send structure. |
| + registry.useElement(node, semantics.setter); |
| + } |
| + |
| + // TODO(johnniwinther): Remove this when selectors are only accessed |
| + // through the send structure. |
| + registry.setSelector(node, setterSelector); |
| + registry.registerStaticInvocation(semantics.setter); |
| + |
| + SendStructure sendStructure = |
| + new IndexSetStructure(semantics, setterSelector); |
| + registry.registerSendStructure(node, sendStructure); |
| + return const NoneResult(); |
| + } else { |
| + // `super[a] += b`. |
| + Selector getterSelector = new Selector.index(); |
| + Selector setterSelector = new Selector.indexSet(); |
| + Selector operatorSelector = |
| + new Selector.binaryOperator(operator.selectorName); |
| + if (semantics == null) { |
| + semantics = computeSuperAccessSemanticsForSelectors( |
| + node, getterSelector, setterSelector); |
| + |
| + registry.registerStaticInvocation(semantics.getter); |
| + registry.registerStaticInvocation(semantics.setter); |
| + |
| + // TODO(johnniwinther): Remove these when elements are only accessed |
| + // through the send structure. |
| + registry.useElement(node, semantics.setter); |
| + registry.useElement(node.selector, semantics.getter); |
| + } |
| + |
| + // TODO(johnniwinther): Remove these when selectors are only accessed |
| + // through the send structure. |
| + registry.setGetterSelectorInComplexSendSet(node, getterSelector); |
| + registry.setSelector(node, setterSelector); |
| + registry.setOperatorSelectorInComplexSendSet(node, operatorSelector); |
| + |
| + registry.registerDynamicInvocation( |
| + new UniverseSelector(operatorSelector, null)); |
| + |
| + SendStructure sendStructure = new CompoundIndexSetStructure( |
| + semantics, operator, getterSelector, setterSelector); |
| + registry.registerSendStructure(node, sendStructure); |
| + return const NoneResult(); |
| + } |
| + } |
| + } |
| + |
| ResolutionResult visitSendSet(SendSet node) { |
| if (node.isIndex) { |
| if (node.isSuperCall) { |
| - // TODO(johnniwinther): Refactor index operations on super. |
| + return handleSuperIndexSendSet(node); |
| } else { |
| return handleIndexSendSet(node); |
| } |