Index: runtime/vm/isolate_reload.cc |
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc |
index 75102c9d1f089b71142cf02708a03df53f6d9a95..f464e34da0217dabf35628f0b9077b21c1f99622 100644 |
--- a/runtime/vm/isolate_reload.cc |
+++ b/runtime/vm/isolate_reload.cc |
@@ -5,6 +5,7 @@ |
#include "vm/isolate_reload.h" |
#include "vm/become.h" |
+#include "vm/bit_vector.h" |
#include "vm/code_generator.h" |
#include "vm/compiler.h" |
#include "vm/dart_api_impl.h" |
@@ -179,11 +180,14 @@ bool IsolateReloadContext::IsSameLibrary( |
IsolateReloadContext::IsolateReloadContext(Isolate* isolate) |
: start_time_micros_(OS::GetCurrentMonotonicMicros()), |
+ reload_timestamp_(OS::GetCurrentTimeMillis()), |
isolate_(isolate), |
+ reload_skipped_(false), |
has_error_(false), |
saved_num_cids_(-1), |
saved_class_table_(NULL), |
num_saved_libs_(-1), |
+ modified_libs_(NULL), |
script_uri_(String::null()), |
error_(Error::null()), |
old_classes_set_storage_(Array::null()), |
@@ -227,7 +231,7 @@ void IsolateReloadContext::ReportSuccess() { |
} |
-void IsolateReloadContext::StartReload() { |
+void IsolateReloadContext::StartReload(bool force_reload) { |
TIMELINE_SCOPE(Reload); |
Thread* thread = Thread::Current(); |
ASSERT(isolate() == thread->isolate()); |
@@ -237,6 +241,15 @@ void IsolateReloadContext::StartReload() { |
ASSERT(!root_lib.IsNull()); |
const String& root_lib_url = String::Handle(root_lib.url()); |
+ // Check to see which libraries have been modified. |
+ modified_libs_ = FindModifiedLibraries(force_reload); |
+ if (!modified_libs_->Contains(root_lib.index())) { |
+ ASSERT(modified_libs_->IsEmpty()); |
+ reload_skipped_ = true; |
+ TIR_Print("Skipping reload. No libraries were modified\n"); |
+ return; |
+ } |
+ |
// Preallocate storage for maps. |
old_classes_set_storage_ = |
HashTables::New<UnorderedHashSet<ClassMapTraits> >(4); |
@@ -313,11 +326,15 @@ void IsolateReloadContext::RegisterClass(const Class& new_cls) { |
void IsolateReloadContext::FinishReload() { |
+ if (reload_skipped_) { |
+ return; |
+ } |
BuildLibraryMapping(); |
TIR_Print("---- DONE FINALIZING\n"); |
if (ValidateReload()) { |
Commit(); |
PostCommit(); |
+ isolate()->set_last_reload_timestamp(reload_timestamp_); |
} else { |
Rollback(); |
} |
@@ -327,7 +344,7 @@ void IsolateReloadContext::FinishReload() { |
RebuildDirectSubclasses(); |
if (FLAG_write_protect_code) { |
- // Disable code page write protection while we are reloading. |
+ // Re-enable code page write protection. |
I->heap()->WriteProtectCode(true); |
} |
@@ -434,8 +451,119 @@ void IsolateReloadContext::CheckpointClasses() { |
} |
-bool IsolateReloadContext::IsCleanLibrary(const Library& lib) { |
- return lib.is_dart_scheme(); |
+Dart_FileModifiedCallback IsolateReloadContext::file_modified_callback_ = NULL; |
+ |
+ |
+bool IsolateReloadContext::ScriptModifiedSince(const Script& script, |
+ int64_t since) { |
+ if (file_modified_callback_ == NULL) { |
+ return true; |
+ } |
+ // We use the resolved url to determine if the script has been modified. |
+ const String& url = String::Handle(script.resolved_url()); |
+ const char* url_chars = url.ToCString(); |
+ return (*file_modified_callback_)(url_chars, since); |
+} |
+ |
+ |
+static void PropagateLibraryModified( |
+ const ZoneGrowableArray<ZoneGrowableArray<intptr_t>* >* imported_by, |
+ intptr_t lib_index, |
+ BitVector* modified_libs) { |
+ ZoneGrowableArray<intptr_t>* dep_libs = (*imported_by)[lib_index]; |
+ for (intptr_t i = 0; i < dep_libs->length(); i++) { |
+ intptr_t dep_lib_index = (*dep_libs)[i]; |
+ if (!modified_libs->Contains(dep_lib_index)) { |
+ modified_libs->Add(dep_lib_index); |
+ PropagateLibraryModified(imported_by, dep_lib_index, modified_libs); |
+ } |
+ } |
+} |
+ |
+ |
+BitVector* IsolateReloadContext::FindModifiedLibraries(bool force_reload) { |
+ Thread* thread = Thread::Current(); |
+ int64_t last_reload = I->last_reload_timestamp(); |
+ |
+ const GrowableObjectArray& libs = |
+ GrowableObjectArray::Handle(object_store()->libraries()); |
+ Library& lib = Library::Handle(); |
+ Array& scripts = Array::Handle(); |
+ Script& script = Script::Handle(); |
+ intptr_t num_libs = libs.Length(); |
+ |
+ // Construct the imported-by graph. |
+ ZoneGrowableArray<ZoneGrowableArray<intptr_t>* >* imported_by = |
+ new ZoneGrowableArray<ZoneGrowableArray<intptr_t>* >(num_libs); |
+ imported_by->SetLength(num_libs); |
+ for (intptr_t i = 0; i < num_libs; i++) { |
+ (*imported_by)[i] = new ZoneGrowableArray<intptr_t>(); |
+ } |
+ Array& imports = Array::Handle(); |
+ Namespace& ns = Namespace::Handle(); |
+ Library& target = Library::Handle(); |
+ |
+ for (intptr_t lib_idx = 0; lib_idx < num_libs; lib_idx++) { |
+ lib ^= libs.At(lib_idx); |
+ ASSERT(lib_idx == lib.index()); |
+ if (lib.is_dart_scheme()) { |
+ // We don't care about imports among dart scheme libraries. |
+ continue; |
+ } |
+ |
+ // Add imports to the import-by graph. |
+ imports = lib.imports(); |
+ for (intptr_t import_idx = 0; import_idx < imports.Length(); import_idx++) { |
+ ns ^= imports.At(import_idx); |
+ if (!ns.IsNull()) { |
+ target = ns.library(); |
+ (*imported_by)[target.index()]->Add(lib.index()); |
+ } |
+ } |
+ |
+ // Add prefixed imports to the import-by graph. |
+ DictionaryIterator entries(lib); |
+ Object& entry = Object::Handle(); |
+ LibraryPrefix& prefix = LibraryPrefix::Handle(); |
+ while (entries.HasNext()) { |
+ entry = entries.GetNext(); |
+ if (entry.IsLibraryPrefix()) { |
+ prefix ^= entry.raw(); |
+ imports = prefix.imports(); |
+ for (intptr_t import_idx = 0; import_idx < imports.Length(); |
+ import_idx++) { |
+ ns ^= imports.At(import_idx); |
+ if (!ns.IsNull()) { |
+ target = ns.library(); |
+ (*imported_by)[target.index()]->Add(lib.index()); |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ BitVector* modified_libs = new(Z) BitVector(Z, num_libs); |
+ |
+ for (intptr_t lib_idx = 0; lib_idx < num_libs; lib_idx++) { |
+ lib ^= libs.At(lib_idx); |
+ if (lib.is_dart_scheme() || modified_libs->Contains(lib_idx)) { |
+ // We don't consider dart scheme libraries during reload. If |
+ // the modified libs set already contains this library, then we |
+ // have already visited it. |
+ continue; |
+ } |
+ scripts = lib.LoadedScripts(); |
+ for (intptr_t script_idx = 0; script_idx < scripts.Length(); script_idx++) { |
+ script ^= scripts.At(script_idx); |
+ if (force_reload || ScriptModifiedSince(script, last_reload)) { |
+ modified_libs->Add(lib_idx); |
+ PropagateLibraryModified(imported_by, lib_idx, modified_libs); |
+ break; |
+ } |
+ } |
+ } |
+ |
+ return modified_libs; |
} |
@@ -462,19 +590,20 @@ void IsolateReloadContext::CheckpointLibraries() { |
num_saved_libs_ = 0; |
for (intptr_t i = 0; i < libs.Length(); i++) { |
lib ^= libs.At(i); |
- if (IsCleanLibrary(lib)) { |
+ if (modified_libs_->Contains(i)) { |
+ // We are going to reload this library. Clear the index. |
+ lib.set_index(-1); |
+ } else { |
// 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); |
} |
+ modified_libs_ = NULL; // Renumbering the libraries has invalidated this. |
old_libraries_set_storage_ = old_libraries_set.Release().raw(); |
// Reset the registered libraries to the filtered array. |
@@ -1026,11 +1155,8 @@ void IsolateReloadContext::BuildLibraryMapping() { |
Library& replacement_or_new = Library::Handle(); |
Library& old = Library::Handle(); |
- for (intptr_t i = 0; i < libs.Length(); i++) { |
+ for (intptr_t i = num_saved_libs_; 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()) { |
if (FLAG_identity_reload) { |