/*
 * Copyright (c) 2021-2025 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_promise_job.h"

#include "ecmascript/builtins/builtins_promise_handler.h"
#include "ecmascript/builtins/builtins_promise.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/jspandafile/js_pandafile_executor.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/js_promise.h"
#include "ecmascript/module/js_dynamic_import.h"
#include "ecmascript/module/js_module_deregister.h"
#include "ecmascript/module/js_module_manager.h"
#include "ecmascript/module/module_path_helper.h"
#include "ecmascript/module/module_tools.h"
#include "ecmascript/module/static/static_module_loader.h"
#include "ecmascript/patch/quick_fix_manager.h"

namespace panda::ecmascript::builtins {
using JSRecordInfo = ecmascript::JSPandaFile::JSRecordInfo;
using ModulePathHelper = ecmascript::ModulePathHelper;
using PathHelper = ecmascript::base::PathHelper;
using StaticModuleLoader = ecmascript::StaticModuleLoader;

#if ENABLE_LATEST_OPTIMIZATION
JSTaggedValue BuiltinsPromiseJob::PromiseReactionJob(EcmaRuntimeCallInfo *argv)
{
    ASSERT(argv);
    BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, Reaction);
    JSThread *thread = argv->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    // 1. Assert: reaction is a PromiseReaction Record.
    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
    ASSERT(value->IsPromiseReaction());
    JSHandle<PromiseReaction> reaction = JSHandle<PromiseReaction>::Cast(value);
    JSHandle<JSTaggedValue> argument = GetCallArg(argv, 1);

    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
    // 2. Let promiseCapability be reaction.[[Capabilities]].
    JSHandle<JSTaggedValue> promiseOrCapability(thread, reaction->GetPromiseOrCapability(thread));
    // 3. Let handler be reaction.[[Handler]].
    JSHandle<JSTaggedValue> handler(thread, reaction->GetHandler(thread));
    constexpr uint32_t argsLength = 1;
    JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
    JSMutableHandle<JSTaggedValue> callArg(thread, undefined);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    bool isRejected = false;
    if (handler->IsString()) {
        // 4. If handler is "Identity", let handlerResult be NormalCompletion(argument).
        // 5. Else if handler is "Thrower", let handlerResult be Completion{[[type]]: throw, [[value]]: argument,
        // [[target]]: empty}.
        callArg.Update(argument);
        if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(),
            JSHandle<EcmaString>(handler), JSHandle<EcmaString>(globalConst->GetHandledThrowerString()))) {
            isRejected = true;
        }
    } else {
        // 6. Else, let handlerResult be Call(handler, undefined, «argument»).
        EcmaRuntimeCallInfo *info =
            EcmaInterpreter::NewRuntimeCallInfo(thread, handler, undefined, undefined, argsLength);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        info->SetCallArg(argument.GetTaggedValue());
        JSTaggedValue taggedValue = JSFunction::Call(info);
        // 7. If handlerResult is an abrupt completion, then
        // a. Let status be Call(promiseCapability.[[Reject]], undefined, «handlerResult.[[value]]»).
        // b. NextJob Completion(status).
        if (thread->HasPendingException()) {
            JSHandle<JSTaggedValue> throwValue = JSPromise::IfThrowGetThrowValue(thread);
            callArg.Update(throwValue);
            thread->ClearExceptionAndExtraErrorMessage();
            isRejected = true;
        } else {
            callArg.Update(taggedValue);
        }
    }
    if (promiseOrCapability->IsUndefined()) {
        return undefined.GetTaggedValue();
    }
    if (promiseOrCapability->IsJSPromise()) {
        if (isRejected) {
            return JSPromise::RejectPromise(thread, JSHandle<JSPromise>::Cast(promiseOrCapability), callArg);
        }
        return BuiltinsPromiseHandler::InnerResolve(thread, JSHandle<JSPromise>::Cast(promiseOrCapability), callArg);
    }
    JSHandle<PromiseCapability> capability = JSHandle<PromiseCapability>::Cast(promiseOrCapability);
    EcmaRuntimeCallInfo *runtimeInfo =
       EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, undefined, undefined, argsLength);
    if (isRejected) {
        runtimeInfo->SetFunction(capability->GetReject(thread));
    } else {
        runtimeInfo->SetFunction(capability->GetResolve(thread));
    }
    runtimeInfo->SetCallArg(callArg.GetTaggedValue());
    // 8. Let status be Call(promiseCapability.[[Resolve]], undefined, «handlerResult.[[value]]»).
    return JSFunction::Call(runtimeInfo);
}
#else // ENABLE_LATEST_OPTIMIZATION
JSTaggedValue BuiltinsPromiseJob::PromiseReactionJob(EcmaRuntimeCallInfo *argv)
{
    ASSERT(argv);
    BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, Reaction);
    JSThread *thread = argv->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    // 1. Assert: reaction is a PromiseReaction Record.
    JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
    ASSERT(value->IsPromiseReaction());
    JSHandle<PromiseReaction> reaction = JSHandle<PromiseReaction>::Cast(value);
    JSHandle<JSTaggedValue> argument = GetCallArg(argv, 1);

    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
    // 2. Let promiseCapability be reaction.[[Capabilities]].
    JSHandle<PromiseCapability> capability(thread, reaction->GetPromiseCapability(thread));
    // 3. Let handler be reaction.[[Handler]].
    JSHandle<JSTaggedValue> handler(thread, reaction->GetHandler(thread));
    JSHandle<JSTaggedValue> call(thread, capability->GetResolve(thread));
    const uint32_t argsLength = 1;
    JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
    EcmaRuntimeCallInfo *runtimeInfo =
        EcmaInterpreter::NewRuntimeCallInfo(thread, call, undefined, undefined, argsLength);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    if (handler->IsString()) {
        // 4. If handler is "Identity", let handlerResult be NormalCompletion(argument).
        // 5. Else if handler is "Thrower", let handlerResult be Completion{[[type]]: throw, [[value]]: argument,
        // [[target]]: empty}.
        runtimeInfo->SetCallArg(argument.GetTaggedValue());
        if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(),
            JSHandle<EcmaString>(handler), JSHandle<EcmaString>(globalConst->GetHandledThrowerString()))) {
            runtimeInfo->SetFunction(capability->GetReject(thread));
        }
    } else {
        // 6. Else, let handlerResult be Call(handler, undefined, «argument»).
        EcmaRuntimeCallInfo *info =
            EcmaInterpreter::NewRuntimeCallInfo(thread, handler, undefined, undefined, argsLength);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        info->SetCallArg(argument.GetTaggedValue());
        JSTaggedValue taggedValue = JSFunction::Call(info);
        // 7. If handlerResult is an abrupt completion, then
        // a. Let status be Call(promiseCapability.[[Reject]], undefined, «handlerResult.[[value]]»).
        // b. NextJob Completion(status).
        if (thread->HasPendingException()) {
            JSHandle<JSTaggedValue> throwValue = JSPromise::IfThrowGetThrowValue(thread);
            runtimeInfo->SetCallArg(throwValue.GetTaggedValue());
            thread->ClearExceptionAndExtraErrorMessage();
            runtimeInfo->SetFunction(capability->GetReject(thread));
        } else {
            runtimeInfo->SetCallArg(taggedValue);
        }
    }
    // 8. Let status be Call(promiseCapability.[[Resolve]], undefined, «handlerResult.[[value]]»).
    return JSFunction::Call(runtimeInfo);
}
#endif // ENABLE_LATEST_OPTIMIZATION

JSTaggedValue BuiltinsPromiseJob::PromiseResolveThenableJob(EcmaRuntimeCallInfo *argv)
{
    ASSERT(argv);
    BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, ResolveThenableJob);
    JSThread *thread = argv->GetThread();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> promise = GetCallArg(argv, 0);
    ASSERT(promise->IsJSPromise());
    // 1. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
    JSHandle<ResolvingFunctionsRecord> resolvingFunctions =
        JSPromise::CreateResolvingFunctions(thread, JSHandle<JSPromise>::Cast(promise));
    JSHandle<JSTaggedValue> thenable = GetCallArg(argv, 1);
    JSHandle<JSTaggedValue> then = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);

    // 2. Let thenCallResult be Call(then, thenable, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»).
    const uint32_t argsLength = 2; // 2: «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»
    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
    EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, then, thenable, undefined, argsLength);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    info->SetCallArg(resolvingFunctions->GetResolveFunction(thread), resolvingFunctions->GetRejectFunction(thread));
    JSTaggedValue result = JSFunction::Call(info);
    JSHandle<JSTaggedValue> thenResult(thread, result);
    // 3. If thenCallResult is an abrupt completion,
    // a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «thenCallResult.[[value]]»).
    // b. NextJob Completion(status).
    if (thread->HasPendingException()) {
        thenResult = JSPromise::IfThrowGetThrowValue(thread);
        thread->ClearExceptionAndExtraErrorMessage();
        JSHandle<JSTaggedValue> reject(thread, resolvingFunctions->GetRejectFunction(thread));
        EcmaRuntimeCallInfo *runtimeInfo =
            EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        runtimeInfo->SetCallArg(thenResult.GetTaggedValue());
        return JSFunction::Call(runtimeInfo);
    }
    // 4. NextJob Completion(thenCallResult).
    return result;
}

JSTaggedValue BuiltinsPromiseJob::DynamicImportJob(EcmaRuntimeCallInfo *argv)
{
    ASSERT(argv);
    BUILTINS_API_TRACE(argv->GetThread(), PromiseJob, DynamicImportJob);
    JSThread *thread = argv->GetThread();
    EcmaVM *vm = thread->GetEcmaVM();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSPromiseReactionsFunction> resolve(GetCallArg(argv, 0));
    JSHandle<JSPromiseReactionsFunction> reject(GetCallArg(argv, 1));   // 1 : reject method
    JSHandle<EcmaString> dirPath(GetCallArg(argv, 2));                  // 2 : current file path(containing file name)
    JSHandle<JSTaggedValue> specifier(GetCallArg(argv, 3));             // 3 : request module's path
    JSHandle<JSTaggedValue> recordName(GetCallArg(argv, 4));            // 4 : js recordName or undefined

    // Let specifierString be Completion(ToString(specifier))
    JSHandle<EcmaString> specifierString = JSTaggedValue::ToString(thread, specifier);
    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));

    // Resolve request module's ohmurl
    CString entryPoint = JSPandaFile::ENTRY_MAIN_FUNCTION;
    CString fileName = ModulePathHelper::Utf8ConvertToString(thread, dirPath.GetTaggedValue());
    // When hot reload happens inside `then()`, restore the base filename or the module may not be found
    fileName = vm->GetQuickFixManager()->GetBaseFileNameForHotReload(thread, fileName);
    CString requestPath = ModulePathHelper::Utf8ConvertToString(thread, specifierString.GetTaggedValue());
    LOG_ECMA(DEBUG) << "Start importing dynamic module : " << requestPath;
    ModuleTraceScope moduleTraceScope = ModuleTraceScope::Open(
        thread, "BuiltinsPromiseJob::DynamicImport:", requestPath);
    std::shared_ptr<JSPandaFile> curJsPandaFile;
    CString recordNameStr;
    if (!recordName->IsUndefined()) {
        recordNameStr = ModulePathHelper::Utf8ConvertToString(thread, recordName.GetTaggedValue());
        curJsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(
            thread, fileName, recordNameStr.c_str(), false, ExecuteTypes::STATIC);
        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
            HandleModuleException(thread, resolve, reject, specifierString));
        if (curJsPandaFile == nullptr) {
            LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << recordNameStr;
        }
        // translate requestPath to OhmUrl
        if (vm->IsNormalizedOhmUrlPack()) {
            ModulePathHelper::TranslateExpressionToNormalized(thread, curJsPandaFile.get(), fileName, recordNameStr,
                requestPath);
            LOG_ECMA(DEBUG) << "Exit Translate Normalized OhmUrl for DynamicImport, resultPath: " << requestPath;
            RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
                HandleModuleException(thread, resolve, reject, specifierString));
        } else if (ModulePathHelper::NeedTranslate(requestPath)) {
            ModulePathHelper::TranslateExpressionInput(curJsPandaFile.get(), requestPath);
            LOG_ECMA(DEBUG) << "Exit Translate OhmUrl for DynamicImport, resultPath: " << requestPath;
        }
    }
    // resolve native module
    ModuleManager *moduleManager = thread->GetModuleManager();
    if (SourceTextModule::IsNativeModule(requestPath)) {
        return DynamicImport::ExecuteNativeOrJsonModule(thread, requestPath,
            SourceTextModule::GetNativeModuleType(requestPath), resolve, reject);
    }
    bool isExport = ModulePathHelper::CheckExportsWithOhmurl(vm, fileName, recordNameStr, requestPath);
    if (!isExport) {
        CString normalizeStr = ModulePathHelper::ReformatPath(requestPath);
        CString msg = "Cannot find dynamic-import module in oh-exports:'" + normalizeStr;
        THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(),
            HandleModuleException(thread, resolve, reject, specifierString));
    }
    CString moduleName;
    if (recordName->IsUndefined()) {
        fileName = ResolveFilenameFromNative(thread, fileName, requestPath);
        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
            HandleModuleException(thread, resolve, reject, specifierString));
        moduleName = fileName;
    } else {
        entryPoint =
            ModulePathHelper::ConcatFileNameWithMerge(thread, curJsPandaFile.get(),
                fileName, recordNameStr, requestPath);
        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
            HandleModuleException(thread, resolve, reject, specifierString));
        moduleName = entryPoint;
    }
    std::shared_ptr<JSPandaFile> jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(
        thread, fileName, entryPoint, false, ExecuteTypes::STATIC);
    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
        HandleModuleException(thread, resolve, reject, specifierString));
    if (jsPandaFile == nullptr) {
        LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << fileName;
    }

    JSRecordInfo *recordInfo = jsPandaFile->CheckAndGetRecordInfo(entryPoint);
    if (recordInfo == nullptr) {
        CString normalizeStr = ModulePathHelper::ReformatPath(entryPoint);
        CString msg = "Cannot find dynamic-import module '" + normalizeStr;
        THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(),
            HandleModuleException(thread, resolve, reject, specifierString));
    }

    if (jsPandaFile->IsJson(recordInfo)) {
        return DynamicImport::ExecuteNativeOrJsonModule(
            thread, entryPoint, ModuleTypes::JSON_MODULE, resolve, reject, jsPandaFile.get());
    }
    // IsInstantiatedModule is for lazy module to execute
    if (!moduleManager->IsModuleLoaded(moduleName) || moduleManager->IsInstantiatedModule(moduleName)) {
        if (!JSPandaFileExecutor::ExecuteFromAbcFile(
            thread, fileName.c_str(), entryPoint.c_str(), false, ExecuteTypes::DYNAMIC)) {
            CString msg = "Cannot execute request dynamic-imported module : " + entryPoint;
            THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(),
                HandleModuleException(thread, resolve, reject, specifierString));
        }
    } else {
        JSHandle<SourceTextModule> moduleRecord = moduleManager->GetImportedModule(moduleName);
        ModuleDeregister::DisableMultiEntryDeregister(thread, moduleRecord, ExecuteTypes::DYNAMIC);
    }

    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
        HandleModuleException(thread, resolve, reject, specifierString));
    JSMutableHandle<JSTaggedValue> moduleNamespace(thread, JSTaggedValue::Undefined());
    // only support importing es module, or return a default object.
    if (!jsPandaFile->IsModule(recordInfo)) {
        moduleNamespace.Update(vm->GetGlobalEnv()->GetExportOfScript());
    } else {
        // b. Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier).
        JSHandle<SourceTextModule> moduleRecord =
            moduleManager->GetImportedModule(moduleName);
        moduleRecord->CheckAndThrowModuleError(thread);
        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
            HandleModuleException(thread, resolve, reject, specifierString));
        JSHandle<JSTaggedValue> nameSp = SourceTextModule::GetModuleNamespace(thread, moduleRecord);
        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
            HandleModuleException(thread, resolve, reject, specifierString));
        // d. Let namespace be ? GetModuleNamespace(moduleRecord).
        moduleNamespace.Update(nameSp);
    }
    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
    EcmaRuntimeCallInfo *info =
        EcmaInterpreter::NewRuntimeCallInfo(thread,
                                            JSHandle<JSTaggedValue>(resolve),
                                            undefined, undefined, 1);
    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread,
        HandleModuleException(thread, resolve, reject, specifierString));
    info->SetCallArg(moduleNamespace.GetTaggedValue());
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSFunction::Call(info);
}

JSTaggedValue BuiltinsPromiseJob::CatchException(JSThread *thread, JSHandle<JSPromiseReactionsFunction> reject)
{
    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
    ASSERT(thread->HasPendingException());
    JSHandle<JSTaggedValue> thenResult = JSPromise::IfThrowGetThrowValue(thread);
    thread->ClearExceptionAndExtraErrorMessage();
    JSHandle<JSTaggedValue> rejectfun(reject);
    EcmaRuntimeCallInfo *runtimeInfo =
        EcmaInterpreter::NewRuntimeCallInfo(thread, rejectfun, undefined, undefined, 1);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    runtimeInfo->SetCallArg(thenResult.GetTaggedValue());
    return JSFunction::Call(runtimeInfo);
}

JSTaggedValue BuiltinsPromiseJob::HandleModuleException(JSThread *thread, JSHandle<JSPromiseReactionsFunction> resolve,
    JSHandle<JSPromiseReactionsFunction> reject, JSHandle<EcmaString> specifierString)
{
    CString requestPath = ModulePathHelper::Utf8ConvertToString(thread, specifierString.GetTaggedValue());
    return HandleModuleException(thread, resolve, reject, requestPath);
}

JSTaggedValue BuiltinsPromiseJob::HandleModuleException(JSThread *thread, JSHandle<JSPromiseReactionsFunction> resolve,
    JSHandle<JSPromiseReactionsFunction> reject, const CString &requestPath)
{
    ASSERT(thread->HasPendingException());
    LOG_ECMA(DEBUG) << "start handle module exception " << requestPath;
    if (thread->GetEcmaVM()->GetArkTSMode() == ArkTSMode::DYNAMIC) {
        return CatchException(thread, reject);
    }
    // If the ohmurl is detected to be in compliance with the dynamic prefix rule, then throw an exception directly
    if (!StaticModuleLoader::CanTryLoadStaticModulePath(requestPath)) {
        LOG_ECMA(DEBUG) << "handle dynamic module exception " << requestPath;
        return CatchException(thread, reject);
    }
    LOG_ECMA(DEBUG) << "try to start load static module: " << requestPath;
    JSHandle<JSTaggedValue> errorReuslt = JSPromise::IfThrowGetThrowValue(thread);
    thread->ClearExceptionAndExtraErrorMessage();
    EcmaVM *vm = thread->GetEcmaVM();
    Local<JSValueRef> getEsModule = StaticModuleLoader::GetStaticModuleLoadFunc(vm);
    if (!getEsModule->IsFunction(vm)) {
        LOG_ECMA(DEBUG) << "napi static module function not found " << requestPath;
        thread->SetException(errorReuslt.GetTaggedValue());
        return CatchException(thread, reject);
    }
    // try load static module;
    Local<FunctionRef> getEsModuleFunc = getEsModule;
    ModuleManager *moduleManager = thread->GetModuleManager();
    JSHandle<JSTaggedValue> exportObject = StaticModuleLoader::LoadStaticModule(thread, getEsModuleFunc, requestPath);
    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
    LOG_ECMA(DEBUG) << "load static module successfull, requestPath: " << requestPath;
    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
    EcmaRuntimeCallInfo *info =
        EcmaInterpreter::NewRuntimeCallInfo(thread,
                                            JSHandle<JSTaggedValue>(resolve),
                                            undefined, undefined, 1);
    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject));
    ASSERT(info != nullptr);
    info->SetCallArg(exportObject.GetTaggedValue());
    return JSFunction::Call(info);
}

}  // namespace panda::ecmascript::builtins