Use AbstractValue/AbstractValueDomain in function_set.dart
Change-Id: I8c2e80d7d6722254eec2c8753c2f37a495a9026a
Reviewed-on: https://dart-review.googlesource.com/55899
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/types/abstract_value_domain.dart b/pkg/compiler/lib/src/types/abstract_value_domain.dart
index 4b2849d..6b35ca4 100644
--- a/pkg/compiler/lib/src/types/abstract_value_domain.dart
+++ b/pkg/compiler/lib/src/types/abstract_value_domain.dart
@@ -6,6 +6,7 @@
import '../constants/values.dart' show ConstantValue;
import '../elements/entities.dart';
+import '../universe/selector.dart';
/// A value in an abstraction of runtime values.
abstract class AbstractValue {}
@@ -255,4 +256,16 @@
/// Returns the value type of [value] if it represents a map value at runtime.
/// Returns [dynamicType] otherwise.
AbstractValue getMapValueType(AbstractValue value);
+
+ /// Compute the type of all potential receivers of the set of live [members].
+ AbstractValue computeReceiver(Iterable<MemberEntity> members);
+
+ /// Returns whether [member] is a potential target when being
+ /// invoked on a [receiver]. [selector] is used to ensure library privacy is
+ /// taken into account.
+ bool canHit(AbstractValue receiver, MemberEntity member, Selector selector);
+
+ /// Returns whether [selector] invoked on a [receiver] can hit a
+ /// [noSuchMethod].
+ bool needsNoSuchMethodHandling(AbstractValue receiver, Selector selector);
}
diff --git a/pkg/compiler/lib/src/types/masks.dart b/pkg/compiler/lib/src/types/masks.dart
index 22a39b0..8e4916c 100644
--- a/pkg/compiler/lib/src/types/masks.dart
+++ b/pkg/compiler/lib/src/types/masks.dart
@@ -409,4 +409,37 @@
}
return result;
}
+
+ @override
+ AbstractValue computeReceiver(Iterable<MemberEntity> members) {
+ assert(_closedWorld
+ .hasAnyStrictSubclass(_closedWorld.commonElements.objectClass));
+ return new TypeMask.unionOf(
+ members.expand((MemberEntity element) {
+ ClassEntity cls = element.enclosingClass;
+ return [cls]..addAll(_closedWorld.mixinUsesOf(cls));
+ }).map((cls) {
+ if (_closedWorld.commonElements.jsNullClass == cls) {
+ return const TypeMask.empty();
+ } else if (_closedWorld.isInstantiated(cls)) {
+ return new TypeMask.nonNullSubclass(cls, _closedWorld);
+ } else {
+ // TODO(johnniwinther): Avoid the need for this case.
+ return const TypeMask.empty();
+ }
+ }),
+ _closedWorld);
+ }
+
+ @override
+ bool canHit(
+ covariant TypeMask receiver, MemberEntity member, Selector selector) {
+ return receiver.canHit(member, selector, _closedWorld);
+ }
+
+ @override
+ bool needsNoSuchMethodHandling(
+ covariant TypeMask receiver, Selector selector) {
+ return receiver.needsNoSuchMethodHandling(selector, _closedWorld);
+ }
}
diff --git a/pkg/compiler/lib/src/universe/function_set.dart b/pkg/compiler/lib/src/universe/function_set.dart
index 4d64297..aee80f5 100644
--- a/pkg/compiler/lib/src/universe/function_set.dart
+++ b/pkg/compiler/lib/src/universe/function_set.dart
@@ -6,11 +6,9 @@
import '../common/names.dart' show Identifiers, Selectors;
import '../elements/entities.dart';
-import '../types/masks.dart';
+import '../types/abstract_value_domain.dart';
import '../util/util.dart' show Hashing, Setlet;
-import '../world.dart' show ClosedWorld;
import 'selector.dart' show Selector;
-import 'world_builder.dart' show ReceiverConstraint;
// TODO(kasperl): This actually holds getters and setters just fine
// too and stricly they aren't functions. Maybe this needs a better
@@ -41,9 +39,9 @@
/// receiver with the given [constraint]. The returned elements may include
/// noSuchMethod handlers that are potential targets indirectly through the
/// noSuchMethod mechanism.
- Iterable<MemberEntity> filter(Selector selector,
- ReceiverConstraint constraint, ClosedWorld closedWorld) {
- return query(selector, constraint, closedWorld).functions;
+ Iterable<MemberEntity> filter(
+ Selector selector, AbstractValue receiver, AbstractValueDomain domain) {
+ return query(selector, receiver, domain).functions;
}
/// Returns the mask for the potential receivers of a dynamic call to
@@ -52,43 +50,38 @@
/// This will narrow the constraints of [constraint] to a [TypeMask] of the
/// set of classes that actually implement the selected member or implement
/// the handling 'noSuchMethod' where the selected member is unimplemented.
- TypeMask receiverType(Selector selector, ReceiverConstraint constraint,
- ClosedWorld closedWorld) {
- return query(selector, constraint, closedWorld).computeMask(closedWorld);
+ AbstractValue receiverType(
+ Selector selector, AbstractValue receiver, AbstractValueDomain domain) {
+ return query(selector, receiver, domain).computeMask(domain);
}
- SelectorMask _createSelectorMask(Selector selector,
- ReceiverConstraint constraint, ClosedWorld closedWorld) {
- return constraint != null
- ? new SelectorMask(selector, constraint)
- : new SelectorMask(
- selector,
- new TypeMask.subclass(
- closedWorld.commonElements.objectClass, closedWorld));
+ SelectorMask _createSelectorMask(
+ Selector selector, AbstractValue receiver, AbstractValueDomain domain) {
+ return receiver != null
+ ? new SelectorMask(selector, receiver)
+ : new SelectorMask(selector, domain.dynamicType);
}
/// Returns the set of functions that can be the target of a call to
/// [selector] on a receiver constrained by [constraint] including
/// 'noSuchMethod' methods where applicable.
- FunctionSetQuery query(Selector selector, ReceiverConstraint constraint,
- ClosedWorld closedWorld) {
+ FunctionSetQuery query(
+ Selector selector, AbstractValue receiver, AbstractValueDomain domain) {
String name = selector.name;
- SelectorMask selectorMask =
- _createSelectorMask(selector, constraint, closedWorld);
+ SelectorMask selectorMask = _createSelectorMask(selector, receiver, domain);
SelectorMask noSuchMethodMask =
- new SelectorMask(Selectors.noSuchMethod_, selectorMask.constraint);
+ new SelectorMask(Selectors.noSuchMethod_, selectorMask.receiver);
FunctionSetNode node = _nodes[name];
FunctionSetNode noSuchMethods = _nodes[Identifiers.noSuchMethod_];
if (node != null) {
- return node.query(
- selectorMask, closedWorld, noSuchMethods, noSuchMethodMask);
+ return node.query(selectorMask, domain, noSuchMethods, noSuchMethodMask);
}
// If there is no method that matches [selector] we know we can
// only hit [:noSuchMethod:].
if (noSuchMethods == null) {
return const EmptyFunctionSetQuery();
}
- return noSuchMethods.query(noSuchMethodMask, closedWorld);
+ return noSuchMethods.query(noSuchMethodMask, domain);
}
void forEach(void action(MemberEntity member)) {
@@ -102,34 +95,32 @@
/// on a receiver constrained by [constraint].
class SelectorMask {
final Selector selector;
- final ReceiverConstraint constraint;
+ final AbstractValue receiver;
final int hashCode;
- SelectorMask(Selector selector, ReceiverConstraint constraint)
- : this.selector = selector,
- this.constraint = constraint,
- this.hashCode =
- Hashing.mixHashCodeBits(selector.hashCode, constraint.hashCode) {
- assert(constraint != null);
+ SelectorMask(this.selector, this.receiver)
+ : this.hashCode =
+ Hashing.mixHashCodeBits(selector.hashCode, receiver.hashCode) {
+ assert(receiver != null);
}
String get name => selector.name;
- bool applies(MemberEntity element, ClosedWorld closedWorld) {
+ bool applies(MemberEntity element, AbstractValueDomain domain) {
if (!selector.appliesUnnamed(element)) return false;
- return constraint.canHit(element, selector, closedWorld);
+ return domain.canHit(receiver, element, selector);
}
- bool needsNoSuchMethodHandling(ClosedWorld closedWorld) {
- return constraint.needsNoSuchMethodHandling(selector, closedWorld);
+ bool needsNoSuchMethodHandling(AbstractValueDomain domain) {
+ return domain.needsNoSuchMethodHandling(receiver, selector);
}
bool operator ==(other) {
if (identical(this, other)) return true;
- return selector == other.selector && constraint == other.constraint;
+ return selector == other.selector && receiver == other.receiver;
}
- String toString() => '($selector,$constraint)';
+ String toString() => '($selector,$receiver)';
}
/// A node in the [FunctionSet] caching all [FunctionSetQuery] object for
@@ -202,7 +193,7 @@
/// Returns the set of functions that can be the target of [selectorMask]
/// including no such method handling where applicable.
- FunctionSetQuery query(SelectorMask selectorMask, ClosedWorld closedWorld,
+ FunctionSetQuery query(SelectorMask selectorMask, AbstractValueDomain domain,
[FunctionSetNode noSuchMethods, SelectorMask noSuchMethodMask]) {
assert(selectorMask.name == name);
FunctionSetQuery result = cache[selectorMask];
@@ -210,7 +201,7 @@
Setlet<MemberEntity> functions;
for (MemberEntity element in elements) {
- if (selectorMask.applies(element, closedWorld)) {
+ if (selectorMask.applies(element, domain)) {
if (functions == null) {
// Defer the allocation of the functions set until we are
// sure we need it. This allows us to return immutable empty
@@ -225,9 +216,9 @@
// add [noSuchMethod] implementations that apply to [mask] as
// potential targets.
if (noSuchMethods != null &&
- selectorMask.needsNoSuchMethodHandling(closedWorld)) {
+ selectorMask.needsNoSuchMethodHandling(domain)) {
FunctionSetQuery noSuchMethodQuery =
- noSuchMethods.query(noSuchMethodMask, closedWorld);
+ noSuchMethods.query(noSuchMethodMask, domain);
if (!noSuchMethodQuery.functions.isEmpty) {
if (functions == null) {
functions =
@@ -263,7 +254,7 @@
const FunctionSetQuery();
/// Compute the type of all potential receivers of this function set.
- TypeMask computeMask(ClosedWorld closedWorld);
+ AbstractValue computeMask(AbstractValueDomain domain);
/// Returns all potential targets of this function set.
Iterable<MemberEntity> get functions;
@@ -273,8 +264,7 @@
const EmptyFunctionSetQuery();
@override
- TypeMask computeMask(ClosedWorld closedWorld) =>
- const TypeMask.nonNullEmpty();
+ AbstractValue computeMask(AbstractValueDomain domain) => domain.emptyType;
@override
Iterable<MemberEntity> get functions => const <MemberEntity>[];
@@ -286,31 +276,14 @@
@override
final Iterable<MemberEntity> functions;
- TypeMask _mask;
+ AbstractValue _receiver;
FullFunctionSetQuery(this.functions);
@override
- TypeMask computeMask(ClosedWorld closedWorld) {
- assert(closedWorld
- .hasAnyStrictSubclass(closedWorld.commonElements.objectClass));
- if (_mask != null) return _mask;
- return _mask = new TypeMask.unionOf(
- functions.expand((MemberEntity element) {
- ClassEntity cls = element.enclosingClass;
- return [cls]..addAll(closedWorld.mixinUsesOf(cls));
- }).map((cls) {
- if (closedWorld.commonElements.jsNullClass == cls) {
- return const TypeMask.empty();
- } else if (closedWorld.isInstantiated(cls)) {
- return new TypeMask.nonNullSubclass(cls, closedWorld);
- } else {
- // TODO(johnniwinther): Avoid the need for this case.
- return const TypeMask.empty();
- }
- }),
- closedWorld);
+ AbstractValue computeMask(AbstractValueDomain domain) {
+ return _receiver ??= domain.computeReceiver(functions);
}
- String toString() => '$_mask:$functions';
+ String toString() => '$_receiver:$functions';
}
diff --git a/pkg/compiler/lib/src/world.dart b/pkg/compiler/lib/src/world.dart
index 91a7d5b..523bca7 100644
--- a/pkg/compiler/lib/src/world.dart
+++ b/pkg/compiler/lib/src/world.dart
@@ -1054,18 +1054,18 @@
if (includesClosureCall(selector, mask)) {
return abstractValueDomain.dynamicType;
}
- return _allFunctions.receiverType(selector, mask, this);
+ return _allFunctions.receiverType(selector, mask, abstractValueDomain);
}
Iterable<MemberEntity> locateMembers(Selector selector, TypeMask mask) {
_ensureFunctionSet();
- return _allFunctions.filter(selector, mask, this);
+ return _allFunctions.filter(selector, mask, abstractValueDomain);
}
bool hasAnyUserDefinedGetter(Selector selector, TypeMask mask) {
_ensureFunctionSet();
return _allFunctions
- .filter(selector, mask, this)
+ .filter(selector, mask, abstractValueDomain)
.any((each) => each.isGetter);
}
@@ -1117,7 +1117,8 @@
}
SideEffects sideEffects = new SideEffects.empty();
_ensureFunctionSet();
- for (MemberEntity e in _allFunctions.filter(selector, mask, this)) {
+ for (MemberEntity e
+ in _allFunctions.filter(selector, mask, abstractValueDomain)) {
if (e.isField) {
if (selector.isGetter) {
if (!fieldNeverChanges(e)) {