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