Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 #include "vm/isolate_reload.h" | |
| 6 | |
| 7 #include "vm/become.h" | |
| 8 #include "vm/code_generator.h" | |
| 9 #include "vm/compiler.h" | |
| 10 #include "vm/dart_api_impl.h" | |
| 11 #include "vm/hash_table.h" | |
| 12 #include "vm/isolate.h" | |
| 13 #include "vm/log.h" | |
| 14 #include "vm/object.h" | |
| 15 #include "vm/object_store.h" | |
| 16 #include "vm/parser.h" | |
| 17 #include "vm/safepoint.h" | |
| 18 #include "vm/service_event.h" | |
| 19 #include "vm/stack_frame.h" | |
| 20 #include "vm/thread.h" | |
| 21 #include "vm/timeline.h" | |
| 22 #include "vm/visitor.h" | |
| 23 | |
| 24 namespace dart { | |
| 25 | |
| 26 DEFINE_FLAG(bool, trace_reload, false, "Trace isolate reloading"); | |
| 27 DEFINE_FLAG(bool, identity_reload, false, "Enable checks for identity reload."); | |
| 28 DEFINE_FLAG(int, reload_every, 0, "Reload every N stack overflow checks."); | |
| 29 DEFINE_FLAG(bool, reload_every_optimized, true, "Only from optimized code."); | |
| 30 | |
| 31 #ifndef PRODUCT | |
| 32 | |
| 33 #define I (isolate()) | |
| 34 #define Z (thread->zone()) | |
| 35 | |
| 36 #define TIMELINE_SCOPE(name) \ | |
| 37 TimelineDurationScope tds##name(Thread::Current(), \ | |
| 38 Timeline::GetIsolateStream(), \ | |
| 39 #name) | |
| 40 | |
| 41 | |
| 42 class ScriptUrlSetTraits { | |
| 43 public: | |
| 44 static bool ReportStats() { return false; } | |
| 45 static const char* Name() { return "ScriptUrlSetTraits"; } | |
| 46 | |
| 47 static bool IsMatch(const Object& a, const Object& b) { | |
| 48 if (!a.IsString() || !b.IsString()) { | |
| 49 return false; | |
| 50 } | |
| 51 | |
| 52 return String::Cast(a).Equals(String::Cast(b)); | |
| 53 } | |
| 54 | |
| 55 static uword Hash(const Object& obj) { | |
| 56 return String::Cast(obj).Hash(); | |
| 57 } | |
| 58 }; | |
| 59 | |
| 60 | |
| 61 class ClassMapTraits { | |
| 62 public: | |
| 63 static bool ReportStats() { return false; } | |
| 64 static const char* Name() { return "ClassMapTraits"; } | |
| 65 | |
| 66 static bool IsMatch(const Object& a, const Object& b) { | |
| 67 if (!a.IsClass() || !b.IsClass()) { | |
| 68 return false; | |
| 69 } | |
| 70 return IsolateReloadContext::IsSameClass(Class::Cast(a), Class::Cast(b)); | |
| 71 } | |
| 72 | |
| 73 static uword Hash(const Object& obj) { | |
| 74 return String::HashRawSymbol(Class::Cast(obj).Name()); | |
| 75 } | |
| 76 }; | |
| 77 | |
| 78 | |
| 79 class LibraryMapTraits { | |
| 80 public: | |
| 81 static bool ReportStats() { return false; } | |
| 82 static const char* Name() { return "LibraryMapTraits"; } | |
| 83 | |
| 84 static bool IsMatch(const Object& a, const Object& b) { | |
| 85 if (!a.IsLibrary() || !b.IsLibrary()) { | |
| 86 return false; | |
| 87 } | |
| 88 return IsolateReloadContext::IsSameLibrary( | |
| 89 Library::Cast(a), Library::Cast(b)); | |
| 90 } | |
| 91 | |
| 92 static uword Hash(const Object& obj) { | |
| 93 return Library::Cast(obj).UrlHash(); | |
| 94 } | |
| 95 }; | |
| 96 | |
| 97 | |
| 98 class BecomeMapTraits { | |
| 99 public: | |
| 100 static bool ReportStats() { return false; } | |
| 101 static const char* Name() { return "BecomeMapTraits"; } | |
| 102 | |
| 103 static bool IsMatch(const Object& a, const Object& b) { | |
| 104 return a.raw() == b.raw(); | |
| 105 } | |
| 106 | |
| 107 static uword Hash(const Object& obj) { | |
| 108 if (obj.IsLibrary()) { | |
| 109 return Library::Cast(obj).UrlHash(); | |
| 110 } else if (obj.IsClass()) { | |
| 111 if (Class::Cast(obj).id() == kFreeListElement) { | |
| 112 return 0; | |
| 113 } | |
| 114 return String::HashRawSymbol(Class::Cast(obj).Name()); | |
| 115 } else if (obj.IsField()) { | |
| 116 return String::HashRawSymbol(Field::Cast(obj).name()); | |
| 117 } | |
| 118 return 0; | |
| 119 } | |
| 120 }; | |
| 121 | |
| 122 | |
| 123 bool IsolateReloadContext::IsSameField(const Field& a, const Field& b) { | |
| 124 if (a.is_static() != b.is_static()) { | |
| 125 return false; | |
| 126 } | |
| 127 const Class& a_cls = Class::Handle(a.Owner()); | |
| 128 const Class& b_cls = Class::Handle(b.Owner()); | |
| 129 | |
| 130 if (!IsSameClass(a_cls, b_cls)) { | |
| 131 return false; | |
| 132 } | |
| 133 | |
| 134 const String& a_name = String::Handle(a.name()); | |
| 135 const String& b_name = String::Handle(b.name()); | |
| 136 | |
| 137 return a_name.Equals(b_name); | |
| 138 } | |
| 139 | |
| 140 | |
| 141 bool IsolateReloadContext::IsSameClass(const Class& a, const Class& b) { | |
| 142 if (a.is_patch() != b.is_patch()) { | |
| 143 // TODO(johnmccutchan): Should we just check the class kind bits? | |
| 144 return false; | |
| 145 } | |
| 146 | |
| 147 // TODO(turnidge): We need to look at generic type arguments for | |
| 148 // synthetic mixin classes. Their names are not necessarily unique | |
| 149 // currently. | |
| 150 const String& a_name = String::Handle(Class::Cast(a).Name()); | |
| 151 const String& b_name = String::Handle(Class::Cast(b).Name()); | |
| 152 | |
| 153 if (!a_name.Equals(b_name)) { | |
| 154 return false; | |
| 155 } | |
| 156 | |
| 157 const Library& a_lib = Library::Handle(Class::Cast(a).library()); | |
| 158 const Library& b_lib = Library::Handle(Class::Cast(b).library()); | |
| 159 return IsSameLibrary(a_lib, b_lib); | |
| 160 } | |
| 161 | |
| 162 | |
| 163 bool IsolateReloadContext::IsSameLibrary( | |
| 164 const Library& a_lib, const Library& b_lib) { | |
| 165 const String& a_lib_url = | |
| 166 String::Handle(a_lib.IsNull() ? String::null() : a_lib.url()); | |
| 167 const String& b_lib_url = | |
| 168 String::Handle(b_lib.IsNull() ? String::null() : b_lib.url()); | |
| 169 return a_lib_url.Equals(b_lib_url); | |
| 170 } | |
| 171 | |
| 172 | |
| 173 IsolateReloadContext::IsolateReloadContext(Isolate* isolate, bool test_mode) | |
| 174 : start_time_micros_(OS::GetCurrentMonotonicMicros()), | |
| 175 isolate_(isolate), | |
| 176 test_mode_(test_mode), | |
| 177 has_error_(false), | |
| 178 saved_num_cids_(-1), | |
| 179 saved_class_table_(NULL), | |
| 180 num_saved_libs_(-1), | |
| 181 script_uri_(String::null()), | |
| 182 error_(Error::null()), | |
| 183 clean_scripts_set_storage_(Array::null()), | |
| 184 compile_time_constants_(Array::null()), | |
| 185 old_classes_set_storage_(Array::null()), | |
| 186 class_map_storage_(Array::null()), | |
| 187 old_libraries_set_storage_(Array::null()), | |
| 188 library_map_storage_(Array::null()), | |
| 189 become_map_storage_(Array::null()), | |
| 190 saved_root_library_(Library::null()), | |
| 191 saved_libraries_(GrowableObjectArray::null()) { | |
| 192 // Preallocate storage for maps. | |
| 193 clean_scripts_set_storage_ = | |
| 194 HashTables::New<UnorderedHashSet<ScriptUrlSetTraits> >(4); | |
| 195 old_classes_set_storage_ = | |
| 196 HashTables::New<UnorderedHashSet<ClassMapTraits> >(4); | |
| 197 class_map_storage_ = | |
| 198 HashTables::New<UnorderedHashMap<ClassMapTraits> >(4); | |
| 199 old_libraries_set_storage_ = | |
| 200 HashTables::New<UnorderedHashSet<LibraryMapTraits> >(4); | |
| 201 library_map_storage_ = | |
| 202 HashTables::New<UnorderedHashMap<LibraryMapTraits> >(4); | |
| 203 become_map_storage_ = | |
| 204 HashTables::New<UnorderedHashMap<BecomeMapTraits> >(4); | |
| 205 } | |
| 206 | |
| 207 | |
| 208 IsolateReloadContext::~IsolateReloadContext() { | |
| 209 } | |
| 210 | |
| 211 | |
| 212 void IsolateReloadContext::ReportError(const Error& error) { | |
| 213 has_error_ = true; | |
| 214 error_ = error.raw(); | |
| 215 if (FLAG_trace_reload) { | |
| 216 THR_Print("ISO-RELOAD: Error: %s\n", error.ToErrorCString()); | |
| 217 } | |
| 218 ServiceEvent service_event(Isolate::Current(), ServiceEvent::kIsolateReload); | |
| 219 service_event.set_reload_error(&error); | |
| 220 Service::HandleEvent(&service_event); | |
| 221 } | |
| 222 | |
| 223 | |
| 224 void IsolateReloadContext::ReportError(const String& error_msg) { | |
| 225 ReportError(LanguageError::Handle(LanguageError::New(error_msg))); | |
| 226 } | |
| 227 | |
| 228 | |
| 229 void IsolateReloadContext::ReportSuccess() { | |
| 230 ServiceEvent service_event(Isolate::Current(), ServiceEvent::kIsolateReload); | |
| 231 Service::HandleEvent(&service_event); | |
| 232 } | |
| 233 | |
| 234 | |
| 235 void IsolateReloadContext::StartReload() { | |
| 236 Thread* thread = Thread::Current(); | |
| 237 | |
| 238 // Grab root library before calling CheckpointBeforeReload. | |
| 239 const Library& root_lib = Library::Handle(object_store()->root_library()); | |
| 240 ASSERT(!root_lib.IsNull()); | |
| 241 const String& root_lib_url = String::Handle(root_lib.url()); | |
| 242 | |
| 243 // Ensure all functions on the stack have unoptimized code. | |
| 244 EnsuredUnoptimizedCodeForStack(); | |
| 245 // Deoptimize all code that had optimizing decisions that are dependent on | |
| 246 // assumptions from field guards or CHA or deferred library prefixes. | |
| 247 // TODO(johnmccutchan): Deoptimizing dependent code here (before the reload) | |
| 248 // is paranoid. This likely can be moved to the commit phase. | |
| 249 DeoptimizeDependentCode(); | |
| 250 Checkpoint(); | |
| 251 | |
| 252 // Block class finalization attempts when calling into the library | |
| 253 // tag handler. | |
| 254 I->BlockClassFinalization(); | |
| 255 Object& result = Object::Handle(thread->zone()); | |
| 256 { | |
| 257 TransitionVMToNative transition(thread); | |
| 258 Api::Scope api_scope(thread); | |
| 259 | |
| 260 Dart_Handle retval = | |
| 261 (I->library_tag_handler())(Dart_kScriptTag, | |
| 262 Api::NewHandle(thread, Library::null()), | |
| 263 Api::NewHandle(thread, root_lib_url.raw())); | |
| 264 result = Api::UnwrapHandle(retval); | |
| 265 } | |
| 266 I->UnblockClassFinalization(); | |
| 267 if (result.IsError()) { | |
| 268 ReportError(Error::Cast(result)); | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 | |
| 273 void IsolateReloadContext::RegisterClass(const Class& new_cls) { | |
| 274 const Class& old_cls = Class::Handle(OldClassOrNull(new_cls)); | |
| 275 if (old_cls.IsNull()) { | |
| 276 Isolate::Current()->class_table()->Register(new_cls); | |
| 277 | |
| 278 if (FLAG_identity_reload) { | |
| 279 TIR_Print("Could not find replacement class for %s\n", | |
| 280 new_cls.ToCString()); | |
| 281 UNREACHABLE(); | |
| 282 } | |
| 283 | |
| 284 // New class maps to itself. | |
| 285 AddClassMapping(new_cls, new_cls); | |
| 286 return; | |
| 287 } | |
| 288 new_cls.set_id(old_cls.id()); | |
| 289 isolate()->class_table()->SetAt(old_cls.id(), new_cls.raw()); | |
| 290 if (!old_cls.is_enum_class()) { | |
| 291 new_cls.CopyCanonicalConstants(old_cls); | |
| 292 } | |
| 293 new_cls.CopyCanonicalTypes(old_cls); | |
| 294 AddBecomeMapping(old_cls, new_cls); | |
| 295 AddClassMapping(new_cls, old_cls); | |
| 296 } | |
| 297 | |
| 298 | |
| 299 void IsolateReloadContext::FinishReload() { | |
| 300 // Disable the background compiler while we are performing the reload. | |
| 301 BackgroundCompiler::Disable(); | |
|
rmacnak
2016/05/13 17:59:34
This should happen before DeoptimizeDependentCode.
| |
| 302 | |
| 303 BuildLibraryMapping(); | |
| 304 TIR_Print("---- DONE FINALIZING\n"); | |
| 305 if (ValidateReload()) { | |
| 306 Commit(); | |
| 307 PostCommit(); | |
| 308 } else { | |
| 309 Rollback(); | |
| 310 } | |
| 311 // ValidateReload mutates the direct subclass information and does | |
| 312 // not remove dead subclasses. Rebuild the direct subclass | |
| 313 // information from scratch. | |
| 314 RebuildDirectSubclasses(); | |
| 315 | |
| 316 BackgroundCompiler::Enable(); | |
| 317 } | |
| 318 | |
| 319 | |
| 320 void IsolateReloadContext::AbortReload(const Error& error) { | |
| 321 ReportError(error); | |
| 322 Rollback(); | |
| 323 } | |
| 324 | |
| 325 | |
| 326 void IsolateReloadContext::EnsuredUnoptimizedCodeForStack() { | |
| 327 TIMELINE_SCOPE(EnsuredUnoptimizedCodeForStack); | |
| 328 StackFrameIterator it(StackFrameIterator::kDontValidateFrames); | |
| 329 | |
| 330 Function& func = Function::Handle(); | |
| 331 while (it.HasNextFrame()) { | |
| 332 StackFrame* frame = it.NextFrame(); | |
| 333 if (frame->IsDartFrame()) { | |
| 334 func = frame->LookupDartFunction(); | |
| 335 ASSERT(!func.IsNull()); | |
| 336 func.EnsureHasCompiledUnoptimizedCode(); | |
| 337 } | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 | |
| 342 void IsolateReloadContext::DeoptimizeDependentCode() { | |
| 343 ClassTable* class_table = I->class_table(); | |
| 344 | |
| 345 const intptr_t bottom = Dart::vm_isolate()->class_table()->NumCids(); | |
| 346 const intptr_t top = I->class_table()->NumCids(); | |
| 347 Class& cls = Class::Handle(); | |
| 348 Array& fields = Array::Handle(); | |
| 349 Field& field = Field::Handle(); | |
| 350 for (intptr_t cls_idx = bottom; cls_idx < top; cls_idx++) { | |
| 351 if (!class_table->HasValidClassAt(cls_idx)) { | |
| 352 // Skip. | |
| 353 continue; | |
| 354 } | |
| 355 | |
| 356 // Deoptimize CHA code. | |
| 357 cls = class_table->At(cls_idx); | |
| 358 ASSERT(!cls.IsNull()); | |
| 359 | |
| 360 cls.DisableAllCHAOptimizedCode(); | |
| 361 | |
| 362 // Deoptimize field guard code. | |
| 363 fields = cls.fields(); | |
| 364 ASSERT(!fields.IsNull()); | |
| 365 for (intptr_t field_idx = 0; field_idx < fields.Length(); field_idx++) { | |
| 366 field = Field::RawCast(fields.At(field_idx)); | |
| 367 ASSERT(!field.IsNull()); | |
| 368 field.DeoptimizeDependentCode(); | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 // TODO(johnmccutchan): Also call LibraryPrefix::InvalidateDependentCode. | |
| 373 } | |
| 374 | |
| 375 | |
| 376 void IsolateReloadContext::CheckpointClasses() { | |
| 377 TIMELINE_SCOPE(CheckpointClasses); | |
| 378 TIR_Print("---- CHECKPOINTING CLASSES\n"); | |
| 379 // Checkpoint classes before a reload. We need to copy the following: | |
| 380 // 1) The size of the class table. | |
| 381 // 2) The class table itself. | |
| 382 // For efficiency, we build a set of classes before the reload. This set | |
| 383 // is used to pair new classes with old classes. | |
| 384 | |
| 385 ClassTable* class_table = I->class_table(); | |
| 386 | |
| 387 // Copy the size of the class table. | |
| 388 saved_num_cids_ = I->class_table()->NumCids(); | |
| 389 | |
| 390 // Copy of the class table. | |
| 391 RawClass** local_saved_class_table = | |
| 392 reinterpret_cast<RawClass**>(malloc(sizeof(RawClass*) * saved_num_cids_)); | |
| 393 | |
| 394 Class& cls = Class::Handle(); | |
| 395 UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_); | |
| 396 for (intptr_t i = 0; i < saved_num_cids_; i++) { | |
| 397 if (class_table->IsValidIndex(i) && | |
| 398 class_table->HasValidClassAt(i)) { | |
| 399 // Copy the class into the saved class table and add it to the set. | |
| 400 local_saved_class_table[i] = class_table->At(i); | |
| 401 if (i != kFreeListElement) { | |
| 402 cls = class_table->At(i); | |
| 403 bool already_present = old_classes_set.Insert(cls); | |
| 404 ASSERT(!already_present); | |
| 405 } | |
| 406 } else { | |
| 407 // No class at this index, mark it as NULL. | |
| 408 local_saved_class_table[i] = NULL; | |
| 409 } | |
| 410 } | |
| 411 old_classes_set_storage_ = old_classes_set.Release().raw(); | |
| 412 // Assigning the field must be done after saving the class table. | |
| 413 saved_class_table_ = local_saved_class_table; | |
| 414 TIR_Print("---- System had %" Pd " classes\n", saved_num_cids_); | |
| 415 } | |
| 416 | |
| 417 | |
| 418 bool IsolateReloadContext::IsCleanLibrary(const Library& lib) { | |
| 419 return lib.is_dart_scheme(); | |
| 420 } | |
| 421 | |
| 422 | |
| 423 void IsolateReloadContext::CheckpointLibraries() { | |
| 424 TIMELINE_SCOPE(CheckpointLibraries); | |
| 425 | |
| 426 // Save the root library in case we abort the reload. | |
| 427 const Library& root_lib = | |
| 428 Library::Handle(object_store()->root_library()); | |
| 429 set_saved_root_library(root_lib); | |
| 430 | |
| 431 // Save the old libraries array in case we abort the reload. | |
| 432 const GrowableObjectArray& libs = | |
| 433 GrowableObjectArray::Handle(object_store()->libraries()); | |
| 434 set_saved_libraries(libs); | |
| 435 | |
| 436 // Make a filtered copy of the old libraries array. Keep "clean" libraries | |
| 437 // that we will use instead of reloading. | |
| 438 const GrowableObjectArray& new_libs = GrowableObjectArray::Handle( | |
| 439 GrowableObjectArray::New(Heap::kOld)); | |
| 440 Library& lib = Library::Handle(); | |
| 441 UnorderedHashSet<LibraryMapTraits> | |
| 442 old_libraries_set(old_libraries_set_storage_); | |
| 443 num_saved_libs_ = 0; | |
| 444 for (intptr_t i = 0; i < libs.Length(); i++) { | |
| 445 lib ^= libs.At(i); | |
| 446 if (IsCleanLibrary(lib)) { | |
| 447 // We are preserving this library across the reload, assign its new index | |
| 448 lib.set_index(new_libs.Length()); | |
| 449 new_libs.Add(lib, Heap::kOld); | |
| 450 num_saved_libs_++; | |
| 451 } else { | |
| 452 // We are going to reload this library. Clear the index. | |
| 453 lib.set_index(-1); | |
| 454 } | |
| 455 // Add old library to old libraries set. | |
| 456 bool already_present = old_libraries_set.Insert(lib); | |
| 457 ASSERT(!already_present); | |
| 458 } | |
| 459 old_libraries_set_storage_ = old_libraries_set.Release().raw(); | |
| 460 | |
| 461 // Reset the registered libraries to the filtered array. | |
| 462 Library::RegisterLibraries(Thread::Current(), new_libs); | |
| 463 // Reset the root library to null. | |
| 464 object_store()->set_root_library(Library::Handle()); | |
| 465 } | |
| 466 | |
| 467 | |
| 468 void IsolateReloadContext::BuildCleanScriptSet() { | |
| 469 const GrowableObjectArray& libs = | |
| 470 GrowableObjectArray::Handle(object_store()->libraries()); | |
| 471 | |
| 472 UnorderedHashSet<ScriptUrlSetTraits> | |
| 473 clean_scripts_set(clean_scripts_set_storage_); | |
| 474 | |
| 475 Library& lib = Library::Handle(); | |
| 476 Array& scripts = Array::Handle(); | |
| 477 Script& script = Script::Handle(); | |
| 478 String& script_url = String::Handle(); | |
| 479 for (intptr_t lib_idx = 0; lib_idx < libs.Length(); lib_idx++) { | |
| 480 lib = Library::RawCast(libs.At(lib_idx)); | |
| 481 ASSERT(!lib.IsNull()); | |
| 482 ASSERT(IsCleanLibrary(lib)); | |
| 483 scripts = lib.LoadedScripts(); | |
| 484 ASSERT(!scripts.IsNull()); | |
| 485 for (intptr_t script_idx = 0; script_idx < scripts.Length(); script_idx++) { | |
| 486 script = Script::RawCast(scripts.At(script_idx)); | |
| 487 ASSERT(!script.IsNull()); | |
| 488 script_url = script.url(); | |
| 489 ASSERT(!script_url.IsNull()); | |
| 490 bool already_present = clean_scripts_set.Insert(script_url); | |
| 491 ASSERT(!already_present); | |
| 492 } | |
| 493 } | |
| 494 | |
| 495 clean_scripts_set_storage_ = clean_scripts_set.Release().raw(); | |
| 496 } | |
| 497 | |
| 498 | |
| 499 void IsolateReloadContext::FilterCompileTimeConstants() { | |
| 500 // Save the compile time constants array. | |
| 501 compile_time_constants_ = I->object_store()->compile_time_constants(); | |
| 502 // Clear the compile time constants array. This will be repopulated | |
| 503 // in the loop below. | |
| 504 I->object_store()->set_compile_time_constants(Array::Handle()); | |
| 505 | |
| 506 if (compile_time_constants_ == Array::null()) { | |
| 507 // Nothing to do. | |
| 508 return; | |
| 509 } | |
| 510 | |
| 511 // Iterate over the saved compile time constants map. | |
| 512 ConstantsMap old_constants(compile_time_constants_); | |
| 513 ConstantsMap::Iterator it(&old_constants); | |
| 514 | |
| 515 Array& key = Array::Handle(); | |
| 516 String& url = String::Handle(); | |
| 517 Smi& token_pos = Smi::Handle(); | |
| 518 Instance& value = Instance::Handle(); | |
| 519 | |
| 520 // We filter the compile time constants map so that after it only contains | |
| 521 // constants from scripts contained in this set. | |
| 522 UnorderedHashSet<ScriptUrlSetTraits> | |
| 523 clean_scripts_set(clean_scripts_set_storage_); | |
| 524 | |
| 525 while (it.MoveNext()) { | |
| 526 const intptr_t entry = it.Current(); | |
| 527 ASSERT(entry != -1); | |
| 528 key = Array::RawCast(old_constants.GetKey(entry)); | |
| 529 ASSERT(!key.IsNull()); | |
| 530 url = String::RawCast(key.At(0)); | |
| 531 ASSERT(!url.IsNull()); | |
| 532 if (clean_scripts_set.ContainsKey(url)) { | |
| 533 // We've found a cached constant from a clean script, add it to the | |
| 534 // compile time constants map again. | |
| 535 token_pos = Smi::RawCast(key.At(1)); | |
| 536 TokenPosition tp(token_pos.Value()); | |
| 537 // Use ^= because this might be null. | |
| 538 value ^= old_constants.GetPayload(entry, 0); | |
| 539 Parser::InsertCachedConstantValue(url, tp, value); | |
| 540 } | |
| 541 } | |
| 542 | |
| 543 old_constants.Release(); | |
| 544 clean_scripts_set.Release(); | |
| 545 } | |
| 546 | |
| 547 | |
| 548 // While reloading everything we do must be reversible so that we can abort | |
| 549 // safely if the reload fails. This function stashes things to the side and | |
| 550 // prepares the isolate for the reload attempt. | |
| 551 void IsolateReloadContext::Checkpoint() { | |
| 552 TIMELINE_SCOPE(Checkpoint); | |
| 553 CheckpointClasses(); | |
| 554 CheckpointLibraries(); | |
| 555 BuildCleanScriptSet(); | |
| 556 FilterCompileTimeConstants(); | |
| 557 } | |
| 558 | |
| 559 | |
| 560 void IsolateReloadContext::RollbackClasses() { | |
| 561 TIR_Print("---- ROLLING BACK CLASS TABLE\n"); | |
| 562 ASSERT(saved_num_cids_ > 0); | |
| 563 ASSERT(saved_class_table_ != NULL); | |
| 564 ClassTable* class_table = I->class_table(); | |
| 565 class_table->SetNumCids(saved_num_cids_); | |
| 566 // Overwrite classes in class table with the saved classes. | |
| 567 for (intptr_t i = 0; i < saved_num_cids_; i++) { | |
| 568 if (class_table->IsValidIndex(i)) { | |
| 569 class_table->SetAt(i, saved_class_table_[i]); | |
| 570 } | |
| 571 } | |
| 572 free(saved_class_table_); | |
| 573 saved_class_table_ = NULL; | |
| 574 saved_num_cids_ = 0; | |
| 575 } | |
| 576 | |
| 577 | |
| 578 void IsolateReloadContext::RollbackLibraries() { | |
| 579 TIR_Print("---- ROLLING BACK LIBRARY CHANGES\n"); | |
| 580 Thread* thread = Thread::Current(); | |
| 581 Library& lib = Library::Handle(); | |
| 582 GrowableObjectArray& saved_libs = GrowableObjectArray::Handle( | |
| 583 Z, saved_libraries()); | |
| 584 if (!saved_libs.IsNull()) { | |
| 585 for (intptr_t i = 0; i < saved_libs.Length(); i++) { | |
| 586 lib = Library::RawCast(saved_libs.At(i)); | |
| 587 // Restore indexes that were modified in CheckpointLibraries. | |
| 588 lib.set_index(i); | |
| 589 } | |
| 590 | |
| 591 // Reset the registered libraries to the filtered array. | |
| 592 Library::RegisterLibraries(Thread::Current(), saved_libs); | |
| 593 } | |
| 594 | |
| 595 Library& saved_root_lib = Library::Handle(Z, saved_root_library()); | |
| 596 if (!saved_root_lib.IsNull()) { | |
| 597 object_store()->set_root_library(saved_root_lib); | |
| 598 } | |
| 599 | |
| 600 set_saved_root_library(Library::Handle()); | |
| 601 set_saved_libraries(GrowableObjectArray::Handle()); | |
| 602 } | |
| 603 | |
| 604 | |
| 605 void IsolateReloadContext::Rollback() { | |
| 606 I->object_store()->set_compile_time_constants( | |
| 607 Array::Handle(compile_time_constants_)); | |
| 608 RollbackClasses(); | |
| 609 RollbackLibraries(); | |
| 610 } | |
| 611 | |
| 612 | |
| 613 #ifdef DEBUG | |
| 614 void IsolateReloadContext::VerifyMaps() { | |
| 615 Class& cls = Class::Handle(); | |
| 616 Class& new_cls = Class::Handle(); | |
| 617 Class& cls2 = Class::Handle(); | |
| 618 Class& new_cls2 = Class::Handle(); | |
| 619 | |
| 620 // Verify that two old classes aren't both mapped to the same new | |
| 621 // class. This could happen is the IsSameClass function is broken. | |
| 622 UnorderedHashMap<ClassMapTraits> class_map(class_map_storage_); | |
| 623 { | |
| 624 UnorderedHashMap<ClassMapTraits>::Iterator it(&class_map); | |
| 625 while (it.MoveNext()) { | |
| 626 const intptr_t entry = it.Current(); | |
| 627 new_cls = Class::RawCast(class_map.GetKey(entry)); | |
| 628 cls = Class::RawCast(class_map.GetPayload(entry, 0)); | |
| 629 if (new_cls.raw() != cls.raw()) { | |
| 630 UnorderedHashMap<ClassMapTraits>::Iterator it2(&class_map); | |
| 631 while (it2.MoveNext()) { | |
| 632 new_cls2 = Class::RawCast(class_map.GetKey(entry)); | |
| 633 if (new_cls.raw() == new_cls2.raw()) { | |
| 634 cls2 = Class::RawCast(class_map.GetPayload(entry, 0)); | |
| 635 if (cls.raw() != cls2.raw()) { | |
| 636 OS::PrintErr( | |
| 637 "Classes '%s' and '%s' are distinct classes but both map to " | |
| 638 "class '%s'\n", | |
| 639 cls.ToCString(), cls2.ToCString(), new_cls.ToCString()); | |
| 640 UNREACHABLE(); | |
| 641 } | |
| 642 } | |
| 643 } | |
| 644 } | |
| 645 } | |
| 646 } | |
| 647 class_map.Release(); | |
| 648 } | |
| 649 | |
| 650 | |
| 651 void IsolateReloadContext::VerifyCanonicalTypeArguments() { | |
| 652 Thread* thread = Thread::Current(); | |
| 653 const Array& table = | |
| 654 Array::Handle(Z, I->object_store()->canonical_type_arguments()); | |
| 655 const intptr_t table_size = table.Length() - 1; | |
| 656 ASSERT(Utils::IsPowerOfTwo(table_size)); | |
| 657 TypeArguments& element = TypeArguments::Handle(Z); | |
| 658 TypeArguments& other_element = TypeArguments::Handle(); | |
| 659 for (intptr_t i = 0; i < table_size; i++) { | |
| 660 element ^= table.At(i); | |
| 661 for (intptr_t j = 0; j < table_size; j++) { | |
| 662 if ((i != j) && (table.At(j) != TypeArguments::null())) { | |
| 663 other_element ^= table.At(j); | |
| 664 if (element.Equals(other_element)) { | |
| 665 // Recursive types may be equal, but have different hashes. | |
| 666 ASSERT(element.IsRecursive()); | |
| 667 ASSERT(other_element.IsRecursive()); | |
| 668 ASSERT(element.Hash() != other_element.Hash()); | |
| 669 } | |
| 670 } | |
| 671 } | |
| 672 } | |
| 673 } | |
| 674 #endif | |
| 675 | |
| 676 | |
| 677 void IsolateReloadContext::Commit() { | |
| 678 TIMELINE_SCOPE(Commit); | |
| 679 TIR_Print("---- COMMITTING REVERSE MAP\n"); | |
| 680 | |
| 681 #ifdef DEBUG | |
| 682 VerifyMaps(); | |
| 683 #endif | |
| 684 | |
| 685 { | |
| 686 TIMELINE_SCOPE(CopyStaticFieldsAndPatchFieldsAndFunctions); | |
| 687 // Copy static field values from the old classes to the new classes. | |
| 688 // Patch fields and functions in the old classes so that they retain | |
| 689 // the old script. | |
| 690 Class& cls = Class::Handle(); | |
| 691 Class& new_cls = Class::Handle(); | |
| 692 | |
| 693 UnorderedHashMap<ClassMapTraits> class_map(class_map_storage_); | |
| 694 | |
| 695 { | |
| 696 UnorderedHashMap<ClassMapTraits>::Iterator it(&class_map); | |
| 697 while (it.MoveNext()) { | |
| 698 const intptr_t entry = it.Current(); | |
| 699 new_cls = Class::RawCast(class_map.GetKey(entry)); | |
| 700 cls = Class::RawCast(class_map.GetPayload(entry, 0)); | |
| 701 if (new_cls.raw() != cls.raw()) { | |
| 702 ASSERT(new_cls.is_enum_class() == cls.is_enum_class()); | |
| 703 if (new_cls.is_enum_class() && new_cls.is_finalized()) { | |
| 704 new_cls.ReplaceEnum(cls); | |
| 705 } | |
| 706 new_cls.CopyStaticFieldValues(cls); | |
| 707 cls.PatchFieldsAndFunctions(); | |
| 708 } | |
| 709 } | |
| 710 } | |
| 711 | |
| 712 class_map.Release(); | |
| 713 } | |
| 714 | |
| 715 // Copy over certain properties of libraries, e.g. is the library | |
| 716 // debuggable? | |
| 717 { | |
| 718 TIMELINE_SCOPE(CopyLibraryBits); | |
| 719 Library& lib = Library::Handle(); | |
| 720 Library& new_lib = Library::Handle(); | |
| 721 | |
| 722 UnorderedHashMap<LibraryMapTraits> lib_map(library_map_storage_); | |
| 723 | |
| 724 { | |
| 725 // Reload existing libraries. | |
| 726 UnorderedHashMap<LibraryMapTraits>::Iterator it(&lib_map); | |
| 727 | |
| 728 while (it.MoveNext()) { | |
| 729 const intptr_t entry = it.Current(); | |
| 730 ASSERT(entry != -1); | |
| 731 new_lib = Library::RawCast(lib_map.GetKey(entry)); | |
| 732 lib = Library::RawCast(lib_map.GetPayload(entry, 0)); | |
| 733 new_lib.set_debuggable(lib.IsDebuggable()); | |
| 734 } | |
| 735 } | |
| 736 | |
| 737 // Release the library map. | |
| 738 lib_map.Release(); | |
| 739 } | |
| 740 | |
| 741 { | |
| 742 TIMELINE_SCOPE(UpdateLibrariesArray); | |
| 743 // Update the libraries array. | |
| 744 Library& lib = Library::Handle(); | |
| 745 const GrowableObjectArray& libs = GrowableObjectArray::Handle( | |
| 746 I->object_store()->libraries()); | |
| 747 for (intptr_t i = 0; i < libs.Length(); i++) { | |
| 748 lib = Library::RawCast(libs.At(i)); | |
| 749 TIR_Print("Lib '%s' at index %" Pd "\n", lib.ToCString(), i); | |
| 750 lib.set_index(i); | |
| 751 } | |
| 752 | |
| 753 // Initialize library side table. | |
| 754 library_infos_.SetLength(libs.Length()); | |
| 755 for (intptr_t i = 0; i < libs.Length(); i++) { | |
| 756 lib = Library::RawCast(libs.At(i)); | |
| 757 // Mark the library dirty if it comes after the libraries we saved. | |
| 758 library_infos_[i].dirty = i >= num_saved_libs_; | |
| 759 } | |
| 760 } | |
| 761 | |
| 762 { | |
| 763 UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_); | |
| 764 intptr_t replacement_count = become_map.NumOccupied(); | |
| 765 const Array& before = | |
| 766 Array::Handle(Array::New(replacement_count, Heap::kOld)); | |
| 767 const Array& after = | |
| 768 Array::Handle(Array::New(replacement_count, Heap::kOld)); | |
| 769 Object& obj = Object::Handle(); | |
| 770 intptr_t replacement_index = 0; | |
| 771 UnorderedHashMap<BecomeMapTraits>::Iterator it(&become_map); | |
| 772 while (it.MoveNext()) { | |
| 773 const intptr_t entry = it.Current(); | |
| 774 obj = become_map.GetKey(entry); | |
| 775 before.SetAt(replacement_index, obj); | |
| 776 obj = become_map.GetPayload(entry, 0); | |
| 777 after.SetAt(replacement_index, obj); | |
| 778 replacement_index++; | |
| 779 } | |
| 780 ASSERT(replacement_index == replacement_count); | |
| 781 become_map.Release(); | |
| 782 | |
| 783 Become::ElementsForwardIdentity(before, after); | |
| 784 } | |
| 785 | |
| 786 if (FLAG_identity_reload) { | |
| 787 if (saved_num_cids_ != I->class_table()->NumCids()) { | |
| 788 TIR_Print("Identity reload failed! B#C=%" Pd " A#C=%" Pd "\n", | |
| 789 saved_num_cids_, | |
| 790 I->class_table()->NumCids()); | |
| 791 } | |
| 792 const GrowableObjectArray& saved_libs = | |
| 793 GrowableObjectArray::Handle(saved_libraries()); | |
| 794 const GrowableObjectArray& libs = | |
| 795 GrowableObjectArray::Handle(I->object_store()->libraries()); | |
| 796 if (saved_libs.Length() != libs.Length()) { | |
| 797 TIR_Print("Identity reload failed! B#L=%" Pd " A#L=%" Pd "\n", | |
| 798 saved_libs.Length(), | |
| 799 libs.Length()); | |
| 800 } | |
| 801 } | |
| 802 | |
| 803 #ifdef DEBUG | |
| 804 // TODO(turnidge): Remove before committing to main branch. | |
| 805 VerifyCanonicalTypeArguments(); | |
| 806 #endif | |
| 807 } | |
| 808 | |
| 809 | |
| 810 bool IsolateReloadContext::IsDirty(const Library& lib) { | |
| 811 const intptr_t index = lib.index(); | |
| 812 if (index == static_cast<classid_t>(-1)) { | |
| 813 // Treat deleted libraries as dirty. | |
| 814 return true; | |
| 815 } | |
| 816 ASSERT((index >= 0) && (index < library_infos_.length())); | |
| 817 return library_infos_[index].dirty; | |
| 818 } | |
| 819 | |
| 820 | |
| 821 void IsolateReloadContext::PostCommit() { | |
| 822 TIMELINE_SCOPE(PostCommit); | |
| 823 set_saved_root_library(Library::Handle()); | |
| 824 set_saved_libraries(GrowableObjectArray::Handle()); | |
| 825 InvalidateWorld(); | |
| 826 } | |
| 827 | |
| 828 | |
| 829 bool IsolateReloadContext::ValidateReload() { | |
| 830 TIMELINE_SCOPE(ValidateReload); | |
| 831 if (has_error_) { | |
| 832 return false; | |
| 833 } | |
| 834 | |
| 835 // Already built. | |
| 836 ASSERT(class_map_storage_ != Array::null()); | |
| 837 UnorderedHashMap<ClassMapTraits> map(class_map_storage_); | |
| 838 UnorderedHashMap<ClassMapTraits>::Iterator it(&map); | |
| 839 Class& cls = Class::Handle(); | |
| 840 Class& new_cls = Class::Handle(); | |
| 841 while (it.MoveNext()) { | |
| 842 const intptr_t entry = it.Current(); | |
| 843 new_cls = Class::RawCast(map.GetKey(entry)); | |
| 844 cls = Class::RawCast(map.GetPayload(entry, 0)); | |
| 845 if (new_cls.raw() != cls.raw()) { | |
| 846 if (!cls.CanReload(new_cls)) { | |
| 847 map.Release(); | |
| 848 return false; | |
| 849 } | |
| 850 } | |
| 851 } | |
| 852 map.Release(); | |
| 853 return true; | |
| 854 } | |
| 855 | |
| 856 | |
| 857 RawClass* IsolateReloadContext::FindOriginalClass(const Class& cls) { | |
| 858 return MappedClass(cls); | |
| 859 } | |
| 860 | |
| 861 | |
| 862 RawClass* IsolateReloadContext::GetClassForHeapWalkAt(intptr_t cid) { | |
| 863 if (saved_class_table_ != NULL) { | |
| 864 ASSERT(cid > 0); | |
| 865 ASSERT(cid < saved_num_cids_); | |
| 866 return saved_class_table_[cid]; | |
| 867 } else { | |
| 868 return isolate_->class_table()->At(cid); | |
| 869 } | |
| 870 } | |
| 871 | |
| 872 | |
| 873 RawLibrary* IsolateReloadContext::saved_root_library() const { | |
| 874 return saved_root_library_; | |
| 875 } | |
| 876 | |
| 877 | |
| 878 void IsolateReloadContext::set_saved_root_library(const Library& value) { | |
| 879 saved_root_library_ = value.raw(); | |
| 880 } | |
| 881 | |
| 882 | |
| 883 RawGrowableObjectArray* IsolateReloadContext::saved_libraries() const { | |
| 884 return saved_libraries_; | |
| 885 } | |
| 886 | |
| 887 | |
| 888 void IsolateReloadContext::set_saved_libraries( | |
| 889 const GrowableObjectArray& value) { | |
| 890 saved_libraries_ = value.raw(); | |
| 891 } | |
| 892 | |
| 893 | |
| 894 void IsolateReloadContext::VisitObjectPointers(ObjectPointerVisitor* visitor) { | |
| 895 visitor->VisitPointers(from(), to()); | |
| 896 if (saved_class_table_ != NULL) { | |
| 897 visitor->VisitPointers( | |
| 898 reinterpret_cast<RawObject**>(&saved_class_table_[0]), saved_num_cids_); | |
| 899 } | |
| 900 } | |
| 901 | |
| 902 | |
| 903 ObjectStore* IsolateReloadContext::object_store() { | |
| 904 return isolate_->object_store(); | |
| 905 } | |
| 906 | |
| 907 | |
| 908 static void ResetICs(const Function& function, const Code& code) { | |
| 909 // TODO(johnmccutchan): Relying on the function's ICData Map can miss ICDatas. | |
| 910 // Use the code's object pool instead. | |
| 911 if (function.ic_data_array() == Array::null()) { | |
| 912 // TODO(johnmccutchan): Even in this case, we need to scan the code's object | |
| 913 // pool instead. | |
| 914 return; // Already reset in an earlier round. | |
| 915 } | |
| 916 | |
| 917 Thread* thread = Thread::Current(); | |
| 918 Zone* zone = thread->zone(); | |
| 919 | |
| 920 ZoneGrowableArray<const ICData*>* ic_data_array = | |
| 921 new(zone) ZoneGrowableArray<const ICData*>(); | |
| 922 function.RestoreICDataMap(ic_data_array, false /* clone ic-data */); | |
| 923 const intptr_t ic_data_array_length = ic_data_array->length(); | |
| 924 if (ic_data_array_length == 0) { | |
| 925 return; | |
| 926 } | |
| 927 const PcDescriptors& descriptors = | |
| 928 PcDescriptors::Handle(code.pc_descriptors()); | |
| 929 PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kIcCall | | |
| 930 RawPcDescriptors::kUnoptStaticCall); | |
| 931 while (iter.MoveNext()) { | |
| 932 const intptr_t index = iter.DeoptId(); | |
| 933 if (index >= ic_data_array_length) { | |
| 934 // TODO(johnmccutchan): Investigate how this can happen. | |
| 935 continue; | |
| 936 } | |
| 937 const ICData* ic_data = (*ic_data_array)[index]; | |
| 938 if (ic_data == NULL) { | |
| 939 // TODO(johnmccutchan): Investigate how this can happen. | |
| 940 continue; | |
| 941 } | |
| 942 bool is_static_call = iter.Kind() == RawPcDescriptors::kUnoptStaticCall; | |
| 943 ic_data->Reset(is_static_call); | |
| 944 } | |
| 945 } | |
| 946 | |
| 947 | |
| 948 void IsolateReloadContext::ResetUnoptimizedICsOnStack() { | |
| 949 Code& code = Code::Handle(); | |
| 950 Function& function = Function::Handle(); | |
| 951 DartFrameIterator iterator; | |
| 952 StackFrame* frame = iterator.NextFrame(); | |
| 953 while (frame != NULL) { | |
| 954 code = frame->LookupDartCode(); | |
| 955 if (code.is_optimized()) { | |
| 956 // If this code is optimized, we need to reset the ICs in the | |
| 957 // corresponding unoptimized code, which will be executed when the stack | |
| 958 // unwinds to the the optimized code. | |
| 959 function = code.function(); | |
| 960 code = function.unoptimized_code(); | |
| 961 ASSERT(!code.IsNull()); | |
| 962 ResetICs(function, code); | |
| 963 } else { | |
| 964 function = code.function(); | |
| 965 ResetICs(function, code); | |
| 966 } | |
| 967 frame = iterator.NextFrame(); | |
| 968 } | |
| 969 } | |
| 970 | |
| 971 | |
| 972 void IsolateReloadContext::ResetMegamorphicCaches() { | |
| 973 object_store()->set_megamorphic_cache_table(GrowableObjectArray::Handle()); | |
| 974 // Since any current optimized code will not make any more calls, it may be | |
| 975 // better to clear the table instead of clearing each of the caches, allow | |
| 976 // the current megamorphic caches get GC'd and any new optimized code allocate | |
| 977 // new ones. | |
| 978 } | |
| 979 | |
| 980 | |
| 981 class MarkFunctionsForRecompilation : public ObjectVisitor { | |
| 982 public: | |
| 983 MarkFunctionsForRecompilation(Isolate* isolate, | |
| 984 IsolateReloadContext* reload_context) | |
| 985 : ObjectVisitor(), | |
| 986 handle_(Object::Handle()), | |
| 987 owning_class_(Class::Handle()), | |
| 988 owning_lib_(Library::Handle()), | |
| 989 code_(Code::Handle()), | |
| 990 reload_context_(reload_context) { | |
| 991 } | |
| 992 | |
| 993 virtual void VisitObject(RawObject* obj) { | |
| 994 // Free-list elements cannot even be wrapped in handles. | |
| 995 if (obj->IsFreeListElement()) { | |
| 996 return; | |
| 997 } | |
| 998 handle_ = obj; | |
| 999 if (handle_.IsFunction()) { | |
| 1000 const Function& func = Function::Cast(handle_); | |
| 1001 | |
| 1002 // Switch to unoptimized code or the lazy compilation stub. | |
| 1003 func.SwitchToLazyCompiledUnoptimizedCode(); | |
| 1004 | |
| 1005 // Grab the current code. | |
| 1006 code_ = func.CurrentCode(); | |
| 1007 ASSERT(!code_.IsNull()); | |
| 1008 const bool clear_code = IsFromDirtyLibrary(func); | |
| 1009 const bool stub_code = code_.IsStubCode(); | |
| 1010 | |
| 1011 // Zero edge counters. | |
| 1012 func.ZeroEdgeCounters(); | |
| 1013 | |
| 1014 if (!stub_code) { | |
| 1015 if (clear_code) { | |
| 1016 ClearAllCode(func); | |
| 1017 } else { | |
| 1018 PreserveUnoptimizedCode(func); | |
| 1019 } | |
| 1020 } | |
| 1021 | |
| 1022 // Clear counters. | |
| 1023 func.set_usage_counter(0); | |
| 1024 func.set_deoptimization_counter(0); | |
| 1025 func.set_optimized_instruction_count(0); | |
| 1026 func.set_optimized_call_site_count(0); | |
| 1027 } | |
| 1028 } | |
| 1029 | |
| 1030 private: | |
| 1031 void ClearAllCode(const Function& func) { | |
| 1032 // Null out the ICData array and code. | |
| 1033 func.ClearICDataArray(); | |
| 1034 func.ClearCode(); | |
| 1035 func.set_was_compiled(false); | |
| 1036 } | |
| 1037 | |
| 1038 void PreserveUnoptimizedCode(const Function& func) { | |
| 1039 ASSERT(!code_.IsNull()); | |
| 1040 // We are preserving the unoptimized code, fill all ICData arrays with | |
| 1041 // the sentinel values so that we have no stale type feedback. | |
| 1042 func.FillICDataWithSentinels(code_); | |
| 1043 } | |
| 1044 | |
| 1045 bool IsFromDirtyLibrary(const Function& func) { | |
| 1046 owning_class_ = func.Owner(); | |
| 1047 owning_lib_ = owning_class_.library(); | |
| 1048 return reload_context_->IsDirty(owning_lib_); | |
| 1049 } | |
| 1050 | |
| 1051 Object& handle_; | |
| 1052 Class& owning_class_; | |
| 1053 Library& owning_lib_; | |
| 1054 Code& code_; | |
| 1055 IsolateReloadContext* reload_context_; | |
| 1056 }; | |
| 1057 | |
| 1058 | |
| 1059 void IsolateReloadContext::MarkAllFunctionsForRecompilation() { | |
| 1060 TIMELINE_SCOPE(MarkAllFunctionsForRecompilation); | |
| 1061 MarkFunctionsForRecompilation visitor(isolate_, this); | |
| 1062 isolate_->heap()->VisitObjects(&visitor); | |
| 1063 } | |
| 1064 | |
| 1065 | |
| 1066 void IsolateReloadContext::InvalidateWorld() { | |
| 1067 ResetMegamorphicCaches(); | |
| 1068 DeoptimizeFunctionsOnStack(); | |
| 1069 ResetUnoptimizedICsOnStack(); | |
| 1070 | |
| 1071 { | |
| 1072 NoSafepointScope no_safepoint; | |
|
rmacnak
2016/05/13 17:59:34
Push scopes into MarkAllFunctionsForRecompilation
Cutch
2016/05/17 18:03:52
Done.
| |
| 1073 HeapIterationScope heap_iteration_scope; | |
| 1074 | |
| 1075 MarkAllFunctionsForRecompilation(); | |
| 1076 } | |
| 1077 } | |
| 1078 | |
| 1079 | |
| 1080 RawClass* IsolateReloadContext::MappedClass(const Class& replacement_or_new) { | |
| 1081 UnorderedHashMap<ClassMapTraits> map(class_map_storage_); | |
| 1082 Class& cls = Class::Handle(); | |
| 1083 cls ^= map.GetOrNull(replacement_or_new); | |
| 1084 // No need to update storage address because no mutation occurred. | |
| 1085 map.Release(); | |
| 1086 return cls.raw(); | |
| 1087 } | |
| 1088 | |
| 1089 | |
| 1090 RawLibrary* IsolateReloadContext::MappedLibrary( | |
| 1091 const Library& replacement_or_new) { | |
| 1092 return Library::null(); | |
| 1093 } | |
| 1094 | |
| 1095 | |
| 1096 RawClass* IsolateReloadContext::OldClassOrNull( | |
| 1097 const Class& replacement_or_new) { | |
| 1098 UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_); | |
| 1099 Class& cls = Class::Handle(); | |
| 1100 cls ^= old_classes_set.GetOrNull(replacement_or_new); | |
| 1101 old_classes_set_storage_ = old_classes_set.Release().raw(); | |
| 1102 return cls.raw(); | |
| 1103 } | |
| 1104 | |
| 1105 | |
| 1106 RawLibrary* IsolateReloadContext::OldLibraryOrNull( | |
| 1107 const Library& replacement_or_new) { | |
| 1108 UnorderedHashSet<LibraryMapTraits> | |
| 1109 old_libraries_set(old_libraries_set_storage_); | |
| 1110 Library& lib = Library::Handle(); | |
| 1111 lib ^= old_libraries_set.GetOrNull(replacement_or_new); | |
| 1112 old_libraries_set_storage_ = old_libraries_set.Release().raw(); | |
| 1113 return lib.raw(); | |
| 1114 } | |
| 1115 | |
| 1116 | |
| 1117 void IsolateReloadContext::BuildLibraryMapping() { | |
| 1118 const GrowableObjectArray& libs = | |
| 1119 GrowableObjectArray::Handle(object_store()->libraries()); | |
| 1120 | |
| 1121 Library& replacement_or_new = Library::Handle(); | |
| 1122 Library& old = Library::Handle(); | |
| 1123 for (intptr_t i = 0; i < libs.Length(); i++) { | |
| 1124 replacement_or_new = Library::RawCast(libs.At(i)); | |
| 1125 if (IsCleanLibrary(replacement_or_new)) { | |
| 1126 continue; | |
| 1127 } | |
| 1128 old ^= OldLibraryOrNull(replacement_or_new); | |
| 1129 if (old.IsNull()) { | |
| 1130 // New library. | |
| 1131 AddLibraryMapping(replacement_or_new, replacement_or_new); | |
| 1132 } else { | |
| 1133 ASSERT(!replacement_or_new.is_dart_scheme()); | |
| 1134 // Replaced class. | |
| 1135 AddLibraryMapping(replacement_or_new, old); | |
| 1136 | |
| 1137 AddBecomeMapping(old, replacement_or_new); | |
| 1138 } | |
| 1139 } | |
| 1140 } | |
| 1141 | |
| 1142 | |
| 1143 void IsolateReloadContext::AddClassMapping(const Class& replacement_or_new, | |
| 1144 const Class& original) { | |
| 1145 UnorderedHashMap<ClassMapTraits> map(class_map_storage_); | |
| 1146 bool update = map.UpdateOrInsert(replacement_or_new, original); | |
| 1147 ASSERT(!update); | |
| 1148 // The storage given to the map may have been reallocated, remember the new | |
| 1149 // address. | |
| 1150 class_map_storage_ = map.Release().raw(); | |
| 1151 } | |
| 1152 | |
| 1153 | |
| 1154 void IsolateReloadContext::AddLibraryMapping(const Library& replacement_or_new, | |
| 1155 const Library& original) { | |
| 1156 UnorderedHashMap<LibraryMapTraits> map(library_map_storage_); | |
| 1157 bool update = map.UpdateOrInsert(replacement_or_new, original); | |
| 1158 ASSERT(!update); | |
| 1159 // The storage given to the map may have been reallocated, remember the new | |
| 1160 // address. | |
| 1161 library_map_storage_ = map.Release().raw(); | |
| 1162 } | |
| 1163 | |
| 1164 | |
| 1165 void IsolateReloadContext::AddStaticFieldMapping( | |
| 1166 const Field& old_field, const Field& new_field) { | |
| 1167 ASSERT(old_field.is_static()); | |
| 1168 ASSERT(new_field.is_static()); | |
| 1169 | |
| 1170 AddBecomeMapping(old_field, new_field); | |
| 1171 } | |
| 1172 | |
| 1173 | |
| 1174 void IsolateReloadContext::AddBecomeMapping(const Object& old, | |
| 1175 const Object& neu) { | |
| 1176 ASSERT(become_map_storage_ != Array::null()); | |
| 1177 UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_); | |
| 1178 bool update = become_map.UpdateOrInsert(old, neu); | |
| 1179 ASSERT(!update); | |
| 1180 become_map_storage_ = become_map.Release().raw(); | |
| 1181 } | |
| 1182 | |
| 1183 | |
| 1184 void IsolateReloadContext::RebuildDirectSubclasses() { | |
| 1185 ClassTable* class_table = I->class_table(); | |
| 1186 intptr_t num_cids = class_table->NumCids(); | |
| 1187 | |
| 1188 // Clear the direct subclasses for all classes. | |
| 1189 Class& cls = Class::Handle(); | |
| 1190 GrowableObjectArray& subclasses = GrowableObjectArray::Handle(); | |
| 1191 for (intptr_t i = 1; i < num_cids; i++) { | |
| 1192 if (class_table->HasValidClassAt(i)) { | |
| 1193 cls = class_table->At(i); | |
| 1194 subclasses = cls.direct_subclasses(); | |
| 1195 if (!subclasses.IsNull()) { | |
| 1196 subclasses.SetLength(0); | |
| 1197 } | |
| 1198 } | |
| 1199 } | |
| 1200 | |
| 1201 // Recompute the direct subclasses. | |
| 1202 AbstractType& super_type = AbstractType::Handle(); | |
| 1203 Class& super_cls = Class::Handle(); | |
| 1204 for (intptr_t i = 1; i < num_cids; i++) { | |
| 1205 if (class_table->HasValidClassAt(i)) { | |
| 1206 cls = class_table->At(i); | |
| 1207 super_type = cls.super_type(); | |
| 1208 if (!super_type.IsNull() && !super_type.IsObjectType()) { | |
| 1209 super_cls = cls.SuperClass(); | |
| 1210 ASSERT(!super_cls.IsNull()); | |
| 1211 super_cls.AddDirectSubclass(cls); | |
| 1212 } | |
| 1213 } | |
| 1214 } | |
| 1215 } | |
| 1216 | |
| 1217 #endif // !PRODUCT | |
| 1218 | |
| 1219 } // namespace dart | |
| OLD | NEW |