blob: 522774da96d0b2361873b2c0b9cc8d1d9fe443b5 [file] [log] [blame]
// Copyright (c) 2012, 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.
part of dart.core;
/// The base class for all Dart objects except `null`.
///
/// Because `Object` is a root of the non-nullable Dart class hierarchy,
/// every other non-`Null` Dart class is a subclass of `Object`.
///
/// When you define a class, you should consider overriding [toString]
/// to return a string describing an instance of that class.
/// You might also need to define [hashCode] and [operator ==], as described in the
/// [Implementing map keys](https://dart.dev/guides/libraries/library-tour#implementing-map-keys)
/// section of the [library tour](https://dart.dev/guides/libraries/library-tour).
@pragma("vm:entry-point")
class Object {
/// Creates a new [Object] instance.
///
/// [Object] instances have no meaningful state, and are only useful
/// through their identity. An [Object] instance is equal to itself
/// only.
@pragma("vm:recognized", "other")
const Object();
/// The equality operator.
///
/// The default behavior for all [Object]s is to return true if and
/// only if this object and [other] are the same object.
///
/// Override this method to specify a different equality relation on
/// a class. The overriding method must still be an equivalence relation.
/// That is, it must be:
///
/// * Total: It must return a boolean for all arguments. It should never throw.
///
/// * Reflexive: For all objects `o`, `o == o` must be true.
///
/// * Symmetric: For all objects `o1` and `o2`, `o1 == o2` and `o2 == o1` must
/// either both be true, or both be false.
///
/// * Transitive: For all objects `o1`, `o2`, and `o3`, if `o1 == o2` and
/// `o2 == o3` are true, then `o1 == o3` must be true.
///
/// The method should also be consistent over time,
/// so whether two objects are equal should only change
/// if at least one of the objects was modified.
///
/// If a subclass overrides the equality operator, it should override
/// the [hashCode] method as well to maintain consistency.
external bool operator ==(Object other);
/// The hash code for this object.
///
/// A hash code is a single integer which represents the state of the object
/// that affects [operator ==] comparisons.
///
/// All objects have hash codes.
/// The default hash code implemented by [Object]
/// represents only the identity of the object,
/// the same way as the default [operator ==] implementation only considers objects
/// equal if they are identical (see [identityHashCode]).
///
/// If [operator ==] is overridden to use the object state instead,
/// the hash code must also be changed to represent that state,
/// otherwise the object cannot be used in hash based data structures
/// like the default [Set] and [Map] implementations.
///
/// Hash codes must be the same for objects that are equal to each other
/// according to [operator ==].
/// The hash code of an object should only change if the object changes
/// in a way that affects equality.
/// There are no further requirements for the hash codes.
/// They need not be consistent between executions of the same program
/// and there are no distribution guarantees.
///
/// Objects that are not equal are allowed to have the same hash code.
/// It is even technically allowed that all instances have the same hash code,
/// but if clashes happen too often,
/// it may reduce the efficiency of hash-based data structures
/// like [HashSet] or [HashMap].
///
/// If a subclass overrides [hashCode], it should override the
/// [operator ==] operator as well to maintain consistency.
external int get hashCode;
/// A string representation of this object.
///
/// Some classes have a default textual representation,
/// often paired with a static `parse` function (like [int.parse]).
/// These classes will provide the textual representation as
/// their string representation.
///
/// Other classes have no meaningful textual representation
/// that a program will care about.
/// Such classes will typically override `toString` to provide
/// useful information when inspecting the object,
/// mainly for debugging or logging.
external String toString();
/// Invoked when a non-existent method or property is accessed.
///
/// A dynamic member invocation can attempt to call a member which
/// doesn't exist on the receiving object. Example:
/// ```dart
/// dynamic object = 1;
/// object.add(42); // Statically allowed, run-time error
/// ```
/// This invalid code will invoke the `noSuchMethod` method
/// of the integer `1` with an [Invocation] representing the
/// `.add(42)` call and arguments (which then throws).
///
/// Classes can override [noSuchMethod] to provide custom behavior
/// for such invalid dynamic invocations.
///
/// A class with a non-default [noSuchMethod] invocation can also
/// omit implementations for members of its interface.
/// Example:
/// ```dart
/// class MockList<T> implements List<T> {
/// noSuchMethod(Invocation invocation) {
/// log(invocation);
/// super.noSuchMethod(invocation); // Will throw.
/// }
/// }
/// void main() {
/// MockList().add(42);
/// }
/// ```
/// This code has no compile-time warnings or errors even though
/// the `MockList` class has no concrete implementation of
/// any of the `List` interface methods.
/// Calls to `List` methods are forwarded to `noSuchMethod`,
/// so this code will `log` an invocation similar to
/// `Invocation.method(#add, [42])` and then throw.
///
/// If a value is returned from `noSuchMethod`,
/// it becomes the result of the original invocation.
/// If the value is not of a type that can be returned by the original
/// invocation, a type error occurs at the invocation.
///
/// The default behavior is to throw a [NoSuchMethodError].
@pragma("vm:entry-point")
external dynamic noSuchMethod(Invocation invocation);
/// 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 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* 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.
@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 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* guaranteed 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);