// 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.

#include "test/unittests/heap/cppgc-js/unified-heap-utils.h"

#include "include/cppgc/platform.h"
#include "include/v8-cppgc.h"
#include "include/v8-function.h"
#include "src/api/api-inl.h"
#include "src/heap/cppgc-js/cpp-heap.h"
#include "src/heap/heap.h"
#include "src/objects/cpp-heap-object-wrapper-inl.h"
#include "src/objects/js-objects.h"
#include "src/objects/objects-inl.h"
#include "test/unittests/heap/heap-utils.h"

namespace v8 {
namespace internal {

// static
v8::Local<v8::Object> WrapperHelper::CreateWrapper(
    v8::Local<v8::Context> context, void* wrappable_object,
    const char* class_name) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::EscapableHandleScope scope(isolate);
  v8::Local<v8::FunctionTemplate> function_t =
      v8::FunctionTemplate::New(isolate);
  if (class_name && strlen(class_name) != 0) {
    function_t->SetClassName(
        v8::String::NewFromUtf8(isolate, class_name).ToLocalChecked());
  }
  v8::Local<v8::Function> function =
      function_t->GetFunction(context).ToLocalChecked();
  v8::Local<v8::Object> instance =
      function->NewInstance(context).ToLocalChecked();
  SetWrappableConnection(isolate, instance, wrappable_object);
  CHECK(!instance.IsEmpty());
  CHECK_EQ(wrappable_object, ReadWrappablePointer(isolate, instance));
  i::DirectHandle<i::JSReceiver> js_obj =
      v8::Utils::OpenDirectHandle(*instance);
  CHECK_EQ(i::JS_API_OBJECT_TYPE, js_obj->map()->instance_type());
  return scope.Escape(instance);
}

// static
void WrapperHelper::ResetWrappableConnection(v8::Isolate* isolate,
                                             v8::Local<v8::Object> api_object) {
  i::DirectHandle<i::JSReceiver> js_obj =
      v8::Utils::OpenDirectHandle(*api_object);
  CppHeapObjectWrapper(Cast<JSObject>(*js_obj))
      .SetCppHeapWrappable(reinterpret_cast<i::Isolate*>(isolate), nullptr,
                           CppHeapPointerTag::kTagForTesting);
}

// static
void WrapperHelper::SetWrappableConnection(v8::Isolate* isolate,
                                           v8::Local<v8::Object> api_object,
                                           void* instance) {
  i::DirectHandle<i::JSReceiver> js_obj =
      v8::Utils::OpenDirectHandle(*api_object);
  CppHeapObjectWrapper(Cast<JSObject>(*js_obj))
      .SetCppHeapWrappable(reinterpret_cast<i::Isolate*>(isolate), instance,
                           CppHeapPointerTag::kTagForTesting);
}

// static
void* WrapperHelper::ReadWrappablePointer(v8::Isolate* isolate,
                                          v8::Local<v8::Object> api_object) {
  i::DirectHandle<i::JSReceiver> js_obj =
      v8::Utils::OpenDirectHandle(*api_object);
  return CppHeapObjectWrapper(Cast<JSObject>(*js_obj))
      .GetCppHeapWrappable(reinterpret_cast<i::Isolate*>(isolate),
                           kAnyCppHeapPointer);
}

}  // namespace internal
}  // namespace v8
