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",