Remove SendResolver.computeSendStructure.

BUG=
R=floitsch@google.com

Committed: 4013a9aae5

Reverted: 73a49a5ddb

Review URL: https://codereview.chromium.org//1306143002.
This commit is contained in:
Johnni Winther 2015-08-25 09:32:26 +02:00
parent 304dc3486f
commit 4ed23aff42
9 changed files with 109 additions and 573 deletions

View file

@ -9,7 +9,8 @@ import '../compiler.dart' show
import '../dart_types.dart' show
DartType;
import '../elements/elements.dart' show
AstElement;
AstElement,
ErroneousElement;
import '../enqueue.dart' show
ResolutionEnqueuer,
WorldImpact;
@ -94,6 +95,9 @@ class ResolutionCallbacks {
/// Register that the application may throw a [RuntimeError].
void onThrowRuntimeError(Registry registry) {}
/// Register that the application has a compile time error.
void onCompileTimeError(Registry registry, ErroneousElement error) {}
/// Register that the application may throw an
/// [AbstractClassInstantiationError].
void onAbstractClassInstantiation(Registry registry) {}

View file

@ -782,6 +782,11 @@ abstract class InferrerVisitor<T, E extends MinimalInferrerEngine<T>>
return handleSendSet(node);
}
@override
T bulkHandleError(Node node, ErroneousElement error, _) {
return types.dynamicType;
}
@override
T visitAssert(Send node, Node expression, _) {
if (!compiler.enableUserAssertions) {

View file

@ -850,9 +850,12 @@ class SimpleTypeInferrerVisitor<T>
isCallOnThis = true;
}
} else {
if (node.receiver != null && elements[node.receiver] is! PrefixElement) {
// TODO(johnniwinther): Avoid blindly recursing on the receiver.
receiverType = visit(node.receiver);
if (node.receiver != null) {
Element receiver = elements[node.receiver];
if (receiver is! PrefixElement && receiver is! ClassElement) {
// TODO(johnniwinther): Avoid blindly recursing on the receiver.
receiverType = visit(node.receiver);
}
}
isCallOnThis = isThisOrSuper(node.receiver);
}

View file

@ -3049,6 +3049,13 @@ class JavaScriptResolutionCallbacks extends ResolutionCallbacks {
registerBackendInstantiation(backend.compiler.stringClass, registry);
}
void onCompileTimeError(Registry registry, ErroneousElement error) {
if (backend.compiler.generateCodeWithCompileTimeErrors) {
// TODO(johnniwinther): This should have its own uncatchable error.
onThrowRuntimeError(registry);
}
}
void onSuperNoSuchMethod(Registry registry) {
assert(registry.isForResolution);
registerBackendStaticInvocation(

View file

@ -762,41 +762,49 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
/// [AccessSemantics] in case of an error, `null` otherwise.
AccessSemantics checkSuperAccess(Send node) {
if (!inInstanceContext) {
return new StaticAccess.invalid(
reportAndCreateErroneousElement(
node, 'super',
MessageKind.NO_SUPER_IN_STATIC, {},
isError: true));
ErroneousElement error = reportAndCreateErroneousElement(
node, 'super',
MessageKind.NO_SUPER_IN_STATIC, {},
isError: true);
registry.registerCompileTimeError(error);
return new StaticAccess.invalid(error);
}
if (node.isConditional) {
// `super?.foo` is not allowed.
return new StaticAccess.invalid(
reportAndCreateErroneousElement(
node, 'super',
MessageKind.INVALID_USE_OF_SUPER, {},
isError: true));
ErroneousElement error = reportAndCreateErroneousElement(
node, 'super',
MessageKind.INVALID_USE_OF_SUPER, {},
isError: true);
registry.registerCompileTimeError(error);
return new StaticAccess.invalid(error);
}
if (currentClass.supertype == null) {
// This is just to guard against internal errors, so no need
// for a real error message.
return new StaticAccess.invalid(
reportAndCreateErroneousElement(
node, 'super',
MessageKind.GENERIC,
{'text': "Object has no superclass"},
isError: true));
ErroneousElement error = reportAndCreateErroneousElement(
node, 'super',
MessageKind.GENERIC,
{'text': "Object has no superclass"},
isError: true);
registry.registerCompileTimeError(error);
return new StaticAccess.invalid(error);
}
registry.registerSuperUse(node);
return null;
}
/// Check that access to `this` is currently allowed.
bool checkThisAccess(Send node) {
/// Check that access to `this` is currently allowed. Returns an
/// [AccessSemantics] in case of an error, `null` otherwise.
AccessSemantics checkThisAccess(Send node) {
if (!inInstanceContext) {
compiler.reportError(node, MessageKind.NO_THIS_AVAILABLE);
return false;
ErroneousElement error = reportAndCreateErroneousElement(
node, 'this',
MessageKind.NO_THIS_AVAILABLE, const {},
isError: true);
registry.registerCompileTimeError(error);
return new StaticAccess.invalid(error);
}
return true;
return null;
}
/// Compute the [AccessSemantics] corresponding to a super access of [target].
@ -1564,22 +1572,24 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
/// Handle access on `this`, like `this()` and `this` when it is parsed as a
/// [Send] node.
ResolutionResult handleThisAccess(Send node) {
AccessSemantics accessSemantics = const DynamicAccess.thisAccess();
if (node.isCall) {
CallStructure callStructure =
resolveArguments(node.argumentsNode).callStructure;
Selector selector = callStructure.callSelector;
// TODO(johnniwinther): Handle invalid this access as an
// [AccessSemantics].
if (checkThisAccess(node)) {
AccessSemantics accessSemantics = checkThisAccess(node);
if (accessSemantics == null) {
accessSemantics = const DynamicAccess.thisAccess();
registry.registerDynamicInvocation(
new UniverseSelector(selector, null));
registry.registerSendStructure(node,
new InvokeStructure(accessSemantics, selector));
}
registry.registerSendStructure(node,
new InvokeStructure(accessSemantics, selector));
// TODO(23998): Remove this when all information goes through
// the [SendStructure].
registry.setSelector(node, selector);
return const NoneResult();
} else {
// TODO(johnniwinther): Handle get of `this` when it is a [Send] node.
internalError(node, "Unexpected node '$node'.");
@ -1938,6 +1948,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
MessageKind.TYPE_VARIABLE_WITHIN_STATIC_MEMBER,
{'typeVariableName': name},
isError: true);
registry.registerCompileTimeError(error);
semantics = new StaticAccess.invalid(error);
// TODO(johnniwinther): Clean up registration of elements and selectors
// for this case.
@ -1983,6 +1994,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
MessageKind.TYPE_VARIABLE_WITHIN_STATIC_MEMBER,
{'typeVariableName': name},
isError: true);
registry.registerCompileTimeError(error);
semantics = new StaticAccess.invalid(error);
} else {
ErroneousElement error;
@ -2273,6 +2285,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
MessageKind.PREFIX_AS_EXPRESSION,
{'prefix': name},
isError: true);
registry.registerCompileTimeError(error);
return handleErroneousAccess(
node, name, new StaticAccess.invalid(error));
}
@ -2394,6 +2407,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
name.text,
MessageKind.THIS_PROPERTY, {},
isError: true);
registry.registerCompileTimeError(error);
AccessSemantics accessSemantics = new StaticAccess.invalid(error);
return handleErroneousAccess(node, name, accessSemantics);
}
@ -2409,12 +2423,14 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
} else if (node.isSuperCall) {
return handleSuperPropertyAccess(node, name);
} else if (node.receiver.isThis()) {
if (checkThisAccess(node)) {
AccessSemantics semantics = checkThisAccess(node);
if (semantics == null) {
return handleThisPropertyAccess(node, name);
} else {
// TODO(johnniwinther): Handle invalid this access as an
// [AccessSemantics].
return handleErroneousAccess(node, name, semantics);
}
// TODO(johnniwinther): Handle invalid this access as an
// [AccessSemantics].
return const NoneResult();
}
ResolutionResult result = visitExpressionPrefix(node.receiver);
if (result.kind == ResultKind.PREFIX) {
@ -2440,12 +2456,14 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
if (text == 'this') {
return handleQualifiedThisAccess(node, name);
} else if (node.receiver.isThis()) {
if (checkThisAccess(node)) {
AccessSemantics semantics = checkThisAccess(node);
if (semantics == null) {
return handleThisPropertyUpdate(node, name, null);
} else {
// TODO(johnniwinther): Handle invalid this access as an
// [AccessSemantics].
return handleUpdate(node, name, semantics);
}
// TODO(johnniwinther): Handle invalid this access as an
// [AccessSemantics].
return const NoneResult();
}
ResolutionResult result = visitExpressionPrefix(node.receiver);
if (result.kind == ResultKind.PREFIX) {
@ -2545,6 +2563,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
isError: true);
// TODO(johnniwinther): Support static instance access as an
// [AccessSemantics].
registry.registerCompileTimeError(error);
return new StaticAccess.invalid(error);
}

View file

@ -416,6 +416,10 @@ class ResolutionRegistry implements Registry {
backend.resolutionCallbacks.onThrowRuntimeError(this);
}
void registerCompileTimeError(ErroneousElement error) {
backend.resolutionCallbacks.onCompileTimeError(this, error);
}
void registerTypeVariableBoundCheck() {
backend.resolutionCallbacks.onTypeVariableBoundCheck(this);
}

View file

@ -44,7 +44,7 @@ abstract class SemanticSendResolvedMixin<R, A>
// TODO(johnniwinther): Support argument.
A arg = null;
SendStructure structure = computeSendStructure(node);
SendStructure structure = elements.getSendStructure(node);
if (structure == null) {
return internalError(node, 'No structure for $node');
} else {

View file

@ -6,8 +6,6 @@ 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
@ -18,548 +16,15 @@ 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,

View file

@ -1107,6 +1107,16 @@ const Map<String, List<Test>> SEND_TESTS = const {
''',
const Visit(VisitKind.VISIT_THIS_INVOKE,
arguments: '(null,42)')),
const Test.clazz(
'''
class C {
call(a, b) {}
static m() { this(null, 42); }
}
''',
const Visit(VisitKind.ERROR_INVALID_INVOKE,
error: MessageKind.NO_THIS_AVAILABLE,
arguments: '(null,42)')),
],
'This properties': const [
// This properties
@ -1137,6 +1147,15 @@ const Map<String, List<Test>> SEND_TESTS = const {
''',
const Visit(VisitKind.VISIT_THIS_PROPERTY_GET,
name: 'foo')),
const Test.clazz(
'''
class C {
var foo;
static m() => this.foo;
}
''',
const Visit(VisitKind.ERROR_INVALID_GET,
error: MessageKind.NO_THIS_AVAILABLE)),
const Test.clazz(
'''
class C {
@ -1206,6 +1225,16 @@ const Map<String, List<Test>> SEND_TESTS = const {
const Visit(VisitKind.VISIT_THIS_PROPERTY_INVOKE,
name: 'foo',
arguments: '(null,42)')),
const Test.clazz(
'''
class C {
var foo;
static m() { this.foo(null, 42); }
}
''',
const Visit(VisitKind.ERROR_INVALID_INVOKE,
error: MessageKind.NO_THIS_AVAILABLE,
arguments: '(null,42)')),
],
'Super fields': const [
// Super fields