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 61011b60178eb4c793dacad38fb70dd09fc4fa15..1b0dfbc5eeed8173b7da2769f58bfd356fbc57a8 100644 |
| --- a/pkg/compiler/lib/src/resolution/members.dart |
| +++ b/pkg/compiler/lib/src/resolution/members.dart |
| @@ -540,9 +540,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| Element target; |
| String name = node.selector.asIdentifier().source; |
| if (identical(name, 'this')) { |
| - // TODO(ahe): Why is this using GENERIC? |
| - error(node.selector, MessageKind.GENERIC, |
| - {'text': "expected an identifier"}); |
| + error(node.selector, MessageKind.THIS_PROPERTY); |
| return const NoneResult(); |
| } else if (node.isSuperCall) { |
| if (node.isOperator) { |
| @@ -957,7 +955,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| return computeSuperAccessSemantics(node, target); |
| } |
| - /// Resolve [node] as subexpression that is _not_ the prefix of a member |
| + /// 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) { |
| bool oldSendIsMemberAccess = sendIsMemberAccess; |
| @@ -967,6 +965,38 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| return result; |
| } |
| + /// Resolve [node] as a subexpression that _is_ the prefix of a member access. |
| + /// For instance `a` in `a.b`, as opposed to `a` in `a + b`. |
| + ResolutionResult visitExpressionPrefix(Node node) { |
| + int oldAllowedCategory = allowedCategory; |
| + bool oldSendIsMemberAccess = sendIsMemberAccess; |
| + allowedCategory |= ElementCategory.PREFIX | ElementCategory.SUPER; |
| + sendIsMemberAccess = true; |
| + ResolutionResult result = visit(node); |
| + sendIsMemberAccess = oldSendIsMemberAccess; |
| + allowedCategory = oldAllowedCategory; |
| + return result; |
| + } |
| + |
| + /// Resolved [node] as a subexpression that is the prefix of a conditional |
| + /// access. For instance `a` in `a?.b`. |
| + ResolutionResult visitConditionalPrefix(Node node) { |
| + // Conditional sends like `e?.foo` treat the receiver as an expression. So |
| + // `C?.foo` needs to be treated like `(C).foo`, not like C.foo. Prefixes and |
| + // super are not allowed on their own in that context. |
| + int oldAllowedCategory = allowedCategory; |
| + bool oldSendIsMemberAccess = sendIsMemberAccess; |
| + sendIsMemberAccess = false; |
| + allowedCategory = |
| + ElementCategory.VARIABLE | |
| + ElementCategory.FUNCTION | |
| + ElementCategory.IMPLIES_TYPE; |
| + ResolutionResult result = visit(node); |
| + sendIsMemberAccess = oldSendIsMemberAccess; |
| + allowedCategory = oldAllowedCategory; |
| + return result; |
| + } |
| + |
| /// Handle a type test expression, like `a is T` and `a is! T`. |
| ResolutionResult handleIs(Send node) { |
| Node expression = node.receiver; |
| @@ -1389,26 +1419,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| /// Handle access of a property of [name] on `this`, like `this.name` and |
| /// `this.name()`, or `name` and `name()` in instance context. |
| ResolutionResult handleThisPropertyAccess(Send node, Name name) { |
| - AccessSemantics accessSemantics = new AccessSemantics.thisProperty(); |
| - SendStructure sendStructure; |
| - Selector selector; |
| - if (node.isCall) { |
| - CallStructure callStructure = resolveArguments(node.argumentsNode); |
| - selector = new Selector(SelectorKind.CALL, name, callStructure); |
| - registry.registerDynamicInvocation(selector); |
| - sendStructure = new InvokeStructure(accessSemantics, selector); |
| - } else { |
| - assert(invariant(node, node.isPropertyAccess)); |
| - selector = new Selector( |
| - SelectorKind.GETTER, name, CallStructure.NO_ARGS); |
| - registry.registerDynamicGetter(selector); |
| - sendStructure = new GetStructure(accessSemantics, selector); |
| - } |
| - registry.registerSendStructure(node, sendStructure); |
| - // TODO(johnniwinther): Remove this when all information goes through |
| - // the [SendStructure]. |
| - registry.setSelector(node, selector); |
| - return const NoneResult(); |
| + AccessSemantics semantics = new AccessSemantics.thisProperty(); |
| + return handleDynamicAccessSemantics(node, name, semantics); |
| } |
| /// Handle access on `this`, like `this()` and `this` when it is parsed as a |
| @@ -1576,12 +1588,175 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| } |
| } |
| + /// Handle qualified access to an unresolved static class member, like `a.b` |
| + /// or `a.b()` where `a` is a class and `b` is unresolved. |
| + ResolutionResult handleUnresolvedStaticMemberAccess( |
| + Send node, Name name, ClassElement receiverClass) { |
| + registry.registerThrowNoSuchMethod(); |
|
karlklose
2015/06/12 07:01:03
Share code with l. 1599-1630, making the message k
Johnni Winther
2015/06/12 07:50:30
Added a TODO. I wan't to specialize the semantics
|
| + // TODO(johnniwinther): Produce a different error if [name] is resolves to |
| + // a constructor. |
| + |
| + // TODO(johnniwinther): With the simplified [TreeElements] invariant, |
| + // try to resolve injected elements if [currentClass] is in the patch |
| + // library of [receiverClass]. |
| + |
| + // TODO(karlklose): this should be reported by the caller of |
| + // [resolveSend] to select better warning messages for getters and |
| + // setters. |
| + ErroneousElement error = reportAndCreateErroneousElement( |
| + node, name.text, MessageKind.MEMBER_NOT_FOUND, |
| + {'className': receiverClass.name, 'memberName': name.text}); |
| + // TODO(johnniwinther): Add an [AccessSemantics] for unresolved static |
| + // member access. |
| + return handleErroneousAccess( |
| + node, name, error, new StaticAccess.unresolved(error)); |
| + } |
| + |
| + /// Handle qualified access of an instance member, like `a.b` or `a.b()` where |
| + /// `a` is a class and `b` is a non-static member. |
| + ResolutionResult handleStaticInstanceMemberAccess( |
| + Send node, Name name, ClassElement receiverClass, Element member) { |
| + |
| + registry.registerThrowNoSuchMethod(); |
| + // TODO(johnniwinther): With the simplified [TreeElements] invariant, |
| + // try to resolve injected elements if [currentClass] is in the patch |
| + // library of [receiverClass]. |
| + |
| + // TODO(karlklose): this should be reported by the caller of |
| + // [resolveSend] to select better warning messages for getters and |
| + // setters. |
| + ErroneousElement error = reportAndCreateErroneousElement( |
| + node, name.text, MessageKind.MEMBER_NOT_STATIC, |
| + {'className': receiverClass.name, 'memberName': name}); |
| + |
| + // TODO(johnniwinther): Add an [AccessSemantics] for statically accessed |
| + // instance members. |
| + return handleErroneousAccess( |
| + node, name, error, new StaticAccess.unresolved(error)); |
| + } |
| + |
| + /// Handle qualified access of an inaccessible private static class member, |
| + /// like `a._b` or `a.b()` where `a` is class, `_b` is static member of `a` |
| + /// but `a` is not defined in the current library. |
| + ResolutionResult handlePrivateStaticMemberAccess( |
| + Send node, Name name, ClassElement receiverClass, Element member) { |
| + registry.registerThrowNoSuchMethod(); |
| + ErroneousElement error = reportAndCreateErroneousElement( |
| + node, name.text, MessageKind.PRIVATE_ACCESS, |
| + {'libraryName': member.library.getLibraryOrScriptName(), |
| + 'name': name}); |
| + // TODO(johnniwinther): Add an [AccessSemantics] for unresolved static |
| + // member access. |
| + return handleErroneousAccess( |
| + node, name, error, new StaticAccess.unresolved(error)); |
| + } |
| + |
| + /// Handle qualified access to a static member, like `a.b` or `a.b()` where |
| + /// `a` is class and `b` is a static member of `a`. |
|
karlklose
2015/06/12 07:01:03
"`a` is *a* class"
Johnni Winther
2015/06/12 07:50:30
Done.
|
| + ResolutionResult handleStaticMemberAccess( |
| + Send node, Name memberName, ClassElement receiverClass) { |
| + String name = memberName.text; |
| + receiverClass.ensureResolved(compiler); |
| + if (node.isOperator) { |
| + // When the resolved receiver is a class, we can have two cases: |
| + // 1) a static send: C.foo, or |
| + // 2) an operator send, where the receiver is a class literal: 'C + 1'. |
| + // The following code that looks up the selector on the resolved |
| + // receiver will treat the second as the invocation of a static operator |
| + // if the resolved receiver is not null. |
| + return const NoneResult(); |
| + } |
| + MembersCreator.computeClassMembersByName( |
| + compiler, receiverClass.declaration, name); |
| + Element member = receiverClass.lookupLocalMember(name); |
| + if (member == null) { |
| + return handleUnresolvedStaticMemberAccess( |
| + node, memberName, receiverClass); |
| + } else if (member.isInstanceMember) { |
| + return handleStaticInstanceMemberAccess( |
| + node, memberName, receiverClass, member); |
| + } else if (memberName.isPrivate && memberName.library != member.library) { |
| + return handlePrivateStaticMemberAccess( |
| + node, memberName, receiverClass, member); |
| + } else { |
| + return handleStaticOrTopLevelAccess(node, memberName, member); |
| + } |
| + } |
| + |
| + /// Handle qualified [Send] where the receiver resolves to an [Element], like |
| + /// `a.b` where `a` is a local, field, class, or prefix, etc. |
| + ResolutionResult handleResolvedQualifiedSend( |
| + Send node, Name name, Element element) { |
| + if (element.isPrefix) { |
| + return oldVisitSend(node); |
| + } else if (element.isClass) { |
| + return handleStaticMemberAccess(node, name, element); |
| + } |
| + return oldVisitSend(node); |
| + } |
| + |
| + /// Handle dynamic access of [semantics]. |
| + ResolutionResult handleDynamicAccessSemantics( |
| + Send node, Name name, AccessSemantics semantics) { |
| + SendStructure sendStructure; |
| + Selector selector; |
| + if (node.isCall) { |
| + CallStructure callStructure = resolveArguments(node.argumentsNode); |
| + selector = new Selector(SelectorKind.CALL, name, callStructure); |
| + registry.registerDynamicInvocation(selector); |
| + sendStructure = new InvokeStructure(semantics, selector); |
| + } else { |
| + assert(invariant(node, node.isPropertyAccess)); |
| + selector = new Selector( |
| + SelectorKind.GETTER, name, CallStructure.NO_ARGS); |
| + registry.registerDynamicGetter(selector); |
| + sendStructure = new GetStructure(semantics, selector); |
| + } |
| + registry.registerSendStructure(node, sendStructure); |
| + // TODO(johnniwinther): Remove this when all information goes through |
| + // the [SendStructure]. |
| + registry.setSelector(node, selector); |
| + return const NoneResult(); |
| + } |
| + |
| + /// Handle dynamic property access, like `a.b` or `a.b()` where `a` is not a |
| + /// prefix or class. |
| + ResolutionResult handleDynamicPropertyAccess(Send node, Name name) { |
| + AccessSemantics semantics = |
| + new DynamicAccess.dynamicProperty(node.receiver); |
| + return handleDynamicAccessSemantics(node, name, semantics); |
| + } |
| + |
| + /// Handle conditional access, like `a?.b` or `a?.b()`. |
| + ResolutionResult handleConditionalAccess(Send node, Name name) { |
| + Node receiver = node.receiver; |
| + visitExpression(receiver); |
| + AccessSemantics semantics = |
| + new DynamicAccess.ifNotNullProperty(receiver); |
| + return handleDynamicAccessSemantics(node, name, semantics); |
| + } |
| + |
| + /// Handle `this` as a qualified property, like `a.this`. |
| + ResolutionResult handleQualifiedThisAccess(Send node, Name name) { |
| + ErroneousElement error = reportAndCreateErroneousElement( |
| + node.selector, |
| + name.text, |
| + MessageKind.THIS_PROPERTY, {}, |
| + isError: true); |
| + // TODO(johnniwinther): Support `this` as property as an [AccessSemantics]. |
| + AccessSemantics accessSemantics = new StaticAccess.unresolved(error); |
| + return handleErroneousAccess(node, name, error, accessSemantics); |
| + } |
| + |
| /// Handle a qualified [Send], that is where the receiver is non-null, like |
| /// `a.b`, `a.b()`, `this.a()` and `super.a()`. |
| ResolutionResult handleQualifiedSend(Send node) { |
| Identifier selector = node.selector.asIdentifier(); |
| - Name name = new Name(selector.source, enclosingElement.library); |
| - if (node.isSuperCall) { |
| + String text = selector.source; |
| + Name name = new Name(text, enclosingElement.library); |
| + if (text == 'this') { |
| + return handleQualifiedThisAccess(node, name); |
| + } else if (node.isSuperCall) { |
| return handleSuperPropertyAccess(node, name); |
| } else if (node.receiver.isThis()) { |
| if (checkThisAccess(node)) { |
| @@ -1590,9 +1765,15 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| // TODO(johnniwinther): Handle invalid this access as an |
| // [AccessSemantics]. |
| return const NoneResult(); |
| + } else if (node.isConditional) { |
| + return handleConditionalAccess(node, name); |
| + } |
| + ResolutionResult result = visitExpressionPrefix(node.receiver); |
| + if (result.element != null) { |
| + return handleResolvedQualifiedSend(node, name, result.element); |
| + } else { |
| + return handleDynamicPropertyAccess(node, name); |
| } |
| - // TODO(johnniwinther): Handle remaining qualified sends. |
| - return oldVisitSend(node); |
| } |
| /// Handle access unresolved access to [name] in a non-instance context. |
| @@ -2464,8 +2645,10 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
| bool oldSendIsMemberAccess = sendIsMemberAccess; |
| sendIsMemberAccess = false; |
| var oldCategory = allowedCategory; |
| - allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION |
| - | ElementCategory.IMPLIES_TYPE; |
| + allowedCategory = |
| + ElementCategory.VARIABLE | |
| + ElementCategory.FUNCTION | |
| + ElementCategory.IMPLIES_TYPE; |
| ResolutionResult result = visit(node.expression); |
| allowedCategory = oldCategory; |
| sendIsMemberAccess = oldSendIsMemberAccess; |