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