Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(285)

Unified Diff: runtime/vm/object.cc

Issue 109593003: Use a trail instead of a mark bit when processing recursive types in the VM (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/object.h ('k') | runtime/vm/raw_object.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/object.cc
===================================================================
--- runtime/vm/object.cc (revision 31568)
+++ runtime/vm/object.cc (working copy)
@@ -3595,7 +3595,7 @@
}
-bool AbstractTypeArguments::IsInstantiated() const {
+bool AbstractTypeArguments::IsInstantiated(GrowableObjectArray* trail) const {
// AbstractTypeArguments is an abstract class.
UNREACHABLE();
return false;
@@ -3673,7 +3673,8 @@
}
-bool AbstractTypeArguments::Equals(const AbstractTypeArguments& other) const {
+bool AbstractTypeArguments::IsEquivalent(const AbstractTypeArguments& other,
+ GrowableObjectArray* trail) const {
if (this->raw() == other.raw()) {
return true;
}
@@ -3689,7 +3690,7 @@
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
other_type = other.TypeAt(i);
- if (!type.Equals(other_type)) {
+ if (!type.IsEquivalent(other_type, trail)) {
return false;
}
}
@@ -3699,7 +3700,8 @@
RawAbstractTypeArguments* AbstractTypeArguments::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ GrowableObjectArray* trail) const {
// AbstractTypeArguments is an abstract class.
UNREACHABLE();
return NULL;
@@ -3808,13 +3810,13 @@
}
-bool TypeArguments::IsInstantiated() const {
+bool TypeArguments::IsInstantiated(GrowableObjectArray* trail) const {
AbstractType& type = AbstractType::Handle();
const intptr_t num_types = Length();
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
ASSERT(!type.IsNull());
- if (!type.IsInstantiated()) {
+ if (!type.IsInstantiated(trail)) {
return false;
}
}
@@ -3964,7 +3966,8 @@
RawAbstractTypeArguments* TypeArguments::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ GrowableObjectArray* trail) const {
ASSERT(!IsInstantiated());
if (!instantiator_type_arguments.IsNull() &&
IsUninstantiatedIdentity() &&
@@ -3978,7 +3981,9 @@
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
if (!type.IsInstantiated()) {
- type = type.InstantiateFrom(instantiator_type_arguments, bound_error);
+ type = type.InstantiateFrom(instantiator_type_arguments,
+ bound_error,
+ trail);
}
instantiated_array.SetTypeAt(i, type);
}
@@ -4109,7 +4114,8 @@
}
-RawAbstractTypeArguments* TypeArguments::Canonicalize() const {
+RawAbstractTypeArguments* TypeArguments::Canonicalize(
+ GrowableObjectArray* trail) const {
if (IsNull() || IsCanonical()) {
ASSERT(IsOld());
return this->raw();
@@ -4131,7 +4137,7 @@
AbstractType& type = AbstractType::Handle(isolate);
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
- type = type.Canonicalize();
+ type = type.Canonicalize(trail);
SetTypeAt(i, type);
}
// Make sure we have an old space object and add it to the table.
@@ -4205,7 +4211,8 @@
}
-RawAbstractTypeArguments* InstantiatedTypeArguments::Canonicalize() const {
+RawAbstractTypeArguments* InstantiatedTypeArguments::Canonicalize(
+ GrowableObjectArray* trail) const {
const intptr_t num_types = Length();
const TypeArguments& type_args = TypeArguments::Handle(
TypeArguments::New(num_types, Heap::kOld));
@@ -4214,7 +4221,7 @@
type = TypeAt(i);
type_args.SetTypeAt(i, type);
}
- return type_args.Canonicalize();
+ return type_args.Canonicalize(trail);
}
@@ -11820,7 +11827,7 @@
}
-bool AbstractType::IsInstantiated() const {
+bool AbstractType::IsInstantiated(GrowableObjectArray* trail) const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
@@ -11875,7 +11882,8 @@
}
-bool AbstractType::Equals(const Instance& other) const {
+bool AbstractType::IsEquivalent(const Instance& other,
+ GrowableObjectArray* trail) const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
@@ -11884,7 +11892,8 @@
RawAbstractType* AbstractType::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ GrowableObjectArray* trail) const {
// AbstractType is an abstract class.
UNREACHABLE();
return NULL;
@@ -11898,7 +11907,7 @@
}
-RawAbstractType* AbstractType::Canonicalize() const {
+RawAbstractType* AbstractType::Canonicalize(GrowableObjectArray* trail) const {
// AbstractType is an abstract class.
UNREACHABLE();
return NULL;
@@ -11914,7 +11923,7 @@
}
String& type_name = String::Handle(type.BuildName(kInternalName));
type_name = String::Concat(type_name, Symbols::SpaceExtendsSpace());
- // Building the bound name may lead into cycles.
+ // Build the bound name without causing divergence.
const AbstractType& bound = AbstractType::Handle(
BoundedType::Cast(*this).bound());
String& bound_name = String::Handle();
@@ -11975,11 +11984,11 @@
}
if (cls.IsSignatureClass()) {
// We may be reporting an error about a malformed function type. In that
- // case, avoid instantiating the signature, since it may lead to cycles.
+ // case, avoid instantiating the signature, since it may cause divergence.
if (!IsFinalized() || IsBeingFinalized() || IsMalformed()) {
return class_name.raw();
}
- // In order to avoid cycles, print the name of a typedef (non-canonical
+ // To avoid divergence, print the name of a typedef (non-canonical
// signature class) as a regular, possibly parameterized, class.
if (cls.IsCanonicalSignatureClass()) {
const Function& signature_function = Function::Handle(
@@ -12384,7 +12393,7 @@
}
-bool Type::IsInstantiated() const {
+bool Type::IsInstantiated(GrowableObjectArray* trail) const {
if (raw_ptr()->type_state_ == RawType::kFinalizedInstantiated) {
return true;
}
@@ -12393,13 +12402,14 @@
}
const AbstractTypeArguments& args =
AbstractTypeArguments::Handle(arguments());
- return args.IsNull() || args.IsInstantiated();
+ return args.IsNull() || args.IsInstantiated(trail);
}
RawAbstractType* Type::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ GrowableObjectArray* trail) const {
ASSERT(IsFinalized() || IsBeingFinalized());
ASSERT(!IsInstantiated());
// Return the uninstantiated type unchanged if malformed. No copy needed.
@@ -12409,7 +12419,8 @@
AbstractTypeArguments& type_arguments =
AbstractTypeArguments::Handle(arguments());
type_arguments = type_arguments.InstantiateFrom(instantiator_type_arguments,
- bound_error);
+ bound_error,
+ trail);
// Note that the type class has to be resolved at this time, but not
// necessarily finalized yet. We may be checking bounds at compile time.
const Class& cls = Class::Handle(type_class());
@@ -12424,14 +12435,18 @@
}
-bool Type::Equals(const Instance& other) const {
+bool Type::IsEquivalent(const Instance& other,
+ GrowableObjectArray* trail) const {
ASSERT(!IsNull());
if (raw() == other.raw()) {
return true;
}
if (other.IsTypeRef()) {
- // TODO(regis): Use trail. For now, we "unfold" the right hand type.
- return Equals(AbstractType::Handle(TypeRef::Cast(other).type()));
+ // Unfold right hand type. Divergence is controlled by left hand type.
+ const AbstractType& other_ref_type = AbstractType::Handle(
+ TypeRef::Cast(other).type());
+ ASSERT(!other_ref_type.IsTypeRef());
+ return IsEquivalent(other_ref_type, trail);
}
if (!other.IsType()) {
return false;
@@ -12475,7 +12490,7 @@
for (intptr_t i = 0; i < num_type_params; i++) {
type_arg = type_args.TypeAt(from_index + i);
other_type_arg = other_type_args.TypeAt(from_index + i);
- if (!type_arg.Equals(other_type_arg)) {
+ if (!type_arg.IsEquivalent(other_type_arg, trail)) {
return false;
}
}
@@ -12497,7 +12512,7 @@
}
-RawAbstractType* Type::Canonicalize() const {
+RawAbstractType* Type::Canonicalize(GrowableObjectArray* trail) const {
ASSERT(IsFinalized());
if (IsCanonical() || IsMalformed()) {
ASSERT(IsMalformed() || AbstractTypeArguments::Handle(arguments()).IsOld());
@@ -12548,7 +12563,7 @@
AbstractTypeArguments& type_args =
AbstractTypeArguments::Handle(isolate, arguments());
ASSERT(type_args.IsNull() || (type_args.Length() == cls.NumTypeArguments()));
- type_args = type_args.Canonicalize();
+ type_args = type_args.Canonicalize(trail);
set_arguments(type_args);
// The type needs to be added to the list. Grow the list if it is full.
if (index == canonical_types_len) {
@@ -12687,53 +12702,44 @@
}
-bool TypeRef::IsInstantiated() const {
- if (is_being_checked()) {
+bool TypeRef::IsInstantiated(GrowableObjectArray* trail) const {
+ if (TestAndAddToTrail(&trail)) {
return true;
}
- set_is_being_checked(true);
- const bool result = AbstractType::Handle(type()).IsInstantiated();
- set_is_being_checked(false);
- return result;
+ return AbstractType::Handle(type()).IsInstantiated(trail);
}
-bool TypeRef::Equals(const Instance& other) const {
- // TODO(regis): Use trail instead of mark bit.
+bool TypeRef::IsEquivalent(const Instance& other,
+ GrowableObjectArray* trail) const {
if (raw() == other.raw()) {
return true;
}
- if (is_being_checked()) {
+ if (TestAndAddBuddyToTrail(&trail, other)) {
return true;
}
- set_is_being_checked(true);
- const bool result = AbstractType::Handle(type()).Equals(other);
- set_is_being_checked(false);
- return result;
+ return AbstractType::Handle(type()).IsEquivalent(other, trail);
}
RawAbstractType* TypeRef::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ GrowableObjectArray* trail) const {
+ TypeRef& instantiated_type_ref = TypeRef::Handle();
+ instantiated_type_ref ^= OnlyBuddyInTrail(trail);
+ if (!instantiated_type_ref.IsNull()) {
+ return instantiated_type_ref.raw();
+ }
+ instantiated_type_ref = TypeRef::New(Type::Handle(Type::DynamicType()));
+ AddOnlyBuddyToTrail(&trail, instantiated_type_ref);
const AbstractType& ref_type = AbstractType::Handle(type());
- // TODO(regis): Use trail instead of mark bit plus temporary redirection,
- // because it could be marked for another reason.
- if (is_being_checked()) {
- ASSERT(ref_type.IsTypeRef());
- return ref_type.raw();
- }
- set_is_being_checked(true);
ASSERT(!ref_type.IsTypeRef());
- const TypeRef& instantiated_type_ref = TypeRef::Handle(
- TypeRef::New(ref_type));
- // TODO(regis): instantiated_type_ref should be stored in the trail instead.
- set_type(instantiated_type_ref);
const AbstractType& instantiated_ref_type = AbstractType::Handle(
- ref_type.InstantiateFrom(instantiator_type_arguments, bound_error));
+ ref_type.InstantiateFrom(instantiator_type_arguments,
+ bound_error,
+ trail));
instantiated_type_ref.set_type(instantiated_ref_type);
- set_type(ref_type);
- set_is_being_checked(false);
return instantiated_type_ref.raw();
}
@@ -12744,43 +12750,92 @@
}
-void TypeRef::set_is_being_checked(bool value) const {
- raw_ptr()->is_being_checked_ = value;
-}
-
-
-// This function only canonicalizes the referenced type, but not the TypeRef
-// itself, since it cannot be canonical by definition.
+// A TypeRef cannot be canonical by definition. Only its referenced type can be.
// Consider the type Derived, where class Derived extends Base<Derived>.
// The first type argument of its flattened type argument vector is Derived,
// i.e. itself, but pointer equality is not possible.
-RawAbstractType* TypeRef::Canonicalize() const {
- // TODO(regis): Use trail, not mark bit.
- if (is_being_checked()) {
+RawAbstractType* TypeRef::Canonicalize(GrowableObjectArray* trail) const {
+ if (TestAndAddToTrail(&trail)) {
return raw();
}
- set_is_being_checked(true);
AbstractType& ref_type = AbstractType::Handle(type());
- ASSERT(!ref_type.IsTypeRef());
- ref_type = ref_type.Canonicalize();
+ ref_type = ref_type.Canonicalize(trail);
set_type(ref_type);
- // No need to call SetCanonical(), since a TypeRef cannot be canonical by
- // definition.
- set_is_being_checked(false);
- // We return the referenced type instead of the TypeRef in order to provide
- // pointer equality in simple cases, e.g. in language/f_bounded_equality_test.
- return ref_type.raw();
+ return raw();
}
intptr_t TypeRef::Hash() const {
- // TODO(regis): Use trail and hash of referenced type.
- // Do not calculate the hash of the referenced type to avoid cycles.
+ // Do not calculate the hash of the referenced type to avoid divergence.
uword result = Class::Handle(AbstractType::Handle(type()).type_class()).id();
return FinalizeHash(result);
}
+bool TypeRef::TestAndAddToTrail(GrowableObjectArray** trail) const {
+ if (*trail == NULL) {
+ *trail = &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New());
+ } else {
+ const intptr_t len = (*trail)->Length();
+ for (intptr_t i = 0; i < len; i++) {
+ if ((*trail)->At(i) == this->raw()) {
+ return true;
+ }
+ }
+ }
+ (*trail)->Add(*this);
+ return false;
+}
+
+
+bool TypeRef::TestAndAddBuddyToTrail(GrowableObjectArray** trail,
+ const Object& buddy) const {
+ if (*trail == NULL) {
+ *trail = &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New());
+ } else {
+ const intptr_t len = (*trail)->Length();
+ ASSERT((len % 2) == 0);
+ for (intptr_t i = 0; i < len; i += 2) {
+ if (((*trail)->At(i) == this->raw()) &&
+ ((*trail)->At(i + 1) == buddy.raw())) {
+ return true;
+ }
+ }
+ }
+ (*trail)->Add(*this);
+ (*trail)->Add(buddy);
+ return false;
+}
+
+
+RawObject* TypeRef::OnlyBuddyInTrail(GrowableObjectArray* trail) const {
+ if (trail == NULL) {
+ return Object::null();
+ }
+ const intptr_t len = trail->Length();
+ ASSERT((len % 2) == 0);
+ for (intptr_t i = 0; i < len; i += 2) {
+ if (trail->At(i) == this->raw()) {
+ ASSERT(trail->At(i + 1) != Object::null());
+ return trail->At(i + 1);
+ }
+ }
+ return Object::null();
+}
+
+
+void TypeRef::AddOnlyBuddyToTrail(GrowableObjectArray** trail,
+ const Object& buddy) const {
+ if (*trail == NULL) {
+ *trail = &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New());
+ } else {
+ ASSERT(OnlyBuddyInTrail(*trail) == Object::null());
+ }
+ (*trail)->Add(*this);
+ (*trail)->Add(buddy);
+}
+
+
RawTypeRef* TypeRef::New() {
ASSERT(Isolate::Current()->object_store()->type_ref_class() != Class::null());
RawObject* raw = Object::Allocate(TypeRef::kClassId,
@@ -12793,7 +12848,6 @@
RawTypeRef* TypeRef::New(const AbstractType& type) {
const TypeRef& result = TypeRef::Handle(TypeRef::New());
result.set_type(type);
- result.set_is_being_checked(false);
return result.raw();
}
@@ -12822,13 +12876,17 @@
}
-bool TypeParameter::Equals(const Instance& other) const {
+bool TypeParameter::IsEquivalent(const Instance& other,
+ GrowableObjectArray* trail) const {
if (raw() == other.raw()) {
return true;
}
if (other.IsTypeRef()) {
- // TODO(regis): Use trail. For now, we "unfold" the right hand type.
- return Equals(AbstractType::Handle(TypeRef::Cast(other).type()));
+ // Unfold right hand type. Divergence is controlled by left hand type.
+ const AbstractType& other_ref_type = AbstractType::Handle(
+ TypeRef::Cast(other).type());
+ ASSERT(!other_ref_type.IsTypeRef());
+ return IsEquivalent(other_ref_type, trail);
}
if (!other.IsTypeParameter()) {
return false;
@@ -12869,7 +12927,8 @@
RawAbstractType* TypeParameter::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ GrowableObjectArray* trail) const {
ASSERT(IsFinalized());
if (instantiator_type_arguments.IsNull()) {
return Type::DynamicType();
@@ -12880,17 +12939,6 @@
// type arguments are canonicalized at type finalization time. It would be too
// early to canonicalize the returned type argument here, since instantiation
// not only happens at run time, but also during type finalization.
- // However, if the type argument is a reference to a canonical type, we
- // return the referenced canonical type instead of the type reference to
- // provide pointer equality in some simple cases, e.g. in
- // language/f_bounded_equality_test.
- if (type_arg.IsTypeRef()) {
- const AbstractType& ref_type = AbstractType::Handle(
- TypeRef::Cast(type_arg).type());
- if (ref_type.IsCanonical()) {
- return ref_type.raw();
- }
- }
return type_arg.raw();
}
@@ -12957,7 +13005,8 @@
intptr_t TypeParameter::Hash() const {
ASSERT(IsFinalized());
uword result = Class::Handle(parameterized_class()).id();
- // Do not include the hash of the bound, which could lead to cycles.
+ // No need to include the hash of the bound, since the type parameter is fully
+ // identified by its class and index.
result <<= index();
return FinalizeHash(result);
}
@@ -13041,35 +13090,37 @@
}
-bool BoundedType::Equals(const Instance& other) const {
+bool BoundedType::IsEquivalent(const Instance& other,
+ GrowableObjectArray* trail) const {
// BoundedType are not canonicalized, because their bound may get finalized
// after the BoundedType is created and initialized.
if (raw() == other.raw()) {
return true;
}
if (other.IsTypeRef()) {
- // TODO(regis): Use trail. For now, we "unfold" the right hand type.
- return Equals(AbstractType::Handle(TypeRef::Cast(other).type()));
+ // Unfold right hand type. Divergence is controlled by left hand type.
+ const AbstractType& other_ref_type = AbstractType::Handle(
+ TypeRef::Cast(other).type());
+ ASSERT(!other_ref_type.IsTypeRef());
+ return IsEquivalent(other_ref_type, trail);
}
if (!other.IsBoundedType()) {
return false;
}
const BoundedType& other_bounded = BoundedType::Cast(other);
if (type_parameter() != other_bounded.type_parameter()) {
- // Not a structural compare.
- // Note that a deep comparison of bounds could lead to cycles.
return false;
}
const AbstractType& this_type = AbstractType::Handle(type());
const AbstractType& other_type = AbstractType::Handle(other_bounded.type());
- if (!this_type.Equals(other_type)) {
+ if (!this_type.IsEquivalent(other_type, trail)) {
return false;
}
const AbstractType& this_bound = AbstractType::Handle(bound());
const AbstractType& other_bound = AbstractType::Handle(other_bounded.bound());
return this_bound.IsFinalized() &&
other_bound.IsFinalized() &&
- this_bound.Equals(other_bound);
+ this_bound.Equals(other_bound); // Different graph, do not pass trail.
}
@@ -13095,31 +13146,25 @@
}
-void BoundedType::set_is_being_checked(bool value) const {
- raw_ptr()->is_being_checked_ = value;
-}
-
-
RawAbstractType* BoundedType::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments,
- Error* bound_error) const {
+ Error* bound_error,
+ GrowableObjectArray* trail) const {
ASSERT(IsFinalized());
AbstractType& bounded_type = AbstractType::Handle(type());
if (!bounded_type.IsInstantiated()) {
bounded_type = bounded_type.InstantiateFrom(instantiator_type_arguments,
- bound_error);
+ bound_error,
+ trail);
}
- if (FLAG_enable_type_checks &&
- bound_error->IsNull() &&
- !is_being_checked()) {
- // Avoid endless recursion while checking and instantiating bound.
- set_is_being_checked(true);
+ if (FLAG_enable_type_checks && bound_error->IsNull()) {
AbstractType& upper_bound = AbstractType::Handle(bound());
ASSERT(!upper_bound.IsObjectType() && !upper_bound.IsDynamicType());
const TypeParameter& type_param = TypeParameter::Handle(type_parameter());
if (!upper_bound.IsInstantiated()) {
upper_bound = upper_bound.InstantiateFrom(instantiator_type_arguments,
- bound_error);
+ bound_error,
+ trail);
}
if (bound_error->IsNull()) {
if (!type_param.CheckBound(bounded_type, upper_bound, bound_error) &&
@@ -13132,7 +13177,6 @@
bounded_type = BoundedType::New(bounded_type, upper_bound, type_param);
}
}
- set_is_being_checked(false);
}
return bounded_type.raw();
}
@@ -13155,7 +13199,8 @@
intptr_t BoundedType::Hash() const {
uword result = AbstractType::Handle(type()).Hash();
- // Do not include the hash of the bound, which could lead to cycles.
+ // No need to include the hash of the bound, since the bound is defined by the
+ // type parameter (modulo instantiation state).
result += TypeParameter::Handle(type_parameter()).Hash();
return FinalizeHash(result);
}
@@ -13178,7 +13223,6 @@
result.set_type(type);
result.set_bound(bound);
result.set_type_parameter(type_parameter);
- result.set_is_being_checked(false);
return result.raw();
}
« no previous file with comments | « runtime/vm/object.h ('k') | runtime/vm/raw_object.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698