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

Unified Diff: runtime/vm/object_reload.cc

Issue 1965823002: Initial isolate reload support (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 7 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
Index: runtime/vm/object_reload.cc
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2cf57e48e9f88a0cf04b919959631d9fdb572491
--- /dev/null
+++ b/runtime/vm/object_reload.cc
@@ -0,0 +1,548 @@
+// Copyright (c) 2016, 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.
+
+#include "vm/object.h"
+
+#include "vm/isolate_reload.h"
+#include "vm/log.h"
+#include "vm/resolver.h"
+#include "vm/symbols.h"
+
+namespace dart {
+
+#ifndef PRODUCT
+
+DECLARE_FLAG(bool, trace_reload);
+DECLARE_FLAG(bool, two_args_smi_icd);
+
+#define IRC (Isolate::Current()->reload_context())
+
+class ObjectReloadUtils : public AllStatic {
+ static void DumpLibraryDictionary(const Library& lib) {
+ DictionaryIterator it(lib);
+ Object& entry = Object::Handle();
+ String& name = String::Handle();
+ TIR_Print("Dumping dictionary for %s\n", lib.ToCString());
+ while (it.HasNext()) {
+ entry = it.GetNext();
+ name = entry.DictionaryName();
+ TIR_Print("%s -> %s\n", name.ToCString(), entry.ToCString());
+ }
+ }
+};
+
+
+void Function::Reparent(const Class& new_cls) const {
+ set_owner(new_cls);
+}
+
+
+void Function::ZeroEdgeCounters() const {
+ const Array& saved_ic_data = Array::Handle(ic_data_array());
+ if (saved_ic_data.IsNull()) {
+ return;
+ }
+ const intptr_t saved_ic_datalength = saved_ic_data.Length();
+ ASSERT(saved_ic_datalength > 0);
+ const Array& edge_counters_array =
+ Array::Handle(Array::RawCast(saved_ic_data.At(0)));
+ ASSERT(!edge_counters_array.IsNull());
+ // Fill edge counters array with zeros.
+ const Smi& zero = Smi::Handle(Smi::New(0));
+ for (intptr_t i = 0; i < edge_counters_array.Length(); i++) {
+ edge_counters_array.SetAt(i, zero);
+ }
+}
+
+
+static void ClearICs(const Function& function, const Code& code) {
+ if (function.ic_data_array() == Array::null()) {
+ return; // Already reset in an earlier round.
+ }
+
+ Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
+
+ ZoneGrowableArray<const ICData*>* ic_data_array =
+ new(zone) ZoneGrowableArray<const ICData*>();
+ function.RestoreICDataMap(ic_data_array, false /* clone ic-data */);
+ if (ic_data_array->length() == 0) {
+ return;
+ }
+ const PcDescriptors& descriptors =
+ PcDescriptors::Handle(code.pc_descriptors());
+ PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kIcCall |
+ RawPcDescriptors::kUnoptStaticCall);
+ while (iter.MoveNext()) {
+ const ICData* ic_data = (*ic_data_array)[iter.DeoptId()];
+ if (ic_data == NULL) {
+ continue;
+ }
+ bool is_static_call = iter.Kind() == RawPcDescriptors::kUnoptStaticCall;
+ ic_data->Reset(is_static_call);
+ }
+}
+
+
+void Function::FillICDataWithSentinels(const Code& code) const {
+ ASSERT(code.raw() == CurrentCode());
+ ClearICs(*this, code);
+}
+
+
+void Class::CopyStaticFieldValues(const Class& old_cls) const {
+ // We only update values for non-enum classes.
+ const bool update_values = !is_enum_class();
+
+ IsolateReloadContext* reload_context = Isolate::Current()->reload_context();
+ ASSERT(reload_context != NULL);
+
+ const Array& old_field_list = Array::Handle(old_cls.fields());
+ Field& old_field = Field::Handle();
+ String& old_name = String::Handle();
+
+ const Array& field_list = Array::Handle(fields());
+ Field& field = Field::Handle();
+ String& name = String::Handle();
+
+ Instance& value = Instance::Handle();
+ for (intptr_t i = 0; i < field_list.Length(); i++) {
+ field = Field::RawCast(field_list.At(i));
+ name = field.name();
+ if (field.is_static()) {
+ // Find the corresponding old field, if it exists, and migrate
+ // over the field value.
+ for (intptr_t j = 0; j < old_field_list.Length(); j++) {
+ old_field = Field::RawCast(old_field_list.At(j));
+ old_name = old_field.name();
+ if (name.Equals(old_name)) {
+ if (update_values) {
+ value = old_field.StaticValue();
+ field.SetStaticValue(value);
+ }
+ reload_context->AddStaticFieldMapping(old_field, field);
+ }
+ }
+ }
+ }
+}
+
+
+void Class::CopyCanonicalConstants(const Class& old_cls) const {
+ if (is_enum_class()) {
+ return;
+ }
+#if defined(DEBUG)
+ {
+ // Class has no canonical constants allocated.
+ const Array& my_constants = Array::Handle(constants());
+ ASSERT(my_constants.Length() == 0);
+ }
+#endif // defined(DEBUG).
+ // Copy old constants into new class.
+ const Array& old_constants = Array::Handle(old_cls.constants());
+ if (old_constants.IsNull() || old_constants.Length() == 0) {
+ return;
+ }
+ TIR_Print("Copied %" Pd " canonical constants for class `%s`\n",
+ old_constants.Length(),
+ ToCString());
+ set_constants(old_constants);
+}
+
+
+void Class::CopyCanonicalTypes(const Class& old_cls) const {
+ const Object& old_canonical_types = Object::Handle(old_cls.canonical_types());
+ if (old_canonical_types.IsNull()) {
+ return;
+ }
+ set_canonical_types(old_canonical_types);
+}
+
+
+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;
+}
+
+
+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)));
+}
+
+
+// TODO(johnmccutchan): The code in the class finalizer canonicalizes all
+// instances and the values array. We probably should do the same thing.
+void Class::ReplaceEnum(const Class& old_enum) const {
+ // We only do this for finalized enum classes.
+ ASSERT(is_enum_class());
+ ASSERT(old_enum.is_enum_class());
+ ASSERT(is_finalized());
+ ASSERT(old_enum.is_finalized());
+
+ Thread* thread = Thread::Current();
+ 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.LookupStaticField(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(
+ LookupStaticField(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 = LookupStaticField(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);
+ }
+ }
+
+ if (to_add.Length() == 0) {
+ // Nothing to do.
+ TIR_Print("Found no missing enums in %s\n", ToCString());
+ return;
+ }
+
+ // 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);
+ }
+
+ // Replace the arrays stored in the static fields.
+ enum_names_field.SetStaticValue(enum_names, true);
+ enum_values_field.SetStaticValue(enum_values, true);
+}
+
+
+void Class::PatchFieldsAndFunctions() const {
+ // Move all old functions and fields to a patch class so that they
+ // still refer to their original script.
+ const PatchClass& patch =
+ PatchClass::Handle(PatchClass::New(*this, Script::Handle(script())));
+ ASSERT(!patch.IsNull());
+
+ const Array& funcs = Array::Handle(functions());
+ Function& func = Function::Handle();
+ Object& owner = Object::Handle();
+ for (intptr_t i = 0; i < funcs.Length(); i++) {
+ func = Function::RawCast(funcs.At(i));
+ if ((func.token_pos() == TokenPosition::kMinSource) ||
+ func.IsClosureFunction()) {
+ // Eval functions do not need to have their script updated.
+ //
+ // Closure functions refer to the parent's script which we can
+ // rely on being updated for us, if necessary.
+ continue;
+ }
+
+ // If the source for this function is already patched, leave it alone.
+ owner = func.RawOwner();
+ ASSERT(!owner.IsNull());
+ if (!owner.IsPatchClass()) {
+ ASSERT(owner.raw() == this->raw());
+ func.set_owner(patch);
+ }
+ }
+
+ const Array& field_list = Array::Handle(fields());
+ Field& field = Field::Handle();
+ for (intptr_t i = 0; i < field_list.Length(); i++) {
+ field = Field::RawCast(field_list.At(i));
+ owner = field.RawOwner();
+ ASSERT(!owner.IsNull());
+ if (!owner.IsPatchClass()) {
+ ASSERT(owner.raw() == this->raw());
+ field.set_owner(patch);
+ }
+ field.ForceDynamicGuardedCidAndLength();
+ }
+}
+
+
+bool Class::CanReload(const Class& replacement) const {
+ ASSERT(IsolateReloadContext::IsSameClass(*this, replacement));
+
+ if (is_enum_class() && !replacement.is_enum_class()) {
+ IRC->ReportError(String::Handle(String::NewFormatted(
+ "Enum class cannot be redefined to be a non-enum class: %s",
+ ToCString())));
+ return false;
+ }
+
+ if (!is_enum_class() && replacement.is_enum_class()) {
+ IRC->ReportError(String::Handle(String::NewFormatted(
+ "Class cannot be redefined to be a enum class: %s",
+ ToCString())));
+ return false;
+ }
+
+ if (is_finalized()) {
+ const Error& error =
+ Error::Handle(replacement.EnsureIsFinalized(Thread::Current()));
+ if (!error.IsNull()) {
+ IRC->ReportError(error);
+ return false;
+ }
+ TIR_Print("Finalized replacement class for %s\n", ToCString());
+ }
+
+ if (is_finalized()) {
+ // Get the field maps for both classes. These field maps walk the class
+ // hierarchy.
+ const Array& fields =
+ Array::Handle(OffsetToFieldMap());
+ const Array& replacement_fields =
+ Array::Handle(replacement.OffsetToFieldMap());
+
+ // Check that we have the same number of fields.
+ if (fields.Length() != replacement_fields.Length()) {
+ IRC->ReportError(String::Handle(String::NewFormatted(
+ "Number of instance fields changed in %s", ToCString())));
+ return false;
+ }
+
+ if (NumTypeArguments() != replacement.NumTypeArguments()) {
+ IRC->ReportError(String::Handle(String::NewFormatted(
+ "Number of type arguments changed in %s", ToCString())));
+ return false;
+ }
+
+ // Verify that field names / offsets match across the entire hierarchy.
+ Field& field = Field::Handle();
+ String& field_name = String::Handle();
+ Field& replacement_field = Field::Handle();
+ String& replacement_field_name = String::Handle();
+ for (intptr_t i = 0; i < fields.Length(); i++) {
+ if (fields.At(i) == Field::null()) {
+ ASSERT(replacement_fields.At(i) == Field::null());
+ continue;
+ }
+ field = Field::RawCast(fields.At(i));
+ replacement_field = Field::RawCast(replacement_fields.At(i));
+ field_name = field.name();
+ replacement_field_name = replacement_field.name();
+ if (!field_name.Equals(replacement_field_name)) {
+ IRC->ReportError(String::Handle(String::NewFormatted(
+ "Name of instance field changed ('%s' vs '%s') in '%s'",
+ field_name.ToCString(),
+ replacement_field_name.ToCString(),
+ ToCString())));
+ return false;
+ }
+ }
+ } else if (is_prefinalized()) {
+ if (!replacement.is_prefinalized()) {
+ IRC->ReportError(String::Handle(String::NewFormatted(
+ "Original class ('%s') is prefinalized and replacement class ('%s')",
+ ToCString(), replacement.ToCString())));
+ return false;
+ }
+ if (instance_size() != replacement.instance_size()) {
+ IRC->ReportError(String::Handle(String::NewFormatted(
+ "Instance size mismatch between '%s' (%" Pd ") and replacement "
+ "'%s' ( %" Pd ")",
+ ToCString(),
+ instance_size(),
+ replacement.ToCString(),
+ replacement.instance_size())));
+ return false;
+ }
+ }
+
+ // native field count check.
+ if (num_native_fields() != replacement.num_native_fields()) {
+ IRC->ReportError(String::Handle(String::NewFormatted(
+ "Number of native fields changed in %s", ToCString())));
+ return false;
+ }
+
+ // TODO(johnmccutchan) type parameter count check.
+
+ TIR_Print("Class `%s` can be reloaded (%" Pd " and %" Pd ")\n",
+ ToCString(),
+ id(),
+ replacement.id());
+ return true;
+}
+
+
+bool Library::CanReload(const Library& replacement) const {
+ return true;
+}
+
+
+static const Function* static_call_target = NULL;
+
+void ICData::Reset(bool is_static_call) const {
+ // TODO(johnmccutchan): ICData should know whether or not it's for a
+ // static call.
+ if (is_static_call) {
+ const Function& old_target = Function::Handle(GetTargetAt(0));
+ if (old_target.IsNull()) {
+ FATAL("old_target is NULL.\n");
+ }
+ static_call_target = &old_target;
+ if (!old_target.is_static()) {
+ // TODO(johnmccutchan): Improve this.
+ TIR_Print("Cannot rebind super-call to %s from %s\n",
+ old_target.ToCString(),
+ Object::Handle(Owner()).ToCString());
+ return;
+ }
+ const String& selector = String::Handle(old_target.name());
+ const Class& cls = Class::Handle(old_target.Owner());
+ const Function& new_target =
+ Function::Handle(cls.LookupStaticFunction(selector));
+ if (new_target.IsNull()) {
+ // TODO(johnmccutchan): Improve this.
+ TIR_Print("Cannot rebind static call to %s from %s\n",
+ old_target.ToCString(),
+ Object::Handle(Owner()).ToCString());
+ return;
+ }
+ ClearAndSetStaticTarget(new_target);
+ } else {
+ ClearWithSentinel();
+ }
+}
+
+#endif // !PRODUCT
+
+} // namespace dart.
« runtime/vm/object.cc ('K') | « runtime/vm/object.cc ('k') | runtime/vm/object_service.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698