Usa AbstractValue in the rest of ssa

Change-Id: Id29d7bdaaeaa0fb6dfd4f5fbba50468cd474cd8e
Reviewed-on: https://dart-review.googlesource.com/56664
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index a723729..c43f13f9 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -23,8 +23,7 @@
 import 'types/masks.dart';
 import 'types/types.dart'
     show GlobalTypeInferenceElementResult, GlobalTypeInferenceMemberResult;
-import 'universe/world_builder.dart'
-    show CodegenWorldBuilder, ReceiverConstraint;
+import 'universe/world_builder.dart' show CodegenWorldBuilder;
 import 'universe/world_impact.dart'
     show ImpactUseCase, WorldImpact, WorldImpactVisitorImpl;
 import 'world.dart' show ClosedWorld;
@@ -376,8 +375,8 @@
 
 class Selection {
   final Entity selectedEntity;
-  final ReceiverConstraint mask;
-  Selection(this.selectedEntity, this.mask);
+  final Object receiverConstraint;
+  Selection(this.selectedEntity, this.receiverConstraint);
 }
 
 /// Interface used to record information from different parts of the compiler so
@@ -462,12 +461,13 @@
         entity,
         impact,
         new WorldImpactVisitorImpl(visitDynamicUse: (dynamicUse) {
-          TypeMask mask = dynamicUse.mask;
+          TypeMask mask = dynamicUse.receiverConstraint;
           selections.addAll(closedWorld
               // TODO(het): Handle `call` on `Closure` through
               // `world.includesClosureCall`.
               .locateMembers(dynamicUse.selector, mask)
-              .map((MemberEntity e) => new Selection(e, dynamicUse.mask)));
+              .map((MemberEntity e) =>
+                  new Selection(e, dynamicUse.receiverConstraint)));
         }, visitStaticUse: (staticUse) {
           selections.add(new Selection(staticUse.element, null));
         }),
@@ -570,7 +570,8 @@
         // Don't register dart2js builtin functions that are not recorded.
         Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
         if (useInfo == null) continue;
-        info.uses.add(new DependencyInfo(useInfo, '${selection.mask}'));
+        info.uses.add(
+            new DependencyInfo(useInfo, '${selection.receiverConstraint}'));
       }
     }
 
@@ -584,7 +585,8 @@
       for (Selection selection in uses) {
         Info useInfo = infoCollector._entityToInfo[selection.selectedEntity];
         if (useInfo == null) continue;
-        info.uses.add(new DependencyInfo(useInfo, '${selection.mask}'));
+        info.uses.add(
+            new DependencyInfo(useInfo, '${selection.receiverConstraint}'));
       }
     }
 
diff --git a/pkg/compiler/lib/src/js_backend/interceptor_data.dart b/pkg/compiler/lib/src/js_backend/interceptor_data.dart
index 079aa91..925d846 100644
--- a/pkg/compiler/lib/src/js_backend/interceptor_data.dart
+++ b/pkg/compiler/lib/src/js_backend/interceptor_data.dart
@@ -9,7 +9,7 @@
 import '../elements/entities.dart';
 import '../elements/types.dart';
 import '../js/js.dart' as jsAst;
-import '../types/masks.dart' show TypeMask;
+import '../types/abstract_value_domain.dart';
 import '../universe/selector.dart';
 import '../world.dart' show ClosedWorld;
 import 'namer.dart';
@@ -25,7 +25,7 @@
   bool isInterceptedName(String name);
   bool isInterceptedSelector(Selector selector);
   bool isInterceptedMixinSelector(
-      Selector selector, TypeMask mask, ClosedWorld closedWorld);
+      Selector selector, AbstractValue mask, ClosedWorld closedWorld);
   Iterable<ClassEntity> get interceptedClasses;
   bool isMixedIntoInterceptedClass(ClassEntity element);
 
@@ -117,7 +117,7 @@
   /// into an intercepted class.  These selectors are not eligible for the
   /// 'dummy explicit receiver' optimization.
   bool isInterceptedMixinSelector(
-      Selector selector, TypeMask mask, ClosedWorld closedWorld) {
+      Selector selector, AbstractValue mask, ClosedWorld closedWorld) {
     Set<MemberEntity> elements =
         _interceptedMixinElements.putIfAbsent(selector.name, () {
       Set<MemberEntity> elements = interceptedMembers[selector.name];
@@ -132,7 +132,8 @@
     if (elements.isEmpty) return false;
     return elements.any((element) {
       return selector.applies(element) &&
-          (mask == null || mask.canHit(element, selector, closedWorld));
+          (mask == null ||
+              closedWorld.abstractValueDomain.canHit(mask, element, selector));
     });
   }
 
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 87b1ceb..37288e6 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -29,7 +29,6 @@
 import '../native/native.dart' as native;
 import '../options.dart';
 import '../types/abstract_value_domain.dart';
-import '../types/masks.dart';
 import '../universe/call_structure.dart' show CallStructure;
 import '../universe/selector.dart' show Selector;
 import '../universe/use.dart'
@@ -365,8 +364,8 @@
         .visitGraph(graph);
     new SsaTypeKnownRemover().visitGraph(graph);
     new SsaTrustedCheckRemover(_options).visitGraph(graph);
-    new SsaInstructionMerger(_closedWorld.abstractValueDomain,
-            generateAtUseSite, _superMemberData)
+    new SsaInstructionMerger(
+            _abstractValueDomain, generateAtUseSite, _superMemberData)
         .visitGraph(graph);
     new SsaConditionMerger(generateAtUseSite, controlFlowOperators)
         .visitGraph(graph);
@@ -1828,8 +1827,8 @@
     _registry.registerUseInterceptor();
   }
 
-  TypeMask getOptimizedSelectorFor(
-      HInvokeDynamic node, Selector selector, TypeMask mask) {
+  AbstractValue getOptimizedSelectorFor(
+      HInvokeDynamic node, Selector selector, AbstractValue mask) {
     if (node.element != null) {
       // Create an artificial type mask to make sure only
       // [node.element] will be enqueued. We're not using the receiver
@@ -1837,7 +1836,7 @@
       // invoke dynamic knows more than the receiver.
       ClassEntity enclosing = node.element.enclosingClass;
       if (_closedWorld.isInstantiated(enclosing)) {
-        return _closedWorld.abstractValueDomain.createNonNullExact(enclosing);
+        return _abstractValueDomain.createNonNullExact(enclosing);
       } else {
         // The element is mixed in so a non-null subtype mask is the most
         // precise we have.
@@ -1847,7 +1846,7 @@
                 node,
                 "Element ${node.element} from $enclosing expected "
                 "to be mixed in."));
-        return _closedWorld.abstractValueDomain.createNonNullSubtype(enclosing);
+        return _abstractValueDomain.createNonNullSubtype(enclosing);
       }
     }
     // If [JSInvocationMirror._invokeOn] is enabled, and this call
@@ -1880,7 +1879,7 @@
       _registry.registerStaticUse(new StaticUse.directInvoke(
           target, selector.callStructure, node.typeArguments));
     } else {
-      TypeMask mask = getOptimizedSelectorFor(node, selector, node.mask);
+      AbstractValue mask = getOptimizedSelectorFor(node, selector, node.mask);
       _registry.registerDynamicUse(
           new ConstrainedDynamicUse(selector, mask, node.typeArguments));
     }
@@ -1895,7 +1894,7 @@
       _registry.registerStaticUse(new StaticUse.directSet(node.element));
     } else {
       Selector selector = node.selector;
-      TypeMask mask = getOptimizedSelectorFor(node, selector, node.mask);
+      AbstractValue mask = getOptimizedSelectorFor(node, selector, node.mask);
       _registry.registerDynamicUse(
           new ConstrainedDynamicUse(selector, mask, node.typeArguments));
     }
@@ -1912,7 +1911,7 @@
       _registry.registerStaticUse(new StaticUse.directGet(node.element));
     } else {
       Selector selector = node.selector;
-      TypeMask mask = getOptimizedSelectorFor(node, selector, node.mask);
+      AbstractValue mask = getOptimizedSelectorFor(node, selector, node.mask);
       _registry.registerDynamicUse(
           new ConstrainedDynamicUse(selector, mask, node.typeArguments));
     }
@@ -2929,19 +2928,18 @@
 
   js.Expression generateReceiverOrArgumentTypeTest(HTypeConversion node) {
     HInstruction input = node.checkedInput;
-    TypeMask inputType = node.inputType ?? input.instructionType;
-    TypeMask checkedType = node.checkedType;
+    AbstractValue inputType = node.inputType ?? input.instructionType;
+    AbstractValue checkedType = node.checkedType;
     // This path is no longer used for indexable primitive types.
-    assert(
-        !checkedType.satisfies(_commonElements.jsIndexableClass, _closedWorld));
+    assert(!_abstractValueDomain.isJsIndexable(checkedType));
     // Figure out if it is beneficial to use a null check.  V8 generally prefers
     // 'typeof' checks, but for integers we cannot compile this test into a
     // single typeof check so the null check is cheaper.
-    bool isIntCheck = checkedType.containsOnlyInt(_closedWorld);
+    bool isIntCheck = _abstractValueDomain.isIntegerOrNull(checkedType);
     bool turnIntoNumCheck =
-        isIntCheck && inputType.containsOnlyInt(_closedWorld);
+        isIntCheck && _abstractValueDomain.isIntegerOrNull(inputType);
     bool turnIntoNullCheck = !turnIntoNumCheck &&
-        (checkedType.nullable() == inputType) &&
+        (_abstractValueDomain.includeNull(checkedType) == inputType) &&
         isIntCheck;
 
     if (turnIntoNullCheck) {
@@ -2952,15 +2950,16 @@
       // input is !int
       checkBigInt(input, '!==', input.sourceInformation);
       return pop();
-    } else if (turnIntoNumCheck || checkedType.containsOnlyNum(_closedWorld)) {
+    } else if (turnIntoNumCheck ||
+        _abstractValueDomain.isNumberOrNull(checkedType)) {
       // input is !num
       checkNum(input, '!==', input.sourceInformation);
       return pop();
-    } else if (checkedType.containsOnlyBool(_closedWorld)) {
+    } else if (_abstractValueDomain.isBooleanOrNull(checkedType)) {
       // input is !bool
       checkBool(input, '!==', input.sourceInformation);
       return pop();
-    } else if (checkedType.containsOnlyString(_closedWorld)) {
+    } else if (_abstractValueDomain.isStringOrNull(checkedType)) {
       // input is !string
       checkString(input, '!==', input.sourceInformation);
       return pop();
@@ -3124,14 +3123,15 @@
   }
 
   bool typeVariableAccessNeedsSubstitution(
-      TypeVariableEntity element, TypeMask receiverMask) {
+      TypeVariableEntity element, AbstractValue receiverMask) {
     ClassEntity cls = element.typeDeclaration;
 
     // See if the receiver type narrows the set of classes to ones that can be
     // indexed.
     // TODO(sra): Currently the only convenient query is [singleClass]. We
     // should iterate over all the concrete classes in [receiverMask].
-    ClassEntity receiverClass = receiverMask.singleClass(_closedWorld);
+    ClassEntity receiverClass =
+        _abstractValueDomain.getExactClass(receiverMask);
     if (receiverClass != null) {
       if (_rtiSubstitutions.isTrivialSubstitution(receiverClass, cls)) {
         return false;
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index bfa7fb2..d3905d1 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -8,7 +8,6 @@
 import '../js_backend/interceptor_data.dart';
 import '../options.dart';
 import '../types/abstract_value_domain.dart';
-import '../types/masks.dart';
 import '../universe/selector.dart' show Selector;
 import '../world.dart' show ClosedWorld;
 import 'nodes.dart';
@@ -71,8 +70,8 @@
     if (node.kind == HIs.RAW_CHECK) {
       HInstruction interceptor = node.interceptor;
       if (interceptor != null) {
-        return new HIsViaInterceptor(node.typeExpression, interceptor,
-            _closedWorld.abstractValueDomain.boolType);
+        return new HIsViaInterceptor(
+            node.typeExpression, interceptor, _abstractValueDomain.boolType);
       }
     }
     return node;
@@ -86,9 +85,10 @@
   String simpleOp(HInstruction left, HInstruction right) {
     // Returns the single identity comparison (== or ===) or null if a more
     // complex expression is required.
-    TypeMask leftType = left.instructionType;
-    TypeMask rightType = right.instructionType;
-    if (leftType.isNullable && rightType.isNullable) {
+    AbstractValue leftType = left.instructionType;
+    AbstractValue rightType = right.instructionType;
+    if (_abstractValueDomain.canBeNull(leftType) &&
+        _abstractValueDomain.canBeNull(rightType)) {
       if (left.isConstantNull() ||
           right.isConstantNull() ||
           (left.isPrimitive(_abstractValueDomain) && leftType == rightType)) {
@@ -108,14 +108,14 @@
 
   HInstruction visitInvokeSuper(HInvokeSuper node) {
     if (node.isInterceptedCall) {
-      TypeMask mask = node.getDartReceiver(_closedWorld).instructionType;
+      AbstractValue mask = node.getDartReceiver(_closedWorld).instructionType;
       tryReplaceInterceptorWithDummy(node, node.selector, mask);
     }
     return node;
   }
 
   void tryReplaceInterceptorWithDummy(
-      HInvoke node, Selector selector, TypeMask mask) {
+      HInvoke node, Selector selector, AbstractValue mask) {
     // Calls of the form
     //
     //     a.foo$1(a, x)
diff --git a/pkg/compiler/lib/src/ssa/graph_builder.dart b/pkg/compiler/lib/src/ssa/graph_builder.dart
index aa99124..2a6830c 100644
--- a/pkg/compiler/lib/src/ssa/graph_builder.dart
+++ b/pkg/compiler/lib/src/ssa/graph_builder.dart
@@ -130,7 +130,7 @@
   /// Pushes a boolean checking [expression] against null.
   pushCheckNull(HInstruction expression) {
     push(new HIdentity(expression, graph.addConstantNull(closedWorld), null,
-        closedWorld.abstractValueDomain.boolType));
+        abstractValueDomain.boolType));
   }
 
   void dup() {
diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
index a3ec846..6d297c5 100644
--- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
+++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
@@ -3,12 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import '../common_elements.dart' show CommonElements;
-import '../constants/constant_system.dart';
 import '../constants/values.dart';
 import '../elements/entities.dart';
 import '../js_backend/interceptor_data.dart';
 import '../types/abstract_value_domain.dart';
-import '../types/masks.dart';
 import '../universe/selector.dart' show Selector;
 import '../world.dart' show ClosedWorld;
 import 'nodes.dart';
@@ -37,23 +35,21 @@
 class SsaSimplifyInterceptors extends HBaseVisitor
     implements OptimizationPhase {
   final String name = "SsaSimplifyInterceptors";
-  final ClosedWorld closedWorld;
-  final ClassEntity enclosingClass;
-  HGraph graph;
+  final ClosedWorld _closedWorld;
+  final ClassEntity _enclosingClass;
+  HGraph _graph;
 
-  SsaSimplifyInterceptors(this.closedWorld, this.enclosingClass);
+  SsaSimplifyInterceptors(this._closedWorld, this._enclosingClass);
 
-  CommonElements get _commonElements => closedWorld.commonElements;
+  CommonElements get _commonElements => _closedWorld.commonElements;
 
-  ConstantSystem get constantSystem => closedWorld.constantSystem;
-
-  InterceptorData get interceptorData => closedWorld.interceptorData;
+  InterceptorData get _interceptorData => _closedWorld.interceptorData;
 
   AbstractValueDomain get _abstractValueDomain =>
-      closedWorld.abstractValueDomain;
+      _closedWorld.abstractValueDomain;
 
   void visitGraph(HGraph graph) {
-    this.graph = graph;
+    this._graph = graph;
     visitDominatorTree(graph);
   }
 
@@ -110,18 +106,16 @@
 
     // All intercepted classes extend `Interceptor`, so if the receiver can't be
     // a class extending `Interceptor` then it can be called directly.
-    return new TypeMask.nonNullSubclass(
-            _commonElements.jsInterceptorClass, closedWorld)
-        .isDisjoint(receiver.instructionType, closedWorld);
+    return !_abstractValueDomain.canBeInterceptor(receiver.instructionType);
   }
 
   HInstruction tryComputeConstantInterceptor(
       HInstruction input, Set<ClassEntity> interceptedClasses) {
-    if (input == graph.explicitReceiverParameter) {
+    if (input == _graph.explicitReceiverParameter) {
       // If `explicitReceiverParameter` is set it means the current method is an
       // interceptor method, and `this` is the interceptor.  The caller just did
       // `getInterceptor(foo).currentMethod(foo)` to enter the current method.
-      return graph.thisInstruction;
+      return _graph.thisInstruction;
     }
 
     ClassEntity constantInterceptor = tryComputeConstantInterceptorFromType(
@@ -131,32 +125,32 @@
 
     // If we just happen to be in an instance method of the constant
     // interceptor, `this` is a shorter alias.
-    if (constantInterceptor == enclosingClass &&
-        graph.thisInstruction != null) {
-      return graph.thisInstruction;
+    if (constantInterceptor == _enclosingClass &&
+        _graph.thisInstruction != null) {
+      return _graph.thisInstruction;
     }
 
     ConstantValue constant = new InterceptorConstantValue(constantInterceptor);
-    return graph.addConstant(constant, closedWorld);
+    return _graph.addConstant(constant, _closedWorld);
   }
 
   ClassEntity tryComputeConstantInterceptorFromType(
-      TypeMask type, Set<ClassEntity> interceptedClasses) {
-    if (type.isNullable) {
-      if (type.isNull) {
+      AbstractValue type, Set<ClassEntity> interceptedClasses) {
+    if (_abstractValueDomain.canBeNull(type)) {
+      if (_abstractValueDomain.isNull(type)) {
         return _commonElements.jsNullClass;
       }
-    } else if (type.containsOnlyInt(closedWorld)) {
+    } else if (_abstractValueDomain.isIntegerOrNull(type)) {
       return _commonElements.jsIntClass;
-    } else if (type.containsOnlyDouble(closedWorld)) {
+    } else if (_abstractValueDomain.isDoubleOrNull(type)) {
       return _commonElements.jsDoubleClass;
-    } else if (type.containsOnlyBool(closedWorld)) {
+    } else if (_abstractValueDomain.isBooleanOrNull(type)) {
       return _commonElements.jsBoolClass;
-    } else if (type.containsOnlyString(closedWorld)) {
+    } else if (_abstractValueDomain.isStringOrNull(type)) {
       return _commonElements.jsStringClass;
-    } else if (type.satisfies(_commonElements.jsArrayClass, closedWorld)) {
+    } else if (_abstractValueDomain.isArray(type)) {
       return _commonElements.jsArrayClass;
-    } else if (type.containsOnlyNum(closedWorld) &&
+    } else if (_abstractValueDomain.isNumberOrNull(type) &&
         !interceptedClasses.contains(_commonElements.jsIntClass) &&
         !interceptedClasses.contains(_commonElements.jsDoubleClass)) {
       // If the method being intercepted is not defined in [int] or [double] we
@@ -175,8 +169,8 @@
       // for a subclass or call methods defined on a subclass.  Provided the
       // code is completely insensitive to the specific instance subclasses, we
       // can use the non-leaf class directly.
-      ClassEntity element = type.singleClass(closedWorld);
-      if (element != null && closedWorld.nativeData.isNativeClass(element)) {
+      ClassEntity element = _abstractValueDomain.getExactClass(type);
+      if (element != null && _closedWorld.nativeData.isNativeClass(element)) {
         return element;
       }
     }
@@ -223,11 +217,11 @@
     // If there is a call that dominates all other uses, we can use just the
     // selector of that instruction.
     if (dominator is HInvokeDynamic &&
-        dominator.isCallOnInterceptor(closedWorld) &&
+        dominator.isCallOnInterceptor(_closedWorld) &&
         node == dominator.receiver &&
         useCount(dominator, node) == 1) {
-      interceptedClasses = interceptorData.getInterceptedClassesOn(
-          dominator.selector.name, closedWorld);
+      interceptedClasses = _interceptorData.getInterceptedClassesOn(
+          dominator.selector.name, _closedWorld);
 
       // If we found that we need number, we must still go through all
       // uses to check if they require int, or double.
@@ -237,8 +231,8 @@
         Set<ClassEntity> required;
         for (HInstruction user in node.usedBy) {
           if (user is! HInvoke) continue;
-          Set<ClassEntity> intercepted = interceptorData
-              .getInterceptedClassesOn(user.selector.name, closedWorld);
+          Set<ClassEntity> intercepted = _interceptorData
+              .getInterceptedClassesOn(user.selector.name, _closedWorld);
           if (intercepted.contains(_commonElements.jsIntClass)) {
             // TODO(johnniwinther): Use type argument when all uses of
             // intercepted classes expect entities instead of elements.
@@ -252,7 +246,8 @@
             required.add(_commonElements.jsDoubleClass);
           }
         }
-        // Don't modify the result of [interceptorData.getInterceptedClassesOn].
+        // Don't modify the result of
+        // [_interceptorData.getInterceptedClassesOn].
         if (required != null) {
           interceptedClasses = interceptedClasses.union(required);
         }
@@ -263,21 +258,21 @@
       interceptedClasses = new Set<ClassEntity>();
       for (HInstruction user in node.usedBy) {
         if (user is HInvokeDynamic &&
-            user.isCallOnInterceptor(closedWorld) &&
+            user.isCallOnInterceptor(_closedWorld) &&
             node == user.receiver &&
             useCount(user, node) == 1) {
-          interceptedClasses.addAll(interceptorData.getInterceptedClassesOn(
-              user.selector.name, closedWorld));
+          interceptedClasses.addAll(_interceptorData.getInterceptedClassesOn(
+              user.selector.name, _closedWorld));
         } else if (user is HInvokeSuper &&
-            user.isCallOnInterceptor(closedWorld) &&
+            user.isCallOnInterceptor(_closedWorld) &&
             node == user.receiver &&
             useCount(user, node) == 1) {
-          interceptedClasses.addAll(interceptorData.getInterceptedClassesOn(
-              user.selector.name, closedWorld));
+          interceptedClasses.addAll(_interceptorData.getInterceptedClassesOn(
+              user.selector.name, _closedWorld));
         } else {
           // Use a most general interceptor for other instructions, example,
           // is-checks and escaping interceptors.
-          interceptedClasses.addAll(interceptorData.interceptedClasses);
+          interceptedClasses.addAll(_interceptorData.interceptedClasses);
           break;
         }
       }
@@ -326,8 +321,8 @@
               _abstractValueDomain.excludeNull(receiver.instructionType),
               interceptedClasses);
           if (interceptorClass != null) {
-            HInstruction constantInstruction = graph.addConstant(
-                new InterceptorConstantValue(interceptorClass), closedWorld);
+            HInstruction constantInstruction = _graph.addConstant(
+                new InterceptorConstantValue(interceptorClass), _closedWorld);
             node.conditionalConstantInterceptor = constantInstruction;
             constantInstruction.usedBy.add(node);
             return false;
@@ -355,8 +350,8 @@
       // See if we can rewrite the is-check to use 'instanceof', i.e. rewrite
       // "getInterceptor(x).$isT" to "x instanceof T".
       if (node == user.interceptor) {
-        if (interceptorData.mayGenerateInstanceofCheck(
-            user.typeExpression, closedWorld)) {
+        if (_interceptorData.mayGenerateInstanceofCheck(
+            user.typeExpression, _closedWorld)) {
           HInstruction instanceofCheck = new HIs.instanceOf(user.typeExpression,
               user.expression, user.instructionType, user.sourceInformation);
           instanceofCheck.sourceElement = user.sourceElement;
@@ -366,11 +361,11 @@
     } else if (user is HInvokeDynamic) {
       if (node == user.inputs[0]) {
         // Replace the user with a [HOneShotInterceptor].
-        HConstant nullConstant = graph.addConstantNull(closedWorld);
+        HConstant nullConstant = _graph.addConstantNull(_closedWorld);
         List<HInstruction> inputs = new List<HInstruction>.from(user.inputs);
         inputs[0] = nullConstant;
         HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor(
-            closedWorld.abstractValueDomain,
+            _abstractValueDomain,
             user.selector,
             user.mask,
             inputs,
@@ -409,7 +404,7 @@
     if (constant == null) return false;
 
     Selector selector = node.selector;
-    TypeMask mask = node.mask;
+    AbstractValue mask = node.mask;
     HInstruction instruction;
     if (selector.isGetter) {
       instruction = new HInvokeDynamicGetter(
diff --git a/pkg/compiler/lib/src/ssa/kernel_impact.dart b/pkg/compiler/lib/src/ssa/kernel_impact.dart
index 8e731b7..950aa78 100644
--- a/pkg/compiler/lib/src/ssa/kernel_impact.dart
+++ b/pkg/compiler/lib/src/ssa/kernel_impact.dart
@@ -510,7 +510,7 @@
     List<DartType> typeArguments = _visitArguments(node.arguments);
     // TODO(johnniwinther): Restrict the dynamic use to only match the known
     // target.
-    ReceiverConstraint constraint;
+    Object constraint;
     MemberEntity member = elementMap.getMember(node.target);
     if (_options.strongMode && useStrongModeWorldStrategy) {
       // TODO(johnniwinther): Restrict this to subclasses?
@@ -610,7 +610,7 @@
       }
     } else {
       visitNode(node.receiver);
-      ReceiverConstraint constraint;
+      Object constraint;
       if (_options.strongMode && useStrongModeWorldStrategy) {
         DartType receiverType = elementMap.getStaticType(node.receiver);
         if (receiverType is InterfaceType) {
@@ -626,7 +626,7 @@
   @override
   void visitPropertyGet(ir.PropertyGet node) {
     visitNode(node.receiver);
-    ReceiverConstraint constraint;
+    Object constraint;
     if (_options.strongMode && useStrongModeWorldStrategy) {
       DartType receiverType = elementMap.getStaticType(node.receiver);
       if (receiverType is InterfaceType) {
@@ -642,7 +642,7 @@
   void visitPropertySet(ir.PropertySet node) {
     visitNode(node.receiver);
     visitNode(node.value);
-    ReceiverConstraint constraint;
+    Object constraint;
     if (_options.strongMode && useStrongModeWorldStrategy) {
       DartType receiverType = elementMap.getStaticType(node.receiver);
       if (receiverType is InterfaceType) {
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index a0ce7be..4c03def 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -18,7 +18,6 @@
 import '../native/native.dart' as native;
 import '../options.dart';
 import '../types/abstract_value_domain.dart';
-//import '../types/masks.dart';
 import '../types/types.dart';
 import '../universe/selector.dart' show Selector;
 import '../universe/side_effects.dart' show SideEffects;
diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart
index d0a0f44..00d06ae 100644
--- a/pkg/compiler/lib/src/ssa/types_propagation.dart
+++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
@@ -6,7 +6,6 @@
 import '../elements/entities.dart';
 import '../options.dart';
 import '../types/abstract_value_domain.dart';
-import '../types/masks.dart';
 import '../types/types.dart';
 import '../universe/selector.dart' show Selector;
 import '../world.dart' show ClosedWorld;
@@ -47,7 +46,7 @@
   AbstractValueDomain get abstractValueDomain =>
       closedWorld.abstractValueDomain;
 
-  TypeMask computeType(HInstruction instruction) {
+  AbstractValue computeType(HInstruction instruction) {
     return instruction.accept(this);
   }
 
@@ -55,8 +54,8 @@
   // whether or not the type was changed.
   bool updateType(HInstruction instruction) {
     // Compute old and new types.
-    TypeMask oldType = instruction.instructionType;
-    TypeMask newType = computeType(instruction);
+    AbstractValue oldType = instruction.instructionType;
+    AbstractValue newType = computeType(instruction);
     assert(newType != null);
     // We unconditionally replace the propagated type with the new type. The
     // computeType must make sure that we eventually reach a stable state.
@@ -127,109 +126,111 @@
     }
   }
 
-  TypeMask visitBinaryArithmetic(HBinaryArithmetic instruction) {
+  AbstractValue visitBinaryArithmetic(HBinaryArithmetic instruction) {
     HInstruction left = instruction.left;
     HInstruction right = instruction.right;
-    if (left.isInteger(closedWorld.abstractValueDomain) &&
-        right.isInteger(closedWorld.abstractValueDomain)) {
-      return closedWorld.abstractValueDomain.intType;
+    if (left.isInteger(abstractValueDomain) &&
+        right.isInteger(abstractValueDomain)) {
+      return abstractValueDomain.intType;
     }
-    if (left.isDouble(closedWorld.abstractValueDomain)) {
-      return closedWorld.abstractValueDomain.doubleType;
+    if (left.isDouble(abstractValueDomain)) {
+      return abstractValueDomain.doubleType;
     }
-    return closedWorld.abstractValueDomain.numType;
+    return abstractValueDomain.numType;
   }
 
-  TypeMask checkPositiveInteger(HBinaryArithmetic instruction) {
+  AbstractValue checkPositiveInteger(HBinaryArithmetic instruction) {
     HInstruction left = instruction.left;
     HInstruction right = instruction.right;
-    if (left.isPositiveInteger(closedWorld.abstractValueDomain) &&
-        right.isPositiveInteger(closedWorld.abstractValueDomain)) {
-      return closedWorld.abstractValueDomain.positiveIntType;
+    if (left.isPositiveInteger(abstractValueDomain) &&
+        right.isPositiveInteger(abstractValueDomain)) {
+      return abstractValueDomain.positiveIntType;
     }
     return visitBinaryArithmetic(instruction);
   }
 
-  TypeMask visitMultiply(HMultiply instruction) {
+  AbstractValue visitMultiply(HMultiply instruction) {
     return checkPositiveInteger(instruction);
   }
 
-  TypeMask visitAdd(HAdd instruction) {
+  AbstractValue visitAdd(HAdd instruction) {
     return checkPositiveInteger(instruction);
   }
 
-  TypeMask visitDivide(HDivide instruction) {
+  AbstractValue visitDivide(HDivide instruction) {
     // Always double, as initialized.
     return instruction.instructionType;
   }
 
-  TypeMask visitTruncatingDivide(HTruncatingDivide instruction) {
+  AbstractValue visitTruncatingDivide(HTruncatingDivide instruction) {
     // Always as initialized.
     return instruction.instructionType;
   }
 
-  TypeMask visitRemainder(HRemainder instruction) {
+  AbstractValue visitRemainder(HRemainder instruction) {
     // Always as initialized.
     return instruction.instructionType;
   }
 
-  TypeMask visitNegate(HNegate instruction) {
+  AbstractValue visitNegate(HNegate instruction) {
     HInstruction operand = instruction.operand;
     // We have integer subclasses that represent ranges, so widen any int
     // subclass to full integer.
-    if (operand.isInteger(closedWorld.abstractValueDomain)) {
-      return closedWorld.abstractValueDomain.intType;
+    if (operand.isInteger(abstractValueDomain)) {
+      return abstractValueDomain.intType;
     }
     return instruction.operand.instructionType;
   }
 
-  TypeMask visitAbs(HAbs instruction) {
+  AbstractValue visitAbs(HAbs instruction) {
     // TODO(sra): Can narrow to non-negative type for integers.
     return instruction.operand.instructionType;
   }
 
-  TypeMask visitInstruction(HInstruction instruction) {
+  AbstractValue visitInstruction(HInstruction instruction) {
     assert(instruction.instructionType != null);
     return instruction.instructionType;
   }
 
-  TypeMask visitPhi(HPhi phi) {
-    TypeMask candidateType = closedWorld.abstractValueDomain.emptyType;
+  AbstractValue visitPhi(HPhi phi) {
+    AbstractValue candidateType = abstractValueDomain.emptyType;
     for (int i = 0, length = phi.inputs.length; i < length; i++) {
-      TypeMask inputType = phi.inputs[i].instructionType;
-      candidateType = candidateType.union(inputType, closedWorld);
+      AbstractValue inputType = phi.inputs[i].instructionType;
+      candidateType = abstractValueDomain.union(candidateType, inputType);
     }
     return candidateType;
   }
 
-  TypeMask visitTypeConversion(HTypeConversion instruction) {
+  AbstractValue visitTypeConversion(HTypeConversion instruction) {
     HInstruction input = instruction.checkedInput;
-    TypeMask inputType = input.instructionType;
-    TypeMask checkedType = instruction.checkedType;
+    AbstractValue inputType = input.instructionType;
+    AbstractValue checkedType = instruction.checkedType;
     if (instruction.isArgumentTypeCheck || instruction.isReceiverTypeCheck) {
       // We must make sure a type conversion for receiver or argument check
       // does not try to do an int check, because an int check is not enough.
       // We only do an int check if the input is integer or null.
-      if (checkedType.containsOnlyNum(closedWorld) &&
-          !checkedType.containsOnlyDouble(closedWorld) &&
-          input.isIntegerOrNull(closedWorld.abstractValueDomain)) {
-        instruction.checkedType = closedWorld.abstractValueDomain.intType;
-      } else if (checkedType.containsOnlyInt(closedWorld) &&
-          !input.isIntegerOrNull(closedWorld.abstractValueDomain)) {
-        instruction.checkedType = closedWorld.abstractValueDomain.numType;
+      if (abstractValueDomain.isNumberOrNull(checkedType) &&
+          !abstractValueDomain.isDoubleOrNull(checkedType) &&
+          input.isIntegerOrNull(abstractValueDomain)) {
+        instruction.checkedType = abstractValueDomain.intType;
+      } else if (abstractValueDomain.isIntegerOrNull(checkedType) &&
+          !input.isIntegerOrNull(abstractValueDomain)) {
+        instruction.checkedType = abstractValueDomain.numType;
       }
     }
 
-    TypeMask outputType = checkedType.intersection(inputType, closedWorld);
-    if (outputType.isEmpty) {
+    AbstractValue outputType =
+        abstractValueDomain.intersection(checkedType, inputType);
+    if (abstractValueDomain.isEmpty(outputType)) {
       // Intersection of double and integer conflicts (is empty), but JS numbers
       // can be both int and double at the same time.  For example, the input
       // can be a literal double '8.0' that is marked as an integer (because 'is
       // int' will return 'true').  What we really need to do is make the
       // overlap between int and double values explicit in the TypeMask system.
-      if (inputType.containsOnlyInt(closedWorld) &&
-          checkedType.containsOnlyDouble(closedWorld)) {
-        if (inputType.isNullable && checkedType.isNullable) {
+      if (abstractValueDomain.isIntegerOrNull(inputType) &&
+          abstractValueDomain.isDoubleOrNull(checkedType)) {
+        if (abstractValueDomain.canBeNull(inputType) &&
+            abstractValueDomain.canBeNull(checkedType)) {
           outputType =
               abstractValueDomain.includeNull(abstractValueDomain.doubleType);
         } else {
@@ -255,10 +256,10 @@
     return outputType;
   }
 
-  TypeMask visitTypeKnown(HTypeKnown instruction) {
+  AbstractValue visitTypeKnown(HTypeKnown instruction) {
     HInstruction input = instruction.checkedInput;
-    TypeMask inputType = input.instructionType;
-    TypeMask outputType =
+    AbstractValue inputType = input.instructionType;
+    AbstractValue outputType =
         abstractValueDomain.intersection(instruction.knownType, inputType);
     if (inputType != outputType) {
       input.replaceAllUsersDominatedBy(instruction.next, instruction);
@@ -266,8 +267,8 @@
     return outputType;
   }
 
-  void convertInput(
-      HInvokeDynamic instruction, HInstruction input, TypeMask type, int kind) {
+  void convertInput(HInvokeDynamic instruction, HInstruction input,
+      AbstractValue type, int kind) {
     Selector selector = (kind == HTypeConversion.RECEIVER_TYPE_CHECK)
         ? instruction.selector
         : null;
@@ -278,15 +279,15 @@
     input.replaceAllUsersDominatedBy(instruction, converted);
   }
 
-  bool isCheckEnoughForNsmOrAe(HInstruction instruction, TypeMask type) {
+  bool isCheckEnoughForNsmOrAe(HInstruction instruction, AbstractValue type) {
     // In some cases, we want the receiver to be an integer,
     // but that does not mean we will get a NoSuchMethodError
     // if it's not: the receiver could be a double.
-    if (type.containsOnlyInt(closedWorld)) {
+    if (abstractValueDomain.isIntegerOrNull(type)) {
       // If the instruction's type is integer or null, the codegen
       // will emit a null check, which is enough to know if it will
       // hit a noSuchMethod.
-      return instruction.isIntegerOrNull(closedWorld.abstractValueDomain);
+      return instruction.isIntegerOrNull(abstractValueDomain);
     }
     return true;
   }
@@ -297,12 +298,12 @@
   bool checkReceiver(HInvokeDynamic instruction) {
     assert(instruction.isInterceptedCall);
     HInstruction receiver = instruction.inputs[1];
-    if (receiver.isNumber(closedWorld.abstractValueDomain)) return false;
-    if (receiver.isNumberOrNull(closedWorld.abstractValueDomain)) {
+    if (receiver.isNumber(abstractValueDomain)) return false;
+    if (receiver.isNumberOrNull(abstractValueDomain)) {
       convertInput(
           instruction,
           receiver,
-          closedWorld.abstractValueDomain.excludeNull(receiver.instructionType),
+          abstractValueDomain.excludeNull(receiver.instructionType),
           HTypeConversion.RECEIVER_TYPE_CHECK);
       return true;
     } else if (instruction.element == null) {
@@ -315,10 +316,10 @@
       if (targets.length == 1) {
         MemberEntity target = targets.first;
         ClassEntity cls = target.enclosingClass;
-        TypeMask type = new TypeMask.nonNullSubclass(cls, closedWorld);
+        AbstractValue type = abstractValueDomain.createNonNullSubclass(cls);
         // We currently only optimize on some primitive types.
-        if (!type.containsOnlyNum(closedWorld) &&
-            !type.containsOnlyBool(closedWorld)) {
+        if (!abstractValueDomain.isNumberOrNull(type) &&
+            !abstractValueDomain.isBooleanOrNull(type)) {
           return false;
         }
         if (!isCheckEnoughForNsmOrAe(receiver, type)) return false;
@@ -341,11 +342,11 @@
     HInstruction right = instruction.inputs[2];
 
     Selector selector = instruction.selector;
-    if (selector.isOperator && left.isNumber(closedWorld.abstractValueDomain)) {
-      if (right.isNumber(closedWorld.abstractValueDomain)) return false;
-      TypeMask type = right.isIntegerOrNull(closedWorld.abstractValueDomain)
-          ? closedWorld.abstractValueDomain.excludeNull(right.instructionType)
-          : closedWorld.abstractValueDomain.numType;
+    if (selector.isOperator && left.isNumber(abstractValueDomain)) {
+      if (right.isNumber(abstractValueDomain)) return false;
+      AbstractValue type = right.isIntegerOrNull(abstractValueDomain)
+          ? abstractValueDomain.excludeNull(right.instructionType)
+          : abstractValueDomain.numType;
       // TODO(ngeoffray): Some number operations don't have a builtin
       // variant and will do the check in their method anyway. We
       // still add a check because it allows to GVN these operations,
@@ -376,7 +377,7 @@
     });
   }
 
-  TypeMask visitInvokeDynamic(HInvokeDynamic instruction) {
+  AbstractValue visitInvokeDynamic(HInvokeDynamic instruction) {
     if (instruction.isInterceptedCall) {
       // We cannot do the following optimization now, because we have to wait
       // for the type propagation to be stable. The receiver of [instruction]
@@ -403,7 +404,7 @@
     }
 
     HInstruction receiver = instruction.getDartReceiver(closedWorld);
-    TypeMask receiverType = receiver.instructionType;
+    AbstractValue receiverType = receiver.instructionType;
     instruction.mask = receiverType;
 
     // Try to specialize the receiver after this call by inserting a refinement
@@ -414,11 +415,11 @@
     // we try to do the least expensive test first.
     const int _MAX_QUICK_USERS = 50;
     if (!instruction.selector.isClosureCall) {
-      TypeMask newType;
-      TypeMask computeNewType() {
+      AbstractValue newType;
+      AbstractValue computeNewType() {
         newType = closedWorld.computeReceiverType(
             instruction.selector, instruction.mask);
-        newType = newType.intersection(receiverType, closedWorld);
+        newType = abstractValueDomain.intersection(newType, receiverType);
         return newType;
       }
 
diff --git a/pkg/compiler/lib/src/types/abstract_value_domain.dart b/pkg/compiler/lib/src/types/abstract_value_domain.dart
index bbc5153..ed28667 100644
--- a/pkg/compiler/lib/src/types/abstract_value_domain.dart
+++ b/pkg/compiler/lib/src/types/abstract_value_domain.dart
@@ -146,6 +146,10 @@
   /// Returns `true` if [value] is an exact class or `null` at runtime.
   bool isExact(covariant AbstractValue value);
 
+  /// Returns the [ClassEntity] if this [value] is a non-null instance of an
+  /// exact class at runtime, and `null` otherwise.
+  ClassEntity getExactClass(covariant AbstractValue value);
+
   /// Returns `true` if [value] a known primitive JavaScript value at runtime.
   bool isPrimitiveValue(covariant AbstractValue value);
 
@@ -312,7 +316,10 @@
   /// Returns `null` if 0 or more than 1 member can be hit at runtime.
   MemberEntity locateSingleMember(AbstractValue receiver, Selector selector);
 
-  /// Returns `true` if [value] is a indexable and iterable JavaScript value at
+  /// Returns `true` if [value] is an indexable JavaScript value at runtime.
+  bool isJsIndexable(covariant AbstractValue value);
+
+  /// Returns `true` if [value] is an indexable and iterable JavaScript value at
   /// runtime.
   ///
   /// JavaScript arrays are both indexable and iterable whereas JavaScript
diff --git a/pkg/compiler/lib/src/types/masks.dart b/pkg/compiler/lib/src/types/masks.dart
index aba1f0a..6e5d2c8 100644
--- a/pkg/compiler/lib/src/types/masks.dart
+++ b/pkg/compiler/lib/src/types/masks.dart
@@ -10,11 +10,9 @@
 import '../elements/entities.dart';
 import '../inferrer/type_graph_inferrer.dart' show TypeGraphInferrer;
 import '../universe/selector.dart' show Selector;
+import '../universe/use.dart' show DynamicUse;
 import '../universe/world_builder.dart'
-    show
-        ReceiverConstraint,
-        UniverseSelectorConstraints,
-        SelectorConstraintsStrategy;
+    show UniverseSelectorConstraints, SelectorConstraintsStrategy;
 import '../util/util.dart';
 import '../world.dart' show ClassQuery, ClosedWorld;
 import 'abstract_value_domain.dart';
@@ -289,6 +287,11 @@
   bool isExact(TypeMask value) => value.isExact || isNull(value);
 
   @override
+  ClassEntity getExactClass(TypeMask mask) {
+    return mask.singleClass(_closedWorld);
+  }
+
+  @override
   bool isPrimitiveValue(TypeMask value) => value.isValue;
 
   @override
@@ -557,6 +560,12 @@
   }
 
   @override
+  bool isJsIndexable(TypeMask mask) {
+    return mask.satisfies(
+        _closedWorld.commonElements.jsIndexableClass, _closedWorld);
+  }
+
+  @override
   bool isJsIndexableAndIterable(covariant TypeMask mask) {
     return mask != null &&
         mask.satisfies(
diff --git a/pkg/compiler/lib/src/types/type_mask.dart b/pkg/compiler/lib/src/types/type_mask.dart
index d7c9c01..0458cb9 100644
--- a/pkg/compiler/lib/src/types/type_mask.dart
+++ b/pkg/compiler/lib/src/types/type_mask.dart
@@ -66,6 +66,15 @@
   UniverseSelectorConstraints createSelectorConstraints(Selector selector) {
     return new IncreasingTypeMaskSet();
   }
+
+  @override
+  bool appliedUnnamed(
+      DynamicUse dynamicUse, MemberEntity member, covariant ClosedWorld world) {
+    Selector selector = dynamicUse.selector;
+    TypeMask mask = dynamicUse.receiverConstraint;
+    return selector.appliesUnnamed(member) &&
+        (mask == null || mask.canHit(member, selector, world));
+  }
 }
 
 /**
@@ -73,7 +82,7 @@
  * operations on it are not guaranteed to be precise and they may
  * yield conservative answers that contain too many classes.
  */
-abstract class TypeMask implements ReceiverConstraint, AbstractValue {
+abstract class TypeMask implements AbstractValue {
   factory TypeMask(
       ClassEntity base, int kind, bool isNullable, ClosedWorld closedWorld) {
     return new FlatTypeMask.normalized(
@@ -361,6 +370,10 @@
    */
   bool canHit(MemberEntity element, Selector selector, ClosedWorld closedWorld);
 
+  /// Returns whether this [TypeMask] applied to [selector] can hit a
+  /// [noSuchMethod].
+  bool needsNoSuchMethodHandling(Selector selector, ClosedWorld world);
+
   /**
    * Returns the [element] that is known to always be hit at runtime
    * on this mask. Returns null if there is none.
diff --git a/pkg/compiler/lib/src/universe/codegen_world_builder.dart b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
index 2476b2b..8301869 100644
--- a/pkg/compiler/lib/src/universe/codegen_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
@@ -256,7 +256,8 @@
     void _process(Map<String, Set<_MemberUsage>> memberMap,
         EnumSet<MemberUse> action(_MemberUsage usage)) {
       _processSet(memberMap, methodName, (_MemberUsage usage) {
-        if (dynamicUse.appliesUnnamed(usage.entity, _world)) {
+        if (selectorConstraintsStrategy.appliedUnnamed(
+            dynamicUse, usage.entity, _world)) {
           memberUsed(usage.entity, action(usage));
           return true;
         }
@@ -294,14 +295,14 @@
       Map<String, Map<Selector, SelectorConstraints>> selectorMap) {
     Selector selector = dynamicUse.selector;
     String name = selector.name;
-    ReceiverConstraint mask = dynamicUse.mask;
+    Object constraint = dynamicUse.receiverConstraint;
     Map<Selector, SelectorConstraints> selectors = selectorMap.putIfAbsent(
         name, () => new Maplet<Selector, SelectorConstraints>());
     UniverseSelectorConstraints constraints =
         selectors.putIfAbsent(selector, () {
       return selectorConstraintsStrategy.createSelectorConstraints(selector);
     });
-    return constraints.addReceiverConstraint(mask);
+    return constraints.addReceiverConstraint(constraint);
   }
 
   Map<Selector, SelectorConstraints> _asUnmodifiable(
diff --git a/pkg/compiler/lib/src/universe/resolution_world_builder.dart b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
index f408104..0a77ecc 100644
--- a/pkg/compiler/lib/src/universe/resolution_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
@@ -600,7 +600,8 @@
     void _process(Map<String, Set<_MemberUsage>> memberMap,
         EnumSet<MemberUse> action(_MemberUsage usage)) {
       _processSet(memberMap, methodName, (_MemberUsage usage) {
-        if (dynamicUse.appliesUnnamed(usage.entity, this)) {
+        if (selectorConstraintsStrategy.appliedUnnamed(
+            dynamicUse, usage.entity, this)) {
           memberUsed(usage.entity, action(usage));
           return true;
         }
@@ -634,7 +635,7 @@
       Map<String, Map<Selector, SelectorConstraints>> selectorMap) {
     Selector selector = dynamicUse.selector;
     String name = selector.name;
-    ReceiverConstraint constraint = dynamicUse.mask;
+    Object constraint = dynamicUse.receiverConstraint;
     Map<Selector, SelectorConstraints> selectors = selectorMap.putIfAbsent(
         name, () => new Maplet<Selector, SelectorConstraints>());
     UniverseSelectorConstraints constraints =
diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart
index 0fcec7e..8788342 100644
--- a/pkg/compiler/lib/src/universe/use.dart
+++ b/pkg/compiler/lib/src/universe/use.dart
@@ -22,10 +22,8 @@
 import '../elements/entities.dart';
 import '../js_model/closure.dart';
 import '../util/util.dart' show equalElements, Hashing;
-import '../world.dart' show World;
 import 'call_structure.dart' show CallStructure;
 import 'selector.dart' show Selector;
-import 'world_builder.dart' show ReceiverConstraint;
 
 enum DynamicUseKind {
   INVOKE,
@@ -34,7 +32,7 @@
 }
 
 /// The use of a dynamic property. [selector] defined the name and kind of the
-/// property and [mask] defines the known constraint for the object on which
+/// property and [receiverConstraint] defines the known constraint for the object on which
 /// the property is accessed.
 class DynamicUse {
   final Selector selector;
@@ -64,12 +62,7 @@
     return sb.toString();
   }
 
-  bool appliesUnnamed(MemberEntity element, World world) {
-    return selector.appliesUnnamed(element) &&
-        (mask == null || mask.canHit(element, selector, world));
-  }
-
-  ReceiverConstraint get mask => null;
+  Object get receiverConstraint => null;
 
   DynamicUseKind get kind {
     if (selector.isGetter) {
@@ -83,18 +76,18 @@
 
   List<DartType> get typeArguments => const <DartType>[];
 
-  int get hashCode =>
-      Hashing.listHash(typeArguments, Hashing.objectsHash(selector, mask));
+  int get hashCode => Hashing.listHash(
+      typeArguments, Hashing.objectsHash(selector, receiverConstraint));
 
   bool operator ==(other) {
     if (identical(this, other)) return true;
     if (other is! DynamicUse) return false;
     return selector == other.selector &&
-        mask == other.mask &&
+        receiverConstraint == other.receiverConstraint &&
         equalElements(typeArguments, other.typeArguments);
   }
 
-  String toString() => '$selector,$mask';
+  String toString() => '$selector,$receiverConstraint';
 }
 
 class GenericDynamicUse extends DynamicUse {
@@ -118,10 +111,11 @@
 /// This is used in the codegen phase where receivers are constrained to a
 /// type mask or similar.
 class ConstrainedDynamicUse extends DynamicUse {
-  final ReceiverConstraint mask;
+  final Object receiverConstraint;
   final List<DartType> _typeArguments;
 
-  ConstrainedDynamicUse(Selector selector, this.mask, this._typeArguments)
+  ConstrainedDynamicUse(
+      Selector selector, this.receiverConstraint, this._typeArguments)
       : super(selector) {
     assert(
         selector.callStructure.typeArgumentCount ==
diff --git a/pkg/compiler/lib/src/universe/world_builder.dart b/pkg/compiler/lib/src/universe/world_builder.dart
index 44d098b..04f422e 100644
--- a/pkg/compiler/lib/src/universe/world_builder.dart
+++ b/pkg/compiler/lib/src/universe/world_builder.dart
@@ -42,27 +42,6 @@
 part 'member_usage.dart';
 part 'resolution_world_builder.dart';
 
-/// The known constraint on receiver for a dynamic call site.
-///
-/// This can for instance be used to constrain this dynamic call to `foo` to
-/// 'receivers of the exact instance `Bar`':
-///
-///     class Bar {
-///        void foo() {}
-///     }
-///     main() => new Bar().foo();
-///
-abstract class ReceiverConstraint {
-  /// Returns whether [element] is a potential target when being
-  /// invoked on a receiver with this constraint. [selector] is used to ensure
-  /// library privacy is taken into account.
-  bool canHit(MemberEntity element, Selector selector, covariant World world);
-
-  /// Returns whether this [TypeMask] applied to [selector] can hit a
-  /// [noSuchMethod].
-  bool needsNoSuchMethodHandling(Selector selector, covariant World world);
-}
-
 /// The combined constraints on receivers all the dynamic call sites of the same
 /// selector.
 ///
@@ -118,7 +97,7 @@
 abstract class UniverseSelectorConstraints extends SelectorConstraints {
   /// Adds [constraint] to these selector constraints. Return `true` if the set
   /// of potential receivers expanded due to the new constraint.
-  bool addReceiverConstraint(covariant ReceiverConstraint constraint);
+  bool addReceiverConstraint(covariant Object constraint);
 }
 
 /// Strategy for computing the constraints on potential receivers of dynamic
@@ -127,6 +106,9 @@
   /// Create a [UniverseSelectorConstraints] to represent the global receiver
   /// constraints for dynamic call sites with [selector].
   UniverseSelectorConstraints createSelectorConstraints(Selector selector);
+
+  /// Returns `true`  if [member] is a potential target of [dynamicUse].
+  bool appliedUnnamed(DynamicUse dynamicUse, MemberEntity member, World world);
 }
 
 class OpenWorldStrategy implements SelectorConstraintsStrategy {
@@ -135,6 +117,12 @@
   OpenWorldConstraints createSelectorConstraints(Selector selector) {
     return new OpenWorldConstraints();
   }
+
+  @override
+  bool appliedUnnamed(DynamicUse dynamicUse, MemberEntity member, World world) {
+    Selector selector = dynamicUse.selector;
+    return selector.appliesUnnamed(member);
+  }
 }
 
 class OpenWorldConstraints extends UniverseSelectorConstraints {
@@ -147,7 +135,7 @@
   bool needsNoSuchMethodHandling(Selector selector, World world) => isAll;
 
   @override
-  bool addReceiverConstraint(ReceiverConstraint constraint) {
+  bool addReceiverConstraint(Object constraint) {
     if (isAll) return false;
     isAll = true;
     return true;
@@ -174,6 +162,15 @@
   StrongModeWorldConstraints createSelectorConstraints(Selector selector) {
     return new StrongModeWorldConstraints();
   }
+
+  @override
+  bool appliedUnnamed(
+      DynamicUse dynamicUse, MemberEntity member, covariant OpenWorld world) {
+    Selector selector = dynamicUse.selector;
+    StrongModeConstraint constraint = dynamicUse.receiverConstraint;
+    return selector.appliesUnnamed(member) &&
+        (constraint == null || constraint.canHit(member, selector, world));
+  }
 }
 
 class StrongModeWorldConstraints extends UniverseSelectorConstraints {
@@ -230,15 +227,13 @@
   }
 }
 
-class StrongModeConstraint implements ReceiverConstraint {
+class StrongModeConstraint {
   final ClassEntity cls;
 
   const StrongModeConstraint(this.cls);
 
-  @override
   bool needsNoSuchMethodHandling(Selector selector, World world) => true;
 
-  @override
   bool canHit(MemberEntity element, Selector selector, OpenWorld world) {
     return world.isInheritedInSubtypeOf(element, cls);
   }
diff --git a/tests/compiler/dart2js/model/enqueuer_test.dart b/tests/compiler/dart2js/model/enqueuer_test.dart
index 1912168..78dd3fe 100644
--- a/tests/compiler/dart2js/model/enqueuer_test.dart
+++ b/tests/compiler/dart2js/model/enqueuer_test.dart
@@ -188,7 +188,7 @@
       ElementEnvironment elementEnvironment,
       String className,
       String methodName,
-      ReceiverConstraint Function(ClassEntity cls) createConstraint) {
+      Object Function(ClassEntity cls) createConstraint) {
     ClassEntity cls = elementEnvironment.lookupClass(
         elementEnvironment.mainLibrary, className);
     Selector selector = new Selector.call(
@@ -200,11 +200,8 @@
     enqueuer.applyImpact(impact);
   }
 
-  void applyImpact(
-      Enqueuer enqueuer,
-      ElementEnvironment elementEnvironment,
-      Impact impact,
-      ReceiverConstraint Function(ClassEntity cls) createConstraint) {
+  void applyImpact(Enqueuer enqueuer, ElementEnvironment elementEnvironment,
+      Impact impact, Object Function(ClassEntity cls) createConstraint) {
     switch (impact.kind) {
       case ImpactKind.instantiate:
         instantiate(enqueuer, elementEnvironment, impact.clsName);
@@ -253,7 +250,7 @@
         compiler.frontendStrategy.elementEnvironment;
     checkInvariant(enqueuer, elementEnvironment);
 
-    ReceiverConstraint createConstraint(ClassEntity cls) {
+    Object createConstraint(ClassEntity cls) {
       return new StrongModeConstraint(cls);
     }
 
@@ -268,7 +265,7 @@
         compiler.backendClosedWorldForTesting.elementEnvironment;
     checkInvariant(enqueuer, elementEnvironment);
 
-    ReceiverConstraint createConstraint(ClassEntity cls) {
+    Object createConstraint(ClassEntity cls) {
       return new TypeMask.subtype(cls, closedWorld);
     }