mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:13:04 +00:00
[vm] runtimeType for record instances and record type equality
TEST=language/records/simple/runtime_type_test Issue: https://github.com/dart-lang/sdk/issues/49719 Change-Id: I031dff68241dfc62ebc3b6350b10ba7d352bab37 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/259621 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
a1e7743a0a
commit
ae2cebcee9
|
@ -81,8 +81,7 @@ DEFINE_NATIVE_ENTRY(Object_runtimeType, 0, 1) {
|
|||
return Type::IntType();
|
||||
} else if (instance.IsDouble()) {
|
||||
return Type::Double();
|
||||
} else if (instance.IsType() || instance.IsFunctionType() ||
|
||||
instance.IsRecordType()) {
|
||||
} else if (instance.IsAbstractType()) {
|
||||
return Type::DartTypeType();
|
||||
} else if (IsArrayClassId(instance.GetClassId())) {
|
||||
const auto& cls = Class::Handle(
|
||||
|
@ -147,8 +146,23 @@ static bool HaveSameRuntimeTypeHelper(Zone* zone,
|
|||
}
|
||||
|
||||
if (left_cid == kRecordCid) {
|
||||
// TODO(dartbug.com/49719)
|
||||
UNIMPLEMENTED();
|
||||
const auto& left_record = Record::Cast(left);
|
||||
const auto& right_record = Record::Cast(right);
|
||||
const intptr_t num_fields = left_record.num_fields();
|
||||
if ((num_fields != right_record.num_fields()) ||
|
||||
(left_record.field_names() != right_record.field_names())) {
|
||||
return false;
|
||||
}
|
||||
Instance& left_field = Instance::Handle(zone);
|
||||
Instance& right_field = Instance::Handle(zone);
|
||||
for (intptr_t i = 0; i < num_fields; ++i) {
|
||||
left_field ^= left_record.FieldAt(i);
|
||||
right_field ^= right_record.FieldAt(i);
|
||||
if (!HaveSameRuntimeTypeHelper(zone, left_field, right_field)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const Class& cls = Class::Handle(zone, left.clazz());
|
||||
|
@ -224,6 +238,26 @@ DEFINE_NATIVE_ENTRY(AbstractType_toString, 0, 1) {
|
|||
return type.UserVisibleName();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(AbstractType_getHashCode, 0, 1) {
|
||||
const AbstractType& type =
|
||||
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(0));
|
||||
intptr_t hash_val = type.Hash();
|
||||
ASSERT(hash_val > 0);
|
||||
ASSERT(Smi::IsValid(hash_val));
|
||||
return Smi::New(hash_val);
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(AbstractType_equality, 0, 2) {
|
||||
const AbstractType& type =
|
||||
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(0));
|
||||
const Instance& other =
|
||||
Instance::CheckedHandle(zone, arguments->NativeArgAt(1));
|
||||
if (type.ptr() == other.ptr()) {
|
||||
return Bool::True().ptr();
|
||||
}
|
||||
return Bool::Get(type.IsEquivalent(other, TypeEquality::kSyntactical)).ptr();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Type_getHashCode, 0, 1) {
|
||||
const Type& type = Type::CheckedHandle(zone, arguments->NativeArgAt(0));
|
||||
intptr_t hash_val = type.Hash();
|
||||
|
@ -242,26 +276,6 @@ DEFINE_NATIVE_ENTRY(Type_equality, 0, 2) {
|
|||
return Bool::Get(type.IsEquivalent(other, TypeEquality::kSyntactical)).ptr();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(FunctionType_getHashCode, 0, 1) {
|
||||
const FunctionType& type =
|
||||
FunctionType::CheckedHandle(zone, arguments->NativeArgAt(0));
|
||||
intptr_t hash_val = type.Hash();
|
||||
ASSERT(hash_val > 0);
|
||||
ASSERT(Smi::IsValid(hash_val));
|
||||
return Smi::New(hash_val);
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(FunctionType_equality, 0, 2) {
|
||||
const FunctionType& type =
|
||||
FunctionType::CheckedHandle(zone, arguments->NativeArgAt(0));
|
||||
const Instance& other =
|
||||
Instance::CheckedHandle(zone, arguments->NativeArgAt(1));
|
||||
if (type.ptr() == other.ptr()) {
|
||||
return Bool::True().ptr();
|
||||
}
|
||||
return Bool::Get(type.IsEquivalent(other, TypeEquality::kSyntactical)).ptr();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(LibraryPrefix_isLoaded, 0, 1) {
|
||||
const LibraryPrefix& prefix =
|
||||
LibraryPrefix::CheckedHandle(zone, arguments->NativeArgAt(0));
|
||||
|
|
|
@ -26,11 +26,11 @@ namespace dart {
|
|||
V(Function_apply, 2) \
|
||||
V(Closure_equals, 2) \
|
||||
V(Closure_computeHash, 1) \
|
||||
V(AbstractType_equality, 2) \
|
||||
V(AbstractType_getHashCode, 1) \
|
||||
V(AbstractType_toString, 1) \
|
||||
V(Type_getHashCode, 1) \
|
||||
V(Type_equality, 2) \
|
||||
V(FunctionType_getHashCode, 1) \
|
||||
V(FunctionType_equality, 2) \
|
||||
V(Type_getHashCode, 1) \
|
||||
V(LibraryPrefix_isLoaded, 1) \
|
||||
V(LibraryPrefix_setLoaded, 1) \
|
||||
V(LibraryPrefix_loadingUnit, 1) \
|
||||
|
|
|
@ -1324,7 +1324,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
|
|||
__ Bind(normal_ir_body);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ ldr(R0, Address(SP, 0 * target::kWordSize));
|
||||
__ ldr(R0, FieldAddress(R0, target::FunctionType::hash_offset()));
|
||||
|
@ -1333,7 +1333,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
|||
__ Bind(normal_ir_body); // Hash not yet computed.
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_equality(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_equality(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ ldm(IA, SP, (1 << R1 | 1 << R2));
|
||||
__ cmp(R1, Operand(R2));
|
||||
|
|
|
@ -1495,7 +1495,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
|
|||
__ Bind(normal_ir_body);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ ldr(R0, Address(SP, 0 * target::kWordSize));
|
||||
__ LoadCompressed(R0, FieldAddress(R0, target::FunctionType::hash_offset()));
|
||||
|
@ -1505,7 +1505,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
|||
__ Bind(normal_ir_body);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_equality(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_equality(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset));
|
||||
__ CompareObjectRegisters(R1, R2);
|
||||
|
|
|
@ -1460,7 +1460,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
|
|||
__ Bind(normal_ir_body);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ movl(EAX, Address(ESP, +1 * target::kWordSize)); // FunctionType object.
|
||||
__ movl(EAX, FieldAddress(EAX, target::FunctionType::hash_offset()));
|
||||
|
@ -1471,7 +1471,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
|||
// Hash not yet computed.
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_equality(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_equality(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ movl(EDI, Address(ESP, +1 * target::kWordSize));
|
||||
__ movl(EBX, Address(ESP, +2 * target::kWordSize));
|
||||
|
|
|
@ -1514,7 +1514,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
|
|||
__ Bind(normal_ir_body);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ lx(A0, Address(SP, 0 * target::kWordSize));
|
||||
__ LoadCompressed(A0, FieldAddress(A0, target::FunctionType::hash_offset()));
|
||||
|
@ -1524,7 +1524,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
|||
__ Bind(normal_ir_body);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_equality(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_equality(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ lx(A0, Address(SP, 1 * target::kWordSize));
|
||||
__ lx(A1, Address(SP, 0 * target::kWordSize));
|
||||
|
|
|
@ -1369,7 +1369,7 @@ void AsmIntrinsifier::Type_equality(Assembler* assembler,
|
|||
__ Bind(normal_ir_body);
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_getHashCode(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ movq(RAX, Address(RSP, +1 * target::kWordSize)); // FunctionType object.
|
||||
__ LoadCompressed(RAX,
|
||||
|
@ -1383,7 +1383,7 @@ void AsmIntrinsifier::FunctionType_getHashCode(Assembler* assembler,
|
|||
// Hash not yet computed.
|
||||
}
|
||||
|
||||
void AsmIntrinsifier::FunctionType_equality(Assembler* assembler,
|
||||
void AsmIntrinsifier::AbstractType_equality(Assembler* assembler,
|
||||
Label* normal_ir_body) {
|
||||
__ movq(RCX, Address(RSP, +1 * target::kWordSize));
|
||||
__ movq(RDX, Address(RSP, +2 * target::kWordSize));
|
||||
|
|
|
@ -362,10 +362,10 @@ namespace dart {
|
|||
OneByteString_substringUnchecked, 0x9b18195e) \
|
||||
V(_OneByteString, ==, OneByteString_equality, 0xb5003d69) \
|
||||
V(_TwoByteString, ==, TwoByteString_equality, 0xb5003d69) \
|
||||
V(_AbstractType, get:hashCode, AbstractType_getHashCode, 0x75e0d454) \
|
||||
V(_AbstractType, ==, AbstractType_equality, 0x465868ae) \
|
||||
V(_Type, get:hashCode, Type_getHashCode, 0x75e0d454) \
|
||||
V(_Type, ==, Type_equality, 0x465868ae) \
|
||||
V(_FunctionType, get:hashCode, FunctionType_getHashCode, 0x75e0d454) \
|
||||
V(_FunctionType, ==, FunctionType_equality, 0x465868ae) \
|
||||
V(::, _getHash, Object_getHash, 0xc60ff758) \
|
||||
|
||||
#define CORE_INTEGER_LIB_INTRINSIC_LIST(V) \
|
||||
|
|
|
@ -21284,6 +21284,23 @@ AbstractTypePtr Type::InstantiateFrom(
|
|||
return instantiated_type.NormalizeFutureOrType(space);
|
||||
}
|
||||
|
||||
// Certain built-in classes are treated as syntactically equivalent.
|
||||
static classid_t NormalizeClassIdForSyntacticalTypeEquality(classid_t cid) {
|
||||
if (IsIntegerClassId(cid)) {
|
||||
return Type::Handle(Type::IntType()).type_class_id();
|
||||
} else if (IsStringClassId(cid)) {
|
||||
return Type::Handle(Type::StringType()).type_class_id();
|
||||
} else if (cid == kDoubleCid) {
|
||||
return Type::Handle(Type::Double()).type_class_id();
|
||||
} else if (IsTypeClassId(cid)) {
|
||||
return Type::Handle(Type::DartTypeType()).type_class_id();
|
||||
} else if (IsArrayClassId(cid)) {
|
||||
return Class::Handle(IsolateGroup::Current()->object_store()->list_class())
|
||||
.id();
|
||||
}
|
||||
return cid;
|
||||
}
|
||||
|
||||
bool Type::IsEquivalent(const Instance& other,
|
||||
TypeEquality kind,
|
||||
TrailPtr trail) const {
|
||||
|
@ -21302,9 +21319,15 @@ bool Type::IsEquivalent(const Instance& other,
|
|||
return false;
|
||||
}
|
||||
const Type& other_type = Type::Cast(other);
|
||||
if (type_class_id() != other_type.type_class_id()) {
|
||||
const classid_t type_cid = type_class_id();
|
||||
const classid_t other_type_cid = other_type.type_class_id();
|
||||
if (type_cid != other_type_cid) {
|
||||
if ((kind != TypeEquality::kSyntactical) ||
|
||||
(NormalizeClassIdForSyntacticalTypeEquality(type_cid) !=
|
||||
NormalizeClassIdForSyntacticalTypeEquality(other_type_cid))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
if (!IsNullabilityEquivalent(thread, other_type, kind)) {
|
||||
|
|
|
@ -9,6 +9,16 @@
|
|||
abstract class _AbstractType implements Type {
|
||||
@pragma("vm:external-name", "AbstractType_toString")
|
||||
external String toString();
|
||||
|
||||
@pragma("vm:recognized", "asm-intrinsic")
|
||||
@pragma("vm:exact-result-type", "dart:core#_Smi")
|
||||
@pragma("vm:external-name", "AbstractType_getHashCode")
|
||||
external int get hashCode;
|
||||
|
||||
@pragma("vm:recognized", "asm-intrinsic")
|
||||
@pragma("vm:exact-result-type", bool)
|
||||
@pragma("vm:external-name", "AbstractType_equality")
|
||||
external bool operator ==(other);
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
|
@ -33,16 +43,6 @@ class _FunctionType extends _AbstractType {
|
|||
factory _FunctionType._uninstantiable() {
|
||||
throw "Unreachable";
|
||||
}
|
||||
|
||||
@pragma("vm:recognized", "asm-intrinsic")
|
||||
@pragma("vm:exact-result-type", "dart:core#_Smi")
|
||||
@pragma("vm:external-name", "FunctionType_getHashCode")
|
||||
external int get hashCode;
|
||||
|
||||
@pragma("vm:recognized", "asm-intrinsic")
|
||||
@pragma("vm:exact-result-type", bool)
|
||||
@pragma("vm:external-name", "FunctionType_equality")
|
||||
external bool operator ==(other);
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
|
|
42
tests/language/records/simple/runtime_type_test.dart
Normal file
42
tests/language/records/simple/runtime_type_test.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code as governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// SharedOptions=--enable-experiment=records
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
Object getType1(Object obj) => obj.runtimeType;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
Object getType2<T>() => T;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
void testRuntimeTypeEquality(bool expected, Object a, Object b) {
|
||||
bool result1 = getType1(a) == getType1(b);
|
||||
Expect.equals(expected, result1);
|
||||
// Test optimized 'a.runtimeType == b.runtimeType' pattern.
|
||||
bool result2 = a.runtimeType == b.runtimeType;
|
||||
Expect.equals(expected, result2);
|
||||
}
|
||||
|
||||
main() {
|
||||
Expect.equals(getType1(true), bool);
|
||||
Expect.equals(getType1(false), getType2<bool>());
|
||||
Expect.equals(getType1(1), getType2<int>());
|
||||
Expect.equals(getType1((true, 3)), getType2<(bool, int)>());
|
||||
Expect.equals(getType1((foo: true, bar: 2)), getType2<({int bar, bool foo})>());
|
||||
Expect.equals(getType1((1, foo: true, false, bar: 2)), getType2<(int, bool, {int bar, bool foo})>());
|
||||
|
||||
testRuntimeTypeEquality(true, (1, 2), (3, 4));
|
||||
testRuntimeTypeEquality(false, (1, 2), (1, 2, 3));
|
||||
testRuntimeTypeEquality(false, (1, 2), (1, false));
|
||||
testRuntimeTypeEquality(true, (1, 2, foo: true), (foo: false, 5, 4));
|
||||
testRuntimeTypeEquality(false, (1, 2, foo: true), (bar: false, 5, 4));
|
||||
testRuntimeTypeEquality(true, (foo: 1, bar: 2), (bar: 3, foo: 4));
|
||||
testRuntimeTypeEquality(false, (foo: 1, bar: 2), (1, foo: 2));
|
||||
testRuntimeTypeEquality(false, (1, 2), 3);
|
||||
testRuntimeTypeEquality(false, (1, 2), 'hey');
|
||||
testRuntimeTypeEquality(false, (1, 2), [1, 2]);
|
||||
}
|
Loading…
Reference in a new issue