* Rename misleading Instance::Equals to CanonicalizeEquals and document it.
* Keep 'Equals' alias for String and Integer. * Add Instance::OperatorEquals that calls or mimics Object.operator==. This is partly in preparation for internalizing HashMap/Set. R=hausner@google.com, rmacnak@google.com Review URL: https://codereview.chromium.org//274333002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@36066 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -19,7 +19,7 @@ void FUNCTION_NAME(Unhandled_equals)(Dart_NativeArguments args) {
|
||||
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
||||
const Instance& expected = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
||||
const Instance& actual = Instance::CheckedHandle(arguments->NativeArgAt(1));
|
||||
if (!expected.Equals(actual)) {
|
||||
if (!expected.CanonicalizeEquals(actual)) {
|
||||
OS::Print("expected: '%s' actual: '%s'\n",
|
||||
expected.ToCString(), actual.ToCString());
|
||||
FATAL("Unhandled_equals fails.\n");
|
||||
|
||||
+31
-11
@@ -12733,7 +12733,7 @@ RawObject* Instance::Evaluate(const String& expr,
|
||||
|
||||
|
||||
|
||||
bool Instance::Equals(const Instance& other) const {
|
||||
bool Instance::CanonicalizeEquals(const Instance& other) const {
|
||||
if (this->raw() == other.raw()) {
|
||||
return true; // "===".
|
||||
}
|
||||
@@ -12844,7 +12844,7 @@ RawInstance* Instance::CheckAndCanonicalize(const char** error_str) const {
|
||||
if (result.IsNull()) {
|
||||
break;
|
||||
}
|
||||
if (this->Equals(result)) {
|
||||
if (this->CanonicalizeEquals(result)) {
|
||||
return result.raw();
|
||||
}
|
||||
index++;
|
||||
@@ -12950,13 +12950,22 @@ bool Instance::IsInstanceOf(const AbstractType& other,
|
||||
}
|
||||
|
||||
|
||||
bool Instance::OperatorEquals(const Instance& other) const {
|
||||
// TODO(koda): Optimize for all builtin classes and all classes
|
||||
// that do not override operator==.
|
||||
const Object& result =
|
||||
Object::Handle(DartLibraryCalls::Equals(*this, other));
|
||||
return result.raw() == Object::bool_true().raw();
|
||||
}
|
||||
|
||||
|
||||
bool Instance::IsIdenticalTo(const Instance& other) const {
|
||||
if (raw() == other.raw()) return true;
|
||||
if (IsInteger() && other.IsInteger()) {
|
||||
return Equals(other);
|
||||
return Integer::Cast(*this).Equals(other);
|
||||
}
|
||||
if (IsDouble() && other.IsDouble()) {
|
||||
if (Equals(other)) return true;
|
||||
if (Double::Cast(*this).CanonicalizeEquals(other)) return true;
|
||||
// Check for NaN.
|
||||
const Double& a_double = Double::Cast(*this);
|
||||
const Double& b_double = Double::Cast(other);
|
||||
@@ -15482,7 +15491,7 @@ void Double::set_value(double value) const {
|
||||
}
|
||||
|
||||
|
||||
bool Double::EqualsToDouble(double value) const {
|
||||
bool Double::BitwiseEqualsToDouble(double value) const {
|
||||
intptr_t value_offset = Double::value_offset();
|
||||
void* this_addr = reinterpret_cast<void*>(
|
||||
reinterpret_cast<uword>(this->raw_ptr()) + value_offset);
|
||||
@@ -15491,14 +15500,25 @@ bool Double::EqualsToDouble(double value) const {
|
||||
}
|
||||
|
||||
|
||||
bool Double::Equals(const Instance& other) const {
|
||||
bool Double::OperatorEquals(const Instance& other) const {
|
||||
if (this->IsNull() || other.IsNull()) {
|
||||
return (this->IsNull() && other.IsNull());
|
||||
}
|
||||
if (!other.IsDouble()) {
|
||||
return false;
|
||||
}
|
||||
return this->value() == Double::Cast(other).value();
|
||||
}
|
||||
|
||||
|
||||
bool Double::CanonicalizeEquals(const Instance& other) const {
|
||||
if (this->raw() == other.raw()) {
|
||||
return true; // "===".
|
||||
}
|
||||
if (other.IsNull() || !other.IsDouble()) {
|
||||
return false;
|
||||
}
|
||||
return EqualsToDouble(Double::Cast(other).value());
|
||||
return BitwiseEqualsToDouble(Double::Cast(other).value());
|
||||
}
|
||||
|
||||
|
||||
@@ -15540,7 +15560,7 @@ RawDouble* Double::NewCanonical(double value) {
|
||||
if (canonical_value.IsNull()) {
|
||||
break;
|
||||
}
|
||||
if (canonical_value.EqualsToDouble(value)) {
|
||||
if (canonical_value.BitwiseEqualsToDouble(value)) {
|
||||
return canonical_value.raw();
|
||||
}
|
||||
index++;
|
||||
@@ -17441,7 +17461,7 @@ void Bool::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
}
|
||||
|
||||
|
||||
bool Array::Equals(const Instance& other) const {
|
||||
bool Array::CanonicalizeEquals(const Instance& other) const {
|
||||
if (this->raw() == other.raw()) {
|
||||
// Both handles point to the same raw instance.
|
||||
return true;
|
||||
@@ -17681,7 +17701,7 @@ RawObject* GrowableObjectArray::RemoveLast() const {
|
||||
}
|
||||
|
||||
|
||||
bool GrowableObjectArray::Equals(const Instance& other) const {
|
||||
bool GrowableObjectArray::CanonicalizeEquals(const Instance& other) const {
|
||||
// If both handles point to the same raw instance they are equal.
|
||||
if (this->raw() == other.raw()) {
|
||||
return true;
|
||||
@@ -18591,7 +18611,7 @@ const char* JSRegExp::Flags() const {
|
||||
}
|
||||
|
||||
|
||||
bool JSRegExp::Equals(const Instance& other) const {
|
||||
bool JSRegExp::CanonicalizeEquals(const Instance& other) const {
|
||||
if (this->raw() == other.raw()) {
|
||||
return true; // "===".
|
||||
}
|
||||
|
||||
+37
-10
@@ -4136,7 +4136,17 @@ class UnwindError : public Error {
|
||||
// in Dart source code.
|
||||
class Instance : public Object {
|
||||
public:
|
||||
virtual bool Equals(const Instance& other) const;
|
||||
// Equality and identity testing.
|
||||
// 1. OperatorEquals: true iff 'this == other' is true in Dart code.
|
||||
// 2. IsIdenticalTo: true iff 'identical(this, other)' is true in Dart code.
|
||||
// 3. CanonicalizeEquals: used to canonicalize compile-time constants, e.g.,
|
||||
// using bitwise equality of fields and list elements.
|
||||
// Subclasses where 1 and 3 coincide may also define a plain Equals, e.g.,
|
||||
// String and Integer.
|
||||
virtual bool OperatorEquals(const Instance& other) const;
|
||||
bool IsIdenticalTo(const Instance& other) const;
|
||||
virtual bool CanonicalizeEquals(const Instance& other) const;
|
||||
|
||||
// Returns Instance::null() if instance cannot be canonicalized.
|
||||
// Any non-canonical number of string will be canonicalized here.
|
||||
// An instance cannot be canonicalized if it still contains non-canonical
|
||||
@@ -4166,10 +4176,6 @@ class Instance : public Object {
|
||||
const TypeArguments& type_instantiator,
|
||||
Error* bound_error) const;
|
||||
|
||||
// Check whether this instance is identical to the argument according to the
|
||||
// specification of dare:core's identical().
|
||||
bool IsIdenticalTo(const Instance& other) const;
|
||||
|
||||
bool IsValidNativeIndex(int index) const {
|
||||
return ((index >= 0) && (index < clazz()->ptr()->num_native_fields_));
|
||||
}
|
||||
@@ -4314,6 +4320,9 @@ class AbstractType : public Instance {
|
||||
virtual RawTypeArguments* arguments() const;
|
||||
virtual intptr_t token_pos() const;
|
||||
virtual bool IsInstantiated(GrowableObjectArray* trail = NULL) const;
|
||||
virtual bool CanonicalizeEquals(const Instance& other) const {
|
||||
return Equals(other);
|
||||
}
|
||||
virtual bool Equals(const Instance& other) const {
|
||||
return IsEquivalent(other);
|
||||
}
|
||||
@@ -4880,6 +4889,17 @@ class Integer : public Number {
|
||||
Heap::Space space = Heap::kNew,
|
||||
const bool silent = false);
|
||||
|
||||
virtual bool OperatorEquals(const Instance& other) const {
|
||||
return Equals(other);
|
||||
}
|
||||
virtual bool CanonicalizeEquals(const Instance& other) const {
|
||||
return Equals(other);
|
||||
}
|
||||
virtual bool Equals(const Instance& other) const {
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Integer is an abstract class.
|
||||
virtual bool IsZero() const {
|
||||
UNREACHABLE();
|
||||
@@ -5144,8 +5164,9 @@ class Double : public Number {
|
||||
return raw_ptr()->value_;
|
||||
}
|
||||
|
||||
bool EqualsToDouble(double value) const;
|
||||
virtual bool Equals(const Instance& other) const;
|
||||
bool BitwiseEqualsToDouble(double value) const;
|
||||
virtual bool OperatorEquals(const Instance& other) const;
|
||||
virtual bool CanonicalizeEquals(const Instance& other) const;
|
||||
|
||||
static RawDouble* New(double d, Heap::Space space = Heap::kNew);
|
||||
|
||||
@@ -5265,6 +5286,12 @@ class String : public Instance {
|
||||
// Compares to an array of UTF-32 encoded characters.
|
||||
bool Equals(const int32_t* characters, intptr_t len) const;
|
||||
|
||||
virtual bool OperatorEquals(const Instance& other) const {
|
||||
return Equals(other);
|
||||
}
|
||||
virtual bool CanonicalizeEquals(const Instance& other) const {
|
||||
return Equals(other);
|
||||
}
|
||||
virtual bool Equals(const Instance& other) const;
|
||||
|
||||
intptr_t CompareTo(const String& other) const;
|
||||
@@ -5895,7 +5922,7 @@ class Array : public Instance {
|
||||
StorePointer(&raw_ptr()->type_arguments_, value.raw());
|
||||
}
|
||||
|
||||
virtual bool Equals(const Instance& other) const;
|
||||
virtual bool CanonicalizeEquals(const Instance& other) const;
|
||||
|
||||
static const intptr_t kBytesPerElement = kWordSize;
|
||||
static const intptr_t kMaxElements = kSmiMax / kBytesPerElement;
|
||||
@@ -6055,7 +6082,7 @@ class GrowableObjectArray : public Instance {
|
||||
StorePointer(&raw_ptr()->type_arguments_, value.raw());
|
||||
}
|
||||
|
||||
virtual bool Equals(const Instance& other) const;
|
||||
virtual bool CanonicalizeEquals(const Instance& other) const;
|
||||
|
||||
virtual RawInstance* CheckAndCanonicalize(const char** error_str) const {
|
||||
UNREACHABLE();
|
||||
@@ -6766,7 +6793,7 @@ class JSRegExp : public Instance {
|
||||
static RawJSRegExp* FromDataStartAddress(void* data);
|
||||
const char* Flags() const;
|
||||
|
||||
virtual bool Equals(const Instance& other) const;
|
||||
virtual bool CanonicalizeEquals(const Instance& other) const;
|
||||
|
||||
static const intptr_t kBytesPerElement = 1;
|
||||
static const intptr_t kMaxElements = kSmiMax / kBytesPerElement;
|
||||
|
||||
@@ -516,11 +516,18 @@ TEST_CASE(Double) {
|
||||
const double dbl_const = 2.0;
|
||||
const Double& dbl1 = Double::Handle(Double::New(dbl_const));
|
||||
const Double& dbl2 = Double::Handle(Double::New(dbl_const));
|
||||
EXPECT(dbl1.Equals(dbl2));
|
||||
EXPECT(dbl1.OperatorEquals(dbl2));
|
||||
EXPECT(dbl1.IsIdenticalTo(dbl2));
|
||||
EXPECT(dbl1.CanonicalizeEquals(dbl2));
|
||||
const Double& dbl3 = Double::Handle(Double::New(3.3));
|
||||
EXPECT(!dbl1.Equals(dbl3));
|
||||
EXPECT(!dbl1.Equals(Smi::Handle(Smi::New(3))));
|
||||
EXPECT(!dbl1.Equals(Double::Handle()));
|
||||
EXPECT(!dbl1.OperatorEquals(dbl3));
|
||||
EXPECT(!dbl1.OperatorEquals(Smi::Handle(Smi::New(3))));
|
||||
EXPECT(!dbl1.OperatorEquals(Double::Handle()));
|
||||
const Double& nan0 = Double::Handle(Double::New(NAN));
|
||||
EXPECT(nan0.IsIdenticalTo(nan0));
|
||||
EXPECT(nan0.CanonicalizeEquals(nan0));
|
||||
EXPECT(!nan0.OperatorEquals(nan0));
|
||||
// TODO(18738): Test bitwise different NaNs after agreement on spec.
|
||||
}
|
||||
{
|
||||
const String& dbl_str0 = String::Handle(String::New("bla"));
|
||||
@@ -1838,16 +1845,16 @@ TEST_CASE(Array) {
|
||||
other_array.SetAt(0, array);
|
||||
other_array.SetAt(2, array);
|
||||
|
||||
EXPECT(array.Equals(array));
|
||||
EXPECT(array.Equals(other_array));
|
||||
EXPECT(array.CanonicalizeEquals(array));
|
||||
EXPECT(array.CanonicalizeEquals(other_array));
|
||||
|
||||
other_array.SetAt(1, other_array);
|
||||
EXPECT(!array.Equals(other_array));
|
||||
EXPECT(!array.CanonicalizeEquals(other_array));
|
||||
|
||||
other_array = Array::New(kArrayLen - 1);
|
||||
other_array.SetAt(0, array);
|
||||
other_array.SetAt(2, array);
|
||||
EXPECT(!array.Equals(other_array));
|
||||
EXPECT(!array.CanonicalizeEquals(other_array));
|
||||
|
||||
EXPECT_EQ(0, Object::empty_array().Length());
|
||||
|
||||
@@ -4014,4 +4021,33 @@ TEST_CASE(PrintJSON) {
|
||||
heap->IterateObjects(&verifier);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE(InstanceEquality) {
|
||||
// Test that Instance::OperatorEquals can call a user-defined operator==.
|
||||
const char* kScript =
|
||||
"class A {\n"
|
||||
" bool operator==(A other) { return true; }\n"
|
||||
"}\n"
|
||||
"main() {\n"
|
||||
" A a = new A();\n"
|
||||
"}";
|
||||
|
||||
Dart_Handle h_lib = TestCase::LoadTestScript(kScript, NULL);
|
||||
EXPECT_VALID(h_lib);
|
||||
Library& lib = Library::Handle();
|
||||
lib ^= Api::UnwrapHandle(h_lib);
|
||||
EXPECT(!lib.IsNull());
|
||||
Dart_Handle result = Dart_Invoke(h_lib, NewString("main"), 0, NULL);
|
||||
EXPECT_VALID(result);
|
||||
const Class& clazz = Class::Handle(GetClass(lib, "A"));
|
||||
EXPECT(!clazz.IsNull());
|
||||
const Instance& a0 = Instance::Handle(Instance::New(clazz));
|
||||
const Instance& a1 = Instance::Handle(Instance::New(clazz));
|
||||
EXPECT(a0.raw() != a1.raw());
|
||||
EXPECT(a0.OperatorEquals(a0));
|
||||
EXPECT(a0.OperatorEquals(a1));
|
||||
EXPECT(a0.IsIdenticalTo(a0));
|
||||
EXPECT(!a0.IsIdenticalTo(a1));
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -572,7 +572,7 @@ TEST_CASE(SerializeArray) {
|
||||
Snapshot::kMessage, Isolate::Current());
|
||||
Array& serialized_array = Array::Handle();
|
||||
serialized_array ^= reader.ReadObject();
|
||||
EXPECT(array.Equals(serialized_array));
|
||||
EXPECT(array.CanonicalizeEquals(serialized_array));
|
||||
|
||||
// Read object back from the snapshot into a C structure.
|
||||
ApiNativeScope scope;
|
||||
@@ -657,7 +657,7 @@ TEST_CASE(SerializeEmptyArray) {
|
||||
Snapshot::kMessage, Isolate::Current());
|
||||
Array& serialized_array = Array::Handle();
|
||||
serialized_array ^= reader.ReadObject();
|
||||
EXPECT(array.Equals(serialized_array));
|
||||
EXPECT(array.CanonicalizeEquals(serialized_array));
|
||||
|
||||
// Read object back from the snapshot into a C structure.
|
||||
ApiNativeScope scope;
|
||||
|
||||
@@ -42,7 +42,7 @@ void FUNCTION_NAME(StackFrame_equals)(Dart_NativeArguments args) {
|
||||
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
||||
const Instance& expected = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
||||
const Instance& actual = Instance::CheckedHandle(arguments->NativeArgAt(1));
|
||||
if (!expected.Equals(actual)) {
|
||||
if (!expected.OperatorEquals(actual)) {
|
||||
OS::Print("expected: '%s' actual: '%s'\n",
|
||||
expected.ToCString(), actual.ToCString());
|
||||
FATAL("Expect_equals fails.\n");
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
EXPECT(!result.IsError()); \
|
||||
Instance& actual = Instance::Handle(); \
|
||||
actual ^= result.raw(); \
|
||||
EXPECT(actual.Equals(Instance::Handle(expected))); \
|
||||
EXPECT(actual.CanonicalizeEquals(Instance::Handle(expected))); \
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
EXPECT(!result.IsError()); \
|
||||
Instance& actual = Instance::Handle(); \
|
||||
actual ^= result.raw(); \
|
||||
EXPECT(actual.Equals(Instance::Handle(expected))); \
|
||||
EXPECT(actual.CanonicalizeEquals(Instance::Handle(expected))); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user