blob: aa5a747db0f6ab531631e634b1954ea03d0d272b [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart2js.semantics_visitor;
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 handleStaticallyResolvedAccess(Send node,
Element element,
Element getter) {
if (element.isErroneous) {
return new StaticAccess.unresolved(element);
} else if (element.isParameter) {
return new StaticAccess.parameter(element);
} else if (element.isLocal) {
if (element.isFunction) {
return new StaticAccess.localFunction(element);
} else {
return new StaticAccess.localVariable(element);
}
} else if (element.isStatic) {
if (element.isField) {
return new StaticAccess.staticField(element);
} else if (element.isGetter) {
return new StaticAccess.staticGetter(element);
} else if (element.isSetter) {
if (getter != null) {
CompoundAccessKind accessKind;
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) {
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.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) {
if (elements.isAssert(node)) {
if (!node.arguments.isEmpty && node.arguments.tail.isEmpty) {
return const AssertStructure();
} else {
return const InvalidAssertStructure();
}
}
AssignmentOperator assignmentOperator;
UnaryOperator unaryOperator;
BinaryOperator binaryOperator;
IncDecOperator incDecOperator;
if (node.isOperator) {
String operatorText = node.selector.asOperator().source;
if (operatorText == 'is') {
if (node.isIsNotCheck) {
return new IsNotStructure(
elements.getType(node.arguments.single.asSend().receiver));
} else {
return new IsStructure(elements.getType(node.arguments.single));
}
} else if (operatorText == 'as') {
return new AsStructure(elements.getType(node.arguments.single));
} else if (operatorText == '&&') {
return const LogicalAndStructure();
} else if (operatorText == '||') {
return const LogicalOrStructure();
}
}
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) {
unaryOperator = UnaryOperator.parse(operatorText);
if (unaryOperator != null) {
switch (unaryOperator.kind) {
case UnaryOperatorKind.NOT:
kind = SendStructureKind.NOT;
break;
default:
kind = SendStructureKind.UNARY;
break;
}
} else {
return const InvalidUnaryStructure();
}
} else {
binaryOperator = BinaryOperator.parse(operatorText);
if (binaryOperator != null) {
switch (binaryOperator.kind) {
case BinaryOperatorKind.EQ:
kind = SendStructureKind.EQ;
break;
case BinaryOperatorKind.NOT_EQ:
if (node.isSuperCall) {
// `super != foo` is a compile-time error.
return const InvalidBinaryStructure();
}
kind = SendStructureKind.NOT_EQ;
break;
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;
} 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;
break;
}
} else {
return const InvalidBinaryStructure();
}
}
}
AccessSemantics semantics = computeAccessSemantics(
node,
isGetOrSet: kind == SendStructureKind.GET ||
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:
return new InvokeStructure(semantics, selector);
case SendStructureKind.UNARY:
return new UnaryStructure(semantics, unaryOperator, selector);
case SendStructureKind.NOT:
assert(selector == null);
return new NotStructure(semantics, selector);
case SendStructureKind.BINARY:
return new BinaryStructure(semantics, binaryOperator, selector);
case SendStructureKind.INDEX:
return new IndexStructure(semantics, selector);
case SendStructureKind.EQ:
return new EqualsStructure(semantics, selector);
case SendStructureKind.NOT_EQ:
return new NotEqualsStructure(semantics, selector);
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 isGetOrSet: 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);
TypeConstantExpression constant = elements.getConstant(
isInvoke ? 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)) {
return new StaticAccess.unresolved(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 StaticAccess.unresolved(getter);
} 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);
}
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.isOperator) {
return new DynamicAccess.dynamicProperty(node.receiver);
} else if (Elements.isClosureSend(node, element)) {
if (element == null) {
if (node.selector.isThis()) {
return new AccessSemantics.thisAccess();
} else {
return new AccessSemantics.expression();
}
} else if (Elements.isErroneous(element)) {
return new StaticAccess.unresolved(element);
} else {
return handleStaticallyResolvedAccess(node, element, getter);
}
} else {
if (Elements.isErroneous(element)) {
return new StaticAccess.unresolved(element);
} else if (element == null || element.isInstanceMember) {
if (node.receiver == null || node.receiver.isThis()) {
return new AccessSemantics.thisProperty();
} else {
return new DynamicAccess.dynamicProperty(node.receiver);
}
} else if (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);
}
}
}
}