Chromium Code Reviews| Index: runtime/vm/isolate_reload.cc |
| diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..99fff04ae8b39585fd7928629cfefee8ca4c174c |
| --- /dev/null |
| +++ b/runtime/vm/isolate_reload.cc |
| @@ -0,0 +1,1238 @@ |
| +// 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/isolate_reload.h" |
| + |
| +#include "vm/become.h" |
| +#include "vm/code_generator.h" |
| +#include "vm/compiler.h" |
| +#include "vm/dart_api_impl.h" |
| +#include "vm/hash_table.h" |
| +#include "vm/isolate.h" |
| +#include "vm/log.h" |
| +#include "vm/object.h" |
| +#include "vm/object_store.h" |
| +#include "vm/parser.h" |
| +#include "vm/safepoint.h" |
| +#include "vm/service_event.h" |
| +#include "vm/stack_frame.h" |
| +#include "vm/thread.h" |
| +#include "vm/timeline.h" |
| +#include "vm/visitor.h" |
| + |
| +namespace dart { |
| + |
| +DEFINE_FLAG(bool, trace_reload, false, "Trace isolate reloading"); |
| +DEFINE_FLAG(bool, identity_reload, false, "Enable checks for identity reload."); |
| +DEFINE_FLAG(int, reload_every, 0, "Reload every N stack overflow checks."); |
| +DEFINE_FLAG(bool, reload_every_optimized, true, "Only from optimized code."); |
| + |
| +#ifndef PRODUCT |
| + |
| +#define I (isolate()) |
| +#define Z (thread->zone()) |
| + |
| +#define TIMELINE_SCOPE(name) \ |
| + TimelineDurationScope tds##name(Thread::Current(), \ |
| + Timeline::GetIsolateStream(), \ |
| + #name) |
| + |
| + |
| +class ScriptUrlSetTraits { |
| + public: |
| + static bool ReportStats() { return false; } |
| + static const char* Name() { return "ScriptUrlSetTraits"; } |
| + |
| + static bool IsMatch(const Object& a, const Object& b) { |
| + if (!a.IsString() || !b.IsString()) { |
| + return false; |
| + } |
| + |
| + return String::Cast(a).Equals(String::Cast(b)); |
| + } |
| + |
| + static uword Hash(const Object& obj) { |
| + return String::Cast(obj).Hash(); |
| + } |
| +}; |
| + |
| + |
| +class ClassMapTraits { |
| + public: |
| + static bool ReportStats() { return false; } |
| + static const char* Name() { return "ClassMapTraits"; } |
| + |
| + static bool IsMatch(const Object& a, const Object& b) { |
| + if (!a.IsClass() || !b.IsClass()) { |
| + return false; |
| + } |
| + return IsolateReloadContext::IsSameClass(Class::Cast(a), Class::Cast(b)); |
| + } |
| + |
| + static uword Hash(const Object& obj) { |
| + return String::HashRawSymbol(Class::Cast(obj).Name()); |
| + } |
| +}; |
| + |
| + |
| +class LibraryMapTraits { |
| + public: |
| + static bool ReportStats() { return false; } |
| + static const char* Name() { return "LibraryMapTraits"; } |
| + |
| + static bool IsMatch(const Object& a, const Object& b) { |
| + if (!a.IsLibrary() || !b.IsLibrary()) { |
| + return false; |
| + } |
| + return IsolateReloadContext::IsSameLibrary( |
| + Library::Cast(a), Library::Cast(b)); |
| + } |
| + |
| + static uword Hash(const Object& obj) { |
| + return Library::Cast(obj).UrlHash(); |
| + } |
| +}; |
| + |
| + |
| +class BecomeMapTraits { |
| + public: |
| + static bool ReportStats() { return false; } |
| + static const char* Name() { return "BecomeMapTraits"; } |
| + |
| + static bool IsMatch(const Object& a, const Object& b) { |
| + return a.raw() == b.raw(); |
| + } |
| + |
| + static uword Hash(const Object& obj) { |
| + if (obj.IsLibrary()) { |
| + return Library::Cast(obj).UrlHash(); |
| + } else if (obj.IsClass()) { |
| + if (Class::Cast(obj).id() == kFreeListElement) { |
| + return 0; |
| + } |
| + return String::HashRawSymbol(Class::Cast(obj).Name()); |
| + } else if (obj.IsField()) { |
| + return String::HashRawSymbol(Field::Cast(obj).name()); |
| + } |
| + return 0; |
| + } |
| +}; |
| + |
| + |
| +bool IsolateReloadContext::IsSameField(const Field& a, const Field& b) { |
| + if (a.is_static() != b.is_static()) { |
| + return false; |
| + } |
| + const Class& a_cls = Class::Handle(a.Owner()); |
| + const Class& b_cls = Class::Handle(b.Owner()); |
| + |
| + if (!IsSameClass(a_cls, b_cls)) { |
| + return false; |
| + } |
| + |
| + const String& a_name = String::Handle(a.name()); |
| + const String& b_name = String::Handle(b.name()); |
| + |
| + return a_name.Equals(b_name); |
| +} |
| + |
| + |
| +bool IsolateReloadContext::IsSameClass(const Class& a, const Class& b) { |
| + if (a.is_patch() != b.is_patch()) { |
| + // TODO(johnmccutchan): Should we just check the class kind bits? |
| + return false; |
| + } |
| + |
| + // TODO(turnidge): We need to look at generic type arguments for |
| + // synthetic mixin classes. Their names are not necessarily unique |
| + // currently. |
| + const String& a_name = String::Handle(Class::Cast(a).Name()); |
| + const String& b_name = String::Handle(Class::Cast(b).Name()); |
| + |
| + if (!a_name.Equals(b_name)) { |
| + return false; |
| + } |
| + |
| + const Library& a_lib = Library::Handle(Class::Cast(a).library()); |
|
rmacnak
2016/05/11 19:56:18
What classes don't have a library?
Cutch
2016/05/12 15:50:14
Acknowledged.
|
| + const String& a_lib_url = |
| + String::Handle(a_lib.IsNull() ? String::null() : a_lib.url()); |
| + |
| + const Library& b_lib = Library::Handle(Class::Cast(b).library()); |
| + const String& b_lib_url = |
| + String::Handle(b_lib.IsNull() ? String::null() : b_lib.url()); |
| + |
| + return a_lib_url.Equals(b_lib_url); |
|
rmacnak
2016/05/11 19:56:18
return IsSameLibrary(a_lib, b_lib)
Cutch
2016/05/12 15:50:14
Done.
|
| +} |
| + |
| + |
| +bool IsolateReloadContext::IsSameLibrary( |
| + const Library& a_lib, const Library& b_lib) { |
| + const String& a_lib_url = |
| + String::Handle(a_lib.IsNull() ? String::null() : a_lib.url()); |
| + const String& b_lib_url = |
| + String::Handle(b_lib.IsNull() ? String::null() : b_lib.url()); |
| + return a_lib_url.Equals(b_lib_url); |
| +} |
| + |
| + |
| +IsolateReloadContext::IsolateReloadContext(Isolate* isolate, bool test_mode) |
| + : start_time_micros_(OS::GetCurrentMonotonicMicros()), |
| + isolate_(isolate), |
| + test_mode_(test_mode), |
| + has_error_(false), |
| + saved_num_cids_(-1), |
| + saved_class_table_(NULL), |
| + num_saved_libs_(-1), |
| + script_uri_(String::null()), |
| + error_(Error::null()), |
| + clean_scripts_set_storage_(Array::null()), |
| + compile_time_constants_(Array::null()), |
| + old_classes_set_storage_(Array::null()), |
| + class_map_storage_(Array::null()), |
| + old_libraries_set_storage_(Array::null()), |
| + library_map_storage_(Array::null()), |
| + become_map_storage_(Array::null()), |
| + saved_root_library_(Library::null()), |
| + saved_libraries_(GrowableObjectArray::null()) { |
| + // Preallocate storage for maps. |
| + clean_scripts_set_storage_ = |
| + HashTables::New<UnorderedHashSet<ScriptUrlSetTraits> >(4); |
| + old_classes_set_storage_ = |
| + HashTables::New<UnorderedHashSet<ClassMapTraits> >(4); |
| + class_map_storage_ = |
| + HashTables::New<UnorderedHashMap<ClassMapTraits> >(4); |
| + old_libraries_set_storage_ = |
| + HashTables::New<UnorderedHashSet<LibraryMapTraits> >(4); |
| + library_map_storage_ = |
| + HashTables::New<UnorderedHashMap<LibraryMapTraits> >(4); |
| + become_map_storage_ = |
| + HashTables::New<UnorderedHashMap<BecomeMapTraits> >(4); |
| +} |
| + |
| + |
| +IsolateReloadContext::~IsolateReloadContext() { |
| +} |
| + |
| + |
| +void IsolateReloadContext::ReportError(const Error& error) { |
| + has_error_ = true; |
| + error_ = error.raw(); |
| + if (FLAG_trace_reload) { |
| + THR_Print("ISO-RELOAD: Error: %s\n", error.ToErrorCString()); |
| + } |
| + ServiceEvent service_event(Isolate::Current(), ServiceEvent::kIsolateReload); |
| + service_event.set_reload_error(&error); |
| + Service::HandleEvent(&service_event); |
| +} |
| + |
| + |
| +void IsolateReloadContext::ReportError(const String& error_msg) { |
| + ReportError(LanguageError::Handle(LanguageError::New(error_msg))); |
| +} |
| + |
| + |
| +void IsolateReloadContext::ReportSuccess() { |
| + ServiceEvent service_event(Isolate::Current(), ServiceEvent::kIsolateReload); |
| + Service::HandleEvent(&service_event); |
| +} |
| + |
| + |
| +void IsolateReloadContext::StartReload() { |
| + Thread* thread = Thread::Current(); |
|
rmacnak
2016/05/11 19:56:18
Add a comment that because we haven't yet decided
Cutch
2016/05/12 15:50:13
I don't think this is the right place for the comm
|
| + |
| + // Grab root library before calling CheckpointBeforeReload. |
| + const Library& root_lib = Library::Handle(object_store()->root_library()); |
|
rmacnak
2016/05/11 19:56:18
root_lib may be null if an isolate reload is reque
Cutch
2016/05/12 15:50:13
We do not allow a reload to be triggered until the
|
| + const String& root_lib_url = String::Handle(root_lib.url()); |
| + |
| + // Switch all functions on the stack to compiled, unoptimized code. |
| + SwitchStackToUnoptimizedCode(); |
| + // Deoptimize all code that had optimizing decisions that are dependent on |
| + // assumptions from field guards or CHA. |
|
rmacnak
2016/05/11 19:56:18
or unloaded deferred prefixes
Cutch
2016/05/12 15:50:14
Done.
|
| + // TODO(johnmccutchan): Deoptimizing dependent code here (before the reload) |
| + // is paranoid. This likely can be moved to the commit phase. |
| + DeoptimizeDependentCode(); |
| + Checkpoint(); |
| + |
| + // Block class finalization attempts when calling into the library |
| + // tag handler. |
| + I->BlockClassFinalization(); |
| + Object& result = Object::Handle(thread->zone()); |
| + { |
| + TransitionVMToNative transition(thread); |
| + Api::Scope api_scope(thread); |
| + |
| + Dart_Handle retval = |
| + (I->library_tag_handler())(Dart_kScriptTag, |
| + Api::NewHandle(thread, Library::null()), |
| + Api::NewHandle(thread, root_lib_url.raw())); |
| + result = Api::UnwrapHandle(retval); |
| + } |
| + I->UnblockClassFinalization(); |
| + if (result.IsError()) { |
| + ReportError(Error::Cast(result)); |
| + } |
| +} |
| + |
| + |
| +void IsolateReloadContext::RegisterClass(const Class& new_cls) { |
| + const Class& old_cls = Class::Handle(OldClassOrNull(new_cls)); |
| + if (old_cls.IsNull()) { |
| + Isolate::Current()->class_table()->Register(new_cls); |
| + |
| + if (FLAG_identity_reload) { |
| + TIR_Print("Could not find replacement class for %s\n", |
| + new_cls.ToCString()); |
| + UNREACHABLE(); |
| + } |
| + |
| + // New class maps to itself. |
| + AddClassMapping(new_cls, new_cls); |
| + return; |
| + } |
| + new_cls.set_id(old_cls.id()); |
| + isolate()->class_table()->SetAt(old_cls.id(), new_cls.raw()); |
| + if (!old_cls.is_enum_class()) { |
| + new_cls.CopyCanonicalConstants(old_cls); |
| + } |
| + new_cls.CopyCanonicalTypes(old_cls); |
| + AddBecomeMapping(old_cls, new_cls); |
| + AddClassMapping(new_cls, old_cls); |
| +} |
| + |
| + |
| +void IsolateReloadContext::FinishReload() { |
| + // Disable the background compiler while we are performing the reload. |
| + BackgroundCompiler::Disable(); |
| + |
| + BuildLibraryMapping(); |
| + TIR_Print("---- DONE FINALIZING\n"); |
| + if (ValidateReload()) { |
| + Commit(); |
| + PostCommit(); |
| + } else { |
| + Rollback(); |
| + } |
| + // ValidateReload mutates the direct subclass information and does |
| + // not remove dead subclasses. Rebuild the direct subclass |
| + // information from scratch. |
| + RebuildDirectSubclasses(); |
| + |
| + BackgroundCompiler::Enable(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::AbortReload(const Error& error) { |
| + ReportError(error); |
| + Rollback(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::SwitchStackToUnoptimizedCode() { |
|
rmacnak
2016/05/11 19:56:18
Doesn't actually switch the function's code. Consi
Cutch
2016/05/12 15:50:13
Done.
|
| + TIMELINE_SCOPE(SwitchStackToUnoptimizedCode); |
| + StackFrameIterator it(StackFrameIterator::kDontValidateFrames); |
| + |
| + Function& func = Function::Handle(); |
| + while (it.HasNextFrame()) { |
| + StackFrame* frame = it.NextFrame(); |
| + if (frame->IsDartFrame()) { |
| + func = frame->LookupDartFunction(); |
| + ASSERT(!func.IsNull()); |
| + func.EnsureHasCompiledUnoptimizedCode(); |
| + } |
| + } |
| +} |
| + |
| + |
| +void IsolateReloadContext::DeoptimizeDependentCode() { |
| + ClassTable* class_table = I->class_table(); |
| + |
| + const intptr_t bottom = Dart::vm_isolate()->class_table()->NumCids(); |
| + const intptr_t top = I->class_table()->NumCids(); |
| + Class& cls = Class::Handle(); |
| + Array& fields = Array::Handle(); |
| + Field& field = Field::Handle(); |
| + for (intptr_t cls_idx = bottom; cls_idx < top; cls_idx++) { |
| + if (!class_table->HasValidClassAt(cls_idx)) { |
| + // Skip. |
| + continue; |
| + } |
| + |
| + // Deoptimize CHA code. |
| + cls = class_table->At(cls_idx); |
| + ASSERT(!cls.IsNull()); |
| + |
| + cls.DisableAllCHAOptimizedCode(); |
| + |
| + // Deoptimize field guard code. |
| + fields = cls.fields(); |
| + ASSERT(!fields.IsNull()); |
| + for (intptr_t field_idx = 0; field_idx < fields.Length(); field_idx++) { |
| + field = Field::RawCast(fields.At(field_idx)); |
| + ASSERT(!field.IsNull()); |
| + field.DeoptimizeDependentCode(); |
| + } |
| + } |
|
rmacnak
2016/05/11 19:56:18
library prefixes also have dependent code.
Cutch
2016/05/12 15:50:13
Added TODO
|
| +} |
| + |
| + |
| +void IsolateReloadContext::CheckpointClasses() { |
| + TIMELINE_SCOPE(CheckpointClasses); |
| + TIR_Print("---- CHECKPOINTING CLASSES\n"); |
| + // Checkpoint classes before a reload. We need to copy the following: |
| + // 1) The size of the class table. |
| + // 2) The class table itself. |
| + // For efficiency, we build a set of classes before the reload. This set |
| + // is used to pair new classes with old classes. |
| + |
| + ClassTable* class_table = I->class_table(); |
| + |
| + // Copy the size of the class table. |
| + saved_num_cids_ = I->class_table()->NumCids(); |
| + |
| + // Copy of the class table. |
| + RawClass** local_saved_class_table = |
| + reinterpret_cast<RawClass**>(malloc(sizeof(RawClass*) * saved_num_cids_)); |
| + |
| + Class& cls = Class::Handle(); |
| + UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_); |
| + for (intptr_t i = 0; i < saved_num_cids_; i++) { |
| + if (class_table->IsValidIndex(i) && |
| + class_table->HasValidClassAt(i)) { |
| + // Copy the class into the saved class table and add it to the set. |
| + local_saved_class_table[i] = class_table->At(i); |
| + if (i != kFreeListElement) { |
|
rmacnak
2016/05/11 19:56:18
Why is this class special? I'd expect either all t
Cutch
2016/05/12 15:50:14
It is special because it doesn't have a name and t
|
| + cls = class_table->At(i); |
| + bool already_present = old_classes_set.Insert(cls); |
| + ASSERT(!already_present); |
| + } |
| + } else { |
| + // No class at this index, mark it as NULL. |
| + local_saved_class_table[i] = NULL; |
| + } |
| + } |
| + old_classes_set_storage_ = old_classes_set.Release().raw(); |
| + // Assigning the field must be done after saving the class table. |
| + saved_class_table_ = local_saved_class_table; |
| + TIR_Print("---- System had %" Pd " classes\n", saved_num_cids_); |
| +} |
| + |
| + |
| +bool IsolateReloadContext::IsCleanLibrary(const Library& lib) { |
| + return lib.is_dart_scheme(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::CheckpointLibraries() { |
| + TIMELINE_SCOPE(CheckpointLibraries); |
| + |
| + // Save the root library in case we abort the reload. |
| + const Library& root_lib = |
| + Library::Handle(object_store()->root_library()); |
| + set_saved_root_library(root_lib); |
| + |
| + // Save the old libraries array in case we abort the reload. |
| + const GrowableObjectArray& libs = |
| + GrowableObjectArray::Handle(object_store()->libraries()); |
| + set_saved_libraries(libs); |
| + |
| + // Make a filtered copy of the old libraries array. Keep "clean" libraries |
| + // that we will use instead of reloading. |
| + const GrowableObjectArray& new_libs = GrowableObjectArray::Handle( |
| + GrowableObjectArray::New(Heap::kOld)); |
| + Library& lib = Library::Handle(); |
| + UnorderedHashSet<LibraryMapTraits> |
| + old_libraries_set(old_libraries_set_storage_); |
| + num_saved_libs_ = 0; |
| + for (intptr_t i = 0; i < libs.Length(); i++) { |
| + lib ^= libs.At(i); |
| + if (IsCleanLibrary(lib)) { |
| + // We are preserving this library across the reload, assign its new index |
| + lib.set_index(new_libs.Length()); |
| + new_libs.Add(lib, Heap::kOld); |
| + num_saved_libs_++; |
| + } else { |
| + // We are going to reload this library. Clear the index. |
| + lib.set_index(-1); |
| + } |
| + // Add old library to old libraries set. |
| + bool already_present = old_libraries_set.Insert(lib); |
| + ASSERT(!already_present); |
| + } |
| + old_libraries_set_storage_ = old_libraries_set.Release().raw(); |
| + |
| + // Reset the registered libraries to the filtered array. |
| + Library::RegisterLibraries(Thread::Current(), new_libs); |
| + // Reset the root library to null. |
| + object_store()->set_root_library(Library::Handle()); |
| +} |
| + |
| + |
| +void IsolateReloadContext::BuildCleanScriptSet() { |
| + const GrowableObjectArray& libs = |
| + GrowableObjectArray::Handle(object_store()->libraries()); |
| + |
| + UnorderedHashSet<ScriptUrlSetTraits> |
| + clean_scripts_set(clean_scripts_set_storage_); |
| + |
| + Library& lib = Library::Handle(); |
| + Array& scripts = Array::Handle(); |
| + Script& script = Script::Handle(); |
| + String& script_url = String::Handle(); |
| + for (intptr_t lib_idx = 0; lib_idx < libs.Length(); lib_idx++) { |
| + lib = Library::RawCast(libs.At(lib_idx)); |
| + ASSERT(!lib.IsNull()); |
| + ASSERT(IsCleanLibrary(lib)); |
| + scripts = lib.LoadedScripts(); |
| + ASSERT(!scripts.IsNull()); |
| + for (intptr_t script_idx = 0; script_idx < scripts.Length(); script_idx++) { |
| + script = Script::RawCast(scripts.At(script_idx)); |
| + ASSERT(!script.IsNull()); |
| + script_url = script.url(); |
| + ASSERT(!script_url.IsNull()); |
| + bool already_present = clean_scripts_set.Insert(script_url); |
| + ASSERT(!already_present); |
| + } |
| + } |
| + |
| + clean_scripts_set_storage_ = clean_scripts_set.Release().raw(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::FilterCompileTimeConstants() { |
| + // Save the compile time constants array. |
| + compile_time_constants_ = I->object_store()->compile_time_constants(); |
| + // Clear the compile time constants array. This will be repopulated |
| + // in the loop below. |
| + I->object_store()->set_compile_time_constants(Array::Handle()); |
| + |
| + if (compile_time_constants_ == Array::null()) { |
| + // Nothing to do. |
| + return; |
| + } |
| + |
| + // Iterate over the saved compile time constants map. |
| + ConstantsMap old_constants(compile_time_constants_); |
| + ConstantsMap::Iterator it(&old_constants); |
| + |
| + Array& key = Array::Handle(); |
| + String& url = String::Handle(); |
| + Smi& token_pos = Smi::Handle(); |
| + Instance& value = Instance::Handle(); |
| + |
| + // We filter the compile time constants map so that after it only contains |
| + // constants from scripts contained in this set. |
| + UnorderedHashSet<ScriptUrlSetTraits> |
| + clean_scripts_set(clean_scripts_set_storage_); |
| + |
| + while (it.MoveNext()) { |
| + const intptr_t entry = it.Current(); |
| + ASSERT(entry != -1); |
| + key = Array::RawCast(old_constants.GetKey(entry)); |
| + ASSERT(!key.IsNull()); |
| + url = String::RawCast(key.At(0)); |
| + ASSERT(!url.IsNull()); |
| + if (clean_scripts_set.ContainsKey(url)) { |
| + // We've found a cached constant from a clean script, add it to the |
| + // compile time constants map again. |
| + token_pos = Smi::RawCast(key.At(1)); |
| + TokenPosition tp(token_pos.Value()); |
| + // Use ^= because this might be null. |
| + value ^= old_constants.GetPayload(entry, 0); |
| + Parser::InsertCachedConstantValue(url, tp, value); |
| + } |
| + } |
| + |
| + old_constants.Release(); |
| + clean_scripts_set.Release(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::Checkpoint() { |
| + TIMELINE_SCOPE(Checkpoint); |
| + CheckpointClasses(); |
| + CheckpointLibraries(); |
| + BuildCleanScriptSet(); |
| + FilterCompileTimeConstants(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::RollbackClasses() { |
| + TIR_Print("---- ROLLING BACK CLASS TABLE\n"); |
| + ASSERT(saved_num_cids_ > 0); |
| + ASSERT(saved_class_table_ != NULL); |
| + ClassTable* class_table = I->class_table(); |
| + class_table->SetNumCids(saved_num_cids_); |
| + // Overwrite classes in class table with the saved classes. |
| + for (intptr_t i = 0; i < saved_num_cids_; i++) { |
| + if (class_table->IsValidIndex(i)) { |
| + class_table->SetAt(i, saved_class_table_[i]); |
| + } |
| + } |
| + free(saved_class_table_); |
| + saved_class_table_ = NULL; |
| + saved_num_cids_ = 0; |
| +} |
| + |
| + |
| +void IsolateReloadContext::RollbackLibraries() { |
| + TIR_Print("---- ROLLING BACK LIBRARY CHANGES\n"); |
| + Thread* thread = Thread::Current(); |
| + Library& lib = Library::Handle(); |
| + GrowableObjectArray& saved_libs = GrowableObjectArray::Handle( |
| + Z, saved_libraries()); |
| + if (!saved_libs.IsNull()) { |
| + for (intptr_t i = 0; i < saved_libs.Length(); i++) { |
| + lib = Library::RawCast(saved_libs.At(i)); |
| + // Restore indexes that were modified in CheckpointLibraries. |
| + lib.set_index(i); |
| + } |
| + |
| + // Reset the registered libraries to the filtered array. |
| + Library::RegisterLibraries(Thread::Current(), saved_libs); |
| + } |
| + |
| + Library& saved_root_lib = Library::Handle(Z, saved_root_library()); |
| + if (!saved_root_lib.IsNull()) { |
| + object_store()->set_root_library(saved_root_lib); |
| + } |
| + |
| + set_saved_root_library(Library::Handle()); |
| + set_saved_libraries(GrowableObjectArray::Handle()); |
| +} |
| + |
| + |
| +void IsolateReloadContext::Rollback() { |
| + I->object_store()->set_compile_time_constants( |
| + Array::Handle(compile_time_constants_)); |
| + RollbackClasses(); |
| + RollbackLibraries(); |
| +} |
| + |
| + |
| +#ifdef DEBUG |
| +void IsolateReloadContext::VerifyMaps() { |
| + Class& cls = Class::Handle(); |
| + Class& new_cls = Class::Handle(); |
| + Class& cls2 = Class::Handle(); |
| + Class& new_cls2 = Class::Handle(); |
| + |
| + // Verify that two old classes aren't both mapped to the same new |
| + // class. This could happen is the IsSameClass function is broken. |
| + UnorderedHashMap<ClassMapTraits> class_map(class_map_storage_); |
| + { |
| + UnorderedHashMap<ClassMapTraits>::Iterator it(&class_map); |
| + while (it.MoveNext()) { |
| + const intptr_t entry = it.Current(); |
| + new_cls = Class::RawCast(class_map.GetKey(entry)); |
| + cls = Class::RawCast(class_map.GetPayload(entry, 0)); |
| + if (new_cls.raw() != cls.raw()) { |
| + UnorderedHashMap<ClassMapTraits>::Iterator it2(&class_map); |
| + while (it2.MoveNext()) { |
| + new_cls2 = Class::RawCast(class_map.GetKey(entry)); |
| + if (new_cls.raw() == new_cls2.raw()) { |
| + cls2 = Class::RawCast(class_map.GetPayload(entry, 0)); |
| + if (cls.raw() != cls2.raw()) { |
| + OS::PrintErr( |
| + "Classes '%s' and '%s' are distinct classes but both map to " |
| + "class '%s'\n", |
| + cls.ToCString(), cls2.ToCString(), new_cls.ToCString()); |
| + UNREACHABLE(); |
| + } |
| + } |
| + } |
| + } |
| + } |
| + } |
| + class_map.Release(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::VerifyCanonicalTypeArguments() { |
| + Thread* thread = Thread::Current(); |
| + const Array& table = |
| + Array::Handle(Z, I->object_store()->canonical_type_arguments()); |
| + const intptr_t table_size = table.Length() - 1; |
| + ASSERT(Utils::IsPowerOfTwo(table_size)); |
| + TypeArguments& element = TypeArguments::Handle(Z); |
| + TypeArguments& other_element = TypeArguments::Handle(); |
| + for (intptr_t i = 0; i < table_size; i++) { |
| + element ^= table.At(i); |
| + for (intptr_t j = 0; j < table_size; j++) { |
| + if ((i != j) && (table.At(j) != TypeArguments::null())) { |
| + other_element ^= table.At(j); |
| + if (element.Equals(other_element)) { |
| + // Recursive types may be equal, but have different hashes. |
| + ASSERT(element.IsRecursive()); |
| + ASSERT(other_element.IsRecursive()); |
| + ASSERT(element.Hash() != other_element.Hash()); |
| + } |
| + } |
| + } |
| + } |
| +} |
| +#endif |
| + |
| + |
| +void IsolateReloadContext::Commit() { |
| + TIMELINE_SCOPE(Commit); |
| + TIR_Print("---- COMMITTING REVERSE MAP\n"); |
| + |
| +#ifdef DEBUG |
| + VerifyMaps(); |
| +#endif |
| + |
| + { |
| + TIMELINE_SCOPE(CopyStaticFieldsAndPatchFieldsAndFunctions); |
| + // Copy static field values from the old classes to the new classes. |
| + // Patch fields and functions in the old classes so that they retain |
| + // the old script. |
| + Class& cls = Class::Handle(); |
| + Class& new_cls = Class::Handle(); |
| + |
| + UnorderedHashMap<ClassMapTraits> class_map(class_map_storage_); |
| + |
| + { |
| + UnorderedHashMap<ClassMapTraits>::Iterator it(&class_map); |
| + while (it.MoveNext()) { |
| + const intptr_t entry = it.Current(); |
| + new_cls = Class::RawCast(class_map.GetKey(entry)); |
| + cls = Class::RawCast(class_map.GetPayload(entry, 0)); |
| + if (new_cls.raw() != cls.raw()) { |
| + ASSERT(new_cls.is_enum_class() == cls.is_enum_class()); |
| + if (new_cls.is_enum_class() && new_cls.is_finalized()) { |
| + new_cls.ReplaceEnum(cls); |
| + } |
| + new_cls.CopyStaticFieldValues(cls); |
| + cls.PatchFieldsAndFunctions(); |
| + } |
| + } |
| + } |
| + |
| + class_map.Release(); |
| + } |
| + |
| + // Copy over certain properties of libraries, e.g. is the library |
| + // debuggable? |
| + { |
| + TIMELINE_SCOPE(CopyLibraryBits); |
| + Library& lib = Library::Handle(); |
| + Library& new_lib = Library::Handle(); |
| + |
| + UnorderedHashMap<LibraryMapTraits> lib_map(library_map_storage_); |
| + |
| + { |
| + // Reload existing libraries. |
| + UnorderedHashMap<LibraryMapTraits>::Iterator it(&lib_map); |
| + |
| + while (it.MoveNext()) { |
| + const intptr_t entry = it.Current(); |
| + ASSERT(entry != -1); |
| + new_lib = Library::RawCast(lib_map.GetKey(entry)); |
| + lib = Library::RawCast(lib_map.GetPayload(entry, 0)); |
| + new_lib.set_debuggable(lib.IsDebuggable()); |
| + } |
| + } |
| + |
| + // Release the library map. |
| + lib_map.Release(); |
| + } |
| + |
| + { |
| + TIMELINE_SCOPE(UpdateLibrariesArray); |
| + // Update the libraries array. |
| + Library& lib = Library::Handle(); |
| + const GrowableObjectArray& libs = GrowableObjectArray::Handle( |
| + I->object_store()->libraries()); |
| + for (intptr_t i = 0; i < libs.Length(); i++) { |
| + lib = Library::RawCast(libs.At(i)); |
| + TIR_Print("Lib '%s' at index %" Pd "\n", lib.ToCString(), i); |
| + lib.set_index(i); |
| + } |
| + |
| + // Initialize library side table. |
| + library_infos_.SetLength(libs.Length()); |
| + for (intptr_t i = 0; i < libs.Length(); i++) { |
| + lib = Library::RawCast(libs.At(i)); |
| + // Mark the library dirty if it comes after the libraries we saved. |
| + library_infos_[i].dirty = i >= num_saved_libs_; |
| + } |
| + } |
| + |
| + { |
| + UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_); |
| + intptr_t replacement_count = become_map.NumOccupied(); |
| + const Array& before = |
| + Array::Handle(Array::New(replacement_count, Heap::kOld)); |
| + const Array& after = |
| + Array::Handle(Array::New(replacement_count, Heap::kOld)); |
| + Object& obj = Object::Handle(); |
| + intptr_t replacement_index = 0; |
| + UnorderedHashMap<BecomeMapTraits>::Iterator it(&become_map); |
| + while (it.MoveNext()) { |
| + const intptr_t entry = it.Current(); |
| + obj = become_map.GetKey(entry); |
| + before.SetAt(replacement_index, obj); |
| + obj = become_map.GetPayload(entry, 0); |
| + after.SetAt(replacement_index, obj); |
| + replacement_index++; |
| + } |
| + ASSERT(replacement_index == replacement_count); |
| + become_map.Release(); |
| + |
| + Become::ElementsForwardIdentity(before, after); |
| + } |
| + |
| + if (FLAG_identity_reload) { |
| + if (saved_num_cids_ != I->class_table()->NumCids()) { |
| + TIR_Print("Identity reload failed! B#C=%" Pd " A#C=%" Pd "\n", |
| + saved_num_cids_, |
| + I->class_table()->NumCids()); |
| + } |
| + const GrowableObjectArray& saved_libs = |
| + GrowableObjectArray::Handle(saved_libraries()); |
| + const GrowableObjectArray& libs = |
| + GrowableObjectArray::Handle(I->object_store()->libraries()); |
| + if (saved_libs.Length() != libs.Length()) { |
| + TIR_Print("Identity reload failed! B#L=%" Pd " A#L=%" Pd "\n", |
| + saved_libs.Length(), |
| + libs.Length()); |
| + } |
| + } |
| + |
| +#ifdef DEBUG |
| + // TODO(turnidge): Remove before committing to main branch. |
| + VerifyCanonicalTypeArguments(); |
| +#endif |
| +} |
| + |
| + |
| +bool IsolateReloadContext::IsDirty(const Library& lib) { |
| + const intptr_t index = lib.index(); |
| + if (index == static_cast<classid_t>(-1)) { |
| + // Treat deleted libraries as dirty. |
| + return true; |
| + } |
| + ASSERT((index >= 0) && (index < library_infos_.length())); |
| + return library_infos_[index].dirty; |
| +} |
| + |
| + |
| +void IsolateReloadContext::PostCommit() { |
| + TIMELINE_SCOPE(PostCommit); |
| + set_saved_root_library(Library::Handle()); |
| + set_saved_libraries(GrowableObjectArray::Handle()); |
| + InvalidateWorld(); |
| +} |
| + |
| + |
| +bool IsolateReloadContext::ValidateReload() { |
| + TIMELINE_SCOPE(ValidateReload); |
| + if (has_error_) { |
| + return false; |
| + } |
| + |
| + // Already built. |
| + ASSERT(class_map_storage_ != Array::null()); |
| + UnorderedHashMap<ClassMapTraits> map(class_map_storage_); |
| + UnorderedHashMap<ClassMapTraits>::Iterator it(&map); |
| + Class& cls = Class::Handle(); |
| + Class& new_cls = Class::Handle(); |
| + while (it.MoveNext()) { |
| + const intptr_t entry = it.Current(); |
| + new_cls = Class::RawCast(map.GetKey(entry)); |
| + cls = Class::RawCast(map.GetPayload(entry, 0)); |
| + if (new_cls.raw() != cls.raw()) { |
| + if (!cls.CanReload(new_cls)) { |
| + map.Release(); |
| + return false; |
| + } |
| + } |
| + } |
| + map.Release(); |
| + return true; |
| +} |
| + |
| + |
| +RawClass* IsolateReloadContext::FindOriginalClass(const Class& cls) { |
| + return MappedClass(cls); |
| +} |
| + |
| + |
| +RawClass* IsolateReloadContext::GetClassForHeapWalkAt(intptr_t cid) { |
| + if (saved_class_table_ != NULL) { |
| + ASSERT(cid > 0); |
| + ASSERT(cid < saved_num_cids_); |
| + return saved_class_table_[cid]; |
| + } else { |
| + return isolate_->class_table()->At(cid); |
| + } |
| +} |
| + |
| + |
| +RawLibrary* IsolateReloadContext::saved_root_library() const { |
| + return saved_root_library_; |
| +} |
| + |
| + |
| +void IsolateReloadContext::set_saved_root_library(const Library& value) { |
| + saved_root_library_ = value.raw(); |
| +} |
| + |
| + |
| +RawGrowableObjectArray* IsolateReloadContext::saved_libraries() const { |
| + return saved_libraries_; |
| +} |
| + |
| + |
| +void IsolateReloadContext::set_saved_libraries( |
| + const GrowableObjectArray& value) { |
| + saved_libraries_ = value.raw(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
| + visitor->VisitPointers(from(), to()); |
| + if (saved_class_table_ != NULL) { |
| + visitor->VisitPointers( |
| + reinterpret_cast<RawObject**>(&saved_class_table_[0]), saved_num_cids_); |
| + } |
| +} |
| + |
| + |
| +ObjectStore* IsolateReloadContext::object_store() { |
| + return isolate_->object_store(); |
| +} |
| + |
| + |
| +static void ResetICs(const Function& function, const Code& code) { |
|
rmacnak
2016/05/11 19:56:18
For code on the stack, we need to find the ICs in
Cutch
2016/05/12 15:50:14
Added TODO.
|
| + if (function.ic_data_array() == Array::null()) { |
| + return; // Already reset in an earlier round. |
|
rmacnak
2016/05/11 19:56:18
I'm wrong here. Even if the ic data array is alrea
Cutch
2016/05/12 15:50:14
Acknowledged.
|
| + } |
| + |
| + 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 */); |
| + const intptr_t ic_data_array_length = ic_data_array->length(); |
| + 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 intptr_t index = iter.DeoptId(); |
| + if (index >= ic_data_array_length) { |
| + // TODO(johnmccutchan): Investigate how this can happen. |
| + continue; |
| + } |
| + const ICData* ic_data = (*ic_data_array)[index]; |
| + if (ic_data == NULL) { |
| + // TODO(johnmccutchan): Investigate how this can happen. |
| + continue; |
| + } |
| + bool is_static_call = iter.Kind() == RawPcDescriptors::kUnoptStaticCall; |
| + ic_data->Reset(is_static_call); |
| + } |
| +} |
| + |
| + |
| +void IsolateReloadContext::ResetUnoptimizedICsOnStack() { |
| + Code& code = Code::Handle(); |
| + Function& function = Function::Handle(); |
| + ObjectPool& object_table = ObjectPool::Handle(); |
| + Object& object_table_entry = Object::Handle(); |
| + DartFrameIterator iterator; |
| + StackFrame* frame = iterator.NextFrame(); |
| + while (frame != NULL) { |
| + code = frame->LookupDartCode(); |
| + if (code.is_optimized()) { |
| + // If this code is optimized, we need to reset the ICs in the |
| + // corresponding unoptimized code, which will be executed when the stack |
| + // unwinds to the the optimized code. We must use the unoptimized code |
| + // referenced from the optimized code's deopt object table, because this |
|
rmacnak
2016/05/11 19:56:18
This comment is out of date. Optimized code doesn'
Cutch
2016/05/12 15:50:13
Acknowledged.
|
| + // is the code that will be used to finish the activation after deopt. It |
| + // can be different from the function's current unoptimized code, which |
| + // may be null if we've already done an atomic install or different code |
| + // if the function has already been recompiled. |
| + function = code.function(); |
|
rmacnak
2016/05/11 19:56:18
code = function.unoptimized_code();
ASSERT(!code.I
Cutch
2016/05/12 15:50:13
Done.
|
| + object_table = code.object_pool(); |
| + intptr_t reset_count = 0; |
| + for (intptr_t i = 0; i < object_table.Length(); i++) { |
| + if (object_table.InfoAt(i) != ObjectPool::kTaggedObject) { |
| + continue; |
| + } |
| + object_table_entry = object_table.ObjectAt(i); |
| + if (object_table_entry.IsCode()) { |
| + code ^= object_table_entry.raw(); |
| + if (code.function() == function.raw()) { |
| + reset_count++; |
| + ResetICs(function, code); |
| + } |
| + // Why are other code objects in this table? Allocation stubs? |
| + } |
| + } |
| + // ASSERT(reset_count == 1); |
| + // vm shot itself in the foot: no reference to unopt code. |
| + } else { |
| + function = code.function(); |
| + ResetICs(function, code); |
| + } |
| + frame = iterator.NextFrame(); |
| + } |
| +} |
| + |
| + |
| +void IsolateReloadContext::ResetMegamorphicCaches() { |
| + object_store()->set_megamorphic_cache_table(GrowableObjectArray::Handle()); |
| + // Since any current optimized code will not make any more calls, it may be |
| + // better to clear the table instead of clearing each of the caches, allow |
| + // the current megamorphic caches get GC'd and any new optimized code allocate |
| + // new ones. |
| +} |
| + |
| + |
| +class MarkFunctionsForRecompilation : public ObjectVisitor { |
| + public: |
| + MarkFunctionsForRecompilation(Isolate* isolate, |
| + IsolateReloadContext* reload_context) |
| + : ObjectVisitor(), |
| + handle_(Object::Handle()), |
| + owning_class_(Class::Handle()), |
| + owning_lib_(Library::Handle()), |
| + code_(Code::Handle()), |
| + reload_context_(reload_context) { |
| + } |
| + |
| + virtual void VisitObject(RawObject* obj) { |
| + // Free-list elements cannot even be wrapped in handles. |
| + if (obj->IsFreeListElement()) { |
| + return; |
| + } |
| + handle_ = obj; |
| + if (handle_.IsFunction()) { |
| + const Function& func = Function::Cast(handle_); |
| + |
| + // Switch to unoptimized code or the lazy compilation stub. |
| + func.SwitchToLazyCompiledUnoptimizedCode(); |
| + |
| + // Grab the current code. |
| + code_ = func.CurrentCode(); |
| + ASSERT(!code_.IsNull()); |
| + const bool clear_code = IsFromDirtyLibrary(func); |
| + const bool stub_code = code_.IsStubCode(); |
| + |
| + // Zero edge counters. |
| + func.ZeroEdgeCounters(); |
|
rmacnak
2016/05/11 19:56:18
Should only be needed in the PreserveUnoptimizedCo
Cutch
2016/05/12 15:50:14
Acknowledged.
|
| + |
| + if (!stub_code) { |
| + if (clear_code) { |
| + ClearAllCode(func); |
| + } else { |
| + PreserveUnoptimizedCode(func); |
| + } |
| + } |
| + |
| + // Clear counters. |
| + func.set_usage_counter(0); |
| + func.set_deoptimization_counter(0); |
| + func.set_optimized_instruction_count(0); |
| + func.set_optimized_call_site_count(0); |
| + } |
| + } |
| + |
| + private: |
| + void ClearAllCode(const Function& func) { |
| + // Null out the ICData array and code. |
| + func.ClearICDataArray(); |
| + func.ClearCode(); |
| + func.set_was_compiled(false); |
| + } |
| + |
| + void PreserveUnoptimizedCode(const Function& func) { |
| + ASSERT(!code_.IsNull()); |
| + // We are preserving the unoptimized code, fill all ICData arrays with |
| + // the sentinel values so that we have no stale type feedback. |
|
rmacnak
2016/05/11 19:56:18
more importantly, no stale method lookups.
Cutch
2016/05/12 15:50:14
Acknowledged.
|
| + func.FillICDataWithSentinels(code_); |
| + } |
| + |
| + bool IsFromDirtyLibrary(const Function& func) { |
| + owning_class_ = func.Owner(); |
| + owning_lib_ = owning_class_.library(); |
| + return reload_context_->IsDirty(owning_lib_); |
| + } |
| + |
| + Object& handle_; |
| + Class& owning_class_; |
| + Library& owning_lib_; |
| + Code& code_; |
| + IsolateReloadContext* reload_context_; |
| +}; |
| + |
| + |
| +void IsolateReloadContext::MarkAllFunctionsForRecompilation() { |
| + TIMELINE_SCOPE(MarkAllFunctionsForRecompilation); |
| + MarkFunctionsForRecompilation visitor(isolate_, this); |
| + isolate_->heap()->VisitObjects(&visitor); |
| +} |
| + |
| + |
| +void IsolateReloadContext::InvalidateWorld() { |
| + ResetMegamorphicCaches(); |
| + |
| + DeoptimizeFunctionsOnStack(); |
| + |
| + { |
| + NoSafepointScope no_safepoint; |
| + HeapIterationScope heap_iteration_scope; |
|
rmacnak
2016/05/11 19:56:18
ResetUnoptimizedICsOnStack isn't a heap walk. Move
Cutch
2016/05/12 15:50:14
Done.
|
| + |
| + ResetUnoptimizedICsOnStack(); |
| + MarkAllFunctionsForRecompilation(); |
| + } |
| +} |
| + |
| + |
| +RawClass* IsolateReloadContext::MappedClass(const Class& replacement_or_new) { |
| + UnorderedHashMap<ClassMapTraits> map(class_map_storage_); |
| + Class& cls = Class::Handle(); |
| + cls ^= map.GetOrNull(replacement_or_new); |
| + // No need to update storage address because no mutation occurred. |
| + map.Release(); |
| + return cls.raw(); |
| +} |
| + |
| + |
| +RawLibrary* IsolateReloadContext::MappedLibrary( |
| + const Library& replacement_or_new) { |
| + return Library::null(); |
| +} |
| + |
| + |
| +RawClass* IsolateReloadContext::OldClassOrNull( |
| + const Class& replacement_or_new) { |
| + UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_); |
| + Class& cls = Class::Handle(); |
| + cls ^= old_classes_set.GetOrNull(replacement_or_new); |
| + old_classes_set_storage_ = old_classes_set.Release().raw(); |
| + return cls.raw(); |
| +} |
| + |
| + |
| +RawLibrary* IsolateReloadContext::OldLibraryOrNull( |
| + const Library& replacement_or_new) { |
| + UnorderedHashSet<LibraryMapTraits> |
| + old_libraries_set(old_libraries_set_storage_); |
| + Library& lib = Library::Handle(); |
| + lib ^= old_libraries_set.GetOrNull(replacement_or_new); |
| + old_libraries_set_storage_ = old_libraries_set.Release().raw(); |
| + return lib.raw(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::BuildLibraryMapping() { |
| + const GrowableObjectArray& libs = |
| + GrowableObjectArray::Handle(object_store()->libraries()); |
| + |
| + Library& replacement_or_new = Library::Handle(); |
| + Library& old = Library::Handle(); |
| + for (intptr_t i = 0; i < libs.Length(); i++) { |
| + replacement_or_new = Library::RawCast(libs.At(i)); |
| + if (IsCleanLibrary(replacement_or_new)) { |
| + continue; |
| + } |
| + old ^= OldLibraryOrNull(replacement_or_new); |
| + if (old.IsNull()) { |
| + // New library. |
| + AddLibraryMapping(replacement_or_new, replacement_or_new); |
| + } else { |
| + ASSERT(!replacement_or_new.is_dart_scheme()); |
| + // Replaced class. |
| + AddLibraryMapping(replacement_or_new, old); |
| + |
| + AddBecomeMapping(old, replacement_or_new); |
| + } |
| + } |
| +} |
| + |
| + |
| +void IsolateReloadContext::AddClassMapping(const Class& replacement_or_new, |
| + const Class& original) { |
| + UnorderedHashMap<ClassMapTraits> map(class_map_storage_); |
| + bool update = map.UpdateOrInsert(replacement_or_new, original); |
| + ASSERT(!update); |
| + // The storage given to the map may have been reallocated, remember the new |
| + // address. |
| + class_map_storage_ = map.Release().raw(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::AddLibraryMapping(const Library& replacement_or_new, |
| + const Library& original) { |
| + UnorderedHashMap<LibraryMapTraits> map(library_map_storage_); |
| + bool update = map.UpdateOrInsert(replacement_or_new, original); |
| + ASSERT(!update); |
| + // The storage given to the map may have been reallocated, remember the new |
| + // address. |
| + library_map_storage_ = map.Release().raw(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::AddStaticFieldMapping( |
| + const Field& old_field, const Field& new_field) { |
| + ASSERT(old_field.is_static()); |
| + ASSERT(new_field.is_static()); |
| + |
| + AddBecomeMapping(old_field, new_field); |
| +} |
| + |
| + |
| +void IsolateReloadContext::AddBecomeMapping(const Object& old, |
| + const Object& neu) { |
| + ASSERT(become_map_storage_ != Array::null()); |
| + UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_); |
| + bool update = become_map.UpdateOrInsert(old, neu); |
| + ASSERT(!update); |
| + become_map_storage_ = become_map.Release().raw(); |
| +} |
| + |
| + |
| +void IsolateReloadContext::RebuildDirectSubclasses() { |
| + ClassTable* class_table = I->class_table(); |
| + intptr_t num_cids = class_table->NumCids(); |
| + |
| + // Clear the direct subclasses for all classes. |
| + Class& cls = Class::Handle(); |
| + GrowableObjectArray& subclasses = GrowableObjectArray::Handle(); |
| + for (intptr_t i = 1; i < num_cids; i++) { |
| + if (class_table->HasValidClassAt(i)) { |
| + cls = class_table->At(i); |
| + subclasses = cls.direct_subclasses(); |
| + if (!subclasses.IsNull()) { |
| + subclasses.SetLength(0); |
| + } |
| + } |
| + } |
| + |
| + // Recompute the direct subclasses. |
| + AbstractType& super_type = AbstractType::Handle(); |
| + Class& super_cls = Class::Handle(); |
| + for (intptr_t i = 1; i < num_cids; i++) { |
| + if (class_table->HasValidClassAt(i)) { |
| + cls = class_table->At(i); |
| + super_type = cls.super_type(); |
| + if (!super_type.IsNull() && !super_type.IsObjectType()) { |
| + super_cls = cls.SuperClass(); |
| + ASSERT(!super_cls.IsNull()); |
| + super_cls.AddDirectSubclass(cls); |
| + } |
| + } |
| + } |
| +} |
| + |
| +#endif // !PRODUCT |
| + |
| +} // namespace dart |