1// Copyright 2020 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/heap/cppgc-js/cpp-snapshot.h"
6
7#include <memory>
8
9#include "include/cppgc/internal/name-trait.h"
10#include "include/cppgc/trace-trait.h"
11#include "include/v8-cppgc.h"
12#include "include/v8-profiler.h"
13#include "src/api/api-inl.h"
14#include "src/base/logging.h"
15#include "src/execution/isolate.h"
16#include "src/heap/cppgc-js/cpp-heap.h"
17#include "src/heap/cppgc/heap-object-header.h"
18#include "src/heap/cppgc/heap-visitor.h"
19#include "src/heap/embedder-tracing.h"
20#include "src/heap/mark-compact.h"
21#include "src/objects/js-objects.h"
22#include "src/profiler/heap-profiler.h"
23
24namespace v8 {
25namespace internal {
26
27class CppGraphBuilderImpl;
28class StateStorage;
29class State;
30
31using cppgc::internal::GCInfo;
32using cppgc::internal::GlobalGCInfoTable;
33using cppgc::internal::HeapObjectHeader;
34
35// Node representing a C++ object on the heap.
36class EmbedderNode : public v8::EmbedderGraph::Node {
37 public:
38  EmbedderNode(cppgc::internal::HeapObjectName name, size_t size)
39      : name_(name), size_(size) {
40    USE(size_);
41  }
42  ~EmbedderNode() override = default;
43
44  const char* Name() final { return name_.value; }
45  size_t SizeInBytes() final { return name_.name_was_hidden ? 0 : size_; }
46
47  void SetWrapperNode(v8::EmbedderGraph::Node* wrapper_node) {
48    // An embedder node may only be merged with a single wrapper node, as
49    // consumers of the graph may merge a node and its wrapper node.
50    //
51    // TODO(chromium:1218404): Add a DCHECK() to avoid overriding an already
52    // set `wrapper_node_`. This can currently happen with global proxies that
53    // are rewired (and still kept alive) after reloading a page, see
54    // `AddEdge`. We accept overriding the wrapper node in such cases,
55    // leading to a random merged node and separated nodes for all other
56    // proxies.
57    wrapper_node_ = wrapper_node;
58  }
59  Node* WrapperNode() final { return wrapper_node_; }
60
61  void SetDetachedness(Detachedness detachedness) {
62    detachedness_ = detachedness;
63  }
64  Detachedness GetDetachedness() final { return detachedness_; }
65
66  // Edge names are passed to V8 but are required to be held alive from the
67  // embedder until the snapshot is compiled.
68  const char* InternalizeEdgeName(std::string edge_name) {
69    const size_t edge_name_len = edge_name.length();
70    named_edges_.emplace_back(std::make_unique<char[]>(edge_name_len + 1));
71    char* named_edge_str = named_edges_.back().get();
72    snprintf(named_edge_str, edge_name_len + 1, "%s", edge_name.c_str());
73    return named_edge_str;
74  }
75
76 private:
77  cppgc::internal::HeapObjectName name_;
78  size_t size_;
79  Node* wrapper_node_ = nullptr;
80  Detachedness detachedness_ = Detachedness::kUnknown;
81  std::vector<std::unique_ptr<char[]>> named_edges_;
82};
83
84// Node representing an artificial root group, e.g., set of Persistent handles.
85class EmbedderRootNode final : public EmbedderNode {
86 public:
87  explicit EmbedderRootNode(const char* name)
88      : EmbedderNode({name, false}, 0) {}
89  ~EmbedderRootNode() final = default;
90
91  bool IsRootNode() final { return true; }
92};
93
94// Canonical state representing real and artificial (e.g. root) objects.
95class StateBase {
96 public:
97  // Objects can either be hidden/visible, or depend on some other nodes while
98  // traversing the same SCC.
99  enum class Visibility {
100    kHidden,
101    kDependentVisibility,
102    kVisible,
103  };
104
105  StateBase(const void* key, size_t state_count, Visibility visibility,
106            EmbedderNode* node, bool visited)
107      : key_(key),
108        state_count_(state_count),
109        visibility_(visibility),
110        node_(node),
111        visited_(visited) {
112    DCHECK_NE(Visibility::kDependentVisibility, visibility);
113  }
114  virtual ~StateBase() = default;
115
116  // Visited objects have already been processed or are currently being
117  // processed, see also IsPending() below.
118  bool IsVisited() const { return visited_; }
119
120  // Pending objects are currently being processed as part of the same SCC.
121  bool IsPending() const { return pending_; }
122
123  bool IsVisibleNotDependent() {
124    auto v = GetVisibility();
125    CHECK_NE(Visibility::kDependentVisibility, v);
126    return v == Visibility::kVisible;
127  }
128
129  void set_node(EmbedderNode* node) {
130    CHECK_EQ(Visibility::kVisible, GetVisibility());
131    DCHECK_NULL(node_);
132    node_ = node;
133  }
134
135  EmbedderNode* get_node() {
136    CHECK_EQ(Visibility::kVisible, GetVisibility());
137    return node_;
138  }
139
140 protected:
141  const void* key_;
142  // State count keeps track of node processing order. It is used to create only
143  // dependencies on ancestors in the sub graph which ensures that there will be
144  // no cycles in dependencies.
145  const size_t state_count_;
146
147  Visibility visibility_;
148  StateBase* visibility_dependency_ = nullptr;
149  EmbedderNode* node_;
150  bool visited_;
151  bool pending_ = false;
152
153  Visibility GetVisibility() {
154    FollowDependencies();
155    return visibility_;
156  }
157
158  StateBase* FollowDependencies() {
159    if (visibility_ != Visibility::kDependentVisibility) {
160      CHECK_NULL(visibility_dependency_);
161      return this;
162    }
163    StateBase* current = this;
164    std::vector<StateBase*> dependencies;
165    while (current->visibility_dependency_ &&
166           current->visibility_dependency_ != current) {
167      DCHECK_EQ(Visibility::kDependentVisibility, current->visibility_);
168      dependencies.push_back(current);
169      current = current->visibility_dependency_;
170    }
171    auto new_visibility = Visibility::kDependentVisibility;
172    auto* new_visibility_dependency = current;
173    if (current->visibility_ == Visibility::kVisible) {
174      new_visibility = Visibility::kVisible;
175      new_visibility_dependency = nullptr;
176    } else if (!IsPending()) {
177      DCHECK(IsVisited());
178      // The object was not visible (above case). Having a dependency on itself
179      // or null means no visible object was found.
180      new_visibility = Visibility::kHidden;
181      new_visibility_dependency = nullptr;
182    }
183    current->visibility_ = new_visibility;
184    current->visibility_dependency_ = new_visibility_dependency;
185    for (auto* state : dependencies) {
186      state->visibility_ = new_visibility;
187      state->visibility_dependency_ = new_visibility_dependency;
188    }
189    return current;
190  }
191
192  friend class State;
193};
194
195class State final : public StateBase {
196 public:
197  State(const HeapObjectHeader& header, size_t state_count)
198      : StateBase(&header, state_count, Visibility::kHidden, nullptr, false) {}
199  ~State() final = default;
200
201  const HeapObjectHeader* header() const {
202    return static_cast<const HeapObjectHeader*>(key_);
203  }
204
205  void MarkVisited() { visited_ = true; }
206
207  void MarkPending() { pending_ = true; }
208  void UnmarkPending() { pending_ = false; }
209
210  void MarkVisible() {
211    visibility_ = Visibility::kVisible;
212    visibility_dependency_ = nullptr;
213  }
214
215  void MarkDependentVisibility(StateBase* dependency) {
216    // Follow and update dependencies as much as possible.
217    dependency = dependency->FollowDependencies();
218    DCHECK(dependency->IsVisited());
219    if (visibility_ == StateBase::Visibility::kVisible) {
220      // Already visible, no dependency needed.
221      DCHECK_NULL(visibility_dependency_);
222      return;
223    }
224    if (dependency->visibility_ == Visibility::kVisible) {
225      // Simple case: Dependency is visible.
226      visibility_ = Visibility::kVisible;
227      visibility_dependency_ = nullptr;
228      return;
229    }
230    if ((visibility_dependency_ &&
231         (visibility_dependency_->state_count_ > dependency->state_count_)) ||
232        (!visibility_dependency_ &&
233         (state_count_ > dependency->state_count_))) {
234      // Only update when new state_count_ < original state_count_. This
235      // ensures that we pick an ancestor as dependency and not a child which
236      // is guaranteed to converge to an answer.
237      //
238      // Dependency is now
239      // a) either pending with unknown visibility (same call chain), or
240      // b) not pending and has defined visibility.
241      //
242      // It's not possible to point to a state that is not pending but has
243      // dependent visibility because dependencies are updated to the top-most
244      // dependency at the beginning of method.
245      if (dependency->IsPending()) {
246        visibility_ = Visibility::kDependentVisibility;
247        visibility_dependency_ = dependency;
248      } else {
249        CHECK_NE(Visibility::kDependentVisibility, dependency->visibility_);
250        if (dependency->visibility_ == Visibility::kVisible) {
251          visibility_ = Visibility::kVisible;
252          visibility_dependency_ = nullptr;
253        }
254      }
255    }
256  }
257
258  void MarkAsWeakContainer() { is_weak_container_ = true; }
259  bool IsWeakContainer() const { return is_weak_container_; }
260
261  void AddEphemeronEdge(const HeapObjectHeader& value) {
262    // This ignores duplicate entries (in different containers) for the same
263    // Key->Value pairs. Only one edge will be emitted in this case.
264    ephemeron_edges_.insert(&value);
265  }
266
267  void AddEagerEphemeronEdge(const void* value, cppgc::TraceCallback callback) {
268    eager_ephemeron_edges_.insert({value, callback});
269  }
270
271  template <typename Callback>
272  void ForAllEphemeronEdges(Callback callback) {
273    for (const HeapObjectHeader* value : ephemeron_edges_) {
274      callback(*value);
275    }
276  }
277
278  template <typename Callback>
279  void ForAllEagerEphemeronEdges(Callback callback) {
280    for (const auto& pair : eager_ephemeron_edges_) {
281      callback(pair.first, pair.second);
282    }
283  }
284
285 private:
286  bool is_weak_container_ = false;
287  // Values that are held alive through ephemerons by this particular key.
288  std::unordered_set<const HeapObjectHeader*> ephemeron_edges_;
289  // Values that are eagerly traced and held alive through ephemerons by this
290  // particular key.
291  std::unordered_map<const void*, cppgc::TraceCallback> eager_ephemeron_edges_;
292};
293
294// Root states are similar to regular states with the difference that they are
295// always visible.
296class RootState final : public StateBase {
297 public:
298  RootState(EmbedderRootNode* node, size_t state_count)
299      // Root states are always visited, visible, and have a node attached.
300      : StateBase(node, state_count, Visibility::kVisible, node, true) {}
301  ~RootState() final = default;
302};
303
304// Abstraction for storing states. Storage allows for creation and lookup of
305// different state objects.
306class StateStorage final {
307 public:
308  bool StateExists(const void* key) const {
309    return states_.find(key) != states_.end();
310  }
311
312  StateBase& GetExistingState(const void* key) const {
313    CHECK(StateExists(key));
314    return *states_.at(key).get();
315  }
316
317  State& GetExistingState(const HeapObjectHeader& header) const {
318    return static_cast<State&>(GetExistingState(&header));
319  }
320
321  State& GetOrCreateState(const HeapObjectHeader& header) {
322    if (!StateExists(&header)) {
323      auto it = states_.insert(std::make_pair(
324          &header, std::make_unique<State>(header, ++state_count_)));
325      DCHECK(it.second);
326      USE(it);
327    }
328    return GetExistingState(header);
329  }
330
331  RootState& CreateRootState(EmbedderRootNode* root_node) {
332    CHECK(!StateExists(root_node));
333    auto it = states_.insert(std::make_pair(
334        root_node, std::make_unique<RootState>(root_node, ++state_count_)));
335    DCHECK(it.second);
336    USE(it);
337    return static_cast<RootState&>(*it.first->second.get());
338  }
339
340  template <typename Callback>
341  void ForAllVisibleStates(Callback callback) {
342    for (auto& state : states_) {
343      if (state.second->IsVisibleNotDependent()) {
344        callback(state.second.get());
345      }
346    }
347  }
348
349 private:
350  std::unordered_map<const void*, std::unique_ptr<StateBase>> states_;
351  size_t state_count_ = 0;
352};
353
354void* ExtractEmbedderDataBackref(Isolate* isolate,
355                                 v8::Local<v8::Value> v8_value) {
356  // See LocalEmbedderHeapTracer::VerboseWrapperTypeInfo for details on how
357  // wrapper objects are set up.
358  if (!v8_value->IsObject()) return nullptr;
359
360  Handle<Object> v8_object = Utils::OpenHandle(*v8_value);
361  if (!v8_object->IsJSObject() ||
362      !JSObject::cast(*v8_object).MayHaveEmbedderFields())
363    return nullptr;
364
365  JSObject js_object = JSObject::cast(*v8_object);
366  return LocalEmbedderHeapTracer::VerboseWrapperInfo(
367             isolate->heap()->local_embedder_heap_tracer()->ExtractWrapperInfo(
368                 isolate, js_object))
369      .instance();
370}
371
372// The following implements a snapshotting algorithm for C++ objects that also
373// filters strongly-connected components (SCCs) of only "hidden" objects that
374// are not (transitively) referencing any non-hidden objects.
375//
376// C++ objects come in two versions.
377// a. Named objects that have been assigned a name through NameProvider.
378// b. Unnamed objects, that are potentially hidden if the build configuration
379//    requires Oilpan to hide such names. Hidden objects have their name
380//    set to NameProvider::kHiddenName.
381//
382// The main challenge for the algorithm is to avoid blowing up the final object
383// graph with hidden nodes that do not carry information. For that reason, the
384// algorithm filters SCCs of only hidden objects, e.g.:
385//   ... -> (object) -> (object) -> (hidden) -> (hidden)
386// In this case the (hidden) objects are filtered from the graph. The trickiest
387// part is maintaining visibility state for objects referencing other objects
388// that are currently being processed.
389//
390// Main algorithm idea (two passes):
391// 1. First pass marks all non-hidden objects and those that transitively reach
392//    non-hidden objects as visible. Details:
393//    - Iterate over all objects.
394//    - If object is non-hidden mark it as visible and also mark parent as
395//      visible if needed.
396//    - If object is hidden, traverse children as DFS to find non-hidden
397//      objects. Post-order process the objects and mark those objects as
398//      visible that have child nodes that are visible themselves.
399//    - Maintain an epoch counter (StateStorage::state_count_) to allow
400//      deferring the visibility decision to other objects in the same SCC. This
401//      is similar to the "lowlink" value in Tarjan's algorithm for SCC.
402//    - After the first pass it is guaranteed that all deferred visibility
403//      decisions can be resolved.
404// 2. Second pass adds nodes and edges for all visible objects.
405//    - Upon first checking the visibility state of an object, all deferred
406//      visibility states are resolved.
407//
408// For practical reasons, the recursion is transformed into an iteration. We do
409// do not use plain Tarjan's algorithm to avoid another pass over all nodes to
410// create SCCs.
411class CppGraphBuilderImpl final {
412 public:
413  CppGraphBuilderImpl(CppHeap& cpp_heap, v8::EmbedderGraph& graph)
414      : cpp_heap_(cpp_heap), graph_(graph) {}
415
416  void Run();
417
418  void VisitForVisibility(State* parent, const HeapObjectHeader&);
419  void VisitForVisibility(State& parent, const TracedReferenceBase&);
420  void VisitEphemeronForVisibility(const HeapObjectHeader& key,
421                                   const HeapObjectHeader& value);
422  void VisitEphemeronWithNonGarbageCollectedValueForVisibility(
423      const HeapObjectHeader& key, const void* value,
424      cppgc::TraceDescriptor value_desc);
425  void VisitWeakContainerForVisibility(const HeapObjectHeader&);
426  void VisitRootForGraphBuilding(RootState&, const HeapObjectHeader&,
427                                 const cppgc::SourceLocation&);
428  void ProcessPendingObjects();
429
430  EmbedderRootNode* AddRootNode(const char* name) {
431    return static_cast<EmbedderRootNode*>(graph_.AddNode(
432        std::unique_ptr<v8::EmbedderGraph::Node>{new EmbedderRootNode(name)}));
433  }
434
435  EmbedderNode* AddNode(const HeapObjectHeader& header) {
436    return static_cast<EmbedderNode*>(
437        graph_.AddNode(std::unique_ptr<v8::EmbedderGraph::Node>{
438            new EmbedderNode(header.GetName(), header.AllocatedSize())}));
439  }
440
441  void AddEdge(State& parent, const HeapObjectHeader& header,
442               const std::string& edge_name) {
443    DCHECK(parent.IsVisibleNotDependent());
444    auto& current = states_.GetExistingState(header);
445    if (!current.IsVisibleNotDependent()) return;
446
447    // Both states are visible. Create nodes in case this is the first edge
448    // created for any of them.
449    if (!parent.get_node()) {
450      parent.set_node(AddNode(*parent.header()));
451    }
452    if (!current.get_node()) {
453      current.set_node(AddNode(header));
454    }
455
456    if (!edge_name.empty()) {
457      graph_.AddEdge(parent.get_node(), current.get_node(),
458                     parent.get_node()->InternalizeEdgeName(edge_name));
459    } else {
460      graph_.AddEdge(parent.get_node(), current.get_node());
461    }
462  }
463
464  void AddEdge(State& parent, const TracedReferenceBase& ref,
465               const std::string& edge_name) {
466    DCHECK(parent.IsVisibleNotDependent());
467    v8::Local<v8::Value> v8_value =
468        ref.Get(reinterpret_cast<v8::Isolate*>(cpp_heap_.isolate()));
469    if (!v8_value.IsEmpty()) {
470      if (!parent.get_node()) {
471        parent.set_node(AddNode(*parent.header()));
472      }
473      auto* v8_node = graph_.V8Node(v8_value);
474      if (!edge_name.empty()) {
475        graph_.AddEdge(parent.get_node(), v8_node,
476                       parent.get_node()->InternalizeEdgeName(edge_name));
477      } else {
478        graph_.AddEdge(parent.get_node(), v8_node);
479      }
480
481      // References that have a class id set may have their internal fields
482      // pointing back to the object. Set up a wrapper node for the graph so
483      // that the snapshot generator  can merge the nodes appropriately.
484      // Even with a set class id, do not set up a wrapper node when the edge
485      // has a specific name.
486      if (!ref.WrapperClassId() || !edge_name.empty()) return;
487
488      void* back_reference_object = ExtractEmbedderDataBackref(
489          reinterpret_cast<v8::internal::Isolate*>(cpp_heap_.isolate()),
490          v8_value);
491      if (back_reference_object) {
492        auto& back_header = HeapObjectHeader::FromObject(back_reference_object);
493        auto& back_state = states_.GetExistingState(back_header);
494
495        // Generally the back reference will point to `parent.header()`. In the
496        // case of global proxy set up the backreference will point to a
497        // different object, which may not have a node at t his point. Merge the
498        // nodes nevertheless as Window objects need to be able to query their
499        // detachedness state.
500        //
501        // TODO(chromium:1218404): See bug description on how to fix this
502        // inconsistency and only merge states when the backref points back
503        // to the same object.
504        if (!back_state.get_node()) {
505          back_state.set_node(AddNode(back_header));
506        }
507        back_state.get_node()->SetWrapperNode(v8_node);
508
509        auto* profiler =
510            reinterpret_cast<Isolate*>(cpp_heap_.isolate())->heap_profiler();
511        if (profiler->HasGetDetachednessCallback()) {
512          back_state.get_node()->SetDetachedness(
513              profiler->GetDetachedness(v8_value, ref.WrapperClassId()));
514        }
515      }
516    }
517  }
518
519  void AddRootEdge(RootState& root, State& child, std::string edge_name) {
520    DCHECK(root.IsVisibleNotDependent());
521    if (!child.IsVisibleNotDependent()) return;
522
523    // Root states always have a node set.
524    DCHECK_NOT_NULL(root.get_node());
525    if (!child.get_node()) {
526      child.set_node(AddNode(*child.header()));
527    }
528
529    if (!edge_name.empty()) {
530      graph_.AddEdge(root.get_node(), child.get_node(),
531                     root.get_node()->InternalizeEdgeName(edge_name));
532      return;
533    }
534    graph_.AddEdge(root.get_node(), child.get_node());
535  }
536
537 private:
538  class WorkstackItemBase;
539  class VisitationItem;
540  class VisitationDoneItem;
541
542  struct MergedNodeItem {
543    EmbedderGraph::Node* node_;
544    v8::Local<v8::Value> value_;
545    uint16_t wrapper_class_id_;
546  };
547
548  CppHeap& cpp_heap_;
549  v8::EmbedderGraph& graph_;
550  StateStorage states_;
551  std::vector<std::unique_ptr<WorkstackItemBase>> workstack_;
552};
553
554// Iterating live objects to mark them as visible if needed.
555class LiveObjectsForVisibilityIterator final
556    : public cppgc::internal::HeapVisitor<LiveObjectsForVisibilityIterator> {
557  friend class cppgc::internal::HeapVisitor<LiveObjectsForVisibilityIterator>;
558
559 public:
560  explicit LiveObjectsForVisibilityIterator(CppGraphBuilderImpl& graph_builder)
561      : graph_builder_(graph_builder) {}
562
563 private:
564  bool VisitHeapObjectHeader(HeapObjectHeader& header) {
565    if (header.IsFree()) return true;
566    graph_builder_.VisitForVisibility(nullptr, header);
567    graph_builder_.ProcessPendingObjects();
568    return true;
569  }
570
571  CppGraphBuilderImpl& graph_builder_;
572};
573
574class ParentScope final {
575 public:
576  explicit ParentScope(StateBase& parent) : parent_(parent) {}
577
578  RootState& ParentAsRootState() const {
579    return static_cast<RootState&>(parent_);
580  }
581  State& ParentAsRegularState() const { return static_cast<State&>(parent_); }
582
583 private:
584  StateBase& parent_;
585};
586
587// This visitor can be used stand-alone to handle fully weak and ephemeron
588// containers or as part of the VisibilityVisitor that recursively traverses
589// the object graph.
590class WeakVisitor : public JSVisitor {
591 public:
592  explicit WeakVisitor(CppGraphBuilderImpl& graph_builder)
593      : JSVisitor(cppgc::internal::VisitorFactory::CreateKey()),
594        graph_builder_(graph_builder) {}
595
596  void VisitWeakContainer(const void* object,
597                          cppgc::TraceDescriptor strong_desc,
598                          cppgc::TraceDescriptor weak_desc, cppgc::WeakCallback,
599                          const void*) final {
600    const auto& container_header =
601        HeapObjectHeader::FromObject(strong_desc.base_object_payload);
602
603    graph_builder_.VisitWeakContainerForVisibility(container_header);
604
605    if (!weak_desc.callback) {
606      // Weak container does not contribute to liveness.
607      return;
608    }
609    // Heap snapshot is always run after a GC so we know there are no dead
610    // entries in the container.
611    if (object) {
612      // The container will itself be traced strongly via the regular Visit()
613      // handling that iterates over all live objects. The visibility visitor
614      // will thus see (because of strongly treating the container):
615      // 1. the container itself;
616      // 2. for each {key} in container: container->key;
617      // 3. for each {key, value} in container: key->value;
618      //
619      // In case the visitor is used stand-alone, we trace through the container
620      // here to create the same state as we would when the container is traced
621      // separately.
622      container_header.Trace(this);
623    }
624  }
625  void VisitEphemeron(const void* key, const void* value,
626                      cppgc::TraceDescriptor value_desc) final {
627    // For ephemerons, the key retains the value.
628    // Key always must be a GarbageCollected object.
629    auto& key_header = HeapObjectHeader::FromObject(key);
630    if (!value_desc.base_object_payload) {
631      // Value does not represent an actual GarbageCollected object but rather
632      // should be traced eagerly.
633      graph_builder_.VisitEphemeronWithNonGarbageCollectedValueForVisibility(
634          key_header, value, value_desc);
635      return;
636    }
637    // Regular path where both key and value are GarbageCollected objects.
638    graph_builder_.VisitEphemeronForVisibility(
639        key_header, HeapObjectHeader::FromObject(value));
640  }
641
642 protected:
643  CppGraphBuilderImpl& graph_builder_;
644};
645
646class VisiblityVisitor final : public WeakVisitor {
647 public:
648  VisiblityVisitor(CppGraphBuilderImpl& graph_builder,
649                   const ParentScope& parent_scope)
650      : WeakVisitor(graph_builder), parent_scope_(parent_scope) {}
651
652  // C++ handling.
653  void Visit(const void*, cppgc::TraceDescriptor desc) final {
654    graph_builder_.VisitForVisibility(
655        &parent_scope_.ParentAsRegularState(),
656        HeapObjectHeader::FromObject(desc.base_object_payload));
657  }
658  void VisitRoot(const void*, cppgc::TraceDescriptor,
659                 const cppgc::SourceLocation&) final {}
660  void VisitWeakRoot(const void*, cppgc::TraceDescriptor, cppgc::WeakCallback,
661                     const void*, const cppgc::SourceLocation&) final {}
662
663  // JS handling.
664  void Visit(const TracedReferenceBase& ref) final {
665    graph_builder_.VisitForVisibility(parent_scope_.ParentAsRegularState(),
666                                      ref);
667  }
668
669 private:
670  const ParentScope& parent_scope_;
671};
672
673class GraphBuildingVisitor final : public JSVisitor {
674 public:
675  GraphBuildingVisitor(CppGraphBuilderImpl& graph_builder,
676                       const ParentScope& parent_scope)
677      : JSVisitor(cppgc::internal::VisitorFactory::CreateKey()),
678        graph_builder_(graph_builder),
679        parent_scope_(parent_scope) {}
680
681  // C++ handling.
682  void Visit(const void*, cppgc::TraceDescriptor desc) final {
683    graph_builder_.AddEdge(
684        parent_scope_.ParentAsRegularState(),
685        HeapObjectHeader::FromObject(desc.base_object_payload), edge_name_);
686  }
687  void VisitWeakContainer(const void* object,
688                          cppgc::TraceDescriptor strong_desc,
689                          cppgc::TraceDescriptor weak_desc, cppgc::WeakCallback,
690                          const void*) final {
691    // Add an edge from the object holding the weak container to the weak
692    // container itself.
693    graph_builder_.AddEdge(
694        parent_scope_.ParentAsRegularState(),
695        HeapObjectHeader::FromObject(strong_desc.base_object_payload),
696        edge_name_);
697  }
698  void VisitRoot(const void*, cppgc::TraceDescriptor desc,
699                 const cppgc::SourceLocation& loc) final {
700    graph_builder_.VisitRootForGraphBuilding(
701        parent_scope_.ParentAsRootState(),
702        HeapObjectHeader::FromObject(desc.base_object_payload), loc);
703  }
704  void VisitWeakRoot(const void*, cppgc::TraceDescriptor, cppgc::WeakCallback,
705                     const void*, const cppgc::SourceLocation&) final {}
706  // JS handling.
707  void Visit(const TracedReferenceBase& ref) final {
708    graph_builder_.AddEdge(parent_scope_.ParentAsRegularState(), ref,
709                           edge_name_);
710  }
711
712  void set_edge_name(std::string edge_name) {
713    edge_name_ = std::move(edge_name);
714  }
715
716 private:
717  CppGraphBuilderImpl& graph_builder_;
718  const ParentScope& parent_scope_;
719  std::string edge_name_;
720};
721
722// Base class for transforming recursion into iteration. Items are processed
723// in stack fashion.
724class CppGraphBuilderImpl::WorkstackItemBase {
725 public:
726  WorkstackItemBase(State* parent, State& current)
727      : parent_(parent), current_(current) {}
728
729  virtual ~WorkstackItemBase() = default;
730  virtual void Process(CppGraphBuilderImpl&) = 0;
731
732 protected:
733  State* parent_;
734  State& current_;
735};
736
737void CppGraphBuilderImpl::ProcessPendingObjects() {
738  while (!workstack_.empty()) {
739    std::unique_ptr<WorkstackItemBase> item = std::move(workstack_.back());
740    workstack_.pop_back();
741    item->Process(*this);
742  }
743}
744
745// Post-order processing of an object. It's guaranteed that all children have
746// been processed first.
747class CppGraphBuilderImpl::VisitationDoneItem final : public WorkstackItemBase {
748 public:
749  VisitationDoneItem(State* parent, State& current)
750      : WorkstackItemBase(parent, current) {}
751
752  void Process(CppGraphBuilderImpl& graph_builder) final {
753    CHECK(parent_);
754    parent_->MarkDependentVisibility(&current_);
755    current_.UnmarkPending();
756  }
757};
758
759class CppGraphBuilderImpl::VisitationItem final : public WorkstackItemBase {
760 public:
761  VisitationItem(State* parent, State& current)
762      : WorkstackItemBase(parent, current) {}
763
764  void Process(CppGraphBuilderImpl& graph_builder) final {
765    if (parent_) {
766      // Re-add the same object for post-order processing. This must happen
767      // lazily, as the parent's visibility depends on its children.
768      graph_builder.workstack_.push_back(std::unique_ptr<WorkstackItemBase>{
769          new VisitationDoneItem(parent_, current_)});
770    }
771    ParentScope parent_scope(current_);
772    VisiblityVisitor object_visitor(graph_builder, parent_scope);
773    current_.header()->Trace(&object_visitor);
774    if (!parent_) {
775      current_.UnmarkPending();
776    }
777  }
778};
779
780void CppGraphBuilderImpl::VisitForVisibility(State* parent,
781                                             const HeapObjectHeader& header) {
782  auto& current = states_.GetOrCreateState(header);
783
784  if (current.IsVisited()) {
785    // Avoid traversing into already visited subgraphs and just update the state
786    // based on a previous result.
787    if (parent) {
788      parent->MarkDependentVisibility(&current);
789    }
790    return;
791  }
792
793  current.MarkVisited();
794  if (header.GetName().name_was_hidden) {
795    current.MarkPending();
796    workstack_.push_back(std::unique_ptr<WorkstackItemBase>{
797        new VisitationItem(parent, current)});
798  } else {
799    // No need to mark/unmark pending as the node is immediately processed.
800    current.MarkVisible();
801    // In case the names are visible, the graph is not traversed in this phase.
802    // Explicitly trace one level to handle weak containers.
803    WeakVisitor weak_visitor(*this);
804    header.Trace(&weak_visitor);
805    if (parent) {
806      // Eagerly update a parent object as its visibility state is now fixed.
807      parent->MarkVisible();
808    }
809  }
810}
811
812void CppGraphBuilderImpl::
813    VisitEphemeronWithNonGarbageCollectedValueForVisibility(
814        const HeapObjectHeader& key, const void* value,
815        cppgc::TraceDescriptor value_desc) {
816  auto& key_state = states_.GetOrCreateState(key);
817  // Eagerly trace the value here, effectively marking key as visible and
818  // queuing processing for all reachable values.
819  ParentScope parent_scope(key_state);
820  VisiblityVisitor visitor(*this, parent_scope);
821  value_desc.callback(&visitor, value);
822  key_state.AddEagerEphemeronEdge(value, value_desc.callback);
823}
824
825void CppGraphBuilderImpl::VisitEphemeronForVisibility(
826    const HeapObjectHeader& key, const HeapObjectHeader& value) {
827  auto& key_state = states_.GetOrCreateState(key);
828  VisitForVisibility(&key_state, value);
829  key_state.AddEphemeronEdge(value);
830}
831
832void CppGraphBuilderImpl::VisitWeakContainerForVisibility(
833    const HeapObjectHeader& container_header) {
834  // Mark the container here as weak container to avoid creating any
835  // outgoing edges in the second phase.
836  states_.GetOrCreateState(container_header).MarkAsWeakContainer();
837}
838
839void CppGraphBuilderImpl::VisitForVisibility(State& parent,
840                                             const TracedReferenceBase& ref) {
841  v8::Local<v8::Value> v8_value =
842      ref.Get(reinterpret_cast<v8::Isolate*>(cpp_heap_.isolate()));
843  if (!v8_value.IsEmpty()) {
844    parent.MarkVisible();
845  }
846}
847
848void CppGraphBuilderImpl::VisitRootForGraphBuilding(
849    RootState& root, const HeapObjectHeader& header,
850    const cppgc::SourceLocation& loc) {
851  State& current = states_.GetExistingState(header);
852  if (!current.IsVisibleNotDependent()) return;
853
854  AddRootEdge(root, current, loc.ToString());
855}
856
857void CppGraphBuilderImpl::Run() {
858  // Sweeping from a previous GC might still be running, in which case not all
859  // pages have been returned to spaces yet.
860  cpp_heap_.sweeper().FinishIfRunning();
861  // First pass: Figure out which objects should be included in the graph -- see
862  // class-level comment on CppGraphBuilder.
863  LiveObjectsForVisibilityIterator visitor(*this);
864  visitor.Traverse(cpp_heap_.raw_heap());
865  // Second pass: Add graph nodes for objects that must be shown.
866  states_.ForAllVisibleStates([this](StateBase* state_base) {
867    // No roots have been created so far, so all StateBase objects are State.
868    State& state = *static_cast<State*>(state_base);
869
870    // Emit no edges for the contents of the weak containers. For both, fully
871    // weak and ephemeron containers, the contents should be retained from
872    // somewhere else.
873    if (state.IsWeakContainer()) return;
874
875    ParentScope parent_scope(state);
876    GraphBuildingVisitor object_visitor(*this, parent_scope);
877    state.header()->Trace(&object_visitor);
878    state.ForAllEphemeronEdges([this, &state](const HeapObjectHeader& value) {
879      AddEdge(state, value, "part of key -> value pair in ephemeron table");
880    });
881    object_visitor.set_edge_name(
882        "part of key -> value pair in ephemeron table");
883    state.ForAllEagerEphemeronEdges(
884        [&object_visitor](const void* value, cppgc::TraceCallback callback) {
885          callback(&object_visitor, value);
886        });
887  });
888  // Add roots.
889  {
890    ParentScope parent_scope(states_.CreateRootState(AddRootNode("C++ roots")));
891    GraphBuildingVisitor object_visitor(*this, parent_scope);
892    cpp_heap_.GetStrongPersistentRegion().Trace(&object_visitor);
893  }
894  {
895    ParentScope parent_scope(
896        states_.CreateRootState(AddRootNode("C++ cross-thread roots")));
897    GraphBuildingVisitor object_visitor(*this, parent_scope);
898    cppgc::internal::PersistentRegionLock guard;
899    cpp_heap_.GetStrongCrossThreadPersistentRegion().Trace(&object_visitor);
900  }
901}
902
903// static
904void CppGraphBuilder::Run(v8::Isolate* isolate, v8::EmbedderGraph* graph,
905                          void* data) {
906  CppHeap* cpp_heap = static_cast<CppHeap*>(data);
907  CHECK_NOT_NULL(cpp_heap);
908  CHECK_NOT_NULL(graph);
909  CppGraphBuilderImpl graph_builder(*cpp_heap, *graph);
910  graph_builder.Run();
911}
912
913}  // namespace internal
914}  // namespace v8
915