| // Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | #ifndef RUNTIME_VM_ISOLATE_RELOAD_H_ | 
 | #define RUNTIME_VM_ISOLATE_RELOAD_H_ | 
 |  | 
 | #include <functional> | 
 | #include <memory> | 
 |  | 
 | #include "include/dart_tools_api.h" | 
 |  | 
 | #include "vm/compiler/jit/compiler.h" | 
 | #include "vm/globals.h" | 
 | #include "vm/growable_array.h" | 
 | #include "vm/hash_map.h" | 
 | #include "vm/heap/become.h" | 
 | #include "vm/heap/safepoint.h" | 
 | #include "vm/log.h" | 
 | #include "vm/object.h" | 
 |  | 
 | DECLARE_FLAG(bool, trace_reload); | 
 | DECLARE_FLAG(bool, trace_reload_verbose); | 
 |  | 
 | // 'Trace Isolate Reload' TIR_Print | 
 | #if defined(_MSC_VER) | 
 | #define TIR_Print(format, ...)                                                 \ | 
 |   if (FLAG_trace_reload) Log::Current()->Print(format, __VA_ARGS__) | 
 | #else | 
 | #define TIR_Print(format, ...)                                                 \ | 
 |   if (FLAG_trace_reload) Log::Current()->Print(format, ##__VA_ARGS__) | 
 | #endif | 
 |  | 
 | // 'Verbose Trace Isolate Reload' VTIR_Print | 
 | #if defined(_MSC_VER) | 
 | #define VTIR_Print(format, ...)                                                \ | 
 |   if (FLAG_trace_reload_verbose) Log::Current()->Print(format, __VA_ARGS__) | 
 | #else | 
 | #define VTIR_Print(format, ...)                                                \ | 
 |   if (FLAG_trace_reload_verbose) Log::Current()->Print(format, ##__VA_ARGS__) | 
 | #endif | 
 |  | 
 | #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) | 
 |  | 
 | namespace dart { | 
 |  | 
 | class BitVector; | 
 | class GrowableObjectArray; | 
 | class Isolate; | 
 | class Library; | 
 | class ObjectLocator; | 
 | class ObjectPointerVisitor; | 
 | class ObjectStore; | 
 | class Script; | 
 | class UpdateClassesVisitor; | 
 |  | 
 | struct FieldMapping { | 
 |   intptr_t offset; | 
 |   intptr_t box_cid;  // kIllegalCid if field is boxed | 
 | }; | 
 |  | 
 | using FieldMappingArray = ZoneGrowableArray<FieldMapping>; | 
 | using FieldOffsetArray = ZoneGrowableArray<intptr_t>; | 
 |  | 
 | class InstanceMorpher : public ZoneAllocated { | 
 |  public: | 
 |   // Creates a new [InstanceMorpher] based on the [from]/[to] class | 
 |   // descriptions. | 
 |   static InstanceMorpher* CreateFromClassDescriptors(Zone* zone, | 
 |                                                      ClassTable* class_table, | 
 |                                                      const Class& from, | 
 |                                                      const Class& to); | 
 |  | 
 |   InstanceMorpher(Zone* zone, | 
 |                   classid_t cid, | 
 |                   const Class& old_class, | 
 |                   const Class& new_class, | 
 |                   FieldMappingArray* mapping, | 
 |                   FieldOffsetArray* new_fields_offsets); | 
 |   virtual ~InstanceMorpher() {} | 
 |  | 
 |   // Adds an object to be morphed. | 
 |   void AddObject(ObjectPtr object); | 
 |  | 
 |   // Create the morphed objects based on the before() list. | 
 |   void CreateMorphedCopies(Become* become); | 
 |  | 
 |   // Append the morper info to JSON array. | 
 |   void AppendTo(JSONArray* array); | 
 |  | 
 |   // Returns the cid associated with the from_ and to_ class. | 
 |   intptr_t cid() const { return cid_; } | 
 |  | 
 |   // Dumps the field mappings for the [cid()] class. | 
 |   void Dump() const; | 
 |  | 
 |  private: | 
 |   Zone* zone_; | 
 |   classid_t cid_; | 
 |   const Class& old_class_; | 
 |   const Class& new_class_; | 
 |   FieldMappingArray* mapping_; | 
 |   FieldOffsetArray* new_fields_offsets_; | 
 |  | 
 |   GrowableArray<const Instance*> before_; | 
 | }; | 
 |  | 
 | class ReasonForCancelling : public ZoneAllocated { | 
 |  public: | 
 |   explicit ReasonForCancelling(Zone* zone) {} | 
 |   virtual ~ReasonForCancelling() {} | 
 |  | 
 |   // Reports a reason for cancelling reload. | 
 |   void Report(IsolateGroupReloadContext* context); | 
 |  | 
 |   // Conversion to a VM error object. | 
 |   // Default implementation calls ToString. | 
 |   virtual ErrorPtr ToError(); | 
 |  | 
 |   // Conversion to a string object. | 
 |   // Default implementation calls ToError. | 
 |   virtual StringPtr ToString(); | 
 |  | 
 |   // Append the reason to JSON array. | 
 |   virtual void AppendTo(JSONArray* array); | 
 |  | 
 |   // Concrete subclasses must override either ToError or ToString. | 
 | }; | 
 |  | 
 | // Abstract class for also capturing the from_ and to_ class. | 
 | class ClassReasonForCancelling : public ReasonForCancelling { | 
 |  public: | 
 |   ClassReasonForCancelling(Zone* zone, const Class& from, const Class& to); | 
 |   void AppendTo(JSONArray* array); | 
 |  | 
 |  protected: | 
 |   const Class& from_; | 
 |   const Class& to_; | 
 | }; | 
 |  | 
 | class IsolateGroupReloadContext { | 
 |  public: | 
 |   IsolateGroupReloadContext(IsolateGroup* isolate, | 
 |                             ClassTable* class_table, | 
 |                             JSONStream* js); | 
 |   ~IsolateGroupReloadContext(); | 
 |  | 
 |   // If kernel_buffer is provided, the VM takes ownership when Reload is called. | 
 |   bool Reload(bool force_reload, | 
 |               const char* root_script_url = nullptr, | 
 |               const char* packages_url = nullptr, | 
 |               const uint8_t* kernel_buffer = nullptr, | 
 |               intptr_t kernel_buffer_size = 0); | 
 |  | 
 |   // All zone allocated objects must be allocated from this zone. | 
 |   Zone* zone() const { return zone_; } | 
 |  | 
 |   IsolateGroup* isolate_group() const { return isolate_group_; } | 
 |   bool reload_aborted() const { return HasReasonsForCancelling(); } | 
 |   bool reload_skipped() const { return reload_skipped_; } | 
 |   ErrorPtr error() const; | 
 |   int64_t start_time_micros() const { return start_time_micros_; } | 
 |   int64_t reload_timestamp() const { return reload_timestamp_; } | 
 |  | 
 |   static Dart_FileModifiedCallback file_modified_callback() { | 
 |     return file_modified_callback_; | 
 |   } | 
 |   static void SetFileModifiedCallback(Dart_FileModifiedCallback callback) { | 
 |     file_modified_callback_ = callback; | 
 |   } | 
 |  | 
 |  private: | 
 |   // Tells whether there are reasons for cancelling the reload. | 
 |   bool HasReasonsForCancelling() const { | 
 |     return !reasons_to_cancel_reload_.is_empty(); | 
 |   } | 
 |  | 
 |   // Record problem for this reload. | 
 |   void AddReasonForCancelling(ReasonForCancelling* reason); | 
 |  | 
 |   // Reports all reasons for cancelling reload. | 
 |   void ReportReasonsForCancelling(); | 
 |  | 
 |   // Reports the details of a reload operation. | 
 |   void ReportOnJSON(JSONStream* stream, intptr_t final_library_count); | 
 |  | 
 |   // Ensures there is a instance morpher for [cid], if not it will use | 
 |   // [instance_morpher] | 
 |   void EnsureHasInstanceMorpherFor(classid_t cid, | 
 |                                    InstanceMorpher* instance_morpher); | 
 |  | 
 |   // Tells whether instance in the heap must be morphed. | 
 |   bool HasInstanceMorphers() const { return !instance_morphers_.is_empty(); } | 
 |  | 
 |   // Called by both FinalizeLoading and FinalizeFailedLoad. | 
 |   void CommonFinalizeTail(intptr_t final_library_count); | 
 |  | 
 |   // Report back through the observatory channels. | 
 |   void ReportError(const Error& error); | 
 |   void ReportSuccess(); | 
 |  | 
 |   void VisitObjectPointers(ObjectPointerVisitor* visitor); | 
 |  | 
 |   void GetRootLibUrl(const char* root_script_url); | 
 |   char* CompileToKernel(bool force_reload, | 
 |                         const char* packages_url, | 
 |                         const uint8_t** kernel_buffer, | 
 |                         intptr_t* kernel_buffer_size); | 
 |   void BuildModifiedLibrariesClosure(BitVector* modified_libs); | 
 |   void FindModifiedSources(bool force_reload, | 
 |                            Dart_SourceFile** modified_sources, | 
 |                            intptr_t* count, | 
 |                            const char* packages_url); | 
 |   bool ScriptModifiedSince(const Script& script, int64_t since); | 
 |  | 
 |   void MorphInstancesPhase1Allocate(ObjectLocator* locator, Become* become); | 
 |   void MorphInstancesPhase2Become(Become* become); | 
 |  | 
 |   void ForEachIsolate(std::function<void(Isolate*)> callback); | 
 |  | 
 |   // The zone used for all reload related allocations. | 
 |   Zone* zone_; | 
 |  | 
 |   IsolateGroup* isolate_group_; | 
 |   ClassTable* class_table_; | 
 |  | 
 |   int64_t start_time_micros_ = -1; | 
 |   int64_t reload_timestamp_ = -1; | 
 |   bool reload_skipped_ = false; | 
 |   bool reload_finalized_ = false; | 
 |   JSONStream* js_; | 
 |   intptr_t num_old_libs_ = -1; | 
 |  | 
 |   intptr_t num_received_libs_ = -1; | 
 |   intptr_t bytes_received_libs_ = -1; | 
 |   intptr_t num_received_classes_ = -1; | 
 |   intptr_t num_received_procedures_ = -1; | 
 |   intptr_t num_saved_libs_ = -1; | 
 |  | 
 |   // Required trait for the instance_morpher_by_cid_; | 
 |   struct MorpherTrait { | 
 |     typedef InstanceMorpher* Value; | 
 |     typedef intptr_t Key; | 
 |     typedef InstanceMorpher* Pair; | 
 |  | 
 |     static Key KeyOf(Pair kv) { return kv->cid(); } | 
 |     static Value ValueOf(Pair kv) { return kv; } | 
 |     static uword Hash(Key key) { return Utils::WordHash(key); } | 
 |     static bool IsKeyEqual(Pair kv, Key key) { return kv->cid() == key; } | 
 |   }; | 
 |  | 
 |   // Collect the necessary instance transformation for schema changes. | 
 |   GrowableArray<InstanceMorpher*> instance_morphers_; | 
 |  | 
 |   // Collects the reasons for cancelling the reload. | 
 |   GrowableArray<ReasonForCancelling*> reasons_to_cancel_reload_; | 
 |  | 
 |   // Hash map from cid to InstanceMorpher. | 
 |   DirectChainedHashMap<MorpherTrait> instance_morpher_by_cid_; | 
 |  | 
 |   // A bit vector indicating which of the original libraries were modified. | 
 |   BitVector* modified_libs_ = nullptr; | 
 |  | 
 |   // A bit vector indicating which of the original libraries were modified, | 
 |   // or where a transitive dependency was modified. | 
 |   BitVector* modified_libs_transitive_ = nullptr; | 
 |  | 
 |   // A bit vector indicating which of the saved libraries that transitively | 
 |   // depend on a modified library. | 
 |   BitVector* saved_libs_transitive_updated_ = nullptr; | 
 |  | 
 |   String& root_lib_url_; | 
 |   ObjectPtr* from() { return reinterpret_cast<ObjectPtr*>(&root_url_prefix_); } | 
 |   StringPtr root_url_prefix_; | 
 |   StringPtr old_root_url_prefix_; | 
 |   ObjectPtr* to() { | 
 |     return reinterpret_cast<ObjectPtr*>(&old_root_url_prefix_); | 
 |   } | 
 |  | 
 |   friend class Isolate; | 
 |   friend class Class;  // AddStaticFieldMapping, AddEnumBecomeMapping. | 
 |   friend class Library; | 
 |   friend class ObjectLocator; | 
 |   friend class ReasonForCancelling; | 
 |   friend class ProgramReloadContext; | 
 |   friend class IsolateGroup; | 
 |  | 
 |   static Dart_FileModifiedCallback file_modified_callback_; | 
 | }; | 
 |  | 
 | class ProgramReloadContext { | 
 |  public: | 
 |   ProgramReloadContext( | 
 |       std::shared_ptr<IsolateGroupReloadContext> group_reload_context, | 
 |       IsolateGroup* isolate_group); | 
 |   ~ProgramReloadContext(); | 
 |  | 
 |   // All zone allocated objects must be allocated from this zone. | 
 |   Zone* zone() const { return zone_; } | 
 |  | 
 |   IsolateGroupReloadContext* group_reload_context() { | 
 |     return group_reload_context_.get(); | 
 |   } | 
 |  | 
 |   static bool IsSameLibrary(const Library& a_lib, const Library& b_lib); | 
 |   static bool IsSameClass(const Class& a, const Class& b); | 
 |  | 
 |  private: | 
 |   bool IsDirty(const Library& lib); | 
 |  | 
 |   void RegisterClass(const Class& new_cls); | 
 |  | 
 |   // Finds the library private key for |replacement_or_new| or return null | 
 |   // if |replacement_or_new| is new. | 
 |   StringPtr FindLibraryPrivateKey(const Library& replacement_or_new); | 
 |  | 
 |   void VisitObjectPointers(ObjectPointerVisitor* visitor); | 
 |  | 
 |   IsolateGroup* isolate_group() { return isolate_group_; } | 
 |   ObjectStore* object_store(); | 
 |  | 
 |   ErrorPtr EnsuredUnoptimizedCodeForStack(); | 
 |   void DeoptimizeDependentCode(); | 
 |  | 
 |   void ReloadPhase1AllocateStorageMapsAndCheckpoint(); | 
 |   void CheckpointClasses(); | 
 |   ObjectPtr ReloadPhase2LoadKernel(kernel::Program* program, | 
 |                                    const String& root_lib_url); | 
 |   void ReloadPhase3FinalizeLoading(); | 
 |   void ReloadPhase4CommitPrepare(); | 
 |   ErrorPtr ReloadPhase4CommitFinish(); | 
 |   void ReloadPhase4Rollback(); | 
 |  | 
 |   void CheckpointLibraries(); | 
 |  | 
 |   void RollbackLibraries(); | 
 |  | 
 |   void VerifyMaps(); | 
 |  | 
 |   void CommitBeforeInstanceMorphing(); | 
 |   void CommitAfterInstanceMorphing(); | 
 |   ErrorPtr PostCommit(); | 
 |  | 
 |   ErrorPtr RunInvalidationVisitors(); | 
 |   void InvalidateKernelInfos( | 
 |       Zone* zone, | 
 |       const GrowableArray<const KernelProgramInfo*>& kernel_infos); | 
 |   void InvalidateFunctions(Zone* zone, | 
 |                            const GrowableArray<const Function*>& functions); | 
 |   ErrorPtr InvalidateSuspendStates( | 
 |       Zone* zone, | 
 |       const GrowableArray<const SuspendState*>& suspend_states); | 
 |   void InvalidateFields(Zone* zone, | 
 |                         const GrowableArray<const Field*>& fields, | 
 |                         const GrowableArray<const Instance*>& instances); | 
 |   void ResetUnoptimizedICsOnStack(); | 
 |   void ResetMegamorphicCaches(); | 
 |   ErrorPtr InvalidateWorld(); | 
 |  | 
 |   struct LibraryInfo { | 
 |     bool dirty; | 
 |   }; | 
 |  | 
 |   // The zone used for all reload related allocations. | 
 |   Zone* zone_; | 
 |   std::shared_ptr<IsolateGroupReloadContext> group_reload_context_; | 
 |   IsolateGroup* isolate_group_; | 
 |   MallocGrowableArray<LibraryInfo> library_infos_; | 
 |  | 
 |   ClassPtr OldClassOrNull(const Class& replacement_or_new); | 
 |   LibraryPtr OldLibraryOrNull(const Library& replacement_or_new); | 
 |   LibraryPtr OldLibraryOrNullBaseMoved(const Library& replacement_or_new); | 
 |  | 
 |   void BuildLibraryMapping(); | 
 |   void BuildRemovedClassesSet(); | 
 |   void ValidateReload(); | 
 |  | 
 |   void AddClassMapping(const Class& replacement_or_new, const Class& original); | 
 |   void AddLibraryMapping(const Library& replacement_or_new, | 
 |                          const Library& original); | 
 |   void AddStaticFieldMapping(const Field& old_field, const Field& new_field); | 
 |   void AddBecomeMapping(const Object& old, const Object& neu); | 
 |   void RestoreClassHierarchyInvariants(); | 
 |  | 
 |   Become become_; | 
 |  | 
 |   ObjectPtr* from() { | 
 |     return reinterpret_cast<ObjectPtr*>(&old_classes_set_storage_); | 
 |   } | 
 |   ArrayPtr old_classes_set_storage_; | 
 |   ArrayPtr class_map_storage_; | 
 |   ArrayPtr removed_class_set_storage_; | 
 |   ArrayPtr old_libraries_set_storage_; | 
 |   ArrayPtr library_map_storage_; | 
 |   LibraryPtr saved_root_library_; | 
 |   GrowableObjectArrayPtr saved_libraries_; | 
 |   ObjectPtr* to() { return reinterpret_cast<ObjectPtr*>(&saved_libraries_); } | 
 |  | 
 |   friend class Isolate; | 
 |   friend class IsolateGroup; | 
 |   friend class Class;  // AddStaticFieldMapping, AddEnumBecomeMapping. | 
 |   friend class Library; | 
 |   friend class ObjectLocator; | 
 |   friend class ReasonForCancelling; | 
 |   friend class IsolateGroupReloadContext; | 
 | }; | 
 |  | 
 | class CallSiteResetter : public ValueObject { | 
 |  public: | 
 |   explicit CallSiteResetter(Zone* zone); | 
 |  | 
 |   void ZeroEdgeCounters(const Function& function); | 
 |   void ResetCaches(const Code& code); | 
 |   void ResetCaches(const ObjectPool& pool); | 
 |   void Reset(const ICData& ic); | 
 |   void ResetSwitchableCalls(const Code& code); | 
 |  | 
 |  private: | 
 |   Zone* zone_; | 
 |   Instructions& instrs_; | 
 |   ObjectPool& pool_; | 
 |   Object& object_; | 
 |   String& name_; | 
 |   Class& new_cls_; | 
 |   Library& new_lib_; | 
 |   Function& new_function_; | 
 |   Field& new_field_; | 
 |   Array& entries_; | 
 |   Function& old_target_; | 
 |   Function& new_target_; | 
 |   Function& caller_; | 
 |   Array& args_desc_array_; | 
 |   Array& ic_data_array_; | 
 |   Array& edge_counters_; | 
 |   PcDescriptors& descriptors_; | 
 |   ICData& ic_data_; | 
 | }; | 
 |  | 
 | // Ensures all other mutators are stopped at a well-defined place where reload | 
 | // is allowed. | 
 | #define RELOAD_OPERATION_SCOPE(thread_expr)                                    \ | 
 |   auto _thread_ = (thread_expr);                                               \ | 
 |                                                                                \ | 
 |   /* As the background compiler is a mutator it participates in safepoint */   \ | 
 |   /* operations. Though the BG compiler won't check into reload safepoint */   \ | 
 |   /* requests - as it's not a well-defined place to do reload.            */   \ | 
 |   /* So we ensure the background compiler is stopped before we get all    */   \ | 
 |   /* other mutators to reload safepoints.                                 */   \ | 
 |   NoBackgroundCompilerScope _stop_bg_compiler_(_thread_);                      \ | 
 |                                                                                \ | 
 |   /* This will enable the current thread to perform reload operations (as */   \ | 
 |   /* well as check-in with other thread's reload operations).             */   \ | 
 |   ReloadParticipationScope _allow_reload_(_thread_);                           \ | 
 |                                                                                \ | 
 |   /* The actual reload operation that will ensure all other mutators are */    \ | 
 |   /* stopped at well-defined places where reload can happen.             */    \ | 
 |   ReloadSafepointOperationScope _safepoint_operation_(_thread_);               \ | 
 |                                                                                \ | 
 |   /* Native calls during reload should not enter reload safepoints. */         \ | 
 |   NoReloadScope _no_reload_(_thread_); | 
 |  | 
 | }  // namespace dart | 
 |  | 
 | #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) | 
 |  | 
 | #endif  // RUNTIME_VM_ISOLATE_RELOAD_H_ |