/*
 * Copyright (c) 2021 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 "ecmascript/builtins/builtins_ark_tools.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "ecmascript/element_accessor-inl.h"
#include "ecmascript/js_function.h"
#include "ecmascript/base/string_helper.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/mem/tagged_object-inl.h"
#include "ecmascript/napi/include/dfx_jsnapi.h"
#include "ecmascript/mem/clock_scope.h"
#include "ecmascript/property_detector-inl.h"
#include "ecmascript/js_arraybuffer.h"
#include "ecmascript/interpreter/fast_runtime_stub-inl.h"
#include "builtins_typedarray.h"
#include "ecmascript/jit/jit.h"

namespace panda::ecmascript::builtins {
using StringHelper = base::StringHelper;

#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
constexpr char FILEDIR[] = "/data/storage/el2/base/files/";
#endif
JSTaggedValue BuiltinsArkTools::ObjectDump(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<EcmaString> str = JSTaggedValue::ToString(thread, GetCallArg(info, 0));
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    // The default log level of ace_engine and js_runtime is error
    LOG_ECMA(ERROR) << ": " << EcmaStringAccessor(str).ToStdString();

    uint32_t numArgs = info->GetArgsNumber();
    for (uint32_t i = 1; i < numArgs; i++) {
        JSHandle<JSTaggedValue> obj = GetCallArg(info, i);
        std::ostringstream oss;
        obj->Dump(oss);

        // The default log level of ace_engine and js_runtime is error
        LOG_ECMA(ERROR) << ": " << oss.str();
    }

    return JSTaggedValue::Undefined();
}

JSTaggedValue BuiltinsArkTools::CompareHClass(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> obj1 = GetCallArg(info, 0);
    JSHandle<JSTaggedValue> obj2 = GetCallArg(info, 1);
    JSHClass *obj1Hclass = obj1->GetTaggedObject()->GetClass();
    JSHClass *obj2Hclass = obj2->GetTaggedObject()->GetClass();
    std::ostringstream oss;
    obj1Hclass->Dump(oss);
    obj2Hclass->Dump(oss);
    bool res = (obj1Hclass == obj2Hclass);
    if (!res) {
        LOG_ECMA(ERROR) << "These two object don't share the same hclass:" << oss.str();
    }
    return JSTaggedValue(res);
}

JSTaggedValue BuiltinsArkTools::DumpHClass(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> obj = GetCallArg(info, 0);
    JSHClass *objHclass = obj->GetTaggedObject()->GetClass();
    std::ostringstream oss;
    objHclass->Dump(oss);

    LOG_ECMA(ERROR) << "hclass:" << oss.str();
    return JSTaggedValue::Undefined();
}

JSTaggedValue BuiltinsArkTools::IsTSHClass(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ASSERT(info->GetArgsNumber() == 1);
    JSHandle<JSTaggedValue> object = GetCallArg(info, 0);
    JSHClass *hclass = object->GetTaggedObject()->GetClass();
    bool isTSHClass = hclass->IsTS();
    return GetTaggedBoolean(isTSHClass);
}

JSTaggedValue BuiltinsArkTools::GetHClass(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ASSERT(info->GetArgsNumber() == 1);
    JSHandle<JSTaggedValue> object = GetCallArg(info, 0);
    JSHClass *hclass = object->GetTaggedObject()->GetClass();
    return JSTaggedValue(hclass);
}

JSTaggedValue BuiltinsArkTools::HasTSSubtyping(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ASSERT(info->GetArgsNumber() == 1);
    JSHandle<JSTaggedValue> object = GetCallArg(info, 0);
    JSHClass *hclass = object->GetTaggedObject()->GetClass();
    return GetTaggedBoolean(hclass->HasTSSubtyping());
}

JSTaggedValue BuiltinsArkTools::IsSlicedString(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ASSERT(info->GetArgsNumber() == 1);
    JSHandle<JSTaggedValue> str = GetCallArg(info, 0);
    return GetTaggedBoolean(str->IsSlicedString());
}

JSTaggedValue BuiltinsArkTools::IsNotHoleProperty(EcmaRuntimeCallInfo *info)
{
    [[maybe_unused]] DisallowGarbageCollection noGc;
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ASSERT(info->GetArgsNumber() == 2);  // 2 : object and key
    JSHandle<JSTaggedValue> object = GetCallArg(info, 0);
    JSTaggedValue key = GetCallArg(info, 1).GetTaggedValue();
    JSHClass *hclass = object->GetTaggedObject()->GetClass();
    int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
    if (entry == -1) {
        return GetTaggedBoolean(false);
    }
    PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry);
    return GetTaggedBoolean(attr.IsNotHole());
}

JSTaggedValue BuiltinsArkTools::HiddenStackSourceFile(EcmaRuntimeCallInfo *info)
{
    [[maybe_unused]] DisallowGarbageCollection noGc;
    ASSERT(info);
    JSThread *thread = info->GetThread();
    thread->SetEnableStackSourceFile(false);
    return JSTaggedValue::True();
}

JSTaggedValue BuiltinsArkTools::ExcutePendingJob(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    thread->GetCurrentEcmaContext()->ExecutePromisePendingJob();
    return JSTaggedValue::True();
}

JSTaggedValue BuiltinsArkTools::GetLexicalEnv(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ASSERT(info->GetArgsNumber() == 1);
    JSHandle<JSTaggedValue> object = GetCallArg(info, 0);
    if (object->IsHeapObject() && object->IsJSFunction()) {
        JSHandle<JSFunction> function = JSHandle<JSFunction>::Cast(object);
        return function->GetLexicalEnv();
    }
    return JSTaggedValue::Null();
}

JSTaggedValue BuiltinsArkTools::ForceFullGC(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap())->CollectGarbage(
        TriggerGCType::FULL_GC, GCReason::EXTERNAL_TRIGGER);
    return JSTaggedValue::True();
}

JSTaggedValue BuiltinsArkTools::HintGC(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    return JSTaggedValue(const_cast<Heap *>(info->GetThread()->GetEcmaVM()->GetHeap())->
        CheckAndTriggerHintGC());
}

JSTaggedValue BuiltinsArkTools::RemoveAOTFlag(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ASSERT(info->GetArgsNumber() == 1);
    JSHandle<JSTaggedValue> object = GetCallArg(info, 0);
    if (object->IsHeapObject() && object->IsJSFunction()) {
        JSHandle<JSFunction> func = JSHandle<JSFunction>::Cast(object);
        JSHandle<Method> method = JSHandle<Method>(thread, func->GetMethod());
        method->SetAotCodeBit(false);
    }

    return JSTaggedValue::Undefined();
}

#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
JSTaggedValue BuiltinsArkTools::StartCpuProfiler(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    auto vm = thread->GetEcmaVM();

    // get file name
    JSHandle<JSTaggedValue> fileNameValue = GetCallArg(info, 0);
    std::string fileName = "";
    if (fileNameValue->IsString()) {
        JSHandle<EcmaString> str = JSTaggedValue::ToString(thread, fileNameValue);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        fileName = EcmaStringAccessor(str).ToStdString() + ".cpuprofile";
    } else {
        fileName = GetProfileName();
    }

    if (!CreateFile(fileName)) {
        LOG_ECMA(ERROR) << "CreateFile failed " << fileName;
    }

    // get sampling interval
    JSHandle<JSTaggedValue> samplingIntervalValue = GetCallArg(info, 1);
    uint32_t interval = 500; // 500:Default Sampling interval 500 microseconds
    if (samplingIntervalValue->IsNumber()) {
        interval = JSTaggedValue::ToUint32(thread, samplingIntervalValue);
    }

    DFXJSNApi::StartCpuProfilerForFile(vm, fileName, interval);
    return JSTaggedValue::Undefined();
}

JSTaggedValue BuiltinsArkTools::StopCpuProfiler(EcmaRuntimeCallInfo *info)
{
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    auto vm = thread->GetEcmaVM();
    DFXJSNApi::StopCpuProfilerForFile(vm);

    return JSTaggedValue::Undefined();
}

std::string BuiltinsArkTools::GetProfileName()
{
    char time1[16] = {0}; // 16:Time format length
    char time2[16] = {0}; // 16:Time format length
    time_t timep = std::time(nullptr);
    struct tm nowTime1;
    localtime_r(&timep, &nowTime1);
    size_t result = 0;
    result = strftime(time1, sizeof(time1), "%Y%m%d", &nowTime1);
    if (result == 0) {
        LOG_ECMA(ERROR) << "get time failed";
        return "";
    }
    result = strftime(time2, sizeof(time2), "%H%M%S", &nowTime1);
    if (result == 0) {
        LOG_ECMA(ERROR) << "get time failed";
        return "";
    }
    std::string profileName = "cpuprofile-";
    profileName += time1;
    profileName += "TO";
    profileName += time2;
    profileName += ".cpuprofile";
    return profileName;
}

bool BuiltinsArkTools::CreateFile(std::string &fileName)
{
    std::string path = FILEDIR + fileName;
    if (access(path.c_str(), F_OK) == 0) {
        if (access(path.c_str(), W_OK) == 0) {
            fileName = path;
            return true;
        }
        LOG_ECMA(ERROR) << "file create failed, W_OK false";
        return false;
    }
    const mode_t defaultMode = S_IRUSR | S_IWUSR | S_IRGRP; // -rw-r--
    int fd = creat(path.c_str(), defaultMode);
    if (fd == -1) {
        fd = creat(fileName.c_str(), defaultMode);
        if (fd == -1) {
            LOG_ECMA(ERROR) << "file create failed, errno = "<< errno;
            return false;
        }
        close(fd);
        return true;
    } else {
        fileName = path;
        close(fd);
        return true;
    }
}
#endif

// It is used to check whether an object is a proto, and this function can be
// used to check whether the state machine of IC is faulty.
JSTaggedValue BuiltinsArkTools::IsPrototype(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> obj = GetCallArg(info, 0);
    JSHClass *objHclass = obj->GetTaggedObject()->GetClass();
    return JSTaggedValue(objHclass->IsPrototype());
}

// It is used to check whether a function is aot compiled.
JSTaggedValue BuiltinsArkTools::IsAOTCompiled(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> obj = GetCallArg(info, 0);
    JSHandle<JSFunction> func(thread, obj.GetTaggedValue());
    Method *method = func->GetCallTarget();
    return JSTaggedValue(method->IsAotWithCallField());
}

JSTaggedValue BuiltinsArkTools::IsOnHeap(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> obj = GetCallArg(info, 0);
    return JSTaggedValue(obj.GetTaggedValue().GetTaggedObject()->GetClass()->IsOnHeapFromBitField());
}

// It is used to check whether a function is aot compiled and deopted at runtime.
JSTaggedValue BuiltinsArkTools::IsAOTDeoptimized(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> obj = GetCallArg(info, 0);
    JSHandle<JSFunction> func(thread, obj.GetTaggedValue());
    Method *method = func->GetCallTarget();
    bool isAotCompiled = method->IsAotWithCallField();
    if (isAotCompiled) {
        uint32_t deoptedCount = method->GetDeoptThreshold();
        uint32_t deoptThreshold = thread->GetEcmaVM()->GetJSOptions().GetDeoptThreshold();
        return JSTaggedValue(deoptedCount != deoptThreshold);
    }

    return JSTaggedValue(false);
}

JSTaggedValue BuiltinsArkTools::PrintTypedOpProfilerAndReset(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> opStrVal = GetCallArg(info, 0);
    std::string opStr = EcmaStringAccessor(opStrVal.GetTaggedValue()).ToStdString();
    TypedOpProfiler *profiler = thread->GetCurrentEcmaContext()->GetTypdOpProfiler();
    if (profiler != nullptr) {
        profiler->PrintAndReset(opStr);
    }
    return JSTaggedValue::Undefined();
}

JSTaggedValue BuiltinsArkTools::GetElementsKind(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> obj = GetCallArg(info, 0);
    JSHandle<JSObject> receiver(thread, obj.GetTaggedValue());
    ElementsKind kind = receiver->GetClass()->GetElementsKind();
    return JSTaggedValue(static_cast<uint32_t>(kind));
}

JSTaggedValue BuiltinsArkTools::IsRegExpReplaceDetectorValid(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    return JSTaggedValue(PropertyDetector::IsRegExpReplaceDetectorValid(env));
}

JSTaggedValue BuiltinsArkTools::IsSymbolIteratorDetectorValid(EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> kind = GetCallArg(info, 0);
    if (!kind->IsString()) {
        return JSTaggedValue::Undefined();
    }
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> mapString = factory->NewFromUtf8("Map");
    if (JSTaggedValue::Equal(thread, kind, JSHandle<JSTaggedValue>(mapString))) {
        return JSTaggedValue(PropertyDetector::IsMapIteratorDetectorValid(env));
    }
    JSHandle<EcmaString> setString = factory->NewFromUtf8("Set");
    if (JSTaggedValue::Equal(thread, kind, JSHandle<JSTaggedValue>(setString))) {
        return JSTaggedValue(PropertyDetector::IsSetIteratorDetectorValid(env));
    }
    JSHandle<EcmaString> stringString = factory->NewFromUtf8("String");
    if (JSTaggedValue::Equal(thread, kind, JSHandle<JSTaggedValue>(stringString))) {
        return JSTaggedValue(PropertyDetector::IsStringIteratorDetectorValid(env));
    }
    JSHandle<EcmaString> arrayString = factory->NewFromUtf8("Array");
    if (JSTaggedValue::Equal(thread, kind, JSHandle<JSTaggedValue>(arrayString))) {
        return JSTaggedValue(PropertyDetector::IsArrayIteratorDetectorValid(env));
    }
    JSHandle<EcmaString> typedarrayString = factory->NewFromUtf8("TypedArray");
    if (JSTaggedValue::Equal(thread, kind, JSHandle<JSTaggedValue>(typedarrayString))) {
        return JSTaggedValue(PropertyDetector::IsTypedArrayIteratorDetectorValid(env));
    }
    return JSTaggedValue::Undefined();
}

JSTaggedValue BuiltinsArkTools::TimeInUs([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    ClockScope scope;
    return JSTaggedValue(scope.GetCurTime());
}
// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::PrepareFunctionForOptimization([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter PrepareFunctionForOptimization()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::OptimizeFunctionOnNextCall([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> thisValue = GetCallArg(info, 0);
    if (!thisValue->IsJSFunction()) {
        return JSTaggedValue::Undefined();
    }
    JSHandle<JSFunction> jsFunction(thisValue);
    Jit::Compile(thread->GetEcmaVM(), jsFunction);

    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::OptimizeMaglevOnNextCall([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter OptimizeMaglevOnNextCall()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::DeoptimizeFunction([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter DeoptimizeFunction()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::OptimizeOsr([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter OptimizeOsr()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::NeverOptimizeFunction([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter NeverOptimizeFunction()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::HeapObjectVerify([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter HeapObjectVerify()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::DisableOptimizationFinalization([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter DisableOptimizationFinalization()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::DeoptimizeNow([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter DeoptimizeNow()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::WaitForBackgroundOptimization([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter WaitForBackgroundOptimization()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::Gc([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter Gc()";
    return JSTaggedValue::Undefined();
}

// empty function for pgoAssertType
JSTaggedValue BuiltinsArkTools::PGOAssertType([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter PGOAssertType";
    return JSTaggedValue::Undefined();
}

JSTaggedValue BuiltinsArkTools::ToLength([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> key = GetCallArg(info, 0);
    return JSTaggedValue::ToLength(thread, key);
}

JSTaggedValue BuiltinsArkTools::HasHoleyElements([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter HasHoleyElements()";
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> array = GetCallArg(info, 0);
    if (!array->IsJSArray()) {
        return JSTaggedValue::False();
    }
    JSHandle<JSObject> obj(array);
    uint32_t len = JSHandle<JSArray>::Cast(array)->GetArrayLength();
    for (uint32_t i = 0; i < len; i++) {
        if (ElementAccessor::Get(obj, i).IsHole()) {
            return JSTaggedValue::True();
        }
    }
    return JSTaggedValue::False();
}

JSTaggedValue BuiltinsArkTools::HasDictionaryElements([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter HasDictionaryElements()";
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> objValue = GetCallArg(info, 0);
    JSHandle<JSObject> obj = JSHandle<JSObject>::Cast(objValue);
    return JSTaggedValue(obj->GetJSHClass()->IsDictionaryMode());
}

JSTaggedValue BuiltinsArkTools::HasSmiElements([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter HasSmiElements()";
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> array = GetCallArg(info, 0);
    if (!array->IsJSArray()) {
        return JSTaggedValue::False();
    }
    JSHandle<JSObject> obj(array);
    uint32_t len = JSHandle<JSArray>::Cast(array)->GetArrayLength();
    for (uint32_t i = 0; i < len; i++) {
        if (ElementAccessor::Get(obj, i).IsInt()) {
            return JSTaggedValue::True();
        }
    }
    return JSTaggedValue::False();
}

JSTaggedValue BuiltinsArkTools::HasDoubleElements([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter HasDoubleElements()";
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> array = GetCallArg(info, 0);
    if (!array->IsJSArray()) {
        return JSTaggedValue::False();
    }
    JSHandle<JSObject> obj(array);
    uint32_t len = JSHandle<JSArray>::Cast(array)->GetArrayLength();
    for (uint32_t i = 0; i < len; i++) {
        if (ElementAccessor::Get(obj, i).IsDouble() && !ElementAccessor::Get(obj, i).IsZero()) {
            return JSTaggedValue::True();
        }
    }
    return JSTaggedValue::False();
}

JSTaggedValue BuiltinsArkTools::HasObjectElements([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter HasObjectElements()";
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> array = GetCallArg(info, 0);
    if (!array->IsJSArray()) {
        return JSTaggedValue::False();
    }
    JSHandle<JSObject> obj(array);
    uint32_t len = JSHandle<JSArray>::Cast(array)->GetArrayLength();
    for (uint32_t i = 0; i < len; i++) {
        if (ElementAccessor::Get(obj, i).IsObject()) {
            return JSTaggedValue::True();
        }
    }
    return JSTaggedValue::False();
}

JSTaggedValue BuiltinsArkTools::ArrayBufferDetach([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> obj1 = GetCallArg(info, 0);
    JSHandle<JSArrayBuffer> arrBuf = JSHandle<JSArrayBuffer>::Cast(obj1);
    arrBuf->Detach(thread);
    return JSTaggedValue::Undefined();
}

JSTaggedValue BuiltinsArkTools::HaveSameMap([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> obj1 = GetCallArg(info, 0);
    JSHandle<JSTaggedValue> obj2 = GetCallArg(info, 1);
    JSHandle<TaggedArray> keys1 = JSTaggedValue::GetOwnPropertyKeys(thread, obj1);
    JSHandle<TaggedArray> keys2 = JSTaggedValue::GetOwnPropertyKeys(thread, obj2);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    uint32_t len = keys1->GetLength();
    if (len != keys2->GetLength()) {
        return JSTaggedValue::False();
    }
    JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
    for (uint32_t i = 0; i < len; i++) {
        if (keys1->Get(i) != keys2->Get(i)) {
            return JSTaggedValue::False();
        }
        keyHandle.Update(keys1->Get(i));
        OperationResult result = JSObject::GetProperty(thread, obj1, keyHandle);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        JSTaggedValue value1 = result.GetValue().GetTaggedValue();
        result = JSObject::GetProperty(thread, obj2, keyHandle);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        JSTaggedValue value2 = result.GetValue().GetTaggedValue();
        if (FastRuntimeStub::FastTypeOf(thread, value1) !=
            FastRuntimeStub::FastTypeOf(thread, value2)) {
            return JSTaggedValue::False();
        }
    }
    return JSTaggedValue::True();
}

JSTaggedValue BuiltinsArkTools::CreatePrivateSymbol([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> symbolName = GetCallArg(info, 0);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSSymbol> privateNameSymbol = factory->NewPrivateNameSymbol(symbolName);
    JSHandle<JSTaggedValue> symbolValue = JSHandle<JSTaggedValue>::Cast(privateNameSymbol);
    return symbolValue.GetTaggedValue();
}

JSTaggedValue BuiltinsArkTools::IsArray([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> array = GetCallArg(info, 0);
    return JSTaggedValue(array->IsJSArray());
}

JSTaggedValue BuiltinsArkTools::CreateDataProperty([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    uint32_t secondArg = 2;
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> key = GetCallArg(info, 1);
    JSHandle<JSTaggedValue> value = GetCallArg(info, secondArg);
    JSHandle<JSObject> obj = JSHandle<JSObject>::Cast(GetCallArg(info, 0));
    JSObject::CreateDataPropertyOrThrow(thread, obj, key, value);
    return value.GetTaggedValue();
}

JSTaggedValue BuiltinsArkTools::FunctionGetInferredName([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> obj = GetCallArg(info, 0);
    if (obj->IsJSFunction()) {
        JSHandle<JSFunction> funcObj = JSHandle<JSFunction>::Cast(obj);
        JSHandle<JSTaggedValue> funcName = JSFunction::GetFunctionName(thread, JSHandle<JSFunctionBase>(funcObj));
        return funcName.GetTaggedValue();
    }
    return thread->GlobalConstants()->GetHandledEmptyString().GetTaggedValue();
}

JSTaggedValue BuiltinsArkTools::StringLessThan([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    ASSERT(info);
    JSThread *thread = info->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> x = GetCallArg(info, 0);
    JSHandle<JSTaggedValue> y = GetCallArg(info, 1);
    ComparisonResult result = JSTaggedValue::Compare(thread, x, y);
    return JSTaggedValue(ComparisonResult::LESS == result);
}

JSTaggedValue BuiltinsArkTools::StringMaxLength([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter StringMaxLength()";
    ASSERT(info);
    return JSTaggedValue(static_cast<uint32_t>(EcmaString::MAX_STRING_LENGTH) - 1);
}

JSTaggedValue BuiltinsArkTools::ArrayBufferMaxByteLength([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter ArrayBufferMaxByteLength()";
    ASSERT(info);
    return JSTaggedValue(INT_MAX);
}

JSTaggedValue BuiltinsArkTools::TypedArrayMaxLength([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter TypedArrayMaxLength()";
    ASSERT(info);
    return JSTaggedValue(BuiltinsTypedArray::MAX_ARRAY_INDEX);
}

JSTaggedValue BuiltinsArkTools::MaxSmi([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter MaxSmi()";
    return JSTaggedValue(INT_MAX);
}

JSTaggedValue BuiltinsArkTools::Is64Bit([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter Is64Bit()";
    bool is64Bit = sizeof(void*) == 8;
    return JSTaggedValue(is64Bit);
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::FinalizeOptimization([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter FinalizeOptimization()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::EnsureFeedbackVectorForFunction([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter EnsureFeedbackVectorForFunction()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::CompileBaseline([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter CompileBaseline()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::DebugGetLoadedScriptIds([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter DebugGetLoadedScriptIds()";
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::ToFastProperties([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter ToFastProperties()";
    return JSTaggedValue::Undefined();
}

JSTaggedValue BuiltinsArkTools::AbortJS([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(FATAL) << "AbortJS()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::InternalizeString([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter InternalizeString()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::HandleDebuggerStatement([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter HandleDebuggerStatement()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::SetAllocationTimeout([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter SetAllocationTimeout()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::HasFastProperties([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter HasFastProperties()";
    ASSERT(info);
    return JSTaggedValue::True();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::HasOwnConstDataProperty([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter HasOwnConstDataProperty()";
    ASSERT(info);
    return JSTaggedValue::True();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::GetHoleNaNUpper([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter GetHoleNaNUpper()";
    ASSERT(info);
    return JSTaggedValue::Null();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::GetHoleNaNLower([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter GetHoleNaNLower()";
    ASSERT(info);
    return JSTaggedValue::Null();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::SystemBreak([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter SystemBreak()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::ScheduleBreak([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter ScheduleBreak()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::EnqueueMicrotask([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter EnqueueMicrotask()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::DebugPrint([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter DebugPrint()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::GetOptimizationStatus([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter GetOptimizationStatus()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::GetUndetectable([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter GetUndetectable()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::SetKeyedProperty([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter SetKeyedProperty()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::DisassembleFunction([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter DisassembleFunction()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::TryMigrateInstance([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter TryMigrateInstance()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::InLargeObjectSpace([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter InLargeObjectSpace()";
    ASSERT(info);
    return JSTaggedValue::True();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::PerformMicrotaskCheckpoint([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter PerformMicrotaskCheckpoint()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::IsJSReceiver([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter IsJSReceiver()";
    ASSERT(info);
    return JSTaggedValue::False();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::IsDictPropertyConstTrackingEnabled([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter IsDictPropertyConstTrackingEnabled()";
    ASSERT(info);
    return JSTaggedValue::False();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::AllocateHeapNumber([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter AllocateHeapNumber()";
    ASSERT(info);
    return JSTaggedValue(0);
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::ConstructConsString([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter ConstructConsString()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::CompleteInobjectSlackTracking([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter CompleteInobjectSlackTracking()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::NormalizeElements([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter NormalizeElements()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::Call([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter Call()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::DebugPushPromise([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter DebugPushPromise()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::SetForceSlowPath([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter SetForceSlowPath()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::NotifyContextDisposed([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter NotifyContextDisposed()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::OptimizeObjectForAddingMultipleProperties([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter OptimizeObjectForAddingMultipleProperties()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::IsBeingInterpreted([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter OptimizeObjectForAddingMultipleProperties()";
    ASSERT(info);
    return JSTaggedValue::Undefined();
}

// empty function for regress-xxx test cases
JSTaggedValue BuiltinsArkTools::ClearFunctionFeedback([[maybe_unused]] EcmaRuntimeCallInfo *info)
{
    LOG_ECMA(DEBUG) << "Enter ClearFunctionFeedback()";
    return JSTaggedValue::Undefined();
}
}  // namespace panda::ecmascript::builtins