// Copyright 2020 PDFium 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 "fxjs/gc/heap.h"

#include "core/fxcrt/fx_system.h"
#include "third_party/base/ptr_util.h"

namespace {

size_t g_platform_ref_count = 0;
v8::Platform* g_platform = nullptr;

}  // namespace

class CFXGC_Platform final : public cppgc::Platform {
 public:
  CFXGC_Platform() = default;
  ~CFXGC_Platform() override = default;

  cppgc::PageAllocator* GetPageAllocator() override {
    return g_platform->GetPageAllocator();
  }

  double MonotonicallyIncreasingTime() override {
    return g_platform->MonotonicallyIncreasingTime();
  }

  std::shared_ptr<cppgc::TaskRunner> GetForegroundTaskRunner() override {
    // V8's default platform creates a new task runner when passed the
    // v8::Isolate pointer the first time. For non-default platforms this will
    // require getting the appropriate task runner.
    return g_platform->GetForegroundTaskRunner(nullptr);
  }

  std::unique_ptr<cppgc::JobHandle> PostJob(
      cppgc::TaskPriority priority,
      std::unique_ptr<cppgc::JobTask> job_task) override {
    return g_platform->PostJob(priority, std::move(job_task));
  }
};

void FXGC_Initialize(v8::Platform* platform) {
  if (platform) {
    ASSERT(!g_platform);
    g_platform = platform;
    cppgc::InitializeProcess(platform->GetPageAllocator());
  }
}

void FXGC_Release() {
  if (g_platform && g_platform_ref_count == 0) {
    cppgc::ShutdownProcess();
    g_platform = nullptr;
  }
}

FXGCScopedHeap FXGC_CreateHeap() {
  // If XFA is included at compile-time, but JS is disabled at run-time,
  // we may still attempt to build a CPDFXFA_Context which will want a
  // heap. But we can't make one because JS is disabled.
  // TODO(tsepez): Stop the context from even being created.
  if (!g_platform)
    return nullptr;

  ++g_platform_ref_count;
  auto heap = cppgc::Heap::Create(std::make_shared<CFXGC_Platform>());
  return FXGCScopedHeap(heap.release());
}

void FXGCHeapDeleter::operator()(cppgc::Heap* heap) {
  ASSERT(heap);
  ASSERT(g_platform_ref_count > 0);
  --g_platform_ref_count;

  heap->ForceGarbageCollectionSlow("FXGCHeapDeleter", "FinalGCBeforeDelete",
                                   cppgc::Heap::StackState::kNoHeapPointers);
  delete heap;
}
