// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/heap/reference-summarizer.h"

#include "src/heap/mark-compact-inl.h"
#include "src/heap/marking-visitor-inl.h"
#include "src/objects/embedder-data-array-inl.h"
#include "src/objects/js-array-buffer-inl.h"

namespace v8 {
namespace internal {

namespace {

// A class which acts as a MarkingState but does not actually update any marking
// bits. It reports all objects as white and all transitions as successful. It
// also tracks which objects are retained by the primary object according to the
// marking visitor.
class ReferenceSummarizerMarkingState final {
 public:
  // Declares that this marking state is collecting retainers, so the marking
  // visitor must fully visit each object and can't update on-heap state.
  static constexpr bool kCollectRetainers = true;

  explicit ReferenceSummarizerMarkingState(HeapObject object)
      : primary_object_(object),
        local_marking_worklists_(&marking_worklists_),
        local_weak_objects_(&weak_objects_) {}

  ~ReferenceSummarizerMarkingState() {
    // Clean up temporary state.
    local_weak_objects_.Publish();
    weak_objects_.Clear();
    local_marking_worklists_.Publish();
    marking_worklists_.Clear();
  }

  // Retrieves the references that were collected by this marker. This operation
  // transfers ownership of the set, so calling it again would yield an empty
  // result.
  ReferenceSummary DestructivelyRetrieveReferences() {
    ReferenceSummary tmp = std::move(references_);
    references_.Clear();
    return tmp;
  }

  // Standard marking visitor functions:

  bool IsWhite(HeapObject obj) const { return true; }

  bool IsBlackOrGrey(HeapObject obj) const { return false; }

  bool WhiteToGrey(HeapObject obj) { return true; }

  bool GreyToBlack(HeapObject obj) { return true; }

  // Adds a retaining relationship found by the marking visitor.
  void AddStrongReferenceForReferenceSummarizer(HeapObject host,
                                                HeapObject obj) {
    AddReference(host, obj, references_.strong_references());
  }

  // Adds a non-retaining weak reference found by the marking visitor. The value
  // in an ephemeron hash table entry is also included here, since it is not
  // known to be strong without further information about the key.
  void AddWeakReferenceForReferenceSummarizer(HeapObject host, HeapObject obj) {
    AddReference(host, obj, references_.weak_references());
  }

  // Other member functions, not part of the marking visitor contract:

  MarkingWorklists::Local* local_marking_worklists() {
    return &local_marking_worklists_;
  }
  WeakObjects::Local* local_weak_objects() { return &local_weak_objects_; }

 private:
  void AddReference(
      HeapObject host, HeapObject obj,
      std::unordered_set<HeapObject, Object::Hasher>& references) {
    // It's possible that the marking visitor handles multiple objects at once,
    // such as a Map and its DescriptorArray, but we're only interested in
    // references from the primary object.
    if (host == primary_object_) {
      references.insert(obj);
    }
  }

  ReferenceSummary references_;
  HeapObject primary_object_;
  MarkingWorklists marking_worklists_;
  MarkingWorklists::Local local_marking_worklists_;
  WeakObjects weak_objects_;
  WeakObjects::Local local_weak_objects_;
};

}  // namespace

ReferenceSummary ReferenceSummary::SummarizeReferencesFrom(Heap* heap,
                                                           HeapObject obj) {
  ReferenceSummarizerMarkingState marking_state(obj);

  MainMarkingVisitor<ReferenceSummarizerMarkingState> visitor(
      &marking_state, marking_state.local_marking_worklists(),
      marking_state.local_weak_objects(), heap, 0 /*mark_compact_epoch*/,
      {} /*code_flush_mode*/, false /*embedder_tracing_enabled*/,
      true /*should_keep_ages_unchanged*/);
  visitor.Visit(obj.map(heap->isolate()), obj);

  return marking_state.DestructivelyRetrieveReferences();
}

}  // namespace internal
}  // namespace v8
