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#ifndef RUNTIME_VM_ISOLATE_RELOAD_H_
6#define RUNTIME_VM_ISOLATE_RELOAD_H_
7
8#include <functional>
9#include <memory>
10
11#include "include/dart_tools_api.h"
12
13#include "vm/globals.h"
14#include "vm/growable_array.h"
15#include "vm/hash_map.h"
16#include "vm/log.h"
17#include "vm/object.h"
18
19DECLARE_FLAG(bool, trace_reload);
20DECLARE_FLAG(bool, trace_reload_verbose);
21
22// 'Trace Isolate Reload' TIR_Print
23#if defined(_MSC_VER)
24#define TIR_Print(format, ...) \
25 if (FLAG_trace_reload) Log::Current()->Print(format, __VA_ARGS__)
26#else
27#define TIR_Print(format, ...) \
28 if (FLAG_trace_reload) Log::Current()->Print(format, ##__VA_ARGS__)
29#endif
30
31// 'Verbose Trace Isolate Reload' VTIR_Print
32#if defined(_MSC_VER)
33#define VTIR_Print(format, ...) \
34 if (FLAG_trace_reload_verbose) Log::Current()->Print(format, __VA_ARGS__)
35#else
36#define VTIR_Print(format, ...) \
37 if (FLAG_trace_reload_verbose) Log::Current()->Print(format, ##__VA_ARGS__)
38#endif
39
40#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
41
42namespace dart {
43
44class BitVector;
45class GrowableObjectArray;
46class Isolate;
47class Library;
48class ObjectLocator;
49class ObjectPointerVisitor;
50class ObjectStore;
51class Script;
52class UpdateClassesVisitor;
53
54class InstanceMorpher : public ZoneAllocated {
55 public:
56 // Creates a new [InstanceMorpher] based on the [from]/[to] class
57 // descriptions.
58 static InstanceMorpher* CreateFromClassDescriptors(
59 Zone* zone,
60 SharedClassTable* shared_class_table,
61 const Class& from,
62 const Class& to);
63
64 InstanceMorpher(Zone* zone,
65 classid_t cid,
66 SharedClassTable* shared_class_table,
67 ZoneGrowableArray<intptr_t>* mapping,
68 ZoneGrowableArray<intptr_t>* new_fields_offsets);
69 virtual ~InstanceMorpher() {}
70
71 // Called on each instance that needs to be morphed.
72 InstancePtr Morph(const Instance& instance) const;
73
74 // Adds an object to be morphed.
75 void AddObject(ObjectPtr object);
76
77 // Create the morphed objects based on the before() list.
78 void CreateMorphedCopies();
79
80 // Append the morper info to JSON array.
81 void AppendTo(JSONArray* array);
82
83 // Returns the list of objects that need to be morphed.
84 const GrowableArray<const Instance*>* before() const { return &before_; }
85
86 // Returns the list of morphed objects (matches order in before()).
87 const GrowableArray<const Instance*>* after() const { return &after_; }
88
89 // Returns the cid associated with the from_ and to_ class.
90 intptr_t cid() const { return cid_; }
91
92 // Dumps the field mappings for the [cid()] class.
93 void Dump() const;
94
95 private:
96 Zone* zone_;
97 classid_t cid_;
98 SharedClassTable* shared_class_table_;
99 ZoneGrowableArray<intptr_t>* mapping_;
100 ZoneGrowableArray<intptr_t>* new_fields_offsets_;
101
102 GrowableArray<const Instance*> before_;
103 GrowableArray<const Instance*> after_;
104};
105
106class ReasonForCancelling : public ZoneAllocated {
107 public:
108 explicit ReasonForCancelling(Zone* zone) {}
109 virtual ~ReasonForCancelling() {}
110
111 // Reports a reason for cancelling reload.
112 void Report(IsolateGroupReloadContext* context);
113
114 // Conversion to a VM error object.
115 // Default implementation calls ToString.
116 virtual ErrorPtr ToError();
117
118 // Conversion to a string object.
119 // Default implementation calls ToError.
120 virtual StringPtr ToString();
121
122 // Append the reason to JSON array.
123 virtual void AppendTo(JSONArray* array);
124
125 // Concrete subclasses must override either ToError or ToString.
126};
127
128// Abstract class for also capturing the from_ and to_ class.
129class ClassReasonForCancelling : public ReasonForCancelling {
130 public:
131 ClassReasonForCancelling(Zone* zone, const Class& from, const Class& to);
132 void AppendTo(JSONArray* array);
133
134 protected:
135 const Class& from_;
136 const Class& to_;
137};
138
139class IsolateGroupReloadContext {
140 public:
141 IsolateGroupReloadContext(IsolateGroup* isolate,
142 SharedClassTable* shared_class_table,
143 JSONStream* js);
144 ~IsolateGroupReloadContext();
145
146 // If kernel_buffer is provided, the VM takes ownership when Reload is called.
147 bool Reload(bool force_reload,
148 const char* root_script_url = NULL,
149 const char* packages_url = NULL,
150 const uint8_t* kernel_buffer = NULL,
151 intptr_t kernel_buffer_size = 0);
152
153 // All zone allocated objects must be allocated from this zone.
154 Zone* zone() const { return zone_; }
155
156 bool UseSavedSizeTableForGC() const {
157 return saved_size_table_.load(std::memory_order_relaxed) != nullptr;
158 }
159
160 IsolateGroup* isolate_group() const { return isolate_group_; }
161 bool reload_aborted() const { return HasReasonsForCancelling(); }
162 bool reload_skipped() const { return reload_skipped_; }
163 ErrorPtr error() const;
164 int64_t start_time_micros() const { return start_time_micros_; }
165 int64_t reload_timestamp() const { return reload_timestamp_; }
166
167 static Dart_FileModifiedCallback file_modified_callback() {
168 return file_modified_callback_;
169 }
170 static void SetFileModifiedCallback(Dart_FileModifiedCallback callback) {
171 file_modified_callback_ = callback;
172 }
173
174 private:
175 intptr_t GetClassSizeForHeapWalkAt(classid_t cid);
176 void DiscardSavedClassTable(bool is_rollback);
177
178 // Tells whether there are reasons for cancelling the reload.
179 bool HasReasonsForCancelling() const {
180 return !reasons_to_cancel_reload_.is_empty();
181 }
182
183 // Record problem for this reload.
184 void AddReasonForCancelling(ReasonForCancelling* reason);
185
186 // Reports all reasons for cancelling reload.
187 void ReportReasonsForCancelling();
188
189 // Reports the details of a reload operation.
190 void ReportOnJSON(JSONStream* stream, intptr_t final_library_count);
191
192 // Ensures there is a instance morpher for [cid], if not it will use
193 // [instance_morpher]
194 void EnsureHasInstanceMorpherFor(classid_t cid,
195 InstanceMorpher* instance_morpher);
196
197 // Tells whether instance in the heap must be morphed.
198 bool HasInstanceMorphers() const { return !instance_morphers_.is_empty(); }
199
200 // Called by both FinalizeLoading and FinalizeFailedLoad.
201 void CommonFinalizeTail(intptr_t final_library_count);
202
203 // Report back through the observatory channels.
204 void ReportError(const Error& error);
205 void ReportSuccess();
206
207 void VisitObjectPointers(ObjectPointerVisitor* visitor);
208
209 void GetRootLibUrl(const char* root_script_url);
210 char* CompileToKernel(bool force_reload,
211 const char* packages_url,
212 const uint8_t** kernel_buffer,
213 intptr_t* kernel_buffer_size);
214 void BuildModifiedLibrariesClosure(BitVector* modified_libs);
215 void FindModifiedSources(bool force_reload,
216 Dart_SourceFile** modified_sources,
217 intptr_t* count,
218 const char* packages_url);
219 bool ScriptModifiedSince(const Script& script, int64_t since);
220
221 void CheckpointSharedClassTable();
222
223 void MorphInstancesPhase1Allocate(ObjectLocator* locator,
224 const Array& before,
225 const Array& after);
226 void MorphInstancesPhase2Become(const Array& before, const Array& after);
227
228 void ForEachIsolate(std::function<void(Isolate*)> callback);
229
230 // The zone used for all reload related allocations.
231 Zone* zone_;
232
233 IsolateGroup* isolate_group_;
234 SharedClassTable* shared_class_table_;
235
236 int64_t start_time_micros_ = -1;
237 int64_t reload_timestamp_ = -1;
238 Isolate* first_isolate_ = nullptr;
239 bool reload_skipped_ = false;
240 bool reload_finalized_ = false;
241 JSONStream* js_;
242 intptr_t num_old_libs_ = -1;
243
244 intptr_t saved_num_cids_ = -1;
245 std::atomic<intptr_t*> saved_size_table_;
246 intptr_t num_received_libs_ = -1;
247 intptr_t bytes_received_libs_ = -1;
248 intptr_t num_received_classes_ = -1;
249 intptr_t num_received_procedures_ = -1;
250 intptr_t num_saved_libs_ = -1;
251
252 // Required trait for the instance_morpher_by_cid_;
253 struct MorpherTrait {
254 typedef InstanceMorpher* Value;
255 typedef intptr_t Key;
256 typedef InstanceMorpher* Pair;
257
258 static Key KeyOf(Pair kv) { return kv->cid(); }
259 static Value ValueOf(Pair kv) { return kv; }
260 static intptr_t Hashcode(Key key) { return key; }
261 static bool IsKeyEqual(Pair kv, Key key) { return kv->cid() == key; }
262 };
263
264 // Collect the necessary instance transformation for schema changes.
265 GrowableArray<InstanceMorpher*> instance_morphers_;
266
267 // Collects the reasons for cancelling the reload.
268 GrowableArray<ReasonForCancelling*> reasons_to_cancel_reload_;
269
270 // Hash map from cid to InstanceMorpher.
271 DirectChainedHashMap<MorpherTrait> instance_morpher_by_cid_;
272
273 // A bit vector indicating which of the original libraries were modified.
274 BitVector* modified_libs_ = nullptr;
275
276 // A bit vector indicating which of the original libraries were modified,
277 // or where a transitive dependency was modified.
278 BitVector* modified_libs_transitive_ = nullptr;
279
280 // A bit vector indicating which of the saved libraries that transitively
281 // depend on a modified libary.
282 BitVector* saved_libs_transitive_updated_ = nullptr;
283
284 String& root_lib_url_;
285 ObjectPtr* from() { return reinterpret_cast<ObjectPtr*>(&root_url_prefix_); }
286 StringPtr root_url_prefix_;
287 StringPtr old_root_url_prefix_;
288 ObjectPtr* to() {
289 return reinterpret_cast<ObjectPtr*>(&old_root_url_prefix_);
290 }
291
292 friend class Isolate;
293 friend class Class; // AddStaticFieldMapping, AddEnumBecomeMapping.
294 friend class Library;
295 friend class ObjectLocator;
296 friend class MarkFunctionsForRecompilation; // IsDirty.
297 friend class ReasonForCancelling;
298 friend class IsolateReloadContext;
299 friend class IsolateGroup; // GetClassSizeForHeapWalkAt
300 friend class ObjectLayout; // GetClassSizeForHeapWalkAt
301
302 static Dart_FileModifiedCallback file_modified_callback_;
303};
304
305class IsolateReloadContext {
306 public:
307 IsolateReloadContext(
308 std::shared_ptr<IsolateGroupReloadContext> group_reload_context,
309 Isolate* isolate);
310 ~IsolateReloadContext();
311
312 // All zone allocated objects must be allocated from this zone.
313 Zone* zone() const { return zone_; }
314
315 IsolateGroupReloadContext* group_reload_context() {
316 return group_reload_context_.get();
317 }
318
319 static bool IsSameLibrary(const Library& a_lib, const Library& b_lib);
320 static bool IsSameClass(const Class& a, const Class& b);
321
322 private:
323 bool IsDirty(const Library& lib);
324
325 // Prefers old classes when we are in the middle of a reload.
326 ClassPtr GetClassForHeapWalkAt(intptr_t cid);
327 void DiscardSavedClassTable(bool is_rollback);
328
329 void RegisterClass(const Class& new_cls);
330
331 // Finds the library private key for |replacement_or_new| or return null
332 // if |replacement_or_new| is new.
333 StringPtr FindLibraryPrivateKey(const Library& replacement_or_new);
334
335 void VisitObjectPointers(ObjectPointerVisitor* visitor);
336
337 Isolate* isolate() { return isolate_; }
338 ObjectStore* object_store();
339
340 void EnsuredUnoptimizedCodeForStack();
341 void DeoptimizeDependentCode();
342
343 void ReloadPhase1AllocateStorageMapsAndCheckpoint();
344 void CheckpointClasses();
345 ObjectPtr ReloadPhase2LoadKernel(kernel::Program* program,
346 const String& root_lib_url);
347 void ReloadPhase3FinalizeLoading();
348 void ReloadPhase4CommitPrepare();
349 void ReloadPhase4CommitFinish();
350 void ReloadPhase4Rollback();
351
352 void CheckpointLibraries();
353
354 void RollbackClasses();
355 void RollbackLibraries();
356
357#ifdef DEBUG
358 void VerifyMaps();
359#endif
360
361 void CommitBeforeInstanceMorphing();
362 void CommitAfterInstanceMorphing();
363 void PostCommit();
364
365 void RunInvalidationVisitors();
366 void InvalidateKernelInfos(
367 Zone* zone,
368 const GrowableArray<const KernelProgramInfo*>& kernel_infos);
369 void InvalidateFunctions(Zone* zone,
370 const GrowableArray<const Function*>& functions);
371 void InvalidateFields(Zone* zone,
372 const GrowableArray<const Field*>& fields,
373 const GrowableArray<const Instance*>& instances);
374 void ResetUnoptimizedICsOnStack();
375 void ResetMegamorphicCaches();
376 void InvalidateWorld();
377
378 struct LibraryInfo {
379 bool dirty;
380 };
381
382 // The zone used for all reload related allocations.
383 Zone* zone_;
384 std::shared_ptr<IsolateGroupReloadContext> group_reload_context_;
385 Isolate* isolate_;
386 intptr_t saved_num_cids_ = -1;
387 intptr_t saved_num_tlc_cids_ = -1;
388 std::atomic<ClassPtr*> saved_class_table_;
389 std::atomic<ClassPtr*> saved_tlc_class_table_;
390 MallocGrowableArray<LibraryInfo> library_infos_;
391
392 ClassPtr OldClassOrNull(const Class& replacement_or_new);
393 LibraryPtr OldLibraryOrNull(const Library& replacement_or_new);
394 LibraryPtr OldLibraryOrNullBaseMoved(const Library& replacement_or_new);
395
396 void BuildLibraryMapping();
397 void BuildRemovedClassesSet();
398 void ValidateReload();
399
400 void AddClassMapping(const Class& replacement_or_new, const Class& original);
401 void AddLibraryMapping(const Library& replacement_or_new,
402 const Library& original);
403 void AddStaticFieldMapping(const Field& old_field, const Field& new_field);
404 void AddBecomeMapping(const Object& old, const Object& neu);
405 void AddEnumBecomeMapping(const Object& old, const Object& neu);
406 void RebuildDirectSubclasses();
407
408 ObjectPtr* from() {
409 return reinterpret_cast<ObjectPtr*>(&old_classes_set_storage_);
410 }
411 ArrayPtr old_classes_set_storage_;
412 ArrayPtr class_map_storage_;
413 ArrayPtr removed_class_set_storage_;
414 ArrayPtr old_libraries_set_storage_;
415 ArrayPtr library_map_storage_;
416 ArrayPtr become_map_storage_;
417 GrowableObjectArrayPtr become_enum_mappings_;
418 LibraryPtr saved_root_library_;
419 GrowableObjectArrayPtr saved_libraries_;
420 ObjectPtr* to() { return reinterpret_cast<ObjectPtr*>(&saved_libraries_); }
421
422 friend class Isolate;
423 friend class Class; // AddStaticFieldMapping, AddEnumBecomeMapping.
424 friend class Library;
425 friend class ObjectLocator;
426 friend class MarkFunctionsForRecompilation; // IsDirty.
427 friend class ReasonForCancelling;
428 friend class IsolateGroupReloadContext;
429};
430
431class CallSiteResetter : public ValueObject {
432 public:
433 explicit CallSiteResetter(Zone* zone);
434
435 void ZeroEdgeCounters(const Function& function);
436 void ResetCaches(const Code& code);
437 void ResetCaches(const ObjectPool& pool);
438 void RebindStaticTargets(const Bytecode& code);
439 void Reset(const ICData& ic);
440 void ResetSwitchableCalls(const Code& code);
441
442 private:
443 Zone* zone_;
444 Instructions& instrs_;
445 ObjectPool& pool_;
446 Object& object_;
447 String& name_;
448 Class& new_cls_;
449 Library& new_lib_;
450 Function& new_function_;
451 Field& new_field_;
452 Array& entries_;
453 Function& old_target_;
454 Function& new_target_;
455 Function& caller_;
456 Array& args_desc_array_;
457 Array& ic_data_array_;
458 Array& edge_counters_;
459 PcDescriptors& descriptors_;
460 ICData& ic_data_;
461};
462
463} // namespace dart
464
465#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
466
467#endif // RUNTIME_VM_ISOLATE_RELOAD_H_
468