1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-08 12:06:26 +00:00

Reland "[VM/runtime] Fix computation of the hash of recursive types."

This is a reland of a528b19d2c

Updating the hash of the empty type argument vector in write-protected memory caused a crash in precomp mode.
The fix is to correctly initialize the hash so it does not need an update.

Original change's description:
> [VM/runtime] Fix computation of the hash of recursive types.
>
> When computing the hash of a type, only include the hashes of the type arguments corresponding to the type parameters of the type's class and not of the whole type argument vector, otherwise, the variable location of TypeRefs in the base class type arguments may result in different hashes.
>
> Add a regression test (this test was not failing in all configurations, since the issue becomes visible depending on the order in which types are loaded and finalized).
>
> This fixes issue https://github.com/dart-lang/sdk/issues/42954
>
> Small improvement: pick hash value 1 for a vector of dynamic types, so it can be cached and not recomputed repeatedly, which was the case with value 0.
>
> Change-Id: I3468952cbf61944a82cf8e00efbf4952bd831fbe
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/157563
> Reviewed-by: Alexander Markov <alexmarkov@google.com>
> Commit-Queue: Régis Crelier <regis@google.com>

Change-Id: I538b4538c69472e09b7bd745c6bd41023e1f154f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/157781
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Regis Crelier 2020-08-07 21:13:35 +00:00 committed by commit-bot@chromium.org
parent 2060964faa
commit f8d1d003c9
4 changed files with 79 additions and 13 deletions

View File

@ -1052,6 +1052,7 @@ void Object::Init(Isolate* isolate) {
Smi::New(0));
empty_type_arguments_->StoreSmi(&empty_type_arguments_->raw_ptr()->hash_,
Smi::New(0));
empty_type_arguments_->ComputeHash();
empty_type_arguments_->SetCanonical();
}
@ -4991,9 +4992,9 @@ void Class::set_declaration_type(const Type& value) const {
ASSERT(!value.IsNull() && value.IsCanonical() && value.IsOld());
ASSERT((declaration_type() == Object::null()) ||
(declaration_type() == value.raw())); // Set during own finalization.
// Since declaration type is used as the runtime type of instances of a
// non-generic class, the nullability is set to kNonNullable instead of
// kLegacy when the non-nullable experiment is enabled.
// Since DeclarationType is used as the runtime type of instances of a
// non-generic class, its nullability must be kNonNullable.
// The exception is DeclarationType of Null which is kNullable.
ASSERT(value.type_class_id() != kNullCid || value.IsNullable());
ASSERT(value.type_class_id() == kNullCid || value.IsNonNullable());
StorePointer(&raw_ptr()->declaration_type_, value.raw());
@ -5863,14 +5864,13 @@ void TypeArguments::set_nullability(intptr_t value) const {
StoreSmi(&raw_ptr()->nullability_, Smi::New(value));
}
intptr_t TypeArguments::ComputeHash() const {
if (IsNull()) return 0;
const intptr_t num_types = Length();
if (IsRaw(0, num_types)) return 0;
intptr_t TypeArguments::HashForRange(intptr_t from_index, intptr_t len) const {
if (IsNull()) return kAllDynamicHash;
if (IsRaw(from_index, len)) return kAllDynamicHash;
uint32_t result = 0;
AbstractType& type = AbstractType::Handle();
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
for (intptr_t i = 0; i < len; i++) {
type = TypeAt(from_index + i);
// The hash may be calculated during type finalization (for debugging
// purposes only) while a type argument is still temporarily null.
if (type.IsNull() || type.IsNullTypeRef()) {
@ -5879,7 +5879,16 @@ intptr_t TypeArguments::ComputeHash() const {
result = CombineHashes(result, type.Hash());
}
result = FinalizeHash(result, kHashBits);
SetHash(result);
return result;
}
intptr_t TypeArguments::ComputeHash() const {
if (IsNull()) return kAllDynamicHash;
const intptr_t num_types = Length();
const uint32_t result = HashForRange(0, num_types);
if (result != 0) {
SetHash(result);
}
return result;
}
@ -20303,7 +20312,7 @@ void Type::EnumerateURIs(URIs* uris) const {
intptr_t Type::ComputeHash() const {
ASSERT(IsFinalized());
uint32_t result = 1;
uint32_t result = 0;
result = CombineHashes(result, type_class_id());
// A legacy type should have the same hash as its non-nullable version to be
// consistent with the definition of type equality in Dart code.
@ -20312,7 +20321,20 @@ intptr_t Type::ComputeHash() const {
type_nullability = Nullability::kNonNullable;
}
result = CombineHashes(result, static_cast<uint32_t>(type_nullability));
result = CombineHashes(result, TypeArguments::Handle(arguments()).Hash());
uint32_t type_args_hash = TypeArguments::kAllDynamicHash;
if (arguments() != TypeArguments::null()) {
// Only include hashes of type arguments corresponding to type parameters.
// This prevents obtaining different hashes depending on the location of
// TypeRefs in the super class type argument vector.
const TypeArguments& type_args = TypeArguments::Handle(arguments());
const Class& cls = Class::Handle(type_class());
const intptr_t num_type_params = cls.NumTypeParameters();
if (num_type_params > 0) {
const intptr_t from_index = cls.NumTypeArguments() - num_type_params;
type_args_hash = type_args.HashForRange(from_index, num_type_params);
}
}
result = CombineHashes(result, type_args_hash);
if (IsFunctionType()) {
AbstractType& type = AbstractType::Handle();
const Function& sig_fun = Function::Handle(signature());

View File

@ -7528,6 +7528,9 @@ class TypeArguments : public Instance {
// architecture.
static const intptr_t kHashBits = 30;
// Hash value for a type argument vector consisting solely of dynamic types.
static const intptr_t kAllDynamicHash = 1;
intptr_t Length() const;
AbstractTypePtr TypeAt(intptr_t index) const;
AbstractTypePtr TypeAtNullSafe(intptr_t index) const;
@ -7742,6 +7745,7 @@ class TypeArguments : public Instance {
return 0;
}
intptr_t Hash() const;
intptr_t HashForRange(intptr_t from_index, intptr_t len) const;
static TypeArgumentsPtr New(intptr_t len, Heap::Space space = Heap::kOld);
@ -11326,7 +11330,7 @@ inline void TypeParameter::SetHash(intptr_t value) const {
}
inline intptr_t TypeArguments::Hash() const {
if (IsNull()) return 0;
if (IsNull()) return kAllDynamicHash;
intptr_t result = Smi::Value(raw_ptr()->hash_);
if (result != 0) {
return result;

View File

@ -0,0 +1,20 @@
// Copyright (c) 2020, 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 "package:expect/expect.dart";
class A<T> {
Type get type => T;
}
class B extends A<B> {}
void main() {
var first = B;
var second = B().type;
Expect.equals(first, second);
Expect.equals(second, first);
Expect.equals(first.hashCode, second.hashCode);
}

View File

@ -0,0 +1,20 @@
// Copyright (c) 2020, 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 "package:expect/expect.dart";
class A<T> {
Type get type => T;
}
class B extends A<B> {}
void main() {
var first = B;
var second = B().type;
Expect.equals(first, second);
Expect.equals(second, first);
Expect.equals(first.hashCode, second.hashCode);
}