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

Unified Diff: runtime/vm/object_reload.cc

Issue 2153143002: Rework how enums are implemented and reloaded (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 5 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.cc ('k') | runtime/vm/parser.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/object_reload.cc
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
index 9b2e3e83e3328e2da5a1729c6e97b4ffb7bdf01f..838e9b8375b3d4d6850ced7752f6c8bfc5bfc4c9 100644
--- a/runtime/vm/object_reload.cc
+++ b/runtime/vm/object_reload.cc
@@ -4,6 +4,7 @@
#include "vm/object.h"
+#include "vm/hash_table.h"
#include "vm/isolate_reload.h"
#include "vm/log.h"
#include "vm/resolver.h"
@@ -14,6 +15,7 @@ namespace dart {
#ifndef PRODUCT
DECLARE_FLAG(bool, trace_reload);
+DECLARE_FLAG(bool, trace_reload_verbose);
DECLARE_FLAG(bool, two_args_smi_icd);
#define IRC (Isolate::Current()->reload_context())
@@ -141,6 +143,8 @@ void Class::CopyStaticFieldValues(const Class& old_cls) const {
void Class::CopyCanonicalConstants(const Class& old_cls) const {
if (is_enum_class()) {
+ // We do not copy enum classes's canonical constants because we explicitly
+ // become the old enum values to the new enum values.
return;
}
#if defined(DEBUG)
@@ -171,31 +175,47 @@ void Class::CopyCanonicalType(const Class& old_cls) const {
}
-static intptr_t IndexOfEnum(const Array& enum_names, const String& name) {
- ASSERT(!enum_names.IsNull());
- ASSERT(!name.IsNull());
- String& enum_name = String::Handle();
- for (intptr_t i = 0; i < enum_names.Length(); i++) {
- enum_name = String::RawCast(enum_names.At(i));
- ASSERT(!enum_name.IsNull());
- if (enum_name.Equals(name)) {
- return i;
- }
- }
-
- return -1;
-}
+class EnumMapTraits {
+ public:
+ static bool ReportStats() { return false; }
+ static const char* Name() { return "EnumMapTraits"; }
+ static bool IsMatch(const Object& a, const Object& b) {
+ return a.raw() == b.raw();
+ }
-static void UpdateEnumIndex(const Instance& enum_value,
- const Field& enum_index_field,
- const intptr_t index) {
- enum_value.SetField(enum_index_field, Smi::Handle(Smi::New(index)));
-}
+ static uword Hash(const Object& obj) {
+ ASSERT(obj.IsString());
+ return String::Cast(obj).Hash();
+ }
+};
-// TODO(johnmccutchan): The code in the class finalizer canonicalizes all
-// instances and the values array. We probably should do the same thing.
+// Given an old enum class, add become mappings from old values to new values.
+// Some notes about how we reload enums below:
+//
+// When an enum is reloaded the following three things can happen, possibly
+// simultaneously.
+//
+// 1) A new enum value is added.
+// This case is handled automatically.
+// 2) Enum values are reordered.
+// We pair old and new enums and the old enums 'become' the new ones so
+// 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.
+//
+// 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());
@@ -204,160 +224,98 @@ void Class::ReplaceEnum(const Class& old_enum) const {
ASSERT(old_enum.is_finalized());
Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
IsolateReloadContext* reload_context = Isolate::Current()->reload_context();
ASSERT(reload_context != NULL);
- TIR_Print("ReplaceEnum `%s` (%" Pd " and %" Pd ")\n",
- ToCString(), id(), old_enum.id());
-
- // Grab '_enum_names' from |old_enum|.
- const Field& old_enum_names_field = Field::Handle(
- old_enum.LookupStaticFieldAllowPrivate(Symbols::_EnumNames()));
- ASSERT(!old_enum_names_field.IsNull());
- const Array& old_enum_names =
- Array::Handle(Array::RawCast(old_enum_names_field.StaticValue()));
- ASSERT(!old_enum_names.IsNull());
-
- // Grab 'values' from |old_enum|.
- const Field& old_enum_values_field = Field::Handle(
- old_enum.LookupStaticFieldAllowPrivate(Symbols::Values()));
- ASSERT(!old_enum_values_field.IsNull());
- const Array& old_enum_values =
- Array::Handle(Array::RawCast(old_enum_values_field.StaticValue()));
- ASSERT(!old_enum_values.IsNull());
-
- // Grab _enum_names from |this|.
- const Field& enum_names_field = Field::Handle(
- LookupStaticFieldAllowPrivate(Symbols::_EnumNames()));
- ASSERT(!enum_names_field.IsNull());
- Array& enum_names =
- Array::Handle(Array::RawCast(enum_names_field.StaticValue()));
- ASSERT(!enum_names.IsNull());
-
- // Grab values from |this|.
- const Field& enum_values_field = Field::Handle(
- LookupStaticFieldAllowPrivate(Symbols::Values()));
- ASSERT(!enum_values_field.IsNull());
- Array& enum_values =
- Array::Handle(Array::RawCast(enum_values_field.StaticValue()));
- ASSERT(!enum_values.IsNull());
-
- // Grab the |index| field.
- const Field& index_field =
- Field::Handle(old_enum.LookupInstanceField(Symbols::Index()));
- ASSERT(!index_field.IsNull());
-
- // Build list of enum from |old_enum| that aren't present in |this|.
- // This array holds pairs: (name, value).
- const GrowableObjectArray& to_add =
- GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld));
- const String& enum_class_name = String::Handle(UserVisibleName());
- String& enum_name = String::Handle();
- String& enum_field_name = String::Handle();
- Object& enum_value = Object::Handle();
- Field& enum_field = Field::Handle();
-
- TIR_Print("New version of enum has %" Pd " elements\n",
- enum_values.Length());
- TIR_Print("Old version of enum had %" Pd " elements\n",
- old_enum_values.Length());
-
- for (intptr_t i = 0; i < old_enum_names.Length(); i++) {
- enum_name = String::RawCast(old_enum_names.At(i));
- const intptr_t index_in_new_cls = IndexOfEnum(enum_names, enum_name);
- if (index_in_new_cls < 0) {
- // Doesn't exist in new enum, add.
- TIR_Print("Adding enum value `%s` to %s\n",
- enum_name.ToCString(),
- this->ToCString());
- enum_value = old_enum_values.At(i);
- ASSERT(!enum_value.IsNull());
- to_add.Add(enum_name);
- to_add.Add(enum_value);
- } else {
- // Exists in both the new and the old.
- TIR_Print("Moving enum value `%s` to %" Pd "\n",
- enum_name.ToCString(),
- index_in_new_cls);
- // Grab old value.
- enum_value = old_enum_values.At(i);
- // Update index to the be new index.
- UpdateEnumIndex(Instance::Cast(enum_value),
- index_field,
- index_in_new_cls);
- // Chop off the 'EnumClass.'
- enum_field_name = String::SubString(enum_name,
- enum_class_name.Length() + 1);
- ASSERT(!enum_field_name.IsNull());
- // Grab the static field.
- enum_field = LookupStaticFieldAllowPrivate(enum_field_name);
- ASSERT(!enum_field.IsNull());
- // Use old value with updated index.
- enum_field.SetStaticValue(Instance::Cast(enum_value), true);
- enum_values.SetAt(index_in_new_cls, enum_value);
- enum_names.SetAt(index_in_new_cls, enum_name);
+ Array& enum_fields = Array::Handle(zone);
+ Field& field = Field::Handle(zone);
+ String& enum_ident = String::Handle();
+ Instance& old_enum_value = Instance::Handle(zone);
+ Instance& enum_value = Instance::Handle(zone);
+
+ Array& enum_map_storage = Array::Handle(zone,
+ HashTables::New<UnorderedHashMap<EnumMapTraits> >(4));
+ ASSERT(!enum_map_storage.IsNull());
+
+ TIR_Print("Replacing enum `%s`\n", String::Handle(Name()).ToCString());
+
+ {
+ UnorderedHashMap<EnumMapTraits> enum_map(enum_map_storage.raw());
+ // Build a map of all enum name -> old enum instance.
+ enum_fields = old_enum.fields();
+ for (intptr_t i = 0; i < enum_fields.Length(); i++) {
+ field = Field::RawCast(enum_fields.At(i));
+ enum_ident = field.name();
+ if (!field.is_static()) {
+ // Enum instances are only held in static fields.
+ continue;
+ }
+ if (enum_ident.Equals(Symbols::Values())) {
+ // 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());
+ bool update = enum_map.UpdateOrInsert(enum_ident, old_enum_value);
+ VTIR_Print("Element %s added to mapping\n", enum_ident.ToCString());
+ ASSERT(!update);
}
+ // The storage given to the map may have been reallocated, remember the new
+ // address.
+ enum_map_storage = enum_map.Release().raw();
}
- if (to_add.Length() == 0) {
- // Nothing to do.
- TIR_Print("Found no missing enums in %s\n", ToCString());
- return;
+ bool enums_deleted = false;
+ {
+ UnorderedHashMap<EnumMapTraits> enum_map(enum_map_storage.raw());
+ // Add a become mapping from the old instances to the new instances.
+ enum_fields = fields();
+ for (intptr_t i = 0; i < enum_fields.Length(); i++) {
+ field = Field::RawCast(enum_fields.At(i));
+ enum_ident = field.name();
+ if (!field.is_static()) {
+ // Enum instances are only held in static fields.
+ continue;
+ }
+ if (enum_ident.Equals(Symbols::Values())) {
+ // Non-enum instance.
+ continue;
+ }
+ enum_value = field.StaticValue();
+ ASSERT(!enum_value.IsNull());
+ old_enum_value ^= enum_map.GetOrNull(enum_ident);
+ if (old_enum_value.IsNull()) {
+ VTIR_Print("New element %s was not found in mapping\n",
+ enum_ident.ToCString());
+ } else {
+ VTIR_Print("Adding element `%s` to become mapping\n",
+ enum_ident.ToCString());
+ bool removed = enum_map.Remove(enum_ident);
+ ASSERT(removed);
+ reload_context->AddEnumBecomeMapping(old_enum_value, enum_value);
+ }
+ }
+ enums_deleted = enum_map.NumOccupied() > 0;
+ // The storage given to the map may have been reallocated, remember the new
+ // address.
+ enum_map_storage = enum_map.Release().raw();
}
- // Grow the values and enum_names arrays.
- const intptr_t offset = enum_names.Length();
- const intptr_t num_to_add = to_add.Length() / 2;
- ASSERT(offset == enum_values.Length());
- enum_names = Array::Grow(enum_names,
- enum_names.Length() + num_to_add,
- Heap::kOld);
- enum_values = Array::Grow(enum_values,
- enum_values.Length() + num_to_add,
- Heap::kOld);
-
- // Install new names and values into the grown arrays. Also, update
- // the index of the new enum values and add static fields for the new
- // enum values.
- Field& enum_value_field = Field::Handle();
- for (intptr_t i = 0; i < num_to_add; i++) {
- const intptr_t target_index = offset + i;
- enum_name = String::RawCast(to_add.At(i * 2));
- enum_value = to_add.At(i * 2 + 1);
-
- // Update the enum value's index into the new arrays.
- TIR_Print("Updating index of %s in %s to %" Pd "\n",
- enum_name.ToCString(),
- ToCString(),
- target_index);
- UpdateEnumIndex(Instance::Cast(enum_value), index_field, target_index);
-
- enum_names.SetAt(target_index, enum_name);
- enum_values.SetAt(target_index, enum_value);
-
- // Install new static field into class.
- // Chop off the 'EnumClass.'
- enum_field_name = String::SubString(enum_name,
- enum_class_name.Length() + 1);
- ASSERT(!enum_field_name.IsNull());
- enum_field_name = Symbols::New(thread, enum_field_name);
- enum_value_field = Field::New(enum_field_name,
- /* is_static = */ true,
- /* is_final = */ true,
- /* is_const = */ true,
- /* is_reflectable = */ true,
- *this,
- Object::dynamic_type(),
- token_pos());
- enum_value_field.set_has_initializer(false);
- enum_value_field.SetStaticValue(Instance::Cast(enum_value), true);
- enum_value_field.RecordStore(Instance::Cast(enum_value));
- AddField(enum_value_field);
+ if (enums_deleted && FLAG_trace_reload_verbose) {
+ // 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");
+ 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());
+ }
+ enum_map.Release();
}
-
- // Replace the arrays stored in the static fields.
- enum_names_field.SetStaticValue(enum_names, true);
- enum_values_field.SetStaticValue(enum_values, true);
}
@@ -609,9 +567,9 @@ void ICData::Reset() const {
if (new_target.IsNull() ||
!new_target.AreValidArguments(args_desc, NULL)) {
// TODO(rmacnak): Patch to a NSME stub.
- TIR_Print("Cannot rebind static call to %s from %s\n",
- old_target.ToCString(),
- Object::Handle(Owner()).ToCString());
+ VTIR_Print("Cannot rebind static call to %s from %s\n",
+ old_target.ToCString(),
+ Object::Handle(Owner()).ToCString());
return;
}
ClearAndSetStaticTarget(new_target);
« no previous file with comments | « runtime/vm/object.cc ('k') | runtime/vm/parser.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698