Index: runtime/vm/object.cc |
=================================================================== |
--- runtime/vm/object.cc (revision 34127) |
+++ runtime/vm/object.cc (working copy) |
@@ -3251,6 +3251,72 @@ |
} |
+intptr_t Class::NumCanonicalTypes() const { |
+ if (CanonicalType() != Type::null()) { |
+ return 1; |
+ } |
+ const Object& types = Object::Handle(canonical_types()); |
+ if (types.IsNull()) { |
+ return 0; |
+ } |
+ intptr_t num_types = Array::Cast(types).Length(); |
+ while ((num_types > 0) && |
+ (Array::Cast(types).At(num_types - 1) == Type::null())) { |
+ num_types--; |
+ } |
+ return num_types; |
+} |
+ |
+ |
+intptr_t Class::FindCanonicalTypeIndex(const Type& needle) const { |
+ Isolate* isolate = Isolate::Current(); |
+ if (EnsureIsFinalized(isolate) != Error::null()) { |
+ return -1; |
+ } |
+ if (needle.raw() == CanonicalType()) { |
+ return 0; |
+ } |
+ REUSABLE_OBJECT_HANDLESCOPE(isolate); |
+ Object& types = isolate->ObjectHandle(); |
+ types = canonical_types(); |
+ if (types.IsNull()) { |
+ return -1; |
+ } |
+ const intptr_t len = Array::Cast(types).Length(); |
+ REUSABLE_ABSTRACT_TYPE_HANDLESCOPE(isolate); |
+ AbstractType& type = isolate->AbstractTypeHandle(); |
+ for (intptr_t i = 0; i < len; i++) { |
+ type ^= Array::Cast(types).At(i); |
+ if (needle.raw() == type.raw()) { |
+ return i; |
+ } |
+ } |
+ // No type found. |
+ return -1; |
+} |
+ |
+ |
+RawType* Class::CanonicalTypeFromIndex(intptr_t idx) const { |
+ Type& type = Type::Handle(); |
+ if (idx == 0) { |
+ type = CanonicalType(); |
+ if (!type.IsNull()) { |
+ return type.raw(); |
+ } |
+ } |
+ Object& types = Object::Handle(canonical_types()); |
+ if (types.IsNull()) { |
+ return Type::null(); |
+ } |
+ if ((idx < 0) || (idx >= Array::Cast(types).Length())) { |
+ return Type::null(); |
+ } |
+ type ^= Array::Cast(types).At(idx); |
+ ASSERT(!type.IsNull()); |
+ return type.raw(); |
+} |
+ |
+ |
void Class::set_allocation_stub(const Code& value) const { |
ASSERT(!value.IsNull()); |
ASSERT(raw_ptr()->allocation_stub_ == Code::null()); |
@@ -3613,7 +3679,7 @@ |
Array& funcs = isolate->ArrayHandle(); |
funcs ^= functions(); |
ASSERT(!funcs.IsNull()); |
- intptr_t len = funcs.Length(); |
+ const intptr_t len = funcs.Length(); |
Function& function = isolate->FunctionHandle(); |
String& function_name = isolate->StringHandle(); |
for (intptr_t i = 0; i < len; i++) { |
@@ -3956,8 +4022,9 @@ |
intptr_t TypeArguments::Hash() const { |
if (IsNull()) return 0; |
- uword result = 0; |
const intptr_t num_types = Length(); |
+ if (IsRaw(0, num_types)) return 0; |
+ intptr_t result = 0; |
AbstractType& type = AbstractType::Handle(); |
for (intptr_t i = 0; i < num_types; i++) { |
type = TypeAt(i); |
@@ -4073,10 +4140,29 @@ |
} |
+bool TypeArguments::HasInstantiations() const { |
+ const Array& prior_instantiations = Array::Handle(instantiations()); |
+ ASSERT(prior_instantiations.Length() > 0); // Always at least a sentinel. |
+ return prior_instantiations.Length() > 1; |
+} |
+ |
+ |
+intptr_t TypeArguments::NumInstantiations() const { |
+ const Array& prior_instantiations = Array::Handle(instantiations()); |
+ ASSERT(prior_instantiations.Length() > 0); // Always at least a sentinel. |
+ intptr_t i = 0; |
+ while (prior_instantiations.At(i) != Smi::New(StubCode::kNoInstantiator)) { |
+ i += 2; |
+ } |
+ return i/2; |
+} |
+ |
+ |
RawArray* TypeArguments::instantiations() const { |
return raw_ptr()->instantiations_; |
} |
+ |
void TypeArguments::set_instantiations(const Array& value) const { |
ASSERT(!value.IsNull()); |
StorePointer(&raw_ptr()->instantiations_, value.raw()); |
@@ -4316,7 +4402,7 @@ |
ASSERT(!prior_instantiations.IsNull() && prior_instantiations.IsArray()); |
// The instantiations cache is initialized with Object::zero_array() and is |
// therefore guaranteed to contain kNoInstantiator. No length check needed. |
- ASSERT(prior_instantiations.Length() > 0); |
+ ASSERT(prior_instantiations.Length() > 0); // Always at least a sentinel. |
intptr_t index = 0; |
while (true) { |
if (prior_instantiations.At(index) == instantiator_type_arguments.raw()) { |
@@ -4335,6 +4421,9 @@ |
} |
// Instantiation did not result in bound error. Canonicalize type arguments. |
result = result.Canonicalize(); |
+ // InstantiateAndCanonicalizeFrom is not reentrant. It cannot have been called |
+ // indirectly, so the prior_instantiations array cannot have grown. |
+ ASSERT(prior_instantiations.raw() == instantiations()); |
// Add instantiator and result to instantiations array. |
intptr_t length = prior_instantiations.Length(); |
if ((index + 2) >= length) { |
@@ -4397,8 +4486,8 @@ |
static void GrowCanonicalTypeArguments(Isolate* isolate, const Array& table) { |
// Last element of the array is the number of used elements. |
- intptr_t table_size = table.Length() - 1; |
- intptr_t new_table_size = table_size * 2; |
+ const intptr_t table_size = table.Length() - 1; |
+ const intptr_t new_table_size = table_size * 2; |
Array& new_table = Array::Handle(isolate, Array::New(new_table_size + 1)); |
// Copy all elements from the original table to the newly allocated |
// array. |
@@ -4434,8 +4523,9 @@ |
table.SetAt(index, arguments); // Remember the new element. |
// Update used count. |
// Last element of the array is the number of used elements. |
- intptr_t table_size = table.Length() - 1; |
- intptr_t used_elements = Smi::Value(Smi::RawCast(table.At(table_size))) + 1; |
+ const intptr_t table_size = table.Length() - 1; |
+ const intptr_t used_elements = |
+ Smi::Value(Smi::RawCast(table.At(table_size))) + 1; |
const Smi& used = Smi::Handle(isolate, Smi::New(used_elements)); |
table.SetAt(table_size, used); |
@@ -4452,7 +4542,7 @@ |
const TypeArguments& arguments, |
intptr_t hash) { |
// Last element of the array is the number of used elements. |
- intptr_t table_size = table.Length() - 1; |
+ const intptr_t table_size = table.Length() - 1; |
ASSERT(Utils::IsPowerOfTwo(table_size)); |
intptr_t index = hash & (table_size - 1); |
@@ -4491,34 +4581,53 @@ |
ASSERT(IsOld()); |
return this->raw(); |
} |
+ const intptr_t num_types = Length(); |
+ if (IsRaw(0, num_types)) { |
+ return TypeArguments::null(); |
+ } |
Isolate* isolate = Isolate::Current(); |
ObjectStore* object_store = isolate->object_store(); |
- const Array& table = Array::Handle(isolate, |
- object_store->canonical_type_arguments()); |
- ASSERT(table.Length() > 0); |
- intptr_t index = FindIndexInCanonicalTypeArguments(isolate, |
- table, |
- *this, |
- Hash()); |
+ Array& table = Array::Handle(isolate, |
+ object_store->canonical_type_arguments()); |
+ // Last element of the array is the number of used elements. |
+ const intptr_t used_elements = |
+ Smi::Value(Smi::RawCast(table.At(table.Length() - 1))); |
+ const intptr_t hash = Hash(); |
+ intptr_t index = |
+ FindIndexInCanonicalTypeArguments(isolate, table, *this, hash); |
TypeArguments& result = TypeArguments::Handle(isolate); |
result ^= table.At(index); |
if (result.IsNull()) { |
// Canonicalize each type argument. |
- const intptr_t num_types = Length(); |
- AbstractType& type = AbstractType::Handle(isolate); |
+ AbstractType& type_arg = AbstractType::Handle(isolate); |
for (intptr_t i = 0; i < num_types; i++) { |
- type = TypeAt(i); |
- type = type.Canonicalize(trail); |
- SetTypeAt(i, type); |
+ type_arg = TypeAt(i); |
+ type_arg = type_arg.Canonicalize(trail); |
+ SetTypeAt(i, type_arg); |
} |
- // Make sure we have an old space object and add it to the table. |
- if (this->IsNew()) { |
- result ^= Object::Clone(*this, Heap::kOld); |
- } else { |
- result ^= this->raw(); |
+ // Canonicalization of a recursive type may change its hash. |
+ const intptr_t new_hash = Hash(); |
+ // Canonicalization of the type argument's own type arguments may add an |
+ // entry to the table, or even grow the table, and thereby change the |
+ // previously calculated index. |
+ table = object_store->canonical_type_arguments(); |
+ if ((new_hash != hash) || |
+ (Smi::Value(Smi::RawCast(table.At(table.Length() - 1))) |
+ != used_elements)) { |
+ index = |
+ FindIndexInCanonicalTypeArguments(isolate, table, *this, new_hash); |
+ result ^= table.At(index); |
} |
- ASSERT(result.IsOld()); |
- InsertIntoCanonicalTypeArguments(isolate, table, result, index); |
+ if (result.IsNull()) { |
+ // Make sure we have an old space object and add it to the table. |
+ if (this->IsNew()) { |
+ result ^= Object::Clone(*this, Heap::kOld); |
+ } else { |
+ result ^= this->raw(); |
+ } |
+ ASSERT(result.IsOld()); |
+ InsertIntoCanonicalTypeArguments(isolate, table, result, index); |
+ } |
} |
ASSERT(result.Equals(*this)); |
ASSERT(!result.IsNull()); |
@@ -8029,7 +8138,7 @@ |
if (!entry.IsNull()) { |
entry_name = entry.DictionaryName(); |
ASSERT(!entry_name.IsNull()); |
- intptr_t hash = entry_name.Hash(); |
+ const intptr_t hash = entry_name.Hash(); |
intptr_t index = hash % new_dict_size; |
new_entry = new_dict.At(index); |
while (!new_entry.IsNull()) { |
@@ -13135,7 +13244,7 @@ |
if (canonical_types.IsNull()) { |
canonical_types = empty_array().raw(); |
} |
- const intptr_t length = canonical_types.Length(); |
+ intptr_t length = canonical_types.Length(); |
// Linear search to see whether this type is already present in the |
// list of canonicalized types. |
// TODO(asiva): Try to re-factor this lookup code to make sharing |
@@ -13152,6 +13261,8 @@ |
} |
index++; |
} |
+ // The type was not found in the table. It is not canonical yet. |
+ |
// Canonicalize the type arguments. |
TypeArguments& type_args = TypeArguments::Handle(isolate, arguments()); |
// In case the type is first canonicalized at runtime, its type argument |
@@ -13159,6 +13270,26 @@ |
ASSERT(type_args.IsNull() || (type_args.Length() >= cls.NumTypeArguments())); |
type_args = type_args.Canonicalize(trail); |
set_arguments(type_args); |
+ |
+ // Canonicalizing the type arguments may have changed the index, may have |
+ // grown the table, or may even have canonicalized this type. |
+ canonical_types ^= cls.canonical_types(); |
+ if (canonical_types.IsNull()) { |
+ canonical_types = empty_array().raw(); |
+ } |
+ length = canonical_types.Length(); |
+ while (index < length) { |
+ type ^= canonical_types.At(index); |
+ if (type.IsNull()) { |
+ break; |
+ } |
+ ASSERT(type.IsFinalized()); |
+ if (this->Equals(type)) { |
+ return type.raw(); |
+ } |
+ index++; |
+ } |
+ |
// The type needs to be added to the list. Grow the list if it is full. |
if (index == length) { |
const intptr_t new_length = (length > 64) ? |
@@ -13270,7 +13401,7 @@ |
} |
if (type_arguments.IsNull()) { |
const char* format = "Type: class '%s'"; |
- intptr_t len = OS::SNPrint(NULL, 0, format, class_name) + 1; |
+ const intptr_t len = OS::SNPrint(NULL, 0, format, class_name) + 1; |
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); |
OS::SNPrint(chars, len, format, class_name); |
return chars; |
@@ -13416,7 +13547,7 @@ |
void TypeRef::AddOnlyBuddyToTrail(GrowableObjectArray** trail, |
- const Object& buddy) const { |
+ const Object& buddy) const { |
if (*trail == NULL) { |
*trail = &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New()); |
} else { |