Index: runtime/vm/object_reload.cc |
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc |
index b48c46e4f3becb3694bad160029d3a1e0e303b93..b41ff6c03d77b3fafe4bc3088882dfd47eac2141 100644 |
--- a/runtime/vm/object_reload.cc |
+++ b/runtime/vm/object_reload.cc |
@@ -204,18 +204,10 @@ class EnumMapTraits { |
// the ordering is always correct (i.e. enum indicies match slots in values |
// array) |
// 3) An existing enum value is removed. |
-// We leave old enum values that have no mapping to the reloaded class |
-// in the heap. This means that if a programmer does the following: |
-// enum Foo { A, B }; var x = Foo.A; |
-// *reload* |
-// enum Foo { B }; |
-// *reload* |
-// enum Foo { A, B }; expect(identical(x, Foo.A)); |
-// The program will fail because we were not able to pair Foo.A on the second |
-// reload. |
+// Each enum class has a canonical 'deleted' enum sentinel instance. |
+// When an enum value is deleted, we 'become' all references to the 'deleted' |
+// sentinel value. The index value is -1. |
// |
-// Deleted enum values still in the heap continue to function but their |
-// index field will not be valid. |
void Class::ReplaceEnum(const Class& old_enum) const { |
// We only do this for finalized enum classes. |
ASSERT(is_enum_class()); |
@@ -237,6 +229,10 @@ void Class::ReplaceEnum(const Class& old_enum) const { |
Instance& old_enum_values = Instance::Handle(zone); |
// The E.values array. |
Instance& enum_values = Instance::Handle(zone); |
+ // The E._deleted_enum_sentinel instance. |
+ Instance& old_deleted_enum_sentinel = Instance::Handle(zone); |
+ // The E._deleted_enum_sentinel instance. |
+ Instance& deleted_enum_sentinel = Instance::Handle(zone); |
Array& enum_map_storage = |
Array::Handle(zone, HashTables::New<UnorderedHashMap<EnumMapTraits> >(4)); |
ASSERT(!enum_map_storage.IsNull()); |
@@ -259,6 +255,11 @@ void Class::ReplaceEnum(const Class& old_enum) const { |
// Non-enum instance. |
continue; |
} |
+ if (enum_ident.Equals(Symbols::_DeletedEnumSentinel())) { |
+ old_deleted_enum_sentinel = field.StaticValue(); |
+ // Non-enum instance. |
+ continue; |
+ } |
old_enum_value = field.StaticValue(); |
ASSERT(!old_enum_value.IsNull()); |
VTIR_Print("Element %s being added to mapping\n", enum_ident.ToCString()); |
@@ -288,6 +289,11 @@ void Class::ReplaceEnum(const Class& old_enum) const { |
// Non-enum instance. |
continue; |
} |
+ if (enum_ident.Equals(Symbols::_DeletedEnumSentinel())) { |
+ deleted_enum_sentinel = field.StaticValue(); |
+ // Non-enum instance. |
+ continue; |
+ } |
enum_value = field.StaticValue(); |
ASSERT(!enum_value.IsNull()); |
old_enum_value ^= enum_map.GetOrNull(enum_ident); |
@@ -313,17 +319,29 @@ void Class::ReplaceEnum(const Class& old_enum) const { |
ASSERT(!enum_values.IsNull()); |
reload_context->AddEnumBecomeMapping(old_enum_values, enum_values); |
- if (enums_deleted && FLAG_trace_reload_verbose) { |
+ // Map the old E._deleted_enum_sentinel to the new E._deleted_enum_sentinel. |
+ ASSERT(!old_deleted_enum_sentinel.IsNull()); |
+ ASSERT(!deleted_enum_sentinel.IsNull()); |
+ reload_context->AddEnumBecomeMapping(old_deleted_enum_sentinel, |
+ deleted_enum_sentinel); |
+ |
+ if (enums_deleted) { |
+ // Map all deleted enums to the deleted enum senintel value. |
// TODO(johnmccutchan): Add this to the reload 'notices' list. |
VTIR_Print( |
- "The following enum values were deleted and are forever lost in " |
- "the heap:\n"); |
+ "The following enum values were deleted from %s and will become the " |
+ "deleted enum sentinel:\n", |
+ old_enum.ToCString()); |
UnorderedHashMap<EnumMapTraits> enum_map(enum_map_storage.raw()); |
UnorderedHashMap<EnumMapTraits>::Iterator it(&enum_map); |
while (it.MoveNext()) { |
const intptr_t entry = it.Current(); |
enum_ident = String::RawCast(enum_map.GetKey(entry)); |
ASSERT(!enum_ident.IsNull()); |
+ old_enum_value ^= enum_map.GetOrNull(enum_ident); |
+ VTIR_Print("Element `%s` was deleted\n", enum_ident.ToCString()); |
+ reload_context->AddEnumBecomeMapping(old_enum_value, |
+ deleted_enum_sentinel); |
} |
enum_map.Release(); |
} |