| 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 0ea0739f795f3a1c7bba31d4e7f171113fe11239..1bd64f232b073b3fa36cadc1d227bba1cb1d5521 100644
|
| --- a/pkg/compiler/lib/src/resolution/members.dart
|
| +++ b/pkg/compiler/lib/src/resolution/members.dart
|
| @@ -187,6 +187,44 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return new ErroneousElementX(kind, arguments, name, enclosingElement);
|
| }
|
|
|
| + /// Report a warning or error on an unresolved access in non-instance context.
|
| + ///
|
| + /// The [ErroneousElement] corresponding to the message is returned.
|
| + ErroneousElement reportCannotResolve(Node node, String name) {
|
| + assert(invariant(node, !inInstanceContext,
|
| + message: "ResolverVisitor.reportCannotResolve must not be called in "
|
| + "instance context."));
|
| +
|
| + // We report an error within initializers because `this` is implicitly
|
| + // accessed when unqualified identifiers are not resolved. For
|
| + // details, see section 16.14.3 of the spec (2nd edition):
|
| + // An unqualified invocation `i` of the form `id(a1, ...)`
|
| + // ...
|
| + // If `i` does not occur inside a top level or static function, `i`
|
| + // is equivalent to `this.id(a1 , ...)`.
|
| + bool inInitializer =
|
| + enclosingElement.isGenerativeConstructor ||
|
| + (enclosingElement.isInstanceMember && enclosingElement.isField);
|
| + MessageKind kind;
|
| + Map arguments = {'name': name};
|
| + if (inInitializer) {
|
| + kind = MessageKind.CANNOT_RESOLVE_IN_INITIALIZER;
|
| + } else if (name == 'await') {
|
| + var functionName = enclosingElement.name;
|
| + if (functionName == '') {
|
| + kind = MessageKind.CANNOT_RESOLVE_AWAIT_IN_CLOSURE;
|
| + } else {
|
| + kind = MessageKind.CANNOT_RESOLVE_AWAIT;
|
| + arguments['functionName'] = functionName;
|
| + }
|
| + } else {
|
| + kind = MessageKind.CANNOT_RESOLVE;
|
| + }
|
| + registry.registerThrowNoSuchMethod();
|
| + return reportAndCreateErroneousElement(
|
| + node, name, kind, arguments, isError: inInitializer);
|
| + }
|
| +
|
| ResolutionResult visitIdentifier(Identifier node) {
|
| if (node.isThis()) {
|
| if (!inInstanceContext) {
|
| @@ -211,36 +249,10 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| // Set the type to be `dynamic` to mark that this is a type literal.
|
| registry.setType(node, const DynamicType());
|
| }
|
| - element = reportLookupErrorIfAny(element, node, node.source);
|
| + element = reportLookupErrorIfAny(element, node, name);
|
| if (element == null) {
|
| if (!inInstanceContext) {
|
| - // We report an error within initializers because `this` is implicitly
|
| - // accessed when unqualified identifiers are not resolved. For
|
| - // details, see section 16.14.3 of the spec (2nd edition):
|
| - // An unqualified invocation `i` of the form `id(a1, ...)`
|
| - // ...
|
| - // If `i` does not occur inside a top level or static function, `i`
|
| - // is equivalent to `this.id(a1 , ...)`.
|
| - bool inInitializer = enclosingElement.isGenerativeConstructor ||
|
| - (enclosingElement.isInstanceMember && enclosingElement.isField);
|
| - MessageKind kind;
|
| - Map arguments = {'name': name};
|
| - if (inInitializer) {
|
| - kind = MessageKind.CANNOT_RESOLVE_IN_INITIALIZER;
|
| - } else if (name == 'await') {
|
| - var functionName = enclosingElement.name;
|
| - if (functionName == '') {
|
| - kind = MessageKind.CANNOT_RESOLVE_AWAIT_IN_CLOSURE;
|
| - } else {
|
| - kind = MessageKind.CANNOT_RESOLVE_AWAIT;
|
| - arguments['functionName'] = functionName;
|
| - }
|
| - } else {
|
| - kind = MessageKind.CANNOT_RESOLVE;
|
| - }
|
| - element = reportAndCreateErroneousElement(node, name, kind,
|
| - arguments, isError: inInitializer);
|
| - registry.registerThrowNoSuchMethod();
|
| + element = reportCannotResolve(node, name);
|
| }
|
| } else if (element.isErroneous) {
|
| // Use the erroneous element.
|
| @@ -485,21 +497,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| // If this send is of the form "assert(expr);", then
|
| // this is an assertion.
|
| if (selector.isAssert) {
|
| - SendStructure sendStructure = const AssertStructure();
|
| - if (selector.argumentCount != 1) {
|
| - error(node.selector,
|
| - MessageKind.WRONG_NUMBER_OF_ARGUMENTS_FOR_ASSERT,
|
| - {'argumentCount': selector.argumentCount});
|
| - sendStructure = const InvalidAssertStructure();
|
| - } else if (selector.namedArgumentCount != 0) {
|
| - error(node.selector,
|
| - MessageKind.ASSERT_IS_GIVEN_NAMED_ARGUMENTS,
|
| - {'argumentCount': selector.namedArgumentCount});
|
| - sendStructure = const InvalidAssertStructure();
|
| - }
|
| - registry.registerAssert(node);
|
| - registry.registerSendStructure(node, sendStructure);
|
| - return const AssertResult();
|
| + internalError(node, "Unexpected assert: $node");
|
| }
|
|
|
| return node.selector.accept(this);
|
| @@ -709,17 +707,20 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return selector;
|
| }
|
|
|
| - void resolveArguments(NodeList list) {
|
| - if (list == null) return;
|
| + CallStructure resolveArguments(NodeList list) {
|
| + if (list == null) return null;
|
| bool oldSendIsMemberAccess = sendIsMemberAccess;
|
| sendIsMemberAccess = false;
|
| Map<String, Node> seenNamedArguments = new Map<String, Node>();
|
| + int argumentCount = 0;
|
| + List<String> namedArguments = <String>[];
|
| for (Link<Node> link = list.nodes; !link.isEmpty; link = link.tail) {
|
| Expression argument = link.head;
|
| visit(argument);
|
| NamedArgument namedArgument = argument.asNamedArgument();
|
| if (namedArgument != null) {
|
| String source = namedArgument.name.source;
|
| + namedArguments.add(source);
|
| if (seenNamedArguments.containsKey(source)) {
|
| reportDuplicateDefinition(
|
| source,
|
| @@ -731,8 +732,10 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| } else if (!seenNamedArguments.isEmpty) {
|
| error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED);
|
| }
|
| + argumentCount++;
|
| }
|
| sendIsMemberAccess = oldSendIsMemberAccess;
|
| + return new CallStructure(argumentCount, namedArguments);
|
| }
|
|
|
| void registerTypeLiteralAccess(Send node, Element target) {
|
| @@ -770,9 +773,14 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| }
|
|
|
| /// Check that access to `super` is currently allowed.
|
| - bool checkSuperAccess(Node node) {
|
| + bool checkSuperAccess(Send node) {
|
| if (!inInstanceContext) {
|
| - compiler.reportError(node, MessageKind.NO_SUPER_AVAILABLE);
|
| + compiler.reportError(node, MessageKind.NO_SUPER_IN_STATIC);
|
| + return false;
|
| + }
|
| + if (node.isConditional) {
|
| + // `super?.foo` is not allowed.
|
| + compiler.reportError(node, MessageKind.INVALID_USE_OF_SUPER);
|
| return false;
|
| }
|
| if (currentClass.supertype == null) {
|
| @@ -786,6 +794,20 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return true;
|
| }
|
|
|
| + /// Check that access to `this` is currently allowed.
|
| + bool checkThisAccess(Send node) {
|
| + if (!inInstanceContext) {
|
| + compiler.reportError(node, MessageKind.NO_THIS_AVAILABLE);
|
| + return false;
|
| + }
|
| + if (node.isConditional) {
|
| + // `this?.foo` is not allowed.
|
| + compiler.reportError(node, MessageKind.INVALID_USE_OF_THIS);
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| +
|
| /// Compute the [AccessSemantics] corresponding to a super access of [target].
|
| AccessSemantics computeSuperAccess(Spannable node, Element target) {
|
| if (target.isErroneous) {
|
| @@ -803,6 +825,22 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| }
|
| }
|
|
|
| + /// 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 computeSuperSemantics(Spannable node,
|
| Selector selector,
|
| {Name alternateName}) {
|
| @@ -830,6 +868,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return computeSuperAccess(node, target);
|
| }
|
|
|
| + /// Resolve [node] as 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;
|
| sendIsMemberAccess = false;
|
| @@ -838,6 +878,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return result;
|
| }
|
|
|
| + /// Handle a type test expression, like `a is T` and `a is! T`.
|
| ResolutionResult handleIs(Send node) {
|
| Node expression = node.receiver;
|
| visitExpression(expression);
|
| @@ -864,6 +905,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| + /// Handle a type cast expression, like `a as T`.
|
| ResolutionResult handleAs(Send node) {
|
| Node expression = node.receiver;
|
| visitExpression(expression);
|
| @@ -875,6 +917,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| + /// Handle the unary expression of an unresolved unary operator [text], like
|
| + /// the no longer supported `+a`.
|
| ResolutionResult handleUnresolvedUnary(Send node, String text) {
|
| Node expression = node.receiver;
|
| if (node.isSuperCall) {
|
| @@ -887,6 +931,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| + /// Handle the unary expression of a user definable unary [operator], like
|
| + /// `-a`, and `-super`.
|
| ResolutionResult handleUserDefinableUnary(Send node, UnaryOperator operator) {
|
| Node expression = node.receiver;
|
| Selector selector = operator.selector;
|
| @@ -921,6 +967,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| + /// Handle a not expression, like `!a`.
|
| ResolutionResult handleNot(Send node, UnaryOperator operator) {
|
| assert(invariant(node, operator.kind == UnaryOperatorKind.NOT));
|
|
|
| @@ -931,6 +978,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| + /// Handle a logical and expression, like `a && b`.
|
| ResolutionResult handleLogicalAnd(Send node) {
|
| Node left = node.receiver;
|
| Node right = node.arguments.head;
|
| @@ -940,6 +988,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| + /// Handle a logical or expression, like `a || b`.
|
| ResolutionResult handleLogicalOr(Send node) {
|
| Node left = node.receiver;
|
| Node right = node.arguments.head;
|
| @@ -949,6 +998,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| + /// Handle an if-null expression, like `a ?? b`.
|
| ResolutionResult handleIfNull(Send node) {
|
| Node left = node.receiver;
|
| Node right = node.arguments.head;
|
| @@ -958,6 +1008,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| + /// Handle the binary expression of an unresolved binary operator [text], like
|
| + /// the no longer supported `a === b`.
|
| ResolutionResult handleUnresolvedBinary(Send node, String text) {
|
| Node left = node.receiver;
|
| Node right = node.arguments.head;
|
| @@ -971,6 +1023,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| + /// Handle the binary expression of a user definable binary [operator], like
|
| + /// `a + b`, `super + b`, `a == b` and `a != b`.
|
| ResolutionResult handleUserDefinableBinary(Send node,
|
| BinaryOperator operator) {
|
| Node left = node.receiver;
|
| @@ -997,7 +1051,6 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| // TODO(johnniwinther): Remove this when all information goes through
|
| // the [SendStructure].
|
| registry.useElement(node, semantics.element);
|
| -
|
| }
|
| } else {
|
| visitExpression(left);
|
| @@ -1048,65 +1101,333 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| return null;
|
| }
|
|
|
| - ResolutionResult visitSend(Send node) {
|
| - if (node.isOperator) {
|
| - String operatorText = node.selector.asOperator().source;
|
| - if (operatorText == 'is') {
|
| - return handleIs(node);
|
| - } else if (operatorText == 'as') {
|
| - return handleAs(node);
|
| - } else if (node.arguments.isEmpty) {
|
| - UnaryOperator operator = UnaryOperator.parse(operatorText);
|
| - if (operator == null) {
|
| - return handleUnresolvedUnary(node, operatorText);
|
| - } else {
|
| - switch (operator.kind) {
|
| - case UnaryOperatorKind.NOT:
|
| - return handleNot(node, operator);
|
| - case UnaryOperatorKind.COMPLEMENT:
|
| - case UnaryOperatorKind.NEGATE:
|
| - assert(invariant(node, operator.isUserDefinable,
|
| - message: "Unexpected unary operator '${operator}'."));
|
| - return handleUserDefinableUnary(node, operator);
|
| - }
|
| - return handleUserDefinableUnary(node, operator);
|
| + /// Handle an invocation of an expression, like `(){}()` or `(foo)()`.
|
| + ResolutionResult handleExpressionInvoke(Send node) {
|
| + assert(invariant(node, node.isCall,
|
| + message: "Unexpected expression: $node"));
|
| + Node expression = node.selector;
|
| + visitExpression(expression);
|
| + CallStructure callStructure = resolveArguments(node.argumentsNode);
|
| + Selector selector = callStructure.callSelector;
|
| + // TODO(johnniwinther): Remove this when all information goes through the
|
| + // [SendStructure].
|
| + registry.setSelector(node, selector);
|
| + registry.registerDynamicInvocation(selector);
|
| + registry.registerSendStructure(node,
|
| + new InvokeStructure(new AccessSemantics.expression(), selector));
|
| + return null;
|
| + }
|
| +
|
| + /// Handle a, possibly invalid, assertion, like `assert(cond)` or `assert()`.
|
| + ResolutionResult handleAssert(Send node) {
|
| + assert(invariant(node, node.isCall,
|
| + message: "Unexpected assert: $node"));
|
| + // If this send is of the form "assert(expr);", then
|
| + // this is an assertion.
|
| +
|
| + CallStructure callStructure = resolveArguments(node.argumentsNode);
|
| + SendStructure sendStructure = const AssertStructure();
|
| + if (callStructure.argumentCount != 1) {
|
| + compiler.reportError(
|
| + node.selector,
|
| + MessageKind.WRONG_NUMBER_OF_ARGUMENTS_FOR_ASSERT,
|
| + {'argumentCount': callStructure.argumentCount});
|
| + sendStructure = const InvalidAssertStructure();
|
| + } else if (callStructure.namedArgumentCount != 0) {
|
| + compiler.reportError(
|
| + node.selector,
|
| + MessageKind.ASSERT_IS_GIVEN_NAMED_ARGUMENTS,
|
| + {'argumentCount': callStructure.namedArgumentCount});
|
| + sendStructure = const InvalidAssertStructure();
|
| + }
|
| + registry.registerAssert(node);
|
| + registry.registerSendStructure(node, sendStructure);
|
| + return const AssertResult();
|
| + }
|
| +
|
| + /// 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 null;
|
| + }
|
| +
|
| + /// Handle access on `this`, like `this()` and `this` when it is parsed as a
|
| + /// [Send] node.
|
| + ResolutionResult handleThisAccess(Send node) {
|
| + AccessSemantics accessSemantics = new AccessSemantics.thisAccess();
|
| + if (node.isCall) {
|
| + CallStructure callStructure = resolveArguments(node.argumentsNode);
|
| + Selector selector = callStructure.callSelector;
|
| + // TODO(johnniwinther): Handle invalid this access as an
|
| + // [AccessSemantics].
|
| + if (checkThisAccess(node)) {
|
| + registry.registerDynamicInvocation(selector);
|
| + registry.registerSendStructure(node,
|
| + new InvokeStructure(accessSemantics, selector));
|
| + }
|
| + // TODO(johnniwinther): Remove this when all information goes through
|
| + // the [SendStructure].
|
| + registry.setSelector(node, selector);
|
| + } else {
|
| + // TODO(johnniwinther): Handle get of `this` when it is a [Send] node.
|
| + internalError(node, "Unexpected node '$node'.");
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + /// Handle access of a super property, like `super.foo` and `super.foo()`.
|
| + ResolutionResult handleSuperPropertyAccess(Send node, Name name) {
|
| + Element target;
|
| + Selector selector;
|
| + CallStructure callStructure = CallStructure.NO_ARGS;
|
| + if (node.isCall) {
|
| + callStructure = resolveArguments(node.argumentsNode);
|
| + selector = new Selector(SelectorKind.CALL, name, callStructure);
|
| + } else {
|
| + selector = new Selector(SelectorKind.GETTER, name, callStructure);
|
| + }
|
| + if (checkSuperAccess(node)) {
|
| + AccessSemantics semantics = computeSuperSemantics(
|
| + 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)) {
|
| + registry.registerThrowNoSuchMethod();
|
| + registry.registerDynamicInvocation(selector);
|
| + registry.registerSuperNoSuchMethod();
|
| + isIncompatibleInvoke = true;
|
| + } else {
|
| + registry.registerStaticInvocation(semantics.element);
|
| + }
|
| + break;
|
| + case AccessKind.SUPER_FIELD:
|
| + case AccessKind.SUPER_GETTER:
|
| + registry.registerStaticUse(semantics.element);
|
| + selector = callStructure.callSelector;
|
| + registry.registerDynamicInvocation(selector);
|
| + 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 {
|
| - BinaryOperator operator = BinaryOperator.parse(operatorText);
|
| - if (operator == null) {
|
| - return handleUnresolvedBinary(node, operatorText);
|
| - } else {
|
| - switch (operator.kind) {
|
| - case BinaryOperatorKind.LOGICAL_AND:
|
| - return handleLogicalAnd(node);
|
| - case BinaryOperatorKind.LOGICAL_OR:
|
| - return handleLogicalOr(node);
|
| - case BinaryOperatorKind.IF_NULL:
|
| - return handleIfNull(node);
|
| - case BinaryOperatorKind.EQ:
|
| - case BinaryOperatorKind.NOT_EQ:
|
| - case BinaryOperatorKind.INDEX:
|
| - case BinaryOperatorKind.ADD:
|
| - case BinaryOperatorKind.SUB:
|
| - case BinaryOperatorKind.MUL:
|
| - case BinaryOperatorKind.DIV:
|
| - case BinaryOperatorKind.IDIV:
|
| - case BinaryOperatorKind.MOD:
|
| - case BinaryOperatorKind.SHL:
|
| - case BinaryOperatorKind.SHR:
|
| - case BinaryOperatorKind.GTEQ:
|
| - case BinaryOperatorKind.GT:
|
| - case BinaryOperatorKind.LTEQ:
|
| - case BinaryOperatorKind.LT:
|
| - case BinaryOperatorKind.AND:
|
| - case BinaryOperatorKind.OR:
|
| - case BinaryOperatorKind.XOR:
|
| - return handleUserDefinableBinary(node, operator);
|
| - }
|
| + 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_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));
|
| }
|
| + target = semantics.element;
|
| }
|
|
|
| + // TODO(johnniwinther): Remove these when all information goes through
|
| + // the [SendStructure].
|
| + registry.useElement(node, target);
|
| + registry.setSelector(node, selector);
|
| + return null;
|
| + }
|
| +
|
| + /// Handle a [Send] whose selector is an [Operator], like `a && b`, `a is T`,
|
| + /// `a + b`, and `~a`.
|
| + ResolutionResult handleOperatorSend(Send node) {
|
| + String operatorText = node.selector.asOperator().source;
|
| + if (operatorText == 'is') {
|
| + return handleIs(node);
|
| + } else if (operatorText == 'as') {
|
| + return handleAs(node);
|
| + } else if (node.arguments.isEmpty) {
|
| + UnaryOperator operator = UnaryOperator.parse(operatorText);
|
| + if (operator == null) {
|
| + return handleUnresolvedUnary(node, operatorText);
|
| + } else {
|
| + switch (operator.kind) {
|
| + case UnaryOperatorKind.NOT:
|
| + return handleNot(node, operator);
|
| + case UnaryOperatorKind.COMPLEMENT:
|
| + case UnaryOperatorKind.NEGATE:
|
| + assert(invariant(node, operator.isUserDefinable,
|
| + message: "Unexpected unary operator '${operator}'."));
|
| + return handleUserDefinableUnary(node, operator);
|
| + }
|
| + return handleUserDefinableUnary(node, operator);
|
| + }
|
| + } else {
|
| + BinaryOperator operator = BinaryOperator.parse(operatorText);
|
| + if (operator == null) {
|
| + return handleUnresolvedBinary(node, operatorText);
|
| + } else {
|
| + switch (operator.kind) {
|
| + case BinaryOperatorKind.LOGICAL_AND:
|
| + return handleLogicalAnd(node);
|
| + case BinaryOperatorKind.LOGICAL_OR:
|
| + return handleLogicalOr(node);
|
| + case BinaryOperatorKind.IF_NULL:
|
| + return handleIfNull(node);
|
| + case BinaryOperatorKind.EQ:
|
| + case BinaryOperatorKind.NOT_EQ:
|
| + case BinaryOperatorKind.INDEX:
|
| + case BinaryOperatorKind.ADD:
|
| + case BinaryOperatorKind.SUB:
|
| + case BinaryOperatorKind.MUL:
|
| + case BinaryOperatorKind.DIV:
|
| + case BinaryOperatorKind.IDIV:
|
| + case BinaryOperatorKind.MOD:
|
| + case BinaryOperatorKind.SHL:
|
| + case BinaryOperatorKind.SHR:
|
| + case BinaryOperatorKind.GTEQ:
|
| + case BinaryOperatorKind.GT:
|
| + case BinaryOperatorKind.LTEQ:
|
| + case BinaryOperatorKind.LT:
|
| + case BinaryOperatorKind.AND:
|
| + case BinaryOperatorKind.OR:
|
| + case BinaryOperatorKind.XOR:
|
| + return handleUserDefinableBinary(node, operator);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /// 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) {
|
| + return handleSuperPropertyAccess(node, name);
|
| + } else if (node.receiver.isThis()) {
|
| + if (checkThisAccess(node)) {
|
| + return handleThisPropertyAccess(node, name);
|
| + }
|
| + // TODO(johnniwinther): Handle invalid this access as an
|
| + // [AccessSemantics].
|
| + return null;
|
| + }
|
| + // TODO(johnniwinther): Handle remaining qualified sends.
|
| + return oldVisitSend(node);
|
| + }
|
| +
|
| + /// Handle access unresolved access to [name] in a non-instance context.
|
| + ResolutionResult handleUnresolvedAccess(
|
| + Send node, Name name, Element element) {
|
| + // TODO(johnniwinther): Support unresolved top level access as an
|
| + // [AccessSemantics].
|
| + AccessSemantics accessSemantics = new StaticAccess.unresolved(element);
|
| + 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);
|
| + }
|
| + // TODO(johnniwinther): Remove this when all information goes through
|
| + // the [SendStructure].
|
| + registry.setSelector(node, selector);
|
| + registry.useElement(node, element);
|
| + registry.registerSendStructure(node, sendStructure);
|
| + return null;
|
| + }
|
| +
|
| + /// Handle an unqualified [Send], that is where the `node.receiver` is null,
|
| + /// like `a`, `a()`, `this()`, `assert()`, and `(){}()`.
|
| + ResolutionResult handleUnqualifiedSend(Send node) {
|
| + Identifier selector = node.selector.asIdentifier();
|
| + if (selector == null) {
|
| + // `(){}()` and `(foo)()`.
|
| + return handleExpressionInvoke(node);
|
| + }
|
| + String text = selector.source;
|
| + if (text == 'assert') {
|
| + // `assert()`.
|
| + return handleAssert(node);
|
| + } else if (text == 'this') {
|
| + // `this()`.
|
| + return handleThisAccess(node);
|
| + } else if (text == 'dynamic') {
|
| + // `dynamic` || `dynamic()`.
|
| + // TODO(johnniwinther): Handle dynamic type literal access.
|
| + return oldVisitSend(node);
|
| + }
|
| + // `name` or `name()`
|
| + Name name = new Name(text, enclosingElement.library);
|
| + Element element = lookupInScope(compiler, node, scope, text);
|
| + if (element == null) {
|
| + if (inInstanceContext) {
|
| + // Implicitly `this.name`.
|
| + return handleThisPropertyAccess(node, name);
|
| + } else {
|
| + // Create [ErroneousElement] for unresolved access.
|
| + ErroneousElement error = reportCannotResolve(node, text);
|
| + return handleUnresolvedAccess(node, name, error);
|
| + }
|
| + }
|
| + return oldVisitSend(node);
|
| + }
|
| +
|
| + ResolutionResult visitSend(Send node) {
|
| + if (node.isOperator) {
|
| + return handleOperatorSend(node);
|
| + } else if (node.receiver != null) {
|
| + return handleQualifiedSend(node);
|
| + } else {
|
| + return handleUnqualifiedSend(node);
|
| + }
|
| + return oldVisitSend(node);
|
| + }
|
| +
|
| + ResolutionResult oldVisitSend(Send node) {
|
| bool oldSendIsMemberAccess = sendIsMemberAccess;
|
| sendIsMemberAccess = node.isPropertyAccess || node.isCall;
|
|
|
| @@ -1190,12 +1511,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
| if (!selector.applies(target, compiler.world)) {
|
| registry.registerThrowNoSuchMethod();
|
| if (node.isSuperCall) {
|
| - // Similar to what we do when we can't find super via selector
|
| - // in [resolveSend] above, we still need to register the invocation,
|
| - // because we might call [:super.noSuchMethod:] which calls
|
| - // [JSInvocationMirror._invokeOn].
|
| - registry.registerDynamicInvocation(selector);
|
| - registry.registerSuperNoSuchMethod();
|
| + internalError(node, "Unexpected super call $node");
|
| }
|
| }
|
| }
|
| @@ -1229,8 +1545,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|
|
| /// Callback for native enqueuer to parse a type. Returns [:null:] on error.
|
| DartType resolveTypeFromString(Node node, String typeName) {
|
| - Element element = lookupInScope(compiler, node,
|
| - scope, typeName);
|
| + Element element = lookupInScope(compiler, node, scope, typeName);
|
| if (element == null) return null;
|
| if (element is! ClassElement) return null;
|
| ClassElement cls = element;
|
|
|