Version 2.14.0-248.0.dev

Merge commit '83376bf1ee81607a182f8558b242f1c9f4883246' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5119c30..f32243e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,10 @@
     daylight saving changes that are not precisely one hour.
     (No change on the Web which uses the JavaScript `Date` object.)
 
+*   Adds static methods `hash`, `hashAll` and `hashAllUnordered` to the
+    `Object` class. These can be used to combine the hash codes of
+    multiple objects in a consistent way.
+
 #### `dart:ffi`
 
 *   Adds the `DynamicLibrary.providesSymbol` function to check whether a symbol
diff --git a/sdk/lib/core/object.dart b/sdk/lib/core/object.dart
index 32b52bf..8d02d06 100644
--- a/sdk/lib/core/object.dart
+++ b/sdk/lib/core/object.dart
@@ -146,4 +146,418 @@
 
   /// A representation of the runtime type of the object.
   external Type get runtimeType;
+
+  /// Creates a combined hash code for a number of objects.
+  ///
+  /// The hash code is computed for all arguments that are actually
+  /// supplied, even if they are `null`, by numerically combining the
+  /// [Object.hashCode] of each argument.
+  ///
+  /// Example:
+  /// ```dart
+  /// class SomeObject {
+  ///   final Object a, b, c;
+  ///   SomeObject(this.a, this.b, this.c);
+  ///   bool operator=(Object other) =>
+  ///       other is SomeObject && a == other.a && b == other.b && c == other.c;
+  ///   int get hashCode => Object.hash(a, b, c);
+  /// }
+  /// ```
+  ///
+  /// The computed value will be consistent when the function is called
+  /// with the same arguments multiple times
+  /// during the execution of a single program.
+  ///
+  /// The hash value generated by this function is *not* guaranteed to be stable
+  /// over different runs of the same program,
+  /// or between code run in different isolates of the same program.
+  /// The exact algorithm used may differ between different platforms,
+  /// or between different versions of the platform libraries,
+  /// and it may depend on values that change on each program execution.
+  ///
+  /// The [hashAll] function gives the same result as this function when
+  /// called with a collection containing the actual arguments
+  /// to this function in the same order.
+  @Since("2.14")
+  static int hash(Object? object1, Object? object2,
+      [Object? object3 = sentinelValue,
+      Object? object4 = sentinelValue,
+      Object? object5 = sentinelValue,
+      Object? object6 = sentinelValue,
+      Object? object7 = sentinelValue,
+      Object? object8 = sentinelValue,
+      Object? object9 = sentinelValue,
+      Object? object10 = sentinelValue,
+      Object? object11 = sentinelValue,
+      Object? object12 = sentinelValue,
+      Object? object13 = sentinelValue,
+      Object? object14 = sentinelValue,
+      Object? object15 = sentinelValue,
+      Object? object16 = sentinelValue,
+      Object? object17 = sentinelValue,
+      Object? object18 = sentinelValue,
+      Object? object19 = sentinelValue,
+      Object? object20 = sentinelValue]) {
+    if (sentinelValue == object3) {
+      return SystemHash.hash2(object1.hashCode, object2.hashCode, _hashSeed);
+    }
+    if (sentinelValue == object4) {
+      return SystemHash.hash3(
+          object1.hashCode, object2.hashCode, object3.hashCode, _hashSeed);
+    }
+    if (sentinelValue == object5) {
+      return SystemHash.hash4(object1.hashCode, object2.hashCode,
+          object3.hashCode, object4.hashCode, _hashSeed);
+    }
+    if (sentinelValue == object6) {
+      return SystemHash.hash5(object1.hashCode, object2.hashCode,
+          object3.hashCode, object4.hashCode, object5.hashCode, _hashSeed);
+    }
+    if (sentinelValue == object7) {
+      return SystemHash.hash6(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object8) {
+      return SystemHash.hash7(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object9) {
+      return SystemHash.hash8(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object10) {
+      return SystemHash.hash9(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object11) {
+      return SystemHash.hash10(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object12) {
+      return SystemHash.hash11(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          object11.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object13) {
+      return SystemHash.hash12(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          object11.hashCode,
+          object12.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object14) {
+      return SystemHash.hash13(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          object11.hashCode,
+          object12.hashCode,
+          object13.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object15) {
+      return SystemHash.hash14(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          object11.hashCode,
+          object12.hashCode,
+          object13.hashCode,
+          object14.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object16) {
+      return SystemHash.hash15(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          object11.hashCode,
+          object12.hashCode,
+          object13.hashCode,
+          object14.hashCode,
+          object15.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object17) {
+      return SystemHash.hash16(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          object11.hashCode,
+          object12.hashCode,
+          object13.hashCode,
+          object14.hashCode,
+          object15.hashCode,
+          object16.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object18) {
+      return SystemHash.hash17(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          object11.hashCode,
+          object12.hashCode,
+          object13.hashCode,
+          object14.hashCode,
+          object15.hashCode,
+          object16.hashCode,
+          object17.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object19) {
+      return SystemHash.hash18(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          object11.hashCode,
+          object12.hashCode,
+          object13.hashCode,
+          object14.hashCode,
+          object15.hashCode,
+          object16.hashCode,
+          object17.hashCode,
+          object18.hashCode,
+          _hashSeed);
+    }
+    if (sentinelValue == object20) {
+      return SystemHash.hash19(
+          object1.hashCode,
+          object2.hashCode,
+          object3.hashCode,
+          object4.hashCode,
+          object5.hashCode,
+          object6.hashCode,
+          object7.hashCode,
+          object8.hashCode,
+          object9.hashCode,
+          object10.hashCode,
+          object11.hashCode,
+          object12.hashCode,
+          object13.hashCode,
+          object14.hashCode,
+          object15.hashCode,
+          object16.hashCode,
+          object17.hashCode,
+          object18.hashCode,
+          object19.hashCode,
+          _hashSeed);
+    }
+    return SystemHash.hash20(
+        object1.hashCode,
+        object2.hashCode,
+        object3.hashCode,
+        object4.hashCode,
+        object5.hashCode,
+        object6.hashCode,
+        object7.hashCode,
+        object8.hashCode,
+        object9.hashCode,
+        object10.hashCode,
+        object11.hashCode,
+        object12.hashCode,
+        object13.hashCode,
+        object14.hashCode,
+        object15.hashCode,
+        object16.hashCode,
+        object17.hashCode,
+        object18.hashCode,
+        object19.hashCode,
+        object20.hashCode,
+        _hashSeed);
+  }
+
+  /// Creates a combined hash code for a sequence of objects.
+  ///
+  /// The hash code is computed for elements in [objects],
+  /// even if they are `null`,
+  /// by numerically combining the [Object.hashCode] of each element
+  /// in iteration order.
+  ///
+  /// The result of `hashAll([o])` is not `o.hashCode`.
+  ///
+  /// Example:
+  /// ```dart
+  /// class SomeObject {
+  ///   final List<String> path;
+  ///   SomeObject(this.path);
+  ///   bool operator=(Object other) {
+  ///     if (other is SomeObject) {
+  ///       if (path.length != other.path.length) return false;
+  ///       for (int i = 0; i < path.length; i++) {
+  ///         if (path[i] != other.path[i]) return false;
+  ///       }
+  ///       return true;
+  ///     }
+  ///     return false;
+  ///   }
+  ///
+  ///   int get hashCode => Object.hashAll(path);
+  /// }
+  /// ```
+  ///
+  /// The computed value will be be consistent when the function is called
+  /// again with objects that have the same hash codes in the same order
+  /// during an execution of a single program.
+  ///
+  /// The hash value generated by this function is *not* guranteed to be stable
+  /// over different runs of the same program,
+  /// or between code run in different isolates of the same program.
+  /// The exact algorithm used may differ between different platforms,
+  /// or between different versions of the platform libraries,
+  /// and it may depend on values that change on each program execution.
+  @Since("2.14")
+  static int hashAll(Iterable<Object?> objects) {
+    int hash = _hashSeed;
+    for (var object in objects) {
+      hash = SystemHash.combine(hash, object.hashCode);
+    }
+    return SystemHash.finish(hash);
+  }
+
+  /// Creates a combined hash code for a collection of objects.
+  ///
+  /// The hash code is computed for elements in [objects],
+  /// even if they are `null`,
+  /// by numerically combining the [Object.hashCode] of each element
+  /// in an order independent way.
+  ///
+  /// The result of `unorderedHashAll({o})` is not `o.hashCode`.
+  ///
+  /// Example:
+  /// ```dart
+  /// bool setEquals<T>(Set<T> set1, Set<T> set2) {
+  ///   var hashCode1 = Object.unorderedHashAll(set1);
+  ///   var hashCode2 = Object.unorderedHashAll(set2);
+  ///   if (hashCode1 != hashCode2) return false;
+  ///   // Compare elements ...
+  /// }
+  /// ```
+  ///
+  /// The computed value will be be consistent when the function is called
+  /// again with objects that have the same hash codes
+  /// during an execution of a single program,
+  /// even if the objects are not necessarily in the same order,
+  ///
+  /// The hash value generated by this function is *not* guranteed to be stable
+  /// over different runs of the same program.
+  /// The exact algorithm used may differ between different platforms,
+  /// or between different versions of the platform libraries,
+  /// and it may depend on values that change per program run
+  @Since("2.14")
+  static int hashAllUnordered(Iterable<Object?> objects) {
+    int sum = 0;
+    int count = 0;
+    const int mask = 0x3FFFFFFF;
+    for (var object in objects) {
+      int objectHash = SystemHash.smear(object.hashCode);
+      sum = (sum + objectHash) & mask;
+      count += 1;
+    }
+    return SystemHash.hash2(sum, count);
+  }
 }
+
+// A per-isolate seed for hash code computations.
+final int _hashSeed = identityHashCode(Object);
diff --git a/sdk/lib/internal/internal.dart b/sdk/lib/internal/internal.dart
index 858f1f5..0e4c898 100644
--- a/sdk/lib/internal/internal.dart
+++ b/sdk/lib/internal/internal.dart
@@ -148,7 +148,7 @@
 ///
 /// [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function
 ///
-/// Usage:
+/// Use:
 /// Hash each value with the hash of the previous value, then get the final
 /// hash by calling finish.
 /// ```
@@ -158,8 +158,9 @@
 /// }
 /// hash = SystemHash.finish(hash);
 /// ```
-// TODO(lrn): Consider specializing this code per platform,
-// so the VM can use its 64-bit integers directly.
+///
+/// TODO(lrn): Consider specializing this code per platform,
+/// so the VM can use its 64-bit integers directly.
 @Since("2.11")
 class SystemHash {
   static int combine(int hash, int value) {
@@ -174,23 +175,24 @@
     return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
   }
 
-  static int hash2(int v1, int v2) {
-    int hash = 0;
+  static int hash2(int v1, int v2, [@Since("2.14") int seed = 0]) {
+    int hash = seed;
     hash = combine(hash, v1);
     hash = combine(hash, v2);
     return finish(hash);
   }
 
-  static int hash3(int v1, int v2, int v3) {
-    int hash = 0;
+  static int hash3(int v1, int v2, int v3, [@Since("2.14") int seed = 0]) {
+    int hash = seed;
     hash = combine(hash, v1);
     hash = combine(hash, v2);
     hash = combine(hash, v3);
     return finish(hash);
   }
 
-  static int hash4(int v1, int v2, int v3, int v4) {
-    int hash = 0;
+  static int hash4(int v1, int v2, int v3, int v4,
+      [@Since("2.14") int seed = 0]) {
+    int hash = seed;
     hash = combine(hash, v1);
     hash = combine(hash, v2);
     hash = combine(hash, v3);
@@ -198,8 +200,9 @@
     return finish(hash);
   }
 
-  static int hash5(int v1, int v2, int v3, int v4, int v5) {
-    int hash = 0;
+  static int hash5(int v1, int v2, int v3, int v4, int v5,
+      [@Since("2.14") int seed = 0]) {
+    int hash = seed;
     hash = combine(hash, v1);
     hash = combine(hash, v2);
     hash = combine(hash, v3);
@@ -208,8 +211,9 @@
     return finish(hash);
   }
 
-  static int hash6(int v1, int v2, int v3, int v4, int v5, int v6) {
-    int hash = 0;
+  static int hash6(int v1, int v2, int v3, int v4, int v5, int v6,
+      [@Since("2.14") int seed = 0]) {
+    int hash = seed;
     hash = combine(hash, v1);
     hash = combine(hash, v2);
     hash = combine(hash, v3);
@@ -219,8 +223,9 @@
     return finish(hash);
   }
 
-  static int hash7(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {
-    int hash = 0;
+  static int hash7(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
+      [@Since("2.14") int seed = 0]) {
+    int hash = seed;
     hash = combine(hash, v1);
     hash = combine(hash, v2);
     hash = combine(hash, v3);
@@ -232,8 +237,9 @@
   }
 
   static int hash8(
-      int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
-    int hash = 0;
+      int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8,
+      [@Since("2.14") int seed = 0]) {
+    int hash = seed;
     hash = combine(hash, v1);
     hash = combine(hash, v2);
     hash = combine(hash, v3);
@@ -246,8 +252,9 @@
   }
 
   static int hash9(
-      int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9) {
-    int hash = 0;
+      int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9,
+      [@Since("2.14") int seed = 0]) {
+    int hash = seed;
     hash = combine(hash, v1);
     hash = combine(hash, v2);
     hash = combine(hash, v3);
@@ -261,8 +268,9 @@
   }
 
   static int hash10(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
-      int v8, int v9, int v10) {
-    int hash = 0;
+      int v8, int v9, int v10,
+      [@Since("2.14") int seed = 0]) {
+    int hash = seed;
     hash = combine(hash, v1);
     hash = combine(hash, v2);
     hash = combine(hash, v3);
@@ -276,14 +284,334 @@
     return finish(hash);
   }
 
+  @Since("2.14")
+  static int hash11(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
+      int v8, int v9, int v10, int v11,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    return finish(hash);
+  }
+
+  @Since("2.14")
+  static int hash12(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
+      int v8, int v9, int v10, int v11, int v12,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    hash = combine(hash, v12);
+    return finish(hash);
+  }
+
+  @Since("2.14")
+  static int hash13(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
+      int v8, int v9, int v10, int v11, int v12, int v13,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    hash = combine(hash, v12);
+    hash = combine(hash, v13);
+    return finish(hash);
+  }
+
+  @Since("2.14")
+  static int hash14(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
+      int v8, int v9, int v10, int v11, int v12, int v13, int v14,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    hash = combine(hash, v12);
+    hash = combine(hash, v13);
+    hash = combine(hash, v14);
+    return finish(hash);
+  }
+
+  @Since("2.14")
+  static int hash15(int v1, int v2, int v3, int v4, int v5, int v6, int v7,
+      int v8, int v9, int v10, int v11, int v12, int v13, int v14, int v15,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    hash = combine(hash, v12);
+    hash = combine(hash, v13);
+    hash = combine(hash, v14);
+    hash = combine(hash, v15);
+    return finish(hash);
+  }
+
+  @Since("2.14")
+  static int hash16(
+      int v1,
+      int v2,
+      int v3,
+      int v4,
+      int v5,
+      int v6,
+      int v7,
+      int v8,
+      int v9,
+      int v10,
+      int v11,
+      int v12,
+      int v13,
+      int v14,
+      int v15,
+      int v16,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    hash = combine(hash, v12);
+    hash = combine(hash, v13);
+    hash = combine(hash, v14);
+    hash = combine(hash, v15);
+    hash = combine(hash, v16);
+    return finish(hash);
+  }
+
+  @Since("2.14")
+  static int hash17(
+      int v1,
+      int v2,
+      int v3,
+      int v4,
+      int v5,
+      int v6,
+      int v7,
+      int v8,
+      int v9,
+      int v10,
+      int v11,
+      int v12,
+      int v13,
+      int v14,
+      int v15,
+      int v16,
+      int v17,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    hash = combine(hash, v12);
+    hash = combine(hash, v13);
+    hash = combine(hash, v14);
+    hash = combine(hash, v15);
+    hash = combine(hash, v16);
+    hash = combine(hash, v17);
+    return finish(hash);
+  }
+
+  @Since("2.14")
+  static int hash18(
+      int v1,
+      int v2,
+      int v3,
+      int v4,
+      int v5,
+      int v6,
+      int v7,
+      int v8,
+      int v9,
+      int v10,
+      int v11,
+      int v12,
+      int v13,
+      int v14,
+      int v15,
+      int v16,
+      int v17,
+      int v18,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    hash = combine(hash, v12);
+    hash = combine(hash, v13);
+    hash = combine(hash, v14);
+    hash = combine(hash, v15);
+    hash = combine(hash, v16);
+    hash = combine(hash, v17);
+    hash = combine(hash, v18);
+    return finish(hash);
+  }
+
+  @Since("2.14")
+  static int hash19(
+      int v1,
+      int v2,
+      int v3,
+      int v4,
+      int v5,
+      int v6,
+      int v7,
+      int v8,
+      int v9,
+      int v10,
+      int v11,
+      int v12,
+      int v13,
+      int v14,
+      int v15,
+      int v16,
+      int v17,
+      int v18,
+      int v19,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    hash = combine(hash, v12);
+    hash = combine(hash, v13);
+    hash = combine(hash, v14);
+    hash = combine(hash, v15);
+    hash = combine(hash, v16);
+    hash = combine(hash, v17);
+    hash = combine(hash, v18);
+    hash = combine(hash, v19);
+    return finish(hash);
+  }
+
+  @Since("2.14")
+  static int hash20(
+      int v1,
+      int v2,
+      int v3,
+      int v4,
+      int v5,
+      int v6,
+      int v7,
+      int v8,
+      int v9,
+      int v10,
+      int v11,
+      int v12,
+      int v13,
+      int v14,
+      int v15,
+      int v16,
+      int v17,
+      int v18,
+      int v19,
+      int v20,
+      [int seed = 0]) {
+    int hash = seed;
+    hash = combine(hash, v1);
+    hash = combine(hash, v2);
+    hash = combine(hash, v3);
+    hash = combine(hash, v4);
+    hash = combine(hash, v5);
+    hash = combine(hash, v6);
+    hash = combine(hash, v7);
+    hash = combine(hash, v8);
+    hash = combine(hash, v9);
+    hash = combine(hash, v10);
+    hash = combine(hash, v11);
+    hash = combine(hash, v12);
+    hash = combine(hash, v13);
+    hash = combine(hash, v14);
+    hash = combine(hash, v15);
+    hash = combine(hash, v16);
+    hash = combine(hash, v17);
+    hash = combine(hash, v18);
+    hash = combine(hash, v19);
+    hash = combine(hash, v20);
+    return finish(hash);
+  }
+
   /// Bit shuffling operation to improve hash codes.
   ///
   /// Dart integers have very simple hash codes (their value),
   /// which is acceptable for the hash above because it smears the bits
   /// as part of the combination.
-  /// However, for the unordered hash based on xor, we need to improve
-  /// the hash code of, e.g., integers, so a set containing the integers
-  /// from zero to 2^n won't always have a zero hashcode.
+  /// However, for the unordered hash, we need to improve
+  /// the hash code of, e.g., integers, to avoid collections of small integers
+  /// too easily having colliding hash results.
   ///
   /// Assumes the input hash code is an unsigned 32-bit integer.
   /// Found by Christopher Wellons [https://github.com/skeeto/hash-prospector].
@@ -298,6 +626,17 @@
   }
 }
 
+/// Sentinel values that should never be exposed outside of platform libraries.
+@Since("2.14")
+class SentinelValue {
+  final int id;
+  const SentinelValue(this.id);
+}
+
+/// A default value to use when only one sentinel is needed.
+@Since("2.14")
+const Object sentinelValue = const SentinelValue(0);
+
 /// Given an [instance] of some generic type [T], and [extract], a first-class
 /// generic function that takes the same number of type parameters as [T],
 /// invokes the function with the same type arguments that were passed to T
diff --git a/tests/corelib/object_hash_test.dart b/tests/corelib/object_hash_test.dart
new file mode 100644
index 0000000..3e9e5d8
--- /dev/null
+++ b/tests/corelib/object_hash_test.dart
@@ -0,0 +1,134 @@
+// Copyright (c) 2018, 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 "dart:math";
+import "dart:typed_data";
+
+import "package:expect/expect.dart";
+
+main() {
+  const nan = double.nan;
+  const inf = double.infinity;
+
+  int hash1234 = Object.hash(1, 2, 3, 4);
+  Expect.type<int>(hash1234);
+  Expect.equals(hash1234, Object.hash(1, 2, 3, 4)); // Consistent.
+  Expect.equals(hash1234, Object.hashAll([1, 2, 3, 4]));
+  Expect.equals(hash1234, Object.hashAll(Uint8List.fromList([1, 2, 3, 4])));
+
+  Expect.notEquals(hash1234, Object.hash(1, 2, 3, 4, null));
+
+  Expect.equals(Object.hash(1, 2, 3, 4, 5, 6, 7, 8, 9),
+      Object.hashAll([1, 2, 3, 4, 5, 6, 7, 8, 9]));
+
+  // Check that we can call `hash` with 2-20 arguments,
+  // and they all agree with `hashAll`.
+  var random = Random();
+  for (var i = 2; i <= 20; i++) {
+    var arguments = [for (var j = 0; j < i; j++) random.nextInt(256)];
+    var hashAll = Object.hashAll(arguments);
+    var hash = Function.apply(Object.hash, arguments);
+    Expect.equals(
+        hashAll,
+        hash,
+        "hashAll and hash disagrees for $i values:\n"
+        "$arguments");
+  }
+
+  // Works for all kinds of objects;
+  int varHash = Object.hash(
+      "string", 3, nan, true, null, Type, #Symbol, const Object(), function);
+  Expect.equals(
+      varHash,
+      Object.hashAll([
+        "string",
+        3,
+        nan,
+        true,
+        null,
+        Type,
+        #Symbol,
+        const Object(),
+        function
+      ]));
+
+  // Object doesn't matter, just its hash code.
+  Expect.equals(hash1234,
+      Object.hash(Hashable(1), Hashable(2), Hashable(3), Hashable(4)));
+
+  // It's potentially possible to get a conflict, but it doesn't happen here.
+  Expect.notEquals("str".hashCode, Object.hashAll(["str"]));
+
+  var hash12345 = Object.hashAllUnordered([1, 2, 3, 4, 5]);
+  for (var p in permutations([1, 2, 3, 4, 5])) {
+    Expect.equals(hash12345, Object.hashAllUnordered(p));
+  }
+  Expect.notEquals(
+      Object.hashAllUnordered(["a", "a"]), Object.hashAllUnordered(["a"]));
+
+  Expect.notEquals(Object.hashAllUnordered(["a", "a"]),
+      Object.hashAllUnordered(["a", "a", "a", "a"]));
+
+  Expect.notEquals(Object.hashAllUnordered(["a", "b"]),
+      Object.hashAllUnordered(["a", "a", "a", "b"]));
+
+  /// Unordered hashing works for all kinds of objects.
+  var unorderHash = Object.hashAllUnordered([
+    "string",
+    3,
+    nan,
+    true,
+    null,
+    Type,
+    #Symbol,
+    const Object(),
+    function,
+  ]);
+
+  var unorderHash2 = Object.hashAllUnordered([
+    true,
+    const Object(),
+    3,
+    function,
+    Type,
+    "string",
+    null,
+    nan,
+    #Symbol,
+  ]);
+  Expect.equals(unorderHash, unorderHash2);
+}
+
+/// Lazily emits all permutations of [values].
+///
+/// Modifes [values] rather than create a new list.
+/// The [values] list is guaranteed to end up in its original state
+/// after all permutations have been read.
+Iterable<List<T>> permutations<T>(List<T> values) {
+  Iterable<List<T>> recPermute(int end) sync* {
+    if (end == 1) {
+      yield values;
+      return;
+    }
+    for (var i = 0; i < end; i++) {
+      yield* recPermute(end - 1);
+      // Rotate values[i:].
+      var tmp = values.first;
+      for (var k = 1; k < end; k++) values[k - 1] = values[k];
+      values[end - 1] = tmp;
+    }
+  }
+
+  return recPermute(values.length);
+}
+
+// static function, used as constant value.
+void function() {}
+
+class Hashable {
+  final Object o;
+  Hashable(this.o);
+  bool operator ==(Object other) => other is Hashable && o == other.o;
+  int get hashCode => o.hashCode;
+}
diff --git a/tests/corelib_2/object_hash_test.dart b/tests/corelib_2/object_hash_test.dart
new file mode 100644
index 0000000..3e9e5d8
--- /dev/null
+++ b/tests/corelib_2/object_hash_test.dart
@@ -0,0 +1,134 @@
+// Copyright (c) 2018, 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 "dart:math";
+import "dart:typed_data";
+
+import "package:expect/expect.dart";
+
+main() {
+  const nan = double.nan;
+  const inf = double.infinity;
+
+  int hash1234 = Object.hash(1, 2, 3, 4);
+  Expect.type<int>(hash1234);
+  Expect.equals(hash1234, Object.hash(1, 2, 3, 4)); // Consistent.
+  Expect.equals(hash1234, Object.hashAll([1, 2, 3, 4]));
+  Expect.equals(hash1234, Object.hashAll(Uint8List.fromList([1, 2, 3, 4])));
+
+  Expect.notEquals(hash1234, Object.hash(1, 2, 3, 4, null));
+
+  Expect.equals(Object.hash(1, 2, 3, 4, 5, 6, 7, 8, 9),
+      Object.hashAll([1, 2, 3, 4, 5, 6, 7, 8, 9]));
+
+  // Check that we can call `hash` with 2-20 arguments,
+  // and they all agree with `hashAll`.
+  var random = Random();
+  for (var i = 2; i <= 20; i++) {
+    var arguments = [for (var j = 0; j < i; j++) random.nextInt(256)];
+    var hashAll = Object.hashAll(arguments);
+    var hash = Function.apply(Object.hash, arguments);
+    Expect.equals(
+        hashAll,
+        hash,
+        "hashAll and hash disagrees for $i values:\n"
+        "$arguments");
+  }
+
+  // Works for all kinds of objects;
+  int varHash = Object.hash(
+      "string", 3, nan, true, null, Type, #Symbol, const Object(), function);
+  Expect.equals(
+      varHash,
+      Object.hashAll([
+        "string",
+        3,
+        nan,
+        true,
+        null,
+        Type,
+        #Symbol,
+        const Object(),
+        function
+      ]));
+
+  // Object doesn't matter, just its hash code.
+  Expect.equals(hash1234,
+      Object.hash(Hashable(1), Hashable(2), Hashable(3), Hashable(4)));
+
+  // It's potentially possible to get a conflict, but it doesn't happen here.
+  Expect.notEquals("str".hashCode, Object.hashAll(["str"]));
+
+  var hash12345 = Object.hashAllUnordered([1, 2, 3, 4, 5]);
+  for (var p in permutations([1, 2, 3, 4, 5])) {
+    Expect.equals(hash12345, Object.hashAllUnordered(p));
+  }
+  Expect.notEquals(
+      Object.hashAllUnordered(["a", "a"]), Object.hashAllUnordered(["a"]));
+
+  Expect.notEquals(Object.hashAllUnordered(["a", "a"]),
+      Object.hashAllUnordered(["a", "a", "a", "a"]));
+
+  Expect.notEquals(Object.hashAllUnordered(["a", "b"]),
+      Object.hashAllUnordered(["a", "a", "a", "b"]));
+
+  /// Unordered hashing works for all kinds of objects.
+  var unorderHash = Object.hashAllUnordered([
+    "string",
+    3,
+    nan,
+    true,
+    null,
+    Type,
+    #Symbol,
+    const Object(),
+    function,
+  ]);
+
+  var unorderHash2 = Object.hashAllUnordered([
+    true,
+    const Object(),
+    3,
+    function,
+    Type,
+    "string",
+    null,
+    nan,
+    #Symbol,
+  ]);
+  Expect.equals(unorderHash, unorderHash2);
+}
+
+/// Lazily emits all permutations of [values].
+///
+/// Modifes [values] rather than create a new list.
+/// The [values] list is guaranteed to end up in its original state
+/// after all permutations have been read.
+Iterable<List<T>> permutations<T>(List<T> values) {
+  Iterable<List<T>> recPermute(int end) sync* {
+    if (end == 1) {
+      yield values;
+      return;
+    }
+    for (var i = 0; i < end; i++) {
+      yield* recPermute(end - 1);
+      // Rotate values[i:].
+      var tmp = values.first;
+      for (var k = 1; k < end; k++) values[k - 1] = values[k];
+      values[end - 1] = tmp;
+    }
+  }
+
+  return recPermute(values.length);
+}
+
+// static function, used as constant value.
+void function() {}
+
+class Hashable {
+  final Object o;
+  Hashable(this.o);
+  bool operator ==(Object other) => other is Hashable && o == other.o;
+  int get hashCode => o.hashCode;
+}
diff --git a/tests/lib/mirrors/class_declarations_test.dart b/tests/lib/mirrors/class_declarations_test.dart
index 83f8964..2cbf8de 100644
--- a/tests/lib/mirrors/class_declarations_test.dart
+++ b/tests/lib/mirrors/class_declarations_test.dart
@@ -10,11 +10,18 @@
 import 'stringify.dart';
 import 'declarations_model.dart' as declarations_model;
 
-Set<DeclarationMirror> inheritedDeclarations(ClassMirror? cm) {
+/// Collects all declarations of [cm] and its super-classes except `Object`.
+///
+/// Includes static declarations of super-classes.
+///
+/// The `Object` class is omitted because this test should be stable against
+/// changes to the platform libraries, as long as the declaration model code
+/// doesn't change.
+Set<DeclarationMirror> transitiveDeclarations(ClassMirror cm) {
   var decls = new Set<DeclarationMirror>();
-  while (cm != null) {
+  while (cm != reflectClass(Object)) {
     decls.addAll(cm.declarations.values);
-    cm = cm.superclass;
+    cm = cm.superclass!;
   }
   return decls;
 }
@@ -163,14 +170,12 @@
     'Method(s(*) in s(Mixin))',
     'Method(s(+) in s(Class))',
     'Method(s(-) in s(Superclass))',
-    'Method(s(==) in s(Object))',
     'TypeVariable(s(C) in s(Class),'
         ' upperBound = Class(s(Object) in s(dart.core), top-level))',
     'Method(s(Class.generativeConstructor) in s(Class), constructor)',
     'Method(s(Class.normalFactory) in s(Class), static, constructor)',
     'Method(s(Class.redirectingConstructor) in s(Class), constructor)',
     'Method(s(Class.redirectingFactory) in s(Class), static, constructor)',
-    'Method(s(Object) in s(Object), constructor)',
     'TypeVariable(s(S) in s(Superclass),'
         ' upperBound = Class(s(Object) in s(dart.core), top-level))',
     'Method(s(Superclass.inheritedGenerativeConstructor)'
@@ -182,7 +187,6 @@
     'Method(s(Superclass.inheritedRedirectingFactory)'
         ' in s(Superclass), static, constructor)',
     'Method(s(abstractMethod) in s(Class), abstract)',
-    'Method(s(hashCode) in s(Object), getter)',
     'Method(s(inheritedInstanceGetter) in s(Superclass), getter)',
     'Method(s(inheritedInstanceMethod) in s(Superclass))',
     'Method(s(inheritedInstanceSetter=) in s(Superclass), setter)',
@@ -199,8 +203,6 @@
     'Method(s(mixinInstanceMethod) in s(Mixin))',
     'Method(s(mixinInstanceSetter=) in s(Mixin), setter)',
     'Variable(s(mixinInstanceVariable) in s(Mixin))',
-    'Method(s(noSuchMethod) in s(Object))',
-    'Method(s(runtimeType) in s(Object), getter)',
     'Method(s(staticGetter) in s(Class), static, getter)',
     'Method(s(staticMethod) in s(Class), static)',
     'Method(s(staticSetter=) in s(Class), static, setter)',
@@ -213,12 +215,11 @@
         ' with test.declarations_model.Mixin.inheritedRedirectingConstructor)'
         ' in s(test.declarations_model.Superclass'
         ' with test.declarations_model.Mixin), constructor)',
-    'Method(s(toString) in s(Object))',
     'Variable(s(mixinStaticVariable) in s(Mixin), static)',
     'Method(s(mixinStaticGetter) in s(Mixin), static, getter)',
     'Method(s(mixinStaticSetter=) in s(Mixin), static, setter)',
     'Method(s(mixinStaticMethod) in s(Mixin), static)'
-  ], inheritedDeclarations(cm).where((dm) => !dm.isPrivate).map(stringify),
+  ], transitiveDeclarations(cm).where((dm) => !dm.isPrivate).map(stringify),
       'transitive public');
   // The public members of Object should be the same in all implementations, so
   // we don't exclude Object here.
@@ -256,113 +257,106 @@
     'Variable(s(staticVariable) in s(Class), static)'
   ], cm.declarations.values.map(stringify), 'declarations');
 
-  Expect.setEquals(
-      [
-        'Method(s(*) in s(Mixin))',
-        'Method(s(+) in s(Class))',
-        'Method(s(-) in s(Superclass))',
-        'TypeVariable(s(C) in s(Class),'
-            ' upperBound = Class(s(Object) in s(dart.core), top-level))',
-        'Method(s(Class._generativeConstructor) in s(Class), private, constructor)',
-        'Method(s(Class._normalFactory) in s(Class), private, static, constructor)',
-        'Method(s(Class._redirectingConstructor)'
-            ' in s(Class), private, constructor)',
-        'Method(s(Class._redirectingFactory)'
-            ' in s(Class), private, static, constructor)',
-        'Method(s(Class.generativeConstructor) in s(Class), constructor)',
-        'Method(s(Class.normalFactory) in s(Class), static, constructor)',
-        'Method(s(Class.redirectingConstructor) in s(Class), constructor)',
-        'Method(s(Class.redirectingFactory) in s(Class), static, constructor)',
-        'TypeVariable(s(S) in s(Superclass),'
-            ' upperBound = Class(s(Object) in s(dart.core), top-level))',
-        'Method(s(Superclass._inheritedGenerativeConstructor)'
-            ' in s(Superclass), private, constructor)',
-        'Method(s(Superclass._inheritedNormalFactory)'
-            ' in s(Superclass), private, static, constructor)',
-        'Method(s(Superclass._inheritedRedirectingConstructor)'
-            ' in s(Superclass), private, constructor)',
-        'Method(s(Superclass._inheritedRedirectingFactory)'
-            ' in s(Superclass), private, static, constructor)',
-        'Method(s(Superclass.inheritedGenerativeConstructor)'
-            ' in s(Superclass), constructor)',
-        'Method(s(Superclass.inheritedNormalFactory)'
-            ' in s(Superclass), static, constructor)',
-        'Method(s(Superclass.inheritedRedirectingConstructor)'
-            ' in s(Superclass), constructor)',
-        'Method(s(Superclass.inheritedRedirectingFactory)'
-            ' in s(Superclass), static, constructor)',
-        'Method(s(_inheritedInstanceGetter) in s(Superclass), private, getter)',
-        'Method(s(_inheritedInstanceMethod) in s(Superclass), private)',
-        'Method(s(_inheritedInstanceSetter=) in s(Superclass), private, setter)',
-        'Variable(s(_inheritedInstanceVariable) in s(Superclass), private)',
-        'Method(s(_inheritedStaticGetter)'
-            ' in s(Superclass), private, static, getter)',
-        'Method(s(_inheritedStaticMethod) in s(Superclass), private, static)',
-        'Method(s(_inheritedStaticSetter=)'
-            ' in s(Superclass), private, static, setter)',
-        'Variable(s(_inheritedStaticVariable) in s(Superclass), private, static)',
-        'Method(s(_instanceGetter) in s(Class), private, getter)',
-        'Method(s(_instanceMethod) in s(Class), private)',
-        'Method(s(_instanceSetter=) in s(Class), private, setter)',
-        'Variable(s(_instanceVariable) in s(Class), private)',
-        'Method(s(_mixinInstanceGetter) in s(Mixin), private, getter)',
-        'Method(s(_mixinInstanceMethod) in s(Mixin), private)',
-        'Method(s(_mixinInstanceSetter=) in s(Mixin), private, setter)',
-        'Variable(s(_mixinInstanceVariable) in s(Mixin), private)',
-        'Method(s(_staticGetter) in s(Class), private, static, getter)',
-        'Method(s(_staticMethod) in s(Class), private, static)',
-        'Method(s(_staticSetter=) in s(Class), private, static, setter)',
-        'Variable(s(_staticVariable) in s(Class), private, static)',
-        'Method(s(abstractMethod) in s(Class), abstract)',
-        'Method(s(inheritedInstanceGetter) in s(Superclass), getter)',
-        'Method(s(inheritedInstanceMethod) in s(Superclass))',
-        'Method(s(inheritedInstanceSetter=) in s(Superclass), setter)',
-        'Variable(s(inheritedInstanceVariable) in s(Superclass))',
-        'Method(s(inheritedStaticGetter) in s(Superclass), static, getter)',
-        'Method(s(inheritedStaticMethod) in s(Superclass), static)',
-        'Method(s(inheritedStaticSetter=) in s(Superclass), static, setter)',
-        'Variable(s(inheritedStaticVariable) in s(Superclass), static)',
-        'Method(s(instanceGetter) in s(Class), getter)',
-        'Method(s(instanceMethod) in s(Class))',
-        'Method(s(instanceSetter=) in s(Class), setter)',
-        'Variable(s(instanceVariable) in s(Class))',
-        'Method(s(mixinInstanceGetter) in s(Mixin), getter)',
-        'Method(s(mixinInstanceMethod) in s(Mixin))',
-        'Method(s(mixinInstanceSetter=) in s(Mixin), setter)',
-        'Variable(s(mixinInstanceVariable) in s(Mixin))',
-        'Method(s(staticGetter) in s(Class), static, getter)',
-        'Method(s(staticMethod) in s(Class), static)',
-        'Method(s(staticSetter=) in s(Class), static, setter)',
-        'Variable(s(staticVariable) in s(Class), static)',
-        'Method(s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin._inheritedGenerativeConstructor)'
-            ' in s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin), private, constructor)',
-        'Method(s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin._inheritedRedirectingConstructor)'
-            ' in s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin), private, constructor)',
-        'Method(s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin.inheritedGenerativeConstructor)'
-            ' in s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin), constructor)',
-        'Method(s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin.inheritedRedirectingConstructor)'
-            ' in s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin), constructor)',
-        'Variable(s(mixinStaticVariable) in s(Mixin), static)',
-        'Variable(s(_mixinStaticVariable) in s(Mixin), private, static)',
-        'Method(s(mixinStaticGetter) in s(Mixin), static, getter)',
-        'Method(s(mixinStaticSetter=) in s(Mixin), static, setter)',
-        'Method(s(mixinStaticMethod) in s(Mixin), static)',
-        'Method(s(_mixinStaticGetter) in s(Mixin), private, static, getter)',
-        'Method(s(_mixinStaticSetter=) in s(Mixin), private, static, setter)',
-        'Method(s(_mixinStaticMethod) in s(Mixin), private, static)'
-      ],
-      inheritedDeclarations(cm)
-          .difference(reflectClass(Object).declarations.values.toSet())
-          .map(stringify),
-      'transitive less Object');
-  // The private members of Object may vary across implementations, so we
-  // exclude the declarations of Object in this test case.
+  Expect.setEquals([
+    'Method(s(*) in s(Mixin))',
+    'Method(s(+) in s(Class))',
+    'Method(s(-) in s(Superclass))',
+    'TypeVariable(s(C) in s(Class),'
+        ' upperBound = Class(s(Object) in s(dart.core), top-level))',
+    'Method(s(Class._generativeConstructor) in s(Class), private, constructor)',
+    'Method(s(Class._normalFactory) in s(Class), private, static, constructor)',
+    'Method(s(Class._redirectingConstructor)'
+        ' in s(Class), private, constructor)',
+    'Method(s(Class._redirectingFactory)'
+        ' in s(Class), private, static, constructor)',
+    'Method(s(Class.generativeConstructor) in s(Class), constructor)',
+    'Method(s(Class.normalFactory) in s(Class), static, constructor)',
+    'Method(s(Class.redirectingConstructor) in s(Class), constructor)',
+    'Method(s(Class.redirectingFactory) in s(Class), static, constructor)',
+    'TypeVariable(s(S) in s(Superclass),'
+        ' upperBound = Class(s(Object) in s(dart.core), top-level))',
+    'Method(s(Superclass._inheritedGenerativeConstructor)'
+        ' in s(Superclass), private, constructor)',
+    'Method(s(Superclass._inheritedNormalFactory)'
+        ' in s(Superclass), private, static, constructor)',
+    'Method(s(Superclass._inheritedRedirectingConstructor)'
+        ' in s(Superclass), private, constructor)',
+    'Method(s(Superclass._inheritedRedirectingFactory)'
+        ' in s(Superclass), private, static, constructor)',
+    'Method(s(Superclass.inheritedGenerativeConstructor)'
+        ' in s(Superclass), constructor)',
+    'Method(s(Superclass.inheritedNormalFactory)'
+        ' in s(Superclass), static, constructor)',
+    'Method(s(Superclass.inheritedRedirectingConstructor)'
+        ' in s(Superclass), constructor)',
+    'Method(s(Superclass.inheritedRedirectingFactory)'
+        ' in s(Superclass), static, constructor)',
+    'Method(s(_inheritedInstanceGetter) in s(Superclass), private, getter)',
+    'Method(s(_inheritedInstanceMethod) in s(Superclass), private)',
+    'Method(s(_inheritedInstanceSetter=) in s(Superclass), private, setter)',
+    'Variable(s(_inheritedInstanceVariable) in s(Superclass), private)',
+    'Method(s(_inheritedStaticGetter)'
+        ' in s(Superclass), private, static, getter)',
+    'Method(s(_inheritedStaticMethod) in s(Superclass), private, static)',
+    'Method(s(_inheritedStaticSetter=)'
+        ' in s(Superclass), private, static, setter)',
+    'Variable(s(_inheritedStaticVariable) in s(Superclass), private, static)',
+    'Method(s(_instanceGetter) in s(Class), private, getter)',
+    'Method(s(_instanceMethod) in s(Class), private)',
+    'Method(s(_instanceSetter=) in s(Class), private, setter)',
+    'Variable(s(_instanceVariable) in s(Class), private)',
+    'Method(s(_mixinInstanceGetter) in s(Mixin), private, getter)',
+    'Method(s(_mixinInstanceMethod) in s(Mixin), private)',
+    'Method(s(_mixinInstanceSetter=) in s(Mixin), private, setter)',
+    'Variable(s(_mixinInstanceVariable) in s(Mixin), private)',
+    'Method(s(_staticGetter) in s(Class), private, static, getter)',
+    'Method(s(_staticMethod) in s(Class), private, static)',
+    'Method(s(_staticSetter=) in s(Class), private, static, setter)',
+    'Variable(s(_staticVariable) in s(Class), private, static)',
+    'Method(s(abstractMethod) in s(Class), abstract)',
+    'Method(s(inheritedInstanceGetter) in s(Superclass), getter)',
+    'Method(s(inheritedInstanceMethod) in s(Superclass))',
+    'Method(s(inheritedInstanceSetter=) in s(Superclass), setter)',
+    'Variable(s(inheritedInstanceVariable) in s(Superclass))',
+    'Method(s(inheritedStaticGetter) in s(Superclass), static, getter)',
+    'Method(s(inheritedStaticMethod) in s(Superclass), static)',
+    'Method(s(inheritedStaticSetter=) in s(Superclass), static, setter)',
+    'Variable(s(inheritedStaticVariable) in s(Superclass), static)',
+    'Method(s(instanceGetter) in s(Class), getter)',
+    'Method(s(instanceMethod) in s(Class))',
+    'Method(s(instanceSetter=) in s(Class), setter)',
+    'Variable(s(instanceVariable) in s(Class))',
+    'Method(s(mixinInstanceGetter) in s(Mixin), getter)',
+    'Method(s(mixinInstanceMethod) in s(Mixin))',
+    'Method(s(mixinInstanceSetter=) in s(Mixin), setter)',
+    'Variable(s(mixinInstanceVariable) in s(Mixin))',
+    'Method(s(staticGetter) in s(Class), static, getter)',
+    'Method(s(staticMethod) in s(Class), static)',
+    'Method(s(staticSetter=) in s(Class), static, setter)',
+    'Variable(s(staticVariable) in s(Class), static)',
+    'Method(s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin._inheritedGenerativeConstructor)'
+        ' in s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin), private, constructor)',
+    'Method(s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin._inheritedRedirectingConstructor)'
+        ' in s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin), private, constructor)',
+    'Method(s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin.inheritedGenerativeConstructor)'
+        ' in s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin), constructor)',
+    'Method(s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin.inheritedRedirectingConstructor)'
+        ' in s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin), constructor)',
+    'Variable(s(mixinStaticVariable) in s(Mixin), static)',
+    'Variable(s(_mixinStaticVariable) in s(Mixin), private, static)',
+    'Method(s(mixinStaticGetter) in s(Mixin), static, getter)',
+    'Method(s(mixinStaticSetter=) in s(Mixin), static, setter)',
+    'Method(s(mixinStaticMethod) in s(Mixin), static)',
+    'Method(s(_mixinStaticGetter) in s(Mixin), private, static, getter)',
+    'Method(s(_mixinStaticSetter=) in s(Mixin), private, static, setter)',
+    'Method(s(_mixinStaticMethod) in s(Mixin), private, static)'
+  ], transitiveDeclarations(cm).map(stringify), 'transitive all');
 }
diff --git a/tests/lib_2/mirrors/class_declarations_test.dart b/tests/lib_2/mirrors/class_declarations_test.dart
index fc43a76..29c1595 100644
--- a/tests/lib_2/mirrors/class_declarations_test.dart
+++ b/tests/lib_2/mirrors/class_declarations_test.dart
@@ -12,9 +12,16 @@
 import 'stringify.dart';
 import 'declarations_model.dart' as declarations_model;
 
-Set<DeclarationMirror> inheritedDeclarations(ClassMirror cm) {
+/// Collects all declarations of [cm] and its super-classes except `Object`.
+///
+/// Includes static declarations of super-classes.
+///
+/// The `Object` class is omitted because this test should be stable against
+/// changes to the platform libraries, as long as the declaration model code
+/// doesn't change.
+Set<DeclarationMirror> transitiveDeclarations(ClassMirror cm) {
   var decls = new Set<DeclarationMirror>();
-  while (cm != null) {
+  while (cm != reflectClass(Object)) {
     decls.addAll(cm.declarations.values);
     cm = cm.superclass;
   }
@@ -165,14 +172,12 @@
     'Method(s(*) in s(Mixin))',
     'Method(s(+) in s(Class))',
     'Method(s(-) in s(Superclass))',
-    'Method(s(==) in s(Object))',
     'TypeVariable(s(C) in s(Class),'
         ' upperBound = Class(s(Object) in s(dart.core), top-level))',
     'Method(s(Class.generativeConstructor) in s(Class), constructor)',
     'Method(s(Class.normalFactory) in s(Class), static, constructor)',
     'Method(s(Class.redirectingConstructor) in s(Class), constructor)',
     'Method(s(Class.redirectingFactory) in s(Class), static, constructor)',
-    'Method(s(Object) in s(Object), constructor)',
     'TypeVariable(s(S) in s(Superclass),'
         ' upperBound = Class(s(Object) in s(dart.core), top-level))',
     'Method(s(Superclass.inheritedGenerativeConstructor)'
@@ -184,7 +189,6 @@
     'Method(s(Superclass.inheritedRedirectingFactory)'
         ' in s(Superclass), static, constructor)',
     'Method(s(abstractMethod) in s(Class), abstract)',
-    'Method(s(hashCode) in s(Object), getter)',
     'Method(s(inheritedInstanceGetter) in s(Superclass), getter)',
     'Method(s(inheritedInstanceMethod) in s(Superclass))',
     'Method(s(inheritedInstanceSetter=) in s(Superclass), setter)',
@@ -201,8 +205,6 @@
     'Method(s(mixinInstanceMethod) in s(Mixin))',
     'Method(s(mixinInstanceSetter=) in s(Mixin), setter)',
     'Variable(s(mixinInstanceVariable) in s(Mixin))',
-    'Method(s(noSuchMethod) in s(Object))',
-    'Method(s(runtimeType) in s(Object), getter)',
     'Method(s(staticGetter) in s(Class), static, getter)',
     'Method(s(staticMethod) in s(Class), static)',
     'Method(s(staticSetter=) in s(Class), static, setter)',
@@ -215,12 +217,11 @@
         ' with test.declarations_model.Mixin.inheritedRedirectingConstructor)'
         ' in s(test.declarations_model.Superclass'
         ' with test.declarations_model.Mixin), constructor)',
-    'Method(s(toString) in s(Object))',
     'Variable(s(mixinStaticVariable) in s(Mixin), static)',
     'Method(s(mixinStaticGetter) in s(Mixin), static, getter)',
     'Method(s(mixinStaticSetter=) in s(Mixin), static, setter)',
     'Method(s(mixinStaticMethod) in s(Mixin), static)'
-  ], inheritedDeclarations(cm).where((dm) => !dm.isPrivate).map(stringify),
+  ], transitiveDeclarations(cm).where((dm) => !dm.isPrivate).map(stringify),
       'transitive public');
   // The public members of Object should be the same in all implementations, so
   // we don't exclude Object here.
@@ -258,113 +259,106 @@
     'Variable(s(staticVariable) in s(Class), static)'
   ], cm.declarations.values.map(stringify), 'declarations');
 
-  Expect.setEquals(
-      [
-        'Method(s(*) in s(Mixin))',
-        'Method(s(+) in s(Class))',
-        'Method(s(-) in s(Superclass))',
-        'TypeVariable(s(C) in s(Class),'
-            ' upperBound = Class(s(Object) in s(dart.core), top-level))',
-        'Method(s(Class._generativeConstructor) in s(Class), private, constructor)',
-        'Method(s(Class._normalFactory) in s(Class), private, static, constructor)',
-        'Method(s(Class._redirectingConstructor)'
-            ' in s(Class), private, constructor)',
-        'Method(s(Class._redirectingFactory)'
-            ' in s(Class), private, static, constructor)',
-        'Method(s(Class.generativeConstructor) in s(Class), constructor)',
-        'Method(s(Class.normalFactory) in s(Class), static, constructor)',
-        'Method(s(Class.redirectingConstructor) in s(Class), constructor)',
-        'Method(s(Class.redirectingFactory) in s(Class), static, constructor)',
-        'TypeVariable(s(S) in s(Superclass),'
-            ' upperBound = Class(s(Object) in s(dart.core), top-level))',
-        'Method(s(Superclass._inheritedGenerativeConstructor)'
-            ' in s(Superclass), private, constructor)',
-        'Method(s(Superclass._inheritedNormalFactory)'
-            ' in s(Superclass), private, static, constructor)',
-        'Method(s(Superclass._inheritedRedirectingConstructor)'
-            ' in s(Superclass), private, constructor)',
-        'Method(s(Superclass._inheritedRedirectingFactory)'
-            ' in s(Superclass), private, static, constructor)',
-        'Method(s(Superclass.inheritedGenerativeConstructor)'
-            ' in s(Superclass), constructor)',
-        'Method(s(Superclass.inheritedNormalFactory)'
-            ' in s(Superclass), static, constructor)',
-        'Method(s(Superclass.inheritedRedirectingConstructor)'
-            ' in s(Superclass), constructor)',
-        'Method(s(Superclass.inheritedRedirectingFactory)'
-            ' in s(Superclass), static, constructor)',
-        'Method(s(_inheritedInstanceGetter) in s(Superclass), private, getter)',
-        'Method(s(_inheritedInstanceMethod) in s(Superclass), private)',
-        'Method(s(_inheritedInstanceSetter=) in s(Superclass), private, setter)',
-        'Variable(s(_inheritedInstanceVariable) in s(Superclass), private)',
-        'Method(s(_inheritedStaticGetter)'
-            ' in s(Superclass), private, static, getter)',
-        'Method(s(_inheritedStaticMethod) in s(Superclass), private, static)',
-        'Method(s(_inheritedStaticSetter=)'
-            ' in s(Superclass), private, static, setter)',
-        'Variable(s(_inheritedStaticVariable) in s(Superclass), private, static)',
-        'Method(s(_instanceGetter) in s(Class), private, getter)',
-        'Method(s(_instanceMethod) in s(Class), private)',
-        'Method(s(_instanceSetter=) in s(Class), private, setter)',
-        'Variable(s(_instanceVariable) in s(Class), private)',
-        'Method(s(_mixinInstanceGetter) in s(Mixin), private, getter)',
-        'Method(s(_mixinInstanceMethod) in s(Mixin), private)',
-        'Method(s(_mixinInstanceSetter=) in s(Mixin), private, setter)',
-        'Variable(s(_mixinInstanceVariable) in s(Mixin), private)',
-        'Method(s(_staticGetter) in s(Class), private, static, getter)',
-        'Method(s(_staticMethod) in s(Class), private, static)',
-        'Method(s(_staticSetter=) in s(Class), private, static, setter)',
-        'Variable(s(_staticVariable) in s(Class), private, static)',
-        'Method(s(abstractMethod) in s(Class), abstract)',
-        'Method(s(inheritedInstanceGetter) in s(Superclass), getter)',
-        'Method(s(inheritedInstanceMethod) in s(Superclass))',
-        'Method(s(inheritedInstanceSetter=) in s(Superclass), setter)',
-        'Variable(s(inheritedInstanceVariable) in s(Superclass))',
-        'Method(s(inheritedStaticGetter) in s(Superclass), static, getter)',
-        'Method(s(inheritedStaticMethod) in s(Superclass), static)',
-        'Method(s(inheritedStaticSetter=) in s(Superclass), static, setter)',
-        'Variable(s(inheritedStaticVariable) in s(Superclass), static)',
-        'Method(s(instanceGetter) in s(Class), getter)',
-        'Method(s(instanceMethod) in s(Class))',
-        'Method(s(instanceSetter=) in s(Class), setter)',
-        'Variable(s(instanceVariable) in s(Class))',
-        'Method(s(mixinInstanceGetter) in s(Mixin), getter)',
-        'Method(s(mixinInstanceMethod) in s(Mixin))',
-        'Method(s(mixinInstanceSetter=) in s(Mixin), setter)',
-        'Variable(s(mixinInstanceVariable) in s(Mixin))',
-        'Method(s(staticGetter) in s(Class), static, getter)',
-        'Method(s(staticMethod) in s(Class), static)',
-        'Method(s(staticSetter=) in s(Class), static, setter)',
-        'Variable(s(staticVariable) in s(Class), static)',
-        'Method(s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin._inheritedGenerativeConstructor)'
-            ' in s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin), private, constructor)',
-        'Method(s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin._inheritedRedirectingConstructor)'
-            ' in s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin), private, constructor)',
-        'Method(s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin.inheritedGenerativeConstructor)'
-            ' in s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin), constructor)',
-        'Method(s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin.inheritedRedirectingConstructor)'
-            ' in s(test.declarations_model.Superclass'
-            ' with test.declarations_model.Mixin), constructor)',
-        'Variable(s(mixinStaticVariable) in s(Mixin), static)',
-        'Variable(s(_mixinStaticVariable) in s(Mixin), private, static)',
-        'Method(s(mixinStaticGetter) in s(Mixin), static, getter)',
-        'Method(s(mixinStaticSetter=) in s(Mixin), static, setter)',
-        'Method(s(mixinStaticMethod) in s(Mixin), static)',
-        'Method(s(_mixinStaticGetter) in s(Mixin), private, static, getter)',
-        'Method(s(_mixinStaticSetter=) in s(Mixin), private, static, setter)',
-        'Method(s(_mixinStaticMethod) in s(Mixin), private, static)'
-      ],
-      inheritedDeclarations(cm)
-          .difference(reflectClass(Object).declarations.values.toSet())
-          .map(stringify),
-      'transitive less Object');
-  // The private members of Object may vary across implementations, so we
-  // exclude the declarations of Object in this test case.
+  Expect.setEquals([
+    'Method(s(*) in s(Mixin))',
+    'Method(s(+) in s(Class))',
+    'Method(s(-) in s(Superclass))',
+    'TypeVariable(s(C) in s(Class),'
+        ' upperBound = Class(s(Object) in s(dart.core), top-level))',
+    'Method(s(Class._generativeConstructor) in s(Class), private, constructor)',
+    'Method(s(Class._normalFactory) in s(Class), private, static, constructor)',
+    'Method(s(Class._redirectingConstructor)'
+        ' in s(Class), private, constructor)',
+    'Method(s(Class._redirectingFactory)'
+        ' in s(Class), private, static, constructor)',
+    'Method(s(Class.generativeConstructor) in s(Class), constructor)',
+    'Method(s(Class.normalFactory) in s(Class), static, constructor)',
+    'Method(s(Class.redirectingConstructor) in s(Class), constructor)',
+    'Method(s(Class.redirectingFactory) in s(Class), static, constructor)',
+    'TypeVariable(s(S) in s(Superclass),'
+        ' upperBound = Class(s(Object) in s(dart.core), top-level))',
+    'Method(s(Superclass._inheritedGenerativeConstructor)'
+        ' in s(Superclass), private, constructor)',
+    'Method(s(Superclass._inheritedNormalFactory)'
+        ' in s(Superclass), private, static, constructor)',
+    'Method(s(Superclass._inheritedRedirectingConstructor)'
+        ' in s(Superclass), private, constructor)',
+    'Method(s(Superclass._inheritedRedirectingFactory)'
+        ' in s(Superclass), private, static, constructor)',
+    'Method(s(Superclass.inheritedGenerativeConstructor)'
+        ' in s(Superclass), constructor)',
+    'Method(s(Superclass.inheritedNormalFactory)'
+        ' in s(Superclass), static, constructor)',
+    'Method(s(Superclass.inheritedRedirectingConstructor)'
+        ' in s(Superclass), constructor)',
+    'Method(s(Superclass.inheritedRedirectingFactory)'
+        ' in s(Superclass), static, constructor)',
+    'Method(s(_inheritedInstanceGetter) in s(Superclass), private, getter)',
+    'Method(s(_inheritedInstanceMethod) in s(Superclass), private)',
+    'Method(s(_inheritedInstanceSetter=) in s(Superclass), private, setter)',
+    'Variable(s(_inheritedInstanceVariable) in s(Superclass), private)',
+    'Method(s(_inheritedStaticGetter)'
+        ' in s(Superclass), private, static, getter)',
+    'Method(s(_inheritedStaticMethod) in s(Superclass), private, static)',
+    'Method(s(_inheritedStaticSetter=)'
+        ' in s(Superclass), private, static, setter)',
+    'Variable(s(_inheritedStaticVariable) in s(Superclass), private, static)',
+    'Method(s(_instanceGetter) in s(Class), private, getter)',
+    'Method(s(_instanceMethod) in s(Class), private)',
+    'Method(s(_instanceSetter=) in s(Class), private, setter)',
+    'Variable(s(_instanceVariable) in s(Class), private)',
+    'Method(s(_mixinInstanceGetter) in s(Mixin), private, getter)',
+    'Method(s(_mixinInstanceMethod) in s(Mixin), private)',
+    'Method(s(_mixinInstanceSetter=) in s(Mixin), private, setter)',
+    'Variable(s(_mixinInstanceVariable) in s(Mixin), private)',
+    'Method(s(_staticGetter) in s(Class), private, static, getter)',
+    'Method(s(_staticMethod) in s(Class), private, static)',
+    'Method(s(_staticSetter=) in s(Class), private, static, setter)',
+    'Variable(s(_staticVariable) in s(Class), private, static)',
+    'Method(s(abstractMethod) in s(Class), abstract)',
+    'Method(s(inheritedInstanceGetter) in s(Superclass), getter)',
+    'Method(s(inheritedInstanceMethod) in s(Superclass))',
+    'Method(s(inheritedInstanceSetter=) in s(Superclass), setter)',
+    'Variable(s(inheritedInstanceVariable) in s(Superclass))',
+    'Method(s(inheritedStaticGetter) in s(Superclass), static, getter)',
+    'Method(s(inheritedStaticMethod) in s(Superclass), static)',
+    'Method(s(inheritedStaticSetter=) in s(Superclass), static, setter)',
+    'Variable(s(inheritedStaticVariable) in s(Superclass), static)',
+    'Method(s(instanceGetter) in s(Class), getter)',
+    'Method(s(instanceMethod) in s(Class))',
+    'Method(s(instanceSetter=) in s(Class), setter)',
+    'Variable(s(instanceVariable) in s(Class))',
+    'Method(s(mixinInstanceGetter) in s(Mixin), getter)',
+    'Method(s(mixinInstanceMethod) in s(Mixin))',
+    'Method(s(mixinInstanceSetter=) in s(Mixin), setter)',
+    'Variable(s(mixinInstanceVariable) in s(Mixin))',
+    'Method(s(staticGetter) in s(Class), static, getter)',
+    'Method(s(staticMethod) in s(Class), static)',
+    'Method(s(staticSetter=) in s(Class), static, setter)',
+    'Variable(s(staticVariable) in s(Class), static)',
+    'Method(s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin._inheritedGenerativeConstructor)'
+        ' in s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin), private, constructor)',
+    'Method(s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin._inheritedRedirectingConstructor)'
+        ' in s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin), private, constructor)',
+    'Method(s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin.inheritedGenerativeConstructor)'
+        ' in s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin), constructor)',
+    'Method(s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin.inheritedRedirectingConstructor)'
+        ' in s(test.declarations_model.Superclass'
+        ' with test.declarations_model.Mixin), constructor)',
+    'Variable(s(mixinStaticVariable) in s(Mixin), static)',
+    'Variable(s(_mixinStaticVariable) in s(Mixin), private, static)',
+    'Method(s(mixinStaticGetter) in s(Mixin), static, getter)',
+    'Method(s(mixinStaticSetter=) in s(Mixin), static, setter)',
+    'Method(s(mixinStaticMethod) in s(Mixin), static)',
+    'Method(s(_mixinStaticGetter) in s(Mixin), private, static, getter)',
+    'Method(s(_mixinStaticSetter=) in s(Mixin), private, static, setter)',
+    'Method(s(_mixinStaticMethod) in s(Mixin), private, static)'
+  ], transitiveDeclarations(cm).map(stringify), 'transitive all');
 }
diff --git a/tools/VERSION b/tools/VERSION
index eeda70a..2264ce2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 247
+PRERELEASE 248
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index e9520a3..b2b13ea 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -1266,7 +1266,7 @@
         "vm-precomp-ffi-qemu-linux-release-arm"
       ],
       "meta": {
-        "description": "This configuration is used for running FFI tests on qemu and FFI unit tests."
+        "description": "This configuration is used for running vm unit tests and FFI tests on qemu and FFI unit tests."
       },
       "steps": [
         {
@@ -1303,6 +1303,13 @@
           ]
         },
         {
+          "name": "vm unit tests",
+          "arguments": [
+            "-ndartk-linux-${mode}-arm-qemu",
+            "vm/cc"
+          ]
+        },
+        {
           "name": "ffi tests",
           "arguments": [
             "-ndartkp-linux-${mode}-arm-qemu",