11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/heap/cppgc-js/cpp-snapshot.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include <memory>
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci#include "include/cppgc/internal/name-trait.h"
101cb0ef41Sopenharmony_ci#include "include/cppgc/trace-trait.h"
111cb0ef41Sopenharmony_ci#include "include/v8-cppgc.h"
121cb0ef41Sopenharmony_ci#include "include/v8-profiler.h"
131cb0ef41Sopenharmony_ci#include "src/api/api-inl.h"
141cb0ef41Sopenharmony_ci#include "src/base/logging.h"
151cb0ef41Sopenharmony_ci#include "src/execution/isolate.h"
161cb0ef41Sopenharmony_ci#include "src/heap/cppgc-js/cpp-heap.h"
171cb0ef41Sopenharmony_ci#include "src/heap/cppgc/heap-object-header.h"
181cb0ef41Sopenharmony_ci#include "src/heap/cppgc/heap-visitor.h"
191cb0ef41Sopenharmony_ci#include "src/heap/embedder-tracing.h"
201cb0ef41Sopenharmony_ci#include "src/heap/mark-compact.h"
211cb0ef41Sopenharmony_ci#include "src/objects/js-objects.h"
221cb0ef41Sopenharmony_ci#include "src/profiler/heap-profiler.h"
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_cinamespace v8 {
251cb0ef41Sopenharmony_cinamespace internal {
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_ciclass CppGraphBuilderImpl;
281cb0ef41Sopenharmony_ciclass StateStorage;
291cb0ef41Sopenharmony_ciclass State;
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ciusing cppgc::internal::GCInfo;
321cb0ef41Sopenharmony_ciusing cppgc::internal::GlobalGCInfoTable;
331cb0ef41Sopenharmony_ciusing cppgc::internal::HeapObjectHeader;
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci// Node representing a C++ object on the heap.
361cb0ef41Sopenharmony_ciclass EmbedderNode : public v8::EmbedderGraph::Node {
371cb0ef41Sopenharmony_ci public:
381cb0ef41Sopenharmony_ci  EmbedderNode(cppgc::internal::HeapObjectName name, size_t size)
391cb0ef41Sopenharmony_ci      : name_(name), size_(size) {
401cb0ef41Sopenharmony_ci    USE(size_);
411cb0ef41Sopenharmony_ci  }
421cb0ef41Sopenharmony_ci  ~EmbedderNode() override = default;
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci  const char* Name() final { return name_.value; }
451cb0ef41Sopenharmony_ci  size_t SizeInBytes() final { return name_.name_was_hidden ? 0 : size_; }
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci  void SetWrapperNode(v8::EmbedderGraph::Node* wrapper_node) {
481cb0ef41Sopenharmony_ci    // An embedder node may only be merged with a single wrapper node, as
491cb0ef41Sopenharmony_ci    // consumers of the graph may merge a node and its wrapper node.
501cb0ef41Sopenharmony_ci    //
511cb0ef41Sopenharmony_ci    // TODO(chromium:1218404): Add a DCHECK() to avoid overriding an already
521cb0ef41Sopenharmony_ci    // set `wrapper_node_`. This can currently happen with global proxies that
531cb0ef41Sopenharmony_ci    // are rewired (and still kept alive) after reloading a page, see
541cb0ef41Sopenharmony_ci    // `AddEdge`. We accept overriding the wrapper node in such cases,
551cb0ef41Sopenharmony_ci    // leading to a random merged node and separated nodes for all other
561cb0ef41Sopenharmony_ci    // proxies.
571cb0ef41Sopenharmony_ci    wrapper_node_ = wrapper_node;
581cb0ef41Sopenharmony_ci  }
591cb0ef41Sopenharmony_ci  Node* WrapperNode() final { return wrapper_node_; }
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci  void SetDetachedness(Detachedness detachedness) {
621cb0ef41Sopenharmony_ci    detachedness_ = detachedness;
631cb0ef41Sopenharmony_ci  }
641cb0ef41Sopenharmony_ci  Detachedness GetDetachedness() final { return detachedness_; }
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci  // Edge names are passed to V8 but are required to be held alive from the
671cb0ef41Sopenharmony_ci  // embedder until the snapshot is compiled.
681cb0ef41Sopenharmony_ci  const char* InternalizeEdgeName(std::string edge_name) {
691cb0ef41Sopenharmony_ci    const size_t edge_name_len = edge_name.length();
701cb0ef41Sopenharmony_ci    named_edges_.emplace_back(std::make_unique<char[]>(edge_name_len + 1));
711cb0ef41Sopenharmony_ci    char* named_edge_str = named_edges_.back().get();
721cb0ef41Sopenharmony_ci    snprintf(named_edge_str, edge_name_len + 1, "%s", edge_name.c_str());
731cb0ef41Sopenharmony_ci    return named_edge_str;
741cb0ef41Sopenharmony_ci  }
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_ci private:
771cb0ef41Sopenharmony_ci  cppgc::internal::HeapObjectName name_;
781cb0ef41Sopenharmony_ci  size_t size_;
791cb0ef41Sopenharmony_ci  Node* wrapper_node_ = nullptr;
801cb0ef41Sopenharmony_ci  Detachedness detachedness_ = Detachedness::kUnknown;
811cb0ef41Sopenharmony_ci  std::vector<std::unique_ptr<char[]>> named_edges_;
821cb0ef41Sopenharmony_ci};
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci// Node representing an artificial root group, e.g., set of Persistent handles.
851cb0ef41Sopenharmony_ciclass EmbedderRootNode final : public EmbedderNode {
861cb0ef41Sopenharmony_ci public:
871cb0ef41Sopenharmony_ci  explicit EmbedderRootNode(const char* name)
881cb0ef41Sopenharmony_ci      : EmbedderNode({name, false}, 0) {}
891cb0ef41Sopenharmony_ci  ~EmbedderRootNode() final = default;
901cb0ef41Sopenharmony_ci
911cb0ef41Sopenharmony_ci  bool IsRootNode() final { return true; }
921cb0ef41Sopenharmony_ci};
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci// Canonical state representing real and artificial (e.g. root) objects.
951cb0ef41Sopenharmony_ciclass StateBase {
961cb0ef41Sopenharmony_ci public:
971cb0ef41Sopenharmony_ci  // Objects can either be hidden/visible, or depend on some other nodes while
981cb0ef41Sopenharmony_ci  // traversing the same SCC.
991cb0ef41Sopenharmony_ci  enum class Visibility {
1001cb0ef41Sopenharmony_ci    kHidden,
1011cb0ef41Sopenharmony_ci    kDependentVisibility,
1021cb0ef41Sopenharmony_ci    kVisible,
1031cb0ef41Sopenharmony_ci  };
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ci  StateBase(const void* key, size_t state_count, Visibility visibility,
1061cb0ef41Sopenharmony_ci            EmbedderNode* node, bool visited)
1071cb0ef41Sopenharmony_ci      : key_(key),
1081cb0ef41Sopenharmony_ci        state_count_(state_count),
1091cb0ef41Sopenharmony_ci        visibility_(visibility),
1101cb0ef41Sopenharmony_ci        node_(node),
1111cb0ef41Sopenharmony_ci        visited_(visited) {
1121cb0ef41Sopenharmony_ci    DCHECK_NE(Visibility::kDependentVisibility, visibility);
1131cb0ef41Sopenharmony_ci  }
1141cb0ef41Sopenharmony_ci  virtual ~StateBase() = default;
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci  // Visited objects have already been processed or are currently being
1171cb0ef41Sopenharmony_ci  // processed, see also IsPending() below.
1181cb0ef41Sopenharmony_ci  bool IsVisited() const { return visited_; }
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci  // Pending objects are currently being processed as part of the same SCC.
1211cb0ef41Sopenharmony_ci  bool IsPending() const { return pending_; }
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci  bool IsVisibleNotDependent() {
1241cb0ef41Sopenharmony_ci    auto v = GetVisibility();
1251cb0ef41Sopenharmony_ci    CHECK_NE(Visibility::kDependentVisibility, v);
1261cb0ef41Sopenharmony_ci    return v == Visibility::kVisible;
1271cb0ef41Sopenharmony_ci  }
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci  void set_node(EmbedderNode* node) {
1301cb0ef41Sopenharmony_ci    CHECK_EQ(Visibility::kVisible, GetVisibility());
1311cb0ef41Sopenharmony_ci    DCHECK_NULL(node_);
1321cb0ef41Sopenharmony_ci    node_ = node;
1331cb0ef41Sopenharmony_ci  }
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci  EmbedderNode* get_node() {
1361cb0ef41Sopenharmony_ci    CHECK_EQ(Visibility::kVisible, GetVisibility());
1371cb0ef41Sopenharmony_ci    return node_;
1381cb0ef41Sopenharmony_ci  }
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci protected:
1411cb0ef41Sopenharmony_ci  const void* key_;
1421cb0ef41Sopenharmony_ci  // State count keeps track of node processing order. It is used to create only
1431cb0ef41Sopenharmony_ci  // dependencies on ancestors in the sub graph which ensures that there will be
1441cb0ef41Sopenharmony_ci  // no cycles in dependencies.
1451cb0ef41Sopenharmony_ci  const size_t state_count_;
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci  Visibility visibility_;
1481cb0ef41Sopenharmony_ci  StateBase* visibility_dependency_ = nullptr;
1491cb0ef41Sopenharmony_ci  EmbedderNode* node_;
1501cb0ef41Sopenharmony_ci  bool visited_;
1511cb0ef41Sopenharmony_ci  bool pending_ = false;
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci  Visibility GetVisibility() {
1541cb0ef41Sopenharmony_ci    FollowDependencies();
1551cb0ef41Sopenharmony_ci    return visibility_;
1561cb0ef41Sopenharmony_ci  }
1571cb0ef41Sopenharmony_ci
1581cb0ef41Sopenharmony_ci  StateBase* FollowDependencies() {
1591cb0ef41Sopenharmony_ci    if (visibility_ != Visibility::kDependentVisibility) {
1601cb0ef41Sopenharmony_ci      CHECK_NULL(visibility_dependency_);
1611cb0ef41Sopenharmony_ci      return this;
1621cb0ef41Sopenharmony_ci    }
1631cb0ef41Sopenharmony_ci    StateBase* current = this;
1641cb0ef41Sopenharmony_ci    std::vector<StateBase*> dependencies;
1651cb0ef41Sopenharmony_ci    while (current->visibility_dependency_ &&
1661cb0ef41Sopenharmony_ci           current->visibility_dependency_ != current) {
1671cb0ef41Sopenharmony_ci      DCHECK_EQ(Visibility::kDependentVisibility, current->visibility_);
1681cb0ef41Sopenharmony_ci      dependencies.push_back(current);
1691cb0ef41Sopenharmony_ci      current = current->visibility_dependency_;
1701cb0ef41Sopenharmony_ci    }
1711cb0ef41Sopenharmony_ci    auto new_visibility = Visibility::kDependentVisibility;
1721cb0ef41Sopenharmony_ci    auto* new_visibility_dependency = current;
1731cb0ef41Sopenharmony_ci    if (current->visibility_ == Visibility::kVisible) {
1741cb0ef41Sopenharmony_ci      new_visibility = Visibility::kVisible;
1751cb0ef41Sopenharmony_ci      new_visibility_dependency = nullptr;
1761cb0ef41Sopenharmony_ci    } else if (!IsPending()) {
1771cb0ef41Sopenharmony_ci      DCHECK(IsVisited());
1781cb0ef41Sopenharmony_ci      // The object was not visible (above case). Having a dependency on itself
1791cb0ef41Sopenharmony_ci      // or null means no visible object was found.
1801cb0ef41Sopenharmony_ci      new_visibility = Visibility::kHidden;
1811cb0ef41Sopenharmony_ci      new_visibility_dependency = nullptr;
1821cb0ef41Sopenharmony_ci    }
1831cb0ef41Sopenharmony_ci    current->visibility_ = new_visibility;
1841cb0ef41Sopenharmony_ci    current->visibility_dependency_ = new_visibility_dependency;
1851cb0ef41Sopenharmony_ci    for (auto* state : dependencies) {
1861cb0ef41Sopenharmony_ci      state->visibility_ = new_visibility;
1871cb0ef41Sopenharmony_ci      state->visibility_dependency_ = new_visibility_dependency;
1881cb0ef41Sopenharmony_ci    }
1891cb0ef41Sopenharmony_ci    return current;
1901cb0ef41Sopenharmony_ci  }
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci  friend class State;
1931cb0ef41Sopenharmony_ci};
1941cb0ef41Sopenharmony_ci
1951cb0ef41Sopenharmony_ciclass State final : public StateBase {
1961cb0ef41Sopenharmony_ci public:
1971cb0ef41Sopenharmony_ci  State(const HeapObjectHeader& header, size_t state_count)
1981cb0ef41Sopenharmony_ci      : StateBase(&header, state_count, Visibility::kHidden, nullptr, false) {}
1991cb0ef41Sopenharmony_ci  ~State() final = default;
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_ci  const HeapObjectHeader* header() const {
2021cb0ef41Sopenharmony_ci    return static_cast<const HeapObjectHeader*>(key_);
2031cb0ef41Sopenharmony_ci  }
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci  void MarkVisited() { visited_ = true; }
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci  void MarkPending() { pending_ = true; }
2081cb0ef41Sopenharmony_ci  void UnmarkPending() { pending_ = false; }
2091cb0ef41Sopenharmony_ci
2101cb0ef41Sopenharmony_ci  void MarkVisible() {
2111cb0ef41Sopenharmony_ci    visibility_ = Visibility::kVisible;
2121cb0ef41Sopenharmony_ci    visibility_dependency_ = nullptr;
2131cb0ef41Sopenharmony_ci  }
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_ci  void MarkDependentVisibility(StateBase* dependency) {
2161cb0ef41Sopenharmony_ci    // Follow and update dependencies as much as possible.
2171cb0ef41Sopenharmony_ci    dependency = dependency->FollowDependencies();
2181cb0ef41Sopenharmony_ci    DCHECK(dependency->IsVisited());
2191cb0ef41Sopenharmony_ci    if (visibility_ == StateBase::Visibility::kVisible) {
2201cb0ef41Sopenharmony_ci      // Already visible, no dependency needed.
2211cb0ef41Sopenharmony_ci      DCHECK_NULL(visibility_dependency_);
2221cb0ef41Sopenharmony_ci      return;
2231cb0ef41Sopenharmony_ci    }
2241cb0ef41Sopenharmony_ci    if (dependency->visibility_ == Visibility::kVisible) {
2251cb0ef41Sopenharmony_ci      // Simple case: Dependency is visible.
2261cb0ef41Sopenharmony_ci      visibility_ = Visibility::kVisible;
2271cb0ef41Sopenharmony_ci      visibility_dependency_ = nullptr;
2281cb0ef41Sopenharmony_ci      return;
2291cb0ef41Sopenharmony_ci    }
2301cb0ef41Sopenharmony_ci    if ((visibility_dependency_ &&
2311cb0ef41Sopenharmony_ci         (visibility_dependency_->state_count_ > dependency->state_count_)) ||
2321cb0ef41Sopenharmony_ci        (!visibility_dependency_ &&
2331cb0ef41Sopenharmony_ci         (state_count_ > dependency->state_count_))) {
2341cb0ef41Sopenharmony_ci      // Only update when new state_count_ < original state_count_. This
2351cb0ef41Sopenharmony_ci      // ensures that we pick an ancestor as dependency and not a child which
2361cb0ef41Sopenharmony_ci      // is guaranteed to converge to an answer.
2371cb0ef41Sopenharmony_ci      //
2381cb0ef41Sopenharmony_ci      // Dependency is now
2391cb0ef41Sopenharmony_ci      // a) either pending with unknown visibility (same call chain), or
2401cb0ef41Sopenharmony_ci      // b) not pending and has defined visibility.
2411cb0ef41Sopenharmony_ci      //
2421cb0ef41Sopenharmony_ci      // It's not possible to point to a state that is not pending but has
2431cb0ef41Sopenharmony_ci      // dependent visibility because dependencies are updated to the top-most
2441cb0ef41Sopenharmony_ci      // dependency at the beginning of method.
2451cb0ef41Sopenharmony_ci      if (dependency->IsPending()) {
2461cb0ef41Sopenharmony_ci        visibility_ = Visibility::kDependentVisibility;
2471cb0ef41Sopenharmony_ci        visibility_dependency_ = dependency;
2481cb0ef41Sopenharmony_ci      } else {
2491cb0ef41Sopenharmony_ci        CHECK_NE(Visibility::kDependentVisibility, dependency->visibility_);
2501cb0ef41Sopenharmony_ci        if (dependency->visibility_ == Visibility::kVisible) {
2511cb0ef41Sopenharmony_ci          visibility_ = Visibility::kVisible;
2521cb0ef41Sopenharmony_ci          visibility_dependency_ = nullptr;
2531cb0ef41Sopenharmony_ci        }
2541cb0ef41Sopenharmony_ci      }
2551cb0ef41Sopenharmony_ci    }
2561cb0ef41Sopenharmony_ci  }
2571cb0ef41Sopenharmony_ci
2581cb0ef41Sopenharmony_ci  void MarkAsWeakContainer() { is_weak_container_ = true; }
2591cb0ef41Sopenharmony_ci  bool IsWeakContainer() const { return is_weak_container_; }
2601cb0ef41Sopenharmony_ci
2611cb0ef41Sopenharmony_ci  void AddEphemeronEdge(const HeapObjectHeader& value) {
2621cb0ef41Sopenharmony_ci    // This ignores duplicate entries (in different containers) for the same
2631cb0ef41Sopenharmony_ci    // Key->Value pairs. Only one edge will be emitted in this case.
2641cb0ef41Sopenharmony_ci    ephemeron_edges_.insert(&value);
2651cb0ef41Sopenharmony_ci  }
2661cb0ef41Sopenharmony_ci
2671cb0ef41Sopenharmony_ci  void AddEagerEphemeronEdge(const void* value, cppgc::TraceCallback callback) {
2681cb0ef41Sopenharmony_ci    eager_ephemeron_edges_.insert({value, callback});
2691cb0ef41Sopenharmony_ci  }
2701cb0ef41Sopenharmony_ci
2711cb0ef41Sopenharmony_ci  template <typename Callback>
2721cb0ef41Sopenharmony_ci  void ForAllEphemeronEdges(Callback callback) {
2731cb0ef41Sopenharmony_ci    for (const HeapObjectHeader* value : ephemeron_edges_) {
2741cb0ef41Sopenharmony_ci      callback(*value);
2751cb0ef41Sopenharmony_ci    }
2761cb0ef41Sopenharmony_ci  }
2771cb0ef41Sopenharmony_ci
2781cb0ef41Sopenharmony_ci  template <typename Callback>
2791cb0ef41Sopenharmony_ci  void ForAllEagerEphemeronEdges(Callback callback) {
2801cb0ef41Sopenharmony_ci    for (const auto& pair : eager_ephemeron_edges_) {
2811cb0ef41Sopenharmony_ci      callback(pair.first, pair.second);
2821cb0ef41Sopenharmony_ci    }
2831cb0ef41Sopenharmony_ci  }
2841cb0ef41Sopenharmony_ci
2851cb0ef41Sopenharmony_ci private:
2861cb0ef41Sopenharmony_ci  bool is_weak_container_ = false;
2871cb0ef41Sopenharmony_ci  // Values that are held alive through ephemerons by this particular key.
2881cb0ef41Sopenharmony_ci  std::unordered_set<const HeapObjectHeader*> ephemeron_edges_;
2891cb0ef41Sopenharmony_ci  // Values that are eagerly traced and held alive through ephemerons by this
2901cb0ef41Sopenharmony_ci  // particular key.
2911cb0ef41Sopenharmony_ci  std::unordered_map<const void*, cppgc::TraceCallback> eager_ephemeron_edges_;
2921cb0ef41Sopenharmony_ci};
2931cb0ef41Sopenharmony_ci
2941cb0ef41Sopenharmony_ci// Root states are similar to regular states with the difference that they are
2951cb0ef41Sopenharmony_ci// always visible.
2961cb0ef41Sopenharmony_ciclass RootState final : public StateBase {
2971cb0ef41Sopenharmony_ci public:
2981cb0ef41Sopenharmony_ci  RootState(EmbedderRootNode* node, size_t state_count)
2991cb0ef41Sopenharmony_ci      // Root states are always visited, visible, and have a node attached.
3001cb0ef41Sopenharmony_ci      : StateBase(node, state_count, Visibility::kVisible, node, true) {}
3011cb0ef41Sopenharmony_ci  ~RootState() final = default;
3021cb0ef41Sopenharmony_ci};
3031cb0ef41Sopenharmony_ci
3041cb0ef41Sopenharmony_ci// Abstraction for storing states. Storage allows for creation and lookup of
3051cb0ef41Sopenharmony_ci// different state objects.
3061cb0ef41Sopenharmony_ciclass StateStorage final {
3071cb0ef41Sopenharmony_ci public:
3081cb0ef41Sopenharmony_ci  bool StateExists(const void* key) const {
3091cb0ef41Sopenharmony_ci    return states_.find(key) != states_.end();
3101cb0ef41Sopenharmony_ci  }
3111cb0ef41Sopenharmony_ci
3121cb0ef41Sopenharmony_ci  StateBase& GetExistingState(const void* key) const {
3131cb0ef41Sopenharmony_ci    CHECK(StateExists(key));
3141cb0ef41Sopenharmony_ci    return *states_.at(key).get();
3151cb0ef41Sopenharmony_ci  }
3161cb0ef41Sopenharmony_ci
3171cb0ef41Sopenharmony_ci  State& GetExistingState(const HeapObjectHeader& header) const {
3181cb0ef41Sopenharmony_ci    return static_cast<State&>(GetExistingState(&header));
3191cb0ef41Sopenharmony_ci  }
3201cb0ef41Sopenharmony_ci
3211cb0ef41Sopenharmony_ci  State& GetOrCreateState(const HeapObjectHeader& header) {
3221cb0ef41Sopenharmony_ci    if (!StateExists(&header)) {
3231cb0ef41Sopenharmony_ci      auto it = states_.insert(std::make_pair(
3241cb0ef41Sopenharmony_ci          &header, std::make_unique<State>(header, ++state_count_)));
3251cb0ef41Sopenharmony_ci      DCHECK(it.second);
3261cb0ef41Sopenharmony_ci      USE(it);
3271cb0ef41Sopenharmony_ci    }
3281cb0ef41Sopenharmony_ci    return GetExistingState(header);
3291cb0ef41Sopenharmony_ci  }
3301cb0ef41Sopenharmony_ci
3311cb0ef41Sopenharmony_ci  RootState& CreateRootState(EmbedderRootNode* root_node) {
3321cb0ef41Sopenharmony_ci    CHECK(!StateExists(root_node));
3331cb0ef41Sopenharmony_ci    auto it = states_.insert(std::make_pair(
3341cb0ef41Sopenharmony_ci        root_node, std::make_unique<RootState>(root_node, ++state_count_)));
3351cb0ef41Sopenharmony_ci    DCHECK(it.second);
3361cb0ef41Sopenharmony_ci    USE(it);
3371cb0ef41Sopenharmony_ci    return static_cast<RootState&>(*it.first->second.get());
3381cb0ef41Sopenharmony_ci  }
3391cb0ef41Sopenharmony_ci
3401cb0ef41Sopenharmony_ci  template <typename Callback>
3411cb0ef41Sopenharmony_ci  void ForAllVisibleStates(Callback callback) {
3421cb0ef41Sopenharmony_ci    for (auto& state : states_) {
3431cb0ef41Sopenharmony_ci      if (state.second->IsVisibleNotDependent()) {
3441cb0ef41Sopenharmony_ci        callback(state.second.get());
3451cb0ef41Sopenharmony_ci      }
3461cb0ef41Sopenharmony_ci    }
3471cb0ef41Sopenharmony_ci  }
3481cb0ef41Sopenharmony_ci
3491cb0ef41Sopenharmony_ci private:
3501cb0ef41Sopenharmony_ci  std::unordered_map<const void*, std::unique_ptr<StateBase>> states_;
3511cb0ef41Sopenharmony_ci  size_t state_count_ = 0;
3521cb0ef41Sopenharmony_ci};
3531cb0ef41Sopenharmony_ci
3541cb0ef41Sopenharmony_civoid* ExtractEmbedderDataBackref(Isolate* isolate,
3551cb0ef41Sopenharmony_ci                                 v8::Local<v8::Value> v8_value) {
3561cb0ef41Sopenharmony_ci  // See LocalEmbedderHeapTracer::VerboseWrapperTypeInfo for details on how
3571cb0ef41Sopenharmony_ci  // wrapper objects are set up.
3581cb0ef41Sopenharmony_ci  if (!v8_value->IsObject()) return nullptr;
3591cb0ef41Sopenharmony_ci
3601cb0ef41Sopenharmony_ci  Handle<Object> v8_object = Utils::OpenHandle(*v8_value);
3611cb0ef41Sopenharmony_ci  if (!v8_object->IsJSObject() ||
3621cb0ef41Sopenharmony_ci      !JSObject::cast(*v8_object).MayHaveEmbedderFields())
3631cb0ef41Sopenharmony_ci    return nullptr;
3641cb0ef41Sopenharmony_ci
3651cb0ef41Sopenharmony_ci  JSObject js_object = JSObject::cast(*v8_object);
3661cb0ef41Sopenharmony_ci  return LocalEmbedderHeapTracer::VerboseWrapperInfo(
3671cb0ef41Sopenharmony_ci             isolate->heap()->local_embedder_heap_tracer()->ExtractWrapperInfo(
3681cb0ef41Sopenharmony_ci                 isolate, js_object))
3691cb0ef41Sopenharmony_ci      .instance();
3701cb0ef41Sopenharmony_ci}
3711cb0ef41Sopenharmony_ci
3721cb0ef41Sopenharmony_ci// The following implements a snapshotting algorithm for C++ objects that also
3731cb0ef41Sopenharmony_ci// filters strongly-connected components (SCCs) of only "hidden" objects that
3741cb0ef41Sopenharmony_ci// are not (transitively) referencing any non-hidden objects.
3751cb0ef41Sopenharmony_ci//
3761cb0ef41Sopenharmony_ci// C++ objects come in two versions.
3771cb0ef41Sopenharmony_ci// a. Named objects that have been assigned a name through NameProvider.
3781cb0ef41Sopenharmony_ci// b. Unnamed objects, that are potentially hidden if the build configuration
3791cb0ef41Sopenharmony_ci//    requires Oilpan to hide such names. Hidden objects have their name
3801cb0ef41Sopenharmony_ci//    set to NameProvider::kHiddenName.
3811cb0ef41Sopenharmony_ci//
3821cb0ef41Sopenharmony_ci// The main challenge for the algorithm is to avoid blowing up the final object
3831cb0ef41Sopenharmony_ci// graph with hidden nodes that do not carry information. For that reason, the
3841cb0ef41Sopenharmony_ci// algorithm filters SCCs of only hidden objects, e.g.:
3851cb0ef41Sopenharmony_ci//   ... -> (object) -> (object) -> (hidden) -> (hidden)
3861cb0ef41Sopenharmony_ci// In this case the (hidden) objects are filtered from the graph. The trickiest
3871cb0ef41Sopenharmony_ci// part is maintaining visibility state for objects referencing other objects
3881cb0ef41Sopenharmony_ci// that are currently being processed.
3891cb0ef41Sopenharmony_ci//
3901cb0ef41Sopenharmony_ci// Main algorithm idea (two passes):
3911cb0ef41Sopenharmony_ci// 1. First pass marks all non-hidden objects and those that transitively reach
3921cb0ef41Sopenharmony_ci//    non-hidden objects as visible. Details:
3931cb0ef41Sopenharmony_ci//    - Iterate over all objects.
3941cb0ef41Sopenharmony_ci//    - If object is non-hidden mark it as visible and also mark parent as
3951cb0ef41Sopenharmony_ci//      visible if needed.
3961cb0ef41Sopenharmony_ci//    - If object is hidden, traverse children as DFS to find non-hidden
3971cb0ef41Sopenharmony_ci//      objects. Post-order process the objects and mark those objects as
3981cb0ef41Sopenharmony_ci//      visible that have child nodes that are visible themselves.
3991cb0ef41Sopenharmony_ci//    - Maintain an epoch counter (StateStorage::state_count_) to allow
4001cb0ef41Sopenharmony_ci//      deferring the visibility decision to other objects in the same SCC. This
4011cb0ef41Sopenharmony_ci//      is similar to the "lowlink" value in Tarjan's algorithm for SCC.
4021cb0ef41Sopenharmony_ci//    - After the first pass it is guaranteed that all deferred visibility
4031cb0ef41Sopenharmony_ci//      decisions can be resolved.
4041cb0ef41Sopenharmony_ci// 2. Second pass adds nodes and edges for all visible objects.
4051cb0ef41Sopenharmony_ci//    - Upon first checking the visibility state of an object, all deferred
4061cb0ef41Sopenharmony_ci//      visibility states are resolved.
4071cb0ef41Sopenharmony_ci//
4081cb0ef41Sopenharmony_ci// For practical reasons, the recursion is transformed into an iteration. We do
4091cb0ef41Sopenharmony_ci// do not use plain Tarjan's algorithm to avoid another pass over all nodes to
4101cb0ef41Sopenharmony_ci// create SCCs.
4111cb0ef41Sopenharmony_ciclass CppGraphBuilderImpl final {
4121cb0ef41Sopenharmony_ci public:
4131cb0ef41Sopenharmony_ci  CppGraphBuilderImpl(CppHeap& cpp_heap, v8::EmbedderGraph& graph)
4141cb0ef41Sopenharmony_ci      : cpp_heap_(cpp_heap), graph_(graph) {}
4151cb0ef41Sopenharmony_ci
4161cb0ef41Sopenharmony_ci  void Run();
4171cb0ef41Sopenharmony_ci
4181cb0ef41Sopenharmony_ci  void VisitForVisibility(State* parent, const HeapObjectHeader&);
4191cb0ef41Sopenharmony_ci  void VisitForVisibility(State& parent, const TracedReferenceBase&);
4201cb0ef41Sopenharmony_ci  void VisitEphemeronForVisibility(const HeapObjectHeader& key,
4211cb0ef41Sopenharmony_ci                                   const HeapObjectHeader& value);
4221cb0ef41Sopenharmony_ci  void VisitEphemeronWithNonGarbageCollectedValueForVisibility(
4231cb0ef41Sopenharmony_ci      const HeapObjectHeader& key, const void* value,
4241cb0ef41Sopenharmony_ci      cppgc::TraceDescriptor value_desc);
4251cb0ef41Sopenharmony_ci  void VisitWeakContainerForVisibility(const HeapObjectHeader&);
4261cb0ef41Sopenharmony_ci  void VisitRootForGraphBuilding(RootState&, const HeapObjectHeader&,
4271cb0ef41Sopenharmony_ci                                 const cppgc::SourceLocation&);
4281cb0ef41Sopenharmony_ci  void ProcessPendingObjects();
4291cb0ef41Sopenharmony_ci
4301cb0ef41Sopenharmony_ci  EmbedderRootNode* AddRootNode(const char* name) {
4311cb0ef41Sopenharmony_ci    return static_cast<EmbedderRootNode*>(graph_.AddNode(
4321cb0ef41Sopenharmony_ci        std::unique_ptr<v8::EmbedderGraph::Node>{new EmbedderRootNode(name)}));
4331cb0ef41Sopenharmony_ci  }
4341cb0ef41Sopenharmony_ci
4351cb0ef41Sopenharmony_ci  EmbedderNode* AddNode(const HeapObjectHeader& header) {
4361cb0ef41Sopenharmony_ci    return static_cast<EmbedderNode*>(
4371cb0ef41Sopenharmony_ci        graph_.AddNode(std::unique_ptr<v8::EmbedderGraph::Node>{
4381cb0ef41Sopenharmony_ci            new EmbedderNode(header.GetName(), header.AllocatedSize())}));
4391cb0ef41Sopenharmony_ci  }
4401cb0ef41Sopenharmony_ci
4411cb0ef41Sopenharmony_ci  void AddEdge(State& parent, const HeapObjectHeader& header,
4421cb0ef41Sopenharmony_ci               const std::string& edge_name) {
4431cb0ef41Sopenharmony_ci    DCHECK(parent.IsVisibleNotDependent());
4441cb0ef41Sopenharmony_ci    auto& current = states_.GetExistingState(header);
4451cb0ef41Sopenharmony_ci    if (!current.IsVisibleNotDependent()) return;
4461cb0ef41Sopenharmony_ci
4471cb0ef41Sopenharmony_ci    // Both states are visible. Create nodes in case this is the first edge
4481cb0ef41Sopenharmony_ci    // created for any of them.
4491cb0ef41Sopenharmony_ci    if (!parent.get_node()) {
4501cb0ef41Sopenharmony_ci      parent.set_node(AddNode(*parent.header()));
4511cb0ef41Sopenharmony_ci    }
4521cb0ef41Sopenharmony_ci    if (!current.get_node()) {
4531cb0ef41Sopenharmony_ci      current.set_node(AddNode(header));
4541cb0ef41Sopenharmony_ci    }
4551cb0ef41Sopenharmony_ci
4561cb0ef41Sopenharmony_ci    if (!edge_name.empty()) {
4571cb0ef41Sopenharmony_ci      graph_.AddEdge(parent.get_node(), current.get_node(),
4581cb0ef41Sopenharmony_ci                     parent.get_node()->InternalizeEdgeName(edge_name));
4591cb0ef41Sopenharmony_ci    } else {
4601cb0ef41Sopenharmony_ci      graph_.AddEdge(parent.get_node(), current.get_node());
4611cb0ef41Sopenharmony_ci    }
4621cb0ef41Sopenharmony_ci  }
4631cb0ef41Sopenharmony_ci
4641cb0ef41Sopenharmony_ci  void AddEdge(State& parent, const TracedReferenceBase& ref,
4651cb0ef41Sopenharmony_ci               const std::string& edge_name) {
4661cb0ef41Sopenharmony_ci    DCHECK(parent.IsVisibleNotDependent());
4671cb0ef41Sopenharmony_ci    v8::Local<v8::Value> v8_value =
4681cb0ef41Sopenharmony_ci        ref.Get(reinterpret_cast<v8::Isolate*>(cpp_heap_.isolate()));
4691cb0ef41Sopenharmony_ci    if (!v8_value.IsEmpty()) {
4701cb0ef41Sopenharmony_ci      if (!parent.get_node()) {
4711cb0ef41Sopenharmony_ci        parent.set_node(AddNode(*parent.header()));
4721cb0ef41Sopenharmony_ci      }
4731cb0ef41Sopenharmony_ci      auto* v8_node = graph_.V8Node(v8_value);
4741cb0ef41Sopenharmony_ci      if (!edge_name.empty()) {
4751cb0ef41Sopenharmony_ci        graph_.AddEdge(parent.get_node(), v8_node,
4761cb0ef41Sopenharmony_ci                       parent.get_node()->InternalizeEdgeName(edge_name));
4771cb0ef41Sopenharmony_ci      } else {
4781cb0ef41Sopenharmony_ci        graph_.AddEdge(parent.get_node(), v8_node);
4791cb0ef41Sopenharmony_ci      }
4801cb0ef41Sopenharmony_ci
4811cb0ef41Sopenharmony_ci      // References that have a class id set may have their internal fields
4821cb0ef41Sopenharmony_ci      // pointing back to the object. Set up a wrapper node for the graph so
4831cb0ef41Sopenharmony_ci      // that the snapshot generator  can merge the nodes appropriately.
4841cb0ef41Sopenharmony_ci      // Even with a set class id, do not set up a wrapper node when the edge
4851cb0ef41Sopenharmony_ci      // has a specific name.
4861cb0ef41Sopenharmony_ci      if (!ref.WrapperClassId() || !edge_name.empty()) return;
4871cb0ef41Sopenharmony_ci
4881cb0ef41Sopenharmony_ci      void* back_reference_object = ExtractEmbedderDataBackref(
4891cb0ef41Sopenharmony_ci          reinterpret_cast<v8::internal::Isolate*>(cpp_heap_.isolate()),
4901cb0ef41Sopenharmony_ci          v8_value);
4911cb0ef41Sopenharmony_ci      if (back_reference_object) {
4921cb0ef41Sopenharmony_ci        auto& back_header = HeapObjectHeader::FromObject(back_reference_object);
4931cb0ef41Sopenharmony_ci        auto& back_state = states_.GetExistingState(back_header);
4941cb0ef41Sopenharmony_ci
4951cb0ef41Sopenharmony_ci        // Generally the back reference will point to `parent.header()`. In the
4961cb0ef41Sopenharmony_ci        // case of global proxy set up the backreference will point to a
4971cb0ef41Sopenharmony_ci        // different object, which may not have a node at t his point. Merge the
4981cb0ef41Sopenharmony_ci        // nodes nevertheless as Window objects need to be able to query their
4991cb0ef41Sopenharmony_ci        // detachedness state.
5001cb0ef41Sopenharmony_ci        //
5011cb0ef41Sopenharmony_ci        // TODO(chromium:1218404): See bug description on how to fix this
5021cb0ef41Sopenharmony_ci        // inconsistency and only merge states when the backref points back
5031cb0ef41Sopenharmony_ci        // to the same object.
5041cb0ef41Sopenharmony_ci        if (!back_state.get_node()) {
5051cb0ef41Sopenharmony_ci          back_state.set_node(AddNode(back_header));
5061cb0ef41Sopenharmony_ci        }
5071cb0ef41Sopenharmony_ci        back_state.get_node()->SetWrapperNode(v8_node);
5081cb0ef41Sopenharmony_ci
5091cb0ef41Sopenharmony_ci        auto* profiler =
5101cb0ef41Sopenharmony_ci            reinterpret_cast<Isolate*>(cpp_heap_.isolate())->heap_profiler();
5111cb0ef41Sopenharmony_ci        if (profiler->HasGetDetachednessCallback()) {
5121cb0ef41Sopenharmony_ci          back_state.get_node()->SetDetachedness(
5131cb0ef41Sopenharmony_ci              profiler->GetDetachedness(v8_value, ref.WrapperClassId()));
5141cb0ef41Sopenharmony_ci        }
5151cb0ef41Sopenharmony_ci      }
5161cb0ef41Sopenharmony_ci    }
5171cb0ef41Sopenharmony_ci  }
5181cb0ef41Sopenharmony_ci
5191cb0ef41Sopenharmony_ci  void AddRootEdge(RootState& root, State& child, std::string edge_name) {
5201cb0ef41Sopenharmony_ci    DCHECK(root.IsVisibleNotDependent());
5211cb0ef41Sopenharmony_ci    if (!child.IsVisibleNotDependent()) return;
5221cb0ef41Sopenharmony_ci
5231cb0ef41Sopenharmony_ci    // Root states always have a node set.
5241cb0ef41Sopenharmony_ci    DCHECK_NOT_NULL(root.get_node());
5251cb0ef41Sopenharmony_ci    if (!child.get_node()) {
5261cb0ef41Sopenharmony_ci      child.set_node(AddNode(*child.header()));
5271cb0ef41Sopenharmony_ci    }
5281cb0ef41Sopenharmony_ci
5291cb0ef41Sopenharmony_ci    if (!edge_name.empty()) {
5301cb0ef41Sopenharmony_ci      graph_.AddEdge(root.get_node(), child.get_node(),
5311cb0ef41Sopenharmony_ci                     root.get_node()->InternalizeEdgeName(edge_name));
5321cb0ef41Sopenharmony_ci      return;
5331cb0ef41Sopenharmony_ci    }
5341cb0ef41Sopenharmony_ci    graph_.AddEdge(root.get_node(), child.get_node());
5351cb0ef41Sopenharmony_ci  }
5361cb0ef41Sopenharmony_ci
5371cb0ef41Sopenharmony_ci private:
5381cb0ef41Sopenharmony_ci  class WorkstackItemBase;
5391cb0ef41Sopenharmony_ci  class VisitationItem;
5401cb0ef41Sopenharmony_ci  class VisitationDoneItem;
5411cb0ef41Sopenharmony_ci
5421cb0ef41Sopenharmony_ci  struct MergedNodeItem {
5431cb0ef41Sopenharmony_ci    EmbedderGraph::Node* node_;
5441cb0ef41Sopenharmony_ci    v8::Local<v8::Value> value_;
5451cb0ef41Sopenharmony_ci    uint16_t wrapper_class_id_;
5461cb0ef41Sopenharmony_ci  };
5471cb0ef41Sopenharmony_ci
5481cb0ef41Sopenharmony_ci  CppHeap& cpp_heap_;
5491cb0ef41Sopenharmony_ci  v8::EmbedderGraph& graph_;
5501cb0ef41Sopenharmony_ci  StateStorage states_;
5511cb0ef41Sopenharmony_ci  std::vector<std::unique_ptr<WorkstackItemBase>> workstack_;
5521cb0ef41Sopenharmony_ci};
5531cb0ef41Sopenharmony_ci
5541cb0ef41Sopenharmony_ci// Iterating live objects to mark them as visible if needed.
5551cb0ef41Sopenharmony_ciclass LiveObjectsForVisibilityIterator final
5561cb0ef41Sopenharmony_ci    : public cppgc::internal::HeapVisitor<LiveObjectsForVisibilityIterator> {
5571cb0ef41Sopenharmony_ci  friend class cppgc::internal::HeapVisitor<LiveObjectsForVisibilityIterator>;
5581cb0ef41Sopenharmony_ci
5591cb0ef41Sopenharmony_ci public:
5601cb0ef41Sopenharmony_ci  explicit LiveObjectsForVisibilityIterator(CppGraphBuilderImpl& graph_builder)
5611cb0ef41Sopenharmony_ci      : graph_builder_(graph_builder) {}
5621cb0ef41Sopenharmony_ci
5631cb0ef41Sopenharmony_ci private:
5641cb0ef41Sopenharmony_ci  bool VisitHeapObjectHeader(HeapObjectHeader& header) {
5651cb0ef41Sopenharmony_ci    if (header.IsFree()) return true;
5661cb0ef41Sopenharmony_ci    graph_builder_.VisitForVisibility(nullptr, header);
5671cb0ef41Sopenharmony_ci    graph_builder_.ProcessPendingObjects();
5681cb0ef41Sopenharmony_ci    return true;
5691cb0ef41Sopenharmony_ci  }
5701cb0ef41Sopenharmony_ci
5711cb0ef41Sopenharmony_ci  CppGraphBuilderImpl& graph_builder_;
5721cb0ef41Sopenharmony_ci};
5731cb0ef41Sopenharmony_ci
5741cb0ef41Sopenharmony_ciclass ParentScope final {
5751cb0ef41Sopenharmony_ci public:
5761cb0ef41Sopenharmony_ci  explicit ParentScope(StateBase& parent) : parent_(parent) {}
5771cb0ef41Sopenharmony_ci
5781cb0ef41Sopenharmony_ci  RootState& ParentAsRootState() const {
5791cb0ef41Sopenharmony_ci    return static_cast<RootState&>(parent_);
5801cb0ef41Sopenharmony_ci  }
5811cb0ef41Sopenharmony_ci  State& ParentAsRegularState() const { return static_cast<State&>(parent_); }
5821cb0ef41Sopenharmony_ci
5831cb0ef41Sopenharmony_ci private:
5841cb0ef41Sopenharmony_ci  StateBase& parent_;
5851cb0ef41Sopenharmony_ci};
5861cb0ef41Sopenharmony_ci
5871cb0ef41Sopenharmony_ci// This visitor can be used stand-alone to handle fully weak and ephemeron
5881cb0ef41Sopenharmony_ci// containers or as part of the VisibilityVisitor that recursively traverses
5891cb0ef41Sopenharmony_ci// the object graph.
5901cb0ef41Sopenharmony_ciclass WeakVisitor : public JSVisitor {
5911cb0ef41Sopenharmony_ci public:
5921cb0ef41Sopenharmony_ci  explicit WeakVisitor(CppGraphBuilderImpl& graph_builder)
5931cb0ef41Sopenharmony_ci      : JSVisitor(cppgc::internal::VisitorFactory::CreateKey()),
5941cb0ef41Sopenharmony_ci        graph_builder_(graph_builder) {}
5951cb0ef41Sopenharmony_ci
5961cb0ef41Sopenharmony_ci  void VisitWeakContainer(const void* object,
5971cb0ef41Sopenharmony_ci                          cppgc::TraceDescriptor strong_desc,
5981cb0ef41Sopenharmony_ci                          cppgc::TraceDescriptor weak_desc, cppgc::WeakCallback,
5991cb0ef41Sopenharmony_ci                          const void*) final {
6001cb0ef41Sopenharmony_ci    const auto& container_header =
6011cb0ef41Sopenharmony_ci        HeapObjectHeader::FromObject(strong_desc.base_object_payload);
6021cb0ef41Sopenharmony_ci
6031cb0ef41Sopenharmony_ci    graph_builder_.VisitWeakContainerForVisibility(container_header);
6041cb0ef41Sopenharmony_ci
6051cb0ef41Sopenharmony_ci    if (!weak_desc.callback) {
6061cb0ef41Sopenharmony_ci      // Weak container does not contribute to liveness.
6071cb0ef41Sopenharmony_ci      return;
6081cb0ef41Sopenharmony_ci    }
6091cb0ef41Sopenharmony_ci    // Heap snapshot is always run after a GC so we know there are no dead
6101cb0ef41Sopenharmony_ci    // entries in the container.
6111cb0ef41Sopenharmony_ci    if (object) {
6121cb0ef41Sopenharmony_ci      // The container will itself be traced strongly via the regular Visit()
6131cb0ef41Sopenharmony_ci      // handling that iterates over all live objects. The visibility visitor
6141cb0ef41Sopenharmony_ci      // will thus see (because of strongly treating the container):
6151cb0ef41Sopenharmony_ci      // 1. the container itself;
6161cb0ef41Sopenharmony_ci      // 2. for each {key} in container: container->key;
6171cb0ef41Sopenharmony_ci      // 3. for each {key, value} in container: key->value;
6181cb0ef41Sopenharmony_ci      //
6191cb0ef41Sopenharmony_ci      // In case the visitor is used stand-alone, we trace through the container
6201cb0ef41Sopenharmony_ci      // here to create the same state as we would when the container is traced
6211cb0ef41Sopenharmony_ci      // separately.
6221cb0ef41Sopenharmony_ci      container_header.Trace(this);
6231cb0ef41Sopenharmony_ci    }
6241cb0ef41Sopenharmony_ci  }
6251cb0ef41Sopenharmony_ci  void VisitEphemeron(const void* key, const void* value,
6261cb0ef41Sopenharmony_ci                      cppgc::TraceDescriptor value_desc) final {
6271cb0ef41Sopenharmony_ci    // For ephemerons, the key retains the value.
6281cb0ef41Sopenharmony_ci    // Key always must be a GarbageCollected object.
6291cb0ef41Sopenharmony_ci    auto& key_header = HeapObjectHeader::FromObject(key);
6301cb0ef41Sopenharmony_ci    if (!value_desc.base_object_payload) {
6311cb0ef41Sopenharmony_ci      // Value does not represent an actual GarbageCollected object but rather
6321cb0ef41Sopenharmony_ci      // should be traced eagerly.
6331cb0ef41Sopenharmony_ci      graph_builder_.VisitEphemeronWithNonGarbageCollectedValueForVisibility(
6341cb0ef41Sopenharmony_ci          key_header, value, value_desc);
6351cb0ef41Sopenharmony_ci      return;
6361cb0ef41Sopenharmony_ci    }
6371cb0ef41Sopenharmony_ci    // Regular path where both key and value are GarbageCollected objects.
6381cb0ef41Sopenharmony_ci    graph_builder_.VisitEphemeronForVisibility(
6391cb0ef41Sopenharmony_ci        key_header, HeapObjectHeader::FromObject(value));
6401cb0ef41Sopenharmony_ci  }
6411cb0ef41Sopenharmony_ci
6421cb0ef41Sopenharmony_ci protected:
6431cb0ef41Sopenharmony_ci  CppGraphBuilderImpl& graph_builder_;
6441cb0ef41Sopenharmony_ci};
6451cb0ef41Sopenharmony_ci
6461cb0ef41Sopenharmony_ciclass VisiblityVisitor final : public WeakVisitor {
6471cb0ef41Sopenharmony_ci public:
6481cb0ef41Sopenharmony_ci  VisiblityVisitor(CppGraphBuilderImpl& graph_builder,
6491cb0ef41Sopenharmony_ci                   const ParentScope& parent_scope)
6501cb0ef41Sopenharmony_ci      : WeakVisitor(graph_builder), parent_scope_(parent_scope) {}
6511cb0ef41Sopenharmony_ci
6521cb0ef41Sopenharmony_ci  // C++ handling.
6531cb0ef41Sopenharmony_ci  void Visit(const void*, cppgc::TraceDescriptor desc) final {
6541cb0ef41Sopenharmony_ci    graph_builder_.VisitForVisibility(
6551cb0ef41Sopenharmony_ci        &parent_scope_.ParentAsRegularState(),
6561cb0ef41Sopenharmony_ci        HeapObjectHeader::FromObject(desc.base_object_payload));
6571cb0ef41Sopenharmony_ci  }
6581cb0ef41Sopenharmony_ci  void VisitRoot(const void*, cppgc::TraceDescriptor,
6591cb0ef41Sopenharmony_ci                 const cppgc::SourceLocation&) final {}
6601cb0ef41Sopenharmony_ci  void VisitWeakRoot(const void*, cppgc::TraceDescriptor, cppgc::WeakCallback,
6611cb0ef41Sopenharmony_ci                     const void*, const cppgc::SourceLocation&) final {}
6621cb0ef41Sopenharmony_ci
6631cb0ef41Sopenharmony_ci  // JS handling.
6641cb0ef41Sopenharmony_ci  void Visit(const TracedReferenceBase& ref) final {
6651cb0ef41Sopenharmony_ci    graph_builder_.VisitForVisibility(parent_scope_.ParentAsRegularState(),
6661cb0ef41Sopenharmony_ci                                      ref);
6671cb0ef41Sopenharmony_ci  }
6681cb0ef41Sopenharmony_ci
6691cb0ef41Sopenharmony_ci private:
6701cb0ef41Sopenharmony_ci  const ParentScope& parent_scope_;
6711cb0ef41Sopenharmony_ci};
6721cb0ef41Sopenharmony_ci
6731cb0ef41Sopenharmony_ciclass GraphBuildingVisitor final : public JSVisitor {
6741cb0ef41Sopenharmony_ci public:
6751cb0ef41Sopenharmony_ci  GraphBuildingVisitor(CppGraphBuilderImpl& graph_builder,
6761cb0ef41Sopenharmony_ci                       const ParentScope& parent_scope)
6771cb0ef41Sopenharmony_ci      : JSVisitor(cppgc::internal::VisitorFactory::CreateKey()),
6781cb0ef41Sopenharmony_ci        graph_builder_(graph_builder),
6791cb0ef41Sopenharmony_ci        parent_scope_(parent_scope) {}
6801cb0ef41Sopenharmony_ci
6811cb0ef41Sopenharmony_ci  // C++ handling.
6821cb0ef41Sopenharmony_ci  void Visit(const void*, cppgc::TraceDescriptor desc) final {
6831cb0ef41Sopenharmony_ci    graph_builder_.AddEdge(
6841cb0ef41Sopenharmony_ci        parent_scope_.ParentAsRegularState(),
6851cb0ef41Sopenharmony_ci        HeapObjectHeader::FromObject(desc.base_object_payload), edge_name_);
6861cb0ef41Sopenharmony_ci  }
6871cb0ef41Sopenharmony_ci  void VisitWeakContainer(const void* object,
6881cb0ef41Sopenharmony_ci                          cppgc::TraceDescriptor strong_desc,
6891cb0ef41Sopenharmony_ci                          cppgc::TraceDescriptor weak_desc, cppgc::WeakCallback,
6901cb0ef41Sopenharmony_ci                          const void*) final {
6911cb0ef41Sopenharmony_ci    // Add an edge from the object holding the weak container to the weak
6921cb0ef41Sopenharmony_ci    // container itself.
6931cb0ef41Sopenharmony_ci    graph_builder_.AddEdge(
6941cb0ef41Sopenharmony_ci        parent_scope_.ParentAsRegularState(),
6951cb0ef41Sopenharmony_ci        HeapObjectHeader::FromObject(strong_desc.base_object_payload),
6961cb0ef41Sopenharmony_ci        edge_name_);
6971cb0ef41Sopenharmony_ci  }
6981cb0ef41Sopenharmony_ci  void VisitRoot(const void*, cppgc::TraceDescriptor desc,
6991cb0ef41Sopenharmony_ci                 const cppgc::SourceLocation& loc) final {
7001cb0ef41Sopenharmony_ci    graph_builder_.VisitRootForGraphBuilding(
7011cb0ef41Sopenharmony_ci        parent_scope_.ParentAsRootState(),
7021cb0ef41Sopenharmony_ci        HeapObjectHeader::FromObject(desc.base_object_payload), loc);
7031cb0ef41Sopenharmony_ci  }
7041cb0ef41Sopenharmony_ci  void VisitWeakRoot(const void*, cppgc::TraceDescriptor, cppgc::WeakCallback,
7051cb0ef41Sopenharmony_ci                     const void*, const cppgc::SourceLocation&) final {}
7061cb0ef41Sopenharmony_ci  // JS handling.
7071cb0ef41Sopenharmony_ci  void Visit(const TracedReferenceBase& ref) final {
7081cb0ef41Sopenharmony_ci    graph_builder_.AddEdge(parent_scope_.ParentAsRegularState(), ref,
7091cb0ef41Sopenharmony_ci                           edge_name_);
7101cb0ef41Sopenharmony_ci  }
7111cb0ef41Sopenharmony_ci
7121cb0ef41Sopenharmony_ci  void set_edge_name(std::string edge_name) {
7131cb0ef41Sopenharmony_ci    edge_name_ = std::move(edge_name);
7141cb0ef41Sopenharmony_ci  }
7151cb0ef41Sopenharmony_ci
7161cb0ef41Sopenharmony_ci private:
7171cb0ef41Sopenharmony_ci  CppGraphBuilderImpl& graph_builder_;
7181cb0ef41Sopenharmony_ci  const ParentScope& parent_scope_;
7191cb0ef41Sopenharmony_ci  std::string edge_name_;
7201cb0ef41Sopenharmony_ci};
7211cb0ef41Sopenharmony_ci
7221cb0ef41Sopenharmony_ci// Base class for transforming recursion into iteration. Items are processed
7231cb0ef41Sopenharmony_ci// in stack fashion.
7241cb0ef41Sopenharmony_ciclass CppGraphBuilderImpl::WorkstackItemBase {
7251cb0ef41Sopenharmony_ci public:
7261cb0ef41Sopenharmony_ci  WorkstackItemBase(State* parent, State& current)
7271cb0ef41Sopenharmony_ci      : parent_(parent), current_(current) {}
7281cb0ef41Sopenharmony_ci
7291cb0ef41Sopenharmony_ci  virtual ~WorkstackItemBase() = default;
7301cb0ef41Sopenharmony_ci  virtual void Process(CppGraphBuilderImpl&) = 0;
7311cb0ef41Sopenharmony_ci
7321cb0ef41Sopenharmony_ci protected:
7331cb0ef41Sopenharmony_ci  State* parent_;
7341cb0ef41Sopenharmony_ci  State& current_;
7351cb0ef41Sopenharmony_ci};
7361cb0ef41Sopenharmony_ci
7371cb0ef41Sopenharmony_civoid CppGraphBuilderImpl::ProcessPendingObjects() {
7381cb0ef41Sopenharmony_ci  while (!workstack_.empty()) {
7391cb0ef41Sopenharmony_ci    std::unique_ptr<WorkstackItemBase> item = std::move(workstack_.back());
7401cb0ef41Sopenharmony_ci    workstack_.pop_back();
7411cb0ef41Sopenharmony_ci    item->Process(*this);
7421cb0ef41Sopenharmony_ci  }
7431cb0ef41Sopenharmony_ci}
7441cb0ef41Sopenharmony_ci
7451cb0ef41Sopenharmony_ci// Post-order processing of an object. It's guaranteed that all children have
7461cb0ef41Sopenharmony_ci// been processed first.
7471cb0ef41Sopenharmony_ciclass CppGraphBuilderImpl::VisitationDoneItem final : public WorkstackItemBase {
7481cb0ef41Sopenharmony_ci public:
7491cb0ef41Sopenharmony_ci  VisitationDoneItem(State* parent, State& current)
7501cb0ef41Sopenharmony_ci      : WorkstackItemBase(parent, current) {}
7511cb0ef41Sopenharmony_ci
7521cb0ef41Sopenharmony_ci  void Process(CppGraphBuilderImpl& graph_builder) final {
7531cb0ef41Sopenharmony_ci    CHECK(parent_);
7541cb0ef41Sopenharmony_ci    parent_->MarkDependentVisibility(&current_);
7551cb0ef41Sopenharmony_ci    current_.UnmarkPending();
7561cb0ef41Sopenharmony_ci  }
7571cb0ef41Sopenharmony_ci};
7581cb0ef41Sopenharmony_ci
7591cb0ef41Sopenharmony_ciclass CppGraphBuilderImpl::VisitationItem final : public WorkstackItemBase {
7601cb0ef41Sopenharmony_ci public:
7611cb0ef41Sopenharmony_ci  VisitationItem(State* parent, State& current)
7621cb0ef41Sopenharmony_ci      : WorkstackItemBase(parent, current) {}
7631cb0ef41Sopenharmony_ci
7641cb0ef41Sopenharmony_ci  void Process(CppGraphBuilderImpl& graph_builder) final {
7651cb0ef41Sopenharmony_ci    if (parent_) {
7661cb0ef41Sopenharmony_ci      // Re-add the same object for post-order processing. This must happen
7671cb0ef41Sopenharmony_ci      // lazily, as the parent's visibility depends on its children.
7681cb0ef41Sopenharmony_ci      graph_builder.workstack_.push_back(std::unique_ptr<WorkstackItemBase>{
7691cb0ef41Sopenharmony_ci          new VisitationDoneItem(parent_, current_)});
7701cb0ef41Sopenharmony_ci    }
7711cb0ef41Sopenharmony_ci    ParentScope parent_scope(current_);
7721cb0ef41Sopenharmony_ci    VisiblityVisitor object_visitor(graph_builder, parent_scope);
7731cb0ef41Sopenharmony_ci    current_.header()->Trace(&object_visitor);
7741cb0ef41Sopenharmony_ci    if (!parent_) {
7751cb0ef41Sopenharmony_ci      current_.UnmarkPending();
7761cb0ef41Sopenharmony_ci    }
7771cb0ef41Sopenharmony_ci  }
7781cb0ef41Sopenharmony_ci};
7791cb0ef41Sopenharmony_ci
7801cb0ef41Sopenharmony_civoid CppGraphBuilderImpl::VisitForVisibility(State* parent,
7811cb0ef41Sopenharmony_ci                                             const HeapObjectHeader& header) {
7821cb0ef41Sopenharmony_ci  auto& current = states_.GetOrCreateState(header);
7831cb0ef41Sopenharmony_ci
7841cb0ef41Sopenharmony_ci  if (current.IsVisited()) {
7851cb0ef41Sopenharmony_ci    // Avoid traversing into already visited subgraphs and just update the state
7861cb0ef41Sopenharmony_ci    // based on a previous result.
7871cb0ef41Sopenharmony_ci    if (parent) {
7881cb0ef41Sopenharmony_ci      parent->MarkDependentVisibility(&current);
7891cb0ef41Sopenharmony_ci    }
7901cb0ef41Sopenharmony_ci    return;
7911cb0ef41Sopenharmony_ci  }
7921cb0ef41Sopenharmony_ci
7931cb0ef41Sopenharmony_ci  current.MarkVisited();
7941cb0ef41Sopenharmony_ci  if (header.GetName().name_was_hidden) {
7951cb0ef41Sopenharmony_ci    current.MarkPending();
7961cb0ef41Sopenharmony_ci    workstack_.push_back(std::unique_ptr<WorkstackItemBase>{
7971cb0ef41Sopenharmony_ci        new VisitationItem(parent, current)});
7981cb0ef41Sopenharmony_ci  } else {
7991cb0ef41Sopenharmony_ci    // No need to mark/unmark pending as the node is immediately processed.
8001cb0ef41Sopenharmony_ci    current.MarkVisible();
8011cb0ef41Sopenharmony_ci    // In case the names are visible, the graph is not traversed in this phase.
8021cb0ef41Sopenharmony_ci    // Explicitly trace one level to handle weak containers.
8031cb0ef41Sopenharmony_ci    WeakVisitor weak_visitor(*this);
8041cb0ef41Sopenharmony_ci    header.Trace(&weak_visitor);
8051cb0ef41Sopenharmony_ci    if (parent) {
8061cb0ef41Sopenharmony_ci      // Eagerly update a parent object as its visibility state is now fixed.
8071cb0ef41Sopenharmony_ci      parent->MarkVisible();
8081cb0ef41Sopenharmony_ci    }
8091cb0ef41Sopenharmony_ci  }
8101cb0ef41Sopenharmony_ci}
8111cb0ef41Sopenharmony_ci
8121cb0ef41Sopenharmony_civoid CppGraphBuilderImpl::
8131cb0ef41Sopenharmony_ci    VisitEphemeronWithNonGarbageCollectedValueForVisibility(
8141cb0ef41Sopenharmony_ci        const HeapObjectHeader& key, const void* value,
8151cb0ef41Sopenharmony_ci        cppgc::TraceDescriptor value_desc) {
8161cb0ef41Sopenharmony_ci  auto& key_state = states_.GetOrCreateState(key);
8171cb0ef41Sopenharmony_ci  // Eagerly trace the value here, effectively marking key as visible and
8181cb0ef41Sopenharmony_ci  // queuing processing for all reachable values.
8191cb0ef41Sopenharmony_ci  ParentScope parent_scope(key_state);
8201cb0ef41Sopenharmony_ci  VisiblityVisitor visitor(*this, parent_scope);
8211cb0ef41Sopenharmony_ci  value_desc.callback(&visitor, value);
8221cb0ef41Sopenharmony_ci  key_state.AddEagerEphemeronEdge(value, value_desc.callback);
8231cb0ef41Sopenharmony_ci}
8241cb0ef41Sopenharmony_ci
8251cb0ef41Sopenharmony_civoid CppGraphBuilderImpl::VisitEphemeronForVisibility(
8261cb0ef41Sopenharmony_ci    const HeapObjectHeader& key, const HeapObjectHeader& value) {
8271cb0ef41Sopenharmony_ci  auto& key_state = states_.GetOrCreateState(key);
8281cb0ef41Sopenharmony_ci  VisitForVisibility(&key_state, value);
8291cb0ef41Sopenharmony_ci  key_state.AddEphemeronEdge(value);
8301cb0ef41Sopenharmony_ci}
8311cb0ef41Sopenharmony_ci
8321cb0ef41Sopenharmony_civoid CppGraphBuilderImpl::VisitWeakContainerForVisibility(
8331cb0ef41Sopenharmony_ci    const HeapObjectHeader& container_header) {
8341cb0ef41Sopenharmony_ci  // Mark the container here as weak container to avoid creating any
8351cb0ef41Sopenharmony_ci  // outgoing edges in the second phase.
8361cb0ef41Sopenharmony_ci  states_.GetOrCreateState(container_header).MarkAsWeakContainer();
8371cb0ef41Sopenharmony_ci}
8381cb0ef41Sopenharmony_ci
8391cb0ef41Sopenharmony_civoid CppGraphBuilderImpl::VisitForVisibility(State& parent,
8401cb0ef41Sopenharmony_ci                                             const TracedReferenceBase& ref) {
8411cb0ef41Sopenharmony_ci  v8::Local<v8::Value> v8_value =
8421cb0ef41Sopenharmony_ci      ref.Get(reinterpret_cast<v8::Isolate*>(cpp_heap_.isolate()));
8431cb0ef41Sopenharmony_ci  if (!v8_value.IsEmpty()) {
8441cb0ef41Sopenharmony_ci    parent.MarkVisible();
8451cb0ef41Sopenharmony_ci  }
8461cb0ef41Sopenharmony_ci}
8471cb0ef41Sopenharmony_ci
8481cb0ef41Sopenharmony_civoid CppGraphBuilderImpl::VisitRootForGraphBuilding(
8491cb0ef41Sopenharmony_ci    RootState& root, const HeapObjectHeader& header,
8501cb0ef41Sopenharmony_ci    const cppgc::SourceLocation& loc) {
8511cb0ef41Sopenharmony_ci  State& current = states_.GetExistingState(header);
8521cb0ef41Sopenharmony_ci  if (!current.IsVisibleNotDependent()) return;
8531cb0ef41Sopenharmony_ci
8541cb0ef41Sopenharmony_ci  AddRootEdge(root, current, loc.ToString());
8551cb0ef41Sopenharmony_ci}
8561cb0ef41Sopenharmony_ci
8571cb0ef41Sopenharmony_civoid CppGraphBuilderImpl::Run() {
8581cb0ef41Sopenharmony_ci  // Sweeping from a previous GC might still be running, in which case not all
8591cb0ef41Sopenharmony_ci  // pages have been returned to spaces yet.
8601cb0ef41Sopenharmony_ci  cpp_heap_.sweeper().FinishIfRunning();
8611cb0ef41Sopenharmony_ci  // First pass: Figure out which objects should be included in the graph -- see
8621cb0ef41Sopenharmony_ci  // class-level comment on CppGraphBuilder.
8631cb0ef41Sopenharmony_ci  LiveObjectsForVisibilityIterator visitor(*this);
8641cb0ef41Sopenharmony_ci  visitor.Traverse(cpp_heap_.raw_heap());
8651cb0ef41Sopenharmony_ci  // Second pass: Add graph nodes for objects that must be shown.
8661cb0ef41Sopenharmony_ci  states_.ForAllVisibleStates([this](StateBase* state_base) {
8671cb0ef41Sopenharmony_ci    // No roots have been created so far, so all StateBase objects are State.
8681cb0ef41Sopenharmony_ci    State& state = *static_cast<State*>(state_base);
8691cb0ef41Sopenharmony_ci
8701cb0ef41Sopenharmony_ci    // Emit no edges for the contents of the weak containers. For both, fully
8711cb0ef41Sopenharmony_ci    // weak and ephemeron containers, the contents should be retained from
8721cb0ef41Sopenharmony_ci    // somewhere else.
8731cb0ef41Sopenharmony_ci    if (state.IsWeakContainer()) return;
8741cb0ef41Sopenharmony_ci
8751cb0ef41Sopenharmony_ci    ParentScope parent_scope(state);
8761cb0ef41Sopenharmony_ci    GraphBuildingVisitor object_visitor(*this, parent_scope);
8771cb0ef41Sopenharmony_ci    state.header()->Trace(&object_visitor);
8781cb0ef41Sopenharmony_ci    state.ForAllEphemeronEdges([this, &state](const HeapObjectHeader& value) {
8791cb0ef41Sopenharmony_ci      AddEdge(state, value, "part of key -> value pair in ephemeron table");
8801cb0ef41Sopenharmony_ci    });
8811cb0ef41Sopenharmony_ci    object_visitor.set_edge_name(
8821cb0ef41Sopenharmony_ci        "part of key -> value pair in ephemeron table");
8831cb0ef41Sopenharmony_ci    state.ForAllEagerEphemeronEdges(
8841cb0ef41Sopenharmony_ci        [&object_visitor](const void* value, cppgc::TraceCallback callback) {
8851cb0ef41Sopenharmony_ci          callback(&object_visitor, value);
8861cb0ef41Sopenharmony_ci        });
8871cb0ef41Sopenharmony_ci  });
8881cb0ef41Sopenharmony_ci  // Add roots.
8891cb0ef41Sopenharmony_ci  {
8901cb0ef41Sopenharmony_ci    ParentScope parent_scope(states_.CreateRootState(AddRootNode("C++ roots")));
8911cb0ef41Sopenharmony_ci    GraphBuildingVisitor object_visitor(*this, parent_scope);
8921cb0ef41Sopenharmony_ci    cpp_heap_.GetStrongPersistentRegion().Trace(&object_visitor);
8931cb0ef41Sopenharmony_ci  }
8941cb0ef41Sopenharmony_ci  {
8951cb0ef41Sopenharmony_ci    ParentScope parent_scope(
8961cb0ef41Sopenharmony_ci        states_.CreateRootState(AddRootNode("C++ cross-thread roots")));
8971cb0ef41Sopenharmony_ci    GraphBuildingVisitor object_visitor(*this, parent_scope);
8981cb0ef41Sopenharmony_ci    cppgc::internal::PersistentRegionLock guard;
8991cb0ef41Sopenharmony_ci    cpp_heap_.GetStrongCrossThreadPersistentRegion().Trace(&object_visitor);
9001cb0ef41Sopenharmony_ci  }
9011cb0ef41Sopenharmony_ci}
9021cb0ef41Sopenharmony_ci
9031cb0ef41Sopenharmony_ci// static
9041cb0ef41Sopenharmony_civoid CppGraphBuilder::Run(v8::Isolate* isolate, v8::EmbedderGraph* graph,
9051cb0ef41Sopenharmony_ci                          void* data) {
9061cb0ef41Sopenharmony_ci  CppHeap* cpp_heap = static_cast<CppHeap*>(data);
9071cb0ef41Sopenharmony_ci  CHECK_NOT_NULL(cpp_heap);
9081cb0ef41Sopenharmony_ci  CHECK_NOT_NULL(graph);
9091cb0ef41Sopenharmony_ci  CppGraphBuilderImpl graph_builder(*cpp_heap, *graph);
9101cb0ef41Sopenharmony_ci  graph_builder.Run();
9111cb0ef41Sopenharmony_ci}
9121cb0ef41Sopenharmony_ci
9131cb0ef41Sopenharmony_ci}  // namespace internal
9141cb0ef41Sopenharmony_ci}  // namespace v8
915