mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
Remove SendResolver.computeSendStructure.
BUG= R=floitsch@google.com Committed:4013a9aae5
Reverted:73a49a5ddb
Review URL: https://codereview.chromium.org//1306143002.
This commit is contained in:
parent
304dc3486f
commit
4ed23aff42
9 changed files with 109 additions and 573 deletions
|
@ -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) {}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue