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

#ifndef V8_HEAP_CPPGC_HEAP_H_
#define V8_HEAP_CPPGC_HEAP_H_

#include <memory>
#include <vector>

#include "include/cppgc/heap.h"
#include "include/cppgc/internal/gc-info.h"
#include "include/cppgc/internal/persistent-node.h"
#include "include/cppgc/liveness-broker.h"
#include "include/cppgc/macros.h"
#include "src/base/page-allocator.h"
#include "src/heap/cppgc/heap-object-header.h"
#include "src/heap/cppgc/marker.h"
#include "src/heap/cppgc/object-allocator.h"
#include "src/heap/cppgc/page-memory.h"
#include "src/heap/cppgc/prefinalizer-handler.h"
#include "src/heap/cppgc/raw-heap.h"
#include "src/heap/cppgc/sweeper.h"

namespace cppgc {
namespace internal {

class Stack;

class V8_EXPORT_PRIVATE LivenessBrokerFactory {
 public:
  static LivenessBroker Create();
};

class V8_EXPORT_PRIVATE Heap final : public cppgc::Heap {
 public:
  // NoGCScope allows going over limits and avoids triggering garbage
  // collection triggered through allocations or even explicitly.
  class V8_EXPORT_PRIVATE NoGCScope final {
    CPPGC_STACK_ALLOCATED();

   public:
    explicit NoGCScope(Heap* heap);
    ~NoGCScope();

    NoGCScope(const NoGCScope&) = delete;
    NoGCScope& operator=(const NoGCScope&) = delete;

   private:
    Heap* const heap_;
  };

  // NoAllocationScope is used in debug mode to catch unwanted allocations. E.g.
  // allocations during GC.
  class V8_EXPORT_PRIVATE NoAllocationScope final {
    CPPGC_STACK_ALLOCATED();

   public:
    explicit NoAllocationScope(Heap* heap);
    ~NoAllocationScope();

    NoAllocationScope(const NoAllocationScope&) = delete;
    NoAllocationScope& operator=(const NoAllocationScope&) = delete;

   private:
    Heap* const heap_;
  };

  struct GCConfig {
    using StackState = Heap::StackState;

    static GCConfig Default() { return {StackState::kMayContainHeapPointers}; }

    StackState stack_state = StackState::kMayContainHeapPointers;
  };

  static Heap* From(cppgc::Heap* heap) { return static_cast<Heap*>(heap); }

  explicit Heap(size_t custom_spaces);
  ~Heap() final;

  inline void* Allocate(size_t size, GCInfoIndex index);
  inline void* Allocate(size_t size, GCInfoIndex index,
                        CustomSpaceIndex space_index);

  void CollectGarbage(GCConfig config = GCConfig::Default());

  PreFinalizerHandler* prefinalizer_handler() {
    return prefinalizer_handler_.get();
  }

  PersistentRegion& GetStrongPersistentRegion() {
    return strong_persistent_region_;
  }
  const PersistentRegion& GetStrongPersistentRegion() const {
    return strong_persistent_region_;
  }
  PersistentRegion& GetWeakPersistentRegion() {
    return weak_persistent_region_;
  }
  const PersistentRegion& GetWeakPersistentRegion() const {
    return weak_persistent_region_;
  }

  RawHeap& raw_heap() { return raw_heap_; }
  const RawHeap& raw_heap() const { return raw_heap_; }

  Stack* stack() { return stack_.get(); }

  PageBackend* page_backend() { return page_backend_.get(); }
  const PageBackend* page_backend() const { return page_backend_.get(); }

  Sweeper& sweeper() { return sweeper_; }

  size_t epoch() const { return epoch_; }

  size_t ObjectPayloadSize() const;

 private:
  bool in_no_gc_scope() const { return no_gc_scope_ > 0; }
  bool is_allocation_allowed() const { return no_allocation_scope_ == 0; }

  RawHeap raw_heap_;

  v8::base::PageAllocator system_allocator_;
  std::unique_ptr<PageBackend> page_backend_;
  ObjectAllocator object_allocator_;
  Sweeper sweeper_;

  std::unique_ptr<Stack> stack_;
  std::unique_ptr<PreFinalizerHandler> prefinalizer_handler_;
  std::unique_ptr<Marker> marker_;

  PersistentRegion strong_persistent_region_;
  PersistentRegion weak_persistent_region_;

  size_t epoch_ = 0;

  size_t no_gc_scope_ = 0;
  size_t no_allocation_scope_ = 0;
};

}  // namespace internal
}  // namespace cppgc

#endif  // V8_HEAP_CPPGC_HEAP_H_