| // Copyright 2011 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_HANDLES_GLOBAL_HANDLES_H_ |
| #define V8_HANDLES_GLOBAL_HANDLES_H_ |
| |
| #include <type_traits> |
| #include <utility> |
| #include <vector> |
| |
| #include "include/v8.h" |
| #include "include/v8-profiler.h" |
| |
| #include "src/utils/utils.h" |
| #include "src/handles/handles.h" |
| #include "src/objects/objects.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| class HeapStats; |
| class RootVisitor; |
| |
| enum WeaknessType { |
| // Embedder gets a handle to the dying object. |
| FINALIZER_WEAK, |
| // In the following cases, the embedder gets the parameter they passed in |
| // earlier, and 0 or 2 first embedder fields. Note that the internal |
| // fields must contain aligned non-V8 pointers. Getting pointers to V8 |
| // objects through this interface would be GC unsafe so in that case the |
| // embedder gets a null pointer instead. |
| PHANTOM_WEAK, |
| PHANTOM_WEAK_2_EMBEDDER_FIELDS, |
| // The handle is automatically reset by the garbage collector when |
| // the object is no longer reachable. |
| PHANTOM_WEAK_RESET_HANDLE |
| }; |
| |
| // Global handles hold handles that are independent of stack-state and can have |
| // callbacks and finalizers attached to them. |
| class V8_EXPORT_PRIVATE GlobalHandles final { |
| public: |
| template <class NodeType> |
| class NodeBlock; |
| |
| // |
| // API for regular handles. |
| // |
| |
| static void MoveGlobal(Address** from, Address** to); |
| |
| static Handle<Object> CopyGlobal(Address* location); |
| |
| static void Destroy(Address* location); |
| |
| // Make the global handle weak and set the callback parameter for the |
| // handle. When the garbage collector recognizes that only weak global |
| // handles point to an object the callback function is invoked (for each |
| // handle) with the handle and corresponding parameter as arguments. By |
| // default the handle still contains a pointer to the object that is being |
| // collected. For this reason the object is not collected until the next |
| // GC. For a phantom weak handle the handle is cleared (set to a Smi) |
| // before the callback is invoked, but the handle can still be identified |
| // in the callback by using the location() of the handle. |
| static void MakeWeak(Address* location, void* parameter, |
| WeakCallbackInfo<void>::Callback weak_callback, |
| v8::WeakCallbackType type); |
| static void MakeWeak(Address** location_addr); |
| |
| static void AnnotateStrongRetainer(Address* location, const char* label); |
| |
| // Clear the weakness of a global handle. |
| static void* ClearWeakness(Address* location); |
| |
| // Tells whether global handle is weak. |
| static bool IsWeak(Address* location); |
| |
| // |
| // API for traced handles. |
| // |
| |
| static void MoveTracedGlobal(Address** from, Address** to); |
| static void DestroyTraced(Address* location); |
| static void SetFinalizationCallbackForTraced( |
| Address* location, void* parameter, |
| WeakCallbackInfo<void>::Callback callback); |
| |
| explicit GlobalHandles(Isolate* isolate); |
| ~GlobalHandles(); |
| |
| // Creates a new global handle that is alive until Destroy is called. |
| Handle<Object> Create(Object value); |
| Handle<Object> Create(Address value); |
| |
| template <typename T> |
| Handle<T> Create(T value) { |
| static_assert(std::is_base_of<Object, T>::value, "static type violation"); |
| // The compiler should only pick this method if T is not Object. |
| static_assert(!std::is_same<Object, T>::value, "compiler error"); |
| return Handle<T>::cast(Create(Object(value))); |
| } |
| |
| Handle<Object> CreateTraced(Object value, Address* slot); |
| Handle<Object> CreateTraced(Address value, Address* slot); |
| |
| void RecordStats(HeapStats* stats); |
| |
| size_t InvokeFirstPassWeakCallbacks(); |
| void InvokeSecondPassPhantomCallbacks(); |
| |
| // Process pending weak handles. |
| // Returns the number of freed nodes. |
| size_t PostGarbageCollectionProcessing( |
| GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags); |
| |
| void IterateStrongRoots(RootVisitor* v); |
| void IterateWeakRoots(RootVisitor* v); |
| void IterateAllRoots(RootVisitor* v); |
| void IterateAllYoungRoots(RootVisitor* v); |
| |
| // Iterates over all handles that have embedder-assigned class ID. |
| void IterateAllRootsWithClassIds(v8::PersistentHandleVisitor* v); |
| |
| // Iterates over all handles in the new space that have embedder-assigned |
| // class ID. |
| void IterateAllYoungRootsWithClassIds(v8::PersistentHandleVisitor* v); |
| |
| // Iterate over all handles in the new space that are weak, unmodified |
| // and have class IDs |
| void IterateYoungWeakRootsWithClassIds(v8::PersistentHandleVisitor* v); |
| |
| // Iterates over all traces handles represented by TracedGlobal. |
| void IterateTracedNodes( |
| v8::EmbedderHeapTracer::TracedGlobalHandleVisitor* visitor); |
| |
| // Marks handles with finalizers on the predicate |should_reset_handle| as |
| // pending. |
| void IterateWeakRootsIdentifyFinalizers( |
| WeakSlotCallbackWithHeap should_reset_handle); |
| // Uses the provided visitor |v| to mark handles with finalizers that are |
| // pending. |
| void IterateWeakRootsForFinalizers(RootVisitor* v); |
| // Marks handles that are phantom or have callbacks based on the predicate |
| // |should_reset_handle| as pending. |
| void IterateWeakRootsForPhantomHandles( |
| WeakSlotCallbackWithHeap should_reset_handle); |
| |
| // Note: The following *Young* methods are used for the Scavenger to |
| // identify and process handles in the young generation. The set of young |
| // handles is complete but the methods may encounter handles that are |
| // already in old space. |
| |
| // Iterates over strong and dependent handles. See the note above. |
| void IterateYoungStrongAndDependentRoots(RootVisitor* v); |
| |
| // Marks weak unmodified handles satisfying |is_dead| as pending. |
| void MarkYoungWeakUnmodifiedObjectsPending(WeakSlotCallbackWithHeap is_dead); |
| |
| // Iterates over weak independent or unmodified handles. |
| // See the note above. |
| void IterateYoungWeakUnmodifiedRootsForFinalizers(RootVisitor* v); |
| void IterateYoungWeakUnmodifiedRootsForPhantomHandles( |
| RootVisitor* v, WeakSlotCallbackWithHeap should_reset_handle); |
| |
| // Identify unmodified objects that are in weak state and marks them |
| // unmodified |
| void IdentifyWeakUnmodifiedObjects(WeakSlotCallback is_unmodified); |
| |
| Isolate* isolate() const { return isolate_; } |
| |
| // Number of global handles. |
| size_t handles_count() const { return handles_count_; } |
| |
| size_t GetAndResetGlobalHandleResetCount() { |
| size_t old = number_of_phantom_handle_resets_; |
| number_of_phantom_handle_resets_ = 0; |
| return old; |
| } |
| |
| #ifdef DEBUG |
| void PrintStats(); |
| void Print(); |
| #endif // DEBUG |
| |
| private: |
| // Internal node structures. |
| class Node; |
| template <class BlockType> |
| class NodeIterator; |
| template <class NodeType> |
| class NodeSpace; |
| class PendingPhantomCallback; |
| class TracedNode; |
| |
| bool InRecursiveGC(unsigned gc_processing_counter); |
| |
| void InvokeSecondPassPhantomCallbacksFromTask(); |
| void InvokeOrScheduleSecondPassPhantomCallbacks(bool synchronous_second_pass); |
| size_t PostScavengeProcessing(unsigned post_processing_count); |
| size_t PostMarkSweepProcessing(unsigned post_processing_count); |
| |
| template <typename T> |
| size_t InvokeFirstPassWeakCallbacks( |
| std::vector<std::pair<T*, PendingPhantomCallback>>* pending); |
| |
| template <typename T> |
| void UpdateAndCompactListOfYoungNode(std::vector<T*>* node_list); |
| void UpdateListOfYoungNodes(); |
| |
| void ApplyPersistentHandleVisitor(v8::PersistentHandleVisitor* visitor, |
| Node* node); |
| |
| Isolate* const isolate_; |
| |
| std::unique_ptr<NodeSpace<Node>> regular_nodes_; |
| // Contains all nodes holding young objects. Note: when the list |
| // is accessed, some of the objects may have been promoted already. |
| std::vector<Node*> young_nodes_; |
| |
| std::unique_ptr<NodeSpace<TracedNode>> traced_nodes_; |
| std::vector<TracedNode*> traced_young_nodes_; |
| |
| // Field always containing the number of handles to global objects. |
| size_t handles_count_ = 0; |
| size_t number_of_phantom_handle_resets_ = 0; |
| |
| std::vector<std::pair<Node*, PendingPhantomCallback>> |
| regular_pending_phantom_callbacks_; |
| std::vector<std::pair<TracedNode*, PendingPhantomCallback>> |
| traced_pending_phantom_callbacks_; |
| std::vector<PendingPhantomCallback> second_pass_callbacks_; |
| bool second_pass_callbacks_task_posted_ = false; |
| bool running_second_pass_callbacks_ = false; |
| |
| // Counter for recursive garbage collections during callback processing. |
| unsigned post_gc_processing_count_ = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(GlobalHandles); |
| }; |
| |
| class GlobalHandles::PendingPhantomCallback final { |
| public: |
| using Data = v8::WeakCallbackInfo<void>; |
| |
| enum InvocationType { kFirstPass, kSecondPass }; |
| |
| PendingPhantomCallback( |
| Data::Callback callback, void* parameter, |
| void* embedder_fields[v8::kEmbedderFieldsInWeakCallback]) |
| : callback_(callback), parameter_(parameter) { |
| for (int i = 0; i < v8::kEmbedderFieldsInWeakCallback; ++i) { |
| embedder_fields_[i] = embedder_fields[i]; |
| } |
| } |
| |
| void Invoke(Isolate* isolate, InvocationType type); |
| |
| Data::Callback callback() const { return callback_; } |
| |
| private: |
| Data::Callback callback_; |
| void* parameter_; |
| void* embedder_fields_[v8::kEmbedderFieldsInWeakCallback]; |
| }; |
| |
| class EternalHandles final { |
| public: |
| EternalHandles() = default; |
| ~EternalHandles(); |
| |
| // Create an EternalHandle, overwriting the index. |
| V8_EXPORT_PRIVATE void Create(Isolate* isolate, Object object, int* index); |
| |
| // Grab the handle for an existing EternalHandle. |
| inline Handle<Object> Get(int index) { |
| return Handle<Object>(GetLocation(index)); |
| } |
| |
| // Iterates over all handles. |
| void IterateAllRoots(RootVisitor* visitor); |
| // Iterates over all handles which might be in the young generation. |
| void IterateYoungRoots(RootVisitor* visitor); |
| // Rebuilds new space list. |
| void PostGarbageCollectionProcessing(); |
| |
| size_t handles_count() const { return size_; } |
| |
| private: |
| static const int kInvalidIndex = -1; |
| static const int kShift = 8; |
| static const int kSize = 1 << kShift; |
| static const int kMask = 0xff; |
| |
| // Gets the slot for an index. This returns an Address* rather than an |
| // ObjectSlot in order to avoid #including slots.h in this header file. |
| inline Address* GetLocation(int index) { |
| DCHECK(index >= 0 && index < size_); |
| return &blocks_[index >> kShift][index & kMask]; |
| } |
| |
| int size_ = 0; |
| std::vector<Address*> blocks_; |
| std::vector<int> young_node_indices_; |
| |
| DISALLOW_COPY_AND_ASSIGN(EternalHandles); |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_HANDLES_GLOBAL_HANDLES_H_ |