/**
 * Copyright (c) 2021-2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cinttypes>

#include "ecmascript/base/json_stringifier.h"
#include "ecmascript/base/typed_array_helper-inl.h"
#include "ecmascript/builtins/builtins_object.h"
#include "ecmascript/shared_objects/concurrent_api_scope.h"
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
#include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
#endif
#include "ecmascript/checkpoint/thread_state_transition.h"
#include "ecmascript/ecma_global_storage.h"
#include "ecmascript/ic/ic_info.h"
#include "ecmascript/interpreter/fast_runtime_stub-inl.h"
#include "ecmascript/interpreter/interpreter_assembly.h"
#include "ecmascript/jsnapi_sendable.h"
#include "ecmascript/jspandafile/js_pandafile_executor.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/lexical_env.h"
#include "ecmascript/linked_hash_table.h"
#include "ecmascript/mem/idle_gc_trigger.h"
#include "ecmascript/module/module_logger.h"
#include "ecmascript/module/napi_module_loader.h"
#include "ecmascript/napi/jsnapi_class_creation_helper.h"
#include "ecmascript/ohos/adapter/modulemanager/module_pkg_parser.h"
#include "ecmascript/ohos/js_pandafile_snapshot_interfaces.h"
#include "ecmascript/ohos/constpool_snapshot_interfaces.h"
#include "ecmascript/ohos/module_snapshot_interfaces.h"
#include "ecmascript/ohos/ohos_constants.h"
#include "ecmascript/ohos/aot_runtime_info.h"
#if defined(ENABLE_EXCEPTION_BACKTRACE)
#include "ecmascript/platform/backtrace.h"
#endif
#include "ecmascript/regexp/regexp_parser.h"
#include "ecmascript/serializer/base_deserializer.h"
#include "ecmascript/serializer/inter_op_value_deserializer.h"
#include "ecmascript/serializer/inter_op_value_serializer.h"
#include "ecmascript/serializer/value_serializer.h"
#include "ecmascript/platform/aot_crash_info.h"
#include "ecmascript/platform/dfx_crash_obj.h"
#ifdef ARK_SUPPORT_INTL
#include "ecmascript/js_bigint.h"
#include "ecmascript/js_collator.h"
#include "ecmascript/js_date_time_format.h"
#include "ecmascript/js_number_format.h"
#endif
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
#include "ecmascript/dfx/hprof/heap_profiler.h"
#endif

namespace panda {
using ecmascript::AccessorData;
using ecmascript::BigInt;
using ecmascript::ByteArray;
using ecmascript::DataViewType;
using ecmascript::ECMAObject;
using ecmascript::EcmaRuntimeCallInfo;
using ecmascript::EcmaString;
using ecmascript::EcmaStringAccessor;
using ecmascript::ErrorType;
using ecmascript::FastRuntimeStub;
using ecmascript::GeneratorContext;
using ecmascript::GlobalEnv;
using ecmascript::GlobalEnvConstants;
using ecmascript::IterationKind;
using ecmascript::JSArray;
using ecmascript::JSArrayBuffer;
using ecmascript::JSDataView;
using ecmascript::JSDate;
using ecmascript::JSFunction;
using ecmascript::JSFunctionBase;
using ecmascript::JSGeneratorFunction;
using ecmascript::JSGeneratorObject;
using ecmascript::JSGeneratorState;
using ecmascript::JSHClass;
using ecmascript::JSIterator;
using ecmascript::JSMap;
using ecmascript::JSMapIterator;
using ecmascript::JSNapiSendable;
using ecmascript::JSNativePointer;
using ecmascript::JSObject;
using ecmascript::JSPandaFile;
using ecmascript::JSPandaFileManager;
using ecmascript::JSPrimitiveRef;
using ecmascript::JSPromise;
using ecmascript::JSProxy;
using ecmascript::ObjectFastOperator;
using ecmascript::JSRegExp;
using ecmascript::JSRuntimeOptions;
using ecmascript::JSSet;
using ecmascript::JSSetIterator;
using ecmascript::JSSymbol;
using ecmascript::JSTaggedNumber;
using ecmascript::JSTaggedType;
using ecmascript::JSTaggedValue;
using ecmascript::JSThread;
using ecmascript::JSType;
using ecmascript::JSTypedArray;
using ecmascript::JSNApiClassCreationHelper;
using ecmascript::LinkedHashMap;
using ecmascript::LinkedHashSet;
using ecmascript::LockHolder;
using ecmascript::MemMapAllocator;
using ecmascript::Method;
using ecmascript::NativeModuleFailureInfo;
using ecmascript::ObjectFactory;
using ecmascript::OperationResult;
using ecmascript::PromiseCapability;
using ecmascript::PropertyDescriptor;
using ecmascript::Region;
using ecmascript::ICInfo;
using ecmascript::ICKind;
using ecmascript::NapiICInfo;
using ecmascript::TaggedArray;
using ecmascript::WeakLinkedHashMap;
using ecmascript::base::BuiltinsBase;
using ecmascript::base::JsonStringifier;
using ecmascript::SharedHeap;
using ecmascript::base::StringHelper;
using ecmascript::base::TypedArrayHelper;
using ecmascript::base::Utf16JsonParser;
using ecmascript::base::Utf8JsonParser;
using ecmascript::builtins::BuiltinsObject;
using ecmascript::job::MicroJobQueue;
using ecmascript::job::QueueType;
#ifdef ARK_SUPPORT_INTL
using ecmascript::JSCollator;
using ecmascript::JSDateTimeFormat;
using ecmascript::JSNumberFormat;
#endif
using ecmascript::DebugInfoExtractor;
using ecmascript::JSWeakMap;
using ecmascript::JSWeakSet;
using ecmascript::RegExpParser;
using ecmascript::base::NumberHelper;
template <typename T>
using JSHandle = ecmascript::JSHandle<T>;
template <typename T>
using JSMutableHandle = ecmascript::JSMutableHandle<T>;

using PathHelper = ecmascript::base::PathHelper;
using ModulePathHelper = ecmascript::ModulePathHelper;
using JsDebuggerManager = ecmascript::tooling::JsDebuggerManager;
using FrameIterator = ecmascript::FrameIterator;
using Concurrent = ecmascript::Concurrent;
using EnableAotJitListHelper = ecmascript::ohos::EnableAotJitListHelper;
using PGOProfilerManager = ecmascript::pgo::PGOProfilerManager;
using AotRuntimeInfo = ecmascript::ohos::AotRuntimeInfo;
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
using HeapProfiler = ecmascript::HeapProfiler;
using HeapProfilerInterface = ecmascript::HeapProfilerInterface;
#endif

namespace {
// NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
constexpr std::string_view ENTRY_POINTER = "_GLOBAL::func_main_0";
}

bool JSNApi::isForked_ = false;
static ecmascript::Mutex *mutex = new panda::ecmascript::Mutex();
StartIdleMonitorCallback JSNApi::startIdleMonitorCallback_ = nullptr;
const static uint32_t API_VERSION_MASK = 100;

// ----------------------------------- ArkCrashHolder --------------------------------------
constexpr size_t FORMATED_FUNCPTR_LENGTH = 36; // length of dec function pointer

ArkCrashHolder::~ArkCrashHolder()
{
    ecmascript::ResetCrashObject(handle_);
    if (data_ != nullptr) {
        delete [] data_;
        data_ = nullptr;
    }
}

void ArkCrashHolder::SetCrashObj(const char* tag, const char* info)
{
    std::string data = "[";
    data += tag,
    data += "] Crash occurred on ";
    data += info;
    data += ", callback: ";

    size_ = data.length();
    const size_t bufSize = size_ + FORMATED_FUNCPTR_LENGTH;
    data_ = new char[bufSize];

    if (memcpy_s(data_, bufSize, data.c_str(), size_) != EOK) {
        LOG_FULL(WARN) << "Failed to init crash holder.";
        size_ = 0;
        data_[0] = '\0';
    };

    handle_ = ecmascript::SetCrashObject(ecmascript::DFXObjectType::STRING, reinterpret_cast<uintptr_t>(data_));
}

void ArkCrashHolder::UpdateCallbackPtr(uintptr_t addr)
{
    if (sprintf_s(data_ + size_, FORMATED_FUNCPTR_LENGTH, "%" PRIuPTR, addr) < 0) {
        LOG_FULL(ERROR) << "Failed to update callback info: " << addr;
    }
}

#undef ENABLE_DFX_CRASH_OBJECT

// ----------------------------------- JSValueRef --------------------------------------
Local<PrimitiveRef> JSValueRef::Undefined(const EcmaVM *vm)
{
    return JSNApiHelper::ToLocal<PrimitiveRef>(
        vm->GetJSThread()->GlobalConstants()->GetHandledUndefined());
}

Local<PrimitiveRef> JSValueRef::Null(const EcmaVM *vm)
{
    return JSNApiHelper::ToLocal<PrimitiveRef>(
        vm->GetJSThread()->GlobalConstants()->GetHandledNull());
}

Local<PrimitiveRef> JSValueRef::Hole(const EcmaVM *vm)
{
    return JSNApiHelper::ToLocal<PrimitiveRef>(
        vm->GetJSThread()->GlobalConstants()->GetHandledHole());
}

Local<PrimitiveRef> JSValueRef::True(const EcmaVM *vm)
{
    return JSNApiHelper::ToLocal<PrimitiveRef>(
        vm->GetJSThread()->GlobalConstants()->GetHandledTrue());
}

Local<PrimitiveRef> JSValueRef::False(const EcmaVM *vm)
{
    return JSNApiHelper::ToLocal<PrimitiveRef>(
        vm->GetJSThread()->GlobalConstants()->GetHandledFalse());
}

Local<ObjectRef> JSValueRef::ToObject(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    if (IsUndefined() || IsNull()) {
        return Undefined(vm);
    }
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj(JSTaggedValue::ToObject(thread, JSNApiHelper::ToJSHandle(this)));
    LOG_IF_SPECIAL(obj, ERROR);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<ObjectRef>(obj);
}

Local<ObjectRef> JSValueRef::ToEcmaObject(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    if (obj->IsECMAObject()) {
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        return JSNApiHelper::ToLocal<ObjectRef>(obj);
    }

    return Undefined(vm);
}

Local<ObjectRef> JSValueRef::ToEcmaObjectWithoutSwitchState(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<ObjectRef>(obj);
}

Local<StringRef> JSValueRef::ToString(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    if (!obj->IsString()) {
        obj = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, obj));
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    }
    return JSNApiHelper::ToLocal<StringRef>(obj);
}

Local<NativePointerRef> JSValueRef::ToNativePointer(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    // The function just get handle' value, and will not read and write js object. Don't need to switch state.
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    return JSNApiHelper::ToLocal<NativePointerRef>(obj);
}

bool JSValueRef::BooleaValue(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).ToBoolean();
}

int64_t JSValueRef::IntegerValue(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> tagged = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(tagged, ERROR);
    if (tagged->IsNumber()) {
        if (!NumberHelper::IsFinite(tagged.GetTaggedValue()) || NumberHelper::IsNaN(tagged.GetTaggedValue())) {
            return 0;
        } else {
            return NumberHelper::DoubleToInt64(tagged->GetNumber());
        }
    }
    JSTaggedNumber number = JSTaggedValue::ToInteger(thread, tagged);
    RETURN_VALUE_IF_ABRUPT(thread, 0);
    return NumberHelper::DoubleToInt64(number.GetNumber());
}

uint32_t JSValueRef::Uint32Value(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> tagged = JSNApiHelper::ToJSHandle(this);
    uint32_t number = 0;
    if (!tagged->IsECMAObject()) {
        number = JSTaggedValue::ToUint32(thread, tagged);
    } else {
        // EcmaObject may call [Symbol.toPrimitive].
        number = JSTaggedValue::ToUint32(thread, tagged);
    }
    RETURN_VALUE_IF_ABRUPT(thread, 0);
    return number;
}

int32_t JSValueRef::Int32Value(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> tagged = JSNApiHelper::ToJSHandle(this);
    int32_t number = 0;
    if (!tagged->IsECMAObject()) {
        number = JSTaggedValue::ToInt32(thread, tagged);
    } else {
        // EcmaObject may call [Symbol.toPrimitive].
        number = JSTaggedValue::ToInt32(thread, tagged);
    }
    RETURN_VALUE_IF_ABRUPT(thread, 0);
    return number;
}

double JSValueRef::GetValueDouble(bool &isNumber)
{
    JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this);
    if (value.IsInt()) {
        isNumber = true;
        return static_cast<double>(value.GetInt());
    }
    if (value.IsDouble()) {
        isNumber = true;
        return value.GetDouble();
    }
    isNumber = false;
    return 0.0;
}

int32_t JSValueRef::GetValueInt32(bool &isNumber)
{
    JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this);
    if (value.IsInt()) {
        isNumber = true;
        return value.GetInt();
    }
    if (value.IsDouble()) {
        isNumber = true;
        return static_cast<int32_t>(ecmascript::base::NumberHelper::DoubleToInt(value.GetDouble(),
            ecmascript::base::INT32_BITS));
    }
    isNumber = false;
    return 0;
}

uint32_t JSValueRef::GetValueUint32(bool &isNumber)
{
    return static_cast<uint32_t>(GetValueInt32(isNumber));
}

int64_t JSValueRef::GetValueInt64(bool &isNumber)
{
    JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this);
    if (value.IsInt()) {
        isNumber = true;
        return static_cast<int64_t>(value.GetInt());
    }
    if (value.IsDouble()) {
        isNumber = true;
        double getVale = value.GetDouble();
        if (!std::isfinite(getVale) || std::isnan(getVale)) {
            return 0;
        }
        return NumberHelper::DoubleToInt64(getVale);
    }
    isNumber = false;
    return 0;
}

bool JSValueRef::GetValueBool(bool &isBool)
{
    JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this);
    if (value.IsTrue()) {
        isBool = true;
        return true;
    }
    if (value.IsFalse()) {
        isBool = true;
        return false;
    }
    isBool = false;
    return false;
}

Local<BooleanRef> JSValueRef::ToBoolean(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<JSTaggedValue> booleanObj(thread, JSTaggedValue(obj->ToBoolean()));
    return JSNApiHelper::ToLocal<BooleanRef>(booleanObj);
}

Local<BigIntRef> JSValueRef::ToBigInt(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<JSTaggedValue> bigIntObj(thread, JSTaggedValue::ToBigInt(thread, obj));
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<BigIntRef>(bigIntObj);
}

Local<NumberRef> JSValueRef::ToNumber(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<JSTaggedValue> number(thread, JSTaggedValue::ToNumber(thread, obj));
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<NumberRef>(number);
}

bool JSValueRef::IsStrictEquals(const EcmaVM *vm, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> xValue = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(xValue, ERROR);
    JSHandle<JSTaggedValue> yValue = JSNApiHelper::ToJSHandle(value);
    return JSTaggedValue::StrictEqual(thread, xValue, yValue);
}

Local<StringRef> JSValueRef::Typeof(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedValue value = FastRuntimeStub::FastTypeOf(thread, JSNApiHelper::ToJSTaggedValue(this));
    return JSNApiHelper::ToLocal<StringRef>(JSHandle<JSTaggedValue>(thread, value));
}

bool JSValueRef::InstanceOf(const EcmaVM *vm, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> origin = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(origin, ERROR);
    JSHandle<JSTaggedValue> target = JSNApiHelper::ToJSHandle(value);
    return JSObject::InstanceOf(thread, origin, target);
}

// Omit exception check for JSValueRef::IsXxx because ark calls here may not
// cause side effect even pending exception exists.
bool JSValueRef::IsUndefined()
{
    return JSNApiHelper::ToJSTaggedValue(this).IsUndefined();
}

bool JSValueRef::IsNull()
{
    return JSNApiHelper::ToJSTaggedValue(this).IsNull();
}

bool JSValueRef::IsHole()
{
    return JSNApiHelper::ToJSTaggedValue(this).IsHole();
}

bool JSValueRef::IsTrue()
{
    return JSNApiHelper::ToJSTaggedValue(this).IsTrue();
}

bool JSValueRef::IsFalse()
{
    return JSNApiHelper::ToJSTaggedValue(this).IsFalse();
}

bool JSValueRef::IsNumber()
{
    return JSNApiHelper::ToJSTaggedValue(this).IsNumber();
}

bool JSValueRef::IsBigInt(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsBigInt();
}

bool JSValueRef::IsInt()
{
    return JSNApiHelper::ToJSTaggedValue(this).IsInt();
}

bool JSValueRef::WithinInt32()
{
    return JSNApiHelper::ToJSTaggedValue(this).WithinInt32();
}

bool JSValueRef::IsBoolean()
{
    return JSNApiHelper::ToJSTaggedValue(this).IsBoolean();
}

bool JSValueRef::IsString(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsString();
}

bool JSValueRef::IsStringWithoutSwitchState(const EcmaVM *vm)
{
    CROSS_THREAD_CHECK(vm);
    return JSNApiHelper::ToJSTaggedValue(this).IsString();
}

bool JSValueRef::IsSymbol(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsSymbol();
}

bool JSValueRef::IsObject(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsECMAObject();
}

bool JSValueRef::IsObjectWithoutSwitchState(const EcmaVM *vm)
{
    CROSS_THREAD_CHECK(vm);
    return JSNApiHelper::ToJSTaggedValue(this).IsECMAObject();
}

bool JSValueRef::IsNativeBindingObject(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSTaggedValue object = JSNApiHelper::ToJSTaggedValue(this);
    if (!object.IsECMAObject()) {
        return false;
    }

    return object.GetTaggedObject()->GetClass()->IsNativeBindingObject();
}

bool JSValueRef::IsArray(const EcmaVM *vm)
{
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsArray(thread);
}

bool JSValueRef::IsJSArray(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSArray();
}

bool JSValueRef::IsConstructor(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this);
    return value.IsHeapObject() && value.IsConstructor();
}

bool JSValueRef::IsFunction(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this);
    return value.IsCallable();
}

bool JSValueRef::IsJSFunction(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this);
    return value.IsJSFunction();
}

bool JSValueRef::IsProxy(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSProxy();
}

bool JSValueRef::IsPromise(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSPromise();
}

bool JSValueRef::IsDataView(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsDataView();
}

bool JSValueRef::IsTypedArray(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsTypedArray();
}

bool JSValueRef::IsNativePointer(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSNativePointer();
}

bool JSValueRef::IsDate(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsDate();
}

bool JSValueRef::IsError(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSError();
}

bool JSValueRef::IsMap(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSMap();
}

bool JSValueRef::IsSet(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSet();
}

bool JSValueRef::IsWeakRef(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSWeakRef();
}

bool JSValueRef::IsWeakMap(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSWeakMap();
}

bool JSValueRef::IsWeakSet(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSWeakSet();
}

bool JSValueRef::IsRegExp(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSRegExp();
}

bool JSValueRef::IsArrayIterator(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSArrayIterator();
}

bool JSValueRef::IsStringIterator(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsStringIterator();
}

bool JSValueRef::IsSetIterator(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSetIterator();
}

bool JSValueRef::IsMapIterator(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSMapIterator();
}

bool JSValueRef::IsArrayBuffer(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsArrayBuffer();
}

bool JSValueRef::IsBuffer(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsArrayBuffer();
}

bool JSValueRef::IsUint8Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSUint8Array();
}

bool JSValueRef::IsInt8Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSInt8Array();
}

bool JSValueRef::IsUint8ClampedArray(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSUint8ClampedArray();
}

bool JSValueRef::IsInt16Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSInt16Array();
}

bool JSValueRef::IsUint16Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSUint16Array();
}

bool JSValueRef::IsInt32Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSInt32Array();
}

bool JSValueRef::IsUint32Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSUint32Array();
}

bool JSValueRef::IsFloat32Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSFloat32Array();
}

bool JSValueRef::IsFloat64Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSFloat64Array();
}

bool JSValueRef::IsBigInt64Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSBigInt64Array();
}

bool JSValueRef::IsBigUint64Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSBigUint64Array();
}

bool JSValueRef::IsJSSharedInt8Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedInt8Array();
}

bool JSValueRef::IsJSSharedUint8Array([[maybe_unused]]const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedUint8Array();
}

bool JSValueRef::IsJSSharedUint8ClampedArray([[maybe_unused]]const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedUint8ClampedArray();
}

bool JSValueRef::IsJSSharedInt16Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedInt16Array();
}

bool JSValueRef::IsJSSharedUint16Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedUint16Array();
}

bool JSValueRef::IsJSSharedInt32Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedInt32Array();
}

bool JSValueRef::IsJSSharedFloat32Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedFloat32Array();
}

bool JSValueRef::IsJSSharedUint32Array(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedUint32Array();
}

bool JSValueRef::IsJSPrimitiveRef(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSPrimitiveRef();
}

bool JSValueRef::IsJSPrimitiveNumber(const EcmaVM *vm)
{
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, FATAL);
    return IsJSPrimitiveRef(vm) ? JSPrimitiveRef::Cast(obj->GetTaggedObject())->IsNumber(thread) : false;
}

bool JSValueRef::IsJSPrimitiveInt(const EcmaVM *vm)
{
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, FATAL);
    return IsJSPrimitiveRef(vm) ? JSPrimitiveRef::Cast(obj->GetTaggedObject())->IsInt(thread) : false;
}

bool JSValueRef::IsJSPrimitiveBoolean(const EcmaVM *vm)
{
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, FATAL);
    return IsJSPrimitiveRef(vm) ? JSPrimitiveRef::Cast(obj->GetTaggedObject())->IsBoolean(thread) : false;
}

bool JSValueRef::IsJSPrimitiveString(const EcmaVM *vm)
{
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, FATAL);
    return IsJSPrimitiveRef(vm) ? JSPrimitiveRef::Cast(obj->GetTaggedObject())->IsString(thread) : false;
}

bool JSValueRef::IsJSPrimitiveSymbol(const EcmaVM *vm)
{
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, FATAL);
    return IsJSPrimitiveRef(vm) ? JSPrimitiveRef::Cast(obj->GetTaggedObject())->IsSymbol(thread) : false;
}

bool JSValueRef::IsGeneratorObject(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsGeneratorObject();
}

bool JSValueRef::IsModuleNamespaceObject(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsModuleNamespace();
}

bool JSValueRef::IsNativeModuleFailureInfoObject(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsNativeModuleFailureInfo();
}

bool JSValueRef::IsSharedArrayBuffer(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsSharedArrayBuffer();
}

bool JSValueRef::IsSendableArrayBuffer(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsSendableArrayBuffer();
}

bool JSValueRef::IsJSLocale(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSLocale();
}

bool JSValueRef::IsJSDateTimeFormat(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSDateTimeFormat();
}

bool JSValueRef::IsJSRelativeTimeFormat(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSRelativeTimeFormat();
}

bool JSValueRef::IsJSIntl(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSIntl();
}

bool JSValueRef::IsJSNumberFormat(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSNumberFormat();
}

bool JSValueRef::IsJSCollator(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSCollator();
}

bool JSValueRef::IsJSPluralRules(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSPluralRules();
}

bool JSValueRef::IsJSListFormat(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSListFormat();
}

bool JSValueRef::IsAsyncGeneratorObject(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsAsyncGeneratorObject();
}

bool JSValueRef::IsAsyncFunction(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAsyncFunction();
}

bool JSValueRef::IsConcurrentFunction(const EcmaVM *vm)
{
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> funcVal = JSNApiHelper::ToJSHandle(this);
    JSHandle<JSFunction> transFunc = JSHandle<JSFunction>::Cast(funcVal);
    return transFunc->GetFunctionKind(thread) == ecmascript::FunctionKind::CONCURRENT_FUNCTION;
}

bool JSValueRef::IsArgumentsObject(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsArguments();
}

bool JSValueRef::IsGeneratorFunction(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsGeneratorFunction();
}

bool JSValueRef::IsAsyncGeneratorFunction(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsAsyncGeneratorFunction();
}

bool JSValueRef::IsArrayList(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIArrayList();
}

bool JSValueRef::IsDeque(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIDeque();
}

bool JSValueRef::IsHashMap(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIHashMap();
}

bool JSValueRef::IsHashSet(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIHashSet();
}

bool JSValueRef::IsLightWeightMap(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPILightWeightMap();
}

bool JSValueRef::IsLightWeightSet(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPILightWeightSet();
}

bool JSValueRef::IsLinkedList(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPILinkedList();
}

bool JSValueRef::IsLinkedListIterator(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPILinkedListIterator();
}

bool JSValueRef::IsList(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIList();
}

bool JSValueRef::IsPlainArray(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIPlainArray();
}

bool JSValueRef::IsQueue(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIQueue();
}

bool JSValueRef::IsStack(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIStack();
}

bool JSValueRef::IsTreeMap(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPITreeMap();
}

bool JSValueRef::IsTreeSet(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPITreeSet();
}

bool JSValueRef::IsVector(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIVector();
}

bool JSValueRef::IsBitVector(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIBitVector();
}

bool JSValueRef::IsSendableObject(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return IsJSShared(vm) && IsObject(vm);
}

bool JSValueRef::IsJSShared(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSShared();
}

bool JSValueRef::IsSharedArray(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedArray();
}

bool JSValueRef::IsSharedTypedArray(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsSharedTypedArray();
}

bool JSValueRef::IsSharedSet(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedSet();
}

bool JSValueRef::IsSharedMap(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedMap();
}

bool JSValueRef::IsSharedMapIterator(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSSharedMapIterator();
}

bool JSValueRef::IsHeapObject()
{
    return JSNApiHelper::ToJSTaggedValue(this).IsHeapObject();
}

void *JSValueRef::GetNativePointerValue(const EcmaVM* vm, bool &isNativePointer)
{
    return GetNativePointerValueImpl(vm, isNativePointer);
}

void *JSValueRef::GetNativePointerWrapperDataValue(const EcmaVM* vm, bool &isNativePointer, bool &flag)
{
    return GetNativePointerWrapperDataValueImpl(vm, isNativePointer, flag);
}

// private
void *JSValueRef::GetNativePointerWrapperDataValueImpl(const EcmaVM* vm, bool &isNativePointer, bool &isWrapperData)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, nullptr);
    ecmascript::ThreadManagedScope managedScope(thread);
    if (!JSNApiHelper::ToJSTaggedValue(this).IsJSNativePointer()) {
        isNativePointer = false;
        return nullptr;
    }
    isNativePointer = true;
    JSHandle<JSTaggedValue> nativePointer = JSNApiHelper::ToJSHandle(this);
    ecmascript::NativeFlag nativeFlag = JSHandle<JSNativePointer>(nativePointer)->GetNativeFlag();
    if (nativeFlag != ecmascript::NativeFlag::WRAPPER_DATA) {
        isWrapperData = false;
        return nullptr;
    }
    isWrapperData = true;
    return JSHandle<JSNativePointer>(nativePointer)->GetExternalPointer();
}

// private
void *JSValueRef::GetNativePointerValueImpl(const EcmaVM* vm, bool &isNativePointer)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    if (!JSNApiHelper::ToJSTaggedValue(this).IsJSNativePointer()) {
        isNativePointer = false;
        return nullptr;
    }
    isNativePointer = true;
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, nullptr);
    JSHandle<JSTaggedValue> nativePointer = JSNApiHelper::ToJSHandle(this);
    return JSHandle<JSNativePointer>(nativePointer)->GetExternalPointer();
}

bool JSValueRef::IsDetachedArraybuffer(const EcmaVM *vm, bool &isArrayBuffer)
{
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    // arraybuffer is not shared. Do not need to switch state
    if (!IsArrayBuffer(vm)) {
        isArrayBuffer = false;
        return false;
    }
    isArrayBuffer = true;
    JSHandle<JSArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    return arrayBuffer->IsDetach(thread);
}

void JSValueRef::DetachedArraybuffer(const EcmaVM *vm, bool &isArrayBuffer)
{
    auto thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    if (IsArrayBuffer(vm)) {
        JSHandle<JSArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
        if (arrayBuffer->IsDetach(thread)) {
            return;
        }
        arrayBuffer->Detach(thread);
        isArrayBuffer = true;
    } else if (IsSendableArrayBuffer(vm)) {
        JSHandle<ecmascript::JSSendableArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
        if (arrayBuffer->IsDetach(thread)) {
            return;
        }
        arrayBuffer->Detach(thread);
        isArrayBuffer = true;
    } else {
        isArrayBuffer = false;
    }
}

void JSValueRef::GetDataViewInfo(const EcmaVM *vm,
                                 bool &isDataView,
                                 size_t *byteLength,
                                 void **data,
                                 JSValueRef **arrayBuffer,
                                 size_t *byteOffset)
{
    auto thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    if (!IsDataView(vm)) {
        isDataView = false;
        return;
    }
    isDataView = true;
    JSHandle<JSDataView> dataView(JSNApiHelper::ToJSHandle(this));

    if (byteLength) {
        *byteLength = dataView->GetByteLength();
    }
    if (data || arrayBuffer) {
        JSHandle<JSArrayBuffer> retArrayBuffer(thread, dataView->GetViewedArrayBuffer(thread));
        if (data) {
            JSTaggedValue bufferData = retArrayBuffer->GetArrayBufferData(thread);
            if (!bufferData.IsJSNativePointer()) {
                *data = nullptr;
            }
            *data = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
        }
        if (arrayBuffer) {
            *arrayBuffer = reinterpret_cast<JSValueRef*>(retArrayBuffer.GetAddress());
        }
    }
    if (byteOffset) {
        *byteOffset = dataView->GetByteOffset();
    }
}

// TryGetArrayLength is only for use by the Napi
void JSValueRef::TryGetArrayLength(const EcmaVM *vm, bool *isPendingException,
    bool *isArrayOrSharedArray, uint32_t *arrayLength)
{
    JSThread *thread = vm->GetJSThread();
    *isPendingException = thread->HasPendingException();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedValue thisValue = JSNApiHelper::ToJSTaggedValue(this);
    if (LIKELY(thisValue.IsJSArray())) {
        *isArrayOrSharedArray = true;
        *arrayLength = (*isPendingException) ?
            0 : JSArray::Cast(thisValue.GetTaggedObject())->GetArrayLength();
    } else if (thisValue.IsJSSharedArray()) {
        *isArrayOrSharedArray = true;
        *arrayLength = (*isPendingException) ?
            0 : ecmascript::JSSharedArray::Cast(thisValue.GetTaggedObject())->GetArrayLength();
    } else {
        *isArrayOrSharedArray = false;
    }
}

bool JSValueRef::IsJsGlobalEnv(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSGlobalEnv();
}

bool JSValueRef::IsSendable(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsSendable();
}

bool JSValueRef::IsWrappedNapiObject(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToJSTaggedValue(this).IsJSWrappedNapiObject();
}

// ---------------------------------- DataView -----------------------------------
Local<DataViewRef> DataViewRef::New(
    const EcmaVM *vm, Local<ArrayBufferRef> arrayBuffer, uint32_t byteOffset, uint32_t byteLength)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();

    JSHandle<JSArrayBuffer> buffer(JSNApiHelper::ToJSHandle(arrayBuffer));
    JSHandle<JSDataView> dataView = factory->NewJSDataView(buffer, byteOffset, byteLength);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<DataViewRef>(JSHandle<JSTaggedValue>(dataView));
}

Local<DataViewRef> DataViewRef::NewWithoutSwitchState(
    const EcmaVM *vm, Local<ArrayBufferRef> arrayBuffer, uint32_t byteOffset, uint32_t byteLength)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    JSHandle<JSArrayBuffer> buffer(JSNApiHelper::ToJSHandle(arrayBuffer));
    LOG_IF_SPECIAL(buffer, FATAL);
    if (byteOffset + byteLength > buffer->GetArrayBufferByteLength()) {
        return JSValueRef::Hole(vm);
    }
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSDataView> dataView = factory->NewJSDataView(buffer, byteOffset, byteLength);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<DataViewRef>(JSHandle<JSTaggedValue>(dataView));
}

uint32_t DataViewRef::ByteLength()
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSHandle<JSDataView> dataView(JSNApiHelper::ToJSHandle(this));
    return dataView->GetByteLength();
}

uint32_t DataViewRef::ByteOffset()
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSHandle<JSDataView> dataView(JSNApiHelper::ToJSHandle(this));
    return dataView->GetByteOffset();
}

Local<ArrayBufferRef> DataViewRef::GetArrayBuffer(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSDataView> dataView(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(dataView, FATAL);
    JSHandle<JSTaggedValue> arrayBuffer(thread, dataView->GetViewedArrayBuffer(thread));
    return JSNApiHelper::ToLocal<ArrayBufferRef>(arrayBuffer);
}
// ---------------------------------- DataView -----------------------------------

// ----------------------------------- PritimitiveRef ---------------------------------------
Local<JSValueRef> PrimitiveRef::GetValue(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    if (obj->IsJSPrimitiveRef()) {
        JSTaggedValue primitiveValue = JSPrimitiveRef::Cast(obj->GetTaggedObject())->GetValue(thread);
        JSHandle<JSTaggedValue> value(thread, primitiveValue);
        return JSNApiHelper::ToLocal<JSValueRef>(value);
    }
    return Local<JSValueRef>();
}

// ----------------------------------- NumberRef ---------------------------------------
Local<NumberRef> NumberRef::New(const EcmaVM *vm, double input)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    if (std::isnan(input)) {
        input = ecmascript::base::NAN_VALUE;
    }
    JSHandle<JSTaggedValue> number(thread, JSTaggedValue(input), true);
    return JSNApiHelper::ToLocal<NumberRef>(number);
}

Local<NumberRef> NumberRef::New(const EcmaVM *vm, int32_t input)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    JSHandle<JSTaggedValue> number(thread, JSTaggedValue(input), true);
    return JSNApiHelper::ToLocal<NumberRef>(number);
}

Local<NumberRef> NumberRef::New(const EcmaVM *vm, uint32_t input)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    JSHandle<JSTaggedValue> number(thread, JSTaggedValue(input), true);
    return JSNApiHelper::ToLocal<NumberRef>(number);
}

Local<NumberRef> NumberRef::New(const EcmaVM *vm, int64_t input)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    JSHandle<JSTaggedValue> number(thread, JSTaggedValue(input), true);
    return JSNApiHelper::ToLocal<NumberRef>(number);
}

double NumberRef::Value()
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    return JSTaggedNumber(JSNApiHelper::ToJSTaggedValue(this)).GetNumber();
}

// ----------------------------------- MapRef ---------------------------------------
Local<JSValueRef> MapRef::Get(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread,
                map->Get(thread, JSNApiHelper::ToJSTaggedValue(*key))));
}

Local<JSValueRef> MapRef::Get(const EcmaVM *vm, const char *utf8)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> key(factory->NewFromUtf8(utf8));
    auto result = JSHandle<JSTaggedValue>(thread, map->Get(thread, key.GetTaggedValue()));
    return JSNApiHelper::ToLocal<JSValueRef>(result);
}

void MapRef::Set(const EcmaVM *vm, Local<JSValueRef> key, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    JSMap::Set(thread, map, JSNApiHelper::ToJSHandle(key), JSNApiHelper::ToJSHandle(value));
}

void MapRef::Set(const EcmaVM *vm, const char *utf8, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> key(factory->NewFromUtf8(utf8));
    JSMap::Set(thread, map, key, JSNApiHelper::ToJSHandle(value));
}

bool MapRef::Has(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    bool result = map->Has(thread, JSNApiHelper::ToJSTaggedValue(*key));
    return result;
}

bool MapRef::Has(const EcmaVM *vm, const char *utf8)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> key(factory->NewFromUtf8(utf8));
    bool result = map->Has(thread, key.GetTaggedValue());
    return result;
}

void MapRef::Delete(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    JSMap::Delete(thread, map, JSNApiHelper::ToJSHandle(key));
}

void MapRef::Clear(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    JSMap::Clear(thread, map);
}

Local<MapRef> MapRef::New(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> constructor = env->GetBuiltinsMapFunction();
    JSHandle<JSMap> map =
        JSHandle<JSMap>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
    JSHandle<LinkedHashMap> hashMap = LinkedHashMap::Create(thread);
    map->SetLinkedMap(thread, hashMap);
    JSHandle<JSTaggedValue> mapTag = JSHandle<JSTaggedValue>::Cast(map);
    return JSNApiHelper::ToLocal<MapRef>(mapTag);
}

int32_t MapRef::GetSize(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    return map->GetSize(thread);
}

int32_t MapRef::GetTotalElements(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    return static_cast<int>((map->GetSize(thread))) +
           LinkedHashMap::Cast(map->GetLinkedMap(thread).GetTaggedObject())->NumberOfDeletedElements();
}

Local<JSValueRef> MapRef::GetKey(const EcmaVM *vm, int entry)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(map, FATAL);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, map->GetKey(thread, entry)));
}

Local<JSValueRef> MapRef::GetValue(const EcmaVM *vm, int entry)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> map(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(map, FATAL);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, map->GetValue(thread, entry)));
}

Local<MapIteratorRef> MapRef::GetEntries(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSTaggedValue> map(JSNApiHelper::ToJSHandle(this));
    JSHandle<JSTaggedValue> mapIter =
        ecmascript::JSMapIterator::CreateMapIterator(thread, map, IterationKind::KEY_AND_VALUE);
    return JSNApiHelper::ToLocal<MapIteratorRef>(mapIter);
}

Local<MapIteratorRef> MapRef::GetKeys(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSTaggedValue> map(JSNApiHelper::ToJSHandle(this));
    JSHandle<JSTaggedValue> mapIter = ecmascript::JSMapIterator::CreateMapIterator(thread, map, IterationKind::KEY);
    return JSNApiHelper::ToLocal<MapIteratorRef>(mapIter);
}

Local<MapIteratorRef> MapRef::GetValues(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSTaggedValue> map(JSNApiHelper::ToJSHandle(this));
    JSHandle<JSTaggedValue> mapIter = ecmascript::JSMapIterator::CreateMapIterator(thread, map, IterationKind::VALUE);
    return JSNApiHelper::ToLocal<MapIteratorRef>(mapIter);
}

// SendableMapRef
Local<SendableMapRef> SendableMapRef::New(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSTaggedValue> constructor = env->GetSBuiltininMapFunction();
    ASSERT(constructor->IsJSSharedFunction() && constructor.GetTaggedValue().IsInSharedHeap());
    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor);
    ASSERT(obj.GetTaggedValue().IsInSharedHeap());
    JSHandle<ecmascript::JSSharedMap> sharedMap = JSHandle<ecmascript::JSSharedMap>::Cast(obj);
    JSHandle<LinkedHashMap> linkedMap =
        LinkedHashMap::Create(thread, LinkedHashMap::MIN_CAPACITY, ecmascript::MemSpaceKind::SHARED);
    sharedMap->SetLinkedMap(thread, linkedMap);
    JSHandle<JSTaggedValue> sharedMapTag = JSHandle<JSTaggedValue>::Cast(sharedMap);
    return JSNApiHelper::ToLocal<SendableMapRef>(sharedMapTag);
}

Local<JSValueRef> SendableMapRef::Get(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread,
        ecmascript::JSSharedMap::Get(thread, map, JSNApiHelper::ToJSTaggedValue(*key))));
}

Local<JSValueRef> SendableMapRef::Get(const EcmaVM *vm, const char *utf8)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> key(factory->NewFromUtf8(utf8));
    auto result = JSHandle<JSTaggedValue>(thread, ecmascript::JSSharedMap::Get(thread, map, key.GetTaggedValue()));
    return JSNApiHelper::ToLocal<JSValueRef>(result);
}

void SendableMapRef::Set(const EcmaVM *vm, Local<JSValueRef> key, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    ecmascript::JSSharedMap::Set(thread, map, JSNApiHelper::ToJSHandle(key), JSNApiHelper::ToJSHandle(value));
}

void SendableMapRef::Set(const EcmaVM *vm, const char *utf8, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> key(factory->NewFromUtf8(utf8));
    ecmascript::JSSharedMap::Set(thread, map, key, JSNApiHelper::ToJSHandle(value));
}

bool SendableMapRef::Has(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    bool result = ecmascript::JSSharedMap::Has(thread, map, JSNApiHelper::ToJSTaggedValue(*key));
    return result;
}

bool SendableMapRef::Has(const EcmaVM *vm, const char *utf8)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> key(factory->NewFromUtf8(utf8));
    bool result = ecmascript::JSSharedMap::Has(thread, map, key.GetTaggedValue());
    return result;
}

void SendableMapRef::Delete(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    ecmascript::JSSharedMap::Delete(thread, map, JSNApiHelper::ToJSHandle(key));
}

void SendableMapRef::Clear(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    ecmascript::JSSharedMap::Clear(thread, map);
}

uint32_t SendableMapRef::GetSize(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    return ecmascript::JSSharedMap::GetSize(thread, map);
}

uint32_t SendableMapRef::GetTotalElements(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    return static_cast<int>(ecmascript::JSSharedMap::GetSize(thread, map)) +
           LinkedHashMap::Cast(map->GetLinkedMap(thread).GetTaggedObject())->NumberOfDeletedElements();
}

Local<JSValueRef> SendableMapRef::GetKey(const EcmaVM *vm, int entry)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(map, FATAL);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread,
        ecmascript::JSSharedMap::GetKey(thread, map, entry)));
}

Local<JSValueRef> SendableMapRef::GetValue(const EcmaVM *vm, int entry)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedMap> map(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(map, FATAL);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread,
        ecmascript::JSSharedMap::GetValue(thread, map, entry)));
}

Local<SendableMapIteratorRef> SendableMapRef::GetEntries(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSTaggedValue> map(JSNApiHelper::ToJSHandle(this));
    JSHandle<JSTaggedValue> sharedMapIter =
        ecmascript::JSSharedMapIterator::CreateMapIterator(thread, map, IterationKind::KEY_AND_VALUE);
    return JSNApiHelper::ToLocal<SendableMapIteratorRef>(sharedMapIter);
}

Local<SendableMapIteratorRef> SendableMapRef::GetKeys(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSTaggedValue> map(JSNApiHelper::ToJSHandle(this));
    JSHandle<JSTaggedValue> sharedMapIter =
        ecmascript::JSSharedMapIterator::CreateMapIterator(thread, map, IterationKind::KEY);
    return JSNApiHelper::ToLocal<SendableMapIteratorRef>(sharedMapIter);
}

Local<SendableMapIteratorRef> SendableMapRef::GetValues(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSTaggedValue> map(JSNApiHelper::ToJSHandle(this));
    JSHandle<JSTaggedValue> sharedMapIter =
        ecmascript::JSSharedMapIterator::CreateMapIterator(thread, map, IterationKind::VALUE);
    return JSNApiHelper::ToLocal<SendableMapIteratorRef>(sharedMapIter);
}

// SendableSetRef
Local<SendableSetRef> SendableSetRef::New(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSTaggedValue> constructor = env->GetSBuiltininSetFunction();
    ASSERT(constructor->IsJSSharedFunction() && constructor.GetTaggedValue().IsInSharedHeap());
    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor);
    ASSERT(obj.GetTaggedValue().IsInSharedHeap());
    JSHandle<ecmascript::JSSharedSet> set = JSHandle<ecmascript::JSSharedSet>::Cast(obj);
    JSHandle<LinkedHashSet> linkedSet =
        LinkedHashSet::Create(thread, LinkedHashSet::MIN_CAPACITY, ecmascript::MemSpaceKind::SHARED);
    set->SetLinkedSet(thread, linkedSet);
    JSHandle<JSTaggedValue> sharedSetTag = JSHandle<JSTaggedValue>::Cast(set);
    return JSNApiHelper::ToLocal<SendableSetRef>(sharedSetTag);
}

uint32_t SendableSetRef::GetSize(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedSet> set(JSNApiHelper::ToJSHandle(this));
    return ecmascript::JSSharedSet::GetSize(thread, set);
}

uint32_t SendableSetRef::GetTotalElements(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedSet> set(JSNApiHelper::ToJSHandle(this));
    return static_cast<int>(ecmascript::JSSharedSet::GetSize(thread, set)) +
           LinkedHashSet::Cast(set->GetLinkedSet(thread).GetTaggedObject())->NumberOfDeletedElements();
}

Local<JSValueRef> SendableSetRef::GetValue(const EcmaVM *vm, int entry)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedSet> set(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(set, FATAL);
    return JSNApiHelper::ToLocal<JSValueRef>(
        JSHandle<JSTaggedValue>(thread, ecmascript::JSSharedSet::GetValue(thread, set, entry)));
}

void SendableSetRef::Add(const EcmaVM *vm, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedSet> set(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(set, FATAL);
    ecmascript::JSSharedSet::Add(thread, set, JSNApiHelper::ToJSHandle(value));
}

// ----------------------------------- MapIteratorRef ---------------------------------------
int32_t MapIteratorRef::GetIndex()
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, -1);
    JSHandle<JSMapIterator> jsMapIter(JSNApiHelper::ToJSHandle(this));
    return jsMapIter->GetNextIndex();
}

Local<JSValueRef> MapIteratorRef::GetKind(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMapIterator> jsMapIter(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(jsMapIter, FATAL);
    IterationKind iterKind = jsMapIter->GetIterationKind();
    Local<JSValueRef> result;
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
    switch (iterKind) {
        case IterationKind::KEY:
            result = JSNApiHelper::ToLocal<JSValueRef>(globalConst->GetHandledKeysString());
            break;
        case IterationKind::VALUE:
            result = JSNApiHelper::ToLocal<JSValueRef>(globalConst->GetHandledValuesString());
            break;
        case IterationKind::KEY_AND_VALUE:
            result = JSNApiHelper::ToLocal<JSValueRef>(globalConst->GetHandledEntriesString());
            break;
        default:
            break;
    }
    return result;
}

Local<MapIteratorRef> MapIteratorRef::New(const EcmaVM *vm, Local<MapRef> map)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMap> jsMap(JSNApiHelper::ToJSHandle(map));
    IterationKind iterKind = IterationKind::KEY_AND_VALUE;
    JSHandle<JSTaggedValue> mapIteratorKeyAndValue =
        JSMapIterator::CreateMapIterator(vm->GetJSThread(), JSHandle<JSTaggedValue>::Cast(jsMap), iterKind);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<JSValueRef>(mapIteratorKeyAndValue);
}

ecmascript::EcmaRuntimeCallInfo *MapIteratorRef::GetEcmaRuntimeCallInfo(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, nullptr);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSMapIterator> jsMapIter(JSNApiHelper::ToJSHandle(this));
    JSHandle<LinkedHashMap> linkedHashMap(thread, jsMapIter->GetIteratedMap(thread));
    uint32_t size = linkedHashMap->GetLength();
    return ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread,
        JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
        JSHandle<JSTaggedValue>(thread, jsMapIter.GetTaggedValue()),
        JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()), size);
}

Local<ArrayRef> MapIteratorRef::Next(const EcmaVM *vm, ecmascript::EcmaRuntimeCallInfo *ecmaRuntimeCallInfo)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> nextTagValResult(thread, JSMapIterator::Next(ecmaRuntimeCallInfo));
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    JSHandle<JSTaggedValue> iteratorVal(thread,
        JSIterator::IteratorValue(thread, nextTagValResult).GetTaggedValue());
    return JSNApiHelper::ToLocal<ArrayRef>(iteratorVal);
}

Local<JSValueRef> MapIteratorRef::Next(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> mapIter(JSNApiHelper::ToJSHandle(this));
    auto result = JSMapIterator::NextInternal(thread, mapIter);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, result));
}

// SendableMapIteratorRef
Local<JSValueRef> SendableMapIteratorRef::Next(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> sharedMapIter(JSNApiHelper::ToJSHandle(this));
    auto result = ecmascript::JSSharedMapIterator::NextInternal(thread, sharedMapIter);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, result));
}

// ----------------------------------- SetIteratorRef ---------------------------------------
int32_t SetIteratorRef::GetIndex()
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, -1);
    JSHandle<JSSetIterator> jsSetIter(JSNApiHelper::ToJSHandle(this));
    return jsSetIter->GetNextIndex();
}

Local<JSValueRef> SetIteratorRef::GetKind(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSSetIterator> jsSetIter(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(jsSetIter, FATAL);
    IterationKind iterKind = jsSetIter->GetIterationKind();
    Local<JSValueRef> result;
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
    switch (iterKind) {
        case IterationKind::KEY:
            result = JSNApiHelper::ToLocal<JSValueRef>(globalConst->GetHandledKeysString());
            break;
        case IterationKind::VALUE:
            result = JSNApiHelper::ToLocal<JSValueRef>(globalConst->GetHandledValuesString());
            break;
        case IterationKind::KEY_AND_VALUE:
            result = JSNApiHelper::ToLocal<JSValueRef>(globalConst->GetHandledEntriesString());
            break;
        default:
            break;
    }
    return result;
}

Local<SetIteratorRef> SetIteratorRef::New(const EcmaVM *vm, Local<SetRef> set)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSSet> jsSet(JSNApiHelper::ToJSHandle(set));
    IterationKind iterKind = IterationKind::KEY_AND_VALUE;
    JSHandle<JSTaggedValue> setIteratorKeyAndValue =
        JSSetIterator::CreateSetIterator(thread, JSHandle<JSTaggedValue>::Cast(jsSet), iterKind);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<JSValueRef>(setIteratorKeyAndValue);
}

ecmascript::EcmaRuntimeCallInfo *SetIteratorRef::GetEcmaRuntimeCallInfo(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, nullptr);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSSetIterator> jsSetIter(JSNApiHelper::ToJSHandle(this));
    JSHandle<LinkedHashSet> linkedHashSet(thread, jsSetIter->GetIteratedSet(thread));
    uint32_t size = linkedHashSet->GetLength();
    return ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread,
        JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()),
        JSHandle<JSTaggedValue>(thread, jsSetIter.GetTaggedValue()),
        JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()), size);
}

Local<ArrayRef> SetIteratorRef::Next(const EcmaVM *vm, ecmascript::EcmaRuntimeCallInfo *ecmaRuntimeCallInfo)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> nextTagValResult(thread, JSSetIterator::Next(ecmaRuntimeCallInfo));
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    JSHandle<JSTaggedValue> iteratorVal(thread,
        JSIterator::IteratorValue(thread, nextTagValResult).GetTaggedValue());
    return JSNApiHelper::ToLocal<ArrayRef>(iteratorVal);
}

// ---------------------------------- Buffer -----------------------------------
Local<BufferRef> BufferRef::New(const EcmaVM *vm, const Local<JSValueRef> &context, int32_t length)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSArrayBuffer> arrayBuffer = JSHandle<JSArrayBuffer>::Cast(factory->NewJSArrayBuffer(length));
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSFunction> current =
        factory->NewJSFunction(env, reinterpret_cast<void *>(BufferRef::BufferToStringCallback));
    Local<StringRef> key = StringRef::NewFromUtf8(vm, "toString");
    JSHandle<JSTaggedValue> keyValue = JSNApiHelper::ToJSHandle(key);
    JSHandle<JSTaggedValue> currentTaggedValue(current);
    JSHandle<JSTaggedValue> obj(arrayBuffer);
    bool result = JSTaggedValue::SetProperty(thread, obj, keyValue, currentTaggedValue);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    if (!result) {
        LOG_ECMA(ERROR) << "SetProperty failed ! ! !";
    }
    current->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return JSNApiHelper::ToLocal<BufferRef>(obj);
}

Local<BufferRef> BufferRef::New(const EcmaVM *vm, int32_t length)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    Local<JSValueRef> context = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread->GetGlobalEnv()));
    return New(vm, context, length);
}

Local<BufferRef> BufferRef::New(const EcmaVM *vm, const Local<JSValueRef> &context, void *buffer,
    int32_t length, const NativePointerCallback &deleter, void *data)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSArrayBuffer> arrayBuffer = factory->NewJSArrayBuffer(buffer, length, deleter, data);

    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSFunction> current =
        factory->NewJSFunction(env, reinterpret_cast<void *>(BufferRef::BufferToStringCallback));
    Local<StringRef> key = StringRef::NewFromUtf8(vm, "toString");
    JSHandle<JSTaggedValue> keyValue = JSNApiHelper::ToJSHandle(key);
    JSHandle<JSTaggedValue> currentTaggedValue(current);
    JSHandle<JSTaggedValue> obj(arrayBuffer);
    bool result = JSTaggedValue::SetProperty(thread, obj, keyValue, currentTaggedValue);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    if (!result) {
        LOG_ECMA(ERROR) << "SetProperty failed ! ! !";
    }
    current->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return JSNApiHelper::ToLocal<ArrayBufferRef>(JSHandle<JSTaggedValue>(arrayBuffer));
}

Local<BufferRef> BufferRef::New(
    const EcmaVM *vm, void *buffer, int32_t length, const NativePointerCallback &deleter, void *data)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    Local<JSValueRef> context = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread->GetGlobalEnv()));
    return New(vm, context, buffer, length, deleter, data);
}

int32_t BufferRef::ByteLength(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<JSArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    return arrayBuffer->GetArrayBufferByteLength();
}

void *BufferRef::GetBuffer(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, nullptr);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<JSArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    JSTaggedValue bufferData = arrayBuffer->GetArrayBufferData(vm->GetJSThread());
    if (!bufferData.IsJSNativePointer()) {
        return nullptr;
    }
    return JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
}

JSTaggedValue BufferRef::BufferToStringCallback(ecmascript::EcmaRuntimeCallInfo *ecmaRuntimeCallInfo)
{
    JSThread *thread = ecmaRuntimeCallInfo->GetThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(thread->GetEcmaVM());
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSTaggedValue> arrayBuff = ecmaRuntimeCallInfo->GetThis();
    JSHandle<JSArrayBuffer> arrayBuffer(arrayBuff);

    uint32_t length = arrayBuffer->GetArrayBufferByteLength();
    JSTaggedValue data = arrayBuffer->GetArrayBufferData(thread);

    ecmascript::CVector<uint16_t> valueTable;
    valueTable.reserve(length);
    for (uint32_t i = 0; i < length; i++) {
        void* rawData = reinterpret_cast<void *>(
            ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer()) + i);
        uint8_t *block = reinterpret_cast<uint8_t *>(rawData);
        uint16_t nextCv = static_cast<uint16_t>(*block);
        valueTable.emplace_back(nextCv);
    }

    auto *char16tData0 = reinterpret_cast<const char16_t *>(valueTable.data());
    std::u16string u16str(char16tData0, length);

    const char16_t *constChar16tData = u16str.data();
    auto *char16tData = const_cast<char16_t *>(constChar16tData);
    auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
    uint32_t u16strSize = u16str.size();
    JSTaggedValue rString = factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
    JSHandle<EcmaString> StringHandle = JSTaggedValue::ToString(thread, rString);
    RETURN_VALUE_IF_ABRUPT(thread, JSTaggedValue::Undefined());
    return StringHandle.GetTaggedValue();
}

// ---------------------------------- Promise --------------------------------------
Local<PromiseCapabilityRef> PromiseCapabilityRef::New(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<GlobalEnv> globalEnv = vm->GetGlobalEnv();
    JSHandle<JSTaggedValue> constructor(globalEnv->GetPromiseFunction());
    JSHandle<JSTaggedValue> capability(JSPromise::NewPromiseCapability(thread, constructor));
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<PromiseCapabilityRef>(capability);
}

Local<PromiseRef> PromiseCapabilityRef::GetPromise(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<PromiseCapability> capacity(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(capacity, FATAL);
    return JSNApiHelper::ToLocal<PromiseRef>(JSHandle<JSTaggedValue>(thread, capacity->GetPromise(thread)));
}

bool PromiseCapabilityRef::Resolve(const EcmaVM *vm, uintptr_t value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    const GlobalEnvConstants *constants = thread->GlobalConstants();

    JSTaggedValue arg = *reinterpret_cast<JSTaggedValue *>(value);
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
    thread->CheckJSTaggedType(arg.GetRawData());
#endif
    JSHandle<PromiseCapability> capacity(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(capacity, FATAL);
    JSTaggedValue resolve = capacity->GetResolve(thread);
    JSTaggedValue undefined = constants->GetUndefined();
    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefined, undefined, 1);
    RETURN_VALUE_IF_ABRUPT(thread, false);
    info->SetCallArg(arg);
    JSFunction::Call(info);
    RETURN_VALUE_IF_ABRUPT(thread, false);

    EcmaVM::ConstCast(vm)->ExecutePromisePendingJob();
    RETURN_VALUE_IF_ABRUPT(thread, false);
    EcmaVM::ClearKeptObjects(thread);
    return true;
}

bool PromiseCapabilityRef::Resolve(const EcmaVM *vm, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    const GlobalEnvConstants *constants = thread->GlobalConstants();

    JSHandle<JSTaggedValue> arg = JSNApiHelper::ToJSHandle(value);
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
    thread->CheckJSTaggedType(arg.GetTaggedValue().GetRawData());
#endif
    JSHandle<PromiseCapability> capacity(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(capacity, FATAL);
    JSHandle<JSTaggedValue> resolve(thread, capacity->GetResolve(thread));
    JSHandle<JSTaggedValue> undefined(constants->GetHandledUndefined());
    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefined, undefined, 1);
    RETURN_VALUE_IF_ABRUPT(thread, false);
    info->SetCallArg(arg.GetTaggedValue());
    JSFunction::Call(info);
    RETURN_VALUE_IF_ABRUPT(thread, false);

    EcmaVM::ConstCast(vm)->ExecutePromisePendingJob();
    RETURN_VALUE_IF_ABRUPT(thread, false);
    EcmaVM::ClearKeptObjects(thread);
    return true;
}

bool PromiseCapabilityRef::Reject(const EcmaVM *vm, uintptr_t reason)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    const GlobalEnvConstants *constants = thread->GlobalConstants();

    JSTaggedValue arg = *reinterpret_cast<JSTaggedValue *>(reason);
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
    thread->CheckJSTaggedType(arg.GetRawData());
#endif
    JSHandle<PromiseCapability> capacity(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(capacity, FATAL);
    JSTaggedValue reject = capacity->GetReject(thread);
    JSTaggedValue undefined = constants->GetUndefined();

    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
    RETURN_VALUE_IF_ABRUPT(thread, false);
    info->SetCallArg(arg);
    JSFunction::Call(info);
    RETURN_VALUE_IF_ABRUPT(thread, false);

    EcmaVM::ConstCast(vm)->ExecutePromisePendingJob();
    RETURN_VALUE_IF_ABRUPT(thread, false);
    EcmaVM::ClearKeptObjects(thread);
    return true;
}

bool PromiseCapabilityRef::Reject(const EcmaVM *vm, Local<JSValueRef> reason)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    const GlobalEnvConstants *constants = thread->GlobalConstants();

    JSHandle<JSTaggedValue> arg = JSNApiHelper::ToJSHandle(reason);
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
    thread->CheckJSTaggedType(arg.GetTaggedValue().GetRawData());
#endif
    JSHandle<PromiseCapability> capacity(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(capacity, FATAL);
    JSHandle<JSTaggedValue> reject(thread, capacity->GetReject(thread));
    JSHandle<JSTaggedValue> undefined(constants->GetHandledUndefined());

    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
    RETURN_VALUE_IF_ABRUPT(thread, false);
    info->SetCallArg(arg.GetTaggedValue());
    JSFunction::Call(info);
    RETURN_VALUE_IF_ABRUPT(thread, false);

    EcmaVM::ConstCast(vm)->ExecutePromisePendingJob();
    RETURN_VALUE_IF_ABRUPT(thread, false);
    EcmaVM::ClearKeptObjects(thread);
    return true;
}

// ----------------------------------- SymbolRef -----------------------------------------
Local<SymbolRef> SymbolRef::New(const EcmaVM *vm, Local<StringRef> description)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSSymbol> symbol = factory->NewJSSymbol();
    if (!description.IsEmpty()) {
        JSTaggedValue desc = JSNApiHelper::ToJSTaggedValue(*description);
        symbol->SetDescription(thread, desc);
    }
    return JSNApiHelper::ToLocal<SymbolRef>(JSHandle<JSTaggedValue>(symbol));
}

Local<StringRef> SymbolRef::GetDescription(const EcmaVM *vm)
{
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedValue description =
        JSSymbol::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetDescription(thread);
    if (!description.IsString()) {
        auto constants = thread->GlobalConstants();
        return JSNApiHelper::ToLocal<StringRef>(constants->GetHandledEmptyString());
    }
    JSHandle<JSTaggedValue> descriptionHandle(thread, description);
    return JSNApiHelper::ToLocal<StringRef>(descriptionHandle);
}

// ----------------------------------- BooleanRef ---------------------------------------
Local<BooleanRef> BooleanRef::New(const EcmaVM *vm, bool value)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> boolean(thread, JSTaggedValue(value));
    return JSNApiHelper::ToLocal<BooleanRef>(boolean);
}

bool BooleanRef::Value()
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    return JSNApiHelper::ToJSTaggedValue(this).IsTrue();
}

// ----------------------------------- StringRef ----------------------------------------
Local<StringRef> StringRef::NewFromUtf8(const EcmaVM *vm, const char *utf8, int length)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    if (length < 0) {
        JSHandle<JSTaggedValue> current(factory->NewFromUtf8(utf8));
        return JSNApiHelper::ToLocal<StringRef>(current);
    }
    JSHandle<JSTaggedValue> current(factory->NewFromUtf8(reinterpret_cast<const uint8_t *>(utf8), length));
    return JSNApiHelper::ToLocal<StringRef>(current);
}

Local<StringRef> StringRef::NewFromUtf8Replacement(const EcmaVM *vm, const char *utf8, int length)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    if (length < 0) {
        JSHandle<JSTaggedValue> current(factory->NewFromUtf8Replacement(utf8));
        return JSNApiHelper::ToLocal<StringRef>(current);
    }
    JSHandle<JSTaggedValue> current(factory->NewFromUtf8Replacement(reinterpret_cast<const uint8_t *>(utf8), length));
    return JSNApiHelper::ToLocal<StringRef>(current);
}

Local<StringRef> StringRef::NewFromUtf8WithoutStringTable(const EcmaVM *vm, const char *utf8, int length)
{
    // This only supports for napi_create_string_utf8
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    if (length < 0) {
        JSHandle<JSTaggedValue> current(factory->NewFromUtf8WithoutStringTable(utf8));
        return JSNApiHelper::ToLocal<StringRef>(current);
    }
    JSHandle<JSTaggedValue> current(
        factory->NewFromUtf8WithoutStringTable(reinterpret_cast<const uint8_t *>(utf8), length));
    return JSNApiHelper::ToLocal<StringRef>(current);
}

Local<StringRef> StringRef::NewFromUtf8WithoutStringTableReplacement(const EcmaVM *vm, const char *utf8, int length)
{
    // This only supports for napi_create_string_utf8
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    if (length < 0) {
        JSHandle<JSTaggedValue> current(factory->NewFromUtf8WithoutStringTableReplacement(utf8));
        return JSNApiHelper::ToLocal<StringRef>(current);
    }
    JSHandle<JSTaggedValue> current(
        factory->NewFromUtf8WithoutStringTableReplacement(reinterpret_cast<const uint8_t *>(utf8), length));
    return JSNApiHelper::ToLocal<StringRef>(current);
}

Local<StringRef> StringRef::NewFromUtf16WithoutStringTable(const EcmaVM *vm, const char16_t *utf16, int length)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    if (length < 0) {
        JSHandle<JSTaggedValue> current(factory->NewFromUtf16WithoutStringTable(utf16));
        return JSNApiHelper::ToLocal<StringRef>(current);
    }
    JSHandle<JSTaggedValue> current(factory->NewFromUtf16WithoutStringTable(reinterpret_cast<const uint16_t *>(utf16),
                                                                            length));
    return JSNApiHelper::ToLocal<StringRef>(current);
}

Local<StringRef> StringRef::NewFromUtf16(const EcmaVM *vm, const char16_t *utf16, int length)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    if (length < 0) {
        JSHandle<JSTaggedValue> current(factory->NewFromUtf16(utf16));
        return JSNApiHelper::ToLocal<StringRef>(current);
    }
    JSHandle<JSTaggedValue> current(factory->NewFromUtf16(reinterpret_cast<const uint16_t *>(utf16), length));
    return JSNApiHelper::ToLocal<StringRef>(current);
}

Local<StringRef> StringRef::NewExternalFromUtf16(const EcmaVM *vm,
                                                 const char16_t *utf16,
                                                 int length,
                                                 ExternalStringFinalizerCallback callback,
                                                 void *hint)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<EcmaString> strHandle(vm->GetJSThread(),
        EcmaStringAccessor::CreateFromExternalResource(
            vm, const_cast<char16_t *>(utf16),
            length, false, static_cast<ecmascript::ExternalStringResourceCallback>(callback), hint));
    JSHandle<JSTaggedValue> current = JSHandle<JSTaggedValue>::Cast(strHandle);
    return JSNApiHelper::ToLocal<StringRef>(current);
}

Local<StringRef> StringRef::NewExternalFromAscii(const EcmaVM *vm,
                                                 const char *ascii,
                                                 int length,
                                                 ExternalStringFinalizerCallback callback,
                                                 void *hint)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<EcmaString> strHandle(vm->GetJSThread(),
        EcmaStringAccessor::CreateFromExternalResource(
            vm, const_cast<char *>(ascii),
            length, true, static_cast<ecmascript::ExternalStringResourceCallback>(callback), hint));
    JSHandle<JSTaggedValue> current = JSHandle<JSTaggedValue>::Cast(strHandle);
    return JSNApiHelper::ToLocal<StringRef>(current);
}

std::string StringRef::ToString(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, "");
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this)).ToStdString(vm->GetJSThread());
}

std::string StringRef::DebuggerToString(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, "");
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this)).DebuggerToStdString(vm->GetJSThread());
}

uint32_t StringRef::Length(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this)).GetLength();
}

bool StringRef::IsCompressed(const panda::EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, false);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return !EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this)).IsUtf16();
}

size_t StringRef::Utf8Length(const EcmaVM *vm, bool isGetBufferSize)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    auto thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<EcmaString> strHandle(thread, EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(this)));
    return EcmaStringAccessor(EcmaStringAccessor::Flatten(vm, strHandle)).GetUtf8Length(thread, isGetBufferSize);
}

uint32_t StringRef::WriteUtf8(const EcmaVM *vm, char *buffer, uint32_t length, bool isWriteBuffer)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    return EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this))
        .WriteToFlatUtf8(thread, reinterpret_cast<uint8_t *>(buffer), length, isWriteBuffer);
}

uint32_t StringRef::WriteUtf16(const EcmaVM *vm, char16_t *buffer, uint32_t length)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this))
        .WriteToUtf16(vm->GetJSThread(), reinterpret_cast<uint16_t *>(buffer), length);
}

uint32_t StringRef::WriteLatin1(const EcmaVM *vm, char *buffer, uint32_t length)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this))
        .WriteToOneByte(vm->GetJSThread(), reinterpret_cast<uint8_t *>(buffer), length);
}

uint32_t StringRef::WriteLatin1WithoutSwitchState(const EcmaVM *vm, char *buffer, uint32_t length)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    CROSS_THREAD_CHECK(vm);
    return EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this))
        .WriteToOneByte(vm->GetJSThread(), reinterpret_cast<uint8_t *>(buffer), length);
}

Local<StringRef> StringRef::GetNapiWrapperString(const EcmaVM *vm)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    JSHandle<JSTaggedValue> napiWrapperString = thread->GlobalConstants()->GetHandledNapiWrapperString();
    return JSNApiHelper::ToLocal<StringRef>(napiWrapperString);
}

const uint16_t *StringRef::GetBufferUtf16(const EcmaVM *vm, uint32_t &length)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    CROSS_THREAD_CHECK(vm);
    auto ecmaStringAccessor = EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this));
    if (!ecmaStringAccessor.IsLineString() || !ecmaStringAccessor.IsUtf16()) {
        LOG_ECMA(DEBUG) << "StringRef GetBufferUtf16 failed: Not LineString or not utf-16.";
        return nullptr;
    }
    length = this->Length(vm);
    return ecmaStringAccessor.GetDataUtf16();
}

Local<TypedArrayRef> StringRef::EncodeIntoUint8Array(const EcmaVM *vm)
{
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> string = JSNApiHelper::ToJSHandle(this);
    uint32_t length = this->Utf8Length(vm, true);
    // 1 because Utf8Length adds 1 for the return value
    if (length <= 1) {
        return Undefined(vm);
    }

    JSHandle<JSObject> obj =
        TypedArrayHelper::FastCreateTypedArray(thread, length - 1, DataViewType::UINT8);
    if (JSNApi::HasPendingException(vm)) {
        LOG_ECMA(ERROR) << "JSNapi EncodeIntoUint8Array: Create TypedArray failed";
        return Undefined(vm);
    }
    JSHandle<JSObject> arrayBuffer(thread, JSTypedArray::Cast(*obj)->GetViewedArrayBufferOrByteArray(thread));
    JSTaggedValue bufferData = JSHandle<JSArrayBuffer>::Cast(arrayBuffer)->GetArrayBufferData(thread);
    void *buffer = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();

    JSHandle<EcmaString> stringHandle = JSHandle<EcmaString>::Cast(string);
    EcmaStringAccessor(stringHandle).WriteToFlatUtf8(thread, reinterpret_cast<uint8_t *>(buffer), length - 1, true);
    JSHandle<JSTaggedValue> typedArrayTag = JSHandle<JSTaggedValue>::Cast(obj);
    return JSNApiHelper::ToLocal<TypedArrayRef>(typedArrayTag);
}

// ---------------------------------- PromiseRejectInfo ---------------------------------
PromiseRejectInfo::PromiseRejectInfo(Local<JSValueRef> promise, Local<JSValueRef> reason,
                                     PromiseRejectInfo::PROMISE_REJECTION_EVENT operation, void* data)
    : promise_(promise), reason_(reason), operation_(operation), data_(data) {}

Local<JSValueRef> PromiseRejectInfo::GetPromise() const
{
    return promise_;
}

Local<JSValueRef> PromiseRejectInfo::GetReason() const
{
    return reason_;
}

PromiseRejectInfo::PROMISE_REJECTION_EVENT PromiseRejectInfo::GetOperation() const
{
    return operation_;
}

void* PromiseRejectInfo::GetData() const
{
    return data_;
}

// ----------------------------------- BigIntRef ---------------------------------------
Local<BigIntRef> BigIntRef::New(const EcmaVM *vm, uint64_t input)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<BigInt> big = BigInt::Uint64ToBigInt(thread, input);
    JSHandle<JSTaggedValue> bigint = JSHandle<JSTaggedValue>::Cast(big);
    return JSNApiHelper::ToLocal<BigIntRef>(bigint);
}

Local<BigIntRef> BigIntRef::New(const EcmaVM *vm, int64_t input)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<BigInt> big = BigInt::Int64ToBigInt(thread, input);
    JSHandle<JSTaggedValue> bigint = JSHandle<JSTaggedValue>::Cast(big);
    return JSNApiHelper::ToLocal<BigIntRef>(bigint);
}

Local<JSValueRef> BigIntRef::CreateBigWords(const EcmaVM *vm, bool sign, uint32_t size, const uint64_t* words)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<BigInt> big = BigInt::CreateBigWords(thread, sign, size, words);
    JSHandle<JSTaggedValue> bigint = JSHandle<JSTaggedValue>::Cast(big);
    return JSNApiHelper::ToLocal<JSValueRef>(bigint);
}

void BigIntRef::BigIntToInt64(const EcmaVM *vm, int64_t *value, bool *lossless)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> bigintVal(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(bigintVal, ERROR);
    BigInt::BigIntToInt64(thread, bigintVal, value, lossless);
}

void BigIntRef::BigIntToUint64(const EcmaVM *vm, uint64_t *value, bool *lossless)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> bigintVal(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(bigintVal, ERROR);
    BigInt::BigIntToUint64(thread, bigintVal, value, lossless);
}

void BigIntRef::GetWordsArray(const EcmaVM *vm, bool* signBit, size_t wordCount, uint64_t* words)
{
    DCHECK_SPECIAL_VALUE(this);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<BigInt> bigintVal(JSNApiHelper::ToJSHandle(this));
    uint32_t len = bigintVal->GetLength();
    uint32_t count = 0;
    uint32_t index = 0;
    ASSERT(wordCount > 0);
    for (; index < wordCount - 1; ++index) {
        words[index] = static_cast<uint64_t>(bigintVal->GetDigit(count++));
        words[index] |= static_cast<uint64_t>(bigintVal->GetDigit(count++)) << 32; // 32 : int32_t bits
    }
    if (len % 2 == 0) { // 2 : len is odd or even
        words[index] = static_cast<uint64_t>(bigintVal->GetDigit(count++));
        words[index] |= static_cast<uint64_t>(bigintVal->GetDigit(count++)) << 32; // 32 : int32_t bits
    } else {
        words[index] = static_cast<uint64_t>(bigintVal->GetDigit(count++));
    }
    *signBit = bigintVal->GetSign();
}

uint32_t BigIntRef::GetWordsArraySize(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<BigInt> bigintVal(JSNApiHelper::ToJSHandle(this));
    uint32_t len = bigintVal->GetLength();
    return len % 2 != 0 ? len / 2 + 1 : len / 2; // 2 : len is odd or even
}

// ----------------------------------- HandleScope -------------------------------------
LocalScope::LocalScope(const EcmaVM *vm) : thread_(vm->GetJSThread())
{
#ifdef USE_HWASAN
    (const_cast<EcmaVM*>(vm))->AddHandleScopeDepth();
#endif
    // Only get handle ptr here. Do not need to swtich state.
    prevNext_ = vm->GetHandleScopeStorageNext();
    prevEnd_ = vm->GetHandleScopeStorageEnd();
    prevHandleStorageIndex_ = vm->GetCurrentHandleStorageIndex();

    prevPrimitiveNext_ = vm->GetPrimitiveScopeStorageNext();
    prevPrimitiveEnd_ = vm->GetPrimitiveScopeStorageEnd();
    prevPrimitiveStorageIndex_ = vm->GetCurrentPrimitiveStorageIndex();
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
    auto heapProfiler = reinterpret_cast<HeapProfiler *>(
        HeapProfilerInterface::GetInstance(const_cast<EcmaVM *>(vm)));
    heapProfiler->IncreaseScopeCount();
    heapProfiler->PushToActiveScopeStack(this, nullptr);
#endif

#if defined(ENABLE_HITRACE_LOCAL_HANDLE_DETECT)
    const_cast<EcmaVM *>(vm)->IncreaseOpenHandleScopes();
    scopeLevel_ = vm->GetOpenHandleScopes();
#endif // ENABLE_HITRACE_LOCAL_HANDLE_DETECT
}

LocalScope::LocalScope(const EcmaVM *vm, JSTaggedType value) : thread_(vm->GetJSThread())
{
    ecmascript::ThreadManagedScope managedScope(reinterpret_cast<JSThread *>(thread_));
#ifdef USE_HWASAN
    (const_cast<EcmaVM*>(vm))->AddHandleScopeDepth();
#endif
    // Simply reserve a slot on the handlescope. The escaped handle will still be retained in this slot.
    ecmascript::EcmaHandleScope::NewHandle(reinterpret_cast<JSThread *>(thread_), value);
    prevNext_ = vm->GetHandleScopeStorageNext();
    prevEnd_ = vm->GetHandleScopeStorageEnd();
    prevHandleStorageIndex_ = vm->GetCurrentHandleStorageIndex();

    prevPrimitiveNext_ = vm->GetPrimitiveScopeStorageNext();
    prevPrimitiveEnd_ = vm->GetPrimitiveScopeStorageEnd();
    prevPrimitiveStorageIndex_ = vm->GetCurrentPrimitiveStorageIndex();
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
    auto heapProfiler = reinterpret_cast<HeapProfiler *>(
        HeapProfilerInterface::GetInstance(const_cast<EcmaVM *>(vm)));
    heapProfiler->IncreaseScopeCount();
    heapProfiler->PushToActiveScopeStack(this, nullptr);
#endif

#if defined(ENABLE_HITRACE_LOCAL_HANDLE_DETECT)
    const_cast<EcmaVM *>(vm)->IncreaseOpenHandleScopes();
    scopeLevel_ = vm->GetOpenHandleScopes();
#endif // ENABLE_HITRACE_LOCAL_HANDLE_DETECT
}

LocalScope::~LocalScope()
{
    ecmascript::ThreadManagedScope managedScope(reinterpret_cast<JSThread *>(thread_));
    auto vm = reinterpret_cast<JSThread *>(thread_)->GetEcmaVM();
#ifdef USE_HWASAN
    vm->DelHandleScopeDepth();
#endif
    vm->SetHandleScopeStorageNext(static_cast<JSTaggedType *>(prevNext_));
    vm->SetPrimitiveScopeStorageNext(static_cast<JSTaggedType *>(prevPrimitiveNext_));

    if (vm->GetHandleScopeStorageEnd() != prevEnd_) {
        vm->SetHandleScopeStorageEnd(static_cast<JSTaggedType *>(prevEnd_));
        vm->ShrinkHandleStorage(prevHandleStorageIndex_);
    }

    if (vm->GetPrimitiveScopeStorageEnd() != prevPrimitiveEnd_) {
        vm->SetPrimitiveScopeStorageEnd(static_cast<JSTaggedType *>(prevPrimitiveEnd_));
        vm->ShrinkPrimitiveStorage(prevPrimitiveStorageIndex_);
    }
#if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
    auto heapProfiler = reinterpret_cast<HeapProfiler *>(HeapProfilerInterface::GetInstance(vm));
    heapProfiler->DecreaseScopeCount();
    heapProfiler->PopFromActiveScopeStack();
#endif

#if defined(ENABLE_HITRACE_LOCAL_HANDLE_DETECT)
    if (scopeLevel_ != vm->GetOpenHandleScopes()) {
#ifdef HOOK_ENABLE
        restraceExt(RES_ARK_LOCAL_HANDLE, (void *)this, sizeof(JSTaggedType), TAG_RES_ARK_LOCAL_HANDLE, true, false);
#endif // HOOK_ENABLE
    }
    const_cast<EcmaVM *>(vm)->DecreaseOpenHandleScopes();
#endif // ENABLE_HITRACE_LOCAL_HANDLE_DETECT
}

// ----------------------------------- EscapeLocalScope ------------------------------
EscapeLocalScope::EscapeLocalScope(const EcmaVM *vm) : LocalScope(vm, JSTaggedValue::Undefined().GetRawData())
{
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
    escapeHandle_ = ToUintPtr(vm->GetHandleScopeStorageNext() - 1);
}

// ----------------------------------- IntegerRef ---------------------------------------
Local<IntegerRef> IntegerRef::New(const EcmaVM *vm, int input)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> integer(thread, JSTaggedValue(input));
    return JSNApiHelper::ToLocal<IntegerRef>(integer);
}

Local<IntegerRef> IntegerRef::NewFromUnsigned(const EcmaVM *vm, unsigned int input)
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> integer(thread, JSTaggedValue(input));
    return JSNApiHelper::ToLocal<IntegerRef>(integer);
}

int IntegerRef::Value()
{
    // Omit exception check because ark calls here may not
    // cause side effect even pending exception exists.
    return JSNApiHelper::ToJSTaggedValue(this).GetInt();
}

// ----------------------------------- ObjectRef ----------------------------------------
Local<ObjectRef> ObjectRef::New(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> object(factory->CreateNapiObject());
    return JSNApiHelper::ToLocal<ObjectRef>(object);
}

Local<ObjectRef> ObjectRef::NewWrappedNapiObject(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> object(factory->CreateNapiObject(true));
    return JSNApiHelper::ToLocal<ObjectRef>(object);
}

uintptr_t ObjectRef::NewObject(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm,
        (thread->GlobalConstants()->GetHandledUndefined()).GetAddress());
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> object(vm->GetFactory()->CreateNapiObject());
    return object.GetAddress();
}

Local<ObjectRef> ObjectRef::NewS(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> globalEnv = vm->GetGlobalEnv();
    JSHandle<JSFunction> constructor(globalEnv->GetSObjectFunction());
    JSHandle<JSTaggedValue> object(factory->NewJSObjectByConstructor(constructor));
    return JSNApiHelper::ToLocal<ObjectRef>(object);
}

Local<ObjectRef> ObjectRef::NewWithProperties(const EcmaVM *vm, size_t propertyCount,
                                              const Local<JSValueRef> *keys,
                                              const PropertyAttribute *attributes)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSTaggedValue> obj;
    auto CreateObjImpl = [vm, thread, propertyCount, keys, attributes] (uintptr_t head) -> JSHandle<JSTaggedValue> {
        const PropertyDescriptor *desc = reinterpret_cast<const PropertyDescriptor *>(head);
        for (size_t i = 0; i < propertyCount; ++i) {
            const PropertyAttribute &attr = attributes[i];
            new (reinterpret_cast<void *>(head)) PropertyDescriptor(thread,
                                                                    JSNApiHelper::ToJSHandle(attr.GetValue(vm)),
                                                                    attr.IsWritable(), attr.IsEnumerable(),
                                                                    attr.IsConfigurable());
            head += sizeof(PropertyDescriptor);
        }
        ObjectFactory *factory = vm->GetFactory();
        return factory->CreateJSObjectWithProperties(propertyCount, keys, desc);
    };
    if (propertyCount <= MAX_PROPERTIES_ON_STACK) {
        char desc[sizeof(PropertyDescriptor) * MAX_PROPERTIES_ON_STACK];
        obj = CreateObjImpl(reinterpret_cast<uintptr_t>(desc));
    } else {
        void *desc = malloc(sizeof(PropertyDescriptor) * propertyCount);
        if (desc == nullptr) {
            LOG_FULL(FATAL) << "malloc failed in NewWithProperties";
        }
        obj = CreateObjImpl(reinterpret_cast<uintptr_t>(desc));
        free(desc);
    }
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return scope.Escape(JSNApiHelper::ToLocal<ObjectRef>(obj));
}

Local<ObjectRef> ObjectRef::NewSWithProperties(const EcmaVM *vm, SendablePropertiesInfo &info)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    ObjectFactory *factory = vm->GetFactory();
    std::vector<PropertyDescriptor> descs;
    JSNapiSendable::InitWithPropertiesInfo(thread, info, descs);
    auto obj = factory->CreateSObjectWithProperties(descs);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return scope.Escape(JSNApiHelper::ToLocal<ObjectRef>(obj));
}

Local<ObjectRef> ObjectRef::NewWithNamedProperties(const EcmaVM *vm, size_t propertyCount,
                                                   const char **keys, const Local<JSValueRef> *values)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> obj = factory->CreateJSObjectWithNamedProperties(propertyCount, keys, values);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return scope.Escape(JSNApiHelper::ToLocal<ObjectRef>(obj));
}

Local<ObjectRef> ObjectRef::CreateNativeModuleFailureInfo(const EcmaVM *vm, const std::string &failureInfo)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    if (EcmaVM::GetErrorInfoEnhance()) {
        JSHandle<NativeModuleFailureInfo> nativeModuleErrorFailureInfo =
            NativeModuleFailureInfo::CreateNativeModuleFailureInfo(vm, failureInfo);
        return JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>::Cast(nativeModuleErrorFailureInfo));
    }
    return JSValueRef::Undefined(vm);
}

Local<ObjectRef> ObjectRef::CreateAccessorData(const EcmaVM *vm,
                                               Local<FunctionRef> getter, Local<FunctionRef> setter)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> getterValue = JSNApiHelper::ToJSHandle(getter);
    JSHandle<JSTaggedValue> setterValue = JSNApiHelper::ToJSHandle(setter);
    JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewAccessorData();
    accessor->SetGetter(thread, getterValue);
    accessor->SetSetter(thread, setterValue);
    return JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>::Cast(accessor));
}

Local<ObjectRef> ObjectRef::CreateSendableAccessorData(const EcmaVM *vm,
                                                       Local<FunctionRef> getter,
                                                       Local<FunctionRef> setter)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> getterValue = JSNApiHelper::ToJSHandle(getter);
    JSHandle<JSTaggedValue> setterValue = JSNApiHelper::ToJSHandle(setter);
    JSHandle<AccessorData> accessor = thread->GetEcmaVM()->GetFactory()->NewSAccessorData();
    accessor->SetGetter(thread, getterValue);
    accessor->SetSetter(thread, setterValue);
    return JSNApiHelper::ToLocal<ObjectRef>(JSHandle<JSTaggedValue>::Cast(accessor));
}

bool ObjectRef::ConvertToNativeBindingObject(const EcmaVM *vm, Local<NativePointerRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(vm);
    JSHandle<JSTaggedValue> object = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSTaggedValue> keyValue = thread->GlobalConstants()->GetHandledNativeBindingSymbol();
    auto key = JSNApiHelper::ToLocal<JSValueRef>(keyValue);
    PropertyAttribute attr(Local<JSValueRef>(value), true, false, false);
    bool result = DefineProperty(vm, key, attr);
    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
    object->GetTaggedObject()->GetClass()->SetIsNativeBindingObject(true);
    return result;
}

Local<NativePointerRef> ObjectRef::GetNativeBindingPointer(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSTaggedValue> object = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSTaggedValue> keyValue = thread->GlobalConstants()->GetHandledNativeBindingSymbol();
    OperationResult ret = JSTaggedValue::GetProperty(thread, object, keyValue);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(ret.GetValue()));
}

bool ObjectRef::Set(const EcmaVM *vm, Local<JSValueRef> key, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(vm);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<JSTaggedValue> keyValue = JSNApiHelper::ToJSHandle(key);
    JSHandle<JSTaggedValue> valueValue = JSNApiHelper::ToJSHandle(value);
    if (!obj->IsHeapObject()) {
        return JSTaggedValue::SetProperty(thread, obj, keyValue, valueValue);
    }
    return ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(),
                                                      keyValue.GetTaggedValue(),
                                                      valueValue.GetTaggedValue());
}

bool ObjectRef::Set(const EcmaVM *vm, const char *utf8, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(vm);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> key(factory->NewFromUtf8(utf8));
    JSHandle<JSTaggedValue> val = JSNApiHelper::ToJSHandle(value);
    if (!obj->IsHeapObject()) {
        return JSTaggedValue::SetProperty(thread, obj, key, val);
    }
    JSTaggedValue res = ObjectFastOperator::TrySetPropertyByNameThroughCacheAtLocal(thread, obj, key, val);
    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
    if (!res.IsHole()) {
        return !res.IsException();
    }
    if (!JSNApi::KeyIsNumber(utf8)) {
        res = ObjectFastOperator::SetPropertyByName(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
                                                    val.GetTaggedValue());
        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
        if (!res.IsHole()) {
            return !res.IsException();
        }
        return JSTaggedValue::SetProperty(thread, obj, key, val, true);
    }
    return ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(),
                                                      key.GetTaggedValue(),
                                                      val.GetTaggedValue());
}

bool ObjectRef::SetWithoutSwitchState(const EcmaVM *vm, const char *utf8, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    [[maybe_unused]] LocalScope scope(vm);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> key(factory->NewFromUtf8(utf8));
    JSHandle<JSTaggedValue> val = JSNApiHelper::ToJSHandle(value);
    JSTaggedValue res = ObjectFastOperator::TrySetPropertyByNameThroughCacheAtLocal(thread, obj, key, val);
    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
    if (!res.IsHole()) {
        return !res.IsException();
    }
    if (!JSNApi::KeyIsNumber(utf8)) {
        res = ObjectFastOperator::SetPropertyByName(thread, obj.GetTaggedValue(), key.GetTaggedValue(),
                                                    val.GetTaggedValue());
        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
        if (!res.IsHole()) {
            return !res.IsException();
        }
        return JSTaggedValue::SetProperty(thread, obj, key, val, true);
    }
    return ObjectFastOperator::FastSetPropertyByValue(thread, obj.GetTaggedValue(),
                                                      key.GetTaggedValue(),
                                                      val.GetTaggedValue());
}

Local<JSValueRef> JSNApi::GetImplements(const EcmaVM *vm, Local<JSValueRef> instance)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedValue result;
    {
        LocalScope scope(vm);
        if (!instance->IsObject(vm)) {
            return JSValueRef::Undefined(vm);
        }
        JSHandle<JSTaggedValue> obj(JSNApiHelper::ToJSHandle(instance));
        OperationResult constructorValue = JSTaggedValue::GetProperty(
            thread, obj, thread->GlobalConstants()->GetHandledConstructorString());
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        result = constructorValue.GetValue().GetTaggedValue();
        if (!result.IsJSFunction()) {
            return JSValueRef::Undefined(vm);
        }
        JSHandle<JSTaggedValue> resultValue(thread, result);
        JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
        JSHandle<JSTaggedValue> interfaceTypeSymbol = env->GetInterfaceTypeSymbol();
        OperationResult interfaceTypeValue = JSObject::GetProperty(thread, resultValue, interfaceTypeSymbol);
        result = interfaceTypeValue.GetValue().GetTaggedValue();
        if (result.IsUndefined()) {
            return JSValueRef::Undefined(vm);
        }
    }
    JSHandle<JSTaggedValue> implementRet(thread, result);
    return JSNApiHelper::ToLocal<JSValueRef>(implementRet);
}

uintptr_t JSNApi::CreateStrongRef(const EcmaVM *vm, Local<JSValueRef> local)
{
    CROSS_THREAD_CHECK(vm);
    return GetGlobalHandleAddr(vm, reinterpret_cast<uintptr_t>(*local));
}

void JSNApi::DeleteStrongRef(const EcmaVM *vm, uintptr_t strongRef)
{
    CROSS_THREAD_CHECK(vm);
    DisposeGlobalHandleAddr(vm, strongRef);
}

void JSNApi::NotifyOnANR()
{
    EcmaVM::NotifyANR();
}

void JSNApi::SetTrackGlobalRef(EcmaVM *vm, bool enable)
{
    CROSS_THREAD_CHECK(vm);
    thread->SetTrackGlobalRef(enable);
}

bool JSNApi::IsTrackGlobalRefEnabled(const EcmaVM *vm)
{
    CROSS_THREAD_CHECK(vm);
    return thread->IsTrackGlobalRefEnabled();
}

void JSNApi::StoreGlobalRefMapping(EcmaVM *vm, uintptr_t slotAddr, void *ref)
{
    CROSS_THREAD_CHECK(vm);
    thread->StoreGlobalRefMapping(slotAddr, ref);
}

void JSNApi::EraseGlobalRefMapping(EcmaVM *vm, uintptr_t slotAddr)
{
    CROSS_THREAD_CHECK(vm);
    thread->EraseGlobalRefMapping(slotAddr);
}

void *JSNApi::FindGlobalRefMapping(const EcmaVM *vm, uintptr_t slotAddr)
{
    CROSS_THREAD_CHECK(vm);
    return thread->FindGlobalRefMapping(slotAddr);
}

bool ObjectRef::Set(const EcmaVM *vm, uint32_t key, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(vm);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<JSTaggedValue> valueValue = JSNApiHelper::ToJSHandle(value);
    if (!obj->IsHeapObject()) {
        return JSTaggedValue::SetProperty(thread, obj, key, valueValue);
    }
    return ObjectFastOperator::FastSetPropertyByIndex(thread, obj.GetTaggedValue(),
                                                      key, valueValue.GetTaggedValue());
}

bool ObjectRef::SetAccessorProperty(const EcmaVM *vm, Local<JSValueRef> key, Local<FunctionRef> getter,
    Local<FunctionRef> setter, PropertyAttribute attribute)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(vm);
    JSHandle<JSTaggedValue> getterValue = JSNApiHelper::ToJSHandle(getter);
    JSHandle<JSTaggedValue> setterValue = JSNApiHelper::ToJSHandle(setter);
    PropertyDescriptor desc(thread, attribute.IsWritable(), attribute.IsEnumerable(), attribute.IsConfigurable());
    desc.SetValue(JSNApiHelper::ToJSHandle(attribute.GetValue(vm)));
    desc.SetSetter(setterValue);
    desc.SetGetter(getterValue);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<JSTaggedValue> keyValue = JSNApiHelper::ToJSHandle(key);
    return JSTaggedValue::DefineOwnProperty(thread, obj, keyValue, desc);
}

Local<JSValueRef> ObjectRef::Get(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedValue result;
    {
        LocalScope scope(vm);
        JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
        LOG_IF_SPECIAL(obj, ERROR);
        JSHandle<JSTaggedValue> keyValue = JSNApiHelper::ToJSHandle(key);
        if (UNLIKELY(!obj->IsHeapObject())) {
            OperationResult ret = JSTaggedValue::GetProperty(thread, obj, keyValue);
            RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
            result = ret.GetValue().GetTaggedValue();
        } else {
            result = ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(),
                                                                keyValue.GetTaggedValue());
            RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        }
    }
    JSHandle<JSTaggedValue> resultValue(thread, result);
    return JSNApiHelper::ToLocal<JSValueRef>(resultValue);
}

Local<JSValueRef> ObjectRef::Get(const EcmaVM *vm, const char *utf8)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedValue result;
    {
        LocalScope scope(vm);
        JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
        LOG_IF_SPECIAL(obj, ERROR);
        ObjectFactory *factory = vm->GetFactory();
        JSHandle<JSTaggedValue> keyValue(factory->NewFromUtf8(utf8));
        if (UNLIKELY(!obj->IsHeapObject())) {
            OperationResult ret = JSTaggedValue::GetProperty(thread, obj, keyValue);
            RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
            result = ret.GetValue().GetTaggedValue();
        } else {
            result = ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(),
                                                                keyValue.GetTaggedValue());
            RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        }
    }
    JSHandle<JSTaggedValue> resultValue(thread, result);
    return JSNApiHelper::ToLocal<JSValueRef>(resultValue);
}

Local<JSValueRef> ObjectRef::Get(const EcmaVM *vm, int32_t key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedValue result;
    {
        LocalScope scope(vm);
        JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
        LOG_IF_SPECIAL(obj, ERROR);
        if (!obj->IsHeapObject()) {
            OperationResult ret = JSTaggedValue::GetProperty(thread, obj, key);
            RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
            result = ret.GetValue().GetTaggedValue();
        } else {
            result = ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), key);
            RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        }
    }
    JSHandle<JSTaggedValue> resultValue(thread, result);
    return JSNApiHelper::ToLocal<JSValueRef>(resultValue);
}

bool ObjectRef::GetOwnProperty(const EcmaVM *vm, Local<JSValueRef> key, PropertyAttribute &property)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<JSTaggedValue> keyValue = JSNApiHelper::ToJSHandle(key);
    PropertyDescriptor desc(thread);
    bool ret = JSTaggedValue::GetOwnProperty(thread, obj, keyValue, desc);
    if (!ret) {
        return false;
    }
    property.SetValue(JSNApiHelper::ToLocal<JSValueRef>(desc.GetValue()));
    if (desc.HasGetter()) {
        property.SetGetter(JSNApiHelper::ToLocal<JSValueRef>(desc.GetGetter()));
    }
    if (desc.HasSetter()) {
        property.SetSetter(JSNApiHelper::ToLocal<JSValueRef>(desc.GetSetter()));
    }
    if (desc.HasWritable()) {
        property.SetWritable(desc.IsWritable());
    }
    if (desc.HasEnumerable()) {
        property.SetEnumerable(desc.IsEnumerable());
    }
    if (desc.HasConfigurable()) {
        property.SetConfigurable(desc.IsConfigurable());
    }

    return true;
}

Local<ArrayRef> ObjectRef::GetOwnPropertyNames(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<TaggedArray> array(JSTaggedValue::GetOwnPropertyKeys(thread, obj));
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    JSHandle<JSTaggedValue> jsArray(JSArray::CreateArrayFromList(thread, array));
    return JSNApiHelper::ToLocal<ArrayRef>(jsArray);
}

Local<ArrayRef> ObjectRef::GetAllPropertyNames(const EcmaVM *vm, uint32_t filter)
{
    // This interface is only used by napi.
    // This interface currently only supports normal objects.
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> obj(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<TaggedArray> array(JSTaggedValue::GetAllPropertyKeys(thread, obj, filter));
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    JSHandle<JSTaggedValue> jsArray(JSArray::CreateArrayFromList(thread, array));
    return JSNApiHelper::ToLocal<ArrayRef>(jsArray);
}

Local<ArrayRef> ObjectRef::GetOwnEnumerablePropertyNames(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSObject> obj(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(obj, ERROR);
    JSHandle<TaggedArray> array(JSObject::EnumerableOwnNames(thread, obj));
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    JSHandle<JSTaggedValue> jsArray(JSArray::CreateArrayFromList(thread, array));
    return JSNApiHelper::ToLocal<ArrayRef>(jsArray);
}

Local<JSValueRef> ObjectRef::GetPrototype(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSObject> object(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<JSTaggedValue> prototype(thread, JSTaggedValue::GetPrototype(thread, JSHandle<JSTaggedValue>(object)));
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<JSValueRef>(prototype);
}

bool ObjectRef::SetPrototype(const EcmaVM *vm, Local<ObjectRef> prototype)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSObject> object(JSNApiHelper::ToJSHandle(this));
    JSHandle<JSObject> proto(JSNApiHelper::ToJSHandle(prototype));
    return JSTaggedValue::SetPrototype(thread, JSHandle<JSTaggedValue>(object), JSHandle<JSTaggedValue>(proto));
}

bool ObjectRef::DefineProperty(const EcmaVM *vm, Local<JSValueRef> key, PropertyAttribute attribute)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> object(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<JSTaggedValue> keyValue(JSNApiHelper::ToJSHandle(key));
    PropertyDescriptor desc(thread, attribute.IsWritable(), attribute.IsEnumerable(), attribute.IsConfigurable());
    desc.SetValue(JSNApiHelper::ToJSHandle(attribute.GetValue(vm)));
    return JSTaggedValue::DefinePropertyOrThrow(thread, object, keyValue, desc);
}

bool ObjectRef::Has(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> object(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<JSTaggedValue> keyValue(JSNApiHelper::ToJSHandle(key));
    return JSTaggedValue::HasProperty(thread, object, keyValue);
}

bool ObjectRef::Has(const EcmaVM *vm, uint32_t key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> object(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(object, ERROR);
    return JSTaggedValue::HasProperty(thread, object, key);
}

bool ObjectRef::HasOwnProperty(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> object(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<JSTaggedValue> keyValue(JSNApiHelper::ToJSHandle(key));
    return JSTaggedValue::HasOwnProperty(thread, object, keyValue);
}

bool ObjectRef::Delete(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> object(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<JSTaggedValue> keyValue(JSNApiHelper::ToJSHandle(key));
    return JSTaggedValue::DeleteProperty(thread, object, keyValue);
}

bool ObjectRef::Delete(const EcmaVM *vm, uint32_t key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> object(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue(key));
    return JSTaggedValue::DeleteProperty(thread, object, keyHandle);
}

Local<JSValueRef> ObjectRef::Freeze(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSTaggedValue> object = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<JSObject> obj(object);
    bool status = JSObject::SetIntegrityLevel(thread, obj, ecmascript::IntegrityLevel::FROZEN);
    if (JSNApi::HasPendingException(vm)) {
        JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(exception));
    }
    if (!status) {
        LOG_ECMA(ERROR) << "Freeze: freeze failed";
        Local<StringRef> message = StringRef::NewFromUtf8(vm, "Freeze: freeze failed");
        Local<JSValueRef> error = Exception::Error(vm, message);
        JSNApi::ThrowException(vm, error);
        JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(exception));
    }
    JSHandle<JSTaggedValue> resultValue(obj);
    return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(resultValue));
}

Local<JSValueRef> ObjectRef::Seal(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSTaggedValue> object = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(object, ERROR);
    JSHandle<JSObject> obj(object);
    bool status = JSObject::SetIntegrityLevel(thread, obj, ecmascript::IntegrityLevel::SEALED);
    if (JSNApi::HasPendingException(vm)) {
        JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(exception));
    }
    if (!status) {
        LOG_ECMA(ERROR) << "Seal: seal failed";
        Local<StringRef> message = StringRef::NewFromUtf8(vm, "Freeze: freeze failed");
        Local<JSValueRef> error = Exception::Error(vm, message);
        JSNApi::ThrowException(vm, error);
        JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(exception));
    }
    JSHandle<JSTaggedValue> resultValue(obj);
    return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(resultValue));
}

void ObjectRef::SetNativePointerFieldCount(const EcmaVM *vm, int32_t count)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    // ObjectRef::New may return special value if exception occurs.
    // So we need do special value check before use it.
    DCHECK_SPECIAL_VALUE(this);
    JSHandle<JSObject> object(JSNApiHelper::ToJSHandle(this));
    JSObject::SetNativePointerFieldCount(thread, object, count);
}

int32_t ObjectRef::GetNativePointerFieldCount(const EcmaVM *vm)
{
    // ObjectRef::New may return special value if exception occurs.
    // So we need do special value check before use it.
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<JSObject> object(JSNApiHelper::ToJSHandle(this));
    return object->GetNativePointerFieldCount(vm->GetJSThread());
}

void *ObjectRef::GetNativePointerField(const EcmaVM *vm, int32_t index)
{
    // ObjectRef::New may return special value if exception occurs.
    // So we need do special value check before use it.
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, nullptr);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<JSObject> object(JSNApiHelper::ToJSHandle(this));
    return object->GetNativePointerField(vm->GetJSThread(), index);
}

void ObjectRef::SetNativePointerField(const EcmaVM *vm, int32_t index, void *nativePointer,
    NativePointerCallback callBack, void *data, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    // ObjectRef::New may return special value if exception occurs.
    // So we need do special value check before use it.
    DCHECK_SPECIAL_VALUE(this);
    JSHandle<JSObject> object(JSNApiHelper::ToJSHandle(this));
    JSObject::SetNativePointerField(thread, object, index, nativePointer, callBack, data, nativeBindingsize);
}

void ObjectRef::SetConcurrentNativePointerField(const EcmaVM *vm, int32_t index, void *nativePointer,
    NativePointerCallback callBack, void *data, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    // ObjectRef::New may return special value if exception occurs.
    // So we need do special value check before use it.
    DCHECK_SPECIAL_VALUE(this);
    JSHandle<JSObject> object(JSNApiHelper::ToJSHandle(this));
    JSObject::SetNativePointerField(thread, object, index, nativePointer, callBack, data, nativeBindingsize,
                                    Concurrent::YES);
}

// -------------------------------- NativePointerRef ------------------------------------
Local<NativePointerRef> NativePointerRef::New(const EcmaVM *vm, void *nativePointer, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(nativePointer, nullptr, nullptr,
        false, nativeBindingsize);
    return JSNApiHelper::ToLocal<NativePointerRef>(JSHandle<JSTaggedValue>(obj));
}

Local<NativePointerRef> NativePointerRef::New(
    const EcmaVM *vm, void *nativePointer, NativePointerCallback callBack, void *data, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(nativePointer, callBack, data,
        false, nativeBindingsize);
    return JSNApiHelper::ToLocal<NativePointerRef>(JSHandle<JSTaggedValue>(obj));
}

Local<NativePointerRef> NativePointerRef::NewConcurrent(
    const EcmaVM *vm, void *nativePointer, NativePointerCallback callBack, void *data, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSNativePointer> obj =
        factory->NewJSNativePointer(nativePointer, callBack, data, false, nativeBindingsize, Concurrent::YES);
    return JSNApiHelper::ToLocal<NativePointerRef>(JSHandle<JSTaggedValue>(obj));
}

Local<NativePointerRef> NativePointerRef::NewSendable(
    const EcmaVM *vm, void *nativePointer, NativePointerCallback callBack, void *data, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSNativePointer> obj =
        factory->NewSJSNativePointer(nativePointer, callBack, data, false, nativeBindingsize);
    return JSNApiHelper::ToLocal<NativePointerRef>(JSHandle<JSTaggedValue>(obj));
}

void *NativePointerRef::Value()
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, nullptr);
    JSHandle<JSTaggedValue> nativePointer = JSNApiHelper::ToJSHandle(this);
    return JSHandle<JSNativePointer>(nativePointer)->GetExternalPointer();
}

Local<NativePointerRef> NativePointerRef::NewWrapperData(
    const EcmaVM *vm, void *nativePointer, NativePointerCallback callBack, void *data, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(nativePointer, callBack, data,
        false, nativeBindingsize, Concurrent::YES, ecmascript::NativeFlag::WRAPPER_DATA);
    return JSNApiHelper::ToLocal<NativePointerRef>(JSHandle<JSTaggedValue>(obj));
}

// ---------------------------------- Buffer -----------------------------------
Local<ArrayBufferRef> ArrayBufferRef::New(const EcmaVM *vm, int32_t length)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSArrayBuffer> arrayBuffer = factory->NewJSArrayBuffer(length);
    return JSNApiHelper::ToLocal<ArrayBufferRef>(JSHandle<JSTaggedValue>(arrayBuffer));
}

Local<ArrayBufferRef> ArrayBufferRef::New(
    const EcmaVM *vm, void *buffer, int32_t length, const NativePointerCallback &deleter, void *data)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSArrayBuffer> arrayBuffer = factory->NewJSArrayBuffer(buffer, length, deleter, data);
    return JSNApiHelper::ToLocal<ArrayBufferRef>(JSHandle<JSTaggedValue>(arrayBuffer));
}

int32_t ArrayBufferRef::ByteLength(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(arrayBuffer, FATAL);
    return arrayBuffer->GetArrayBufferByteLength();
}

void *ArrayBufferRef::GetBuffer(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, nullptr);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<JSArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    JSTaggedValue bufferData = arrayBuffer->GetArrayBufferData(vm->GetJSThread());
    if (!bufferData.IsJSNativePointer()) {
        return nullptr;
    }
    return JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
}

void *ArrayBufferRef::GetBufferAndLength(const EcmaVM *vm, int32_t *length)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, nullptr);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<JSArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    JSTaggedValue bufferData = arrayBuffer->GetArrayBufferData(vm->GetJSThread());
    void *data = nullptr;
    if (bufferData.IsJSNativePointer()) {
        data = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
    }
    LOG_IF_SPECIAL(arrayBuffer, FATAL);
    *length = arrayBuffer->GetArrayBufferByteLength();
    return data;
}

void ArrayBufferRef::Detach(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    // arraybuffer is not shared. Do not need to switch state
    JSHandle<JSArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    arrayBuffer->Detach(thread);
}

bool ArrayBufferRef::IsDetach(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, false);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    return arrayBuffer->IsDetach(thread);
}

Local<SendableArrayBufferRef> SendableArrayBufferRef::New(const EcmaVM *vm, int32_t length)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<ecmascript::JSSendableArrayBuffer> arrayBuffer = factory->NewJSSendableArrayBuffer(length);
    return JSNApiHelper::ToLocal<SendableArrayBufferRef>(JSHandle<JSTaggedValue>(arrayBuffer));
}

Local<SendableArrayBufferRef> SendableArrayBufferRef::New(
    const EcmaVM *vm, void *buffer, int32_t length, const NativePointerCallback &deleter, void *data)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<ecmascript::JSSendableArrayBuffer> arrayBuffer =
        factory->NewJSSendableArrayBuffer(buffer, length, deleter, data);
    return JSNApiHelper::ToLocal<SendableArrayBufferRef>(JSHandle<JSTaggedValue>(arrayBuffer));
}

int32_t SendableArrayBufferRef::ByteLength(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSendableArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(arrayBuffer, FATAL);
    return arrayBuffer->GetArrayBufferByteLength();
}

void SendableArrayBufferRef::Detach(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSendableArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    arrayBuffer->Detach(thread);
}

bool SendableArrayBufferRef::IsDetach(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, false);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSendableArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    return arrayBuffer->IsDetach(thread);
}

void *SendableArrayBufferRef::GetBuffer(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, nullptr);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<ecmascript::JSSendableArrayBuffer> arrayBuffer(JSNApiHelper::ToJSHandle(this));
    JSTaggedValue bufferData = arrayBuffer->GetArrayBufferData(vm->GetJSThread());
    if (!bufferData.IsJSNativePointer()) {
        return nullptr;
    }
    return JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
}

// ---------------------------------- DateRef -----------------------------------
Local<DateRef> DateRef::New(const EcmaVM *vm, double time)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> globalEnv = vm->GetGlobalEnv();
    JSHandle<JSFunction> dateFunction(globalEnv->GetDateFunction());
    JSHandle<JSDate> dateObject(factory->NewJSObjectByConstructor(dateFunction));
    dateObject->SetTimeValue(thread, JSTaggedValue(time));
    return JSNApiHelper::ToLocal<DateRef>(JSHandle<JSTaggedValue>(dateObject));
}

Local<StringRef> DateRef::ToString(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSDate> date(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(date, ERROR);
    JSTaggedValue dateStr = date->ToString(thread);
    if (!dateStr.IsString()) {
        auto constants = thread->GlobalConstants();
        return JSNApiHelper::ToLocal<StringRef>(constants->GetHandledEmptyString());
    }
    JSHandle<JSTaggedValue> dateStrHandle(thread, dateStr);
    return JSNApiHelper::ToLocal<StringRef>(dateStrHandle);
}

double DateRef::GetTime(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0.0);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    JSHandle<JSDate> date(JSNApiHelper::ToJSHandle(this));
    if (!date->IsDate()) {
        LOG_ECMA(ERROR) << "Not a Date Object";
    }
    return date->GetTimeValue(vm->GetJSThread()).GetDouble();
}

// ---------------------------------- TypedArray -----------------------------------
uint32_t TypedArrayRef::ByteLength(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTypedArray> typedArray(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(typedArray, FATAL);
    return typedArray->GetByteLength();
}

uint32_t TypedArrayRef::ByteOffset(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTypedArray> typedArray(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(typedArray, FATAL);
    return typedArray->GetByteOffset();
}

uint32_t TypedArrayRef::ArrayLength(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTypedArray> typedArray(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(typedArray, FATAL);
    return typedArray->GetArrayLength();
}

Local<ArrayBufferRef> TypedArrayRef::GetArrayBuffer(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTypedArray> typedArray(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(typedArray, ERROR);
    JSHandle<JSTaggedValue> arrayBuffer(thread, JSTypedArray::GetOffHeapBuffer(thread, typedArray));
    return JSNApiHelper::ToLocal<ArrayBufferRef>(arrayBuffer);
}

uint32_t SendableTypedArrayRef::ByteLength(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedTypedArray> typedArray(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(typedArray, FATAL);
    return typedArray->GetByteLength();
}

uint32_t SendableTypedArrayRef::ByteOffset(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedTypedArray> typedArray(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(typedArray, FATAL);
    return typedArray->GetByteOffset();
}

uint32_t SendableTypedArrayRef::ArrayLength(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedTypedArray> typedArray(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(typedArray, FATAL);
    return typedArray->GetArrayLength();
}

Local<SendableArrayBufferRef> SendableTypedArrayRef::GetArrayBuffer(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<ecmascript::JSSharedTypedArray> typedArray(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(typedArray, ERROR);
    JSHandle<JSTaggedValue> arrayBuffer(thread,
        ecmascript::JSSharedTypedArray::GetSharedOffHeapBuffer(thread, typedArray));
    return JSNApiHelper::ToLocal<SendableArrayBufferRef>(arrayBuffer);
}

// ----------------------------------- FunctionRef --------------------------------------
Local<FunctionRef> FunctionRef::New(EcmaVM *vm, const Local<JSValueRef> &context, FunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
#if defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSFunction> current(factory->NewNormalJSApiFunction(env,
        reinterpret_cast<void *>(Callback::RegisterCallback)));
#else // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSFunction> current(factory->NewJSFunction(env, reinterpret_cast<void *>(Callback::RegisterCallback)));
#endif // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast<void *>(nativeFunc),
                                     deleter, data, nativeBindingsize);
    current->SetCallNapi(callNapi);
    current->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}

Local<FunctionRef> FunctionRef::New(EcmaVM *vm, FunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    Local<JSValueRef> context = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread->GetGlobalEnv()));
    return New(vm, context, nativeFunc, deleter, data, callNapi, nativeBindingsize);
}

Local<FunctionRef> FunctionRef::NewConcurrent(EcmaVM *vm, const Local<JSValueRef> &context, FunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
#if defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSFunction> current(factory->NewNormalJSApiFunction(env,
        reinterpret_cast<void *>(Callback::RegisterCallback)));
#else // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSFunction> current(factory->NewJSFunction(env, reinterpret_cast<void *>(Callback::RegisterCallback)));
#endif // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast<void *>(nativeFunc), deleter,
                                     data, nativeBindingsize, Concurrent::YES);
    current->SetCallNapi(callNapi);
    current->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}

Local<FunctionRef> FunctionRef::NewConcurrent(EcmaVM *vm, FunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    Local<JSValueRef> context = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread->GetGlobalEnv()));
    return NewConcurrent(vm, context, nativeFunc, deleter, data, callNapi, nativeBindingsize);
}

Local<FunctionRef> FunctionRef::New(EcmaVM *vm, const Local<JSValueRef> &context, InternalFunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
#if defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSFunction> current(factory->NewNormalJSApiFunction(env, reinterpret_cast<void *>(nativeFunc)));
#else // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSFunction> current(factory->NewJSFunction(env, reinterpret_cast<void *>(nativeFunc)));
#endif // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSFunction::SetFunctionExtraInfo(thread, current, nullptr, deleter, data, nativeBindingsize);
    current->SetCallNapi(callNapi);
    current->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}

Local<FunctionRef> FunctionRef::New(EcmaVM *vm, InternalFunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    Local<JSValueRef> context = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread->GetGlobalEnv()));
    return New(vm, context, nativeFunc, deleter, data, callNapi, nativeBindingsize);
}

Local<FunctionRef> FunctionRef::NewSendable(EcmaVM *vm,
                                            InternalFunctionCallback nativeFunc,
                                            NativePointerCallback deleter,
                                            void *data,
                                            bool callNapi,
                                            size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSFunction> current(factory->NewSFunction(env, reinterpret_cast<void *>(nativeFunc)));
    JSFunction::SetSFunctionExtraInfo(thread, current, nullptr, deleter, data, nativeBindingsize);
    current->SetCallNapi(callNapi);
    // for naive SendableFunction, it's lexicalEnv should be undefined
    ASSERT(current->GetLexicalEnv(thread) == JSTaggedValue::Undefined());
    return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}

Local<FunctionRef> FunctionRef::NewConcurrent(EcmaVM *vm, const Local<JSValueRef> &context,
    InternalFunctionCallback nativeFunc, NativePointerCallback deleter, void *data, bool callNapi,
    size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSFunction> current(factory->NewJSFunction(env, reinterpret_cast<void *>(nativeFunc)));
    JSFunction::SetFunctionExtraInfo(thread, current, nullptr, deleter, data, nativeBindingsize, Concurrent::YES);
    current->SetCallNapi(callNapi);
    current->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
}

Local<FunctionRef> FunctionRef::NewConcurrent(EcmaVM *vm, InternalFunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    Local<JSValueRef> context = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread->GetGlobalEnv()));
    return NewConcurrent(vm, context, nativeFunc, deleter, data, callNapi, nativeBindingsize);
}

Local<FunctionRef> FunctionRef::NewConcurrentWithName(EcmaVM *vm, const Local<JSValueRef> &context,
                                                      InternalFunctionCallback nativeFunc,
                                                      NativePointerCallback deleter, const char *name, void *data,
                                                      bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);

    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> functionName(factory->NewFromUtf8(name));
    PropertyDescriptor nameDesc(thread, functionName, false, false, true);
    ecmascript::PropertyAttributes attr(nameDesc);
    attr.SetIsInlinedProps(true);
    attr.SetOffset(0);
    attr.SetRepresentation(ecmascript::Representation::TAGGED);

    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSHClass> hclass = JSHandle<JSHClass>::Cast(env->GetNormalFunctionClass());
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
    ecmascript::Representation rep =
        ecmascript::PropertyAttributes::TranslateToRep(nameDesc.GetValue().GetTaggedValue());
    hclass = JSHClass::SetPropertyOfObjHClass<true>(thread, hclass, globalConst->GetHandledNameString(), attr, rep,
                                                    true, JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS);
    JSHandle<JSFunction> func = factory->NewNativeFunctionByHClass(hclass, reinterpret_cast<void *>(nativeFunc),
                                                                   ecmascript::FunctionKind::NORMAL_FUNCTION);
    func->SetPropertyInlinedProps<true>(thread, 0, nameDesc.GetValue().GetTaggedValue());
    JSFunction::SetFunctionExtraInfo(thread, func, nullptr, deleter, data, nativeBindingsize, Concurrent::YES);
    ASSERT_PRINT(func->IsExtensible(), "Function must be extensible");

    func->SetCallNapi(callNapi);
    func->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(func));
}

Local<FunctionRef> FunctionRef::NewClassFunction(EcmaVM *vm, const Local<JSValueRef> &context,
    FunctionCallback nativeFunc, NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
#if defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSHClass> hclass = JSHandle<JSHClass>::Cast(env->GetApiFunctionClassWithoutName());
    JSHandle<JSFunction> current =
        factory->NewConstructorJSApiFunctionByHClass(reinterpret_cast<void *>(Callback::RegisterCallback), hclass);
#else // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSHClass> hclass = JSHandle<JSHClass>::Cast(env->GetFunctionClassWithoutName());
    JSHandle<JSFunction> current =
        factory->NewJSFunctionByHClass(reinterpret_cast<void *>(Callback::RegisterCallback),
        hclass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR);
#endif // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSFunction::InitClassFunction(thread, current, callNapi);
    JSFunction::SetFunctionExtraInfo(thread, current, reinterpret_cast<void *>(nativeFunc),
                                     deleter, data, nativeBindingsize);
    Local<FunctionRef> result = JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
    current->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return scope.Escape(result);
}

Local<FunctionRef> FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    Local<JSValueRef> context = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread->GetGlobalEnv()));
    return NewClassFunction(vm, context, nativeFunc, deleter, data, callNapi, nativeBindingsize);
}

Local<FunctionRef> FunctionRef::NewConcurrentClassFunction(EcmaVM *vm, const Local<JSValueRef> &context,
    InternalFunctionCallback nativeFunc, NativePointerCallback deleter, void *data, bool callNapi,
    size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
#if defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSHClass> hclass = JSHandle<JSHClass>::Cast(env->GetApiFunctionClassWithoutName());
    JSHandle<JSFunction> current =
        factory->NewConstructorJSApiFunctionByHClass(reinterpret_cast<void *>(nativeFunc), hclass);
#else // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSHClass> hclass = JSHandle<JSHClass>::Cast(env->GetFunctionClassWithoutName());
    JSHandle<JSFunction> current =
        factory->NewJSFunctionByHClass(reinterpret_cast<void *>(nativeFunc),
        hclass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR);
#endif // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSFunction::InitClassFunction(thread, current, callNapi);
    JSFunction::SetFunctionExtraInfo(thread, current, nullptr, deleter, data, nativeBindingsize, Concurrent::YES);
    Local<FunctionRef> result = JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
    current->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return scope.Escape(result);
}

Local<FunctionRef> FunctionRef::NewConcurrentClassFunctionWithName(
    const EcmaVM *vm, const Local<JSValueRef> &context, InternalFunctionCallback nativeFunc,
    NativePointerCallback deleter, const char *name, void *data, bool callNapi, size_t propertyCount,
    size_t staticPropCount, Local<panda::JSValueRef> *keys, PropertyAttribute *attrs, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();

    JSHandle<JSFunction> classFunc;
    if (propertyCount <= MAX_PROPERTIES_ON_STACK) {
        char descs[sizeof(PropertyDescriptor) * MAX_PROPERTIES_ON_STACK];
        classFunc = JSNApiClassCreationHelper::CreateClassFuncWithProperties(
            thread, name, nativeFunc, callNapi, propertyCount, staticPropCount, keys, attrs,
            reinterpret_cast<PropertyDescriptor *>(&descs[0]));
    } else {
        PropertyDescriptor *descs =
            reinterpret_cast<PropertyDescriptor *>(malloc(sizeof(PropertyDescriptor) * propertyCount));
        if (descs != nullptr) {
            classFunc = JSNApiClassCreationHelper::CreateClassFuncWithProperties(
                thread, name, nativeFunc, callNapi, propertyCount, staticPropCount, keys, attrs, descs);
            free(descs);
        } else {
            LOG_FULL(FATAL) << "malloc failed in NewConcurrentClassFunctionWithName";
        }
    }
    classFunc->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    JSFunction::SetFunctionExtraInfo(thread, classFunc, nullptr, deleter, data, nativeBindingsize, Concurrent::YES);
    Local<FunctionRef> result = JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(classFunc));
    return scope.Escape(result);
}

Local<FunctionRef> FunctionRef::NewConcurrentClassFunction(EcmaVM *vm, InternalFunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    Local<JSValueRef> context = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread->GetGlobalEnv()));
    return NewConcurrentClassFunction(vm, context, nativeFunc, deleter, data, callNapi, nativeBindingsize);
}

Local<FunctionRef> FunctionRef::NewClassFunction(EcmaVM *vm, const Local<JSValueRef> &context,
    InternalFunctionCallback nativeFunc, NativePointerCallback deleter, void *data, bool callNapi,
    size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
#if defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSHClass> hclass = JSHandle<JSHClass>::Cast(env->GetApiFunctionClassWithoutName());
    JSHandle<JSFunction> current =
        factory->NewConstructorJSApiFunctionByHClass(reinterpret_cast<void *>(nativeFunc), hclass);
#else // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSHandle<JSHClass> hclass = JSHandle<JSHClass>::Cast(env->GetFunctionClassWithoutName());
    JSHandle<JSFunction> current =
        factory->NewJSFunctionByHClass(reinterpret_cast<void *>(nativeFunc),
        hclass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR);
#endif // defined(ENABLE_API_FUNCTION_OPTIMIZATION) && ENABLE_MEMORY_OPTIMIZATION
    JSFunction::InitClassFunction(thread, current, callNapi);
    JSFunction::SetFunctionExtraInfo(thread, current, nullptr, deleter, data, nativeBindingsize);
    Local<FunctionRef> result = JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(current));
    current->SetLexicalEnv(thread, JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context)));
    return scope.Escape(result);
}

Local<FunctionRef> FunctionRef::NewClassFunction(EcmaVM *vm, InternalFunctionCallback nativeFunc,
    NativePointerCallback deleter, void *data, bool callNapi, size_t nativeBindingsize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    Local<JSValueRef> context = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread->GetGlobalEnv()));
    return NewClassFunction(vm, context, nativeFunc, deleter, data, callNapi, nativeBindingsize);
}

Local<FunctionRef> FunctionRef::NewSendableClassFunction(const EcmaVM *vm,
                                                         InternalFunctionCallback nativeFunc,
                                                         NativePointerCallback deleter,
                                                         void *data,
                                                         Local<StringRef> name,
                                                         SendablePropertiesInfos &infos,
                                                         Local<FunctionRef> parent,
                                                         bool callNapi,
                                                         size_t nativeBindingSize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    ObjectFactory *factory = vm->GetFactory();

    JSNapiSendable sendable(thread, infos, name);
    JSHandle<JSHClass> prototypeHClass = JSHClass::CreateSPrototypeHClass(thread, sendable.GetNonStaticDescs());
    JSHandle<JSObject> prototype = factory->NewSharedOldSpaceJSObject(prototypeHClass);
    JSHandle<JSHClass> constructorHClass = JSHClass::CreateSConstructorHClass(thread, sendable.GetStaticDescs());
    JSHandle<JSFunction> constructor = factory->NewSFunctionByHClass(
        reinterpret_cast<void *>(nativeFunc), constructorHClass, ecmascript::FunctionKind::CLASS_CONSTRUCTOR);

    sendable.SetSConstructor(constructor);
    JSObject::SetSProperties(thread, prototype, sendable.GetNonStaticDescs());
    JSObject::SetSProperties(thread, JSHandle<JSObject>::Cast(constructor), sendable.GetStaticDescs());

    if (parent->IsHole()) {
        JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
        prototypeHClass->SetPrototype(thread, env->GetSObjectFunctionPrototype());
        constructorHClass->SetPrototype(thread, env->GetSFunctionPrototype());
    } else if (parent->IsNull()) {
        JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
        prototypeHClass->SetPrototype(thread, JSTaggedValue::Null());
        constructorHClass->SetPrototype(thread, env->GetSFunctionPrototype());
    } else {
        auto parentPrototype = parent->GetFunctionPrototype(vm);
        prototypeHClass->SetPrototype(thread, JSNApiHelper::ToJSHandle(parentPrototype));
        constructorHClass->SetPrototype(thread, JSNApiHelper::ToJSHandle(parent));
    }
    prototypeHClass->SetExtensible(false);
    constructor->SetHomeObject(thread, prototype);
    constructor->SetProtoOrHClass(thread, prototype);
    // for naive SendableClassFunction, it doesn't need to store itself to lexicalEnv
    ASSERT(constructor->GetLexicalEnv(thread) == JSTaggedValue::Undefined());
    constructor->SetCallNapi(callNapi);
    JSFunction::SetSFunctionExtraInfo(thread, constructor, nullptr, deleter, data, nativeBindingSize);

    JSHClass *parentIHClass{nullptr};
    if (!parent->IsHole() && !parent->IsNull()) {
        JSHandle<JSFunction> parentHandle(JSNApiHelper::ToJSHandle(parent));
        parentIHClass = reinterpret_cast<JSHClass *>(parentHandle->GetProtoOrHClass(thread).GetTaggedObject());
    }
    JSHandle<JSHClass> iHClass = JSHClass::CreateSHClass(thread, sendable.GetInstanceDescs(), parentIHClass);
    iHClass->SetPrototype(thread, JSHandle<JSTaggedValue>(prototype));
    iHClass->SetExtensible(false);
    constructor->SetProtoOrHClass(thread, iHClass);
    constructorHClass->SetExtensible(false);

    Local<FunctionRef> result = JSNApiHelper::ToLocal<FunctionRef>(JSHandle<JSTaggedValue>(constructor));
    return scope.Escape(result);
}

Local<JSValueRef> FunctionRef::Call(const EcmaVM *vm, Local<JSValueRef> thisObj,
    const Local<JSValueRef> argv[],  // NOLINTNEXTLINE(modernize-avoid-c-arrays)
    int32_t length,
    bool clearSingleStepper)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    FunctionCallScope callScope(EcmaVM::ConstCast(vm));
    if (!IsFunction(vm)) {
        return JSValueRef::Undefined(vm);
    }
    if (LIKELY(clearSingleStepper)) {
        vm->GetJsDebuggerManager()->ClearSingleStepper();
    }
    JSHandle<JSTaggedValue> func = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(func, ERROR);
    JSHandle<JSTaggedValue> thisValue = JSNApiHelper::ToJSHandle(thisObj);
    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisValue, undefined, length);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    for (int32_t i = 0; i < length; i++) {
        JSHandle<JSTaggedValue> arg = JSNApiHelper::ToJSHandle(argv[i]);
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
        thread->CheckJSTaggedType(arg.GetTaggedValue().GetRawData());
#endif
        info->SetCallArg(i, arg.GetTaggedValue());
    }
    JSTaggedValue result = JSFunction::Call(info);
#if ECMASCRIPT_ENABLE_STUB_RESULT_CHECK
    thread->CheckJSTaggedType(result.GetRawData());
#endif
    if (thread->HasPendingException()) {
        ecmascript::JsStackInfo::BuildCrashInfo(thread);
    }
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    JSHandle<JSTaggedValue> resultValue(thread, result);

    EcmaVM::ClearKeptObjects(thread);
    vm->GetJsDebuggerManager()->NotifyReturnNative();
    return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(resultValue));
}

JSValueRef* FunctionRef::CallForNapi(const EcmaVM *vm, JSValueRef *thisObj,
    JSValueRef *const argv[],  // NOLINTNEXTLINE(modernize-avoid-c-arrays)
    int32_t length)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, *JSValueRef::Hole(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedValue result;
    FunctionCallScope callScope(EcmaVM::ConstCast(vm));
    ASSERT(IsFunction(vm)); // IsFunction check has been done in napi.
    {
        LocalScope scope(vm);
        ecmascript::tooling::JsDebuggerManager *dm = vm->GetJsDebuggerManager();
        bool isDebugApp = dm->IsDebugApp();
        if (isDebugApp) {
            dm->ClearSingleStepper();
        }
        JSTaggedValue func = *reinterpret_cast<JSTaggedValue *>(this);
        JSTaggedValue undefined = thread->GlobalConstants()->GetUndefined();
        JSTaggedValue thisValue = undefined;
        if (thisObj != nullptr) {
            thisValue = *reinterpret_cast<JSTaggedValue *>(thisObj);
        }
        EcmaRuntimeCallInfo *info =
            ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisValue, undefined, length);
        RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Hole(vm));
        for (int32_t i = 0; i < length; i++) {
            if (argv[i]) {
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
                thread->CheckJSTaggedType(JSNApiHelper::ToJSTaggedValue(argv[i]).GetRawData());
#endif
                // NewRuntimeCallInfo has set Undefined defaultly in Argv's slot.
                info->SetCallArg(i, JSNApiHelper::ToJSTaggedValue(argv[i]));
            }
        }
        if (LIKELY(thread->IsAsmInterpreter())) {
            STACK_LIMIT_CHECK(thread, reinterpret_cast<JSValueRef *>(*JSValueRef::Hole(vm)));
            auto *hclass = func.GetTaggedObject()->GetClass();
            if (hclass->IsClassConstructor()) {
                RETURN_STACK_BEFORE_THROW_IF_ASM(thread);
                THROW_TYPE_ERROR_AND_RETURN(thread, "class constructor cannot call",
                    reinterpret_cast<JSValueRef *>(*JSValueRef::Hole(vm)));
            }
            result = ecmascript::InterpreterAssembly::Execute(info);
        } else {
            result = JSFunction::Call(info);
        }
#if ECMASCRIPT_ENABLE_STUB_RESULT_CHECK
        thread->CheckJSTaggedType(result.GetRawData());
#endif
        RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Hole(vm));
        EcmaVM::ClearKeptObjects(thread);
        if (isDebugApp && dm->IsMixedDebugEnabled()) {
            dm->NotifyReturnNative();
        }
    }
    JSHandle<JSTaggedValue> resultValue(thread, result);
    return reinterpret_cast<JSValueRef *>(resultValue.GetAddress());
}

Local<JSValueRef> FunctionRef::Constructor(const EcmaVM *vm,
    const Local<JSValueRef> argv[],  // NOLINTNEXTLINE(modernize-avoid-c-arrays)
    int32_t length)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    FunctionCallScope callScope(EcmaVM::ConstCast(vm));
    if (!IsFunction(vm)) {
        return JSValueRef::Undefined(vm);
    }
    JSHandle<JSTaggedValue> func = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(func, ERROR);
    JSHandle<JSTaggedValue> newTarget = func;
    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, newTarget, length);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    for (int32_t i = 0; i < length; i++) {
        JSHandle<JSTaggedValue> arg = JSNApiHelper::ToJSHandle(argv[i]);
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
        thread->CheckJSTaggedType(arg.GetTaggedValue().GetRawData());
#endif
        info->SetCallArg(i, arg.GetTaggedValue());
    }
    JSTaggedValue result = JSFunction::Construct(info);
#if ECMASCRIPT_ENABLE_STUB_RESULT_CHECK
    thread->CheckJSTaggedType(result.GetRawData());
#endif
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    JSHandle<JSTaggedValue> resultValue(thread, result);
    return JSNApiHelper::ToLocal<JSValueRef>(resultValue);
}

JSValueRef* FunctionRef::ConstructorOptimize(const EcmaVM *vm,
    JSValueRef* argv[],  // NOLINTNEXTLINE(modernize-avoid-c-arrays)
    int32_t length)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, *JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedValue result;
    FunctionCallScope callScope(EcmaVM::ConstCast(vm));
    ASSERT(IsFunction(vm)); // IsFunction check has been done in napi.
    {
        LocalScope scope(vm);
        JSTaggedValue func = *reinterpret_cast<JSTaggedValue*>(this);
        JSTaggedValue newTarget = func;
        JSTaggedValue undefined = thread->GlobalConstants()->GetUndefined();
        EcmaRuntimeCallInfo *info =
            ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, newTarget, length);
        RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Undefined(vm));
        for (int32_t i = 0; i < length; ++i) {
            JSTaggedValue arg =
                argv[i] == nullptr ? JSTaggedValue::Undefined() : JSNApiHelper::ToJSTaggedValue(argv[i]);
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
                thread->CheckJSTaggedType(arg.GetRawData());
#endif
            info->SetCallArg(i, arg);
        }
        result = JSFunction::ConstructInternal(info);
        RETURN_VALUE_IF_ABRUPT(thread, *JSValueRef::Undefined(vm));
    }
#if ECMASCRIPT_ENABLE_STUB_RESULT_CHECK
    thread->CheckJSTaggedType(result.GetRawData());
#endif
    JSHandle<JSTaggedValue> resultValue(thread, result);
    return reinterpret_cast<JSValueRef*>(resultValue.GetAddress());
}

Local<JSValueRef> FunctionRef::GetFunctionPrototype(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> func = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(func, FATAL);
    JSHandle<JSTaggedValue> prototype(thread, JSHandle<JSFunction>(func)->GetFunctionPrototype(thread));
    return JSNApiHelper::ToLocal<JSValueRef>(prototype);
}

bool FunctionRef::Inherit(const EcmaVM *vm, Local<FunctionRef> parent)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(vm);
    JSHandle<JSTaggedValue> parentValue = JSNApiHelper::ToJSHandle(parent);
    JSHandle<JSObject> parentHandle = JSHandle<JSObject>::Cast(parentValue);
    JSHandle<JSObject> thisHandle = JSHandle<JSObject>::Cast(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(thisHandle, ERROR);
    // Set this.__proto__ to parent
    bool res = JSObject::SetPrototype(thread, thisHandle, parentValue);
    if (!res) {
        return false;
    }
    // Set this.Prototype.__proto__ to parent.Prototype
    JSHandle<JSTaggedValue> parentPrototype(thread, JSFunction::PrototypeGetter(thread, parentHandle));
    JSHandle<JSTaggedValue> thisPrototype(thread, JSFunction::PrototypeGetter(thread, thisHandle));
    return JSObject::SetPrototype(thread, JSHandle<JSObject>::Cast(thisPrototype), parentPrototype);
}

void FunctionRef::SetName(const EcmaVM *vm, Local<StringRef> name)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(vm);
    JSFunction *func = JSFunction::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject());
    JSTaggedValue key = JSNApiHelper::ToJSTaggedValue(*name);
    JSFunction::SetFunctionNameNoPrefix(thread, func, key);
}

Local<StringRef> FunctionRef::GetName(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSFunctionBase> func(thread, JSNApiHelper::ToJSTaggedValue(this));
    JSHandle<JSTaggedValue> name = JSFunctionBase::GetFunctionName(thread, func);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return scope.Escape(JSNApiHelper::ToLocal<StringRef>(name));
}

Local<StringRef> FunctionRef::GetSourceCode(const EcmaVM *vm, int lineNumber)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSFunctionBase> func(thread, JSNApiHelper::ToJSTaggedValue(this));
    JSHandle<Method> method(thread, func->GetMethod(thread));
    const JSPandaFile *jsPandaFile = method->GetJSPandaFile(thread);
    DebugInfoExtractor *debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
    ecmascript::CString entry = JSPandaFile::ENTRY_FUNCTION_NAME;
    if (!jsPandaFile->IsBundlePack()) {
        JSFunction *function = JSFunction::Cast(func.GetTaggedValue().GetTaggedObject());
        ecmascript::CString recordName = function->GetRecordName(thread);
        ASSERT(!recordName.empty());
        entry = recordName;
    }

    uint32_t mainMethodIndex = jsPandaFile->GetMainMethodIndex(entry);
    JSMutableHandle<JSTaggedValue> sourceCodeHandle(thread, BuiltinsBase::GetTaggedString(thread, ""));
    if (mainMethodIndex == 0) {
        return scope.Escape(JSNApiHelper::ToLocal<StringRef>(sourceCodeHandle));
    }

    const std::string &allSourceCode = debugExtractor->GetSourceCode(panda_file::File::EntityId(mainMethodIndex));
    std::string sourceCode = StringHelper::GetSpecifiedLine(allSourceCode, lineNumber);
    uint32_t codeLen = sourceCode.length();
    if (codeLen == 0) {
        return scope.Escape(JSNApiHelper::ToLocal<StringRef>(sourceCodeHandle));
    }

    if (sourceCode[codeLen - 1] == '\r') {
        sourceCode = sourceCode.substr(0, codeLen - 1);
    }
    sourceCodeHandle.Update(BuiltinsBase::GetTaggedString(thread, sourceCode.c_str()));
    return scope.Escape(JSNApiHelper::ToLocal<StringRef>(sourceCodeHandle));
}

bool FunctionRef::IsNative(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSFunctionBase> func(thread, JSNApiHelper::ToJSTaggedValue(this));
    JSHandle<Method> method(thread, func->GetMethod(thread));
    return method->IsNativeWithCallField();
}

void FunctionRef::SetData(const EcmaVM *vm, void *data, NativePointerCallback deleter, [[maybe_unused]] bool callNapi)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> funcValue = JSNApiHelper::ToJSHandle(this);
    JSHandle<JSFunction> function(funcValue);
    if (function->IsJSShared()) {
        JSFunction::SetSFunctionExtraInfo(thread, function, nullptr, deleter, data, 0);
    } else {
        JSFunction::SetFunctionExtraInfo(thread, function, nullptr, deleter, data, 0);
    }
}

void* FunctionRef::GetData(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, nullptr);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> funcValue = JSNApiHelper::ToJSHandle(this);
    JSHandle<JSFunctionBase> function(funcValue);
    JSTaggedValue extraInfoValue = function->GetFunctionExtraInfo(thread);
    if (!extraInfoValue.IsNativePointer()) {
        return nullptr;
    }
    auto extraInfo = JSNativePointer::Cast(extraInfoValue.GetTaggedObject());
    return extraInfo->GetData();
}

// ----------------------------------- ArrayRef ----------------------------------------
Local<ArrayRef> ArrayRef::New(const EcmaVM *vm, uint32_t length)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedNumber arrayLen(length);
    JSHandle<JSTaggedValue> array = JSArray::ArrayCreate(thread, arrayLen);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<ArrayRef>(array);
}

uint32_t ArrayRef::Length(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    return JSArray::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetArrayLength();
}

Local<JSValueRef> ArrayRef::GetValueAt(const EcmaVM *vm, Local<JSValueRef> obj, uint32_t index)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> object = JSNApiHelper::ToJSHandle(obj);
    JSHandle<JSTaggedValue> result = JSArray::FastGetPropertyByValue(thread, object, index);
    return JSNApiHelper::ToLocal<JSValueRef>(result);
}

bool ArrayRef::SetValueAt(const EcmaVM *vm, Local<JSValueRef> obj, uint32_t index, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> objectHandle = JSNApiHelper::ToJSHandle(obj);
    JSHandle<JSTaggedValue> valueHandle = JSNApiHelper::ToJSHandle(value);
    return JSArray::FastSetPropertyByValue(thread, objectHandle, index, valueHandle);
}

// ----------------------------------- SendableArrayRef ----------------------------------------
Local<SendableArrayRef> SendableArrayRef::New(const EcmaVM *vm, uint32_t length)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSTaggedNumber arrayLen(length);
    JSHandle<JSTaggedValue> array = ecmascript::JSSharedArray::ArrayCreate(thread, arrayLen);
    JSHandle<JSTaggedValue> initialValue(thread, JSTaggedValue::Undefined());
    JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
    for (uint32_t i = 0; i < length; i++) {
        key.Update(JSTaggedValue(i));
        JSObject::CreateDataPropertyOrThrow(
            thread, JSHandle<JSObject>(array), key, initialValue, ecmascript::JSShared::SCheckMode::SKIP);
    }
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<SendableArrayRef>(array);
}

uint32_t SendableArrayRef::Length(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    return ecmascript::JSSharedArray::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetArrayLength();
}

Local<JSValueRef> SendableArrayRef::GetValueAt(const EcmaVM *vm, Local<JSValueRef> obj, uint32_t index)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> object = JSNApiHelper::ToJSHandle(obj);
    // Add Concurrent check for shared array
    [[maybe_unused]] ecmascript::ConcurrentApiScope<ecmascript::JSSharedArray> scope(thread, object);
    JSHandle<JSTaggedValue> result = ecmascript::JSSharedArray::FastGetPropertyByValue(thread, object, index);
    return JSNApiHelper::ToLocal<JSValueRef>(result);
}

bool SendableArrayRef::SetProperty(const EcmaVM *vm, Local<JSValueRef> obj, uint32_t index, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> objectHandle = JSNApiHelper::ToJSHandle(obj);
    JSHandle<JSTaggedValue> valueHandle = JSNApiHelper::ToJSHandle(value);
    return ecmascript::JSSharedArray::SetProperty(
        thread, objectHandle, index, valueHandle, true, ecmascript::SCheckMode::CHECK);
}

// ---------------------------------- Error ---------------------------------------
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define EXCEPTION_ERROR_NEW(name, type)                                                                 \
    Local<JSValueRef> Exception::name(const EcmaVM *vm, Local<StringRef> message)                       \
    {                                                                                                   \
        CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));                    \
        ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());                                 \
        ObjectFactory *factory = vm->GetFactory();                                                      \
                                                                                                        \
        JSHandle<EcmaString> messageValue(JSNApiHelper::ToJSHandle(message));                           \
        JSHandle<JSTaggedValue> result(factory->NewJSError(ErrorType::type,                             \
                                                           messageValue, ecmascript::StackCheck::NO));  \
        return JSNApiHelper::ToLocal<JSValueRef>(result);                                               \
    }

EXCEPTION_ERROR_ALL(EXCEPTION_ERROR_NEW)

#undef EXCEPTION_ERROR_NEW
// ---------------------------------- Error ---------------------------------------

// ---------------------------------- FunctionCallScope ---------------------------------------
FunctionCallScope::FunctionCallScope(EcmaVM *vm) : vm_(vm)
{
    vm_->IncreaseCallDepth();
}

FunctionCallScope::~FunctionCallScope()
{
    vm_->DecreaseCallDepth();
    if (vm_->IsTopLevelCallDepth()) {
        ecmascript::ThreadManagedScope managedScope(vm_->GetJSThread());
        vm_->ExecutePromisePendingJob();
    }
}

// -------------------------------------  JSExecutionScope ------------------------------
JSExecutionScope::JSExecutionScope([[maybe_unused]] const EcmaVM *vm)
{
}

JSExecutionScope::~JSExecutionScope()
{
    lastCurrentThread_ = nullptr;
    isRevert_ = false;
}

// ------------------------------------ JsiNativeScope -----------------------------------------------

JsiNativeScope::JsiNativeScope(const EcmaVM *vm)
{
    thread_ = vm->GetAssociatedJSThread();
#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
    if (vm->IsCollectingScopeLockStats()) {
        const_cast<EcmaVM*>(vm)->IncreaseEnterJsiNativeScopeCount();
        const_cast<EcmaVM*>(vm)->IncreaseUpdateThreadStateTransCount();
    }
#endif
    if (LIKELY(!vm->IsEnableCMCGC())) {
        oldThreadState_ = static_cast<uint16_t>(thread_->TransferToNonRunning(ecmascript::ThreadState::NATIVE));
        hasSwitchState_ = oldThreadState_ != static_cast<uint16_t>(ecmascript::ThreadState::NATIVE);
    } else {
        isEnableCMCGC_ = true;
        hasSwitchState_ = thread_->GetThreadHolder()->TransferToNativeIfInRunning();
    }
}

JsiNativeScope::~JsiNativeScope()
{
    if (hasSwitchState_) {
        if (LIKELY(!isEnableCMCGC_)) {
            thread_->TransferInNonRunning(static_cast<ecmascript::ThreadState>(oldThreadState_));
        } else {
            thread_->GetThreadHolder()->TransferToRunning();
        }
    }
}

// ------------------------------------ JsiFastNativeScope -----------------------------------------------

JsiFastNativeScope::JsiFastNativeScope(const EcmaVM *vm)
{
    thread_ = vm->GetAssociatedJSThread();
#if ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT
    if (vm->IsCollectingScopeLockStats()) {
        const_cast<EcmaVM*>(vm)->IncreaseEnterFastNativeScopeCount();
        const_cast<EcmaVM*>(vm)->IncreaseUpdateThreadStateTransCount();
    }
#endif
    if (LIKELY(!vm->IsEnableCMCGC())) {
        oldThreadState_ = static_cast<uint16_t>(thread_->TransferToRunningIfNonRunning());
        hasSwitchState_ = oldThreadState_ != static_cast<uint16_t>(ecmascript::ThreadState::RUNNING);
    } else {
        isEnableCMCGC_ = true;
#ifdef PANDA_JS_ETS_HYBRID_MODE
        // This is a temporary impl to adapt interop to cmc, because some interop call napi
        // without transfering to NATIVE
        extraCoroutineSwitchedForInterop_ = InterOpCoroutineToNative(thread_->GetThreadHolder());
#endif
        hasSwitchState_ = thread_->GetThreadHolder()->TransferToRunningIfInNative();
    }
}

JsiFastNativeScope::~JsiFastNativeScope()
{
    if (hasSwitchState_) {
        if (LIKELY(!isEnableCMCGC_)) {
            thread_->TransferToNonRunningInRunning(static_cast<ecmascript::ThreadState>(oldThreadState_));
        } else {
            thread_->GetThreadHolder()->TransferToNative();
        }
    }
#if defined(PANDA_JS_ETS_HYBRID_MODE)
    if (isEnableCMCGC_ && extraCoroutineSwitchedForInterop_) {
        InterOpCoroutineToRunning(thread_->GetThreadHolder());
    }
#endif
}

// ------------------------------------ JsiRuntimeCallInfo -----------------------------------------------
void *JsiRuntimeCallInfo::GetData()
{
    ecmascript::ThreadManagedScope managedScope(thread_);
    JSHandle<JSTaggedValue> constructor = BuiltinsBase::GetConstructor(reinterpret_cast<EcmaRuntimeCallInfo *>(this));
    if (!constructor->IsJSFunction()) {
        return nullptr;
    }
    JSHandle<JSFunctionBase> function(constructor);
    JSTaggedValue extraInfoValue = function->GetFunctionExtraInfo(thread_);
    if (!extraInfoValue.IsJSNativePointer()) {
        return nullptr;
    }
    return JSNativePointer::Cast(extraInfoValue.GetTaggedObject())->GetData();
}

EcmaVM *JsiRuntimeCallInfo::GetVM() const
{
    return thread_->GetEcmaVM();
}

// ---------------------------------------JSNApi-------------------------------------------
PatchErrorCode JSNApi::LoadPatch(EcmaVM *vm, const std::string &patchFileName, const std::string &baseFileName)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, PatchErrorCode::INTERNAL_ERROR);
    ecmascript::ThreadManagedScope managedScope(thread);
    ecmascript::QuickFixManager *quickFixManager = vm->GetQuickFixManager();
    return quickFixManager->LoadPatch(thread, patchFileName, baseFileName);
}

PatchErrorCode JSNApi::LoadPatch(EcmaVM *vm,
                                 const std::string &patchFileName, uint8_t *patchBuffer, size_t patchSize,
                                 const std::string &baseFileName, uint8_t *baseBuffer, size_t baseSize)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, PatchErrorCode::INTERNAL_ERROR);
    ecmascript::ThreadManagedScope managedScope(thread);
    ecmascript::QuickFixManager *quickFixManager = vm->GetQuickFixManager();
    return quickFixManager->LoadPatch(
        thread, patchFileName, patchBuffer, patchSize, baseFileName, baseBuffer, baseSize);
}

PatchErrorCode JSNApi::UnloadPatch(EcmaVM *vm, const std::string &patchFileName)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, PatchErrorCode::INTERNAL_ERROR);
    ecmascript::ThreadManagedScope managedScope(thread);
    ecmascript::QuickFixManager *quickFixManager = vm->GetQuickFixManager();
    return quickFixManager->UnloadPatch(thread, patchFileName);
}

/*
 * check whether the exception is caused by quickfix methods.
 */
bool JSNApi::IsQuickFixCausedException(EcmaVM *vm, Local<ObjectRef> exception, const std::string &patchFileName)
{
    if (exception.IsEmpty()) {
        return false;
    }
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    ecmascript::QuickFixManager *quickFixManager = vm->GetQuickFixManager();
    JSHandle<JSTaggedValue> exceptionInfo = JSNApiHelper::ToJSHandle(exception);
    return quickFixManager->IsQuickFixCausedException(thread, exceptionInfo, patchFileName);
}

/*
 * register quickfix query function.
 */
void JSNApi::RegisterQuickFixQueryFunc(EcmaVM *vm, std::function<bool(std::string baseFileName,
                        std::string &patchFileName,
                        uint8_t **patchBuffer,
                        size_t &patchSize)> callBack)
{
    CROSS_THREAD_CHECK(vm);
    ecmascript::QuickFixManager *quickFixManager = vm->GetQuickFixManager();
    quickFixManager->RegisterQuickFixQueryFunc(callBack);
}

bool JSNApi::IsBundle(EcmaVM *vm)
{
    return vm->IsBundlePack();
}

void JSNApi::SetBundle(EcmaVM *vm, bool value)
{
    vm->SetIsBundlePack(value);
}

bool JSNApi::IsNormalizedOhmUrlPack(EcmaVM *vm)
{
    return vm->IsNormalizedOhmUrlPack();
}

bool JSNApi::IsOhmUrl(const std::string &srcName)
{
    return ModulePathHelper::IsOhmUrl(srcName.c_str());
}

void JSNApi::SetModuleInfo(EcmaVM *vm, const std::string &assetPath, const std::string &entryPoint)
{
    SetAssetPath(vm, assetPath);
    size_t pos = entryPoint.find_first_of("/");
    if (pos != std::string::npos) {
        SetBundleName(vm, entryPoint.substr(0, pos));
        ecmascript::CString moduleName = ModulePathHelper::GetModuleName(entryPoint.c_str());
        if (!moduleName.empty()) {
            SetModuleName(vm, moduleName.c_str());
            return;
        }
    }
    std::string errmsg = "SetModuleInfo: entryPoint:" + entryPoint + "is invalid.";
    LOG_ECMA(ERROR) << errmsg;
    Local<StringRef> message = StringRef::NewFromUtf8(vm, errmsg.c_str());
    Local<JSValueRef> error = Exception::Error(vm, message);
    JSNApi::ThrowException(vm, error);
}

// note: The function SetAssetPath is a generic interface for previewing and physical machines.
void JSNApi::SetAssetPath(EcmaVM *vm, const std::string &assetPath)
{
    ecmascript::CString path = assetPath.c_str();
    // check input assetPath
#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS)
    if (!ModulePathHelper::ValidateAbcPath(path, ecmascript::ValidateFilePath::ABC)) {
        LOG_FULL(FATAL) << "Invalid input assetPath: " << assetPath.c_str();
    }
#endif
    vm->SetAssetPath(path);
}

void JSNApi::SetLoop(EcmaVM *vm, void *loop)
{
    vm->SetLoop(loop);
}

void JSNApi::SetWeakFinalizeTaskCallback(EcmaVM *vm, const WeakFinalizeTaskCallback &callback)
{
    vm->GetAssociatedJSThread()->SetWeakFinalizeTaskCallback(callback);
}

void JSNApi::SetAsyncCleanTaskCallback(EcmaVM *vm, const NativePointerTaskCallback &callback)
{
    vm->GetAssociatedJSThread()->SetAsyncCleanTaskCallback(callback);
}

void JSNApi::SetTriggerGCTaskCallback(EcmaVM *vm, const TriggerGCTaskCallback& callback)
{
    vm->GetHeap()->GetIdleGCTrigger()->SetTriggerGCTaskCallback(callback);
}

std::string JSNApi::GetAssetPath(EcmaVM *vm)
{
    return vm->GetAssetPath().c_str();
}

void JSNApi::SetMockModuleList(EcmaVM *vm, const std::map<std::string, std::string> &list)
{
    vm->SetMockModuleList(list);
}

void JSNApi::SetHmsModuleList(EcmaVM *vm, const std::vector<panda::HmsMap> &list)
{
    vm->SetHmsModuleList(list);
}

void JSNApi::SetPkgAliasList(EcmaVM *vm, const std::map<std::string, std::string> &list)
{
    ecmascript::CMap<ecmascript::CString, ecmascript::CString> pkgAliasList;
    for (auto it = list.begin(); it != list.end(); ++it) {
        pkgAliasList.emplace(it->first.c_str(), it->second.c_str());
    }
    vm->SetPkgAliasList(pkgAliasList);
}

void JSNApi::UpdatePkgAliasList(EcmaVM *vm, const std::map<std::string, std::string> &list)
{
    ecmascript::CMap<ecmascript::CString, ecmascript::CString> pkgAliasList;
    for (auto &[alias, pkgName]: list) {
        pkgAliasList.emplace(alias, pkgName);
    }
    vm->UpdatePkgAliasList(pkgAliasList);
    ecmascript::CMap<uint32_t, EcmaVM *> workerList = vm->GetWorkList();
    for (auto &[workerId, workerVm]: workerList) {
        workerVm->UpdatePkgAliasList(pkgAliasList);
    }
}

void JSNApi::SetPkgNameList(EcmaVM *vm, const std::map<std::string, std::string> &list)
{
    ecmascript::CMap<ecmascript::CString, ecmascript::CString> pkgNameList;
    for (auto it = list.begin(); it != list.end(); ++it) {
        pkgNameList.emplace(it->first.c_str(), it->second.c_str());
    }
    vm->SetPkgNameList(pkgNameList);
}

void JSNApi::UpdatePkgNameList(EcmaVM *vm, const std::map<std::string, std::string> &list)
{
    ecmascript::CMap<ecmascript::CString, ecmascript::CString> pkgNameList;
    for (auto &[moduleName, pkgName]: list) {
        pkgNameList.emplace(moduleName, pkgName);
    }
    vm->UpdatePkgNameList(pkgNameList);
    ecmascript::CMap<uint32_t, EcmaVM *> workerList = vm->GetWorkList();
    for (auto &[workerId, workerVm]: workerList) {
        workerVm->UpdatePkgNameList(pkgNameList);
    }
}

std::string JSNApi::GetPkgName(EcmaVM *vm, const std::string &moduleName)
{
    return vm->GetPkgName(moduleName.c_str()).c_str();
}

void JSNApi::SetpkgContextInfoList(EcmaVM *vm, const std::map<std::string,
    std::vector<std::vector<std::string>>> &list)
{
    ecmascript::CMap<ecmascript::CString, ecmascript::CMap<ecmascript::CString,
        ecmascript::CVector<ecmascript::CString>>> pkgContextInfoList;
    for (auto it = list.begin(); it != list.end(); it++) {
        const std::vector<std::vector<std::string>> vec = it->second;
        ecmascript::CMap<ecmascript::CString, ecmascript::CVector<ecmascript::CString>> map;
        for (size_t i = 0; i < vec.size(); i++) {
            ecmascript::CString pkgName = vec[i][0].c_str();
            ecmascript::CVector<ecmascript::CString> pkgContextInfo;
            for (size_t j = 1; j < vec[i].size(); j++) {
                pkgContextInfo.emplace_back(vec[i][j].c_str());
            }
            map.emplace(pkgName, pkgContextInfo);
        }
        pkgContextInfoList.emplace(it->first.c_str(), map);
    }
    vm->SetpkgContextInfoList(pkgContextInfoList);
}

void JSNApi::UpdatePkgContextInfoList(EcmaVM *vm,
    const std::map<std::string, std::vector<std::vector<std::string>>> &list)
{
    ecmascript::CMap<ecmascript::CString, ecmascript::CMap<ecmascript::CString,
        ecmascript::CVector<ecmascript::CString>>> pkgContextInfoList;
    for (auto &[moduleName, pkgContextInfos]: list) {
        ecmascript::CMap<ecmascript::CString, ecmascript::CVector<ecmascript::CString>> map;
        for (auto &datas: pkgContextInfos) {
            if (datas.empty()) {
                continue;
            }
            ecmascript::CString pkgName = datas[0].c_str();
            ecmascript::CVector<ecmascript::CString> pkgContextInfo;
            for (size_t i = 1; i < datas.size(); i++) {
                pkgContextInfo.emplace_back(datas[i].c_str());
            }
            map.emplace(pkgName, pkgContextInfo);
        }
        pkgContextInfoList.emplace(moduleName, map);
    }
    vm->UpdatePkgContextInfoList(pkgContextInfoList);

    ecmascript::CMap<uint32_t, EcmaVM *> workerList = vm->GetWorkList();
    for (auto &[workerId, workerVm]: workerList) {
        workerVm->UpdatePkgContextInfoList(pkgContextInfoList);
    }
}

void JSNApi::SetPkgContextInfoList(EcmaVM *vm, const std::unordered_map<std::string,
    std::pair<std::unique_ptr<uint8_t[]>, size_t>> &pkgInfoMap)
{
    ecmascript::CMap<ecmascript::CString, ecmascript::CMap<ecmascript::CString,
        ecmascript::CVector<ecmascript::CString>>> pkgContextInfoList;
    ecmascript::CMap <ecmascript::CString, ecmascript::CString> pkgAliasList;
    ecmascript::CUnorderedMap<ecmascript::CString, ecmascript::CUnorderedMap<ecmascript::CString,
        ecmascript::CUnorderedSet<ecmascript::CString>>> ohExportsList;
    ecmascript::ohos::ModulePkgParser::ParseModulePkgJson(vm, pkgInfoMap, pkgContextInfoList, pkgAliasList,
        ohExportsList);
    vm->SetpkgContextInfoList(pkgContextInfoList);
    vm->SetPkgAliasList(pkgAliasList);
    vm->SetOhExportsList(ohExportsList);
}

void JSNApi::UpdatePkgContextInfoList(EcmaVM *vm, const std::unordered_map<std::string,
    std::pair<std::unique_ptr<uint8_t[]>, size_t>> &pkgInfoMap)
{
    ecmascript::CMap<ecmascript::CString, ecmascript::CMap<ecmascript::CString,
        ecmascript::CVector<ecmascript::CString>>> pkgContextInfoList;
    ecmascript::CMap <ecmascript::CString, ecmascript::CString> pkgAliasList;
    ecmascript::CUnorderedMap<ecmascript::CString, ecmascript::CUnorderedMap<ecmascript::CString,
        ecmascript::CUnorderedSet<ecmascript::CString>>> ohExportsMap;
    ecmascript::ohos::ModulePkgParser::ParseModulePkgJson(vm, pkgInfoMap, pkgContextInfoList, pkgAliasList,
        ohExportsMap);
    vm->UpdatePkgContextInfoList(pkgContextInfoList);
    vm->UpdatePkgAliasList(pkgAliasList);
    vm->UpdateOhExportsList(ohExportsMap);
    ecmascript::CMap<uint32_t, EcmaVM *> workerList = vm->GetWorkList();
    for (auto &[_, workerVm]: workerList) {
        workerVm->UpdatePkgContextInfoList(pkgContextInfoList);
        workerVm->UpdatePkgAliasList(pkgAliasList);
        workerVm->UpdateOhExportsList(ohExportsMap);
    }
}

// Only used for env created by napi to set module execution mode
void JSNApi::SetExecuteBufferMode(const EcmaVM *vm)
{
    ecmascript::ModuleManager *moduleManager =
        vm->GetAssociatedJSThread()->GetModuleManager();
    moduleManager->SetExecuteMode(ecmascript::ModuleExecuteMode::ExecuteBufferMode);
}

void JSNApi::SetStopPreLoadSoCallback(EcmaVM *vm, const StopPreLoadSoCallback &callback)
{
    vm->AddStopPreLoadCallback(callback);
}

bool JSNApi::InitForConcurrentThread(EcmaVM *vm, ConcurrentCallback cb, void *data)
{
    vm->SetConcurrentCallback(cb, data);

    return true;
}

bool JSNApi::InitForConcurrentFunction(EcmaVM *vm, Local<JSValueRef> function, void *taskInfo)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(vm);
    JSHandle<JSTaggedValue> funcVal = JSNApiHelper::ToJSHandle(function);
    JSHandle<JSFunction> transFunc = JSHandle<JSFunction>::Cast(funcVal);
    if (transFunc->GetFunctionKind(thread) != ecmascript::FunctionKind::CONCURRENT_FUNCTION) {
        LOG_ECMA(ERROR) << "Function is not concurrent";
        return false;
    }
    JSFunction::SetFunctionExtraInfo(thread, transFunc, nullptr, nullptr, taskInfo);
    transFunc->SetTaskConcurrentFuncFlag(1); // 1 : concurrent function flag
    thread->SetTaskInfo(reinterpret_cast<uintptr_t>(taskInfo));
    thread->SetIsInConcurrentScope(true);
    return true;
}

void* JSNApi::GetCurrentTaskInfo(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, nullptr);
    return reinterpret_cast<void*>(thread->GetTaskInfo());
}

void JSNApi::ClearCurrentTaskInfo(const EcmaVM *vm)
{
    JSThread *thread = vm->GetJSThread();
    thread->SetTaskInfo(reinterpret_cast<uintptr_t>(nullptr));
    thread->SetIsInConcurrentScope(false);
}

void JSNApi::SetLargeHeap(bool isLargeHeap)
{
    LOG_ECMA(DEBUG) << "Set large heap: " << isLargeHeap;
    auto instance = ecmascript::Runtime::GetInstance();
    ASSERT(instance != nullptr);
    instance->SetEnableLargeHeap(isLargeHeap);
}

void JSNApi::SetBundleName(EcmaVM *vm, const std::string &bundleName)
{
    ecmascript::CString name = bundleName.c_str();
    vm->SetBundleName(name);
}

std::string JSNApi::GetBundleName(EcmaVM *vm)
{
    return vm->GetBundleName().c_str();
}

void JSNApi::SetModuleName(EcmaVM *vm, const std::string &moduleName)
{
    ecmascript::CString name = moduleName.c_str();
    ecmascript::pgo::PGOProfilerManager::GetInstance()->SetModuleName(moduleName);
    vm->SetModuleName(name);
}

std::string JSNApi::GetModuleName(EcmaVM *vm)
{
    return vm->GetModuleName().c_str();
}

void JSNApi::SetThreadName(EcmaVM *vm, const std::string &name)
{
    CROSS_THREAD_CHECK(vm);
    vm->GetJSThread()->SetThreadName(name);
}

std::pair<std::string, std::string> JSNApi::GetCurrentModuleInfo(EcmaVM *vm, bool needRecordName)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return vm->GetCurrentModuleInfo(needRecordName);
}

std::string JSNApi::NormalizePath(const std::string &string)
{
    return PathHelper::NormalizePath(string.c_str()).c_str();
}

// Enable cross thread execution.
void JSNApi::AllowCrossThreadExecution(EcmaVM *vm)
{
    LOG_ECMA(WARN) << "enable cross thread execution";
    vm->GetAssociatedJSThread()->SetCrossThreadExecution(true);
}

// Enable cross thread execution except in gc process.
bool JSNApi::CheckAndSetAllowCrossThreadExecution(EcmaVM *vm)
{
    if (ecmascript::g_isEnableCMCGC) {
        return false;
    } else {
        if (vm->GetHeap()->InGC() || SharedHeap::GetInstance()->InGC()) {
            return false;
        }
        LOG_ECMA(WARN) << "enable cross thread execution when not in gc process";
        vm->GetAssociatedJSThread()->SetCrossThreadExecution(true);
        return true;
    }
}

void JSNApi::DisallowCrossThreadExecution(EcmaVM *vm)
{
    LOG_ECMA(WARN) << "disable cross thread execution";
    vm->GetAssociatedJSThread()->SetCrossThreadExecution(false);
}

void* JSNApi::GetEnv(EcmaVM *vm)
{
    JSThread *thread = vm->GetJSThread();
    return thread->GetEnv();
}

void JSNApi::SetEnv(EcmaVM *vm, void *env)
{
    JSThread *thread = vm->GetJSThread();
    thread->SetEnv(env);
}

void JSNApi::SynchronizVMInfo(EcmaVM *vm, const EcmaVM *hostVM)
{
    std::atomic_thread_fence(std::memory_order_seq_cst);
    vm->SetBundleName(hostVM->GetBundleName());
    vm->SetModuleName(hostVM->GetModuleName());
    vm->SetAssetPath(hostVM->GetAssetPath());
    vm->SetIsBundlePack(hostVM->IsBundlePack());
    vm->SetPkgNameList(const_cast<EcmaVM *>(hostVM)->GetPkgNameList());
    vm->SetPkgAliasList(const_cast<EcmaVM *>(hostVM)->GetPkgAliasList());
    vm->SetpkgContextInfoList(const_cast<EcmaVM *>(hostVM)->GetPkgContextInfoList());
    vm->SetOhExportsList(const_cast<EcmaVM *>(hostVM)->GetOhExportList());

    ecmascript::ModuleManager *vmModuleManager =
        vm->GetAssociatedJSThread()->GetModuleManager();
    ecmascript::ModuleManager *hostVMModuleManager =
        hostVM->GetAssociatedJSThread()->GetModuleManager();
    vmModuleManager->SetExecuteMode(hostVMModuleManager->GetExecuteMode());
    vm->SetResolveBufferCallback(hostVM->GetResolveBufferCallback());
    vm->SetResolveBufferCallbackForHybridApp(hostVM->GetResolveBufferCallbackForHybridApp());
}

bool JSNApi::GetEnableAsmInterpreter(const EcmaVM *vm)
{
    if (vm == nullptr) {
        return false;
    }
    return const_cast<EcmaVM *>(vm)->GetJSOptions().GetEnableAsmInterpreter();
}

std::string JSNApi::GetStubFile(const EcmaVM *vm)
{
    if (vm == nullptr) {
        return "";
    }
    return const_cast<EcmaVM *>(vm)->GetJSOptions().GetStubFile();
}

bool JSNApi::IsProfiling(EcmaVM *vm)
{
    return vm->GetProfilerState();
}

void JSNApi::SetProfilerState(const EcmaVM *vm, bool value)
{
    const_cast<EcmaVM*>(vm)->SetProfilerState(value);
}

void JSNApi::SetSourceMapTranslateCallback(EcmaVM *vm, SourceMapTranslateCallback callback)
{
    vm->SetSourceMapTranslateCallback(callback);
}

void JSNApi::SetSourceMapCallback(EcmaVM *vm, SourceMapCallback callback)
{
    vm->SetSourceMapCallback(callback);
}

void JSNApi::GetStackBeforeCallNapiSuccess([[maybe_unused]] EcmaVM *vm,
                                           [[maybe_unused]] bool &getStackBeforeCallNapiSuccess)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
    JSThread *thread = vm->GetJSThread();
    if (thread->GetIsProfiling()) {
        ecmascript::ThreadManagedScope managedScope(thread);
        getStackBeforeCallNapiSuccess = vm->GetProfiler()->GetStackBeforeCallNapi(thread);
    }
#endif
}

void JSNApi::GetStackAfterCallNapi([[maybe_unused]] EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
    JSThread *thread = vm->GetJSThread();
    if (thread->GetIsProfiling()) {
        ecmascript::ThreadManagedScope managedScope(thread);
        vm->GetProfiler()->GetStackAfterCallNapi(thread);
    }
#endif
}

EcmaVM *JSNApi::CreateJSVM(const RuntimeOption &option)
{
    JSRuntimeOptions runtimeOptions;
    runtimeOptions.SetArkProperties(option.GetArkProperties());
    runtimeOptions.SetMemConfigProperty(option.GetMemConfigProperty());
    runtimeOptions.SetArkBundleName(option.GetArkBundleName());
    runtimeOptions.SetLongPauseTime(option.GetLongPauseTime());
    runtimeOptions.SetGcThreadNum(option.GetGcThreadNum());
    runtimeOptions.SetIsWorker(option.GetIsWorker());
    runtimeOptions.SetIsRestrictedWorker(option.GetIsRestrictedWorker());
// Disable the asm-interpreter of ark-engine for ios-platform temporarily.
#if !defined(PANDA_TARGET_IOS)
    // asmInterpreter
    runtimeOptions.SetEnableAsmInterpreter(option.GetEnableAsmInterpreter());
#else
    runtimeOptions.SetEnableAsmInterpreter(false);
#endif
    runtimeOptions.SetEnableBuiltinsLazy(option.GetEnableBuiltinsLazy());
    runtimeOptions.SetAsmOpcodeDisableRange(option.GetAsmOpcodeDisableRange());
#ifdef ANDROID_PLATFORM
    // stub.an
    if (!option.GetStubFile().empty()) {
        runtimeOptions.SetStubFile(option.GetStubFile());
    }
#endif
    // aot
    runtimeOptions.SetEnableAOT(option.GetEnableAOT());
    runtimeOptions.SetEnablePGOProfiler(option.GetEnableProfile());
    runtimeOptions.SetPGOProfilerPath(option.GetProfileDir());
    // Dfx
    runtimeOptions.SetLogLevel(common::Log::LevelToString(common::Log::ConvertFromRuntime(option.GetLogLevel())));
    runtimeOptions.SetEnableArkTools(option.GetEnableArkTools());
    runtimeOptions.SetLargeHeap(option.GetLargeHeap());
    return CreateEcmaVM(runtimeOptions);
}

EcmaVM *JSNApi::CreateEcmaVM(const JSRuntimeOptions &options)
{
    return EcmaVM::Create(options);
}

void JSNApi::DestroyJSVM(EcmaVM *ecmaVm)
{
    if (UNLIKELY(ecmaVm == nullptr)) {
        return;
    }
    ecmaVm->GetJSThread()->ManagedCodeBegin();
    EcmaVM::Destroy(ecmaVm);
}

void JSNApi::IgnoreFinalizeCallback(EcmaVM *ecmaVm)
{
    CROSS_THREAD_CHECK(ecmaVm);
    ecmascript::ThreadManagedScope scope(thread);
    if (ecmascript::g_isEnableCMCGC) {
        thread->IgnoreFinalizeCallback();
    }
}

void JSNApi::RegisterUncatchableErrorHandler(EcmaVM *ecmaVm, const UncatchableErrorHandler &handler)
{
    ecmaVm->RegisterUncatchableErrorHandler(handler);
}

JSNApi::UncatchableErrorHandler JSNApi::GetUncatchableErrorHandler(const EcmaVM *ecmaVm)
{
    return ecmaVm->GetUncatchableErrorHandler();
}

void JSNApi::TriggerGC(const EcmaVM *vm, TRIGGER_GC_TYPE gcType)
{
    TriggerGC(vm, ecmascript::GCReason::EXTERNAL_TRIGGER, gcType);
}

void JSNApi::TriggerGC(const EcmaVM *vm, ecmascript::GCReason reason, TRIGGER_GC_TYPE gcType)
{
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    if (thread != nullptr && vm->IsInitialized()) {
#if defined(ENABLE_EXCEPTION_BACKTRACE)
        if (thread->IsMainThreadFast()) {
            LOG_ECMA(INFO) << "JSNApi::TriggerGC gcType: " << static_cast<int>(gcType);
            std::ostringstream stack;
            ecmascript::Backtrace(stack, true);
            LOG_ECMA(INFO) << stack.str();
        }
#endif
        if (ecmascript::g_isEnableCMCGC) {
            common::GCReason cmcReason = common::GC_REASON_USER;
            bool async = true;
            if (gcType == TRIGGER_GC_TYPE::FULL_GC || gcType == TRIGGER_GC_TYPE::SHARED_FULL_GC ||
                reason == ecmascript::GCReason::ALLOCATION_FAILED) {
                cmcReason = common::GC_REASON_BACKUP;
                async = false;
            }
            common::BaseRuntime::RequestGC(cmcReason, async, common::GC_TYPE_FULL);
        } else {
            auto sHeap = ecmascript::SharedHeap::GetInstance();
            switch (gcType) {
                case TRIGGER_GC_TYPE::SEMI_GC:
                    vm->CollectGarbage(vm->GetHeap()->SelectGCType(), reason);
                    break;
                case TRIGGER_GC_TYPE::OLD_GC:
                    vm->CollectGarbage(ecmascript::TriggerGCType::OLD_GC, reason);
                    break;
                case TRIGGER_GC_TYPE::FULL_GC:
                    vm->CollectGarbage(ecmascript::TriggerGCType::FULL_GC, reason);
                    break;
                case TRIGGER_GC_TYPE::SHARED_GC:
                    sHeap->CollectGarbage<ecmascript::TriggerGCType::SHARED_GC,
                        ecmascript::GCReason::EXTERNAL_TRIGGER>(thread);
                    break;
                case TRIGGER_GC_TYPE::SHARED_FULL_GC:
                    sHeap->CollectGarbage<ecmascript::TriggerGCType::SHARED_FULL_GC,
                        ecmascript::GCReason::EXTERNAL_TRIGGER>(thread);
                    break;
                default:
                    break;
            }
        }
    }
}

void JSNApi::HintGC(const EcmaVM *vm, MemoryReduceDegree degree, ecmascript::GCReason reason)
{
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    if (thread != nullptr && vm->IsInitialized()) {
        const_cast<ecmascript::Heap *>(vm->GetHeap())->CheckAndTriggerHintGC(degree, reason);
    }
}

void JSNApi::TriggerIdleGC(const EcmaVM *vm, TRIGGER_IDLE_GC_TYPE gcType)
{
    CROSS_THREAD_CHECK(vm);
    if (thread != nullptr && vm->IsInitialized()) {
        ecmascript::ThreadManagedScope managedScope(thread);
        vm->GetHeap()->GetIdleGCTrigger()->TryTriggerIdleGC(gcType);
    }
}

size_t JSNApi::GetEcmaVMExpectedMemoryReclamationSize(const EcmaVM *vm)
{
    if (vm == nullptr) {
        LOG_ECMA(ERROR) << "get expected memory reclamation size but vm is nullptr";
        return 0;
    }
    return vm->GetHeap()->GetIdleGCTrigger()->GetExpectedMemoryReclamationSize();
}

void JSNApi::SetStartIdleMonitorCallback(const StartIdleMonitorCallback& callback)
{
    startIdleMonitorCallback_ = callback;
}

StartIdleMonitorCallback JSNApi::GetStartIdleMonitorCallback()
{
    return startIdleMonitorCallback_;
}

void JSNApi::SetNotifyDeferFreezeCallback(const NotifyDeferFreezeCallback& callback)
{
    ecmascript::Runtime::GetInstance()->SetNotifyDeferFreezeCallback(callback);
}

void JSNApi::ThrowException(const EcmaVM *vm, Local<JSValueRef> error)
{
    auto thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    if (thread->HasPendingException()) {
        LOG_ECMA(DEBUG) << "An exception has already occurred before, keep old exception here.";
        return;
    }
    thread->SetException(JSNApiHelper::ToJSTaggedValue(*error));
}

void JSNApi::PrintExceptionInfo(const EcmaVM *vm)
{
    JSThread* thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] ecmascript::EcmaHandleScope handleScope(thread);

    if (!HasPendingException(vm)) {
        return;
    }
    Local<ObjectRef> exception = GetAndClearUncaughtException(vm);
    JSHandle<JSTaggedValue> exceptionHandle = JSNApiHelper::ToJSHandle(exception);
    if (exceptionHandle->IsJSError()) {
        ecmascript::base::ErrorHelper::PrintJSErrorInfo(thread, exceptionHandle);
        ThrowException(vm, exception);
        return;
    }
    JSHandle<EcmaString> result = JSTaggedValue::ToString(thread, exceptionHandle);
    ecmascript::CString string = ConvertToString(thread, *result);
    LOG_ECMA(WARN) << string;
    ThrowException(vm, exception);
}

void JSNApi::SetOnErrorCallback(EcmaVM *vm, OnErrorCallback cb, void* data)
{
    JSThread* thread = vm->GetJSThread();
    thread->SetOnErrorCallback(cb, data);
}

#if defined(ECMASCRIPT_SUPPORT_DEBUGGER) && !defined(PANDA_TARGET_IOS)
bool JSNApi::StartDebuggerCheckParameters(EcmaVM *vm, const DebugOption &option, int32_t instanceId,
                                          const DebuggerPostTask &debuggerPostTask)
{
    if (vm == nullptr) {
        LOG_ECMA(ERROR) << "[StartDebugger] vm is nullptr";
        return false;
    }

    if (option.port < 0) {
        LOG_ECMA(ERROR) << "[StartDebugger] option.port is -1" ;
        return false;
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    const auto &handler = vm->GetJsDebuggerManager()->GetDebugLibraryHandle();
    if (handler.IsValid()) {
        LOG_ECMA(ERROR) << "[StartDebugger] handler has already loaded";
        return false;
    }
    if (option.libraryPath == nullptr) {
        LOG_ECMA(ERROR) << "[StartDebugger] option.libraryPath is nullptr";
        return false;
    }
    auto handle = panda::os::library_loader::Load(std::string(option.libraryPath));
    if (!handle) {
        LOG_ECMA(ERROR) << "[StartDebugger] Load library fail: " << option.libraryPath << " " << errno;
        return false;
    }
    auto sym = panda::os::library_loader::ResolveSymbol(handle.Value(), "StartDebug");
    if (!sym) {
        LOG_ECMA(ERROR) << "[StartDebugger] Resolve symbol fail: " << sym.Error().ToString();
        return false;
    }
    using StartDebugger = bool (*)(
        const std::string &, EcmaVM *, bool, int32_t, const DebuggerPostTask &, int);

    vm->GetJsDebuggerManager()->SetDebugMode(option.isDebugMode);
    vm->GetJsDebuggerManager()->SetIsDebugApp(true);
    vm->GetJsDebuggerManager()->SetDebugLibraryHandle(std::move(handle.Value()));
    vm->GetJsDebuggerManager()->SetFaApp(option.isFaApp);
    bool ret = reinterpret_cast<StartDebugger>(sym.Value())(
        "PandaDebugger", vm, option.isDebugMode, instanceId, debuggerPostTask, option.port);
    if (!ret) {
        // Reset the config
        vm->GetJsDebuggerManager()->SetDebugMode(false);
        panda::os::library_loader::LibraryHandle libraryHandle(nullptr);
        vm->GetJsDebuggerManager()->SetDebugLibraryHandle(std::move(libraryHandle));
    }
    return ret;
}
#endif

// for previewer, cross platform and testcase debugger
bool JSNApi::StartDebugger([[maybe_unused]] EcmaVM *vm, [[maybe_unused]] const DebugOption &option,
                           [[maybe_unused]] int32_t instanceId,
                           [[maybe_unused]] const DebuggerPostTask &debuggerPostTask)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
#if !defined(PANDA_TARGET_IOS)
    LOG_ECMA(INFO) << "JSNApi::StartDebugger, isDebugMode = " << option.isDebugMode
        << ", port = " << option.port << ", instanceId = " << instanceId;
    return StartDebuggerCheckParameters(vm, option, instanceId, debuggerPostTask);
#else
    if (vm == nullptr) {
        return false;
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    vm->GetJsDebuggerManager()->SetDebugMode(option.isDebugMode);
    vm->GetJsDebuggerManager()->SetFaApp(option.isFaApp);
    bool ret = OHOS::ArkCompiler::Toolchain::StartDebug(
        DEBUGGER_NAME, vm, option.isDebugMode, instanceId, debuggerPostTask, option.port);
    if (!ret) {
        // Reset the config
        vm->GetJsDebuggerManager()->SetDebugMode(false);
    }
    return ret;
#endif // PANDA_TARGET_IOS
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
    return false;
#endif // ECMASCRIPT_SUPPORT_DEBUGGER
}

// rk
// FA or Stage
bool JSNApi::StartDebuggerForOldProcess([[maybe_unused]] EcmaVM *vm, [[maybe_unused]] const DebugOption &option,
                                        [[maybe_unused]] int32_t instanceId,
                                        [[maybe_unused]] const DebuggerPostTask &debuggerPostTask)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
#if !defined(PANDA_TARGET_IOS)
    LOG_ECMA(INFO) << "JSNApi::StartDebuggerForOldProcess, isDebugMode = " << option.isDebugMode
        << ", instanceId = " << instanceId;
    if (vm == nullptr) {
        LOG_ECMA(ERROR) << "[StartDebuggerForOldProcess] vm is nullptr";
        return false;
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    const auto &handle = vm->GetJsDebuggerManager()->GetDebugLibraryHandle();
    if (!handle.IsValid()) {
        LOG_ECMA(ERROR) << "[StartDebuggerForOldProcess] Get library handle fail: " << option.libraryPath;
        return false;
    }

    using StartDebug = bool (*)(
        const std::string &, EcmaVM *, bool, int32_t, const DebuggerPostTask &, int);

    auto sym = panda::os::library_loader::ResolveSymbol(handle, "StartDebug");
    if (!sym) {
        LOG_ECMA(ERROR) << "[StartDebuggerForOldProcess] Resolve symbol fail: " << sym.Error().ToString();
        return false;
    }

    bool ret = reinterpret_cast<StartDebug>(sym.Value())(
        "PandaDebugger", vm, option.isDebugMode, instanceId, debuggerPostTask, option.port);
    if (!ret) {
        // Reset the config
        vm->GetJsDebuggerManager()->SetDebugMode(false);
        panda::os::library_loader::LibraryHandle libraryHandle(nullptr);
        vm->GetJsDebuggerManager()->SetDebugLibraryHandle(std::move(libraryHandle));
    }
    return ret;
#else
    if (vm == nullptr) {
        LOG_ECMA(ERROR) << "[StartDebuggerForOldProcess] vm is nullptr";
        return false;
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    vm->GetJsDebuggerManager()->SetDebugMode(option.isDebugMode);
    bool ret = OHOS::ArkCompiler::Toolchain::StartDebug(
        DEBUGGER_NAME, vm, option.isDebugMode, instanceId, debuggerPostTask, option.port);
    if (!ret) {
        // Reset the config
        vm->GetJsDebuggerManager()->SetDebugMode(false);
    }
    return ret;
#endif // PANDA_TARGET_IOS
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
    return false;
#endif // ECMASCRIPT_SUPPORT_DEBUGGER
}

// ohos or emulator
// FA or Stage
bool JSNApi::StartDebuggerForSocketPair([[maybe_unused]] int tid, [[maybe_unused]] int socketfd)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
    LOG_ECMA(INFO) << "JSNApi::StartDebuggerForSocketPair, tid = " << tid << ", socketfd = " << socketfd;
    JsDebuggerManager *jsDebuggerManager = JsDebuggerManager::GetJsDebuggerManager(tid);
    if (jsDebuggerManager == nullptr) {
        LOG_ECMA(ERROR) << "[StartDebuggerForSocketPair] jsDebuggerManager is nullptr";
        return false;
    }
    const auto &handle = jsDebuggerManager->GetDebugLibraryHandle();
    if (!handle.IsValid()) {
        LOG_ECMA(ERROR) << "[StartDebuggerForSocketPair] Get library handle fail";
        return false;
    }

    using StartDebugForSocketpair = bool (*)(int, int, bool);

    auto sym = panda::os::library_loader::ResolveSymbol(handle, "StartDebugForSocketpair");
    if (!sym) {
        LOG_ECMA(ERROR) << "[StartDebuggerForSocketPair] Resolve symbol fail: " << sym.Error().ToString();
        return false;
    }
    // false: not hybrid
    bool ret = reinterpret_cast<StartDebugForSocketpair>(sym.Value())(tid, socketfd, false);
    if (!ret) {
        // Reset the config
        jsDebuggerManager->SetDebugMode(false);
        panda::os::library_loader::LibraryHandle libraryHandle(nullptr);
        jsDebuggerManager->SetDebugLibraryHandle(std::move(libraryHandle));
    }
    return ret;
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
    return false;
#endif // ECMASCRIPT_SUPPORT_DEBUGGER
}

// ohos or emulator
// FA or Stage
// release or debug hap : aa start
//                        aa start -D
//                        aa start -p
//                        new worker
bool JSNApi::NotifyDebugMode([[maybe_unused]] int tid,
                             [[maybe_unused]] EcmaVM *vm,
                             [[maybe_unused]] const DebugOption &option,
                             [[maybe_unused]] int32_t instanceId,
                             [[maybe_unused]] const DebuggerPostTask &debuggerPostTask,
                             [[maybe_unused]] bool debugApp)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
    LOG_ECMA(INFO) << "JSNApi::NotifyDebugMode, tid = " << tid << ", debugApp = " << debugApp
        << ", isDebugMode = " << option.isDebugMode << ", instanceId = " << instanceId;
    if (vm == nullptr) {
        LOG_ECMA(ERROR) << "[NotifyDebugMode] vm is nullptr";
        return false;
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);

    bool ret = false;
    if (!debugApp) {
        return true;
    }

    if (option.libraryPath == nullptr) {
        LOG_ECMA(ERROR) << "[NotifyDebugMode] option.libraryPath is nullptr";
        return false;
    }
    JsDebuggerManager *jsDebuggerManager = vm->GetJsDebuggerManager();
    auto handle = panda::os::library_loader::Load(std::string(option.libraryPath));
    if (!handle) {
        LOG_ECMA(ERROR) << "[NotifyDebugMode] Load library fail: " << option.libraryPath << " " << errno;
        return false;
    }
    JsDebuggerManager::AddJsDebuggerManager(tid, jsDebuggerManager);
    jsDebuggerManager->SetDebugLibraryHandle(std::move(handle.Value()));
    jsDebuggerManager->SetDebugMode(option.isDebugMode && debugApp);
    jsDebuggerManager->SetIsDebugApp(debugApp);
    jsDebuggerManager->SetFaApp(option.isFaApp);
#ifdef PANDA_TARGET_ARM32
    ret = StartDebuggerForOldProcess(vm, option, instanceId, debuggerPostTask);
#else
    ret = true;
#endif

    // store debugger postTask in inspector.
    using StoreDebuggerInfo = void (*)(int, EcmaVM *, const DebuggerPostTask &);
    auto symOfStoreDebuggerInfo = panda::os::library_loader::ResolveSymbol(
        jsDebuggerManager->GetDebugLibraryHandle(), "StoreDebuggerInfo");
    if (!symOfStoreDebuggerInfo) {
        LOG_ECMA(ERROR) << "[NotifyDebugMode] Resolve StoreDebuggerInfo symbol fail: " <<
            symOfStoreDebuggerInfo.Error().ToString();
        return false;
    }
    reinterpret_cast<StoreDebuggerInfo>(symOfStoreDebuggerInfo.Value())(tid, vm, debuggerPostTask);

#ifndef PANDA_TARGET_ARM32
    // Initialize debugger
    using InitializeDebuggerForSocketpair = bool(*)(void*, bool);
    auto sym = panda::os::library_loader::ResolveSymbol(
        jsDebuggerManager->GetDebugLibraryHandle(), "InitializeDebuggerForSocketpair");
    if (!sym) {
        LOG_ECMA(ERROR) << "[NotifyDebugMode] Resolve InitializeDebuggerForSocketpair symbol fail: "
            << sym.Error().ToString();
        return false;
    }
    // false: not hybrid
    if (!reinterpret_cast<InitializeDebuggerForSocketpair>(sym.Value())(vm, false)) {
        LOG_ECMA(ERROR) << "[NotifyDebugMode] InitializeDebuggerForSocketpair fail";
        return false;
    }
#endif

    if (option.isDebugMode) {
        using WaitForDebugger = void (*)(EcmaVM *);
        auto symOfWaitForDebugger = panda::os::library_loader::ResolveSymbol(
            jsDebuggerManager->GetDebugLibraryHandle(), "WaitForDebugger");
        if (!symOfWaitForDebugger) {
            LOG_ECMA(ERROR) << "[NotifyDebugMode] Resolve symbol WaitForDebugger fail: " <<
                symOfWaitForDebugger.Error().ToString();
            return false;
        }
        reinterpret_cast<WaitForDebugger>(symOfWaitForDebugger.Value())(vm);
    }
    auto anFileDataMgr = ecmascript::AnFileDataManager::GetInstance();
    if (anFileDataMgr != nullptr && anFileDataMgr->SafeGetStubFileInfo()) {
        anFileDataMgr->SafeGetStubFileInfo()->RegisterToDebugger();
    }
    return ret;

#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
    return false;
#endif // ECMASCRIPT_SUPPORT_DEBUGGER
}

bool JSNApi::StoreDebugInfo([[maybe_unused]] int tid,
                            [[maybe_unused]] EcmaVM *vm,
                            [[maybe_unused]] const DebugOption &option,
                            [[maybe_unused]] const DebuggerPostTask &debuggerPostTask,
                            [[maybe_unused]] bool debugApp)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
    LOG_ECMA(INFO) << "JSNApi::StoreDebugInfo, tid = " << tid;
    if (vm == nullptr) {
        LOG_ECMA(ERROR) << "[StoreDebugInfo] vm is nullptr";
        return false;
    }

    JsDebuggerManager *jsDebuggerManager = vm->GetJsDebuggerManager();
    const auto &handler = jsDebuggerManager->GetDebugLibraryHandle();
    if (handler.IsValid()) {
        LOG_ECMA(INFO) << "[StoreDebugInfo] handler has already loaded";
        return false;
    }

    if (option.libraryPath == nullptr) {
        LOG_ECMA(ERROR) << "[StoreDebugInfo] option.libraryPath is nullptr";
        return false;
    }
    auto handle = panda::os::library_loader::Load(std::string(option.libraryPath));
    if (!handle) {
        LOG_ECMA(ERROR) << "[StoreDebugInfo] Load library fail: " << option.libraryPath << " " << errno;
        return false;
    }
    JsDebuggerManager::AddJsDebuggerManager(tid, jsDebuggerManager);
    jsDebuggerManager->SetDebugLibraryHandle(std::move(handle.Value()));
    jsDebuggerManager->SetDebugMode(option.isDebugMode && debugApp);
    jsDebuggerManager->SetIsDebugApp(debugApp);
    // store debugger postTask in inspector.
    using StoreDebuggerInfo = void (*)(int, EcmaVM *, const DebuggerPostTask &);
    auto symOfStoreDebuggerInfo = panda::os::library_loader::ResolveSymbol(
        jsDebuggerManager->GetDebugLibraryHandle(), "StoreDebuggerInfo");
    if (!symOfStoreDebuggerInfo) {
        LOG_ECMA(ERROR) << "[StoreDebugInfo] Resolve StoreDebuggerInfo symbol fail: " <<
            symOfStoreDebuggerInfo.Error().ToString();
        return false;
    }
    reinterpret_cast<StoreDebuggerInfo>(symOfStoreDebuggerInfo.Value())(tid, vm, debuggerPostTask);
    bool ret = false;
    using InitializeDebuggerForSocketpair = bool(*)(void*, bool);
    auto sym = panda::os::library_loader::ResolveSymbol(handler, "InitializeDebuggerForSocketpair");
    if (!sym) {
        LOG_ECMA(ERROR) << "[InitializeDebuggerForSocketpair] Resolve symbol fail: " << sym.Error().ToString();
        return false;
    }
    // false: not hybrid
    ret = reinterpret_cast<InitializeDebuggerForSocketpair>(sym.Value())(vm, false);
    if (!ret) {
    // Reset the config
        vm->GetJsDebuggerManager()->SetDebugMode(false);
        return false;
    }
    return ret;
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
    return false;
#endif // ECMASCRIPT_SUPPORT_DEBUGGER
}

bool JSNApi::StopDebugger([[maybe_unused]] EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
#if !defined(PANDA_TARGET_IOS)
    LOG_ECMA(DEBUG) << "JSNApi::StopDebugger";
    if (vm == nullptr) {
        LOG_ECMA(ERROR) << "[StopDebugger] vm is nullptr";
        return false;
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);

    const auto &handle = vm->GetJsDebuggerManager()->GetDebugLibraryHandle();

    using StopDebug = void (*)(void *, bool);

    auto sym = panda::os::library_loader::ResolveSymbol(handle, "StopDebug");
    if (!sym) {
        LOG_ECMA(WARN) << sym.Error().ToString();
        return false;
    }
    // false: not hybrid
    reinterpret_cast<StopDebug>(sym.Value())(vm, false);

    vm->GetJsDebuggerManager()->SetDebugMode(false);
    uint32_t tid = vm->GetTid();
    JsDebuggerManager::DeleteJsDebuggerManager(tid);
    return true;
#else
    if (vm == nullptr) {
        LOG_ECMA(ERROR) << "[StopDebugger] vm is nullptr";
        return false;
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);

    OHOS::ArkCompiler::Toolchain::StopDebug(vm);
    vm->GetJsDebuggerManager()->SetDebugMode(false);
    return true;
#endif // PANDA_TARGET_IOS
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
    return false;
#endif // ECMASCRIPT_SUPPORT_DEBUGGER
}

bool JSNApi::StopDebugger([[maybe_unused]] int tid)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
    LOG_ECMA(DEBUG) << "JSNApi::StopDebugger, tid = " << tid;
    JsDebuggerManager *jsDebuggerManager = JsDebuggerManager::GetJsDebuggerManager(tid);
    if (jsDebuggerManager == nullptr) {
        LOG_ECMA(ERROR) << "[StopDebugger] jsDebuggerManager is nullptr";
        return false;
    }

    const auto &handle = jsDebuggerManager->GetDebugLibraryHandle();

    using StopOldDebug = void (*)(int, const std::string &);

    auto sym = panda::os::library_loader::ResolveSymbol(handle, "StopOldDebug");
    if (!sym) {
        LOG_ECMA(ERROR) << sym.Error().ToString();
        return false;
    }

    reinterpret_cast<StopOldDebug>(sym.Value())(tid, "PandaDebugger");

    return true;
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
    return false;
#endif // ECMASCRIPT_SUPPORT_DEBUGGER
}

bool JSNApi::IsMixedDebugEnabled([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
    return vm->GetJsDebuggerManager()->IsMixedDebugEnabled();
#else
    return false;
#endif
}

bool JSNApi::IsDebugModeEnabled([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    if (vm != nullptr && vm->GetJsDebuggerManager() != nullptr) {
        return vm->GetJsDebuggerManager()->IsDebugMode();
    }
    return false;
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
    return false;
#endif
}

void JSNApi::NotifyNativeCalling([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] const void *nativeAddress)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    vm->GetJsDebuggerManager()->GetNotificationManager()->NativeCallingEvent(nativeAddress);
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
#endif
}

void JSNApi::NotifyNativeReturn([[maybe_unused]] const EcmaVM *vm,  [[maybe_unused]] const void *nativeAddress)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    vm->GetJsDebuggerManager()->GetNotificationManager()->NativeReturnEvent(nativeAddress);
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
#endif
}

void JSNApi::NotifyLoadModule([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_DEBUGGER)
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    // if load module, it needs to check whether clear singlestepper_
    vm->GetJsDebuggerManager()->ClearSingleStepper();
#else
    LOG_ECMA(ERROR) << "Not support arkcompiler debugger";
#endif
}

void JSNApi::NotifyUIIdle(const EcmaVM *vm, [[maybe_unused]] int idleTime)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    vm->GetHeap()->GetIdleGCTrigger()->NotifyVsyncIdleStart();
}

bool JSNApi::NotifyLooperIdleStart(const EcmaVM *vm, int64_t timestamp, int idleTime)
{
    if (vm->IsPostForked()) {
        return vm->GetHeap()->GetIdleGCTrigger()->NotifyLooperIdleStart(timestamp, idleTime);
    }
    return false;
}

void JSNApi::NotifyLooperIdleEnd(const EcmaVM *vm, int64_t timestamp)
{
    if (vm->IsPostForked()) {
        vm->GetHeap()->GetIdleGCTrigger()->NotifyLooperIdleEnd(timestamp);
    }
}

bool JSNApi::IsJSMainThreadOfEcmaVM(const EcmaVM *vm)
{
    return vm->GetJSThread()->IsMainThreadFast();
}

void JSNApi::SetDeviceDisconnectCallback(EcmaVM *vm, DeviceDisconnectCallback cb)
{
    vm->SetDeviceDisconnectCallback(cb);
}

bool JSNApi::KeyIsNumber(const char* utf8)
{
    const char *ptr = utf8;
    for (char c = *ptr; c; c = *++ptr) {
        if (c >= '0' && c <= '9') {
            continue;
        } else {
            return false;
        }
    }
    return true;
}

bool JSNApi::IsSerializationTimeoutCheckEnabled(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    // Open Control Timeout Consumption
    if (const_cast<EcmaVM *>(vm)->GetJSOptions().EnableSerializationTimeoutCheck()) {
        return thread->IsMainThread();
    }

    // Currently only log trace on main thread
    auto jsDebuggerManager = vm->GetJsDebuggerManager();
    if (jsDebuggerManager != nullptr) {
        if (jsDebuggerManager->IsSerializationTimeoutCheckEnabled()) {
            return thread->IsMainThread();
        }
    }
    return false;
}

void JSNApi::GenerateTimeoutTraceIfNeeded(const EcmaVM *vm, std::chrono::system_clock::time_point &start,
                                          std::chrono::system_clock::time_point &end, bool isSerialization)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope scope(thread);
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    auto threshold = std::chrono::duration_cast<std::chrono::milliseconds>
            (std::chrono::milliseconds(vm->GetJsDebuggerManager()->GetSerializationCheckThreshold())).count();
    LOG_ECMA(DEBUG) << "JSNAPI::" << (isSerialization ? "SerializeValue" : "DeserializeValue") << " tid: "
        << thread->GetThreadId() << " threshold: " << threshold << " duration: " << duration;
    if (duration >= threshold) {
        std::stringstream tagMsg;
        auto startTimeMS = std::chrono::time_point_cast<std::chrono::nanoseconds>(start);
        tagMsg << (isSerialization ? "SerializationTimeout::tid=" : "DeserializationTimeout::tid=");
        tagMsg << thread->GetThreadId();
        tagMsg << (isSerialization ? ";task=serialization;startTime=" : ";task=deserialization;startTime=");
        tagMsg << startTimeMS.time_since_epoch().count() << ";duration=" << duration;
        ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, tagMsg.str().c_str(), "");
    }
}

void JSNApi::LoadAotFile(EcmaVM *vm, const std::string &moduleName)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope scope(thread);

    std::string aotFileName = ecmascript::AOTFileManager::GetAOTFileFullPath(vm, moduleName);
    if (aotFileName.empty()) {
        LOG_ECMA(INFO) << "can not find aot file";
        return;
    }
    // Disable PGO for applications when an/ai file exists
    if (isForked_) {
        vm->DisablePGOProfilerWithAOTFile(aotFileName);
    }
    LOG_ECMA(INFO) << "start to load aot file: " << aotFileName;
    vm->LoadAOTFiles(aotFileName);
}

// function for bundle abc
bool JSNApi::ExecuteForAbsolutePath(const EcmaVM *vm, const std::string &fileName, const std::string &entry,
                                    bool needUpdate, const ecmascript::ExecuteTypes &executeType)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    LOG_ECMA(DEBUG) << "start to execute absolute path ark file: " << fileName;
    ecmascript::ThreadManagedScope scope(thread);
    if (!ecmascript::JSPandaFileExecutor::ExecuteFromAbsolutePathAbcFile(
        thread, fileName.c_str(), entry, needUpdate, executeType)) {
        if (thread->HasPendingException()) {
            ecmascript::JsStackInfo::BuildCrashInfo(thread);
            thread->HandleUncaughtException();
        }
        LOG_ECMA(ERROR) << "Cannot execute absolute path ark file '" << fileName
                        << "' with entry '" << entry << "'" << std::endl;
        return false;
    }
    return true;
}

bool JSNApi::Execute(const EcmaVM *vm, const std::string &fileName, const std::string &entry,
                     bool needUpdate, const ecmascript::ExecuteTypes &executeType)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    LOG_ECMA(DEBUG) << "start to execute ark file: " << fileName;
    ecmascript::ThreadManagedScope scope(thread);
    if (!ecmascript::JSPandaFileExecutor::ExecuteFromAbcFile(
        thread, fileName.c_str(), entry, needUpdate, executeType)) {
        if (thread->HasPendingException()) {
            ecmascript::JsStackInfo::BuildCrashInfo(thread);
            thread->HandleUncaughtException();
        }
        LOG_ECMA(ERROR) << "Cannot execute ark file '" << fileName
                        << "' with entry '" << entry << "'" << std::endl;
        return false;
    }
    return true;
}

// The security interface needs to be modified accordingly.
bool JSNApi::Execute(EcmaVM *vm, const uint8_t *data, int32_t size, const std::string &entry,
                     const std::string &filename, bool needUpdate, [[maybe_unused]] void* fileMapper)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    LOG_ECMA(DEBUG) << "start to execute ark buffer: " << filename;
    ecmascript::ThreadManagedScope scope(thread);
    if (!ecmascript::JSPandaFileExecutor::ExecuteFromBuffer(thread, data, size, entry, filename.c_str(), needUpdate)) {
        if (thread->HasPendingException()) {
            ecmascript::JsStackInfo::BuildCrashInfo(thread);
            thread->HandleUncaughtException();
        }
        LOG_ECMA(ERROR) << "Cannot execute ark buffer file '" << filename
                        << "' with entry '" << entry << "'" << std::endl;
        return false;
    }
    return true;
}

int JSNApi::ExecuteWithSingletonPatternFlag(EcmaVM *vm, const std::string &bundleName,
    const std::string &moduleName, const std::string &ohmurl, bool isSingletonPattern)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, ecmascript::JSPandaFileExecutor::ROUTE_INTERNAL_ERROR);
    ecmascript::ThreadManagedScope scope(thread);
    int result = ecmascript::JSPandaFileExecutor::ExecuteAbcFileWithSingletonPatternFlag(thread, bundleName.c_str(),
        moduleName.c_str(), ohmurl.c_str(), isSingletonPattern);
    if (!result) {
        if (thread->HasPendingException()) {
            ecmascript::JsStackInfo::BuildCrashInfo(thread);
            thread->HandleUncaughtException();
        }
    }
    return result;
}

bool JSNApi::IsExecuteModuleInAbcFile(EcmaVM *vm, const std::string &bundleName,
    const std::string &moduleName, const std::string &ohmurl)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope scope(thread);
    bool result = ecmascript::JSPandaFileExecutor::IsExecuteModuleInAbcFile(thread, bundleName.c_str(),
        moduleName.c_str(), ohmurl.c_str());
    if (thread->HasPendingException()) {
        thread->ClearException();
    }
    return result;
}

/*
 * srcFilename: data/storage/el1/bundle/modulename/ets/modules.abc
 * ohmUrl : @normalized:N&moduleName&bundleName&entryPath&
 */
bool JSNApi::FindModuleInAbcFile(EcmaVM *vm, const std::string &srcFilename, const std::string &ohmUrl)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope scope(thread);
    bool result = ecmascript::JSPandaFileExecutor::FindModuleInAbcFile(thread, srcFilename.c_str(), ohmUrl.c_str());
    return result;
}

// The security interface needs to be modified accordingly.
bool JSNApi::ExecuteModuleBuffer(EcmaVM *vm, const uint8_t *data, int32_t size, const std::string &filename,
                                 bool needUpdate)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    LOG_ECMA(DEBUG) << "start to execute module buffer: " << filename;
    ecmascript::ThreadManagedScope scope(thread);
    if (!ecmascript::JSPandaFileExecutor::ExecuteModuleBuffer(thread, data, size, filename.c_str(), needUpdate)) {
        if (thread->HasPendingException()) {
            ecmascript::JsStackInfo::BuildCrashInfo(thread);
            thread->HandleUncaughtException();
        }
        LOG_ECMA(ERROR) << "Cannot execute module buffer file '" << filename;
        return false;
    }
    return true;
}

/*
 * srcFilename: data/storage/el1/bundle/modulename/ets/modules.abc
 * ohmUrl :     1. @bundle:bundleName/moduleName@namespace/ets/pages/Index
 *              2. @package:pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx/xxx
 *              3. @normalized:N&moduleName&bundleName&entryPath&version
 *              4. @normalized:N&moduleName&bundleName&entryPath&
 */
bool JSNApi::ExecuteSecureWithOhmUrl(EcmaVM *vm, uint8_t *data, int32_t size, const std::string &srcFilename,
                                     const std::string &ohmUrl)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    LOG_ECMA(INFO) << "start to execute ark buffer with secure memory use file: " << srcFilename <<
                      ", entrypoint: " << ohmUrl;
    ecmascript::ThreadManagedScope scope(thread);
    ecmascript::CString filename = PathHelper::NormalizePath(srcFilename.c_str());
    // check input filePath
#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS)
    if (!ModulePathHelper::ValidateAbcPath(filename, ecmascript::ValidateFilePath::ETS_MODULES)) {
        LOG_FULL(FATAL) << "ExecuteSecureWithOhmUrl: Invalid input filePath: " << srcFilename <<
                           ", input OhmUrl:" << ohmUrl;
    }
#endif
    ecmascript::CString entryPoint;
    // Check and translate OhmUrl to recordName
    if (!ModulePathHelper::CheckAndGetRecordName(thread, ohmUrl.c_str(), entryPoint)) {
        LOG_FULL(FATAL) << "ExecuteSecureWithOhmUrl: Invalid input OhmUrl: " << ohmUrl <<
                           ", input filePath:" << filename;
    }
    if (!ecmascript::JSPandaFileExecutor::ExecuteSecureWithOhmUrl(thread, data, size, filename, entryPoint)) {
        if (thread->HasPendingException()) {
            ecmascript::JsStackInfo::BuildCrashInfo(thread);
            thread->HandleUncaughtException();
        }
        LOG_ECMA(ERROR) << "Cannot execute ark buffer file '" << srcFilename
                        << "' with entry '" << ohmUrl << "'" << std::endl;
        return false;
    }
    return true;
}

// file mapper only used for secure memory
bool JSNApi::ExecuteSecure(EcmaVM *vm, uint8_t *data, int32_t size, const std::string &entry,
                           const std::string &filename, bool needUpdate, void *fileMapper)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    LOG_ECMA(INFO) << "start to execute ark buffer with secure memory: " << filename;
    ecmascript::ThreadManagedScope scope(thread);
    if (!ecmascript::JSPandaFileExecutor::ExecuteFromBufferSecure(
        thread, data, size, entry, filename.c_str(), needUpdate, fileMapper)) {
        if (thread->HasPendingException()) {
            ecmascript::JsStackInfo::BuildCrashInfo(thread);
            thread->HandleUncaughtException();
        }
        LOG_ECMA(ERROR) << "Cannot execute ark buffer file '" << filename
                        << "' with entry '" << entry << "'" << std::endl;
        return false;
    }
    return true;
}

bool JSNApi::ExecuteModuleBufferSecure(EcmaVM *vm, uint8_t* data, int32_t size, const std::string &filename,
                                       bool needUpdate, void *fileMapper)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    LOG_ECMA(INFO) << "start to execute module buffer with secure memory: " << filename;
    ecmascript::ThreadManagedScope scope(thread);
    if (!ecmascript::JSPandaFileExecutor::ExecuteModuleBufferSecure(thread, data, size, filename.c_str(),
                                                                    needUpdate, fileMapper)) {
        if (thread->HasPendingException()) {
            ecmascript::JsStackInfo::BuildCrashInfo(thread);
            thread->HandleUncaughtException();
        }
        LOG_ECMA(ERROR) << "Cannot execute module buffer file '" << filename;
        return false;
    }
    return true;
}

void JSNApi::PreFork(EcmaVM *vm)
{
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    vm->PreFork();
}

void JSNApi::UpdateAOTCompileStatus(ecmascript::JSRuntimeOptions &jsOption, const RuntimeOption &option)
{
    // When AOT compilation failed, disable PGO and JIT
    bool aotHasException = false;
    for (const auto &value : option.GetAOTCompileStatusMap()) {
        auto moduleCompileStatus = static_cast<RuntimeOption::AOTCompileStatus>(value.second);
        if (moduleCompileStatus == RuntimeOption::AOTCompileStatus::COMPILE_FAILED ||
            moduleCompileStatus == RuntimeOption::AOTCompileStatus::COMPILE_CRASH) {
            aotHasException = true;
            break;
        }
    }
    jsOption.SetAOTHasException(aotHasException);
}

void JSNApi::PostFork(EcmaVM *vm, const RuntimeOption &option)
{
    isForked_ = true;
    JSRuntimeOptions &jsOption = vm->GetJSOptions();
    jsOption.SetEnablePGOProfiler(option.GetEnableProfile());
    jsOption.SetEnableJIT(option.GetEnableJIT());
    jsOption.SetEnableDFXHiSysEvent(option.GetEnableDFXHiSysEvent());
    jsOption.SetEnableBaselineJIT(option.GetEnableBaselineJIT());
    jsOption.SetMaxAotMethodSize(JSRuntimeOptions::MAX_APP_COMPILE_METHOD_SIZE);
    jsOption.SetArkTSMode(option.GetArkTSMode());
    ecmascript::pgo::PGOProfilerManager::GetInstance()->SetBundleName(option.GetBundleName());
    ecmascript::pgo::PGOProfilerManager::GetInstance()->SetMaxAotMethodSize(jsOption.GetMaxAotMethodSize());
    JSRuntimeOptions runtimeOptions;
    runtimeOptions.SetLogLevel(common::Log::LevelToString(common::Log::ConvertFromRuntime(option.GetLogLevel())));
    common::Log::Initialize(runtimeOptions.GetLogOptions());

    // 1. system switch 2. an file dir exits 3. whitelist 4. escape mechanism
    bool enableAOT = jsOption.GetEnableAOT() &&
                     !option.GetAnDir().empty() &&
                     EnableAotJitListHelper::GetInstance()->IsEnableAot(option.GetBundleName()) &&
                     !ecmascript::AotCrashInfo::IsAotEscaped();
    if (enableAOT) {
        ecmascript::AnFileDataManager::GetInstance()->SetDir(option.GetAnDir());
        ecmascript::AnFileDataManager::GetInstance()->SetEnable(true);
    }
    UpdateAOTCompileStatus(jsOption, option);

    LOG_ECMA(INFO) << "asmint: " << jsOption.GetEnableAsmInterpreter()
                    << ", aot: " << enableAOT
                    << ", jit: " << option.GetEnableJIT()
                    << ", baseline jit: " << option.GetEnableBaselineJIT()
                    << ", bundle name: " <<  option.GetBundleName()
                    << ", arkTSMode: " << option.GetArkTSMode();
    jsOption.SetEnableWarmStartupSmartGC(option.GetEnableWarmStartupSmartGC());
    vm->PostFork(jsOption);
}

void JSNApi::UpdateArkTSMode(EcmaVM *vm, const std::string &arkTSMode)
{
    JSRuntimeOptions &jsOption = vm->GetJSOptions();
    jsOption.SetArkTSMode(arkTSMode);
}

void JSNApi::AddWorker(EcmaVM *hostVm, EcmaVM *workerVm)
{
    if (hostVm != nullptr && workerVm != nullptr) {
        hostVm->WorkersetInfo(workerVm);
        workerVm->SetBundleName(hostVm->GetBundleName());
    }
}

bool JSNApi::DeleteWorker(EcmaVM *hostVm, EcmaVM *workerVm)
{
    if (hostVm != nullptr && workerVm != nullptr) {
        return hostVm->DeleteWorker(workerVm);
    }
    return false;
}

Local<ObjectRef> JSNApi::GetUncaughtException(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToLocal<ObjectRef>(vm->GetEcmaUncaughtException());
}

Local<ObjectRef> JSNApi::GetAndClearUncaughtException(const EcmaVM *vm)
{
    if (LIKELY(!HasPendingException(vm))) {
        return Local<ObjectRef>();
    }
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return JSNApiHelper::ToLocal<ObjectRef>(vm->GetAndClearEcmaUncaughtException());
}

bool JSNApi::HasPendingException(const EcmaVM *vm)
{
    return vm->GetJSThread()->HasPendingException();
}

bool JSNApi::IsExecutingPendingJob(const EcmaVM *vm)
{
    return EcmaVM::ConstCast(vm)->IsExecutingPendingJob();
}

bool JSNApi::HasPendingJob(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return EcmaVM::ConstCast(vm)->HasPendingJob();
}

void JSNApi::EnableUserUncaughtErrorHandler(EcmaVM *vm)
{
    return vm->GetJSThread()->EnableUserUncaughtErrorHandler();
}

Local<ObjectRef> JSNApi::GetGlobalObject(const EcmaVM *vm)
{
    auto thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<GlobalEnv> globalEnv = vm->GetGlobalEnv();
    JSHandle<JSTaggedValue> global(thread, globalEnv->GetGlobalObject());
    return JSNApiHelper::ToLocal<ObjectRef>(global);
}

Local<ObjectRef> JSNApi::GetGlobalObject(const EcmaVM *vm, const Local<JSValueRef> &context)
{
    JSThread* thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope scope(thread);
    if (!context->IsJsGlobalEnv(vm)) {
        LOG_ECMA(WARN) << "Attempted to call GetGlobalObject on a non-JsGlobalEnv object.";
        return JSValueRef::Undefined(vm);
    }
    JSHandle<GlobalEnv> globalEnv = JSHandle<GlobalEnv>(JSNApiHelper::ToJSHandle(context));
    JSHandle<JSTaggedValue> global(thread, globalEnv->GetGlobalObject());
    return JSNApiHelper::ToLocal<ObjectRef>(global);
}

void JSNApi::ExecutePendingJob(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    EcmaVM::ConstCast(vm)->ExecutePromisePendingJob();
}

uintptr_t JSNApi::GetHandleAddr(const EcmaVM *vm, uintptr_t localAddress)
{
    if (localAddress == 0) {
        return 0;
    }
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope scope(thread);
    JSTaggedType value = *(reinterpret_cast<JSTaggedType *>(localAddress));
    return ecmascript::EcmaHandleScope::NewHandle(thread, value);
}

uintptr_t JSNApi::GetGlobalHandleAddr(const EcmaVM *vm, uintptr_t localAddress)
{
    if (localAddress == 0) {
        return 0;
    }
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope scope(thread);
    JSTaggedType value = *(reinterpret_cast<JSTaggedType *>(localAddress));
    return thread->NewGlobalHandle(value);
}

int JSNApi::GetStartRealTime(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    return vm->GetProcessStartRealtime();
}

void JSNApi::NotifyTaskBegin(const EcmaVM *vm)
{
    const_cast<ecmascript::Heap *>(vm->GetHeap())->NotifyRecordMemorySize();
}

void JSNApi::NotifyTaskFinished(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    const_cast<ecmascript::Heap *>(vm->GetHeap())->CheckAndTriggerTaskFinishedGC();
}

bool JSNApi::IsMultiThreadCheckEnabled(const EcmaVM *vm)
{
    return vm->GetThreadCheckStatus();
}

uint32_t JSNApi::GetCurrentThreadId()
{
    return JSThread::GetCurrentThreadId();
}

void JSNApi::SetVMAPIVersion(EcmaVM *vm, const int32_t apiVersion)
{
    vm->SetVMAPIVersion(static_cast<uint32_t>(apiVersion) % API_VERSION_MASK);
}

std::vector<ecmascript::HeapMemoryInfo> JSNApi::GetAllVMHeapMemoryInfo()
{
    std::vector<ecmascript::HeapMemoryInfo> heapInfos;
    auto runtime = ecmascript::Runtime::GetInstance();
    ecmascript::SuspendAllScopeFromExternal suspendAllScope(nullptr);

    runtime->GCIterateThreadList([&heapInfos](JSThread* thread) {
        ecmascript::HeapMemoryInfo info;
        info.threadId = thread->GetThreadId();
        info.threadName = thread->GetThreadName();
        info.heapType = "local";
        info.heapObjectSize = 0;

        auto threadVm = thread->GetEcmaVM();
        ASSERT(threadVm != nullptr);
        auto* heap = threadVm->GetHeap();
        ASSERT(heap != nullptr);
        size_t heapSize = heap->GetHeapObjectSize();
        info.heapObjectSize = static_cast<uint32_t>(AlignUp(heapSize, 1_KB) / 1_KB);

        heapInfos.push_back(info);
    });

    auto* sharedHeap = ecmascript::SharedHeap::GetInstance();
    ecmascript::HeapMemoryInfo sharedInfo;
    sharedInfo.threadId = 0;
    sharedInfo.threadName = "[SharedHeap]";
    sharedInfo.heapType = "shared";
    size_t sharedSize = sharedHeap->GetHeapObjectSize();
    sharedInfo.heapObjectSize = static_cast<uint32_t>(AlignUp(sharedSize, 1_KB) / 1_KB);
    heapInfos.push_back(sharedInfo);

    return heapInfos;
}

void JSNApi::UpdateStackInfo(EcmaVM *vm, void *currentStackInfo, uint32_t opKind)
{
    vm->GetJSThread()->UpdateStackInfo(currentStackInfo, static_cast<ecmascript::JSThread::StackInfoOpKind>(opKind));
}

Local<JSValueRef> JSNApi::CreateContext(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    thread->SetMultiContextTriggered(true);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<GlobalEnv> globalEnv = factory->NewGlobalEnv();
    auto notificationMgr = vm->GetJsDebuggerManager()->GetNotificationManager();
    notificationMgr->SetDebuggerAccessorEvent(globalEnv);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(globalEnv));
}

Local<JSValueRef> JSNApi::GetCurrentContext(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<GlobalEnv> currentGlobalEnv = thread->GetGlobalEnv();
    JSHandle<JSTaggedValue> envContext = JSHandle<JSTaggedValue>(currentGlobalEnv);
    return JSNApiHelper::ToLocal<JSValueRef>(envContext);
}

int JSNApi::SwitchContext(const EcmaVM *vm, const Local<JSValueRef> &context)
{
    if (context.IsEmpty()) {
        return static_cast<int>(SwitchContextResult::EMPTY);
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, static_cast<int>(SwitchContextResult::FAILED));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSTaggedValue> contextValue = JSNApiHelper::ToJSHandle(context);
    JSHandle<GlobalEnv> globalEnv = JSHandle<GlobalEnv>(contextValue);
    thread->SetGlueGlobalEnv(globalEnv.GetTaggedValue());
    return static_cast<int>(SwitchContextResult::SUCCESS);
}

uintptr_t JSNApi::SetWeak(const EcmaVM *vm, uintptr_t localAddress)
{
    if (localAddress == 0) {
        return 0;
    }
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    CROSS_THREAD_CHECK(vm);
    return thread->SetWeak(localAddress);
}

uintptr_t JSNApi::SetWeakCallback(const EcmaVM *vm, uintptr_t localAddress, void *ref,
                                  WeakRefClearCallBack freeGlobalCallBack, WeakRefClearCallBack nativeFinalizeCallback)
{
    if (localAddress == 0) {
        return 0;
    }
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    CROSS_THREAD_CHECK(vm);
    return thread->SetWeak(localAddress, ref, freeGlobalCallBack, nativeFinalizeCallback);
}

uintptr_t JSNApi::ClearWeak(const EcmaVM *vm, uintptr_t localAddress)
{
    if (localAddress == 0) {
        return 0;
    }
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    if (JSTaggedValue(reinterpret_cast<ecmascript::Node *>(localAddress)->GetObject())
        .IsUndefined()) {
        LOG_ECMA(ERROR) << "The object of weak reference has been recycled!";
        return 0;
    }
    CROSS_THREAD_CHECK(vm);
    return thread->ClearWeak(localAddress);
}

bool JSNApi::IsWeak(const EcmaVM *vm, uintptr_t localAddress)
{
    if (localAddress == 0) {
        return false;
    }
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope scope(thread);
    return thread->IsWeak(localAddress);
}

void JSNApi::DisposeGlobalHandleAddr(const EcmaVM *vm, uintptr_t addr)
{
    if (addr == 0 || !reinterpret_cast<ecmascript::Node *>(addr)->IsUsing()) {
        return;
    }
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope scope(thread);
    thread->DisposeGlobalHandle(addr);
}

void *JSNApi::InterOpSerializeValue(const EcmaVM *vm, Local<JSValueRef> value, Local<JSValueRef> transfer,
                                    Local<JSValueRef> cloneList, bool defaultTransfer, bool defaultCloneShared)
{
    if (!ecmascript::Runtime::GetInstance()->IsHybridVm()) {
        return SerializeValue(vm, value, transfer, cloneList, defaultTransfer, defaultCloneShared);
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, nullptr);
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSTaggedValue> arkValue = JSNApiHelper::ToJSHandle(value);
    JSHandle<JSTaggedValue> arkTransfer = JSNApiHelper::ToJSHandle(transfer);
    JSHandle<JSTaggedValue> arkCloneList = JSNApiHelper::ToJSHandle(cloneList);
    bool serializationTimeoutCheckEnabled = IsSerializationTimeoutCheckEnabled(vm);
    std::chrono::system_clock::time_point startTime;
    std::chrono::system_clock::time_point endTime;
    if (serializationTimeoutCheckEnabled) {
        startTime = std::chrono::system_clock::now();
    }
    ecmascript::InterOpValueSerializer serializer(thread, defaultTransfer, defaultCloneShared);
    std::unique_ptr<ecmascript::SerializeData> data;
    if (serializer.WriteValue(thread, arkValue, arkTransfer, arkCloneList)) {
        data = serializer.Release();
    }
    if (serializationTimeoutCheckEnabled) {
        endTime = std::chrono::system_clock::now();
        GenerateTimeoutTraceIfNeeded(vm, startTime, endTime, true);
    }
    if (data == nullptr) {
        return nullptr;
    } else {
        return reinterpret_cast<void *>(data.release());
    }
}

Local<JSValueRef> JSNApi::InterOpDeserializeValue(const EcmaVM *vm, void *recoder, void *hint)
{
    if (!ecmascript::Runtime::GetInstance()->IsHybridVm()) {
        return DeserializeValue(vm, recoder, hint);
    }
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    EscapeLocalScope escapeScope(vm);
    std::unique_ptr<ecmascript::SerializeData> data(reinterpret_cast<ecmascript::SerializeData *>(recoder));
    ecmascript::InterOpValueDeserializer deserializer(thread, data.release(), hint);
    bool serializationTimeoutCheckEnabled = IsSerializationTimeoutCheckEnabled(vm);
    std::chrono::system_clock::time_point startTime;
    std::chrono::system_clock::time_point endTime;
    if (serializationTimeoutCheckEnabled) {
        startTime = std::chrono::system_clock::now();
    }
    JSHandle<JSTaggedValue> result = deserializer.ReadValue();
    if (serializationTimeoutCheckEnabled) {
        endTime = std::chrono::system_clock::now();
        GenerateTimeoutTraceIfNeeded(vm, startTime, endTime, false);
    }
    return escapeScope.Escape(JSNApiHelper::ToLocal<JSValueRef>(result));
}

void *JSNApi::SerializeValue(const EcmaVM *vm, Local<JSValueRef> value, Local<JSValueRef> transfer,
                             Local<JSValueRef> cloneList, bool defaultTransfer,
                             bool defaultCloneShared, bool needSerializeStack)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, nullptr);
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSTaggedValue> arkValue = JSNApiHelper::ToJSHandle(value);
    JSHandle<JSTaggedValue> arkTransfer = JSNApiHelper::ToJSHandle(transfer);
    JSHandle<JSTaggedValue> arkCloneList = JSNApiHelper::ToJSHandle(cloneList);
    bool serializationTimeoutCheckEnabled = IsSerializationTimeoutCheckEnabled(vm);
    std::chrono::system_clock::time_point startTime;
    std::chrono::system_clock::time_point endTime;
    if (serializationTimeoutCheckEnabled) {
        startTime = std::chrono::system_clock::now();
    }
    ecmascript::ValueSerializer serializer(thread, defaultTransfer, defaultCloneShared, needSerializeStack);
    std::unique_ptr<ecmascript::SerializeData> data;
    if (serializer.WriteValue(thread, arkValue, arkTransfer, arkCloneList)) {
        data = serializer.Release();
    }
    if (serializationTimeoutCheckEnabled) {
        endTime = std::chrono::system_clock::now();
        GenerateTimeoutTraceIfNeeded(vm, startTime, endTime, true);
    }
    if (data == nullptr) {
        return nullptr;
    } else {
        return reinterpret_cast<void *>(data.release());
    }
}

void *JSNApi::SerializeValueWithError(const EcmaVM *vm, Local<JSValueRef> value, Local<JSValueRef> transfer,
                                      Local<JSValueRef> cloneList, std::string &error, bool defaultTransfer,
                                      bool defaultCloneShared, bool needSerializeStack)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, nullptr);
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSTaggedValue> arkValue = JSNApiHelper::ToJSHandle(value);
    JSHandle<JSTaggedValue> arkTransfer = JSNApiHelper::ToJSHandle(transfer);
    JSHandle<JSTaggedValue> arkCloneList = JSNApiHelper::ToJSHandle(cloneList);
    bool serializationTimeoutCheckEnabled = IsSerializationTimeoutCheckEnabled(vm);
    std::chrono::system_clock::time_point startTime;
    std::chrono::system_clock::time_point endTime;
    if (serializationTimeoutCheckEnabled) {
        startTime = std::chrono::system_clock::now();
    }
    ecmascript::ValueSerializer serializer(thread, defaultTransfer, defaultCloneShared, needSerializeStack);
    std::unique_ptr<ecmascript::SerializeData> data;
    if (serializer.WriteValue(thread, arkValue, arkTransfer, arkCloneList)) {
        data = serializer.Release();
    }
    if (serializationTimeoutCheckEnabled) {
        endTime = std::chrono::system_clock::now();
        GenerateTimeoutTraceIfNeeded(vm, startTime, endTime, true);
    }
    if (data == nullptr) {
        error = serializer.Release()->GetErrorMessage();
        return nullptr;
    } else {
        return reinterpret_cast<void *>(data.release());
    }
}

void *JSNApi::SerializeValue(const EcmaVM *vm, Local<JSValueRef> value, Local<JSValueRef> transfer,
                             Local<JSValueRef> cloneList, const SerializeOptions& options)
{
    return SerializeValue(vm, value, transfer, cloneList,
                          options.defaultTransfer,
                          options.defaultCloneShared,
                          options.needSerializeStack);
}

void *JSNApi::SerializeValueWithError(const EcmaVM *vm, Local<JSValueRef> value, Local<JSValueRef> transfer,
                                      Local<JSValueRef> cloneList, std::string &error,
                                      const SerializeOptions& options)
{
    return SerializeValueWithError(vm, value, transfer, cloneList, error,
                                   options.defaultTransfer,
                                   options.defaultCloneShared,
                                   options.needSerializeStack);
}

Local<JSValueRef> JSNApi::DeserializeValue(const EcmaVM *vm, void *recoder, void *hint)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    EscapeLocalScope escapeScope(vm);
    std::unique_ptr<ecmascript::SerializeData> data(reinterpret_cast<ecmascript::SerializeData *>(recoder));
    ecmascript::BaseDeserializer deserializer(thread, data.release(), hint);
    bool serializationTimeoutCheckEnabled = IsSerializationTimeoutCheckEnabled(vm);
    std::chrono::system_clock::time_point startTime;
    std::chrono::system_clock::time_point endTime;
    if (serializationTimeoutCheckEnabled) {
        startTime = std::chrono::system_clock::now();
    }
    JSHandle<JSTaggedValue> result = deserializer.ReadValue();
    if (serializationTimeoutCheckEnabled) {
        endTime = std::chrono::system_clock::now();
        GenerateTimeoutTraceIfNeeded(vm, startTime, endTime, false);
    }
    return escapeScope.Escape(JSNApiHelper::ToLocal<JSValueRef>(result));
}

void JSNApi::DeleteSerializationData(void *data)
{
    ecmascript::SerializeData *value = reinterpret_cast<ecmascript::SerializeData *>(data);
    delete value;
    value = nullptr;
}

void HostPromiseRejectionTracker(const EcmaVM *vm,
                                 const JSHandle<JSPromise> promise,
                                 const JSHandle<JSTaggedValue> reason,
                                 const ecmascript::PromiseRejectionEvent operation,
                                 void* data)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::PromiseRejectCallback promiseRejectCallback = vm->GetPromiseRejectCallback();
    if (promiseRejectCallback != nullptr) {
        Local<JSValueRef> promiseVal = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>::Cast(promise));
        PromiseRejectInfo promiseRejectInfo(promiseVal, JSNApiHelper::ToLocal<JSValueRef>(reason),
                              static_cast<PromiseRejectInfo::PROMISE_REJECTION_EVENT>(operation), data);
        promiseRejectCallback(reinterpret_cast<void*>(&promiseRejectInfo));
    }
}

void JSNApi::SetHostPromiseRejectionTracker(EcmaVM *vm, void *cb, void* data)
{
    CROSS_THREAD_CHECK(vm);
    vm->SetHostPromiseRejectionTracker(HostPromiseRejectionTracker);
    vm->SetPromiseRejectCallback(reinterpret_cast<ecmascript::PromiseRejectCallback>(cb));
    vm->SetPromiseRejectInfoData(data);
}

void JSNApi::SetTimerTaskCallback(EcmaVM *vm, TimerTaskCallback callback)
{
    CROSS_THREAD_CHECK(vm);
    // register TimerTask to ark_js_runtime
    vm->SetTimerTaskCallback(callback);
}

void JSNApi::SetCancelTimerCallback(EcmaVM *vm, CancelTimerCallback callback)
{
    CROSS_THREAD_CHECK(vm);
    // register CancelTimerCallback to ark_js_runtime
    vm->SetCancelTimerCallback(callback);
}

// post task after runtime initialized
void JSNApi::NotifyEnvInitialized(EcmaVM *vm)
{
    ecmascript::ModuleLogger::SetModuleLoggerTask(vm);
}

void JSNApi::SetReleaseSecureMemCallback(ReleaseSecureMemCallback releaseSecureMemFunc)
{
    // register release secure mem func to ark_js_runtime
    ecmascript::Runtime::GetInstance()->SetReleaseSecureMemCallback(releaseSecureMemFunc);
}

// Arkui trigger jsPandafile Seralize when cold start is end.
void JSNApi::PandaFileSerialize(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    if (const_cast<EcmaVM *>(vm)->GetJSOptions().DisableJSPandaFileAndModuleSnapshot()) {
        return;
    }
    ecmascript::CString path(ecmascript::ohos::OhosConstants::PANDAFILE_AND_MODULE_SNAPSHOT_DIR);
    ecmascript::ohos::JSPandaFileSnapshotInterfaces::Serialize(vm, path);
}

// Arkui trigger module Seralize when cold start is end.
void JSNApi::ModuleSerialize(const EcmaVM *vm)
{
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    JSRuntimeOptions &options = const_cast<EcmaVM *>(vm)->GetJSOptions();
    if (options.DisableJSPandaFileAndModuleSnapshot()) {
        return;
    }
    ecmascript::CString path(ecmascript::ohos::OhosConstants::PANDAFILE_AND_MODULE_SNAPSHOT_DIR);
    ecmascript::ohos::ConstPoolSnapshotInterfaces::Serialize(vm, path);

    if (options.DisableModuleSnapshot()) {
        return;
    }
    ecmascript::ohos::ModuleSnapshotInterfaces::Serialize(vm, path);
}

// Ability Runtime trigger module Deseralize when application start.
void JSNApi::ModuleDeserialize(EcmaVM *vm, const uint32_t appVersion)
{
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    if (vm->GetJSOptions().DisableJSPandaFileAndModuleSnapshot() || vm->GetJSOptions().DisableModuleSnapshot()) {
        return;
    }
    vm->SetApplicationVersionCode(appVersion);
    ecmascript::CString path(ecmascript::ohos::OhosConstants::PANDAFILE_AND_MODULE_SNAPSHOT_DIR);
    ecmascript::ohos::ModuleSnapshotInterfaces::Deserialize(vm, path);
}

void JSNApi::SetHostResolveBufferTracker(EcmaVM *vm,
    std::function<bool(std::string dirPath, uint8_t **buff, size_t *buffSize, std::string &errorMsg)> cb)
{
    vm->SetResolveBufferCallback(cb);
}

void JSNApi::SetHostResolveBufferTrackerForHybridApp(EcmaVM *vm,
    std::function<bool(std::string dirPath, uint8_t **buff, size_t *buffSize, std::string &errorMsg)> cb)
{
    vm->SetResolveBufferCallbackForHybridApp(cb);
}


void JSNApi::SetSearchHapPathTracker(EcmaVM *vm,
    std::function<bool(const std::string moduleName, std::string &hapPath)> cb)
{
    vm->SetSearchHapPathCallBack(cb);
}

void JSNApi::SetMultiThreadCheck(bool multiThreadCheck)
{
    EcmaVM::SetMultiThreadCheck(multiThreadCheck);
}

void JSNApi::SetErrorInfoEnhance(bool errorInfoEnhance)
{
    EcmaVM::SetErrorInfoEnhance(errorInfoEnhance);
}

void JSNApi::SetIsMainProcess(bool isMainProcess)
{
    ecmascript::Runtime::GetInstance()->SetIsMainProcess(isMainProcess);
}

void JSNApi::SetRequestAotCallback([[maybe_unused]] EcmaVM *vm, const std::function<int32_t
    (const std::string &bundleName, const std::string &moduleName, int32_t triggerMode)> &cb)
{
    ecmascript::pgo::PGOProfilerManager::GetInstance()->SetRequestAotCallback(cb);
}

void JSNApi::SetUnloadNativeModuleCallback(EcmaVM *vm, const std::function<bool(const std::string &moduleKey)> &cb)
{
    vm->SetUnloadNativeModuleCallback(cb);
}

void JSNApi::SetNativePtrGetter(EcmaVM *vm, void* cb)
{
    vm->SetNativePtrGetter(reinterpret_cast<ecmascript::NativePtrGetter>(cb));
}

void JSNApi::SetNativeReferenceDataGetter(EcmaVM *vm, void* cb)
{
    vm->SetNativeReferenceDataGetter(reinterpret_cast<ecmascript::NativeReferenceDataCallbackGetter>(cb));
}

void JSNApi::SetHostEnqueueJob(const EcmaVM *vm, Local<JSValueRef> cb, QueueType queueType)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSFunction> fun = JSHandle<JSFunction>::Cast(JSNApiHelper::ToJSHandle(cb));
    JSHandle<TaggedArray> array = vm->GetFactory()->EmptyArray();
    JSHandle<MicroJobQueue> job = vm->GetMicroJobQueue();
    MicroJobQueue::EnqueueJob(thread, job, queueType, fun, array);
}

bool JSNApi::ExecuteModuleFromBuffer(EcmaVM *vm, const void *data, int32_t size, const std::string &file)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope scope(thread);
    if (!ecmascript::JSPandaFileExecutor::ExecuteFromBuffer(thread, data, size, ENTRY_POINTER, file.c_str(), false,
        ecmascript::ExecuteTypes::NATIVE_MODULE)) {
        if (thread->HasPendingException()) {
            ecmascript::JsStackInfo::BuildCrashInfo(thread);
        }
        std::cerr << "Cannot execute panda file from memory" << std::endl;
        return false;
    }
    return true;
}

JSNApi::PandaFileType JSNApi::GetFileType(const uint8_t *data, int32_t size)
{
    panda_file::PandaFileType fileType = panda_file::GetFileType(data, size);
    switch (fileType) {
        case panda_file::PandaFileType::FILE_FORMAT_INVALID: {
            return JSNApi::PandaFileType::FILE_FORMAT_INVALID;
        }
        case panda_file::PandaFileType::FILE_DYNAMIC: {
            return JSNApi::PandaFileType::FILE_DYNAMIC;
        }
        case panda_file::PandaFileType::FILE_STATIC: {
            return JSNApi::PandaFileType::FILE_STATIC;
        }
        default: {
            UNREACHABLE();
        }
    }
}

Local<JSValueRef> JSNApi::NapiHasProperty(const EcmaVM *vm, uintptr_t nativeObj, uintptr_t key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSTaggedValue> obj(nativeObj);
    if (!(obj->IsECMAObject() || obj->IsCallable())) {
        // When input validation is failed, we return JSTaggedValue::Hole to napi native engine.
        // Using JSTaggedValue::Hole as the "hand-shaking-protocol" to tell native engine to change error state.
        JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(holeHandle));
    }
    LOG_IF_SPECIAL(obj, ERROR);
    JSMutableHandle<JSTaggedValue> keyValue(key);
    JSTaggedValue res = ObjectFastOperator::TryFastHasProperty(thread, obj.GetTaggedValue(),
                                                               keyValue);
    if (!res.IsHole()) {
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, res)));
    }
    auto ret = JSTaggedValue(JSTaggedValue::HasProperty(thread, obj, keyValue));
    return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, ret)));
}

Local<JSValueRef> JSNApi::NapiHasOwnProperty(const EcmaVM *vm, uintptr_t nativeObj, uintptr_t key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSTaggedValue> obj(nativeObj);
    if (!(obj->IsECMAObject() || obj->IsCallable())) {
        // When input validation is failed, we return JSTaggedValue::Hole to napi native engine.
        // Using JSTaggedValue::Hole as the "hand-shaking-protocol" to tell native engine to change error state.
        JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(holeHandle));
    }
    LOG_IF_SPECIAL(obj, ERROR);
    JSMutableHandle<JSTaggedValue> keyValue(key);
    JSTaggedValue res = ObjectFastOperator::TryFastHasProperty(thread, obj.GetTaggedValue(),
                                                               keyValue);
    if (!res.IsHole()) {
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, res)));
    }
    auto ret = JSTaggedValue(JSTaggedValue::HasProperty(thread, obj, keyValue));
    return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, ret)));
}

Local<JSValueRef> JSNApi::NapiGetProperty(const EcmaVM *vm, uintptr_t nativeObj, uintptr_t key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSTaggedValue> obj(nativeObj);
    if (!(obj->IsECMAObject() || obj->IsCallable())) {
        // When input validation is failed, we return JSTaggedValue::Hole to napi native engine.
        // Using JSTaggedValue::Hole as the "hand-shaking-protocol" to tell native engine to change error state.
        JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(holeHandle));
    }
    LOG_IF_SPECIAL(obj, ERROR);
    JSMutableHandle<JSTaggedValue> keyValue(key);
    if (!obj->IsHeapObject()) {
        OperationResult ret = JSTaggedValue::GetProperty(thread, obj, keyValue);
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(ret.GetValue()));
    }

    JSTaggedValue res = ObjectFastOperator::TryFastGetPropertyByValue(thread, obj.GetTaggedValue(),
                                                                      keyValue);
    if (!res.IsHole()) {
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, res)));
    }

    JSTaggedValue ret = ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(),
                                                                   keyValue.GetTaggedValue());
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, ret)));
}

Local<JSValueRef> JSNApi::NapiDeleteProperty(const EcmaVM *vm, uintptr_t nativeObj, uintptr_t key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSTaggedValue> obj(nativeObj);
    if (!(obj->IsECMAObject() || obj->IsCallable())) {
        // When input validation is failed, we return JSTaggedValue::Hole to napi native engine.
        // Using JSTaggedValue::Hole as the "hand-shaking-protocol" to tell native engine to change error state.
        JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(holeHandle));
    }
    LOG_IF_SPECIAL(obj, ERROR);
    JSMutableHandle<JSTaggedValue> keyValue(key);
    if (keyValue->IsString() && !EcmaStringAccessor(keyValue.GetTaggedValue()).IsInternString()) {
        [[maybe_unused]] ecmascript::EcmaHandleScope handleScope(thread);
        auto string = thread->GetEcmaVM()->GetFactory()->InternString(keyValue);
        EcmaStringAccessor(string).SetInternString();
        keyValue.Update(JSTaggedValue(string));
    }
    auto ret = JSTaggedValue(JSTaggedValue::DeleteProperty(thread, obj, keyValue));
    return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, ret)));
}

Local<JSValueRef> JSNApi::NapiGetNamedProperty(const EcmaVM *vm, uintptr_t nativeObj, const char* utf8Key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    EscapeLocalScope scope(vm);
    JSHandle<JSTaggedValue> obj(nativeObj);
    if (UNLIKELY(!(obj->IsECMAObject() || obj->IsCallable()))) {
        // When input validation is failed, we return JSTaggedValue::Hole to napi native engine.
        // Using JSTaggedValue::Hole as the "hand-shaking-protocol" to tell native engine to change error state.
        JSHandle<JSTaggedValue> holeHandle(thread, JSTaggedValue::Hole());
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(holeHandle));
    }
    LOG_IF_SPECIAL(obj, ERROR);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<JSTaggedValue> keyValue(factory->NewFromUtf8(utf8Key));

    // FastPath - Try find key entry in cache directly.
    JSTaggedValue res = ObjectFastOperator::TryGetPropertyByNameThroughCacheAtLocal(thread, obj.GetTaggedValue(),
                                                                                    keyValue.GetTaggedValue());
    if (!res.IsHole()) {
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, res)));
    }
    if (!KeyIsNumber(utf8Key)) {
        res = ObjectFastOperator::GetPropertyByName(thread, obj.GetTaggedValue(), keyValue.GetTaggedValue());
        if (res.IsHole()) {
            res = JSTaggedValue::GetProperty(thread, obj, keyValue).GetValue().GetTaggedValue();
            RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
            return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, res)));
        }
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, res)));
    }
    res = ObjectFastOperator::FastGetPropertyByValue(thread, obj.GetTaggedValue(), keyValue.GetTaggedValue());
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, res)));
}

// Allocate a per-callsite IC cache (NapiICInfo: 3-slot TaggedArray stored as a global
// handle). Slots 0-1 hold the IC pair; slot 2 is a direction tag that records whether
// this info was populated by a load or store miss. The returned opaque handle is passed
// to NapiGetPropertyWithCallsiteInfo / NapiSetPropertyWithCallsiteInfo as the |info|
// parameter. One ICInfo should be created per unique get/set callsite in native code
// and reused across invocations at that callsite. Must be released with
// NapiDeleteCallsiteInfo when no longer needed.
uintptr_t JSNApi::NapiCreateCallsiteInfo(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, 0);
    ecmascript::ThreadManagedScope managedScope(thread);
    [[maybe_unused]] LocalScope scope(vm);
    JSHandle<ICInfo> array = vm->GetFactory()->NewICInfo(NapiICInfo::LENGTH);
    return thread->NewGlobalHandle(array.GetTaggedValue().GetRawData());
}

// Release a CallsiteInfo handle previously created by NapiCreateCallsiteInfo.
// Safe to call with info == 0 (no-op).
void JSNApi::NapiDeleteCallsiteInfo(const EcmaVM *vm, uintptr_t info)
{
    if (info == 0) {
        return;
    }
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    thread->DisposeGlobalHandle(info);
}

Local<JSValueRef> JSNApi::NapiGetPropertyWithCallsiteInfo(const EcmaVM *vm, uintptr_t nativeObj, uintptr_t key,
                                                          uintptr_t info, bool* hit)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    if (hit != nullptr) {
        *hit = false;
    }

    JSHandle<JSTaggedValue> obj(nativeObj);
    if (UNLIKELY(!(obj->IsECMAObject() || obj->IsCallable()))) {
        return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, JSTaggedValue::Hole()));
    }

    JSHandle<NapiICInfo> icInfo = NapiICInfo::SafeCast(JSHandle<JSTaggedValue>(info));
    if (UNLIKELY(icInfo.IsEmpty() || !icInfo->CanLoadIC(thread))) {
        return NapiGetProperty(vm, nativeObj, key);
    }

    // Scope-free fast path: key IC path is handle-free and allocation-free. Return directly via
    // NewHandle into caller's scope with no EscapeLocalScope overhead.
    JSMutableHandle<JSTaggedValue> keyHandle(key);
    JSTaggedValue fastRes = NapiICInfo::TryLoadKeyIC(thread, icInfo, obj.GetTaggedValue(), keyHandle.GetTaggedValue());
    if (LIKELY(!fastRes.IsHole())) {
        RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
        if (hit != nullptr) {
            *hit = true;
        }
        ASSERT(vm->GetHandleScopeStorageNext() != nullptr);
        return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, fastRes));
    }

    // Scoped path: key interning and IC miss handling may allocate
    {
        EscapeLocalScope scope(vm);
        JSTaggedValue res = NapiICInfo::TryLoadICOrMiss(thread, icInfo, obj, keyHandle, hit);
        if (LIKELY(!res.IsHole())) {
            RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
            return scope.Escape(JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, res)));
        }
    }

    LOG_ECMA(DEBUG) << "NapiGetPropertyWithCallsiteInfo: IC doesn't work, go slow path";
    return NapiGetProperty(vm, nativeObj, key);
}

bool JSNApi::NapiSetPropertyWithCallsiteInfo(const EcmaVM *vm, uintptr_t nativeObj, uintptr_t key, uintptr_t value,
                                             uintptr_t info, bool* hit)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    LocalScope scope(vm);
    if (hit != nullptr) {
        *hit = false;
    }

    JSHandle<JSTaggedValue> obj(nativeObj);
    JSMutableHandle<JSTaggedValue> keyHandle(key);
    JSHandle<JSTaggedValue> valueHandle(value);
    JSHandle<NapiICInfo> icInfo = NapiICInfo::SafeCast(JSHandle<JSTaggedValue>(info));

    if (UNLIKELY(icInfo.IsEmpty() || !icInfo->CanStoreIC(thread))) {
        bool setSucceeded = JSNApiHelper::ToLocal<ObjectRef>(obj)->Set(vm,
            JSNApiHelper::ToLocal<JSValueRef>(keyHandle),
            JSNApiHelper::ToLocal<JSValueRef>(valueHandle));
        RETURN_VALUE_IF_ABRUPT(thread, false);
        return setSucceeded;
    }

    JSTaggedValue res = NapiICInfo::TryStoreICOrMiss(thread, icInfo, obj, keyHandle, valueHandle, hit);
    if (LIKELY(!res.IsHole())) {
        RETURN_VALUE_IF_ABRUPT(thread, false);
        return !res.IsException();
    }

    // Slow path: MEGA, or IC returned Hole.
    LOG_ECMA(DEBUG) << "NapiSetPropertyWithCallsiteInfo: IC doesn't work, go slow path";
    bool setSucceeded = JSNApiHelper::ToLocal<ObjectRef>(obj)->Set(vm,
        JSNApiHelper::ToLocal<JSValueRef>(keyHandle),
        JSNApiHelper::ToLocal<JSValueRef>(valueHandle));
    RETURN_VALUE_IF_ABRUPT(thread, false);
    return setSucceeded;
}

Local<JSValueRef> JSNApi::CreateLocal(const EcmaVM *vm, panda::JSValueRef src)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    JSHandle<JSTaggedValue> handle(thread, JSNApiHelper::ToJSTaggedValue(&src));
    return JSNApiHelper::ToLocal<JSValueRef>(handle);
}

Local<ObjectRef> JSNApi::GetExportObject(EcmaVM *vm, const std::string &file, const std::string &key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    ecmascript::CString entry = file.c_str();
    ecmascript::CString name = vm->GetAssetPath();
    if (!vm->IsBundlePack()) {
        ModulePathHelper::ParseAbcPathAndOhmUrl(vm, entry, name, entry);
        std::shared_ptr<JSPandaFile> jsPandaFile =
            JSPandaFileManager::GetInstance()->LoadJSPandaFile(
                thread, name, entry.c_str(), false, ecmascript::ExecuteTypes::STATIC);
        if (jsPandaFile == nullptr) {
            JSHandle<JSTaggedValue> exportObj(thread, JSTaggedValue::Null());
            return JSNApiHelper::ToLocal<ObjectRef>(exportObj);
        }
        if (!jsPandaFile->IsRecordWithBundleName()) {
            PathHelper::AdaptOldIsaRecord(entry);
        }
    }
    ecmascript::ModuleManager *moduleManager = thread->GetModuleManager();
    JSHandle<ecmascript::SourceTextModule> ecmaModule = moduleManager->GetImportedModule(entry);
    if (ecmaModule->GetIsNewBcVersion()) {
        int index = ecmascript::ModuleManager::GetExportObjectIndex(vm, ecmaModule, key.c_str());
        JSTaggedValue result = ecmaModule->GetModuleValue(thread, index, false);
        JSHandle<JSTaggedValue> exportObj(thread, result);
        return JSNApiHelper::ToLocal<ObjectRef>(exportObj);
    }
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<EcmaString> keyHandle = factory->NewFromASCII(key.c_str());

    JSTaggedValue result = ecmaModule->GetModuleValue(thread, keyHandle.GetTaggedValue(), false);
    JSHandle<JSTaggedValue> exportObj(thread, result);
    return JSNApiHelper::ToLocal<ObjectRef>(exportObj);
}

Local<ObjectRef> JSNApi::GetExportObjectFromBuffer(EcmaVM *vm, const std::string &file,
                                                   const std::string &key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    ecmascript::ModuleManager *moduleManager = thread->GetModuleManager();
    JSHandle<ecmascript::SourceTextModule> ecmaModule = moduleManager->GetImportedModule(file.c_str());
    if (ecmaModule->GetIsNewBcVersion()) {
        int index = ecmascript::ModuleManager::GetExportObjectIndex(vm, ecmaModule, key.c_str());
        JSTaggedValue result = ecmaModule->GetModuleValue(thread, index, false);
        JSHandle<JSTaggedValue> exportObj(thread, result);
        return JSNApiHelper::ToLocal<ObjectRef>(exportObj);
    }

    ObjectFactory *factory = vm->GetFactory();
    JSHandle<EcmaString> keyHandle = factory->NewFromASCII(key.c_str());
    JSTaggedValue result = ecmaModule->GetModuleValue(thread, keyHandle.GetTaggedValue(), false);
    JSHandle<JSTaggedValue> exportObj(thread, result);
    return JSNApiHelper::ToLocal<ObjectRef>(exportObj);
}

Local<ObjectRef> JSNApi::GetExportObjectFromOhmUrl(EcmaVM *vm, const std::string &ohmUrl, const std::string &key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    ecmascript::CString recordName;
    // Check and translate OhmUrl to recordName
    if (!ModulePathHelper::CheckAndGetRecordName(thread, ohmUrl.c_str(), recordName)) {
        LOG_FULL(FATAL) << "ExecuteSecureWithOhmUrl: Invalid input OhmUrl: " << ohmUrl;
        return JSValueRef::Undefined(vm);
    }
    ecmascript::ModuleManager *moduleManager = thread->GetModuleManager();
    JSHandle<ecmascript::SourceTextModule> ecmaModule = moduleManager->GetImportedModule(recordName.c_str());
    int index = ecmascript::ModuleManager::GetExportObjectIndex(vm, ecmaModule, key.c_str());
    JSTaggedValue result = ecmaModule->GetModuleValue(thread, index, false);
    JSHandle<JSTaggedValue> exportObj(thread, result);
    return JSNApiHelper::ToLocal<ObjectRef>(exportObj);
}

Local<ObjectRef> JSNApi::ExecuteNativeModule(EcmaVM *vm, const std::string &key)
{
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ModuleManager *moduleManager = thread->GetModuleManager();
    JSHandle<JSTaggedValue> exportObj = moduleManager->LoadNativeModule(thread, key.c_str());
    return JSNApiHelper::ToLocal<ObjectRef>(exportObj);
}

Local<ObjectRef> JSNApi::GetModuleNameSpaceFromFile(EcmaVM *vm, const std::string &file)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    std::pair<std::string, std::string> moduleInfo = vm->GetCurrentModuleInfo(false);
    if (thread->HasPendingException()) {
        thread->HandleUncaughtException();
        return JSValueRef::Undefined(vm);
    }
    ecmascript::CString moduleName = moduleInfo.first.c_str();
    ecmascript::CString abcPath = moduleInfo.second.c_str();
    JSHandle<JSTaggedValue> moduleNamespace =
        ecmascript::NapiModuleLoader::LoadModuleNameSpace(vm, file.c_str(), moduleName, abcPath);
    return JSNApiHelper::ToLocal<ObjectRef>(moduleNamespace);
}


template<ForHybridApp isHybrid>
Local<ObjectRef> JSNApi::GetModuleNameSpaceWithModuleInfo(EcmaVM *vm, const std::string &file,
                                                          const std::string &module_path)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ecmascript::CString requestPath = file.c_str();
    ecmascript::CString modulePath = module_path.c_str();
    JSHandle<JSTaggedValue> nameSp =
        ecmascript::NapiModuleLoader::LoadModuleNameSpace<isHybrid>(vm, requestPath, modulePath);
    return JSNApiHelper::ToLocal<ObjectRef>(nameSp);
}
template Local<ObjectRef> JSNApi::GetModuleNameSpaceWithModuleInfo<ForHybridApp::Normal>(EcmaVM *vm,
    const std::string &file, const std::string &module_path);
template Local<ObjectRef> JSNApi::GetModuleNameSpaceWithModuleInfo<ForHybridApp::Hybrid>(EcmaVM *vm,
    const std::string &file, const std::string &module_path);

Local<ObjectRef> JSNApi::GetModuleNameSpaceWithModuleInfoForNormalApp(EcmaVM *vm, const std::string &file,
    const std::string &module_path)
{
    return JSNApi::GetModuleNameSpaceWithModuleInfo<ForHybridApp::Normal>(vm, file, module_path);
}
Local<ObjectRef> JSNApi::GetModuleNameSpaceWithModuleInfoForHybridApp(EcmaVM *vm, const std::string &file,
    const std::string &module_path)
{
    if (module_path.empty()) {
        return JSValueRef::Undefined(vm);
    }
    return JSNApi::GetModuleNameSpaceWithModuleInfo<ForHybridApp::Hybrid>(vm, file, module_path);
}

bool JSNApi::IsCrossBundleHsp(const EcmaVM *vm, const std::string &ohmurl)
{
    return ModulePathHelper::IsCrossBundleHsp(vm, ohmurl.c_str());
}

Local<ObjectRef> JSNApi::GetModuleNameSpaceWithOhmurlForHybridApp(EcmaVM *vm, const std::string &ohmurl,
                                                                  const std::string &fileName)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSTaggedValue> result =
        ecmascript::NapiModuleLoader::LoadModuleNameSpaceWithOhmurl<ForHybridApp::Hybrid>(thread, ohmurl.c_str(),
            fileName.c_str());
    return JSNApiHelper::ToLocal<ObjectRef>(result);
}

uintptr_t JSNApi::GetSendableGlobalHandleAddr(const EcmaVM *vm, uintptr_t localAddress)
{
    if (localAddress == 0) {
        return 0;
    }
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSTaggedValue> handle = JSHandle<JSTaggedValue>(localAddress);
    if (!handle->IsSendable()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot create a sendable reference with a non-sendable variable.", 0);
    }

    return ecmascript::Runtime::GetInstance()->NewSendableGlobalHandle(handle.GetTaggedType());
}

void JSNApi::DisposeSendableGlobalHandleAddr(const EcmaVM *vm, uintptr_t addr)
{
    if (addr == 0) {
        return;
    }
    CROSS_THREAD_CHECK(vm);
    ecmascript::ThreadManagedScope scope(thread);
    if (!reinterpret_cast<ecmascript::Node *>(addr)->IsUsing()) {
        return;
    }
    ecmascript::Runtime::GetInstance()->DisposeSendableGlobalHandle(addr);
}

// ---------------------------------- Promise -------------------------------------
Local<PromiseRef> PromiseRef::Catch(const EcmaVM *vm, Local<FunctionRef> handler)
{
    ecmascript::ThreadManagedScope scope(vm->GetJSThread());
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    const GlobalEnvConstants *constants = thread->GlobalConstants();

    JSHandle<JSTaggedValue> promise = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(promise, ERROR);
    JSHandle<JSTaggedValue> catchKey(thread, constants->GetPromiseCatchString());
    JSHandle<JSTaggedValue> reject = JSNApiHelper::ToJSHandle(handler);
    JSHandle<JSTaggedValue> undefined = constants->GetHandledUndefined();
    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promise, undefined, 1);
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
    thread->CheckJSTaggedType(reject.GetTaggedValue().GetRawData());
#endif
    info->SetCallArg(reject.GetTaggedValue());
    JSTaggedValue result = JSFunction::Invoke(info, catchKey);
#if ECMASCRIPT_ENABLE_STUB_RESULT_CHECK
    thread->CheckJSTaggedType(result.GetRawData());
#endif
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<PromiseRef>(JSHandle<JSTaggedValue>(thread, result));
}

Local<PromiseRef> PromiseRef::Finally(const EcmaVM *vm, Local<FunctionRef> handler)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    const GlobalEnvConstants *constants = thread->GlobalConstants();

    JSHandle<JSTaggedValue> promise = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(promise, ERROR);
    JSHandle<JSTaggedValue> finallyKey = constants->GetHandledPromiseFinallyString();
    JSHandle<JSTaggedValue> resolver = JSNApiHelper::ToJSHandle(handler);
    JSHandle<JSTaggedValue> undefined(constants->GetHandledUndefined());
    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promise, undefined, 2); // 2: two args
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
    thread->CheckJSTaggedType(resolver.GetTaggedValue().GetRawData());
    thread->CheckJSTaggedType(undefined.GetTaggedValue().GetRawData());
#endif
    info->SetCallArg(resolver.GetTaggedValue(), undefined.GetTaggedValue());
    JSTaggedValue result = JSFunction::Invoke(info, finallyKey);
#if ECMASCRIPT_ENABLE_STUB_RESULT_CHECK
    thread->CheckJSTaggedType(result.GetRawData());
#endif
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<PromiseRef>(JSHandle<JSTaggedValue>(thread, result));
}

Local<PromiseRef> PromiseRef::Then(const EcmaVM *vm, Local<FunctionRef> handler)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    const GlobalEnvConstants *constants = thread->GlobalConstants();

    JSHandle<JSTaggedValue> promise = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(promise, ERROR);
    JSHandle<JSTaggedValue> thenKey(thread, constants->GetPromiseThenString());
    JSHandle<JSTaggedValue> resolver = JSNApiHelper::ToJSHandle(handler);
    JSHandle<JSTaggedValue> undefined(constants->GetHandledUndefined());
    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promise, undefined, 2); // 2: two args
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
    thread->CheckJSTaggedType(resolver.GetTaggedValue().GetRawData());
    thread->CheckJSTaggedType(undefined.GetTaggedValue().GetRawData());
#endif
    info->SetCallArg(resolver.GetTaggedValue(), undefined.GetTaggedValue());
    JSTaggedValue result = JSFunction::Invoke(info, thenKey);
#if ECMASCRIPT_ENABLE_STUB_RESULT_CHECK
    thread->CheckJSTaggedType(result.GetRawData());
#endif
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<PromiseRef>(JSHandle<JSTaggedValue>(thread, result));
}

Local<PromiseRef> PromiseRef::Then(const EcmaVM *vm, Local<FunctionRef> onFulfilled, Local<FunctionRef> onRejected)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    const GlobalEnvConstants *constants = thread->GlobalConstants();

    JSHandle<JSTaggedValue> promise = JSNApiHelper::ToJSHandle(this);
    LOG_IF_SPECIAL(promise, ERROR);
    JSHandle<JSTaggedValue> thenKey(thread, constants->GetPromiseThenString());
    JSHandle<JSTaggedValue> resolver = JSNApiHelper::ToJSHandle(onFulfilled);
    JSHandle<JSTaggedValue> reject = JSNApiHelper::ToJSHandle(onRejected);
    JSHandle<JSTaggedValue> undefined(constants->GetHandledUndefined());
    EcmaRuntimeCallInfo *info =
        ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promise, undefined, 2); // 2: two args
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
#if ECMASCRIPT_ENABLE_STUB_ARGV_CHECK
    thread->CheckJSTaggedType(resolver.GetTaggedValue().GetRawData());
    thread->CheckJSTaggedType(reject.GetTaggedValue().GetRawData());
#endif
    info->SetCallArg(resolver.GetTaggedValue(), reject.GetTaggedValue());
    JSTaggedValue result = JSFunction::Invoke(info, thenKey);
#if ECMASCRIPT_ENABLE_STUB_RESULT_CHECK
    thread->CheckJSTaggedType(result.GetRawData());
#endif
    RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm));
    return JSNApiHelper::ToLocal<PromiseRef>(JSHandle<JSTaggedValue>(thread, result));
}

Local<JSValueRef> PromiseRef::GetPromiseState(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSPromise> promise(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(promise, ERROR);

    ecmascript::PromiseState state = promise->GetPromiseState();
    std::string promiseStateStr;
    switch (state) {
        case ecmascript::PromiseState::PENDING:
            promiseStateStr = "Pending";
            break;
        case ecmascript::PromiseState::FULFILLED:
            promiseStateStr = "Fulfilled";
            break;
        case ecmascript::PromiseState::REJECTED:
            promiseStateStr = "Rejected";
            break;
    }

    ObjectFactory *factory = vm->GetFactory();
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(factory->NewFromStdString(promiseStateStr)));
}

Local<JSValueRef> PromiseRef::GetPromiseResult(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSPromise> promise(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(promise, ERROR);

    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, promise->GetPromiseResult(thread)));
}
// ---------------------------------- ProxyRef -----------------------------------------
Local<JSValueRef> ProxyRef::GetHandler(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSProxy> jsProxy(JSNApiHelper::ToJSHandle(this));
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, jsProxy->GetHandler(thread)));
}

Local<JSValueRef> ProxyRef::GetTarget(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope scope(thread);
    JSHandle<JSProxy> jsProxy(JSNApiHelper::ToJSHandle(this));
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, jsProxy->GetTarget(thread)));
}

bool ProxyRef::IsRevoked()
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, false);
    JSHandle<JSProxy> jsProxy(JSNApiHelper::ToJSHandle(this));
    return jsProxy->GetIsRevoked();
}

// ---------------------------------- SetRef --------------------------------------
int32_t SetRef::GetSize(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSSet> set(JSNApiHelper::ToJSHandle(this));
    return set->GetSize(thread);
}

int32_t SetRef::GetTotalElements(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSSet> set(JSNApiHelper::ToJSHandle(this));
    return static_cast<int>(set->GetSize(thread)) +
           LinkedHashSet::Cast(set->GetLinkedSet(thread).GetTaggedObject())->NumberOfDeletedElements();
}

Local<JSValueRef> SetRef::GetValue(const EcmaVM *vm, int entry)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSSet> set(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(set, FATAL);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, set->GetValue(thread, entry)));
}

Local<SetRef> SetRef::New(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> constructor = env->GetBuiltinsSetFunction();
    JSHandle<JSSet> set =
        JSHandle<JSSet>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
    JSHandle<LinkedHashSet> hashSet = LinkedHashSet::Create(thread);
    set->SetLinkedSet(thread, hashSet);
    JSHandle<JSTaggedValue> setTag = JSHandle<JSTaggedValue>::Cast(set);
    return JSNApiHelper::ToLocal<SetRef>(setTag);
}

void SetRef::Add(const EcmaVM *vm, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSSet> set(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(set, ERROR);
    JSSet::Add(thread, set, JSNApiHelper::ToJSHandle(value));
}

// ---------------------------------- WeakMapRef --------------------------------------
int32_t WeakMapRef::GetSize(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSWeakMap> weakMap(JSNApiHelper::ToJSHandle(this));
    return weakMap->GetSize(thread);
}

int32_t WeakMapRef::GetTotalElements(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSWeakMap> weakMap(JSNApiHelper::ToJSHandle(this));
    return weakMap->GetSize(thread) +
           WeakLinkedHashMap::Cast(weakMap->GetWeakLinkedMap(thread).GetTaggedObject())->NumberOfDeletedElements();
}

Local<JSValueRef> WeakMapRef::GetKey(const EcmaVM *vm, int entry)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSWeakMap> weakMap(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(weakMap, FATAL);
    JSTaggedValue key = weakMap->GetKey(thread, entry);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, key.GetWeakRawValue()));
}

Local<JSValueRef> WeakMapRef::GetValue(const EcmaVM *vm, int entry)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    JSHandle<JSWeakMap> weakMap(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(weakMap, FATAL);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, weakMap->GetValue(thread, entry)));
}

Local<WeakMapRef> WeakMapRef::New(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> constructor = env->GetBuiltinsWeakMapFunction();
    JSHandle<JSWeakMap> weakMap =
        JSHandle<JSWeakMap>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
    JSHandle<WeakLinkedHashMap> hashMap = WeakLinkedHashMap::Create(thread);
    weakMap->SetWeakLinkedMap(thread, hashMap);
    JSHandle<JSTaggedValue> weakMapTag = JSHandle<JSTaggedValue>::Cast(weakMap);
    return JSNApiHelper::ToLocal<WeakMapRef>(weakMapTag);
}

void WeakMapRef::Set(const EcmaVM *vm, const Local<JSValueRef> &key, const Local<JSValueRef> &value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSWeakMap> weakMap(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(weakMap, FATAL);
    JSWeakMap::Set(thread, weakMap, JSNApiHelper::ToJSHandle(key), JSNApiHelper::ToJSHandle(value));
}

bool WeakMapRef::Has(const EcmaVM *vm, Local<JSValueRef> key)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, false);
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, false);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSWeakMap> weakMap(JSNApiHelper::ToJSHandle(this));
    return weakMap->Has(thread, JSNApiHelper::ToJSTaggedValue(*key));
}

// ---------------------------------- WeakSetRef --------------------------------------
int32_t WeakSetRef::GetSize(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSWeakSet> weakSet(JSNApiHelper::ToJSHandle(this));
    return weakSet->GetSize(thread);
}

int32_t WeakSetRef::GetTotalElements(const EcmaVM *vm)
{
    DCHECK_SPECIAL_VALUE_WITH_RETURN(this, 0);
    JSThread *thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSWeakSet> weakSet(JSNApiHelper::ToJSHandle(this));
    return weakSet->GetSize(thread) +
           LinkedHashSet::Cast(weakSet->GetLinkedSet(thread).GetTaggedObject())->NumberOfDeletedElements();
}

Local<JSValueRef> WeakSetRef::GetValue(const EcmaVM *vm, int entry)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSWeakSet> weakSet(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(weakSet, FATAL);
    JSTaggedValue value = weakSet->GetValue(thread, entry);
    return JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread, value.GetWeakRawValue()));
}

Local<WeakSetRef> WeakSetRef::New(const EcmaVM *vm)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK_WITH_RETURN(vm, JSValueRef::Undefined(vm));
    ecmascript::ThreadManagedScope managedScope(thread);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> constructor = env->GetBuiltinsWeakSetFunction();
    JSHandle<JSWeakSet> weakSet =
        JSHandle<JSWeakSet>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
    JSHandle<LinkedHashSet> hashWeakSet = LinkedHashSet::Create(thread);
    weakSet->SetLinkedSet(thread, hashWeakSet);
    JSHandle<JSTaggedValue> setTag = JSHandle<JSTaggedValue>::Cast(weakSet);
    return JSNApiHelper::ToLocal<WeakSetRef>(setTag);
}

void WeakSetRef::Add(const EcmaVM *vm, Local<JSValueRef> value)
{
    CROSS_THREAD_AND_EXCEPTION_CHECK(vm);
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<JSWeakSet> weakSet(JSNApiHelper::ToJSHandle(this));
    LOG_IF_SPECIAL(weakSet, ERROR);
    JSWeakSet::Add(thread, weakSet, JSNApiHelper::ToJSHandle(value));
}

TryCatch::~TryCatch() {}

bool TryCatch::HasCaught() const
{
    return ecmaVm_->GetJSThread()->HasPendingException();
}

void TryCatch::Rethrow()
{
    rethrow_ = true;
}

Local<ObjectRef> TryCatch::GetAndClearException()
{
    ecmascript::ThreadManagedScope managedScope(ecmaVm_->GetJSThread());
    ecmaVm_->GetJSThread()->SetExtraErrorMessage(JSTaggedValue::Hole());
    return JSNApiHelper::ToLocal<ObjectRef>(ecmaVm_->GetAndClearEcmaUncaughtException());
}

Local<ObjectRef> TryCatch::GetException()
{
    ecmascript::ThreadManagedScope managedScope(ecmaVm_->GetJSThread());
    return JSNApiHelper::ToLocal<ObjectRef>(ecmaVm_->GetEcmaUncaughtException());
}

void TryCatch::ClearException()
{
    ecmaVm_->GetJSThread()->ClearExceptionAndExtraErrorMessage();
}

void* TryCatch::GetEnv()
{
    return ecmaVm_->GetJSThread()->GetEnv();
}

bool ExternalStringCache::RegisterStringCacheTable(const EcmaVM *vm, uint32_t size)
{
    auto instance = ecmascript::Runtime::GetInstance();
    ASSERT(instance != nullptr);

    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return instance->CreateStringCacheTable(size);
}

bool ExternalStringCache::SetCachedString(const EcmaVM *vm, const char *name, uint32_t propertyIndex)
{
    auto instance = ecmascript::Runtime::GetInstance();
    ASSERT(instance != nullptr);

    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    [[maybe_unused]] LocalScope scope(vm);
    ObjectFactory *factory = vm->GetFactory();
    JSHandle<EcmaString> str = factory->NewFromUtf8(name);
    return instance->SetCachedString(str, propertyIndex);
}

Local<StringRef> ExternalStringCache::GetCachedString(const EcmaVM *vm, uint32_t propertyIndex)
{
    auto instance = ecmascript::Runtime::GetInstance();
    ASSERT(instance != nullptr);
    auto thread = vm->GetJSThread();
    ecmascript::ThreadManagedScope managedScope(thread);
    JSHandle<EcmaString> str = instance->GetCachedString(thread, propertyIndex);
    return JSNApiHelper::ToLocal<StringRef>(JSHandle<JSTaggedValue>(str));
}

bool ExternalStringCache::HasCachedString([[maybe_unused]] const EcmaVM *vm, uint32_t propertyIndex)
{
    auto instance = ecmascript::Runtime::GetInstance();
    ASSERT(instance != nullptr);
    ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
    return instance->HasCachedString(propertyIndex);
}

} // namespace panda