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 <memory>
8
9#include "vm/bit_vector.h"
10#include "vm/compiler/jit/compiler.h"
11#include "vm/dart_api_impl.h"
12#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
13#include "vm/hash.h"
14#endif
15#include "vm/hash_table.h"
16#include "vm/heap/become.h"
17#include "vm/heap/safepoint.h"
18#include "vm/isolate.h"
19#include "vm/kernel_isolate.h"
20#include "vm/kernel_loader.h"
21#include "vm/log.h"
22#include "vm/object.h"
23#include "vm/object_store.h"
24#include "vm/parser.h"
25#include "vm/runtime_entry.h"
26#include "vm/service_event.h"
27#include "vm/stack_frame.h"
28#include "vm/thread.h"
29#include "vm/timeline.h"
30#include "vm/type_testing_stubs.h"
31#include "vm/visitor.h"
32
33namespace dart {
34
35DEFINE_FLAG(int, reload_every, 0, "Reload every N stack overflow checks.");
36DEFINE_FLAG(bool, trace_reload, false, "Trace isolate reloading");
37
38#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
39DEFINE_FLAG(bool,
40 trace_reload_verbose,
41 false,
42 "trace isolate reloading verbose");
43DEFINE_FLAG(bool, identity_reload, false, "Enable checks for identity reload.");
44DEFINE_FLAG(bool, reload_every_optimized, true, "Only from optimized code.");
45DEFINE_FLAG(bool,
46 reload_every_back_off,
47 false,
48 "Double the --reload-every value after each reload.");
49DEFINE_FLAG(bool,
50 reload_force_rollback,
51 false,
52 "Force all reloads to fail and rollback.");
53DEFINE_FLAG(bool,
54 check_reloaded,
55 false,
56 "Assert that an isolate has reloaded at least once.")
57DEFINE_FLAG(bool, gc_during_reload, false, "Cause explicit GC during reload.");
58
59DECLARE_FLAG(bool, trace_deoptimization);
60
61#define I (isolate())
62#define Z zone_
63
64#define TIMELINE_SCOPE(name) \
65 TimelineBeginEndScope tbes##name(Thread::Current(), \
66 Timeline::GetIsolateStream(), #name)
67
68// The ObjectLocator is used for collecting instances that
69// needs to be morphed.
70class ObjectLocator : public ObjectVisitor {
71 public:
72 explicit ObjectLocator(IsolateGroupReloadContext* context)
73 : context_(context), count_(0) {}
74
75 void VisitObject(ObjectPtr obj) {
76 InstanceMorpher* morpher =
77 context_->instance_morpher_by_cid_.LookupValue(obj->GetClassId());
78 if (morpher != NULL) {
79 morpher->AddObject(obj);
80 count_++;
81 }
82 }
83
84 // Return the number of located objects for morphing.
85 intptr_t count() { return count_; }
86
87 private:
88 IsolateGroupReloadContext* context_;
89 intptr_t count_;
90};
91
92static bool HasNoTasks(Heap* heap) {
93 MonitorLocker ml(heap->old_space()->tasks_lock());
94 return heap->old_space()->tasks() == 0;
95}
96
97// TODO(dartbug.com/36097): Once classes are split up into a read-only
98// descriptor which can be shared across isolates, we can make this function
99// take descriptors instead of the isolate-specific [Class] objects.
100//
101// (The information we access from [from]/[to] *must* be the same across
102// isolates.)
103InstanceMorpher* InstanceMorpher::CreateFromClassDescriptors(
104 Zone* zone,
105 SharedClassTable* shared_class_table,
106 const Class& from,
107 const Class& to) {
108 auto mapping = new (zone) ZoneGrowableArray<intptr_t>();
109 auto new_fields_offsets = new (zone) ZoneGrowableArray<intptr_t>();
110
111 if (from.NumTypeArguments() > 0) {
112 // Add copying of the optional type argument field.
113 intptr_t from_offset = from.host_type_arguments_field_offset();
114 ASSERT(from_offset != Class::kNoTypeArguments);
115 intptr_t to_offset = to.host_type_arguments_field_offset();
116 ASSERT(to_offset != Class::kNoTypeArguments);
117 mapping->Add(from_offset);
118 mapping->Add(to_offset);
119 }
120
121 // Add copying of the instance fields if matching by name.
122 // Note: currently the type of the fields are ignored.
123 const Array& from_fields =
124 Array::Handle(from.OffsetToFieldMap(true /* original classes */));
125 const Array& to_fields = Array::Handle(to.OffsetToFieldMap());
126 Field& from_field = Field::Handle();
127 Field& to_field = Field::Handle();
128 String& from_name = String::Handle();
129 String& to_name = String::Handle();
130
131 // Scan across all the fields in the new class definition.
132 for (intptr_t i = 0; i < to_fields.Length(); i++) {
133 if (to_fields.At(i) == Field::null()) {
134 continue; // Ignore non-fields.
135 }
136
137 // Grab the field's name.
138 to_field = Field::RawCast(to_fields.At(i));
139 ASSERT(to_field.is_instance());
140 to_name = to_field.name();
141
142 // Did this field not exist in the old class definition?
143 bool new_field = true;
144
145 // Find this field in the old class.
146 for (intptr_t j = 0; j < from_fields.Length(); j++) {
147 if (from_fields.At(j) == Field::null()) {
148 continue; // Ignore non-fields.
149 }
150 from_field = Field::RawCast(from_fields.At(j));
151 ASSERT(from_field.is_instance());
152 from_name = from_field.name();
153 if (from_name.Equals(to_name)) {
154 // Success
155 mapping->Add(from_field.HostOffset());
156 mapping->Add(to_field.HostOffset());
157 // Field did exist in old class deifnition.
158 new_field = false;
159 }
160 }
161
162 if (new_field) {
163 const Field& field = Field::Handle(to_field.raw());
164 field.set_needs_load_guard(true);
165 field.set_is_unboxing_candidate(false);
166 new_fields_offsets->Add(field.HostOffset());
167 }
168 }
169
170 ASSERT(from.id() == to.id());
171 return new (zone) InstanceMorpher(zone, to.id(), shared_class_table, mapping,
172 new_fields_offsets);
173}
174
175InstanceMorpher::InstanceMorpher(
176 Zone* zone,
177 classid_t cid,
178 SharedClassTable* shared_class_table,
179 ZoneGrowableArray<intptr_t>* mapping,
180 ZoneGrowableArray<intptr_t>* new_fields_offsets)
181 : zone_(zone),
182 cid_(cid),
183 shared_class_table_(shared_class_table),
184 mapping_(mapping),
185 new_fields_offsets_(new_fields_offsets),
186 before_(zone, 16),
187 after_(zone, 16) {}
188
189void InstanceMorpher::AddObject(ObjectPtr object) {
190 ASSERT(object->GetClassId() == cid_);
191 const Instance& instance = Instance::Cast(Object::Handle(Z, object));
192 before_.Add(&instance);
193}
194
195InstancePtr InstanceMorpher::Morph(const Instance& instance) const {
196 // Code can reference constants / canonical objects either directly in the
197 // instruction stream (ia32) or via an object pool.
198 //
199 // We have the following invariants:
200 //
201 // a) Those canonical objects don't change state (i.e. are not mutable):
202 // our optimizer can e.g. execute loads of such constants at
203 // compile-time.
204 //
205 // => We ensure that const-classes with live constants cannot be
206 // reloaded to become non-const classes (see Class::CheckReload).
207 //
208 // b) Those canonical objects live in old space: e.g. on ia32 the scavenger
209 // does not make the RX pages writable and therefore cannot update
210 // pointers embedded in the instruction stream.
211 //
212 // In order to maintain these invariants we ensure to always morph canonical
213 // objects to old space.
214 const bool is_canonical = instance.IsCanonical();
215 const Heap::Space space = is_canonical ? Heap::kOld : Heap::kNew;
216 const auto& result = Instance::Handle(
217 Z, Instance::NewFromCidAndSize(shared_class_table_, cid_, space));
218
219 // We preserve the canonical bit of the object, since this object is present
220 // in the class's constants.
221 if (is_canonical) {
222 result.SetCanonical();
223 }
224#if defined(HASH_IN_OBJECT_HEADER)
225 const uint32_t hash = Object::GetCachedHash(instance.raw());
226 Object::SetCachedHash(result.raw(), hash);
227#endif
228
229 // Morph the context from instance to result using mapping_.
230 Object& value = Object::Handle(Z);
231 for (intptr_t i = 0; i < mapping_->length(); i += 2) {
232 intptr_t from_offset = mapping_->At(i);
233 intptr_t to_offset = mapping_->At(i + 1);
234 ASSERT(from_offset > 0);
235 ASSERT(to_offset > 0);
236 value = instance.RawGetFieldAtOffset(from_offset);
237 result.RawSetFieldAtOffset(to_offset, value);
238 }
239
240 for (intptr_t i = 0; i < new_fields_offsets_->length(); i++) {
241 const intptr_t field_offset = new_fields_offsets_->At(i);
242 result.RawSetFieldAtOffset(field_offset, Object::sentinel());
243 }
244
245 // Convert the instance into a filler object.
246 Become::MakeDummyObject(instance);
247 return result.raw();
248}
249
250void InstanceMorpher::CreateMorphedCopies() {
251 for (intptr_t i = 0; i < before_.length(); i++) {
252 const Instance& copy = Instance::Handle(Z, Morph(*before_.At(i)));
253 after_.Add(&copy);
254 }
255}
256
257void InstanceMorpher::Dump() const {
258 LogBlock blocker;
259 THR_Print("Morphing objects with cid: %d via this mapping: ", cid_);
260 for (int i = 0; i < mapping_->length(); i += 2) {
261 THR_Print(" %" Pd "->%" Pd, mapping_->At(i), mapping_->At(i + 1));
262 }
263 THR_Print("\n");
264}
265
266void InstanceMorpher::AppendTo(JSONArray* array) {
267 JSONObject jsobj(array);
268 jsobj.AddProperty("type", "ShapeChangeMapping");
269 jsobj.AddProperty64("class-id", cid_);
270 jsobj.AddProperty("instanceCount", before_.length());
271 JSONArray map(&jsobj, "fieldOffsetMappings");
272 for (int i = 0; i < mapping_->length(); i += 2) {
273 JSONArray pair(&map);
274 pair.AddValue(mapping_->At(i));
275 pair.AddValue(mapping_->At(i + 1));
276 }
277}
278
279void ReasonForCancelling::Report(IsolateGroupReloadContext* context) {
280 const Error& error = Error::Handle(ToError());
281 context->ReportError(error);
282}
283
284ErrorPtr ReasonForCancelling::ToError() {
285 // By default create the error returned from ToString.
286 const String& message = String::Handle(ToString());
287 return LanguageError::New(message);
288}
289
290StringPtr ReasonForCancelling::ToString() {
291 UNREACHABLE();
292 return NULL;
293}
294
295void ReasonForCancelling::AppendTo(JSONArray* array) {
296 JSONObject jsobj(array);
297 jsobj.AddProperty("type", "ReasonForCancelling");
298 const String& message = String::Handle(ToString());
299 jsobj.AddProperty("message", message.ToCString());
300}
301
302ClassReasonForCancelling::ClassReasonForCancelling(Zone* zone,
303 const Class& from,
304 const Class& to)
305 : ReasonForCancelling(zone),
306 from_(Class::ZoneHandle(zone, from.raw())),
307 to_(Class::ZoneHandle(zone, to.raw())) {}
308
309void ClassReasonForCancelling::AppendTo(JSONArray* array) {
310 JSONObject jsobj(array);
311 jsobj.AddProperty("type", "ReasonForCancelling");
312 jsobj.AddProperty("class", from_);
313 const String& message = String::Handle(ToString());
314 jsobj.AddProperty("message", message.ToCString());
315}
316
317ErrorPtr IsolateGroupReloadContext::error() const {
318 ASSERT(!reasons_to_cancel_reload_.is_empty());
319 // Report the first error to the surroundings.
320 return reasons_to_cancel_reload_.At(0)->ToError();
321}
322
323class ScriptUrlSetTraits {
324 public:
325 static bool ReportStats() { return false; }
326 static const char* Name() { return "ScriptUrlSetTraits"; }
327
328 static bool IsMatch(const Object& a, const Object& b) {
329 if (!a.IsString() || !b.IsString()) {
330 return false;
331 }
332
333 return String::Cast(a).Equals(String::Cast(b));
334 }
335
336 static uword Hash(const Object& obj) { return String::Cast(obj).Hash(); }
337};
338
339class ClassMapTraits {
340 public:
341 static bool ReportStats() { return false; }
342 static const char* Name() { return "ClassMapTraits"; }
343
344 static bool IsMatch(const Object& a, const Object& b) {
345 if (!a.IsClass() || !b.IsClass()) {
346 return false;
347 }
348 return IsolateReloadContext::IsSameClass(Class::Cast(a), Class::Cast(b));
349 }
350
351 static uword Hash(const Object& obj) {
352 uword class_name_hash = String::HashRawSymbol(Class::Cast(obj).Name());
353 LibraryPtr raw_library = Class::Cast(obj).library();
354 if (raw_library == Library::null()) {
355 return class_name_hash;
356 }
357 return FinalizeHash(
358 CombineHashes(class_name_hash,
359 String::Hash(Library::Handle(raw_library).private_key())),
360 /* hashbits= */ 30);
361 }
362};
363
364class LibraryMapTraits {
365 public:
366 static bool ReportStats() { return false; }
367 static const char* Name() { return "LibraryMapTraits"; }
368
369 static bool IsMatch(const Object& a, const Object& b) {
370 if (!a.IsLibrary() || !b.IsLibrary()) {
371 return false;
372 }
373 return IsolateReloadContext::IsSameLibrary(Library::Cast(a),
374 Library::Cast(b));
375 }
376
377 static uword Hash(const Object& obj) { return Library::Cast(obj).UrlHash(); }
378};
379
380class BecomeMapTraits {
381 public:
382 static bool ReportStats() { return false; }
383 static const char* Name() { return "BecomeMapTraits"; }
384
385 static bool IsMatch(const Object& a, const Object& b) {
386 return a.raw() == b.raw();
387 }
388
389 static uword Hash(const Object& obj) {
390 if (obj.IsLibrary()) {
391 return Library::Cast(obj).UrlHash();
392 } else if (obj.IsClass()) {
393 return String::HashRawSymbol(Class::Cast(obj).Name());
394 } else if (obj.IsField()) {
395 return String::HashRawSymbol(Field::Cast(obj).name());
396 } else if (obj.IsClosure()) {
397 return String::HashRawSymbol(
398 Function::Handle(Closure::Cast(obj).function()).name());
399 } else {
400 FATAL1("Unexpected type in become: %s\n", obj.ToCString());
401 }
402 return 0;
403 }
404};
405
406bool IsolateReloadContext::IsSameClass(const Class& a, const Class& b) {
407 // TODO(turnidge): We need to look at generic type arguments for
408 // synthetic mixin classes. Their names are not necessarily unique
409 // currently.
410 const String& a_name = String::Handle(a.Name());
411 const String& b_name = String::Handle(b.Name());
412
413 if (!a_name.Equals(b_name)) {
414 return false;
415 }
416
417 const Library& a_lib = Library::Handle(a.library());
418 const Library& b_lib = Library::Handle(b.library());
419
420 if (a_lib.IsNull() || b_lib.IsNull()) {
421 return a_lib.raw() == b_lib.raw();
422 }
423 return (a_lib.private_key() == b_lib.private_key());
424}
425
426bool IsolateReloadContext::IsSameLibrary(const Library& a_lib,
427 const Library& b_lib) {
428 const String& a_lib_url =
429 String::Handle(a_lib.IsNull() ? String::null() : a_lib.url());
430 const String& b_lib_url =
431 String::Handle(b_lib.IsNull() ? String::null() : b_lib.url());
432 return a_lib_url.Equals(b_lib_url);
433}
434
435IsolateGroupReloadContext::IsolateGroupReloadContext(
436 IsolateGroup* isolate_group,
437 SharedClassTable* shared_class_table,
438 JSONStream* js)
439 : zone_(Thread::Current()->zone()),
440 isolate_group_(isolate_group),
441 shared_class_table_(shared_class_table),
442 start_time_micros_(OS::GetCurrentMonotonicMicros()),
443 reload_timestamp_(OS::GetCurrentTimeMillis()),
444 js_(js),
445 saved_size_table_(nullptr),
446 instance_morphers_(zone_, 0),
447 reasons_to_cancel_reload_(zone_, 0),
448 instance_morpher_by_cid_(zone_),
449 root_lib_url_(String::Handle(Z, String::null())),
450 root_url_prefix_(String::null()),
451 old_root_url_prefix_(String::null()) {}
452IsolateGroupReloadContext::~IsolateGroupReloadContext() {}
453
454IsolateReloadContext::IsolateReloadContext(
455 std::shared_ptr<IsolateGroupReloadContext> group_reload_context,
456 Isolate* isolate)
457 : zone_(Thread::Current()->zone()),
458 group_reload_context_(group_reload_context),
459 isolate_(isolate),
460 saved_class_table_(nullptr),
461 saved_tlc_class_table_(nullptr),
462 old_classes_set_storage_(Array::null()),
463 class_map_storage_(Array::null()),
464 removed_class_set_storage_(Array::null()),
465 old_libraries_set_storage_(Array::null()),
466 library_map_storage_(Array::null()),
467 become_map_storage_(Array::null()),
468 become_enum_mappings_(GrowableObjectArray::null()),
469 saved_root_library_(Library::null()),
470 saved_libraries_(GrowableObjectArray::null()) {
471 // NOTE: DO NOT ALLOCATE ANY RAW OBJECTS HERE. The IsolateReloadContext is not
472 // associated with the isolate yet and if a GC is triggered here the raw
473 // objects will not be properly accounted for.
474 ASSERT(zone_ != NULL);
475}
476
477IsolateReloadContext::~IsolateReloadContext() {
478 ASSERT(zone_ == Thread::Current()->zone());
479 ASSERT(saved_class_table_.load(std::memory_order_relaxed) == nullptr);
480 ASSERT(saved_tlc_class_table_.load(std::memory_order_relaxed) == nullptr);
481}
482
483void IsolateGroupReloadContext::ReportError(const Error& error) {
484 // TODO(dartbug.com/36097): We need to change the "reloadSources" service-api
485 // call to accept an isolate group instead of an isolate.
486 Isolate* isolate = Isolate::Current();
487 if (Isolate::IsVMInternalIsolate(isolate)) {
488 return;
489 }
490 TIR_Print("ISO-RELOAD: Error: %s\n", error.ToErrorCString());
491 ServiceEvent service_event(isolate, ServiceEvent::kIsolateReload);
492 service_event.set_reload_error(&error);
493 Service::HandleEvent(&service_event);
494}
495
496void IsolateGroupReloadContext::ReportSuccess() {
497 // TODO(dartbug.com/36097): We need to change the "reloadSources" service-api
498 // call to accept an isolate group instead of an isolate.
499 Isolate* isolate = Isolate::Current();
500 if (Isolate::IsVMInternalIsolate(isolate)) {
501 return;
502 }
503 ServiceEvent service_event(isolate, ServiceEvent::kIsolateReload);
504 Service::HandleEvent(&service_event);
505}
506
507class Aborted : public ReasonForCancelling {
508 public:
509 Aborted(Zone* zone, const Error& error)
510 : ReasonForCancelling(zone),
511 error_(Error::ZoneHandle(zone, error.raw())) {}
512
513 private:
514 const Error& error_;
515
516 ErrorPtr ToError() { return error_.raw(); }
517 StringPtr ToString() {
518 return String::NewFormatted("%s", error_.ToErrorCString());
519 }
520};
521
522static intptr_t CommonSuffixLength(const char* a, const char* b) {
523 const intptr_t a_length = strlen(a);
524 const intptr_t b_length = strlen(b);
525 intptr_t a_cursor = a_length;
526 intptr_t b_cursor = b_length;
527
528 while ((a_cursor >= 0) && (b_cursor >= 0)) {
529 if (a[a_cursor] != b[b_cursor]) {
530 break;
531 }
532 a_cursor--;
533 b_cursor--;
534 }
535
536 ASSERT((a_length - a_cursor) == (b_length - b_cursor));
537 return (a_length - a_cursor);
538}
539
540static void AcceptCompilation(Thread* thread) {
541 TransitionVMToNative transition(thread);
542 Dart_KernelCompilationResult result = KernelIsolate::AcceptCompilation();
543 if (result.status != Dart_KernelCompilationStatus_Ok) {
544 FATAL1(
545 "An error occurred in the CFE while accepting the most recent"
546 " compilation results: %s",
547 result.error);
548 }
549}
550
551// If [root_script_url] is null, attempt to load from [kernel_buffer].
552bool IsolateGroupReloadContext::Reload(bool force_reload,
553 const char* root_script_url,
554 const char* packages_url,
555 const uint8_t* kernel_buffer,
556 intptr_t kernel_buffer_size) {
557 TIMELINE_SCOPE(Reload);
558
559 Thread* thread = Thread::Current();
560
561 // All isolates have the same sources, so all of them have the same libraries.
562 // We use the [first_isolate_] here to determine which of libraries have
563 // changed.
564 ASSERT(first_isolate_ == nullptr);
565 first_isolate_ = thread->isolate();
566
567 // All isolates within an isolate group need to share one heap.
568 // TODO(dartbug.com/36097): Remove this assert once the shared heap CL has
569 // landed.
570 RELEASE_ASSERT(!FLAG_enable_isolate_groups);
571 Heap* heap = first_isolate_->heap();
572
573 num_old_libs_ = GrowableObjectArray::Handle(
574 Z, first_isolate_->object_store()->libraries())
575 .Length();
576
577 // Grab root library before calling CheckpointBeforeReload.
578 GetRootLibUrl(root_script_url);
579
580 std::unique_ptr<kernel::Program> kernel_program;
581
582 // Reset stats.
583 num_received_libs_ = 0;
584 bytes_received_libs_ = 0;
585 num_received_classes_ = 0;
586 num_received_procedures_ = 0;
587
588 bool did_kernel_compilation = false;
589 bool skip_reload = false;
590 {
591 // Load the kernel program and figure out the modified libraries.
592 intptr_t* p_num_received_classes = nullptr;
593 intptr_t* p_num_received_procedures = nullptr;
594
595 // ReadKernelFromFile checks to see if the file at
596 // root_script_url is a valid .dill file. If that's the case, a Program*
597 // is returned. Otherwise, this is likely a source file that needs to be
598 // compiled, so ReadKernelFromFile returns NULL.
599 kernel_program = kernel::Program::ReadFromFile(root_script_url);
600 if (kernel_program != nullptr) {
601 num_received_libs_ = kernel_program->library_count();
602 bytes_received_libs_ = kernel_program->kernel_data_size();
603 p_num_received_classes = &num_received_classes_;
604 p_num_received_procedures = &num_received_procedures_;
605 } else {
606 if (kernel_buffer == NULL || kernel_buffer_size == 0) {
607 char* error = CompileToKernel(force_reload, packages_url,
608 &kernel_buffer, &kernel_buffer_size);
609 did_kernel_compilation = true;
610 if (error != nullptr) {
611 TIR_Print("---- LOAD FAILED, ABORTING RELOAD\n");
612 const auto& error_str = String::Handle(Z, String::New(error));
613 free(error);
614 const ApiError& error = ApiError::Handle(Z, ApiError::New(error_str));
615 AddReasonForCancelling(new Aborted(Z, error));
616 ReportReasonsForCancelling();
617 CommonFinalizeTail(num_old_libs_);
618 return false;
619 }
620 }
621 const auto& typed_data = ExternalTypedData::Handle(
622 Z, ExternalTypedData::NewFinalizeWithFree(
623 const_cast<uint8_t*>(kernel_buffer), kernel_buffer_size));
624 kernel_program = kernel::Program::ReadFromTypedData(typed_data);
625 }
626
627 ExternalTypedData& external_typed_data =
628 ExternalTypedData::Handle(Z, kernel_program.get()->typed_data()->raw());
629 IsolateGroupSource* source = Isolate::Current()->source();
630 source->add_loaded_blob(Z, external_typed_data);
631
632 modified_libs_ = new (Z) BitVector(Z, num_old_libs_);
633 kernel::KernelLoader::FindModifiedLibraries(
634 kernel_program.get(), first_isolate_, modified_libs_, force_reload,
635 &skip_reload, p_num_received_classes, p_num_received_procedures);
636 modified_libs_transitive_ = new (Z) BitVector(Z, num_old_libs_);
637 BuildModifiedLibrariesClosure(modified_libs_);
638
639 ASSERT(num_saved_libs_ == -1);
640 num_saved_libs_ = 0;
641 for (intptr_t i = 0; i < modified_libs_->length(); i++) {
642 if (!modified_libs_->Contains(i)) {
643 num_saved_libs_++;
644 }
645 }
646 }
647
648 if (skip_reload) {
649 ASSERT(modified_libs_->IsEmpty());
650 reload_skipped_ = true;
651 ReportOnJSON(js_, num_old_libs_);
652
653 // If we use the CFE and performed a compilation, we need to notify that
654 // we have accepted the compilation to clear some state in the incremental
655 // compiler.
656 if (did_kernel_compilation) {
657 AcceptCompilation(thread);
658 }
659 TIR_Print("---- SKIPPING RELOAD (No libraries were modified)\n");
660 return false;
661 }
662
663 TIR_Print("---- STARTING RELOAD\n");
664
665 intptr_t number_of_isolates = 0;
666 isolate_group_->ForEachIsolate(
667 [&](Isolate* isolate) { number_of_isolates++; });
668
669 // Disable the background compiler while we are performing the reload.
670 ForEachIsolate(
671 [&](Isolate* isolate) { BackgroundCompiler::Disable(isolate); });
672
673 // Wait for any concurrent marking tasks to finish and turn off the
674 // concurrent marker during reload as we might be allocating new instances
675 // (constants) when loading the new kernel file and this could cause
676 // inconsistency between the saved class table and the new class table.
677 const bool old_concurrent_mark_flag =
678 heap->old_space()->enable_concurrent_mark();
679 if (old_concurrent_mark_flag) {
680 heap->WaitForMarkerTasks(thread);
681 heap->old_space()->set_enable_concurrent_mark(false);
682 }
683
684 // Ensure all functions on the stack have unoptimized code.
685 // Deoptimize all code that had optimizing decisions that are dependent on
686 // assumptions from field guards or CHA or deferred library prefixes.
687 // TODO(johnmccutchan): Deoptimizing dependent code here (before the reload)
688 // is paranoid. This likely can be moved to the commit phase.
689 ForEachIsolate([&](Isolate* isolate) {
690 isolate->reload_context()->EnsuredUnoptimizedCodeForStack();
691 isolate->reload_context()->DeoptimizeDependentCode();
692 isolate->reload_context()->ReloadPhase1AllocateStorageMapsAndCheckpoint();
693 });
694 // Renumbering the libraries has invalidated this.
695 modified_libs_ = nullptr;
696 modified_libs_transitive_ = nullptr;
697
698 if (FLAG_gc_during_reload) {
699 // We use kLowMemory to force the GC to compact, which is more likely to
700 // discover untracked pointers (and other issues, like incorrect class
701 // table).
702 heap->CollectAllGarbage(Heap::kLowMemory);
703 }
704
705 // Copy the size table for isolate group & class tables for each isolate.
706 {
707 TIMELINE_SCOPE(CheckpointClasses);
708 CheckpointSharedClassTable();
709 ForEachIsolate([&](Isolate* isolate) {
710 isolate->reload_context()->CheckpointClasses();
711 });
712 }
713
714 if (FLAG_gc_during_reload) {
715 // We use kLowMemory to force the GC to compact, which is more likely to
716 // discover untracked pointers (and other issues, like incorrect class
717 // table).
718 heap->CollectAllGarbage(Heap::kLowMemory);
719 }
720
721 // We synchronously load the hot-reload kernel diff (which includes changed
722 // libraries and any libraries transitively depending on them).
723 //
724 // If loading the hot-reload diff succeeded we'll finalize the loading, which
725 // will either commit or reject the reload request.
726 const auto& results = Array::Handle(Z, Array::New(number_of_isolates));
727 intptr_t isolateIndex = 0;
728 intptr_t load_errors = 0;
729
730 auto& tmp = Object::Handle(Z);
731 ForEachIsolate([&](Isolate* isolate) {
732 tmp = isolate->reload_context()->ReloadPhase2LoadKernel(
733 kernel_program.get(), root_lib_url_);
734 if (tmp.IsError()) {
735 results.SetAt(isolateIndex, tmp);
736 load_errors++;
737 }
738 isolateIndex++;
739 });
740
741 const auto& result = Object::Handle(results.At(0));
742
743 if (load_errors > 0) {
744 TIR_Print("---- LOAD FAILED, ABORTING RELOAD\n");
745
746 const auto& error = Error::Cast(result);
747 AddReasonForCancelling(new Aborted(Z, error));
748
749 DiscardSavedClassTable(/*is_rollback=*/true);
750 ForEachIsolate([&](Isolate* isolate) {
751 isolate->reload_context()->ReloadPhase4Rollback();
752 });
753 CommonFinalizeTail(num_old_libs_);
754 } else {
755 ASSERT(!reload_skipped_ && !reload_finalized_);
756 TIR_Print("---- LOAD SUCCEEDED\n");
757
758 ForEachIsolate([&](Isolate* isolate) {
759 isolate->reload_context()->ReloadPhase3FinalizeLoading();
760 });
761
762 if (FLAG_gc_during_reload) {
763 // We use kLowMemory to force the GC to compact, which is more likely to
764 // discover untracked pointers (and other issues, like incorrect class
765 // table).
766 heap->CollectAllGarbage(Heap::kLowMemory);
767 }
768
769 if (!FLAG_reload_force_rollback && !HasReasonsForCancelling()) {
770 TIR_Print("---- COMMITTING RELOAD\n");
771 ForEachIsolate([&](Isolate* isolate) {
772 isolate->reload_context()->ReloadPhase4CommitPrepare();
773 });
774 bool discard_class_tables = true;
775 if (HasInstanceMorphers()) {
776 // Find all objects that need to be morphed (reallocated to a new size).
777 ObjectLocator locator(this);
778 {
779 HeapIterationScope iteration(Thread::Current());
780 iteration.IterateObjects(&locator);
781 }
782
783 // We are still using the old class table at this point.
784 if (FLAG_gc_during_reload) {
785 // We use kLowMemory to force the GC to compact, which is more likely
786 // to discover untracked pointers (and other issues, like incorrect
787 // class table).
788 heap->CollectAllGarbage(Heap::kLowMemory);
789 }
790 const intptr_t count = locator.count();
791 if (count > 0) {
792 TIMELINE_SCOPE(MorphInstances);
793
794 // While we are reallocating instances to their new size, the heap
795 // will contain a mix of instances with the old and new sizes that
796 // have the same cid. This makes the heap unwalkable until the
797 // "become" operation below replaces all the instances of the old
798 // size with forwarding corpses. Force heap growth to prevent size
799 // confusion during this period.
800 NoHeapGrowthControlScope scope;
801 // The HeapIterationScope above ensures no other GC tasks can be
802 // active.
803 ASSERT(HasNoTasks(heap));
804
805 const Array& before = Array::Handle(Z, Array::New(count));
806 const Array& after = Array::Handle(Z, Array::New(count));
807
808 MorphInstancesPhase1Allocate(&locator, before, after);
809 {
810 // Apply the new class table before "become". Become will replace
811 // all the instances of the old size with forwarding corpses, then
812 // perform a heap walk to fix references to the forwarding corpses.
813 // During this heap walk, it will encounter instances of the new
814 // size, so it requires the new class table.
815 ASSERT(HasNoTasks(heap));
816
817 // We accepted the hot-reload and morphed instances. So now we can
818 // commit to the changed class table and deleted the saved one.
819 DiscardSavedClassTable(/*is_rollback=*/false);
820 ForEachIsolate([&](Isolate* isolate) {
821 isolate->reload_context()->DiscardSavedClassTable(
822 /*is_rollback=*/false);
823 });
824 }
825 MorphInstancesPhase2Become(before, after);
826
827 discard_class_tables = false;
828 }
829 // We are using the new class table now.
830 if (FLAG_gc_during_reload) {
831 // We use kLowMemory to force the GC to compact, which is more likely
832 // to discover untracked pointers (and other issues, like incorrect
833 // class table).
834 heap->CollectAllGarbage(Heap::kLowMemory);
835 }
836 }
837 if (discard_class_tables) {
838 DiscardSavedClassTable(/*is_rollback=*/false);
839 ForEachIsolate([&](Isolate* isolate) {
840 isolate->reload_context()->DiscardSavedClassTable(
841 /*is_rollback=*/false);
842 });
843 }
844 ForEachIsolate([&](Isolate* isolate) {
845 isolate->reload_context()->ReloadPhase4CommitFinish();
846 });
847 TIR_Print("---- DONE COMMIT\n");
848 isolate_group_->set_last_reload_timestamp(reload_timestamp_);
849 } else {
850 TIR_Print("---- ROLLING BACK");
851 DiscardSavedClassTable(/*is_rollback=*/true);
852 ForEachIsolate([&](Isolate* isolate) {
853 isolate->reload_context()->ReloadPhase4Rollback();
854 });
855 }
856
857 // ValidateReload mutates the direct subclass information and does
858 // not remove dead subclasses. Rebuild the direct subclass
859 // information from scratch.
860 ForEachIsolate([&](Isolate* isolate) {
861 isolate->reload_context()->RebuildDirectSubclasses();
862 });
863 const intptr_t final_library_count =
864 GrowableObjectArray::Handle(Z,
865 first_isolate_->object_store()->libraries())
866 .Length();
867 CommonFinalizeTail(final_library_count);
868
869 // If we use the CFE and performed a compilation, we need to notify that
870 // we have accepted the compilation to clear some state in the incremental
871 // compiler.
872 if (did_kernel_compilation) {
873 AcceptCompilation(thread);
874 }
875 }
876
877 // Re-enable the background compiler. Do this before propagating any errors.
878 ForEachIsolate(
879 [&](Isolate* isolate) { BackgroundCompiler::Enable(isolate); });
880
881 // Reenable concurrent marking if it was initially on.
882 if (old_concurrent_mark_flag) {
883 heap->old_space()->set_enable_concurrent_mark(true);
884 }
885
886 bool success;
887 if (load_errors == 0 || HasReasonsForCancelling()) {
888 ReportSuccess();
889 success = true;
890 } else {
891 ReportReasonsForCancelling();
892 success = false;
893 }
894
895 // Re-queue any shutdown requests so they can inform each isolate's own thread
896 // to shut down.
897 isolateIndex = 0;
898 ForEachIsolate([&](Isolate* isolate) {
899 tmp = results.At(isolateIndex);
900 if (tmp.IsUnwindError()) {
901 Isolate::KillIfExists(isolate, UnwindError::Cast(tmp).is_user_initiated()
902 ? Isolate::kKillMsg
903 : Isolate::kInternalKillMsg);
904 }
905 isolateIndex++;
906 });
907
908 return success;
909}
910
911/// Copied in from https://dart-review.googlesource.com/c/sdk/+/77722.
912static void PropagateLibraryModified(
913 const ZoneGrowableArray<ZoneGrowableArray<intptr_t>*>* imported_by,
914 intptr_t lib_index,
915 BitVector* modified_libs) {
916 ZoneGrowableArray<intptr_t>* dep_libs = (*imported_by)[lib_index];
917 for (intptr_t i = 0; i < dep_libs->length(); i++) {
918 intptr_t dep_lib_index = (*dep_libs)[i];
919 if (!modified_libs->Contains(dep_lib_index)) {
920 modified_libs->Add(dep_lib_index);
921 PropagateLibraryModified(imported_by, dep_lib_index, modified_libs);
922 }
923 }
924}
925
926/// Copied in from https://dart-review.googlesource.com/c/sdk/+/77722.
927void IsolateGroupReloadContext::BuildModifiedLibrariesClosure(
928 BitVector* modified_libs) {
929 const GrowableObjectArray& libs =
930 GrowableObjectArray::Handle(first_isolate_->object_store()->libraries());
931 Library& lib = Library::Handle();
932 intptr_t num_libs = libs.Length();
933
934 // Construct the imported-by graph.
935 ZoneGrowableArray<ZoneGrowableArray<intptr_t>*>* imported_by = new (zone_)
936 ZoneGrowableArray<ZoneGrowableArray<intptr_t>*>(zone_, num_libs);
937 imported_by->SetLength(num_libs);
938 for (intptr_t i = 0; i < num_libs; i++) {
939 (*imported_by)[i] = new (zone_) ZoneGrowableArray<intptr_t>(zone_, 0);
940 }
941 Array& ports = Array::Handle();
942 Namespace& ns = Namespace::Handle();
943 Library& target = Library::Handle();
944 String& target_url = String::Handle();
945
946 for (intptr_t lib_idx = 0; lib_idx < num_libs; lib_idx++) {
947 lib ^= libs.At(lib_idx);
948 ASSERT(lib_idx == lib.index());
949 if (lib.is_dart_scheme()) {
950 // We don't care about imports among dart scheme libraries.
951 continue;
952 }
953
954 // Add imports to the import-by graph.
955 ports = lib.imports();
956 for (intptr_t import_idx = 0; import_idx < ports.Length(); import_idx++) {
957 ns ^= ports.At(import_idx);
958 if (!ns.IsNull()) {
959 target = ns.library();
960 target_url = target.url();
961 if (!target_url.StartsWith(Symbols::DartExtensionScheme())) {
962 (*imported_by)[target.index()]->Add(lib.index());
963 }
964 }
965 }
966
967 // Add exports to the import-by graph.
968 ports = lib.exports();
969 for (intptr_t export_idx = 0; export_idx < ports.Length(); export_idx++) {
970 ns ^= ports.At(export_idx);
971 if (!ns.IsNull()) {
972 target = ns.library();
973 (*imported_by)[target.index()]->Add(lib.index());
974 }
975 }
976
977 // Add prefixed imports to the import-by graph.
978 DictionaryIterator entries(lib);
979 Object& entry = Object::Handle();
980 LibraryPrefix& prefix = LibraryPrefix::Handle();
981 while (entries.HasNext()) {
982 entry = entries.GetNext();
983 if (entry.IsLibraryPrefix()) {
984 prefix ^= entry.raw();
985 ports = prefix.imports();
986 for (intptr_t import_idx = 0; import_idx < ports.Length();
987 import_idx++) {
988 ns ^= ports.At(import_idx);
989 if (!ns.IsNull()) {
990 target = ns.library();
991 (*imported_by)[target.index()]->Add(lib.index());
992 }
993 }
994 }
995 }
996 }
997
998 for (intptr_t lib_idx = 0; lib_idx < num_libs; lib_idx++) {
999 lib ^= libs.At(lib_idx);
1000 if (lib.is_dart_scheme() || modified_libs_transitive_->Contains(lib_idx)) {
1001 // We don't consider dart scheme libraries during reload. If
1002 // the modified libs set already contains this library, then we
1003 // have already visited it.
1004 continue;
1005 }
1006 if (modified_libs->Contains(lib_idx)) {
1007 modified_libs_transitive_->Add(lib_idx);
1008 PropagateLibraryModified(imported_by, lib_idx, modified_libs_transitive_);
1009 }
1010 }
1011}
1012
1013void IsolateGroupReloadContext::GetRootLibUrl(const char* root_script_url) {
1014 const auto& old_root_lib =
1015 Library::Handle(first_isolate_->object_store()->root_library());
1016 ASSERT(!old_root_lib.IsNull());
1017 const auto& old_root_lib_url = String::Handle(old_root_lib.url());
1018
1019 // Root library url.
1020 if (root_script_url != nullptr) {
1021 root_lib_url_ = String::New(root_script_url);
1022 } else {
1023 root_lib_url_ = old_root_lib_url.raw();
1024 }
1025
1026 // Check to see if the base url of the loaded libraries has moved.
1027 if (!old_root_lib_url.Equals(root_lib_url_)) {
1028 const char* old_root_library_url_c = old_root_lib_url.ToCString();
1029 const char* root_library_url_c = root_lib_url_.ToCString();
1030 const intptr_t common_suffix_length =
1031 CommonSuffixLength(root_library_url_c, old_root_library_url_c);
1032 root_url_prefix_ = String::SubString(
1033 root_lib_url_, 0, root_lib_url_.Length() - common_suffix_length + 1);
1034 old_root_url_prefix_ =
1035 String::SubString(old_root_lib_url, 0,
1036 old_root_lib_url.Length() - common_suffix_length + 1);
1037 }
1038}
1039
1040char* IsolateGroupReloadContext::CompileToKernel(bool force_reload,
1041 const char* packages_url,
1042 const uint8_t** kernel_buffer,
1043 intptr_t* kernel_buffer_size) {
1044 Dart_SourceFile* modified_scripts = nullptr;
1045 intptr_t modified_scripts_count = 0;
1046 FindModifiedSources(force_reload, &modified_scripts, &modified_scripts_count,
1047 packages_url);
1048
1049 Dart_KernelCompilationResult retval = {};
1050 {
1051 const char* root_lib_url = root_lib_url_.ToCString();
1052 TransitionVMToNative transition(Thread::Current());
1053 retval = KernelIsolate::CompileToKernel(root_lib_url, nullptr, 0,
1054 modified_scripts_count,
1055 modified_scripts, true, nullptr);
1056 }
1057 if (retval.status != Dart_KernelCompilationStatus_Ok) {
1058 if (retval.kernel != nullptr) {
1059 free(retval.kernel);
1060 }
1061 return retval.error;
1062 }
1063 *kernel_buffer = retval.kernel;
1064 *kernel_buffer_size = retval.kernel_size;
1065 return nullptr;
1066}
1067
1068void IsolateReloadContext::ReloadPhase1AllocateStorageMapsAndCheckpoint() {
1069 // Preallocate storage for maps.
1070 old_classes_set_storage_ =
1071 HashTables::New<UnorderedHashSet<ClassMapTraits> >(4);
1072 class_map_storage_ = HashTables::New<UnorderedHashMap<ClassMapTraits> >(4);
1073 removed_class_set_storage_ =
1074 HashTables::New<UnorderedHashSet<ClassMapTraits> >(4);
1075 old_libraries_set_storage_ =
1076 HashTables::New<UnorderedHashSet<LibraryMapTraits> >(4);
1077 library_map_storage_ =
1078 HashTables::New<UnorderedHashMap<LibraryMapTraits> >(4);
1079 become_map_storage_ = HashTables::New<UnorderedHashMap<BecomeMapTraits> >(4);
1080 // Keep a separate array for enum mappings to avoid having to invoke
1081 // hashCode on the instances.
1082 become_enum_mappings_ = GrowableObjectArray::New(Heap::kOld);
1083
1084 // While reloading everything we do must be reversible so that we can abort
1085 // safely if the reload fails. This function stashes things to the side and
1086 // prepares the isolate for the reload attempt.
1087 {
1088 TIMELINE_SCOPE(Checkpoint);
1089 CheckpointLibraries();
1090 }
1091}
1092
1093ObjectPtr IsolateReloadContext::ReloadPhase2LoadKernel(
1094 kernel::Program* program,
1095 const String& root_lib_url) {
1096 Thread* thread = Thread::Current();
1097
1098 const Object& tmp = kernel::KernelLoader::LoadEntireProgram(program);
1099 if (tmp.IsError()) {
1100 return tmp.raw();
1101 }
1102
1103 // If main method disappeared or were not there to begin with then
1104 // KernelLoader will return null. In this case lookup library by
1105 // URL.
1106 auto& lib = Library::Handle(Library::RawCast(tmp.raw()));
1107 if (lib.IsNull()) {
1108 lib = Library::LookupLibrary(thread, root_lib_url);
1109 }
1110 isolate_->object_store()->set_root_library(lib);
1111 return Object::null();
1112}
1113
1114void IsolateReloadContext::ReloadPhase3FinalizeLoading() {
1115 BuildLibraryMapping();
1116 BuildRemovedClassesSet();
1117 ValidateReload();
1118}
1119
1120void IsolateReloadContext::ReloadPhase4CommitPrepare() {
1121 CommitBeforeInstanceMorphing();
1122}
1123
1124void IsolateReloadContext::ReloadPhase4CommitFinish() {
1125 CommitAfterInstanceMorphing();
1126 PostCommit();
1127}
1128
1129void IsolateReloadContext::ReloadPhase4Rollback() {
1130 RollbackClasses();
1131 RollbackLibraries();
1132}
1133
1134void IsolateReloadContext::RegisterClass(const Class& new_cls) {
1135 const Class& old_cls = Class::Handle(OldClassOrNull(new_cls));
1136 if (old_cls.IsNull()) {
1137 if (new_cls.IsTopLevel()) {
1138 I->class_table()->RegisterTopLevel(new_cls);
1139 } else {
1140 I->class_table()->Register(new_cls);
1141 }
1142
1143 if (FLAG_identity_reload) {
1144 TIR_Print("Could not find replacement class for %s\n",
1145 new_cls.ToCString());
1146 UNREACHABLE();
1147 }
1148
1149 // New class maps to itself.
1150 AddClassMapping(new_cls, new_cls);
1151 return;
1152 }
1153 VTIR_Print("Registering class: %s\n", new_cls.ToCString());
1154 new_cls.set_id(old_cls.id());
1155 I->class_table()->SetAt(old_cls.id(), new_cls.raw());
1156 if (!old_cls.is_enum_class()) {
1157 new_cls.CopyCanonicalConstants(old_cls);
1158 }
1159 new_cls.CopyDeclarationType(old_cls);
1160 AddBecomeMapping(old_cls, new_cls);
1161 AddClassMapping(new_cls, old_cls);
1162}
1163
1164void IsolateGroupReloadContext::CommonFinalizeTail(
1165 intptr_t final_library_count) {
1166 RELEASE_ASSERT(!reload_finalized_);
1167 ReportOnJSON(js_, final_library_count);
1168 reload_finalized_ = true;
1169}
1170
1171void IsolateGroupReloadContext::ReportOnJSON(JSONStream* stream,
1172 intptr_t final_library_count) {
1173 JSONObject jsobj(stream);
1174 jsobj.AddProperty("type", "ReloadReport");
1175 jsobj.AddProperty("success", reload_skipped_ || !HasReasonsForCancelling());
1176 {
1177 if (HasReasonsForCancelling()) {
1178 // Reload was rejected.
1179 JSONArray array(&jsobj, "notices");
1180 for (intptr_t i = 0; i < reasons_to_cancel_reload_.length(); i++) {
1181 ReasonForCancelling* reason = reasons_to_cancel_reload_.At(i);
1182 reason->AppendTo(&array);
1183 }
1184 return;
1185 }
1186
1187 JSONObject details(&jsobj, "details");
1188 details.AddProperty("finalLibraryCount", final_library_count);
1189 details.AddProperty("receivedLibraryCount", num_received_libs_);
1190 details.AddProperty("receivedLibrariesBytes", bytes_received_libs_);
1191 details.AddProperty("receivedClassesCount", num_received_classes_);
1192 details.AddProperty("receivedProceduresCount", num_received_procedures_);
1193 if (reload_skipped_) {
1194 // Reload was skipped.
1195 details.AddProperty("savedLibraryCount", final_library_count);
1196 details.AddProperty("loadedLibraryCount", static_cast<intptr_t>(0));
1197 } else {
1198 // Reload was successful.
1199 const intptr_t loaded_library_count =
1200 final_library_count - num_saved_libs_;
1201 details.AddProperty("savedLibraryCount", num_saved_libs_);
1202 details.AddProperty("loadedLibraryCount", loaded_library_count);
1203 JSONArray array(&jsobj, "shapeChangeMappings");
1204 for (intptr_t i = 0; i < instance_morphers_.length(); i++) {
1205 instance_morphers_.At(i)->AppendTo(&array);
1206 }
1207 }
1208 }
1209}
1210
1211void IsolateReloadContext::EnsuredUnoptimizedCodeForStack() {
1212 TIMELINE_SCOPE(EnsuredUnoptimizedCodeForStack);
1213 StackFrameIterator it(ValidationPolicy::kDontValidateFrames,
1214 Thread::Current(),
1215 StackFrameIterator::kNoCrossThreadIteration);
1216
1217 Function& func = Function::Handle();
1218 while (it.HasNextFrame()) {
1219 StackFrame* frame = it.NextFrame();
1220 if (frame->IsDartFrame() && !frame->is_interpreted()) {
1221 func = frame->LookupDartFunction();
1222 ASSERT(!func.IsNull());
1223 // Force-optimized functions don't need unoptimized code because their
1224 // optimized code cannot deopt.
1225 if (!func.ForceOptimize()) {
1226 func.EnsureHasCompiledUnoptimizedCode();
1227 }
1228 }
1229 }
1230}
1231
1232void IsolateReloadContext::DeoptimizeDependentCode() {
1233 TIMELINE_SCOPE(DeoptimizeDependentCode);
1234 ClassTable* class_table = I->class_table();
1235
1236 const intptr_t bottom = Dart::vm_isolate()->class_table()->NumCids();
1237 const intptr_t top = I->class_table()->NumCids();
1238 Class& cls = Class::Handle();
1239 Array& fields = Array::Handle();
1240 Field& field = Field::Handle();
1241 for (intptr_t cls_idx = bottom; cls_idx < top; cls_idx++) {
1242 if (!class_table->HasValidClassAt(cls_idx)) {
1243 // Skip.
1244 continue;
1245 }
1246
1247 // Deoptimize CHA code.
1248 cls = class_table->At(cls_idx);
1249 ASSERT(!cls.IsNull());
1250
1251 cls.DisableAllCHAOptimizedCode();
1252
1253 // Deoptimize field guard code.
1254 fields = cls.fields();
1255 ASSERT(!fields.IsNull());
1256 for (intptr_t field_idx = 0; field_idx < fields.Length(); field_idx++) {
1257 field = Field::RawCast(fields.At(field_idx));
1258 ASSERT(!field.IsNull());
1259 field.DeoptimizeDependentCode();
1260 }
1261 }
1262
1263 DeoptimizeTypeTestingStubs();
1264
1265 // TODO(rmacnak): Also call LibraryPrefix::InvalidateDependentCode.
1266}
1267
1268void IsolateGroupReloadContext::CheckpointSharedClassTable() {
1269 // Copy the size table for isolate group.
1270 intptr_t* saved_size_table = nullptr;
1271 shared_class_table_->CopyBeforeHotReload(&saved_size_table, &saved_num_cids_);
1272
1273 Thread* thread = Thread::Current();
1274 {
1275 NoSafepointScope no_safepoint_scope(thread);
1276
1277 // The saved_size_table_ will now become source of truth for GC.
1278 saved_size_table_.store(saved_size_table, std::memory_order_release);
1279 }
1280
1281 // But the concurrent sweeper may still be reading from the old table.
1282 thread->heap()->WaitForSweeperTasks(thread);
1283
1284 // Now we can clear the old table. This satisfies asserts during class
1285 // registration and encourages fast failure if we use the wrong table
1286 // for GC during reload, but isn't strictly needed for correctness.
1287 shared_class_table_->ResetBeforeHotReload();
1288}
1289
1290void IsolateReloadContext::CheckpointClasses() {
1291 TIR_Print("---- CHECKPOINTING CLASSES\n");
1292 // Checkpoint classes before a reload. We need to copy the following:
1293 // 1) The size of the class table.
1294 // 2) The class table itself.
1295 // For efficiency, we build a set of classes before the reload. This set
1296 // is used to pair new classes with old classes.
1297
1298 // Copy the class table for isolate.
1299 ClassTable* class_table = I->class_table();
1300 ClassPtr* saved_class_table = nullptr;
1301 ClassPtr* saved_tlc_class_table = nullptr;
1302 class_table->CopyBeforeHotReload(&saved_class_table, &saved_tlc_class_table,
1303 &saved_num_cids_, &saved_num_tlc_cids_);
1304
1305 // Copy classes into saved_class_table_ first. Make sure there are no
1306 // safepoints until saved_class_table_ is filled up and saved so class raw
1307 // pointers in saved_class_table_ are properly visited by GC.
1308 {
1309 NoSafepointScope no_safepoint_scope(Thread::Current());
1310
1311 // The saved_class_table_ is now source of truth for GC.
1312 saved_class_table_.store(saved_class_table, std::memory_order_release);
1313 saved_tlc_class_table_.store(saved_tlc_class_table,
1314 std::memory_order_release);
1315
1316 // We can therefore wipe out all of the old entries (if that table is used
1317 // for GC during the hot-reload we have a bug).
1318 class_table->ResetBeforeHotReload();
1319 }
1320
1321 // Add classes to the set. Set is stored in the Array, so adding an element
1322 // may allocate Dart object on the heap and trigger GC.
1323 Class& cls = Class::Handle();
1324 UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_);
1325 for (intptr_t i = 0; i < saved_num_cids_; i++) {
1326 if (class_table->IsValidIndex(i) && class_table->HasValidClassAt(i)) {
1327 if (i != kFreeListElement && i != kForwardingCorpse) {
1328 cls = class_table->At(i);
1329 bool already_present = old_classes_set.Insert(cls);
1330 ASSERT(!already_present);
1331 }
1332 }
1333 }
1334 for (intptr_t i = 0; i < saved_num_tlc_cids_; i++) {
1335 const intptr_t cid = ClassTable::CidFromTopLevelIndex(i);
1336 if (class_table->IsValidIndex(cid) && class_table->HasValidClassAt(cid)) {
1337 cls = class_table->At(cid);
1338 bool already_present = old_classes_set.Insert(cls);
1339 ASSERT(!already_present);
1340 }
1341 }
1342 old_classes_set_storage_ = old_classes_set.Release().raw();
1343 TIR_Print("---- System had %" Pd " classes\n", saved_num_cids_);
1344}
1345
1346Dart_FileModifiedCallback IsolateGroupReloadContext::file_modified_callback_ =
1347 nullptr;
1348
1349bool IsolateGroupReloadContext::ScriptModifiedSince(const Script& script,
1350 int64_t since) {
1351 if (IsolateGroupReloadContext::file_modified_callback_ == NULL) {
1352 return true;
1353 }
1354 // We use the resolved url to determine if the script has been modified.
1355 const String& url = String::Handle(script.resolved_url());
1356 const char* url_chars = url.ToCString();
1357 return (*IsolateGroupReloadContext::file_modified_callback_)(url_chars,
1358 since);
1359}
1360
1361static bool ContainsScriptUri(const GrowableArray<const char*>& seen_uris,
1362 const char* uri) {
1363 for (intptr_t i = 0; i < seen_uris.length(); i++) {
1364 const char* seen_uri = seen_uris.At(i);
1365 size_t seen_len = strlen(seen_uri);
1366 if (seen_len != strlen(uri)) {
1367 continue;
1368 } else if (strncmp(seen_uri, uri, seen_len) == 0) {
1369 return true;
1370 }
1371 }
1372 return false;
1373}
1374
1375void IsolateGroupReloadContext::FindModifiedSources(
1376 bool force_reload,
1377 Dart_SourceFile** modified_sources,
1378 intptr_t* count,
1379 const char* packages_url) {
1380 const int64_t last_reload = isolate_group_->last_reload_timestamp();
1381 GrowableArray<const char*> modified_sources_uris;
1382 const auto& libs =
1383 GrowableObjectArray::Handle(first_isolate_->object_store()->libraries());
1384 Library& lib = Library::Handle(Z);
1385 Array& scripts = Array::Handle(Z);
1386 Script& script = Script::Handle(Z);
1387 String& uri = String::Handle(Z);
1388
1389 for (intptr_t lib_idx = 0; lib_idx < libs.Length(); lib_idx++) {
1390 lib ^= libs.At(lib_idx);
1391 if (lib.is_dart_scheme()) {
1392 // We don't consider dart scheme libraries during reload.
1393 continue;
1394 }
1395 scripts = lib.LoadedScripts();
1396 for (intptr_t script_idx = 0; script_idx < scripts.Length(); script_idx++) {
1397 script ^= scripts.At(script_idx);
1398 uri = script.url();
1399 const bool dart_scheme = uri.StartsWith(Symbols::DartScheme());
1400 if (dart_scheme) {
1401 // If a user-defined class mixes in a mixin from dart:*, it's list of
1402 // scripts will have a dart:* script as well. We don't consider those
1403 // during reload.
1404 continue;
1405 }
1406 if (ContainsScriptUri(modified_sources_uris, uri.ToCString())) {
1407 // We've already accounted for this script in a prior library.
1408 continue;
1409 }
1410
1411 if (force_reload || ScriptModifiedSince(script, last_reload)) {
1412 modified_sources_uris.Add(uri.ToCString());
1413 }
1414 }
1415 }
1416
1417 // In addition to all sources, we need to check if the .packages file
1418 // contents have been modified.
1419 if (packages_url != NULL) {
1420 if (IsolateGroupReloadContext::file_modified_callback_ == NULL ||
1421 (*IsolateGroupReloadContext::file_modified_callback_)(packages_url,
1422 last_reload)) {
1423 modified_sources_uris.Add(packages_url);
1424 }
1425 }
1426
1427 *count = modified_sources_uris.length();
1428 if (*count == 0) {
1429 return;
1430 }
1431
1432 *modified_sources = Z->Alloc<Dart_SourceFile>(*count);
1433 for (intptr_t i = 0; i < *count; ++i) {
1434 (*modified_sources)[i].uri = modified_sources_uris[i];
1435 (*modified_sources)[i].source = NULL;
1436 }
1437}
1438
1439void IsolateReloadContext::CheckpointLibraries() {
1440 TIMELINE_SCOPE(CheckpointLibraries);
1441 TIR_Print("---- CHECKPOINTING LIBRARIES\n");
1442 // Save the root library in case we abort the reload.
1443 const Library& root_lib = Library::Handle(object_store()->root_library());
1444 saved_root_library_ = root_lib.raw();
1445
1446 // Save the old libraries array in case we abort the reload.
1447 const GrowableObjectArray& libs =
1448 GrowableObjectArray::Handle(object_store()->libraries());
1449 saved_libraries_ = libs.raw();
1450
1451 // Make a filtered copy of the old libraries array. Keep "clean" libraries
1452 // that we will use instead of reloading.
1453 const GrowableObjectArray& new_libs =
1454 GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld));
1455 Library& lib = Library::Handle();
1456 UnorderedHashSet<LibraryMapTraits> old_libraries_set(
1457 old_libraries_set_storage_);
1458
1459 group_reload_context_->saved_libs_transitive_updated_ = new (Z)
1460 BitVector(Z, group_reload_context_->modified_libs_transitive_->length());
1461 for (intptr_t i = 0; i < libs.Length(); i++) {
1462 lib ^= libs.At(i);
1463 if (group_reload_context_->modified_libs_->Contains(i)) {
1464 // We are going to reload this library. Clear the index.
1465 lib.set_index(-1);
1466 } else {
1467 // We are preserving this library across the reload, assign its new index
1468 lib.set_index(new_libs.Length());
1469 new_libs.Add(lib, Heap::kOld);
1470
1471 if (group_reload_context_->modified_libs_transitive_->Contains(i)) {
1472 // Remember the new index.
1473 group_reload_context_->saved_libs_transitive_updated_->Add(lib.index());
1474 }
1475 }
1476 // Add old library to old libraries set.
1477 bool already_present = old_libraries_set.Insert(lib);
1478 ASSERT(!already_present);
1479 }
1480 old_libraries_set_storage_ = old_libraries_set.Release().raw();
1481
1482 // Reset the registered libraries to the filtered array.
1483 Library::RegisterLibraries(Thread::Current(), new_libs);
1484 // Reset the root library to null.
1485 object_store()->set_root_library(Library::Handle());
1486}
1487
1488void IsolateReloadContext::RollbackClasses() {
1489 TIR_Print("---- ROLLING BACK CLASS TABLE\n");
1490 ASSERT((saved_num_cids_ + saved_num_tlc_cids_) > 0);
1491 ASSERT(saved_class_table_.load(std::memory_order_relaxed) != nullptr);
1492 ASSERT(saved_tlc_class_table_.load(std::memory_order_relaxed) != nullptr);
1493
1494 DiscardSavedClassTable(/*is_rollback=*/true);
1495}
1496
1497void IsolateReloadContext::RollbackLibraries() {
1498 TIR_Print("---- ROLLING BACK LIBRARY CHANGES\n");
1499 Thread* thread = Thread::Current();
1500 Library& lib = Library::Handle();
1501 const auto& saved_libs = GrowableObjectArray::Handle(Z, saved_libraries_);
1502 if (!saved_libs.IsNull()) {
1503 for (intptr_t i = 0; i < saved_libs.Length(); i++) {
1504 lib = Library::RawCast(saved_libs.At(i));
1505 // Restore indexes that were modified in CheckpointLibraries.
1506 lib.set_index(i);
1507 }
1508
1509 // Reset the registered libraries to the filtered array.
1510 Library::RegisterLibraries(thread, saved_libs);
1511 }
1512
1513 Library& saved_root_lib = Library::Handle(Z, saved_root_library_);
1514 if (!saved_root_lib.IsNull()) {
1515 object_store()->set_root_library(saved_root_lib);
1516 }
1517
1518 saved_root_library_ = Library::null();
1519 saved_libraries_ = GrowableObjectArray::null();
1520}
1521
1522#ifdef DEBUG
1523void IsolateReloadContext::VerifyMaps() {
1524 TIMELINE_SCOPE(VerifyMaps);
1525 Class& cls = Class::Handle();
1526 Class& new_cls = Class::Handle();
1527 Class& cls2 = Class::Handle();
1528
1529 // Verify that two old classes aren't both mapped to the same new
1530 // class. This could happen is the IsSameClass function is broken.
1531 UnorderedHashMap<ClassMapTraits> class_map(class_map_storage_);
1532 UnorderedHashMap<ClassMapTraits> reverse_class_map(
1533 HashTables::New<UnorderedHashMap<ClassMapTraits> >(
1534 class_map.NumOccupied()));
1535 {
1536 UnorderedHashMap<ClassMapTraits>::Iterator it(&class_map);
1537 while (it.MoveNext()) {
1538 const intptr_t entry = it.Current();
1539 new_cls = Class::RawCast(class_map.GetKey(entry));
1540 cls = Class::RawCast(class_map.GetPayload(entry, 0));
1541 cls2 ^= reverse_class_map.GetOrNull(new_cls);
1542 if (!cls2.IsNull()) {
1543 OS::PrintErr(
1544 "Classes '%s' and '%s' are distinct classes but both map "
1545 " to class '%s'\n",
1546 cls.ToCString(), cls2.ToCString(), new_cls.ToCString());
1547 UNREACHABLE();
1548 }
1549 bool update = reverse_class_map.UpdateOrInsert(cls, new_cls);
1550 ASSERT(!update);
1551 }
1552 }
1553 class_map.Release();
1554 reverse_class_map.Release();
1555}
1556#endif
1557
1558void IsolateReloadContext::CommitBeforeInstanceMorphing() {
1559 TIMELINE_SCOPE(Commit);
1560
1561#ifdef DEBUG
1562 VerifyMaps();
1563#endif
1564
1565 // Copy over certain properties of libraries, e.g. is the library
1566 // debuggable?
1567 {
1568 TIMELINE_SCOPE(CopyLibraryBits);
1569 Library& lib = Library::Handle();
1570 Library& new_lib = Library::Handle();
1571
1572 UnorderedHashMap<LibraryMapTraits> lib_map(library_map_storage_);
1573
1574 {
1575 // Reload existing libraries.
1576 UnorderedHashMap<LibraryMapTraits>::Iterator it(&lib_map);
1577
1578 while (it.MoveNext()) {
1579 const intptr_t entry = it.Current();
1580 ASSERT(entry != -1);
1581 new_lib = Library::RawCast(lib_map.GetKey(entry));
1582 lib = Library::RawCast(lib_map.GetPayload(entry, 0));
1583 new_lib.set_debuggable(lib.IsDebuggable());
1584 // Native extension support.
1585 new_lib.set_native_entry_resolver(lib.native_entry_resolver());
1586 new_lib.set_native_entry_symbol_resolver(
1587 lib.native_entry_symbol_resolver());
1588 }
1589 }
1590
1591 // Release the library map.
1592 lib_map.Release();
1593 }
1594
1595 {
1596 TIMELINE_SCOPE(CopyStaticFieldsAndPatchFieldsAndFunctions);
1597 // Copy static field values from the old classes to the new classes.
1598 // Patch fields and functions in the old classes so that they retain
1599 // the old script.
1600 Class& old_cls = Class::Handle();
1601 Class& new_cls = Class::Handle();
1602 UnorderedHashMap<ClassMapTraits> class_map(class_map_storage_);
1603
1604 {
1605 UnorderedHashMap<ClassMapTraits>::Iterator it(&class_map);
1606 while (it.MoveNext()) {
1607 const intptr_t entry = it.Current();
1608 new_cls = Class::RawCast(class_map.GetKey(entry));
1609 old_cls = Class::RawCast(class_map.GetPayload(entry, 0));
1610 if (new_cls.raw() != old_cls.raw()) {
1611 ASSERT(new_cls.is_enum_class() == old_cls.is_enum_class());
1612 if (new_cls.is_enum_class() && new_cls.is_finalized()) {
1613 new_cls.ReplaceEnum(this, old_cls);
1614 } else {
1615 new_cls.CopyStaticFieldValues(this, old_cls);
1616 }
1617 old_cls.PatchFieldsAndFunctions();
1618 old_cls.MigrateImplicitStaticClosures(this, new_cls);
1619 }
1620 }
1621 }
1622
1623 class_map.Release();
1624
1625 {
1626 UnorderedHashSet<ClassMapTraits> removed_class_set(
1627 removed_class_set_storage_);
1628 UnorderedHashSet<ClassMapTraits>::Iterator it(&removed_class_set);
1629 while (it.MoveNext()) {
1630 const intptr_t entry = it.Current();
1631 old_cls ^= removed_class_set.GetKey(entry);
1632 old_cls.PatchFieldsAndFunctions();
1633 }
1634 removed_class_set.Release();
1635 }
1636 }
1637
1638 {
1639 TIMELINE_SCOPE(UpdateLibrariesArray);
1640 // Update the libraries array.
1641 Library& lib = Library::Handle();
1642 const GrowableObjectArray& libs =
1643 GrowableObjectArray::Handle(I->object_store()->libraries());
1644 for (intptr_t i = 0; i < libs.Length(); i++) {
1645 lib = Library::RawCast(libs.At(i));
1646 VTIR_Print("Lib '%s' at index %" Pd "\n", lib.ToCString(), i);
1647 lib.set_index(i);
1648 }
1649
1650 // Initialize library side table.
1651 library_infos_.SetLength(libs.Length());
1652 for (intptr_t i = 0; i < libs.Length(); i++) {
1653 lib = Library::RawCast(libs.At(i));
1654 // Mark the library dirty if it comes after the libraries we saved.
1655 library_infos_[i].dirty =
1656 i >= group_reload_context_->num_saved_libs_ ||
1657 group_reload_context_->saved_libs_transitive_updated_->Contains(
1658 lib.index());
1659 }
1660 }
1661}
1662
1663void IsolateReloadContext::CommitAfterInstanceMorphing() {
1664 {
1665 const GrowableObjectArray& become_enum_mappings =
1666 GrowableObjectArray::Handle(become_enum_mappings_);
1667 UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_);
1668 intptr_t replacement_count =
1669 become_map.NumOccupied() + become_enum_mappings.Length() / 2;
1670 const Array& before =
1671 Array::Handle(Array::New(replacement_count, Heap::kOld));
1672 const Array& after =
1673 Array::Handle(Array::New(replacement_count, Heap::kOld));
1674 Object& obj = Object::Handle();
1675 intptr_t replacement_index = 0;
1676 UnorderedHashMap<BecomeMapTraits>::Iterator it(&become_map);
1677 while (it.MoveNext()) {
1678 const intptr_t entry = it.Current();
1679 obj = become_map.GetKey(entry);
1680 before.SetAt(replacement_index, obj);
1681 obj = become_map.GetPayload(entry, 0);
1682 after.SetAt(replacement_index, obj);
1683 replacement_index++;
1684 }
1685 for (intptr_t i = 0; i < become_enum_mappings.Length(); i += 2) {
1686 obj = become_enum_mappings.At(i);
1687 before.SetAt(replacement_index, obj);
1688 obj = become_enum_mappings.At(i + 1);
1689 after.SetAt(replacement_index, obj);
1690 replacement_index++;
1691 }
1692 ASSERT(replacement_index == replacement_count);
1693 become_map.Release();
1694
1695 Become::ElementsForwardIdentity(before, after);
1696 }
1697
1698 // Rehash constants map for all classes. Constants are hashed by content, and
1699 // content may have changed from fields being added or removed.
1700 {
1701 TIMELINE_SCOPE(RehashConstants);
1702 I->RehashConstants();
1703 }
1704
1705#ifdef DEBUG
1706 I->ValidateConstants();
1707#endif
1708
1709 if (FLAG_identity_reload) {
1710 if (saved_num_cids_ != I->class_table()->NumCids()) {
1711 TIR_Print("Identity reload failed! B#C=%" Pd " A#C=%" Pd "\n",
1712 saved_num_cids_, I->class_table()->NumCids());
1713 }
1714 if (saved_num_tlc_cids_ != I->class_table()->NumTopLevelCids()) {
1715 TIR_Print("Identity reload failed! B#TLC=%" Pd " A#TLC=%" Pd "\n",
1716 saved_num_tlc_cids_, I->class_table()->NumTopLevelCids());
1717 }
1718 const auto& saved_libs = GrowableObjectArray::Handle(saved_libraries_);
1719 const GrowableObjectArray& libs =
1720 GrowableObjectArray::Handle(I->object_store()->libraries());
1721 if (saved_libs.Length() != libs.Length()) {
1722 TIR_Print("Identity reload failed! B#L=%" Pd " A#L=%" Pd "\n",
1723 saved_libs.Length(), libs.Length());
1724 }
1725 }
1726}
1727
1728bool IsolateReloadContext::IsDirty(const Library& lib) {
1729 const intptr_t index = lib.index();
1730 if (index == static_cast<classid_t>(-1)) {
1731 // Treat deleted libraries as dirty.
1732 return true;
1733 }
1734 ASSERT((index >= 0) && (index < library_infos_.length()));
1735 return library_infos_[index].dirty;
1736}
1737
1738void IsolateReloadContext::PostCommit() {
1739 TIMELINE_SCOPE(PostCommit);
1740 saved_root_library_ = Library::null();
1741 saved_libraries_ = GrowableObjectArray::null();
1742 InvalidateWorld();
1743}
1744
1745void IsolateGroupReloadContext::AddReasonForCancelling(
1746 ReasonForCancelling* reason) {
1747 reasons_to_cancel_reload_.Add(reason);
1748}
1749
1750void IsolateGroupReloadContext::EnsureHasInstanceMorpherFor(
1751 classid_t cid,
1752 InstanceMorpher* instance_morpher) {
1753 for (intptr_t i = 0; i < instance_morphers_.length(); ++i) {
1754 if (instance_morphers_[i]->cid() == cid) {
1755 return;
1756 }
1757 }
1758 instance_morphers_.Add(instance_morpher);
1759 instance_morpher_by_cid_.Insert(instance_morpher);
1760 ASSERT(instance_morphers_[instance_morphers_.length() - 1]->cid() == cid);
1761}
1762
1763void IsolateGroupReloadContext::ReportReasonsForCancelling() {
1764 ASSERT(FLAG_reload_force_rollback || HasReasonsForCancelling());
1765 for (int i = 0; i < reasons_to_cancel_reload_.length(); i++) {
1766 reasons_to_cancel_reload_.At(i)->Report(this);
1767 }
1768}
1769
1770void IsolateGroupReloadContext::MorphInstancesPhase1Allocate(
1771 ObjectLocator* locator,
1772 const Array& before,
1773 const Array& after) {
1774 ASSERT(HasInstanceMorphers());
1775
1776 if (FLAG_trace_reload) {
1777 LogBlock blocker;
1778 TIR_Print("MorphInstance: \n");
1779 for (intptr_t i = 0; i < instance_morphers_.length(); i++) {
1780 instance_morphers_.At(i)->Dump();
1781 }
1782 }
1783
1784 const intptr_t count = locator->count();
1785 TIR_Print("Found %" Pd " object%s subject to morphing.\n", count,
1786 (count > 1) ? "s" : "");
1787
1788 for (intptr_t i = 0; i < instance_morphers_.length(); i++) {
1789 instance_morphers_.At(i)->CreateMorphedCopies();
1790 }
1791
1792 // Create the inputs for Become.
1793 intptr_t index = 0;
1794 for (intptr_t i = 0; i < instance_morphers_.length(); i++) {
1795 InstanceMorpher* morpher = instance_morphers_.At(i);
1796 for (intptr_t j = 0; j < morpher->before()->length(); j++) {
1797 before.SetAt(index, *morpher->before()->At(j));
1798 after.SetAt(index, *morpher->after()->At(j));
1799 index++;
1800 }
1801 }
1802 ASSERT(index == count);
1803}
1804
1805void IsolateGroupReloadContext::MorphInstancesPhase2Become(const Array& before,
1806 const Array& after) {
1807 ASSERT(HasInstanceMorphers());
1808
1809 Become::ElementsForwardIdentity(before, after);
1810 // The heap now contains only instances with the new size. Ordinary GC is safe
1811 // again.
1812}
1813
1814void IsolateGroupReloadContext::ForEachIsolate(
1815 std::function<void(Isolate*)> callback) {
1816 isolate_group_->ForEachIsolate(callback);
1817}
1818
1819void IsolateReloadContext::ValidateReload() {
1820 TIMELINE_SCOPE(ValidateReload);
1821
1822 TIR_Print("---- VALIDATING RELOAD\n");
1823
1824 // Validate libraries.
1825 {
1826 ASSERT(library_map_storage_ != Array::null());
1827 UnorderedHashMap<LibraryMapTraits> map(library_map_storage_);
1828 UnorderedHashMap<LibraryMapTraits>::Iterator it(&map);
1829 Library& lib = Library::Handle();
1830 Library& new_lib = Library::Handle();
1831 while (it.MoveNext()) {
1832 const intptr_t entry = it.Current();
1833 new_lib = Library::RawCast(map.GetKey(entry));
1834 lib = Library::RawCast(map.GetPayload(entry, 0));
1835 if (new_lib.raw() != lib.raw()) {
1836 lib.CheckReload(new_lib, this);
1837 }
1838 }
1839 map.Release();
1840 }
1841
1842 // Validate classes.
1843 {
1844 ASSERT(class_map_storage_ != Array::null());
1845 UnorderedHashMap<ClassMapTraits> map(class_map_storage_);
1846 UnorderedHashMap<ClassMapTraits>::Iterator it(&map);
1847 Class& cls = Class::Handle();
1848 Class& new_cls = Class::Handle();
1849 while (it.MoveNext()) {
1850 const intptr_t entry = it.Current();
1851 new_cls = Class::RawCast(map.GetKey(entry));
1852 cls = Class::RawCast(map.GetPayload(entry, 0));
1853 if (new_cls.raw() != cls.raw()) {
1854 cls.CheckReload(new_cls, this);
1855 }
1856 }
1857 map.Release();
1858 }
1859}
1860
1861ClassPtr IsolateReloadContext::GetClassForHeapWalkAt(intptr_t cid) {
1862 ClassPtr* class_table = nullptr;
1863 intptr_t index = -1;
1864 if (ClassTable::IsTopLevelCid(cid)) {
1865 class_table = saved_tlc_class_table_.load(std::memory_order_acquire);
1866 index = ClassTable::IndexFromTopLevelCid(cid);
1867 ASSERT(index < saved_num_tlc_cids_);
1868 } else {
1869 class_table = saved_class_table_.load(std::memory_order_acquire);
1870 index = cid;
1871 ASSERT(cid > 0 && cid < saved_num_cids_);
1872 }
1873 if (class_table != nullptr) {
1874 return class_table[index];
1875 }
1876 return isolate_->class_table()->At(cid);
1877}
1878
1879intptr_t IsolateGroupReloadContext::GetClassSizeForHeapWalkAt(classid_t cid) {
1880 if (ClassTable::IsTopLevelCid(cid)) {
1881 return 0;
1882 }
1883 intptr_t* size_table = saved_size_table_.load(std::memory_order_acquire);
1884 if (size_table != nullptr) {
1885 ASSERT(cid < saved_num_cids_);
1886 return size_table[cid];
1887 } else {
1888 return shared_class_table_->SizeAt(cid);
1889 }
1890}
1891
1892void IsolateReloadContext::DiscardSavedClassTable(bool is_rollback) {
1893 ClassPtr* local_saved_class_table =
1894 saved_class_table_.load(std::memory_order_relaxed);
1895 ClassPtr* local_saved_tlc_class_table =
1896 saved_tlc_class_table_.load(std::memory_order_relaxed);
1897 I->class_table()->ResetAfterHotReload(
1898 local_saved_class_table, local_saved_tlc_class_table, saved_num_cids_,
1899 saved_num_tlc_cids_, is_rollback);
1900 saved_class_table_.store(nullptr, std::memory_order_release);
1901 saved_tlc_class_table_.store(nullptr, std::memory_order_release);
1902}
1903
1904void IsolateGroupReloadContext::DiscardSavedClassTable(bool is_rollback) {
1905 intptr_t* local_saved_size_table = saved_size_table_;
1906 shared_class_table_->ResetAfterHotReload(local_saved_size_table,
1907 saved_num_cids_, is_rollback);
1908 saved_size_table_.store(nullptr, std::memory_order_release);
1909}
1910
1911void IsolateGroupReloadContext::VisitObjectPointers(
1912 ObjectPointerVisitor* visitor) {
1913 visitor->VisitPointers(from(), to());
1914}
1915
1916void IsolateReloadContext::VisitObjectPointers(ObjectPointerVisitor* visitor) {
1917 visitor->VisitPointers(from(), to());
1918
1919 ClassPtr* saved_class_table =
1920 saved_class_table_.load(std::memory_order_relaxed);
1921 if (saved_class_table != NULL) {
1922 auto class_table = reinterpret_cast<ObjectPtr*>(&(saved_class_table[0]));
1923 visitor->VisitPointers(class_table, saved_num_cids_);
1924 }
1925 ClassPtr* saved_tlc_class_table =
1926 saved_class_table_.load(std::memory_order_relaxed);
1927 if (saved_tlc_class_table != NULL) {
1928 auto class_table =
1929 reinterpret_cast<ObjectPtr*>(&(saved_tlc_class_table[0]));
1930 visitor->VisitPointers(class_table, saved_num_tlc_cids_);
1931 }
1932}
1933
1934ObjectStore* IsolateReloadContext::object_store() {
1935 return isolate_->object_store();
1936}
1937
1938void IsolateReloadContext::ResetUnoptimizedICsOnStack() {
1939 Thread* thread = Thread::Current();
1940 StackZone stack_zone(thread);
1941 Zone* zone = stack_zone.GetZone();
1942
1943 Code& code = Code::Handle(zone);
1944 Bytecode& bytecode = Bytecode::Handle(zone);
1945 Function& function = Function::Handle(zone);
1946 CallSiteResetter resetter(zone);
1947 DartFrameIterator iterator(thread,
1948 StackFrameIterator::kNoCrossThreadIteration);
1949 StackFrame* frame = iterator.NextFrame();
1950 while (frame != NULL) {
1951 if (frame->is_interpreted()) {
1952 bytecode = frame->LookupDartBytecode();
1953 resetter.RebindStaticTargets(bytecode);
1954 } else {
1955 code = frame->LookupDartCode();
1956 if (code.is_optimized() && !code.is_force_optimized()) {
1957 // If this code is optimized, we need to reset the ICs in the
1958 // corresponding unoptimized code, which will be executed when the stack
1959 // unwinds to the optimized code.
1960 function = code.function();
1961 code = function.unoptimized_code();
1962 ASSERT(!code.IsNull());
1963 resetter.ResetSwitchableCalls(code);
1964 resetter.ResetCaches(code);
1965 } else {
1966 resetter.ResetSwitchableCalls(code);
1967 resetter.ResetCaches(code);
1968 }
1969 }
1970 frame = iterator.NextFrame();
1971 }
1972}
1973
1974void IsolateReloadContext::ResetMegamorphicCaches() {
1975 object_store()->set_megamorphic_cache_table(GrowableObjectArray::Handle());
1976 // Since any current optimized code will not make any more calls, it may be
1977 // better to clear the table instead of clearing each of the caches, allow
1978 // the current megamorphic caches get GC'd and any new optimized code allocate
1979 // new ones.
1980}
1981
1982class InvalidationCollector : public ObjectVisitor {
1983 public:
1984 InvalidationCollector(Zone* zone,
1985 GrowableArray<const Function*>* functions,
1986 GrowableArray<const KernelProgramInfo*>* kernel_infos,
1987 GrowableArray<const Field*>* fields,
1988 GrowableArray<const Instance*>* instances)
1989 : zone_(zone),
1990 functions_(functions),
1991 kernel_infos_(kernel_infos),
1992 fields_(fields),
1993 instances_(instances) {}
1994 virtual ~InvalidationCollector() {}
1995
1996 void VisitObject(ObjectPtr obj) {
1997 intptr_t cid = obj->GetClassId();
1998 if (cid == kFunctionCid) {
1999 const Function& func =
2000 Function::Handle(zone_, static_cast<FunctionPtr>(obj));
2001 if (!func.ForceOptimize()) {
2002 // Force-optimized functions cannot deoptimize.
2003 functions_->Add(&func);
2004 }
2005 } else if (cid == kKernelProgramInfoCid) {
2006 kernel_infos_->Add(&KernelProgramInfo::Handle(
2007 zone_, static_cast<KernelProgramInfoPtr>(obj)));
2008 } else if (cid == kFieldCid) {
2009 fields_->Add(&Field::Handle(zone_, static_cast<FieldPtr>(obj)));
2010 } else if (cid > kNumPredefinedCids) {
2011 instances_->Add(&Instance::Handle(zone_, static_cast<InstancePtr>(obj)));
2012 }
2013 }
2014
2015 private:
2016 Zone* const zone_;
2017 GrowableArray<const Function*>* const functions_;
2018 GrowableArray<const KernelProgramInfo*>* const kernel_infos_;
2019 GrowableArray<const Field*>* const fields_;
2020 GrowableArray<const Instance*>* const instances_;
2021};
2022
2023typedef UnorderedHashMap<SmiTraits> IntHashMap;
2024
2025void IsolateReloadContext::RunInvalidationVisitors() {
2026 TIR_Print("---- RUNNING INVALIDATION HEAP VISITORS\n");
2027 Thread* thread = Thread::Current();
2028 StackZone stack_zone(thread);
2029 Zone* zone = stack_zone.GetZone();
2030
2031 Thread* mutator_thread = I->mutator_thread();
2032 if (mutator_thread != nullptr) {
2033 Interpreter* interpreter = mutator_thread->interpreter();
2034 if (interpreter != nullptr) {
2035 interpreter->ClearLookupCache();
2036 }
2037 }
2038
2039 GrowableArray<const Function*> functions(4 * KB);
2040 GrowableArray<const KernelProgramInfo*> kernel_infos(KB);
2041 GrowableArray<const Field*> fields(4 * KB);
2042 GrowableArray<const Instance*> instances(4 * KB);
2043
2044 {
2045 HeapIterationScope iteration(thread);
2046 InvalidationCollector visitor(zone, &functions, &kernel_infos, &fields,
2047 &instances);
2048 iteration.IterateObjects(&visitor);
2049 }
2050
2051 InvalidateKernelInfos(zone, kernel_infos);
2052 InvalidateFunctions(zone, functions);
2053 InvalidateFields(zone, fields, instances);
2054}
2055
2056void IsolateReloadContext::InvalidateKernelInfos(
2057 Zone* zone,
2058 const GrowableArray<const KernelProgramInfo*>& kernel_infos) {
2059 TIMELINE_SCOPE(InvalidateKernelInfos);
2060 HANDLESCOPE(Thread::Current());
2061
2062 Array& data = Array::Handle(zone);
2063 Object& key = Object::Handle(zone);
2064 Smi& value = Smi::Handle(zone);
2065 for (intptr_t i = 0; i < kernel_infos.length(); i++) {
2066 const KernelProgramInfo& info = *kernel_infos[i];
2067 // Clear the libraries cache.
2068 {
2069 data = info.libraries_cache();
2070 ASSERT(!data.IsNull());
2071 IntHashMap table(&key, &value, &data);
2072 table.Clear();
2073 info.set_libraries_cache(table.Release());
2074 }
2075 // Clear the classes cache.
2076 {
2077 data = info.classes_cache();
2078 ASSERT(!data.IsNull());
2079 IntHashMap table(&key, &value, &data);
2080 table.Clear();
2081 info.set_classes_cache(table.Release());
2082 }
2083 // Clear the bytecode object table.
2084 if (info.bytecode_component() != Array::null()) {
2085 kernel::BytecodeReader::ResetObjectTable(info);
2086 }
2087 }
2088}
2089
2090void IsolateReloadContext::InvalidateFunctions(
2091 Zone* zone,
2092 const GrowableArray<const Function*>& functions) {
2093 TIMELINE_SCOPE(InvalidateFunctions);
2094 HANDLESCOPE(Thread::Current());
2095
2096 CallSiteResetter resetter(zone);
2097
2098 Class& owning_class = Class::Handle(zone);
2099 Library& owning_lib = Library::Handle(zone);
2100 Code& code = Code::Handle(zone);
2101 Bytecode& bytecode = Bytecode::Handle(zone);
2102 for (intptr_t i = 0; i < functions.length(); i++) {
2103 const Function& func = *functions[i];
2104 if (func.IsSignatureFunction()) {
2105 continue;
2106 }
2107
2108 // Switch to unoptimized code or the lazy compilation stub.
2109 func.SwitchToLazyCompiledUnoptimizedCode();
2110
2111 // Grab the current code.
2112 code = func.CurrentCode();
2113 ASSERT(!code.IsNull());
2114 bytecode = func.bytecode();
2115
2116 owning_class = func.Owner();
2117 owning_lib = owning_class.library();
2118 const bool clear_code = IsDirty(owning_lib);
2119 const bool stub_code = code.IsStubCode();
2120
2121 // Zero edge counters, before clearing the ICDataArray, since that's where
2122 // they're held.
2123 resetter.ZeroEdgeCounters(func);
2124
2125 if (!bytecode.IsNull()) {
2126 resetter.RebindStaticTargets(bytecode);
2127 }
2128
2129 if (stub_code) {
2130 // Nothing to reset.
2131 } else if (clear_code) {
2132 VTIR_Print("Marking %s for recompilation, clearing code\n",
2133 func.ToCString());
2134 // Null out the ICData array and code.
2135 func.ClearICDataArray();
2136 func.ClearCode();
2137 func.SetWasCompiled(false);
2138 } else {
2139 // We are preserving the unoptimized code, reset instance calls and type
2140 // test caches.
2141 resetter.ResetSwitchableCalls(code);
2142 resetter.ResetCaches(code);
2143 }
2144
2145 // Clear counters.
2146 func.set_usage_counter(0);
2147 func.set_deoptimization_counter(0);
2148 func.set_optimized_instruction_count(0);
2149 func.set_optimized_call_site_count(0);
2150 }
2151}
2152
2153// Finds fields that are initialized or have a value that does not conform to
2154// the field's static type, setting Field::needs_load_guard(). Accessors for
2155// such fields are compiled with additional checks to handle lazy initialization
2156// and to preserve type soundness.
2157class FieldInvalidator {
2158 public:
2159 explicit FieldInvalidator(Zone* zone)
2160 : cls_(Class::Handle(zone)),
2161 cls_fields_(Array::Handle(zone)),
2162 entry_(Object::Handle(zone)),
2163 value_(Instance::Handle(zone)),
2164 type_(AbstractType::Handle(zone)),
2165 cache_(SubtypeTestCache::Handle(zone)),
2166 entries_(Array::Handle(zone)),
2167 instantiator_type_arguments_(TypeArguments::Handle(zone)),
2168 function_type_arguments_(TypeArguments::Handle(zone)),
2169 instance_cid_or_function_(Object::Handle(zone)),
2170 instance_type_arguments_(TypeArguments::Handle(zone)),
2171 parent_function_type_arguments_(TypeArguments::Handle(zone)),
2172 delayed_function_type_arguments_(TypeArguments::Handle(zone)) {}
2173
2174 void CheckStatics(const GrowableArray<const Field*>& fields) {
2175 Thread* thread = Thread::Current();
2176 Isolate* isolate = thread->isolate();
2177 bool null_safety = isolate->null_safety();
2178 HANDLESCOPE(thread);
2179 instantiator_type_arguments_ = TypeArguments::null();
2180 for (intptr_t i = 0; i < fields.length(); i++) {
2181 const Field& field = *fields[i];
2182 if (!field.is_static()) {
2183 continue;
2184 }
2185 if (field.needs_load_guard()) {
2186 continue; // Already guarding.
2187 }
2188 value_ = field.StaticValue();
2189 if (value_.raw() != Object::sentinel().raw()) {
2190 CheckValueType(null_safety, value_, field);
2191 }
2192 }
2193 }
2194
2195 void CheckInstances(const GrowableArray<const Instance*>& instances) {
2196 Thread* thread = Thread::Current();
2197 Isolate* isolate = thread->isolate();
2198 bool null_safety = isolate->null_safety();
2199 HANDLESCOPE(thread);
2200 for (intptr_t i = 0; i < instances.length(); i++) {
2201 CheckInstance(null_safety, *instances[i]);
2202 }
2203 }
2204
2205 private:
2206 DART_FORCE_INLINE
2207 void CheckInstance(bool null_safety, const Instance& instance) {
2208 cls_ = instance.clazz();
2209 if (cls_.NumTypeArguments() > 0) {
2210 instantiator_type_arguments_ = instance.GetTypeArguments();
2211 } else {
2212 instantiator_type_arguments_ = TypeArguments::null();
2213 }
2214 cls_fields_ = cls_.OffsetToFieldMap();
2215 for (intptr_t i = 0; i < cls_fields_.Length(); i++) {
2216 entry_ = cls_fields_.At(i);
2217 if (!entry_.IsField()) {
2218 continue;
2219 }
2220 const Field& field = Field::Cast(entry_);
2221 CheckInstanceField(null_safety, instance, field);
2222 }
2223 }
2224
2225 DART_FORCE_INLINE
2226 void CheckInstanceField(bool null_safety,
2227 const Instance& instance,
2228 const Field& field) {
2229 if (field.needs_load_guard()) {
2230 return; // Already guarding.
2231 }
2232 value_ ^= instance.GetField(field);
2233 if (value_.raw() == Object::sentinel().raw()) {
2234 if (field.is_late()) {
2235 // Late fields already have lazy initialization logic.
2236 return;
2237 }
2238 // Needs guard for initialization.
2239 ASSERT(!FLAG_identity_reload);
2240 field.set_needs_load_guard(true);
2241 return;
2242 }
2243 CheckValueType(null_safety, value_, field);
2244 }
2245
2246 DART_FORCE_INLINE
2247 void CheckValueType(bool null_safety,
2248 const Instance& value,
2249 const Field& field) {
2250 if (!null_safety && value.IsNull()) {
2251 return;
2252 }
2253 type_ = field.type();
2254 if (type_.IsDynamicType()) {
2255 return;
2256 }
2257
2258 cls_ = value.clazz();
2259 const intptr_t cid = cls_.id();
2260 if (cid == kClosureCid) {
2261 instance_cid_or_function_ = Closure::Cast(value).function();
2262 instance_type_arguments_ =
2263 Closure::Cast(value).instantiator_type_arguments();
2264 parent_function_type_arguments_ =
2265 Closure::Cast(value).function_type_arguments();
2266 delayed_function_type_arguments_ =
2267 Closure::Cast(value).delayed_type_arguments();
2268 } else {
2269 instance_cid_or_function_ = Smi::New(cid);
2270 if (cls_.NumTypeArguments() > 0) {
2271 instance_type_arguments_ = value_.GetTypeArguments();
2272 } else {
2273 instance_type_arguments_ = TypeArguments::null();
2274 }
2275 parent_function_type_arguments_ = TypeArguments::null();
2276 delayed_function_type_arguments_ = TypeArguments::null();
2277 }
2278
2279 cache_ = field.type_test_cache();
2280 if (cache_.IsNull()) {
2281 cache_ = SubtypeTestCache::New();
2282 field.set_type_test_cache(cache_);
2283 }
2284 entries_ = cache_.cache();
2285
2286 bool cache_hit = false;
2287 for (intptr_t i = 0; entries_.At(i) != Object::null();
2288 i += SubtypeTestCache::kTestEntryLength) {
2289 if ((entries_.At(i + SubtypeTestCache::kInstanceClassIdOrFunction) ==
2290 instance_cid_or_function_.raw()) &&
2291 (entries_.At(i + SubtypeTestCache::kInstanceTypeArguments) ==
2292 instance_type_arguments_.raw()) &&
2293 (entries_.At(i + SubtypeTestCache::kInstantiatorTypeArguments) ==
2294 instantiator_type_arguments_.raw()) &&
2295 (entries_.At(i + SubtypeTestCache::kFunctionTypeArguments) ==
2296 function_type_arguments_.raw()) &&
2297 (entries_.At(
2298 i + SubtypeTestCache::kInstanceParentFunctionTypeArguments) ==
2299 parent_function_type_arguments_.raw()) &&
2300 (entries_.At(
2301 i + SubtypeTestCache::kInstanceDelayedFunctionTypeArguments) ==
2302 delayed_function_type_arguments_.raw())) {
2303 cache_hit = true;
2304 if (entries_.At(i + SubtypeTestCache::kTestResult) !=
2305 Bool::True().raw()) {
2306 ASSERT(!FLAG_identity_reload);
2307 field.set_needs_load_guard(true);
2308 }
2309 break;
2310 }
2311 }
2312
2313 if (!cache_hit) {
2314 if (!value.IsAssignableTo(type_, instantiator_type_arguments_,
2315 function_type_arguments_)) {
2316 ASSERT(!FLAG_identity_reload);
2317 field.set_needs_load_guard(true);
2318 } else {
2319 cache_.AddCheck(instance_cid_or_function_, instance_type_arguments_,
2320 instantiator_type_arguments_, function_type_arguments_,
2321 parent_function_type_arguments_,
2322 delayed_function_type_arguments_, Bool::True());
2323 }
2324 }
2325 }
2326
2327 Class& cls_;
2328 Array& cls_fields_;
2329 Object& entry_;
2330 Instance& value_;
2331 AbstractType& type_;
2332 SubtypeTestCache& cache_;
2333 Array& entries_;
2334 TypeArguments& instantiator_type_arguments_;
2335 TypeArguments& function_type_arguments_;
2336 Object& instance_cid_or_function_;
2337 TypeArguments& instance_type_arguments_;
2338 TypeArguments& parent_function_type_arguments_;
2339 TypeArguments& delayed_function_type_arguments_;
2340};
2341
2342void IsolateReloadContext::InvalidateFields(
2343 Zone* zone,
2344 const GrowableArray<const Field*>& fields,
2345 const GrowableArray<const Instance*>& instances) {
2346 TIMELINE_SCOPE(InvalidateFields);
2347 SafepointMutexLocker ml(isolate()->group()->subtype_test_cache_mutex());
2348 FieldInvalidator invalidator(zone);
2349 invalidator.CheckStatics(fields);
2350 invalidator.CheckInstances(instances);
2351}
2352
2353void IsolateReloadContext::InvalidateWorld() {
2354 TIMELINE_SCOPE(InvalidateWorld);
2355 TIR_Print("---- INVALIDATING WORLD\n");
2356 ResetMegamorphicCaches();
2357 if (FLAG_trace_deoptimization) {
2358 THR_Print("Deopt for reload\n");
2359 }
2360 DeoptimizeFunctionsOnStack();
2361 ResetUnoptimizedICsOnStack();
2362 RunInvalidationVisitors();
2363}
2364
2365ClassPtr IsolateReloadContext::OldClassOrNull(const Class& replacement_or_new) {
2366 UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_);
2367 Class& cls = Class::Handle();
2368 cls ^= old_classes_set.GetOrNull(replacement_or_new);
2369 old_classes_set_storage_ = old_classes_set.Release().raw();
2370 return cls.raw();
2371}
2372
2373StringPtr IsolateReloadContext::FindLibraryPrivateKey(
2374 const Library& replacement_or_new) {
2375 const Library& old = Library::Handle(OldLibraryOrNull(replacement_or_new));
2376 if (old.IsNull()) {
2377 return String::null();
2378 }
2379#if defined(DEBUG)
2380 VTIR_Print("`%s` is getting `%s`'s private key.\n",
2381 String::Handle(replacement_or_new.url()).ToCString(),
2382 String::Handle(old.url()).ToCString());
2383#endif
2384 return old.private_key();
2385}
2386
2387LibraryPtr IsolateReloadContext::OldLibraryOrNull(
2388 const Library& replacement_or_new) {
2389 UnorderedHashSet<LibraryMapTraits> old_libraries_set(
2390 old_libraries_set_storage_);
2391 Library& lib = Library::Handle();
2392 lib ^= old_libraries_set.GetOrNull(replacement_or_new);
2393 old_libraries_set.Release();
2394
2395 if (lib.IsNull() &&
2396 (group_reload_context_->root_url_prefix_ != String::null()) &&
2397 (group_reload_context_->old_root_url_prefix_ != String::null())) {
2398 return OldLibraryOrNullBaseMoved(replacement_or_new);
2399 }
2400 return lib.raw();
2401}
2402
2403// Attempt to find the pair to |replacement_or_new| with the knowledge that
2404// the base url prefix has moved.
2405LibraryPtr IsolateReloadContext::OldLibraryOrNullBaseMoved(
2406 const Library& replacement_or_new) {
2407 const String& url_prefix =
2408 String::Handle(group_reload_context_->root_url_prefix_);
2409 const String& old_url_prefix =
2410 String::Handle(group_reload_context_->old_root_url_prefix_);
2411 const intptr_t prefix_length = url_prefix.Length();
2412 const intptr_t old_prefix_length = old_url_prefix.Length();
2413 const String& new_url = String::Handle(replacement_or_new.url());
2414 const String& suffix =
2415 String::Handle(String::SubString(new_url, prefix_length));
2416 if (!new_url.StartsWith(url_prefix)) {
2417 return Library::null();
2418 }
2419 Library& old = Library::Handle();
2420 String& old_url = String::Handle();
2421 String& old_suffix = String::Handle();
2422 const auto& saved_libs = GrowableObjectArray::Handle(saved_libraries_);
2423 ASSERT(!saved_libs.IsNull());
2424 for (intptr_t i = 0; i < saved_libs.Length(); i++) {
2425 old = Library::RawCast(saved_libs.At(i));
2426 old_url = old.url();
2427 if (!old_url.StartsWith(old_url_prefix)) {
2428 continue;
2429 }
2430 old_suffix = String::SubString(old_url, old_prefix_length);
2431 if (old_suffix.IsNull()) {
2432 continue;
2433 }
2434 if (old_suffix.Equals(suffix)) {
2435 TIR_Print("`%s` is moving to `%s`\n", old_url.ToCString(),
2436 new_url.ToCString());
2437 return old.raw();
2438 }
2439 }
2440 return Library::null();
2441}
2442
2443void IsolateReloadContext::BuildLibraryMapping() {
2444 const GrowableObjectArray& libs =
2445 GrowableObjectArray::Handle(object_store()->libraries());
2446
2447 Library& replacement_or_new = Library::Handle();
2448 Library& old = Library::Handle();
2449 for (intptr_t i = group_reload_context_->num_saved_libs_; i < libs.Length();
2450 i++) {
2451 replacement_or_new = Library::RawCast(libs.At(i));
2452 old = OldLibraryOrNull(replacement_or_new);
2453 if (old.IsNull()) {
2454 if (FLAG_identity_reload) {
2455 TIR_Print("Could not find original library for %s\n",
2456 replacement_or_new.ToCString());
2457 UNREACHABLE();
2458 }
2459 // New library.
2460 AddLibraryMapping(replacement_or_new, replacement_or_new);
2461 } else {
2462 ASSERT(!replacement_or_new.is_dart_scheme());
2463 // Replaced class.
2464 AddLibraryMapping(replacement_or_new, old);
2465
2466 AddBecomeMapping(old, replacement_or_new);
2467 }
2468 }
2469}
2470
2471// Find classes that have been removed from the program.
2472// Instances of these classes may still be referenced from variables, so the
2473// functions of these class may still execute in the future, and they need to
2474// be given patch class owners still they correctly reference their (old) kernel
2475// data even after the library's kernel data is updated.
2476//
2477// Note that all such classes must belong to a library that has either been
2478// changed or removed.
2479void IsolateReloadContext::BuildRemovedClassesSet() {
2480 // Find all old classes [mapped_old_classes_set].
2481 UnorderedHashMap<ClassMapTraits> class_map(class_map_storage_);
2482 UnorderedHashSet<ClassMapTraits> mapped_old_classes_set(
2483 HashTables::New<UnorderedHashSet<ClassMapTraits> >(
2484 class_map.NumOccupied()));
2485 {
2486 UnorderedHashMap<ClassMapTraits>::Iterator it(&class_map);
2487 Class& cls = Class::Handle();
2488 Class& new_cls = Class::Handle();
2489 while (it.MoveNext()) {
2490 const intptr_t entry = it.Current();
2491 new_cls = Class::RawCast(class_map.GetKey(entry));
2492 cls = Class::RawCast(class_map.GetPayload(entry, 0));
2493 mapped_old_classes_set.InsertOrGet(cls);
2494 }
2495 }
2496 class_map.Release();
2497
2498 // Find all reloaded libraries [mapped_old_library_set].
2499 UnorderedHashMap<LibraryMapTraits> library_map(library_map_storage_);
2500 UnorderedHashMap<LibraryMapTraits>::Iterator it_library(&library_map);
2501 UnorderedHashSet<LibraryMapTraits> mapped_old_library_set(
2502 HashTables::New<UnorderedHashSet<LibraryMapTraits> >(
2503 library_map.NumOccupied()));
2504 {
2505 Library& old_library = Library::Handle();
2506 Library& new_library = Library::Handle();
2507 while (it_library.MoveNext()) {
2508 const intptr_t entry = it_library.Current();
2509 new_library ^= library_map.GetKey(entry);
2510 old_library ^= library_map.GetPayload(entry, 0);
2511 if (new_library.raw() != old_library.raw()) {
2512 mapped_old_library_set.InsertOrGet(old_library);
2513 }
2514 }
2515 }
2516
2517 // For every old class, check if it's library was reloaded and if
2518 // the class was mapped. If the class wasn't mapped - add it to
2519 // [removed_class_set].
2520 UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_);
2521 UnorderedHashSet<ClassMapTraits>::Iterator it(&old_classes_set);
2522 UnorderedHashSet<ClassMapTraits> removed_class_set(
2523 removed_class_set_storage_);
2524 Class& old_cls = Class::Handle();
2525 Class& new_cls = Class::Handle();
2526 Library& old_library = Library::Handle();
2527 Library& mapped_old_library = Library::Handle();
2528 while (it.MoveNext()) {
2529 const intptr_t entry = it.Current();
2530 old_cls ^= Class::RawCast(old_classes_set.GetKey(entry));
2531 old_library = old_cls.library();
2532 if (old_library.IsNull()) {
2533 continue;
2534 }
2535 mapped_old_library ^= mapped_old_library_set.GetOrNull(old_library);
2536 if (!mapped_old_library.IsNull()) {
2537 new_cls ^= mapped_old_classes_set.GetOrNull(old_cls);
2538 if (new_cls.IsNull()) {
2539 removed_class_set.InsertOrGet(old_cls);
2540 }
2541 }
2542 }
2543 removed_class_set_storage_ = removed_class_set.Release().raw();
2544
2545 old_classes_set.Release();
2546 mapped_old_classes_set.Release();
2547 mapped_old_library_set.Release();
2548 library_map.Release();
2549}
2550
2551void IsolateReloadContext::AddClassMapping(const Class& replacement_or_new,
2552 const Class& original) {
2553 UnorderedHashMap<ClassMapTraits> map(class_map_storage_);
2554 bool update = map.UpdateOrInsert(replacement_or_new, original);
2555 ASSERT(!update);
2556 // The storage given to the map may have been reallocated, remember the new
2557 // address.
2558 class_map_storage_ = map.Release().raw();
2559}
2560
2561void IsolateReloadContext::AddLibraryMapping(const Library& replacement_or_new,
2562 const Library& original) {
2563 UnorderedHashMap<LibraryMapTraits> map(library_map_storage_);
2564 bool update = map.UpdateOrInsert(replacement_or_new, original);
2565 ASSERT(!update);
2566 // The storage given to the map may have been reallocated, remember the new
2567 // address.
2568 library_map_storage_ = map.Release().raw();
2569}
2570
2571void IsolateReloadContext::AddStaticFieldMapping(const Field& old_field,
2572 const Field& new_field) {
2573 ASSERT(old_field.is_static());
2574 ASSERT(new_field.is_static());
2575
2576 AddBecomeMapping(old_field, new_field);
2577}
2578
2579void IsolateReloadContext::AddBecomeMapping(const Object& old,
2580 const Object& neu) {
2581 ASSERT(become_map_storage_ != Array::null());
2582 UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_);
2583 bool update = become_map.UpdateOrInsert(old, neu);
2584 ASSERT(!update);
2585 become_map_storage_ = become_map.Release().raw();
2586}
2587
2588void IsolateReloadContext::AddEnumBecomeMapping(const Object& old,
2589 const Object& neu) {
2590 const GrowableObjectArray& become_enum_mappings =
2591 GrowableObjectArray::Handle(become_enum_mappings_);
2592 become_enum_mappings.Add(old);
2593 become_enum_mappings.Add(neu);
2594 ASSERT((become_enum_mappings.Length() % 2) == 0);
2595}
2596
2597void IsolateReloadContext::RebuildDirectSubclasses() {
2598 ClassTable* class_table = I->class_table();
2599 intptr_t num_cids = class_table->NumCids();
2600
2601 // Clear the direct subclasses for all classes.
2602 Class& cls = Class::Handle();
2603 GrowableObjectArray& subclasses = GrowableObjectArray::Handle();
2604 for (intptr_t i = 1; i < num_cids; i++) {
2605 if (class_table->HasValidClassAt(i)) {
2606 cls = class_table->At(i);
2607 if (!cls.is_declaration_loaded()) {
2608 continue; // Can't have any subclasses or implementors yet.
2609 }
2610 subclasses = cls.direct_subclasses();
2611 if (!subclasses.IsNull()) {
2612 cls.ClearDirectSubclasses();
2613 }
2614 subclasses = cls.direct_implementors();
2615 if (!subclasses.IsNull()) {
2616 cls.ClearDirectImplementors();
2617 }
2618 }
2619 }
2620
2621 // Recompute the direct subclasses / implementors.
2622
2623 AbstractType& super_type = AbstractType::Handle();
2624 Class& super_cls = Class::Handle();
2625
2626 Array& interface_types = Array::Handle();
2627 AbstractType& interface_type = AbstractType::Handle();
2628 Class& interface_class = Class::Handle();
2629
2630 for (intptr_t i = 1; i < num_cids; i++) {
2631 if (class_table->HasValidClassAt(i)) {
2632 cls = class_table->At(i);
2633 if (!cls.is_declaration_loaded()) {
2634 continue; // Will register itself later when loaded.
2635 }
2636 super_type = cls.super_type();
2637 if (!super_type.IsNull() && !super_type.IsObjectType()) {
2638 super_cls = cls.SuperClass();
2639 ASSERT(!super_cls.IsNull());
2640 super_cls.AddDirectSubclass(cls);
2641 }
2642
2643 interface_types = cls.interfaces();
2644 if (!interface_types.IsNull()) {
2645 const intptr_t mixin_index = cls.is_transformed_mixin_application()
2646 ? interface_types.Length() - 1
2647 : -1;
2648 for (intptr_t j = 0; j < interface_types.Length(); ++j) {
2649 interface_type ^= interface_types.At(j);
2650 interface_class = interface_type.type_class();
2651 interface_class.AddDirectImplementor(
2652 cls, /* is_mixin = */ i == mixin_index);
2653 }
2654 }
2655 }
2656 }
2657}
2658
2659#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
2660
2661} // namespace dart
2662