Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(185)

Side by Side Diff: runtime/vm/isolate_reload.cc

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

Powered by Google App Engine
This is Rietveld 408576698