Revert "Add ClassHierarchy to share code between J/KClosedWorld"

This reverts commit a8e930caf3ab089c867b5421d3941228733e2af1.

Compile time regression on some large apps.

TBR=johnniwinther@google.com

Change-Id: Ic1b736a5d50814ed8b8d24a9dc4a2d910e5f70df
Reviewed-on: https://dart-review.googlesource.com/62446
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/frontend_strategy.dart b/pkg/compiler/lib/src/frontend_strategy.dart
index 45f1261..f6b8293 100644
--- a/pkg/compiler/lib/src/frontend_strategy.dart
+++ b/pkg/compiler/lib/src/frontend_strategy.dart
@@ -23,7 +23,7 @@
 import 'library_loader.dart';
 import 'native/enqueue.dart' show NativeResolutionEnqueuer;
 import 'native/resolver.dart';
-import 'universe/class_hierarchy.dart';
+import 'universe/class_hierarchy_builder.dart';
 import 'universe/world_builder.dart';
 import 'universe/world_impact.dart';
 
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart
index 7311eb2..644cfd2 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart
@@ -334,50 +334,23 @@
     assert(TypeMask.assertIsNormalized(this, closedWorld));
     assert(TypeMask.assertIsNormalized(other, closedWorld));
     FlatTypeMask flatOther = other;
-
-    ClassEntity otherBase = flatOther.base;
-
-    bool includeNull = isNullable && flatOther.isNullable;
-
-    TypeMask emptyOrNull() {
-      return includeNull
-          ? closedWorld.abstractValueDomain.nullType
-          : closedWorld.abstractValueDomain.emptyType;
+    if (isEmptyOrNull) {
+      return flatOther.isNullable ? this : nonNullable();
+    } else if (flatOther.isEmptyOrNull) {
+      return isNullable ? flatOther : other.nonNullable();
+    } else if (base == flatOther.base) {
+      return intersectionSame(flatOther, closedWorld);
+    } else if (closedWorld.isSubclassOf(flatOther.base, base)) {
+      return intersectionStrictSubclass(flatOther, closedWorld);
+    } else if (closedWorld.isSubclassOf(base, flatOther.base)) {
+      return flatOther.intersectionStrictSubclass(this, closedWorld);
+    } else if (closedWorld.isSubtypeOf(flatOther.base, base)) {
+      return intersectionStrictSubtype(flatOther, closedWorld);
+    } else if (closedWorld.isSubtypeOf(base, flatOther.base)) {
+      return flatOther.intersectionStrictSubtype(this, closedWorld);
+    } else {
+      return intersectionDisjoint(flatOther, closedWorld);
     }
-
-    if (isEmptyOrNull || flatOther.isEmptyOrNull) {
-      return emptyOrNull();
-    }
-
-    SubclassResult result = closedWorld.classHierarchy
-        .commonSubclasses(base, _classQuery, otherBase, flatOther._classQuery);
-
-    FlatTypeMask createSingle(ClassEntity cls) {
-      switch (result.query) {
-        case ClassQuery.EXACT:
-          return includeNull
-              ? new TypeMask.exact(cls, closedWorld)
-              : new TypeMask.nonNullExact(cls, closedWorld);
-        case ClassQuery.SUBCLASS:
-          return includeNull
-              ? new TypeMask.subclass(cls, closedWorld)
-              : new TypeMask.nonNullSubclass(cls, closedWorld);
-        case ClassQuery.SUBTYPE:
-          return includeNull
-              ? new TypeMask.subtype(cls, closedWorld)
-              : new TypeMask.nonNullSubtype(cls, closedWorld);
-      }
-      throw new UnsupportedError("Unexpected ClassQuery: ${result.query}.");
-    }
-
-    List<FlatTypeMask> masks =
-        result.classes.map<FlatTypeMask>(createSingle).toList();
-    if (masks.isEmpty) return emptyOrNull();
-    if (masks.length == 1) return masks.single;
-    if (masks.length > UnionTypeMask.MAX_UNION_LENGTH) {
-      return UnionTypeMask.flatten(masks, closedWorld);
-    }
-    return new UnionTypeMask._internal(masks);
   }
 
   bool isDisjoint(TypeMask other, JClosedWorld closedWorld) {
@@ -459,6 +432,54 @@
     }
   }
 
+  TypeMask intersectionStrictSubtype(
+      FlatTypeMask other, JClosedWorld closedWorld) {
+    assert(base != other.base);
+    assert(closedWorld.isSubtypeOf(other.base, base));
+    if (!isSubtype) return intersectionHelper(other, closedWorld);
+    // Only the other mask puts constraints on the intersection mask,
+    // so base the combined flags on the other mask. Only if both
+    // masks are nullable, will the result be nullable too.
+    // The result is guaranteed to be normalized, as the other type
+    // was normalized.
+    int combined = other.flags & ((flags & 1) | ~1);
+    if (other.flags == combined) {
+      return other;
+    } else {
+      return new FlatTypeMask.normalized(other.base, combined, closedWorld);
+    }
+  }
+
+  TypeMask intersectionDisjoint(FlatTypeMask other, JClosedWorld closedWorld) {
+    assert(base != other.base);
+    assert(!closedWorld.isSubtypeOf(base, other.base));
+    assert(!closedWorld.isSubtypeOf(other.base, base));
+    return intersectionHelper(other, closedWorld);
+  }
+
+  TypeMask intersectionHelper(FlatTypeMask other, JClosedWorld closedWorld) {
+    assert(base != other.base);
+    assert(!closedWorld.isSubclassOf(base, other.base));
+    assert(!closedWorld.isSubclassOf(other.base, base));
+    // If one of the masks are exact or if both of them are subclass
+    // masks, then the intersection is empty.
+    if (isExact || other.isExact) return intersectionEmpty(other);
+    if (isSubclass && other.isSubclass) return intersectionEmpty(other);
+    assert(isSubtype || other.isSubtype);
+    int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE;
+    Iterable<ClassEntity> candidates = closedWorld.commonSubclasses(
+        base, _classQuery, other.base, other._classQuery);
+    if (candidates.isEmpty) return intersectionEmpty(other);
+    // Run through the list of candidates and compute the union. The
+    // result will only be nullable if both masks are nullable. We have
+    // to normalize here, as we generate types based on new base classes.
+    int combined = (kind << 1) | (flags & other.flags & 1);
+    Iterable<TypeMask> masks = candidates.map((ClassEntity cls) {
+      return new FlatTypeMask.normalized(cls, combined, closedWorld);
+    });
+    return UnionTypeMask.unionOf(masks, closedWorld);
+  }
+
   TypeMask intersectionEmpty(FlatTypeMask other) {
     return isNullable && other.isNullable
         ? new TypeMask.empty()
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
index 23752a0..2b6ae66 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
@@ -9,13 +9,12 @@
 import '../../constants/values.dart' show ConstantValue, PrimitiveConstantValue;
 import '../../elements/entities.dart';
 import '../../types/abstract_value_domain.dart';
-import '../../universe/class_hierarchy.dart';
 import '../../universe/selector.dart' show Selector;
 import '../../universe/use.dart' show DynamicUse;
 import '../../universe/world_builder.dart'
     show UniverseSelectorConstraints, SelectorConstraintsStrategy;
 import '../../util/util.dart';
-import '../../world.dart' show JClosedWorld;
+import '../../world.dart' show ClassQuery, JClosedWorld;
 import '../type_graph_inferrer.dart' show TypeGraphInferrer;
 import 'constants.dart';
 
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 3b6fc65..8b424b6 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -38,7 +38,7 @@
 import '../ssa/ssa.dart' show SsaFunctionCompiler;
 import '../tracer.dart';
 import '../universe/call_structure.dart' show CallStructure;
-import '../universe/class_hierarchy.dart'
+import '../universe/class_hierarchy_builder.dart'
     show ClassHierarchyBuilder, ClassQueries;
 import '../universe/selector.dart' show Selector;
 import '../universe/use.dart' show StaticUse;
diff --git a/pkg/compiler/lib/src/js_backend/impact_transformer.dart b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
index b7a388a..a438262 100644
--- a/pkg/compiler/lib/src/js_backend/impact_transformer.dart
+++ b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
@@ -4,7 +4,7 @@
 
 library js_backend.backend.impact_transformer;
 
-import '../universe/class_hierarchy.dart' show ClassHierarchyBuilder;
+import '../universe/class_hierarchy_builder.dart' show ClassHierarchyBuilder;
 
 import '../common.dart';
 import '../common_elements.dart';
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart
index 54863e5..a449b52 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart
@@ -13,11 +13,10 @@
 import '../js/js.dart' show js;
 import '../js_emitter/js_emitter.dart' show Emitter;
 import '../options.dart';
-import '../universe/class_hierarchy.dart';
 import '../universe/feature.dart';
 import '../universe/selector.dart';
 import '../universe/world_builder.dart';
-import '../world.dart' show JClosedWorld, KClosedWorld;
+import '../world.dart' show ClassQuery, JClosedWorld, KClosedWorld;
 import 'backend_usage.dart';
 import 'namer.dart';
 import 'native_data.dart';
@@ -1470,8 +1469,7 @@
         classesNeedingTypeArguments.add(cls);
 
         // TODO(ngeoffray): This should use subclasses, not subtypes.
-        closedWorld.classHierarchy.forEachStrictSubtypeOf(cls,
-            (ClassEntity sub) {
+        closedWorld.forEachStrictSubtypeOf(cls, (ClassEntity sub) {
           potentiallyNeedTypeArguments(sub);
         });
       } else if (entity is FunctionEntity) {
@@ -1696,16 +1694,19 @@
                 impliedClass(runtimeTypeUse.argumentType);
 
             // TODO(johnniwinther): Special case use of `this.runtimeType`.
-            SubclassResult result = closedWorld.classHierarchy.commonSubclasses(
-                receiverClass,
-                ClassQuery.SUBTYPE,
-                argumentClass,
-                ClassQuery.SUBTYPE);
-
-            for (ClassEntity cls in result.classes) {
-              addClass(cls);
-              if (neededOnAll) break;
+            if (closedWorld.isSubtypeOf(receiverClass, argumentClass)) {
+              addClass(receiverClass);
+            } else if (closedWorld.isSubtypeOf(argumentClass, receiverClass)) {
+              addClass(argumentClass);
             }
+            if (neededOnAll) break;
+            // TODO(johnniwinther): Special case use of `this.runtimeType`.
+            // TODO(johnniwinther): Rename [commonSubclasses] to something like
+            // [strictCommonClasses] and support the non-strict case directly.
+            // Since we do a subclassesOf
+            classesDirectlyNeedingRuntimeType.addAll(
+                closedWorld.commonSubclasses(receiverClass, ClassQuery.SUBTYPE,
+                    argumentClass, ClassQuery.SUBTYPE));
             break;
           case RuntimeTypeUseKind.unknown:
             addClass(impliedClass(runtimeTypeUse.receiverType));
@@ -1716,17 +1717,15 @@
       Set<ClassEntity> allClassesNeedingRuntimeType;
       if (neededOnAll) {
         neededOnFunctions = true;
-        allClassesNeedingRuntimeType = closedWorld.classHierarchy
-            .subclassesOf(commonElements.objectClass)
-            .toSet();
+        allClassesNeedingRuntimeType =
+            closedWorld.subclassesOf(commonElements.objectClass).toSet();
       } else {
         allClassesNeedingRuntimeType = new Set<ClassEntity>();
         // TODO(johnniwinther): Support this operation directly in
         // [ClosedWorld] using the [ClassSet]s.
         for (ClassEntity cls in classesDirectlyNeedingRuntimeType) {
           if (!allClassesNeedingRuntimeType.contains(cls)) {
-            allClassesNeedingRuntimeType
-                .addAll(closedWorld.classHierarchy.subtypesOf(cls));
+            allClassesNeedingRuntimeType.addAll(closedWorld.subtypesOf(cls));
           }
         }
       }
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 1f4923a..6b68731 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -289,10 +289,10 @@
       });
     }
 
-    closedWorld.classHierarchy
+    closedWorld
         .getClassHierarchyNode(closedWorld.commonElements.objectClass)
         .forEachSubclass((ClassEntity cls) {
-      convertClassSet(closedWorld.classHierarchy.getClassSet(cls));
+      convertClassSet(closedWorld.getClassSet(cls));
     }, ClassHierarchyNode.ALL);
 
     Set<MemberEntity> liveInstanceMembers =
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index b3e5f49..2136ceb 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -43,7 +43,7 @@
 import '../ordered_typeset.dart';
 import '../ssa/kernel_impact.dart';
 import '../ssa/type_builder.dart';
-import '../universe/class_hierarchy.dart';
+import '../universe/class_hierarchy_builder.dart';
 import '../universe/class_set.dart';
 import '../universe/selector.dart';
 import '../universe/world_builder.dart';
@@ -2073,6 +2073,9 @@
 
   final Map<ClassEntity, Set<ClassEntity>> typesImplementedBySubclasses;
 
+  final Map<ClassEntity, ClassHierarchyNode> _classHierarchyNodes;
+  final Map<ClassEntity, ClassSet> _classSets;
+
   // TODO(johnniwinther): Can this be derived from [ClassSet]s?
   final Set<ClassEntity> _implementedClasses;
 
@@ -2086,8 +2089,6 @@
 
   final Iterable<MemberEntity> processedMembers;
 
-  final ClassHierarchy classHierarchy;
-
   KClosedWorldImpl(this.elementMap,
       {CompilerOptions options,
       this.elementEnvironment,
@@ -2110,8 +2111,8 @@
       Map<ClassEntity, ClassHierarchyNode> classHierarchyNodes,
       Map<ClassEntity, ClassSet> classSets})
       : _implementedClasses = implementedClasses,
-        classHierarchy = new ClassHierarchyImpl(
-            commonElements, classHierarchyNodes, classSets) {
+        _classHierarchyNodes = classHierarchyNodes,
+        _classSets = classSets {
     computeRtiNeed(resolutionWorldBuilder, rtiNeedBuilder, options);
   }
 
@@ -2119,6 +2120,152 @@
   bool isImplemented(ClassEntity cls) {
     return _implementedClasses.contains(cls);
   }
+
+  /// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
+  /// of known classes.
+  ///
+  /// This method is only provided for testing. For queries on classes, use the
+  /// methods defined in [JClosedWorld].
+  ClassHierarchyNode getClassHierarchyNode(ClassEntity cls) {
+    return _classHierarchyNodes[cls];
+  }
+
+  /// Returns [ClassSet] for [cls] used to model the extends and implements
+  /// relations of known classes.
+  ///
+  /// This method is only provided for testing. For queries on classes, use the
+  /// methods defined in [JClosedWorld].
+  ClassSet getClassSet(ClassEntity cls) {
+    return _classSets[cls];
+  }
+
+  // TODO(johnniwinther): Share the methods based on [ClassSet] and
+  // [ClassHierarchyNode] between KClosedWorld and JClosedWorld.
+  /// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an
+  /// instance of [y].
+  bool isSubtypeOf(ClassEntity x, ClassEntity y) {
+    ClassSet classSet = _classSets[y];
+    assert(classSet != null,
+        failedAt(y, "No ClassSet for $y (${y.runtimeType}): ${_classSets}"));
+    ClassHierarchyNode classHierarchyNode = _classHierarchyNodes[x];
+    assert(classHierarchyNode != null,
+        failedAt(x, "No ClassHierarchyNode for $x"));
+    return classSet.hasSubtype(classHierarchyNode);
+  }
+
+  /// Returns an iterable over the directly instantiated that implement [cls]
+  /// possibly including [cls] itself, if it is live.
+  Iterable<ClassEntity> subtypesOf(ClassEntity cls) {
+    ClassSet classSet = _classSets[cls];
+    if (classSet == null) {
+      return const <ClassEntity>[];
+    } else {
+      return classSet
+          .subtypesByMask(ClassHierarchyNode.EXPLICITLY_INSTANTIATED);
+    }
+  }
+
+  /// Returns an iterable over the directly instantiated classes that extend
+  /// [cls] possibly including [cls] itself, if it is live.
+  Iterable<ClassEntity> subclassesOf(ClassEntity cls) {
+    ClassHierarchyNode hierarchy = _classHierarchyNodes[cls];
+    if (hierarchy == null) return const <ClassEntity>[];
+    return hierarchy
+        .subclassesByMask(ClassHierarchyNode.EXPLICITLY_INSTANTIATED);
+  }
+
+  /// Returns an iterable over the directly instantiated that implement [cls]
+  /// _not_ including [cls].
+  Iterable<ClassEntity> strictSubtypesOf(ClassEntity cls) {
+    ClassSet classSet = _classSets[cls];
+    if (classSet == null) {
+      return const <ClassEntity>[];
+    } else {
+      return classSet.subtypesByMask(ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
+          strict: true);
+    }
+  }
+
+  Iterable<ClassEntity> getInterfaces(ClassEntity cls) {
+    return elementMap._getInterfaces(cls).map((t) => t.element);
+  }
+
+  ClassEntity getSuperClass(ClassEntity cls) {
+    return elementMap._getSuperType(cls)?.element;
+  }
+
+  /// Returns an iterable over the directly instantiated classes that extend
+  /// [cls] _not_ including [cls] itself.
+  Iterable<ClassEntity> strictSubclassesOf(ClassEntity cls) {
+    ClassHierarchyNode subclasses = _classHierarchyNodes[cls];
+    if (subclasses == null) return const <ClassEntity>[];
+    return subclasses.subclassesByMask(
+        ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
+        strict: true);
+  }
+
+  Set<ClassEntity> _commonContainedClasses(ClassEntity cls1, ClassQuery query1,
+      ClassEntity cls2, ClassQuery query2) {
+    Iterable<ClassEntity> xSubset = _containedSubset(cls1, query1);
+    if (xSubset == null) return null;
+    Iterable<ClassEntity> ySubset = _containedSubset(cls2, query2);
+    if (ySubset == null) return null;
+    return xSubset.toSet().intersection(ySubset.toSet());
+  }
+
+  Iterable<ClassEntity> _containedSubset(ClassEntity cls, ClassQuery query) {
+    switch (query) {
+      case ClassQuery.EXACT:
+        return null;
+      case ClassQuery.SUBCLASS:
+        return strictSubclassesOf(cls);
+      case ClassQuery.SUBTYPE:
+        return strictSubtypesOf(cls);
+    }
+    throw new ArgumentError('Unexpected query: $query.');
+  }
+
+  Iterable<ClassEntity> commonSubclasses(ClassEntity cls1, ClassQuery query1,
+      ClassEntity cls2, ClassQuery query2) {
+    // TODO(johnniwinther): Use [ClassSet] to compute this.
+    // Compute the set of classes that are contained in both class subsets.
+    Set<ClassEntity> common =
+        _commonContainedClasses(cls1, query1, cls2, query2);
+    if (common == null || common.isEmpty) return const <ClassEntity>[];
+    // Narrow down the candidates by only looking at common classes
+    // that do not have a superclass or supertype that will be a
+    // better candidate.
+    return common.where((ClassEntity each) {
+      bool containsSuperclass = common.contains(getSuperClass(each));
+      // If the superclass is also a candidate, then we don't want to
+      // deal with this class. If we're only looking for a subclass we
+      // know we don't have to look at the list of interfaces because
+      // they can never be in the common set.
+      if (containsSuperclass ||
+          query1 == ClassQuery.SUBCLASS ||
+          query2 == ClassQuery.SUBCLASS) {
+        return !containsSuperclass;
+      }
+      // Run through the direct supertypes of the class. If the common
+      // set contains the direct supertype of the class, we ignore the
+      // the class because the supertype is a better candidate.
+
+      for (ClassEntity interface in getInterfaces(each)) {
+        if (common.contains(interface)) return false;
+      }
+      return true;
+    });
+  }
+
+  /// Applies [f] to each live class that implements [cls] _not_ including [cls]
+  /// itself.
+  void forEachStrictSubtypeOf(
+      ClassEntity cls, IterationStep f(ClassEntity cls)) {
+    ClassSet classSet = _classSets[cls];
+    if (classSet == null) return;
+    classSet.forEachSubtype(f, ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
+        strict: true);
+  }
 }
 
 // Interface for testing equivalence of Kernel-based entities.
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index 9e91d03..19e2aa3 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -31,7 +31,7 @@
 import '../native/enqueue.dart' show NativeResolutionEnqueuer;
 import '../native/resolver.dart';
 import '../options.dart';
-import '../universe/class_hierarchy.dart';
+import '../universe/class_hierarchy_builder.dart';
 import '../universe/world_builder.dart';
 import '../universe/world_impact.dart';
 import 'deferred_load.dart';
diff --git a/pkg/compiler/lib/src/universe/class_hierarchy.dart b/pkg/compiler/lib/src/universe/class_hierarchy.dart
deleted file mode 100644
index f19320a..0000000
--- a/pkg/compiler/lib/src/universe/class_hierarchy.dart
+++ /dev/null
@@ -1,543 +0,0 @@
-// 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.
-
-import '../common.dart';
-import '../common_elements.dart';
-import '../elements/entities.dart';
-import '../elements/types.dart' show InterfaceType;
-import 'class_set.dart';
-
-// TODO(johnniwinther): Move more methods from `JClosedWorld` to
-// `ClassHierarchy`.
-abstract class ClassHierarchy {
-  /// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
-  /// of known classes.
-  ///
-  /// This method is only provided for testing. For queries on classes, use the
-  /// methods defined in [JClosedWorld].
-  ClassHierarchyNode getClassHierarchyNode(ClassEntity cls);
-
-  /// Returns [ClassSet] for [cls] used to model the extends and implements
-  /// relations of known classes.
-  ///
-  /// This method is only provided for testing. For queries on classes, use the
-  /// methods defined in [JClosedWorld].
-  ClassSet getClassSet(ClassEntity cls);
-
-  /// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an
-  /// instance of [y].
-  bool isSubtypeOf(ClassEntity x, ClassEntity y);
-
-  /// Returns a [SubclassResult] for the subclasses that are contained in
-  /// the subclass/subtype sets of both [cls1] and [cls2].
-  ///
-  /// Classes that are implied by included superclasses/supertypes are not
-  /// returned.
-  ///
-  /// For instance for this hierarchy
-  ///
-  ///     class A {}
-  ///     class B {}
-  ///     class C implements A, B {}
-  ///     class D extends C {}
-  ///
-  /// the query
-  ///
-  ///     commonSubclasses(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE)
-  ///
-  /// return the set {C} because [D] is implied by [C].
-  SubclassResult commonSubclasses(
-      ClassEntity cls1, ClassQuery query1, ClassEntity cls2, ClassQuery query2);
-
-  /// Returns an iterable over the directly instantiated that implement [cls]
-  /// possibly including [cls] itself, if it is live.
-  Iterable<ClassEntity> subtypesOf(ClassEntity cls);
-
-  /// Returns an iterable over the live classes that extend [cls] including
-  /// [cls] itself.
-  Iterable<ClassEntity> subclassesOf(ClassEntity cls);
-
-  /// Applies [f] to each live class that implements [cls] _not_ including [cls]
-  /// itself.
-  void forEachStrictSubtypeOf(
-      ClassEntity cls, IterationStep f(ClassEntity cls));
-}
-
-class ClassHierarchyImpl implements ClassHierarchy {
-  final CommonElements _commonElements;
-  final Map<ClassEntity, ClassHierarchyNode> _classHierarchyNodes;
-  final Map<ClassEntity, ClassSet> _classSets;
-
-  ClassHierarchyImpl(
-      this._commonElements, this._classHierarchyNodes, this._classSets);
-
-  /// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
-  /// of known classes.
-  ///
-  /// This method is only provided for testing. For queries on classes, use the
-  /// methods defined in [JClosedWorld].
-  ClassHierarchyNode getClassHierarchyNode(ClassEntity cls) {
-    return _classHierarchyNodes[cls];
-  }
-
-  /// Returns [ClassSet] for [cls] used to model the extends and implements
-  /// relations of known classes.
-  ///
-  /// This method is only provided for testing. For queries on classes, use the
-  /// methods defined in [JClosedWorld].
-  ClassSet getClassSet(ClassEntity cls) {
-    return _classSets[cls];
-  }
-
-  /// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an
-  /// instance of [y].
-  bool isSubtypeOf(ClassEntity x, ClassEntity y) {
-    ClassSet classSet = _classSets[y];
-    assert(classSet != null,
-        failedAt(y, "No ClassSet for $y (${y.runtimeType}): ${_classSets}"));
-    ClassHierarchyNode classHierarchyNode = _classHierarchyNodes[x];
-    assert(classHierarchyNode != null,
-        failedAt(x, "No ClassHierarchyNode for $x"));
-    return classSet.hasSubtype(classHierarchyNode);
-  }
-
-  /// Return `true` if [x] is a (non-strict) subclass of [y].
-  bool isSubclassOf(ClassEntity x, ClassEntity y) {
-    return _classHierarchyNodes[y].hasSubclass(_classHierarchyNodes[x]);
-  }
-
-  /// Returns an iterable over the directly instantiated that implement [cls]
-  /// possibly including [cls] itself, if it is live.
-  Iterable<ClassEntity> subtypesOf(ClassEntity cls) {
-    ClassSet classSet = _classSets[cls];
-    if (classSet == null) {
-      return const <ClassEntity>[];
-    } else {
-      return classSet
-          .subtypesByMask(ClassHierarchyNode.EXPLICITLY_INSTANTIATED);
-    }
-  }
-
-  /// Returns an iterable over the directly instantiated classes that extend
-  /// [cls] possibly including [cls] itself, if it is live.
-  Iterable<ClassEntity> subclassesOf(ClassEntity cls) {
-    ClassHierarchyNode hierarchy = _classHierarchyNodes[cls];
-    if (hierarchy == null) return const <ClassEntity>[];
-    return hierarchy
-        .subclassesByMask(ClassHierarchyNode.EXPLICITLY_INSTANTIATED);
-  }
-
-  /// Returns an iterable over the directly instantiated that implement [cls]
-  /// _not_ including [cls].
-  Iterable<ClassEntity> strictSubtypesOf(ClassEntity cls) {
-    ClassSet classSet = _classSets[cls];
-    if (classSet == null) {
-      return const <ClassEntity>[];
-    } else {
-      return classSet.subtypesByMask(ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
-          strict: true);
-    }
-  }
-
-  /// Returns an iterable over the directly instantiated classes that extend
-  /// [cls] _not_ including [cls] itself.
-  Iterable<ClassEntity> strictSubclassesOf(ClassEntity cls) {
-    ClassHierarchyNode subclasses = _classHierarchyNodes[cls];
-    if (subclasses == null) return const <ClassEntity>[];
-    return subclasses.subclassesByMask(
-        ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
-        strict: true);
-  }
-
-  /// Applies [f] to each live class that extend [cls] _not_ including [cls]
-  /// itself.
-  void forEachStrictSubclassOf(
-      ClassEntity cls, IterationStep f(ClassEntity cls)) {
-    ClassHierarchyNode subclasses = _classHierarchyNodes[cls];
-    if (subclasses == null) return;
-    subclasses.forEachSubclass(f, ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
-        strict: true);
-  }
-
-  /// Applies [f] to each live class that implements [cls] _not_ including [cls]
-  /// itself.
-  void forEachStrictSubtypeOf(
-      ClassEntity cls, IterationStep f(ClassEntity cls)) {
-    ClassSet classSet = _classSets[cls];
-    if (classSet == null) return;
-    classSet.forEachSubtype(f, ClassHierarchyNode.EXPLICITLY_INSTANTIATED,
-        strict: true);
-  }
-
-  SubclassResult commonSubclasses(ClassEntity cls1, ClassQuery query1,
-      ClassEntity cls2, ClassQuery query2) {
-    if (query1 == ClassQuery.EXACT && query2 == ClassQuery.EXACT) {
-      // Exact classes [cls1] and [cls2] must be identical to have any classes
-      // in common.
-      if (cls1 != cls2) {
-        return const SubclassResult.empty();
-      }
-      return new SubclassResult.exact(cls1);
-    } else if (query1 == ClassQuery.EXACT) {
-      if (query2 == ClassQuery.SUBCLASS) {
-        // Exact [cls1] must be a subclass of [cls2] to have any classes in
-        // common.
-        if (isSubclassOf(cls1, cls2)) {
-          return new SubclassResult.exact(cls1);
-        }
-      } else if (query2 == ClassQuery.SUBTYPE) {
-        // Exact [cls1] must be a subtype of [cls2] to have any classes in
-        // common.
-        if (isSubtypeOf(cls1, cls2)) {
-          return new SubclassResult.exact(cls1);
-        }
-      }
-      return const SubclassResult.empty();
-    } else if (query2 == ClassQuery.EXACT) {
-      if (query1 == ClassQuery.SUBCLASS) {
-        // Exact [cls2] must be a subclass of [cls1] to have any classes in
-        // common.
-        if (isSubclassOf(cls2, cls1)) {
-          return new SubclassResult.exact(cls2);
-        }
-      } else if (query1 == ClassQuery.SUBTYPE) {
-        // Exact [cls2] must be a subtype of [cls1] to have any classes in
-        // common.
-        if (isSubtypeOf(cls2, cls1)) {
-          return new SubclassResult.exact(cls2);
-        }
-      }
-      return const SubclassResult.empty();
-    } else if (query1 == ClassQuery.SUBCLASS && query2 == ClassQuery.SUBCLASS) {
-      // [cls1] must be a subclass of [cls2] or vice versa to have any classes
-      // in common.
-      if (cls1 == cls2 || isSubclassOf(cls1, cls2)) {
-        // The subclasses of [cls1] are contained within the subclasses of
-        // [cls2].
-        return new SubclassResult.subclass(cls1);
-      } else if (isSubclassOf(cls2, cls1)) {
-        // The subclasses of [cls2] are contained within the subclasses of
-        // [cls1].
-        return new SubclassResult.subclass(cls2);
-      }
-      return const SubclassResult.empty();
-    } else if (query1 == ClassQuery.SUBCLASS) {
-      if (isSubtypeOf(cls1, cls2)) {
-        // The subclasses of [cls1] are all subtypes of [cls2].
-        return new SubclassResult.subclass(cls1);
-      }
-      if (cls1 == _commonElements.objectClass) {
-        // Since [cls1] is `Object` all subtypes of [cls2] are contained within
-        // the subclasses of [cls1].
-        return new SubclassResult.subtype(cls2);
-      }
-      // Find all the root subclasses of [cls1] of that implement [cls2].
-      //
-      // For this hierarchy:
-      //
-      //     class I {}
-      //     class A {}
-      //     class B extends A implements I {}
-      //     class C extends B {}
-      //     class D extends A implements I {}
-      //
-      // the common subclasses of "subclass of A" and "subtype of I" returns
-      // "subclasses of {B, D}". The inclusion of class `C` is implied because
-      // it is a subclass of `B`.
-      List<ClassEntity> classes = <ClassEntity>[];
-      forEachStrictSubclassOf(cls1, (ClassEntity subclass) {
-        if (isSubtypeOf(subclass, cls2)) {
-          classes.add(subclass);
-          // Skip subclasses of [subclass]; they all implement [cls2] by
-          // inheritance and are included in the subclasses of [subclass].
-          return IterationStep.SKIP_SUBCLASSES;
-        }
-        return IterationStep.CONTINUE;
-      });
-      return new SubclassResult.subclasses(classes);
-    } else if (query2 == ClassQuery.SUBCLASS) {
-      if (isSubtypeOf(cls2, cls1)) {
-        // The subclasses of [cls2] are all subtypes of [cls1].
-        return new SubclassResult.subclass(cls2);
-      }
-      if (cls2 == _commonElements.objectClass) {
-        // Since [cls2] is `Object` all subtypes of [cls1] are contained within
-        // the subclasses of [cls2].
-        return new SubclassResult.subtype(cls1);
-      }
-      // Find all the root subclasses of [cls2] of that implement [cls1].
-      List<ClassEntity> classes = <ClassEntity>[];
-      forEachStrictSubclassOf(cls2, (ClassEntity subclass) {
-        if (isSubtypeOf(subclass, cls1)) {
-          classes.add(subclass);
-          // Skip subclasses of [subclass]; they all implement [cls1] by
-          // inheritance and are included in the subclasses of [subclass].
-          return IterationStep.SKIP_SUBCLASSES;
-        }
-        return IterationStep.CONTINUE;
-      });
-      return new SubclassResult.subclasses(classes);
-    } else {
-      if (cls1 == cls2 || isSubtypeOf(cls1, cls2)) {
-        // The subtypes of [cls1] are contained within the subtypes of [cls2].
-        return new SubclassResult.subtype(cls1);
-      } else if (isSubtypeOf(cls2, cls1)) {
-        // The subtypes of [cls2] are contained within the subtypes of [cls1].
-        return new SubclassResult.subtype(cls2);
-      }
-      // Find all the root subclasses of [cls1] of that implement [cls2].
-      //
-      // For this hierarchy:
-      //
-      //     class I {}
-      //     class A {}
-      //     class B extends A implements I {}
-      //     class C extends B {}
-      //     class D extends A implements I {}
-      //     class E implements B {}
-      //     class F extends E {}
-      //
-      // the common subclasses of "subtype of A" and "subtype of I" returns
-      // "subclasses of {B, D, E}". The inclusion of classes `C` and `F` is
-      // implied because they are subclasses of `B` and `E`, respectively.
-      List<ClassEntity> classes = <ClassEntity>[];
-      forEachStrictSubtypeOf(cls1, (ClassEntity subclass) {
-        if (isSubtypeOf(subclass, cls2)) {
-          classes.add(subclass);
-          // Skip subclasses of [subclass]; they all implement [cls2] by
-          // inheritance and are included in the subclasses of [subclass].
-          return IterationStep.SKIP_SUBCLASSES;
-        }
-        return IterationStep.CONTINUE;
-      });
-      return new SubclassResult.subclasses(classes);
-    }
-  }
-}
-
-class ClassHierarchyBuilder {
-  // We keep track of subtype and subclass relationships in four
-  // distinct sets to make class hierarchy analysis faster.
-  final Map<ClassEntity, ClassHierarchyNode> classHierarchyNodes =
-      <ClassEntity, ClassHierarchyNode>{};
-  final Map<ClassEntity, ClassSet> classSets = <ClassEntity, ClassSet>{};
-  final Map<ClassEntity, Set<ClassEntity>> mixinUses =
-      new Map<ClassEntity, Set<ClassEntity>>();
-
-  final CommonElements _commonElements;
-  final ClassQueries _classQueries;
-
-  ClassHierarchyBuilder(this._commonElements, this._classQueries);
-
-  void registerClass(ClassEntity cls) {
-    _ensureClassSet(_classQueries.getDeclaration(cls));
-  }
-
-  ClassHierarchyNode _ensureClassHierarchyNode(ClassEntity cls) {
-    assert(_classQueries.checkClass(cls));
-    return classHierarchyNodes.putIfAbsent(cls, () {
-      ClassHierarchyNode parentNode;
-      ClassEntity superclass = _classQueries.getSuperClass(cls);
-      if (superclass != null) {
-        parentNode = _ensureClassHierarchyNode(superclass);
-      }
-      return new ClassHierarchyNode(
-          parentNode, cls, _classQueries.getHierarchyDepth(cls));
-    });
-  }
-
-  ClassSet _ensureClassSet(ClassEntity cls) {
-    assert(_classQueries.checkClass(cls));
-    return classSets.putIfAbsent(cls, () {
-      ClassHierarchyNode node = _ensureClassHierarchyNode(cls);
-      ClassSet classSet = new ClassSet(node);
-
-      for (InterfaceType type in _classQueries.getSupertypes(cls)) {
-        // TODO(johnniwinther): Optimization: Avoid adding [cls] to
-        // superclasses.
-        ClassSet subtypeSet = _ensureClassSet(type.element);
-        subtypeSet.addSubtype(node);
-      }
-
-      ClassEntity appliedMixin = _classQueries.getAppliedMixin(cls);
-      while (appliedMixin != null) {
-        // TODO(johnniwinther): Use the data stored in [ClassSet].
-        registerMixinUse(cls, appliedMixin);
-        ClassSet mixinSet = _ensureClassSet(appliedMixin);
-        mixinSet.addMixinApplication(node);
-
-        // In case of
-        //
-        //    class A {}
-        //    class B = Object with A;
-        //    class C = Object with B;
-        //
-        // we need to register that C not only mixes in B but also A.
-        appliedMixin = _classQueries.getAppliedMixin(appliedMixin);
-      }
-      return classSet;
-    });
-  }
-
-  void _updateSuperClassHierarchyNodeForClass(ClassHierarchyNode node) {
-    // Ensure that classes implicitly implementing `Function` are in its
-    // subtype set.
-    ClassEntity cls = node.cls;
-    if (cls != _commonElements.functionClass &&
-        _classQueries.implementsFunction(cls)) {
-      ClassSet subtypeSet = _ensureClassSet(_commonElements.functionClass);
-      subtypeSet.addSubtype(node);
-    }
-    if (!node.isInstantiated && node.parentNode != null) {
-      _updateSuperClassHierarchyNodeForClass(node.parentNode);
-    }
-  }
-
-  void updateClassHierarchyNodeForClass(ClassEntity cls,
-      {bool directlyInstantiated: false, bool abstractlyInstantiated: false}) {
-    ClassHierarchyNode node = _ensureClassSet(cls).node;
-    _updateSuperClassHierarchyNodeForClass(node);
-    if (directlyInstantiated) {
-      node.isDirectlyInstantiated = true;
-    }
-    if (abstractlyInstantiated) {
-      node.isAbstractlyInstantiated = true;
-    }
-  }
-
-  void registerMixinUse(ClassEntity mixinApplication, ClassEntity mixin) {
-    // TODO(johnniwinther): Add map restricted to live classes.
-    // We don't support patch classes as mixin.
-    Set<ClassEntity> users =
-        mixinUses.putIfAbsent(mixin, () => new Set<ClassEntity>());
-    users.add(mixinApplication);
-  }
-
-  bool _isSubtypeOf(ClassEntity x, ClassEntity y) {
-    assert(
-        classSets.containsKey(x), "ClassSet for $x has not been computed yet.");
-    ClassSet classSet = classSets[y];
-    assert(classSet != null,
-        failedAt(y, "No ClassSet for $y (${y.runtimeType}): ${classSets}"));
-    ClassHierarchyNode classHierarchyNode = classHierarchyNodes[x];
-    assert(classHierarchyNode != null,
-        failedAt(x, "No ClassHierarchyNode for $x"));
-    return classSet.hasSubtype(classHierarchyNode);
-  }
-
-  bool isInheritedInSubtypeOf(ClassEntity x, ClassEntity y) {
-    ClassSet classSet = classSets[x];
-    assert(classSet != null,
-        failedAt(x, "No ClassSet for $x (${x.runtimeType}): ${classSets}"));
-
-    if (_isSubtypeOf(x, y)) {
-      // [x] implements [y] itself, possible through supertypes.
-      return true;
-    }
-
-    /// Returns `true` if any live subclass of [node] implements [y].
-    bool subclassImplements(ClassHierarchyNode node, {bool strict}) {
-      return node.anySubclass((ClassEntity z) => _isSubtypeOf(z, y),
-          ClassHierarchyNode.INSTANTIATED,
-          strict: strict);
-    }
-
-    if (subclassImplements(classSet.node, strict: true)) {
-      // A subclass of [x] implements [y].
-      return true;
-    }
-
-    for (ClassHierarchyNode mixinApplication
-        in classSet.mixinApplicationNodes) {
-      if (subclassImplements(mixinApplication, strict: false)) {
-        // A subclass of [mixinApplication] implements [y].
-        return true;
-      }
-    }
-    return false;
-  }
-}
-
-abstract class ClassQueries {
-  bool checkClass(covariant ClassEntity cls);
-  bool validateClass(covariant ClassEntity cls);
-
-  /// Returns the declaration of [cls].
-  ClassEntity getDeclaration(covariant ClassEntity cls);
-
-  /// Returns the class mixed into [cls] if any.
-  // TODO(johnniwinther): Replace this by a `getAppliedMixins` function that
-  // return transitively mixed in classes like in:
-  //     class A {}
-  //     class B = Object with A;
-  //     class C = Object with B;
-  ClassEntity getAppliedMixin(covariant ClassEntity cls);
-
-  /// Returns the hierarchy depth of [cls].
-  int getHierarchyDepth(covariant ClassEntity cls);
-
-  /// Returns `true` if [cls] implements `Function` either explicitly or through
-  /// a `call` method.
-  bool implementsFunction(covariant ClassEntity cls);
-
-  /// Returns the superclass of [cls] if any.
-  ClassEntity getSuperClass(covariant ClassEntity cls);
-
-  /// Returns all supertypes of [cls].
-  Iterable<InterfaceType> getSupertypes(covariant ClassEntity cls);
-}
-
-/// Enum values defining subset of classes included in queries.
-enum ClassQuery {
-  /// Only the class itself is included.
-  EXACT,
-
-  /// The class and all subclasses (transitively) are included.
-  SUBCLASS,
-
-  /// The class and all classes that implement or subclass it (transitively)
-  /// are included.
-  SUBTYPE,
-}
-
-/// Result computed in [ClassHierarchy.commonSubclasses].
-class SubclassResult {
-  /// The classes in the result set. The classes are always disjoint wrt. the
-  /// interpretation of [query].
-  final List<ClassEntity> classes;
-
-  /// How [classes] should be interpreted: If `ClassQuery.EXACT`, only the
-  /// classes in [classes] are on the result set. If `ClassQuery.SUBCLASS`,
-  /// non-strict subclasses of the classes in [classes] are in the result set.
-  /// If `ClassQuery.SUBTYPE`, non-strict subtypes of the classes in [classes]
-  /// are in the result set.
-  final ClassQuery query;
-
-  /// Creates the empty result set.
-  const SubclassResult.empty()
-      : query = ClassQuery.EXACT,
-        classes = const <ClassEntity>[];
-
-  /// Creates the single set of [cls].
-  SubclassResult.exact(ClassEntity cls)
-      : query = ClassQuery.EXACT,
-        classes = <ClassEntity>[cls];
-
-  /// Creates the set of subclasses of [cls].
-  SubclassResult.subclass(ClassEntity cls)
-      : query = ClassQuery.SUBCLASS,
-        classes = <ClassEntity>[cls];
-
-  /// Creates the set of subtypes of [cls].
-  SubclassResult.subtype(ClassEntity cls)
-      : query = ClassQuery.SUBTYPE,
-        classes = <ClassEntity>[cls];
-
-  /// Creates the set of classes that are subclasses of a class in [classes].
-  SubclassResult.subclasses(this.classes) : query = ClassQuery.SUBCLASS;
-
-  SubclassResult.internal(this.query, this.classes);
-
-  String toString() => 'SubclassResult($query,$classes)';
-}
diff --git a/pkg/compiler/lib/src/universe/class_hierarchy_builder.dart b/pkg/compiler/lib/src/universe/class_hierarchy_builder.dart
new file mode 100644
index 0000000..337975f
--- /dev/null
+++ b/pkg/compiler/lib/src/universe/class_hierarchy_builder.dart
@@ -0,0 +1,181 @@
+// 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.
+
+import '../common.dart';
+import '../common_elements.dart';
+import '../elements/entities.dart';
+import '../elements/types.dart' show InterfaceType;
+import 'class_set.dart';
+
+class ClassHierarchyBuilder {
+  // We keep track of subtype and subclass relationships in four
+  // distinct sets to make class hierarchy analysis faster.
+  final Map<ClassEntity, ClassHierarchyNode> classHierarchyNodes =
+      <ClassEntity, ClassHierarchyNode>{};
+  final Map<ClassEntity, ClassSet> classSets = <ClassEntity, ClassSet>{};
+  final Map<ClassEntity, Set<ClassEntity>> mixinUses =
+      new Map<ClassEntity, Set<ClassEntity>>();
+
+  final CommonElements _commonElements;
+  final ClassQueries _classQueries;
+
+  ClassHierarchyBuilder(this._commonElements, this._classQueries);
+
+  void registerClass(ClassEntity cls) {
+    _ensureClassSet(_classQueries.getDeclaration(cls));
+  }
+
+  ClassHierarchyNode _ensureClassHierarchyNode(ClassEntity cls) {
+    assert(_classQueries.checkClass(cls));
+    return classHierarchyNodes.putIfAbsent(cls, () {
+      ClassHierarchyNode parentNode;
+      ClassEntity superclass = _classQueries.getSuperClass(cls);
+      if (superclass != null) {
+        parentNode = _ensureClassHierarchyNode(superclass);
+      }
+      return new ClassHierarchyNode(
+          parentNode, cls, _classQueries.getHierarchyDepth(cls));
+    });
+  }
+
+  ClassSet _ensureClassSet(ClassEntity cls) {
+    assert(_classQueries.checkClass(cls));
+    return classSets.putIfAbsent(cls, () {
+      ClassHierarchyNode node = _ensureClassHierarchyNode(cls);
+      ClassSet classSet = new ClassSet(node);
+
+      for (InterfaceType type in _classQueries.getSupertypes(cls)) {
+        // TODO(johnniwinther): Optimization: Avoid adding [cls] to
+        // superclasses.
+        ClassSet subtypeSet = _ensureClassSet(type.element);
+        subtypeSet.addSubtype(node);
+      }
+
+      ClassEntity appliedMixin = _classQueries.getAppliedMixin(cls);
+      while (appliedMixin != null) {
+        // TODO(johnniwinther): Use the data stored in [ClassSet].
+        registerMixinUse(cls, appliedMixin);
+        ClassSet mixinSet = _ensureClassSet(appliedMixin);
+        mixinSet.addMixinApplication(node);
+
+        // In case of
+        //
+        //    class A {}
+        //    class B = Object with A;
+        //    class C = Object with B;
+        //
+        // we need to register that C not only mixes in B but also A.
+        appliedMixin = _classQueries.getAppliedMixin(appliedMixin);
+      }
+      return classSet;
+    });
+  }
+
+  void _updateSuperClassHierarchyNodeForClass(ClassHierarchyNode node) {
+    // Ensure that classes implicitly implementing `Function` are in its
+    // subtype set.
+    ClassEntity cls = node.cls;
+    if (cls != _commonElements.functionClass &&
+        _classQueries.implementsFunction(cls)) {
+      ClassSet subtypeSet = _ensureClassSet(_commonElements.functionClass);
+      subtypeSet.addSubtype(node);
+    }
+    if (!node.isInstantiated && node.parentNode != null) {
+      _updateSuperClassHierarchyNodeForClass(node.parentNode);
+    }
+  }
+
+  void updateClassHierarchyNodeForClass(ClassEntity cls,
+      {bool directlyInstantiated: false, bool abstractlyInstantiated: false}) {
+    ClassHierarchyNode node = _ensureClassSet(cls).node;
+    _updateSuperClassHierarchyNodeForClass(node);
+    if (directlyInstantiated) {
+      node.isDirectlyInstantiated = true;
+    }
+    if (abstractlyInstantiated) {
+      node.isAbstractlyInstantiated = true;
+    }
+  }
+
+  void registerMixinUse(ClassEntity mixinApplication, ClassEntity mixin) {
+    // TODO(johnniwinther): Add map restricted to live classes.
+    // We don't support patch classes as mixin.
+    Set<ClassEntity> users =
+        mixinUses.putIfAbsent(mixin, () => new Set<ClassEntity>());
+    users.add(mixinApplication);
+  }
+
+  bool _isSubtypeOf(ClassEntity x, ClassEntity y) {
+    assert(
+        classSets.containsKey(x), "ClassSet for $x has not been computed yet.");
+    ClassSet classSet = classSets[y];
+    assert(classSet != null,
+        failedAt(y, "No ClassSet for $y (${y.runtimeType}): ${classSets}"));
+    ClassHierarchyNode classHierarchyNode = classHierarchyNodes[x];
+    assert(classHierarchyNode != null,
+        failedAt(x, "No ClassHierarchyNode for $x"));
+    return classSet.hasSubtype(classHierarchyNode);
+  }
+
+  bool isInheritedInSubtypeOf(ClassEntity x, ClassEntity y) {
+    ClassSet classSet = classSets[x];
+    assert(classSet != null,
+        failedAt(x, "No ClassSet for $x (${x.runtimeType}): ${classSets}"));
+
+    if (_isSubtypeOf(x, y)) {
+      // [x] implements [y] itself, possible through supertypes.
+      return true;
+    }
+
+    /// Returns `true` if any live subclass of [node] implements [y].
+    bool subclassImplements(ClassHierarchyNode node, {bool strict}) {
+      return node.anySubclass((ClassEntity z) => _isSubtypeOf(z, y),
+          ClassHierarchyNode.INSTANTIATED,
+          strict: strict);
+    }
+
+    if (subclassImplements(classSet.node, strict: true)) {
+      // A subclass of [x] implements [y].
+      return true;
+    }
+
+    for (ClassHierarchyNode mixinApplication
+        in classSet.mixinApplicationNodes) {
+      if (subclassImplements(mixinApplication, strict: false)) {
+        // A subclass of [mixinApplication] implements [y].
+        return true;
+      }
+    }
+    return false;
+  }
+}
+
+abstract class ClassQueries {
+  bool checkClass(covariant ClassEntity cls);
+  bool validateClass(covariant ClassEntity cls);
+
+  /// Returns the declaration of [cls].
+  ClassEntity getDeclaration(covariant ClassEntity cls);
+
+  /// Returns the class mixed into [cls] if any.
+  // TODO(johnniwinther): Replace this by a `getAppliedMixins` function that
+  // return transitively mixed in classes like in:
+  //     class A {}
+  //     class B = Object with A;
+  //     class C = Object with B;
+  ClassEntity getAppliedMixin(covariant ClassEntity cls);
+
+  /// Returns the hierarchy depth of [cls].
+  int getHierarchyDepth(covariant ClassEntity cls);
+
+  /// Returns `true` if [cls] implements `Function` either explicitly or through
+  /// a `call` method.
+  bool implementsFunction(covariant ClassEntity cls);
+
+  /// Returns the superclass of [cls] if any.
+  ClassEntity getSuperClass(covariant ClassEntity cls);
+
+  /// Returns all supertypes of [cls].
+  Iterable<InterfaceType> getSupertypes(covariant ClassEntity cls);
+}
diff --git a/pkg/compiler/lib/src/universe/world_builder.dart b/pkg/compiler/lib/src/universe/world_builder.dart
index 2417fd3..c1bf88d 100644
--- a/pkg/compiler/lib/src/universe/world_builder.dart
+++ b/pkg/compiler/lib/src/universe/world_builder.dart
@@ -28,7 +28,7 @@
 import '../util/enumset.dart';
 import '../util/util.dart';
 import '../world.dart' show World, JClosedWorld, KClosedWorld, OpenWorld;
-import 'class_hierarchy.dart' show ClassHierarchyBuilder, ClassQueries;
+import 'class_hierarchy_builder.dart' show ClassHierarchyBuilder, ClassQueries;
 import 'selector.dart' show Selector;
 import 'use.dart'
     show
diff --git a/pkg/compiler/lib/src/world.dart b/pkg/compiler/lib/src/world.dart
index d440deb..2a33f49 100644
--- a/pkg/compiler/lib/src/world.dart
+++ b/pkg/compiler/lib/src/world.dart
@@ -23,7 +23,6 @@
 import 'ordered_typeset.dart';
 import 'options.dart';
 import 'types/abstract_value_domain.dart';
-import 'universe/class_hierarchy.dart';
 import 'universe/class_set.dart';
 import 'universe/function_set.dart' show FunctionSet;
 import 'universe/selector.dart' show Selector;
@@ -69,8 +68,6 @@
 
   Iterable<MemberEntity> get processedMembers;
 
-  ClassHierarchy get classHierarchy;
-
   /// Returns `true` if [cls] is either directly or indirectly instantiated.
   bool isInstantiated(ClassEntity cls);
 
@@ -175,6 +172,27 @@
   /// Returns an iterable over the common supertypes of the [classes].
   Iterable<ClassEntity> commonSupertypesOf(Iterable<ClassEntity> classes);
 
+  /// Returns an iterable of the classes that are contained in the
+  /// strict subclass/subtype sets of both [cls1] and [cls2].
+  ///
+  /// Classes that are implied by included superclasses/supertypes are not
+  /// returned.
+  ///
+  /// For instance for this hierarchy
+  ///
+  ///     class A {}
+  ///     class B {}
+  ///     class C implements A, B {}
+  ///     class D extends C {}
+  ///
+  /// the query
+  ///
+  ///     commonSubclasses(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE)
+  ///
+  /// return the set {C} because [D] is implied by [C].
+  Iterable<ClassEntity> commonSubclasses(
+      ClassEntity cls1, ClassQuery query1, ClassEntity cls2, ClassQuery query2);
+
   /// Returns an iterable over the live mixin applications that mixin [cls].
   Iterable<ClassEntity> mixinUsesOf(ClassEntity cls);
 
@@ -337,6 +355,19 @@
   bool isInheritedInSubtypeOf(MemberEntity member, ClassEntity type);
 }
 
+/// Enum values defining subset of classes included in queries.
+enum ClassQuery {
+  /// Only the class itself is included.
+  EXACT,
+
+  /// The class and all subclasses (transitively) are included.
+  SUBCLASS,
+
+  /// The class and all classes that implement or subclass it (transitively)
+  /// are included.
+  SUBTYPE,
+}
+
 abstract class ClosedWorldBase implements JClosedWorld {
   final ConstantSystem constantSystem;
   final NativeData nativeData;
@@ -373,8 +404,6 @@
 
   final Iterable<MemberEntity> processedMembers;
 
-  final ClassHierarchy classHierarchy;
-
   ClosedWorldBase(
       this.elementEnvironment,
       this.dartTypes,
@@ -396,9 +425,7 @@
       AbstractValueStrategy abstractValueStrategy)
       : this._implementedClasses = implementedClasses,
         this._classHierarchyNodes = classHierarchyNodes,
-        this._classSets = classSets,
-        classHierarchy = new ClassHierarchyImpl(
-            commonElements, classHierarchyNodes, classSets) {}
+        this._classSets = classSets {}
 
   bool checkEntity(covariant Entity element);
 
@@ -638,6 +665,27 @@
     return classSet != null ? classSet.getLubOfInstantiatedSubtypes() : null;
   }
 
+  Set<ClassEntity> _commonContainedClasses(ClassEntity cls1, ClassQuery query1,
+      ClassEntity cls2, ClassQuery query2) {
+    Iterable<ClassEntity> xSubset = _containedSubset(cls1, query1);
+    if (xSubset == null) return null;
+    Iterable<ClassEntity> ySubset = _containedSubset(cls2, query2);
+    if (ySubset == null) return null;
+    return xSubset.toSet().intersection(ySubset.toSet());
+  }
+
+  Iterable<ClassEntity> _containedSubset(ClassEntity cls, ClassQuery query) {
+    switch (query) {
+      case ClassQuery.EXACT:
+        return null;
+      case ClassQuery.SUBCLASS:
+        return strictSubclassesOf(cls);
+      case ClassQuery.SUBTYPE:
+        return strictSubtypesOf(cls);
+    }
+    throw new ArgumentError('Unexpected query: $query.');
+  }
+
   /// Returns `true` if [cls] is mixed into a live class.
   bool isUsedAsMixin(ClassEntity cls) {
     return !mixinUsesOf(cls).isEmpty;
@@ -761,6 +809,38 @@
     return commonSupertypes;
   }
 
+  Iterable<ClassEntity> commonSubclasses(ClassEntity cls1, ClassQuery query1,
+      ClassEntity cls2, ClassQuery query2) {
+    // TODO(johnniwinther): Use [ClassSet] to compute this.
+    // Compute the set of classes that are contained in both class subsets.
+    Set<ClassEntity> common =
+        _commonContainedClasses(cls1, query1, cls2, query2);
+    if (common == null || common.isEmpty) return const <ClassEntity>[];
+    // Narrow down the candidates by only looking at common classes
+    // that do not have a superclass or supertype that will be a
+    // better candidate.
+    return common.where((ClassEntity each) {
+      bool containsSuperclass = common.contains(getSuperClass(each));
+      // If the superclass is also a candidate, then we don't want to
+      // deal with this class. If we're only looking for a subclass we
+      // know we don't have to look at the list of interfaces because
+      // they can never be in the common set.
+      if (containsSuperclass ||
+          query1 == ClassQuery.SUBCLASS ||
+          query2 == ClassQuery.SUBCLASS) {
+        return !containsSuperclass;
+      }
+      // Run through the direct supertypes of the class. If the common
+      // set contains the direct supertype of the class, we ignore the
+      // the class because the supertype is a better candidate.
+
+      for (ClassEntity interface in getInterfaces(each)) {
+        if (common.contains(interface)) return false;
+      }
+      return true;
+    });
+  }
+
   /// Returns an iterable over the live mixin applications that mixin [cls].
   Iterable<ClassEntity> mixinUsesOf(ClassEntity cls) {
     if (_liveMixinUses == null) {
@@ -957,11 +1037,23 @@
   InterceptorData get interceptorData;
   ElementEnvironment get elementEnvironment;
   CommonElements get commonElements;
-  ClassHierarchy get classHierarchy;
 
   /// Returns `true` if [cls] is implemented by an instantiated class.
   bool isImplemented(ClassEntity cls);
 
+  /// Returns [ClassHierarchyNode] for [cls] used to model the class hierarchies
+  /// of known classes.
+  ///
+  /// This method is only provided for testing. For queries on classes, use the
+  /// methods defined in [JClosedWorld].
+  ClassHierarchyNode getClassHierarchyNode(ClassEntity cls);
+
+  /// Returns [ClassSet] for [cls] used to model the extends and implements
+  /// relations of known classes.
+  ///
+  /// This method is only provided for testing. For queries on classes, use the
+  /// methods defined in [JClosedWorld].
+  ClassSet getClassSet(ClassEntity cls);
   Iterable<MemberEntity> get liveInstanceMembers;
   Map<ClassEntity, Set<ClassEntity>> get mixinUses;
   Map<ClassEntity, Set<ClassEntity>> get typesImplementedBySubclasses;
@@ -973,4 +1065,42 @@
   Iterable<MemberEntity> get processedMembers;
   RuntimeTypesNeed get rtiNeed;
   NoSuchMethodData get noSuchMethodData;
+
+  /// Returns `true` if [x] is a subtype of [y], that is, if [x] implements an
+  /// instance of [y].
+  bool isSubtypeOf(ClassEntity x, ClassEntity y);
+
+  /// Returns an iterable of the classes that are contained in the
+  /// strict subclass/subtype sets of both [cls1] and [cls2].
+  ///
+  /// Classes that are implied by included superclasses/supertypes are not
+  /// returned.
+  ///
+  /// For instance for this hierarchy
+  ///
+  ///     class A {}
+  ///     class B {}
+  ///     class C implements A, B {}
+  ///     class D extends C {}
+  ///
+  /// the query
+  ///
+  ///     commonSubclasses(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE)
+  ///
+  /// return the set {C} because [D] is implied by [C].
+  Iterable<ClassEntity> commonSubclasses(
+      ClassEntity cls1, ClassQuery query1, ClassEntity cls2, ClassQuery query2);
+
+  /// Returns an iterable over the directly instantiated that implement [cls]
+  /// possibly including [cls] itself, if it is live.
+  Iterable<ClassEntity> subtypesOf(ClassEntity cls);
+
+  /// Returns an iterable over the live classes that extend [cls] including
+  /// [cls] itself.
+  Iterable<ClassEntity> subclassesOf(ClassEntity cls);
+
+  /// Applies [f] to each live class that implements [cls] _not_ including [cls]
+  /// itself.
+  void forEachStrictSubtypeOf(
+      ClassEntity cls, IterationStep f(ClassEntity cls));
 }
diff --git a/tests/compiler/dart2js/jsinterop/world_test.dart b/tests/compiler/dart2js/jsinterop/world_test.dart
index 25377d5..cb9cf2c 100644
--- a/tests/compiler/dart2js/jsinterop/world_test.dart
+++ b/tests/compiler/dart2js/jsinterop/world_test.dart
@@ -11,7 +11,6 @@
 import 'package:compiler/src/compiler.dart';
 import 'package:compiler/src/elements/entities.dart' show ClassEntity;
 import 'package:compiler/src/elements/names.dart';
-import 'package:compiler/src/universe/class_hierarchy.dart';
 import 'package:compiler/src/universe/selector.dart';
 import 'package:compiler/src/world.dart';
 import '../helpers/element_lookup.dart';
diff --git a/tests/compiler/dart2js/model/class_set_test.dart b/tests/compiler/dart2js/model/class_set_test.dart
index 833334f..f91acf6 100644
--- a/tests/compiler/dart2js/model/class_set_test.dart
+++ b/tests/compiler/dart2js/model/class_set_test.dart
@@ -68,7 +68,7 @@
 
   void checkClass(ClassEntity cls,
       {bool directlyInstantiated: false, bool indirectlyInstantiated: false}) {
-    ClassHierarchyNode node = world.classHierarchy.getClassHierarchyNode(cls);
+    ClassHierarchyNode node = world.getClassHierarchyNode(cls);
     Expect.isNotNull(node, "Expected ClassHierarchyNode for $cls.");
     Expect.equals(
         directlyInstantiated || indirectlyInstantiated,
@@ -139,7 +139,7 @@
   }
 
   iterator = new ClassHierarchyNodeIterable(
-          world.classHierarchy.getClassHierarchyNode(G), ClassHierarchyNode.ALL)
+          world.getClassHierarchyNode(G), ClassHierarchyNode.ALL)
       .iterator;
   checkState(G, currentNode: null, stack: null);
   Expect.isNull(iterator.current);
@@ -151,7 +151,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-          world.classHierarchy.getClassHierarchyNode(G), ClassHierarchyNode.ALL,
+          world.getClassHierarchyNode(G), ClassHierarchyNode.ALL,
           includeRoot: false)
       .iterator;
   checkState(G, currentNode: null, stack: null);
@@ -161,7 +161,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-          world.classHierarchy.getClassHierarchyNode(C), ClassHierarchyNode.ALL)
+          world.getClassHierarchyNode(C), ClassHierarchyNode.ALL)
       .iterator;
   checkState(C, currentNode: null, stack: null);
   Expect.isNull(iterator.current);
@@ -182,7 +182,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-          world.classHierarchy.getClassHierarchyNode(D), ClassHierarchyNode.ALL)
+          world.getClassHierarchyNode(D), ClassHierarchyNode.ALL)
       .iterator;
   checkState(D, currentNode: null, stack: null);
   Expect.isNull(iterator.current);
@@ -194,7 +194,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-          world.classHierarchy.getClassHierarchyNode(B), ClassHierarchyNode.ALL)
+          world.getClassHierarchyNode(B), ClassHierarchyNode.ALL)
       .iterator;
   checkState(B, currentNode: null, stack: null);
   Expect.isNull(iterator.current);
@@ -209,7 +209,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-          world.classHierarchy.getClassHierarchyNode(B), ClassHierarchyNode.ALL,
+          world.getClassHierarchyNode(B), ClassHierarchyNode.ALL,
           includeRoot: false)
       .iterator;
   checkState(B, currentNode: null, stack: null);
@@ -222,7 +222,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-      world.classHierarchy.getClassHierarchyNode(B),
+      world.getClassHierarchyNode(B),
       new EnumSet<Instantiation>.fromValues(<Instantiation>[
         Instantiation.DIRECTLY_INSTANTIATED,
         Instantiation.UNINSTANTIATED
@@ -237,7 +237,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-          world.classHierarchy.getClassHierarchyNode(A), ClassHierarchyNode.ALL)
+          world.getClassHierarchyNode(A), ClassHierarchyNode.ALL)
       .iterator;
   checkState(A, currentNode: null, stack: null);
   Expect.isNull(iterator.current);
@@ -267,7 +267,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-          world.classHierarchy.getClassHierarchyNode(A), ClassHierarchyNode.ALL,
+          world.getClassHierarchyNode(A), ClassHierarchyNode.ALL,
           includeRoot: false)
       .iterator;
   checkState(A, currentNode: null, stack: null);
@@ -295,7 +295,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-      world.classHierarchy.getClassHierarchyNode(A),
+      world.getClassHierarchyNode(A),
       new EnumSet<Instantiation>.fromValues(<Instantiation>[
         Instantiation.DIRECTLY_INSTANTIATED,
         Instantiation.UNINSTANTIATED
@@ -325,7 +325,7 @@
   Expect.isNull(iterator.current);
 
   iterator = new ClassHierarchyNodeIterable(
-          world.classHierarchy.getClassHierarchyNode(A),
+          world.getClassHierarchyNode(A),
           new EnumSet<Instantiation>.fromValues(<Instantiation>[
             Instantiation.DIRECTLY_INSTANTIATED,
             Instantiation.UNINSTANTIATED
@@ -400,7 +400,7 @@
   ClassEntity X = env.getClass("X");
 
   void checkForEachSubclass(ClassEntity cls, List<ClassEntity> expected) {
-    ClassSet classSet = world.classHierarchy.getClassSet(cls);
+    ClassSet classSet = world.getClassSet(cls);
     List<ClassEntity> visited = <ClassEntity>[];
     classSet.forEachSubclass((cls) {
       visited.add(cls);
@@ -437,7 +437,7 @@
   checkForEachSubclass(X, [X]);
 
   void checkForEachSubtype(ClassEntity cls, List<ClassEntity> expected) {
-    ClassSet classSet = world.classHierarchy.getClassSet(cls);
+    ClassSet classSet = world.getClassSet(cls);
     List<ClassEntity> visited = <ClassEntity>[];
     classSet.forEachSubtype((cls) {
       visited.add(cls);
@@ -482,7 +482,7 @@
       mask = ClassHierarchyNode.ALL;
     }
 
-    ClassSet classSet = world.classHierarchy.getClassSet(cls);
+    ClassSet classSet = world.getClassSet(cls);
     List<ClassEntity> visited = <ClassEntity>[];
 
     IterationStep visit(_cls) {
@@ -539,7 +539,7 @@
 
   void checkAny(ClassEntity cls, List<ClassEntity> expected,
       {ClassEntity find, bool expectedResult, bool anySubtype: false}) {
-    ClassSet classSet = world.classHierarchy.getClassSet(cls);
+    ClassSet classSet = world.getClassSet(cls);
     List<ClassEntity> visited = <ClassEntity>[];
 
     bool visit(cls) {
diff --git a/tests/compiler/dart2js/model/subtypeset_test.dart b/tests/compiler/dart2js/model/subtypeset_test.dart
index fac2f27..fd5e963 100644
--- a/tests/compiler/dart2js/model/subtypeset_test.dart
+++ b/tests/compiler/dart2js/model/subtypeset_test.dart
@@ -67,7 +67,7 @@
 
   void checkClass(ClassEntity cls, List<ClassEntity> expectedSubtypes,
       {bool checkSubset: false}) {
-    ClassSet node = world.classHierarchy.getClassSet(cls);
+    ClassSet node = world.getClassSet(cls);
     Set<ClassEntity> actualSubtypes = node.subtypes().toSet();
     if (checkSubset) {
       for (ClassEntity subtype in expectedSubtypes) {
diff --git a/tests/compiler/dart2js/model/world_test.dart b/tests/compiler/dart2js/model/world_test.dart
index 78e93f1..3abdd5d 100644
--- a/tests/compiler/dart2js/model/world_test.dart
+++ b/tests/compiler/dart2js/model/world_test.dart
@@ -9,9 +9,8 @@
 import 'package:compiler/src/common/names.dart';
 import 'package:compiler/src/common_elements.dart';
 import 'package:compiler/src/elements/entities.dart';
-import 'package:compiler/src/universe/class_hierarchy.dart';
 import 'package:compiler/src/universe/class_set.dart';
-import 'package:compiler/src/world.dart' show JClosedWorld;
+import 'package:compiler/src/world.dart' show ClassQuery, JClosedWorld;
 import '../type_test_helper.dart';
 
 void main() {
@@ -551,106 +550,65 @@
   ClassEntity A = env.getElement("A");
   ClassEntity B = env.getElement("B");
   ClassEntity C = env.getElement("C");
+  ClassEntity D = env.getElement("D");
   ClassEntity F = env.getElement("F");
   ClassEntity G = env.getElement("G");
+  ClassEntity H = env.getElement("H");
   ClassEntity I = env.getElement("I");
   ClassEntity J = env.getElement("J");
 
   void check(ClassEntity cls1, ClassQuery query1, ClassEntity cls2,
-      ClassQuery query2, SubclassResult expectedResult) {
-    SubclassResult result1 =
-        closedWorld.classHierarchy.commonSubclasses(cls1, query1, cls2, query2);
-    SubclassResult result2 =
-        closedWorld.classHierarchy.commonSubclasses(cls2, query2, cls1, query1);
-    Expect.equals(
-        result1.query,
-        result2.query,
+      ClassQuery query2, List<ClassEntity> expectedResult) {
+    Iterable<ClassEntity> result1 =
+        closedWorld.commonSubclasses(cls1, query1, cls2, query2);
+    Iterable<ClassEntity> result2 =
+        closedWorld.commonSubclasses(cls2, query2, cls1, query1);
+    Expect.setEquals(
+        result1,
+        result2,
         "Asymmetric results for ($cls1,$query1) vs ($cls2,$query2):"
         "\n a vs b: $result1\n b vs a: $result2");
     Expect.setEquals(
-        result1.classes,
-        result2.classes,
-        "Asymmetric results for ($cls1,$query1) vs ($cls2,$query2):"
-        "\n a vs b: $result1\n b vs a: $result2");
-    Expect.equals(
-        expectedResult.query,
-        result1.query,
-        "Unexpected results for ($cls1,$query1) vs ($cls2,$query2):"
-        "\n expected: $expectedResult\n actual: $result1");
-    Expect.setEquals(
-        expectedResult.classes,
-        result1.classes,
+        expectedResult,
+        result1,
         "Unexpected results for ($cls1,$query1) vs ($cls2,$query2):"
         "\n expected: $expectedResult\n actual: $result1");
   }
 
-  check(A, ClassQuery.EXACT, A, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, [A]));
-  check(A, ClassQuery.EXACT, A, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.EXACT, [A]));
-  check(A, ClassQuery.EXACT, A, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.EXACT, [A]));
-  check(A, ClassQuery.SUBCLASS, A, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [A]));
-  check(A, ClassQuery.SUBCLASS, A, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [A]));
-  check(A, ClassQuery.SUBTYPE, A, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.SUBTYPE, [A]));
+  check(A, ClassQuery.EXACT, A, ClassQuery.EXACT, []);
+  check(A, ClassQuery.EXACT, A, ClassQuery.SUBCLASS, []);
+  check(A, ClassQuery.EXACT, A, ClassQuery.SUBTYPE, []);
+  check(A, ClassQuery.SUBCLASS, A, ClassQuery.SUBCLASS, [C]);
+  check(A, ClassQuery.SUBCLASS, A, ClassQuery.SUBTYPE, [C]);
+  check(A, ClassQuery.SUBTYPE, A, ClassQuery.SUBTYPE, [C, D]);
 
-  check(A, ClassQuery.EXACT, B, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(A, ClassQuery.EXACT, B, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(A, ClassQuery.SUBCLASS, B, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(A, ClassQuery.EXACT, B, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(A, ClassQuery.SUBTYPE, B, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(A, ClassQuery.SUBCLASS, B, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(A, ClassQuery.SUBCLASS, B, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [G]));
-  check(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [J]));
-  check(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [F, G, I, J]));
+  check(A, ClassQuery.EXACT, B, ClassQuery.EXACT, []);
+  check(A, ClassQuery.EXACT, B, ClassQuery.SUBCLASS, []);
+  check(A, ClassQuery.SUBCLASS, B, ClassQuery.EXACT, []);
+  check(A, ClassQuery.EXACT, B, ClassQuery.SUBTYPE, []);
+  check(A, ClassQuery.SUBTYPE, B, ClassQuery.EXACT, []);
+  check(A, ClassQuery.SUBCLASS, B, ClassQuery.SUBCLASS, []);
+  check(A, ClassQuery.SUBCLASS, B, ClassQuery.SUBTYPE, [G]);
+  check(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBCLASS, [J]);
+  check(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE, [F, G, I, J]);
 
-  check(A, ClassQuery.EXACT, C, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(A, ClassQuery.EXACT, C, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(A, ClassQuery.SUBCLASS, C, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, [C]));
-  check(A, ClassQuery.EXACT, C, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(A, ClassQuery.SUBTYPE, C, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, [C]));
-  check(A, ClassQuery.SUBCLASS, C, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [C]));
-  check(A, ClassQuery.SUBCLASS, C, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [C]));
-  check(A, ClassQuery.SUBTYPE, C, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [C]));
-  check(A, ClassQuery.SUBTYPE, C, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.SUBTYPE, [C]));
+  check(A, ClassQuery.EXACT, C, ClassQuery.EXACT, []);
+  check(A, ClassQuery.EXACT, C, ClassQuery.SUBCLASS, []);
+  check(A, ClassQuery.SUBCLASS, C, ClassQuery.EXACT, []);
+  check(A, ClassQuery.EXACT, C, ClassQuery.SUBTYPE, []);
+  check(A, ClassQuery.SUBTYPE, C, ClassQuery.EXACT, []);
+  check(A, ClassQuery.SUBCLASS, C, ClassQuery.SUBCLASS, [G]);
+  check(A, ClassQuery.SUBCLASS, C, ClassQuery.SUBTYPE, [G]);
+  check(A, ClassQuery.SUBTYPE, C, ClassQuery.SUBCLASS, [G]);
+  check(A, ClassQuery.SUBTYPE, C, ClassQuery.SUBTYPE, [F, G, H]);
 
-  check(B, ClassQuery.EXACT, C, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(B, ClassQuery.EXACT, C, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(B, ClassQuery.SUBCLASS, C, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(B, ClassQuery.EXACT, C, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(B, ClassQuery.SUBTYPE, C, ClassQuery.EXACT,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(B, ClassQuery.SUBCLASS, C, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.EXACT, []));
-  check(B, ClassQuery.SUBCLASS, C, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, []));
-  check(B, ClassQuery.SUBTYPE, C, ClassQuery.SUBCLASS,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [G]));
-  check(B, ClassQuery.SUBTYPE, C, ClassQuery.SUBTYPE,
-      new SubclassResult.internal(ClassQuery.SUBCLASS, [F, G]));
+  check(B, ClassQuery.EXACT, C, ClassQuery.EXACT, []);
+  check(B, ClassQuery.EXACT, C, ClassQuery.SUBCLASS, []);
+  check(B, ClassQuery.SUBCLASS, C, ClassQuery.EXACT, []);
+  check(B, ClassQuery.EXACT, C, ClassQuery.SUBTYPE, []);
+  check(B, ClassQuery.SUBTYPE, C, ClassQuery.EXACT, []);
+  check(B, ClassQuery.SUBCLASS, C, ClassQuery.SUBCLASS, []);
+  check(B, ClassQuery.SUBCLASS, C, ClassQuery.SUBTYPE, []);
+  check(B, ClassQuery.SUBTYPE, C, ClassQuery.SUBCLASS, [G]);
+  check(B, ClassQuery.SUBTYPE, C, ClassQuery.SUBTYPE, [F, G]);
 }
diff --git a/tests/compiler/dart2js/needs_no_such_method_test.dart b/tests/compiler/dart2js/needs_no_such_method_test.dart
index 735e390..5cd35d2 100644
--- a/tests/compiler/dart2js/needs_no_such_method_test.dart
+++ b/tests/compiler/dart2js/needs_no_such_method_test.dart
@@ -9,9 +9,8 @@
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/elements/names.dart';
 import 'package:compiler/src/universe/call_structure.dart';
-import 'package:compiler/src/universe/class_hierarchy.dart';
 import 'package:compiler/src/universe/selector.dart';
-import 'package:compiler/src/world.dart' show JClosedWorld;
+import 'package:compiler/src/world.dart' show JClosedWorld, ClassQuery;
 import 'type_test_helper.dart';
 
 void main() {