blob: ac69128acc33c2324b8eca66c562c4542497df78 [file] [log] [blame]
// Copyright (c) 2017, 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.
/// Declares classes which describe a call: selectors and arguments.
library vm.transformations.type_flow.calls;
import 'dart:core' hide Type;
import 'package:kernel/ast.dart';
import 'types.dart';
import 'utils.dart';
enum CallKind {
Method, // x.foo(..) or foo()
PropertyGet, // ... x.foo ...
PropertySet, // x.foo = ...
FieldInitializer,
}
/// [Selector] encapsulates the way of calling (at the call site).
abstract class Selector {
/// Call kind: how call is performed?
final CallKind callKind;
Selector(this.callKind);
/// Interface or concrete target, may be null.
Member get member;
/// Selector name.
Name get name => member.name;
bool get isSetter => (callKind == CallKind.PropertySet);
@override
int get hashCode => callKind.hashCode;
@override
bool operator ==(other) => other is Selector && other.callKind == callKind;
/// Static approximation of Dart return type.
DartType get staticReturnType {
if (member == null) {
return const DynamicType();
}
switch (callKind) {
case CallKind.Method:
return (member is Procedure)
? member.function.returnType
: const BottomType();
case CallKind.PropertyGet:
return member.getterType;
case CallKind.PropertySet:
case CallKind.FieldInitializer:
return const BottomType();
}
return null;
}
bool memberAgreesToCallKind(Member member) {
switch (callKind) {
case CallKind.Method:
return ((member is Procedure) &&
!member.isGetter &&
!member.isSetter) ||
(member is Constructor);
case CallKind.PropertyGet:
return (member is Field) || ((member is Procedure) && member.isGetter);
case CallKind.PropertySet:
return (member is Field) || ((member is Procedure) && member.isSetter);
case CallKind.FieldInitializer:
return (member is Field);
}
return false;
}
String get _callKindPrefix {
switch (callKind) {
case CallKind.Method:
return '';
case CallKind.PropertyGet:
return 'get ';
case CallKind.PropertySet:
return 'set ';
case CallKind.FieldInitializer:
return 'init ';
}
return '';
}
}
/// Direct call to [member].
class DirectSelector extends Selector {
final Member member;
DirectSelector(this.member, {CallKind callKind = CallKind.Method})
: super(callKind) {
assertx((callKind == CallKind.Method) ||
(callKind == CallKind.PropertyGet) ||
memberAgreesToCallKind(member));
}
@override
int get hashCode => (super.hashCode ^ member.hashCode) & kHashMask;
@override
bool operator ==(other) =>
other is DirectSelector && super == (other) && other.member == member;
@override
String toString() => 'direct ${_callKindPrefix}[$member]';
}
/// Interface call via known interface target [member].
class InterfaceSelector extends Selector {
final Member member;
InterfaceSelector(this.member, {CallKind callKind = CallKind.Method})
: super(callKind) {
assertx(memberAgreesToCallKind(member));
}
@override
int get hashCode => (super.hashCode ^ member.hashCode + 31) & kHashMask;
@override
bool operator ==(other) =>
other is InterfaceSelector && super == (other) && other.member == member;
@override
String toString() => '${_callKindPrefix}[$member]';
}
/// Dynamic call.
class DynamicSelector extends Selector {
@override
final Name name;
static final kCall = new DynamicSelector(CallKind.Method, new Name('call'));
DynamicSelector(CallKind callKind, this.name) : super(callKind);
@override
Member get member => null;
@override
int get hashCode => (super.hashCode ^ name.hashCode + 37) & kHashMask;
@override
bool operator ==(other) =>
other is DynamicSelector && super == (other) && other.name == name;
@override
String toString() => 'dynamic ${_callKindPrefix}[$name]';
}
/// Arguments passed to a call, including implicit receiver argument.
// TODO(alexmarkov): take type arguments into account
class Args<T extends TypeExpr> {
final List<T> values;
final List<String> names;
int _hashCode;
Args(this.values, {this.names = const <String>[]}) {
assertx(isSorted(names));
}
Args.withReceiver(Args<T> args, T receiver)
: values = new List.from(args.values),
names = args.names {
values[0] = receiver;
}
int get positionalCount => values.length - names.length;
int get namedCount => names.length;
T get receiver => values[0];
@override
int get hashCode => _hashCode ??= _computeHashCode();
int _computeHashCode() {
int hash = 1231;
for (var v in values) {
hash = (((hash * 31) & kHashMask) + v.hashCode) & kHashMask;
}
for (var n in names) {
hash = (((hash * 31) & kHashMask) + n.hashCode) & kHashMask;
}
return hash;
}
@override
bool operator ==(other) {
if (other is Args<T> &&
(this.values.length == other.values.length) &&
(this.names.length == other.names.length)) {
for (int i = 0; i < values.length; i++) {
if (values[i] != other.values[i]) {
return false;
}
}
for (int i = 0; i < names.length; i++) {
if (names[i] != other.names[i]) {
return false;
}
}
return true;
}
return false;
}
@override
String toString() {
StringBuffer buf = new StringBuffer();
buf.write("(");
for (int i = 0; i < positionalCount; i++) {
if (i != 0) {
buf.write(", ");
}
buf.write(values[i]);
}
for (int i = 0; i < names.length; i++) {
if (positionalCount + i != 0) {
buf.write(", ");
}
buf.write(names[i]);
buf.write(': ');
buf.write(values[positionalCount + i]);
}
buf.write(")");
return buf.toString();
}
}