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