1/*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/ecma_context.h"
17
18#include "ecmascript/builtins/builtins.h"
19#include "ecmascript/builtins/builtins_regexp.h"
20#include "ecmascript/builtins/builtins_number.h"
21#include "ecmascript/compiler/pgo_type/pgo_type_manager.h"
22#include "ecmascript/dfx/vmstat/opt_code_profiler.h"
23#include "ecmascript/jit/jit.h"
24#include "ecmascript/linked_hash_table.h"
25#include "ecmascript/module/module_logger.h"
26#include "ecmascript/jspandafile/abc_buffer_cache.h"
27#include "ecmascript/platform/aot_crash_info.h"
28#include "ecmascript/platform/log.h"
29#include "ecmascript/regexp/regexp_parser_cache.h"
30#include "ecmascript/require/js_require_manager.h"
31#include "ecmascript/runtime.h"
32#include "ecmascript/snapshot/mem/snapshot.h"
33#include "ecmascript/stubs/runtime_stubs.h"
34#include "ecmascript/sustaining_js_handle.h"
35
36namespace panda::ecmascript {
37using PathHelper = base::PathHelper;
38
39EcmaContext::EcmaContext(JSThread *thread)
40    : thread_(thread),
41      vm_(thread->GetEcmaVM()),
42      factory_(vm_->GetFactory()),
43      aotFileManager_(vm_->GetAOTFileManager())
44{
45}
46
47/* static */
48EcmaContext *EcmaContext::Create(JSThread *thread)
49{
50    LOG_ECMA(INFO) << "EcmaContext::Create";
51    auto context = new EcmaContext(thread);
52    return context;
53}
54
55// static
56bool EcmaContext::Destroy(EcmaContext *context)
57{
58    if (context != nullptr) {
59        delete context;
60        context = nullptr;
61        return true;
62    }
63    return false;
64}
65
66bool EcmaContext::Initialize()
67{
68    LOG_ECMA(DEBUG) << "EcmaContext::Initialize";
69    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "EcmaContext::Initialize");
70    [[maybe_unused]] EcmaHandleScope scope(thread_);
71    propertiesCache_ = new PropertiesCache();
72    regExpParserCache_ = new RegExpParserCache();
73    unsharedConstpools_.fill(JSTaggedValue::Hole());
74    thread_->SetUnsharedConstpools(reinterpret_cast<uintptr_t>(unsharedConstpools_.data()));
75
76    thread_->SetGlobalConst(&globalConst_);
77    globalConst_.Init(thread_);
78    JSHandle<JSHClass> hClassHandle = JSHandle<JSHClass>(thread_, globalConst_.GetHClassClass());
79    JSHandle<JSHClass> globalEnvClass = factory_->NewEcmaHClass(
80        *hClassHandle,
81        GlobalEnv::SIZE,
82        JSType::GLOBAL_ENV);
83    auto arrayHClassIndexMaps = Elements::InitializeHClassMap();
84    thread_->SetArrayHClassIndexMap(arrayHClassIndexMaps);
85    JSHandle<GlobalEnv> globalEnv = factory_->NewGlobalEnv(*globalEnvClass);
86    globalEnv->Init(thread_);
87    globalEnv_ = globalEnv.GetTaggedValue();
88    Builtins builtins;
89    bool builtinsLazyEnabled = vm_->GetJSOptions().IsWorker() && vm_->GetJSOptions().GetEnableBuiltinsLazy();
90    thread_->SetEnableLazyBuiltins(builtinsLazyEnabled);
91    builtins.Initialize(globalEnv, thread_, builtinsLazyEnabled);
92
93    InitializeDefaultLocale();
94    InitializeDefaultCompareStringsOption();
95    SetupRegExpResultCache();
96    SetupRegExpGlobalResult();
97    SetupNumberToStringResultCache();
98    SetupStringSplitResultCache();
99    SetupStringToListResultCache();
100    microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue();
101    moduleManager_ = new ModuleManager(vm_);
102    ptManager_ = new kungfu::PGOTypeManager(vm_);
103    optCodeProfiler_ = new OptCodeProfiler();
104    abcBufferCache_ = new AbcBufferCache();
105    if (vm_->GetJSOptions().GetTypedOpProfiler()) {
106        typedOpProfiler_ = new TypedOpProfiler();
107    }
108    if (vm_->GetJSOptions().EnableModuleLog() && !vm_->GetJSOptions().IsWorker()) {
109        moduleLogger_ = new ModuleLogger(vm_);
110    }
111    functionProtoTransitionTable_ = new FunctionProtoTransitionTable(thread_);
112    sustainingJSHandleList_ = new SustainingJSHandleList();
113    initialized_ = true;
114    return true;
115}
116
117void EcmaContext::InitializeEcmaScriptRunStat()
118{
119    // NOLINTNEXTLINE(modernize-avoid-c-arrays)
120    static const char *runtimeCallerNames[] = {
121// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
122#define INTERPRETER_CALLER_NAME(name) "Interpreter::" #name,
123    INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_NAME)  // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
124#undef INTERPRETER_CALLER_NAME
125// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
126#define BUILTINS_API_NAME(class, name) "BuiltinsApi::" #class "_" #name,
127    BUILTINS_API_LIST(BUILTINS_API_NAME)
128#undef BUILTINS_API_NAME
129#define ABSTRACT_OPERATION_NAME(class, name) "AbstractOperation::" #class "_" #name,
130    ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_NAME)
131#undef ABSTRACT_OPERATION_NAME
132#define MEM_ALLOCATE_AND_GC_NAME(name) "Memory::" #name,
133    MEM_ALLOCATE_AND_GC_LIST(MEM_ALLOCATE_AND_GC_NAME)
134#undef MEM_ALLOCATE_AND_GC_NAME
135#define DEF_RUNTIME_ID(name) "Runtime::" #name,
136    RUNTIME_STUB_WITH_GC_LIST(DEF_RUNTIME_ID)
137#undef DEF_RUNTIME_ID
138    };
139    static_assert(sizeof(runtimeCallerNames) == sizeof(const char *) * ecmascript::RUNTIME_CALLER_NUMBER,
140                  "Invalid runtime caller number");
141    runtimeStat_ = vm_->GetChunk()->New<EcmaRuntimeStat>(runtimeCallerNames, ecmascript::RUNTIME_CALLER_NUMBER);
142    if (UNLIKELY(runtimeStat_ == nullptr)) {
143        LOG_FULL(FATAL) << "alloc runtimeStat_ failed";
144        UNREACHABLE();
145    }
146}
147
148void EcmaContext::ClearIcuCache(JSThread *thread)
149{
150    for (uint32_t i = 0; i < static_cast<uint32_t>(IcuFormatterType::ICU_FORMATTER_TYPE_COUNT); i++) {
151        auto &icuFormatter = icuObjCache_[i];
152        NativePointerCallback deleteEntry = icuFormatter.deleteEntry;
153        if (deleteEntry != nullptr) {
154            deleteEntry(thread->GetEnv(), icuFormatter.icuObj, vm_);
155        }
156        icuFormatter = EcmaContext::IcuFormatter{};
157    }
158}
159
160void EcmaContext::SetRuntimeStatEnable(bool flag)
161{
162    static uint64_t start = 0;
163    if (flag) {
164        start = PandaRuntimeTimer::Now();
165        if (runtimeStat_ == nullptr) {
166            InitializeEcmaScriptRunStat();
167        }
168    } else {
169        LOG_ECMA(INFO) << "Runtime State duration:" << PandaRuntimeTimer::Now() - start << "(ns)";
170        if (runtimeStat_ != nullptr && runtimeStat_->IsRuntimeStatEnabled()) {
171            runtimeStat_->Print();
172            runtimeStat_->ResetAllCount();
173        }
174    }
175    if (runtimeStat_ != nullptr) {
176        runtimeStat_->SetRuntimeStatEnabled(flag);
177    }
178}
179
180EcmaContext::~EcmaContext()
181{
182    if (runtimeStat_ != nullptr && runtimeStat_->IsRuntimeStatEnabled()) {
183        runtimeStat_->Print();
184    }
185    for (auto n : handleStorageNodes_) {
186        delete n;
187    }
188    handleStorageNodes_.clear();
189    currentHandleStorageIndex_ = -1;
190#ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK
191    handleScopeCount_ = 0;
192    primitiveScopeCount_ = 0;
193#endif
194    handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr;
195
196    for (auto n : primitiveStorageNodes_) {
197        delete n;
198    }
199    primitiveStorageNodes_.clear();
200    currentPrimitiveStorageIndex_ = -1;
201    primitiveScopeStorageNext_ = primitiveScopeStorageEnd_ = nullptr;
202
203    if (vm_->IsEnableBaselineJit() || vm_->IsEnableFastJit()) {
204        // clear jit task
205        vm_->GetJit()->ClearTask(this);
206    }
207
208    ClearBufferData();
209    // clear c_address: c++ pointer delete
210    if (!vm_->IsBundlePack()) {
211        std::shared_ptr<JSPandaFile> jsPandaFile =
212            JSPandaFileManager::GetInstance()->FindJSPandaFile(vm_->GetAssetPath());
213        if (jsPandaFile != nullptr) {
214            jsPandaFile->DeleteParsedConstpoolVM(vm_);
215        }
216    }
217    ClearDefaultLocale();
218    ClearDefaultComapreStringsOption();
219    // clear icu cache
220    ClearIcuCache(thread_);
221
222    if (runtimeStat_ != nullptr) {
223        vm_->GetChunk()->Delete(runtimeStat_);
224        runtimeStat_ = nullptr;
225    }
226    if (optCodeProfiler_ != nullptr) {
227        delete optCodeProfiler_;
228        optCodeProfiler_ = nullptr;
229    }
230    if (typedOpProfiler_ != nullptr) {
231        delete typedOpProfiler_;
232        typedOpProfiler_ = nullptr;
233    }
234    if (moduleManager_ != nullptr) {
235        delete moduleManager_;
236        moduleManager_ = nullptr;
237    }
238    if (moduleLogger_ != nullptr) {
239        delete moduleLogger_;
240        moduleLogger_ = nullptr;
241    }
242    if (ptManager_ != nullptr) {
243        delete ptManager_;
244        ptManager_ = nullptr;
245    }
246    if (regExpParserCache_ != nullptr) {
247        delete regExpParserCache_;
248        regExpParserCache_ = nullptr;
249    }
250    if (aotFileManager_ != nullptr) {
251        aotFileManager_ = nullptr;
252    }
253    if (propertiesCache_ != nullptr) {
254        delete propertiesCache_;
255        propertiesCache_ = nullptr;
256    }
257    if (sustainingJSHandleList_ != nullptr) {
258        delete sustainingJSHandleList_;
259        sustainingJSHandleList_ = nullptr;
260    }
261    if (functionProtoTransitionTable_ != nullptr) {
262        delete functionProtoTransitionTable_;
263        functionProtoTransitionTable_ = nullptr;
264    }
265    if (abcBufferCache_ != nullptr) {
266        delete abcBufferCache_;
267        abcBufferCache_ = nullptr;
268    }
269    // clear join stack
270    joinStack_.clear();
271
272    for (auto v : stringifyCache_) {
273        v.clear();
274    }
275}
276
277JSTaggedValue EcmaContext::InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc, JSHandle<JSTaggedValue> &thisArg,
278                                                   const JSPandaFile *jsPandaFile, std::string_view entryPoint,
279                                                   CJSInfo* cjsInfo)
280{
281    aotFileManager_->SetAOTMainFuncEntry(mainFunc, jsPandaFile, entryPoint);
282    return JSFunction::InvokeOptimizedEntrypoint(thread_, mainFunc, thisArg, cjsInfo);
283}
284
285JSTaggedValue EcmaContext::ExecuteAot(size_t actualNumArgs, JSTaggedType *args,
286                                      const JSTaggedType *prevFp, bool needPushArgv)
287{
288    INTERPRETER_TRACE(thread_, ExecuteAot);
289    ASSERT(thread_->IsInManagedState());
290    auto entry = thread_->GetRTInterface(kungfu::RuntimeStubCSigns::ID_JSFunctionEntry);
291    // entry of aot
292    auto res = reinterpret_cast<JSFunctionEntryType>(entry)(thread_->GetGlueAddr(),
293                                                            actualNumArgs,
294                                                            args,
295                                                            reinterpret_cast<uintptr_t>(prevFp),
296                                                            needPushArgv);
297    return res;
298}
299
300Expected<JSTaggedValue, bool> EcmaContext::CommonInvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile,
301    std::string_view entryPoint, JSHandle<JSFunction> &func, bool executeFromJob)
302{
303    ASSERT(thread_->IsInManagedState());
304    JSHandle<JSTaggedValue> global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject();
305    JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
306    CString entry = entryPoint.data();
307    JSRecordInfo *recordInfo = nullptr;
308    bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, &recordInfo);
309    if (!hasRecord) {
310        CString msg = "Cannot find module '" + entry + "' , which is application Entry Point";
311        THROW_REFERENCE_ERROR_AND_RETURN(thread_, msg.c_str(), Unexpected(false));
312    }
313    if (jsPandaFile->IsModule(recordInfo)) {
314        global = undefined;
315        CString moduleName = jsPandaFile->GetJSPandaFileDesc();
316        if (!jsPandaFile->IsBundlePack()) {
317            moduleName = entry;
318        }
319        JSHandle<SourceTextModule> module;
320        if (jsPandaFile->IsSharedModule(recordInfo)) {
321            module = SharedModuleManager::GetInstance()->GetSModule(thread_, entry);
322        } else {
323            module = moduleManager_->HostGetImportedModule(moduleName);
324        }
325        // esm -> SourceTextModule; cjs or script -> string of recordName
326        module->SetSendableEnv(thread_, JSTaggedValue::Undefined());
327        func->SetModule(thread_, module);
328    } else {
329        // if it is Cjs at present, the module slot of the function is not used. We borrow it to store the recordName,
330        // which can avoid the problem of larger memory caused by the new slot
331        JSHandle<EcmaString> recordName = factory_->NewFromUtf8(entry);
332        func->SetModule(thread_, recordName);
333    }
334    vm_->CheckStartCpuProfiler();
335
336    JSTaggedValue result;
337    if (jsPandaFile->IsCjs(recordInfo)) {
338        CJSExecution(func, global, jsPandaFile, entryPoint);
339    } else {
340        if (aotFileManager_->IsLoadMain(jsPandaFile, entry)) {
341            EcmaRuntimeStatScope runtimeStatScope(vm_);
342            result = InvokeEcmaAotEntrypoint(func, global, jsPandaFile, entryPoint);
343        } else if (vm_->GetJSOptions().IsEnableForceJitCompileMain()) {
344            Jit::Compile(vm_, func, CompilerTier::FAST);
345            EcmaRuntimeStatScope runtimeStatScope(vm_);
346            result = JSFunction::InvokeOptimizedEntrypoint(thread_, func, global, nullptr);
347        } else if (vm_->GetJSOptions().IsEnableForceBaselineCompileMain()) {
348            Jit::Compile(vm_, func, CompilerTier::BASELINE);
349            EcmaRuntimeCallInfo *info =
350                EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle<JSTaggedValue>(func), global, undefined, 0);
351            EcmaRuntimeStatScope runtimeStatScope(vm_);
352            result = EcmaInterpreter::Execute(info);
353        } else {
354            EcmaRuntimeCallInfo *info =
355                EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle<JSTaggedValue>(func), global, undefined, 0);
356            EcmaRuntimeStatScope runtimeStatScope(vm_);
357            result = EcmaInterpreter::Execute(info);
358        }
359
360        if (!thread_->HasPendingException() && !executeFromJob) {
361            JSHandle<JSTaggedValue> handleResult(thread_, result);
362            job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
363            result = handleResult.GetTaggedValue();
364        }
365    }
366    if (thread_->HasPendingException()) {
367#ifdef PANDA_TARGET_OHOS
368        return result;
369#else
370        return Unexpected(false);
371#endif
372    }
373    return result;
374}
375
376Expected<JSTaggedValue, bool> EcmaContext::InvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile,
377                                                                std::string_view entryPoint, bool executeFromJob)
378{
379    [[maybe_unused]] EcmaHandleScope scope(thread_);
380    auto &options = const_cast<EcmaVM *>(thread_->GetEcmaVM())->GetJSOptions();
381    if (options.EnableModuleLog()) {
382        LOG_FULL(INFO) << "current executing file's name " << entryPoint.data();
383    }
384    ModuleLogger *moduleLogger = GetModuleLogger();
385    if (moduleLogger != nullptr) {
386        moduleLogger->SetStartTime(CString(entryPoint));
387    }
388    JSHandle<Program> program = JSPandaFileManager::GetInstance()->GenerateProgram(vm_, jsPandaFile, entryPoint);
389    if (program.IsEmpty()) {
390        LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed";
391        return Unexpected(false);
392    }
393    // for debugger
394    vm_->GetJsDebuggerManager()->GetNotificationManager()->LoadModuleEvent(
395        jsPandaFile->GetJSPandaFileDesc(), entryPoint);
396
397    JSHandle<JSFunction> func(thread_, program->GetMainFunction());
398    Expected<JSTaggedValue, bool> result = CommonInvokeEcmaEntrypoint(jsPandaFile, entryPoint, func, executeFromJob);
399
400#ifdef PANDA_TARGET_OHOS
401    if (thread_->HasPendingException()) {
402        HandleUncaughtException();
403    }
404#endif
405
406    if (moduleLogger != nullptr) {
407        moduleLogger->SetEndTime(CString(entryPoint));
408    }
409    return result;
410}
411
412Expected<JSTaggedValue, bool> EcmaContext::InvokeEcmaEntrypointForHotReload(
413    const JSPandaFile *jsPandaFile, std::string_view entryPoint, bool executeFromJob)
414{
415    [[maybe_unused]] EcmaHandleScope scope(thread_);
416    JSHandle<Program> program = JSPandaFileManager::GetInstance()->GenerateProgram(vm_, jsPandaFile, entryPoint);
417
418    JSHandle<JSFunction> func(thread_, program->GetMainFunction());
419    Expected<JSTaggedValue, bool> result = CommonInvokeEcmaEntrypoint(jsPandaFile, entryPoint, func, executeFromJob);
420
421    JSHandle<JSTaggedValue> finalModuleRecord(thread_, func->GetModule());
422    // avoid GC problems.
423    GlobalHandleCollection gloalHandleCollection(thread_);
424    JSHandle<JSTaggedValue> moduleRecordHandle =
425        gloalHandleCollection.NewHandle<JSTaggedValue>(finalModuleRecord->GetRawData());
426    CString recordName = entryPoint.data();
427    AddPatchModule(recordName, moduleRecordHandle);
428
429    // print exception information
430    if (thread_->HasPendingException() &&
431        Method::Cast(func->GetMethod())->GetMethodName() != JSPandaFile::PATCH_FUNCTION_NAME_0) {
432        return Unexpected(false);
433    }
434    return result;
435}
436
437void EcmaContext::CJSExecution(JSHandle<JSFunction> &func, JSHandle<JSTaggedValue> &thisArg,
438                               const JSPandaFile *jsPandaFile, std::string_view entryPoint)
439{
440    // create "module", "exports", "require", "filename", "dirname"
441    JSHandle<CjsModule> module = factory_->NewCjsModule();
442    JSHandle<JSTaggedValue> require = GetGlobalEnv()->GetCjsRequireFunction();
443    JSHandle<CjsExports> exports = factory_->NewCjsExports();
444    CString fileNameStr;
445    CString dirNameStr;
446    if (jsPandaFile->IsBundlePack()) {
447        ModulePathHelper::ResolveCurrentPath(dirNameStr, fileNameStr, jsPandaFile);
448    } else {
449        JSTaggedValue funcFileName = func->GetModule();
450        ASSERT(funcFileName.IsString());
451        fileNameStr = ModulePathHelper::Utf8ConvertToString(funcFileName);
452        dirNameStr = PathHelper::ResolveDirPath(fileNameStr);
453    }
454    JSHandle<JSTaggedValue> fileName = JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf8(fileNameStr));
455    JSHandle<JSTaggedValue> dirName = JSHandle<JSTaggedValue>::Cast(factory_->NewFromUtf8(dirNameStr));
456    CJSInfo cjsInfo(module, require, exports, fileName, dirName);
457    RequireManager::InitializeCommonJS(thread_, cjsInfo);
458    if (aotFileManager_->IsLoadMain(jsPandaFile, entryPoint.data())) {
459        EcmaRuntimeStatScope runtimeStateScope(vm_);
460        isAotEntry_ = true;
461        InvokeEcmaAotEntrypoint(func, thisArg, jsPandaFile, entryPoint, &cjsInfo);
462    } else {
463        // Execute main function
464        JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
465        EcmaRuntimeCallInfo *info =
466            EcmaInterpreter::NewRuntimeCallInfo(thread_,
467                                                JSHandle<JSTaggedValue>(func),
468                                                thisArg, undefined, 5); // 5 : argument numbers
469        RETURN_IF_ABRUPT_COMPLETION(thread_);
470        if (info == nullptr) {
471            LOG_ECMA(ERROR) << "CJSExecution Stack overflow!";
472            return;
473        }
474        info->SetCallArg(cjsInfo.exportsHdl.GetTaggedValue(),
475            cjsInfo.requireHdl.GetTaggedValue(),
476            cjsInfo.moduleHdl.GetTaggedValue(),
477            cjsInfo.filenameHdl.GetTaggedValue(),
478            cjsInfo.dirnameHdl.GetTaggedValue());
479        EcmaRuntimeStatScope runtimeStatScope(vm_);
480        EcmaInterpreter::Execute(info);
481    }
482    if (!thread_->HasPendingException()) {
483        // Collecting module.exports : exports ---> module.exports --->Module._cache
484        RequireManager::CollectExecutedExp(thread_, cjsInfo);
485    }
486}
487
488void EcmaContext::LoadProtoTransitionTable(JSTaggedValue constpool)
489{
490    JSTaggedValue protoTransitionTable = ConstantPool::Cast(constpool.GetTaggedObject())->GetProtoTransTableInfo();
491    functionProtoTransitionTable_->UpdateProtoTransitionTable(
492        thread_, JSHandle<PointerToIndexDictionary>(thread_, protoTransitionTable));
493}
494
495void EcmaContext::ResetProtoTransitionTableOnConstpool(JSTaggedValue constpool)
496{
497    ConstantPool::Cast(constpool.GetTaggedObject())->SetProtoTransTableInfo(thread_, JSTaggedValue::Undefined());
498}
499
500// just find unshared constpool, not create
501JSTaggedValue EcmaContext::FindUnsharedConstpool(JSTaggedValue sharedConstpool)
502{
503    ConstantPool *shareCp = ConstantPool::Cast(sharedConstpool.GetTaggedObject());
504    int32_t constpoolIndex = shareCp->GetUnsharedConstpoolIndex();
505    // unshared constpool index is default INT32_MAX.
506    ASSERT(0 <= constpoolIndex && constpoolIndex != ConstantPool::CONSTPOOL_TYPE_FLAG &&
507        constpoolIndex < UNSHARED_CONSTANTPOOL_COUNT);
508    return unsharedConstpools_[constpoolIndex];
509}
510
511JSTaggedValue EcmaContext::FindOrCreateUnsharedConstpool(JSTaggedValue sharedConstpool)
512{
513    JSTaggedValue unsharedConstpool = FindUnsharedConstpool(sharedConstpool);
514    if (unsharedConstpool.IsHole()) {
515        ConstantPool *shareCp = ConstantPool::Cast(sharedConstpool.GetTaggedObject());
516        int32_t constpoolIndex = shareCp->GetUnsharedConstpoolIndex();
517        // unshared constpool index is default INT32_MAX.
518        ASSERT(0 <= constpoolIndex && constpoolIndex != ConstantPool::CONSTPOOL_TYPE_FLAG &&
519            constpoolIndex < UNSHARED_CONSTANTPOOL_COUNT);
520        ASSERT(constpoolIndex != INT32_MAX);
521        JSHandle<ConstantPool> unshareCp =
522            ConstantPool::CreateUnSharedConstPoolBySharedConstpool(vm_, shareCp->GetJSPandaFile(), shareCp);
523        unsharedConstpool = unshareCp.GetTaggedValue();
524        SetUnsharedConstpool(constpoolIndex, unsharedConstpool);
525    }
526    return unsharedConstpool;
527}
528
529void EcmaContext::EraseUnusedConstpool(const JSPandaFile *jsPandaFile, int32_t index, int32_t constpoolIndex)
530{
531    // unshared constpool index is default INT32_MAX.
532    ASSERT(constpoolIndex != ConstantPool::CONSTPOOL_TYPE_FLAG);
533
534    SetUnsharedConstpool(constpoolIndex, JSTaggedValue::Hole());
535    auto iter = cachedSharedConstpools_.find(jsPandaFile);
536    if (iter == cachedSharedConstpools_.end()) {
537        return;
538    }
539    auto constpoolIter = iter->second.find(index);
540    if (constpoolIter == iter->second.end()) {
541        return;
542    }
543
544    iter->second.erase(constpoolIter);
545    if (iter->second.size() == 0) {
546        cachedSharedConstpools_.erase(iter);
547    }
548}
549
550std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> EcmaContext::FindConstpools(
551    const JSPandaFile *jsPandaFile)
552{
553    return Runtime::GetInstance()->FindConstpools(jsPandaFile);
554}
555
556// For new version instruction.
557JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
558{
559    panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
560    int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
561    return FindConstpool(jsPandaFile, index);
562}
563
564JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, int32_t index)
565{
566    JSTaggedValue contextCache = FindConstpoolFromContextCache(jsPandaFile, index);
567    if (!contextCache.IsHole()) {
568        return contextCache;
569    }
570    return Runtime::GetInstance()->FindConstpool(jsPandaFile, index);
571}
572
573JSTaggedValue EcmaContext::FindConstpoolFromContextCache(const JSPandaFile *jsPandaFile, int32_t index)
574{
575    auto iter = cachedSharedConstpools_.find(jsPandaFile);
576    if (iter != cachedSharedConstpools_.end()) {
577        auto constpoolIter = iter->second.find(index);
578        if (constpoolIter != iter->second.end()) {
579            return constpoolIter->second;
580        }
581    }
582    return JSTaggedValue::Hole();
583}
584
585bool EcmaContext::HasCachedConstpool(const JSPandaFile *jsPandaFile) const
586{
587    if (cachedSharedConstpools_.find(jsPandaFile) != cachedSharedConstpools_.end()) {
588        return true;
589    }
590
591    return Runtime::GetInstance()->HasCachedConstpool(jsPandaFile);
592}
593
594JSHandle<ConstantPool> EcmaContext::AddOrUpdateConstpool(const JSPandaFile *jsPandaFile,
595                                                         JSHandle<ConstantPool> constpool,
596                                                         int32_t index)
597{
598    constpool = Runtime::GetInstance()->AddOrUpdateConstpool(jsPandaFile, constpool, index);
599    AddContextConstpoolCache(jsPandaFile, constpool, index);
600    return constpool;
601}
602
603void EcmaContext::AddContextConstpoolCache(const JSPandaFile *jsPandaFile,
604                                           JSHandle<ConstantPool> constpool,
605                                           int32_t index)
606{
607    if (cachedSharedConstpools_.find(jsPandaFile) == cachedSharedConstpools_.end()) {
608        cachedSharedConstpools_[jsPandaFile] = CMap<int32_t, JSTaggedValue>();
609    }
610    auto &constpoolMap = cachedSharedConstpools_[jsPandaFile];
611    ASSERT(constpoolMap.find(index) == constpoolMap.end());
612    constpoolMap.insert({index, constpool.GetTaggedValue()});
613}
614
615void EcmaContext::SetUnsharedConstpool(JSHandle<ConstantPool> sharedConstpool, JSTaggedValue unsharedConstpool)
616{
617    int32_t constpoolIndex = sharedConstpool->GetUnsharedConstpoolIndex();
618    SetUnsharedConstpool(constpoolIndex, unsharedConstpool);
619}
620
621void EcmaContext::SetUnsharedConstpool(int32_t constpoolIndex, JSTaggedValue unsharedConstpool)
622{
623    CheckUnsharedConstpoolArrayLimit(constpoolIndex);
624    ASSERT(0 <= constpoolIndex && constpoolIndex < UNSHARED_CONSTANTPOOL_COUNT);
625    unsharedConstpools_[constpoolIndex] = unsharedConstpool;
626}
627
628void EcmaContext::UpdateConstpoolWhenDeserialAI(const std::string& fileName,
629                                                JSHandle<ConstantPool> aiCP, int32_t index)
630{
631    auto pf = JSPandaFileManager::GetInstance()->FindJSPandaFile(fileName.c_str());
632    if (pf == nullptr) {
633        return;
634    }
635    JSTaggedValue sharedConstpool = FindConstpool(pf.get(), index);
636    JSHandle<ConstantPool> sharedCPHandle = JSHandle<ConstantPool>(thread_, sharedConstpool);
637    if (sharedConstpool.IsHole()) {
638        return;
639    }
640    JSTaggedValue unsharedConstpool = FindOrCreateUnsharedConstpool(sharedCPHandle.GetTaggedValue());
641    JSHandle<ConstantPool> unsharedCP = JSHandle<ConstantPool>(thread_, unsharedConstpool);
642    JSHandle<ConstantPool> sharedCP = JSHandle<ConstantPool>(thread_, sharedCPHandle.GetTaggedValue());
643    ConstantPool::UpdateConstpoolWhenDeserialAI(vm_, aiCP, sharedCP, unsharedCP);
644}
645
646JSTaggedValue EcmaContext::FindCachedConstpoolAndLoadAiIfNeeded(const JSPandaFile *jsPandaFile, int32_t index)
647{
648    JSTaggedValue constpool = FindConstpoolFromContextCache(jsPandaFile, index);
649    if (!constpool.IsHole()) {
650        return constpool;
651    }
652    constpool = Runtime::GetInstance()->FindConstpool(jsPandaFile, index);
653    if (!constpool.IsHole()) {
654        AddContextConstpoolCache(jsPandaFile, JSHandle<ConstantPool>(thread_, constpool), index);
655    }
656    // Getting the cached constpool in runtime means the ai data has not been loaded in current thread.
657    // And we need to reload it
658    aotFileManager_->LoadAiFile(jsPandaFile);
659    return constpool;
660}
661
662JSHandle<ConstantPool> EcmaContext::FindOrCreateConstPool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
663{
664    panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
665    int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
666    JSTaggedValue constpool = FindCachedConstpoolAndLoadAiIfNeeded(jsPandaFile, index);
667    if (constpool.IsHole()) {
668        JSHandle<ConstantPool> newConstpool = ConstantPool::CreateUnSharedConstPool(vm_, jsPandaFile, id);
669        JSHandle<ConstantPool> newSConstpool;
670        if (jsPandaFile->IsLoadedAOT()) {
671            newSConstpool = ConstantPool::CreateSharedConstPoolForAOT(vm_, newConstpool, index);
672        } else {
673            newSConstpool = ConstantPool::CreateSharedConstPool(vm_, jsPandaFile, id, index);
674        }
675        newSConstpool = AddOrUpdateConstpool(jsPandaFile, newSConstpool, index);
676        SetUnsharedConstpool(newSConstpool, newConstpool.GetTaggedValue());
677        return newSConstpool;
678    } else if (jsPandaFile->IsLoadedAOT()) {
679        // For aot, after getting the cached shared constpool,
680        // worker thread need to create and bind the correspoding unshared constpool.
681        FindOrCreateUnsharedConstpool(constpool);
682    }
683    return JSHandle<ConstantPool>(thread_, constpool);
684}
685
686void EcmaContext::CreateAllConstpool(const JSPandaFile *jsPandaFile)
687{
688    auto headers = jsPandaFile->GetPandaFile()->GetIndexHeaders();
689    uint32_t index = 0;
690    for (const auto &header : headers) {
691        auto constpoolSize = header.method_idx_size;
692        JSHandle<ConstantPool> sconstpool = factory_->NewSConstantPool(constpoolSize);
693        sconstpool->SetJSPandaFile(jsPandaFile);
694        sconstpool->SetIndexHeader(&header);
695        sconstpool->SetSharedConstpoolId(JSTaggedValue(index));
696        sconstpool = AddOrUpdateConstpool(jsPandaFile, sconstpool, index);
697        index++;
698
699        JSHandle<ConstantPool> constpool = factory_->NewConstantPool(constpoolSize);
700        constpool->SetJSPandaFile(jsPandaFile);
701        constpool->SetIndexHeader(&header);
702        SetUnsharedConstpool(sconstpool, constpool.GetTaggedValue());
703    }
704}
705
706JSHandle<JSTaggedValue> EcmaContext::GetAndClearEcmaUncaughtException() const
707{
708    JSHandle<JSTaggedValue> exceptionHandle = GetEcmaUncaughtException();
709    thread_->ClearException();  // clear for ohos app
710    return exceptionHandle;
711}
712
713void EcmaContext::RelocateConstantString(const JSPandaFile *jsPandaFile)
714{
715    if (!jsPandaFile->IsFirstMergedAbc()) {
716        return;
717    }
718    vm_->GetEcmaStringTable()->RelocateConstantData(vm_, jsPandaFile);
719}
720
721JSHandle<JSTaggedValue> EcmaContext::GetEcmaUncaughtException() const
722{
723    if (!thread_->HasPendingException()) {
724        return JSHandle<JSTaggedValue>();
725    }
726    JSHandle<JSTaggedValue> exceptionHandle(thread_, thread_->GetException());
727    return exceptionHandle;
728}
729
730void EcmaContext::EnableUserUncaughtErrorHandler()
731{
732    isUncaughtExceptionRegistered_ = true;
733}
734
735void EcmaContext::HandleUncaughtException(JSTaggedValue exception)
736{
737    if (isUncaughtExceptionRegistered_) {
738        return;
739    }
740    [[maybe_unused]] EcmaHandleScope handleScope(thread_);
741    JSHandle<JSTaggedValue> exceptionHandle(thread_, exception);
742    // if caught exceptionHandle type is JSError
743    thread_->ClearException();
744    if (exceptionHandle->IsJSError()) {
745        PrintJSErrorInfo(thread_, exceptionHandle);
746        return;
747    }
748    JSHandle<EcmaString> result = JSTaggedValue::ToString(thread_, exceptionHandle);
749    CString string = ConvertToString(*result);
750    LOG_NO_TAG(ERROR) << string;
751}
752
753void EcmaContext::HandleUncaughtException()
754{
755    if (!thread_->HasPendingException()) {
756        return;
757    }
758    JSTaggedValue exception = thread_->GetException();
759    HandleUncaughtException(exception);
760}
761
762// static
763void EcmaContext::PrintJSErrorInfo(JSThread *thread, const JSHandle<JSTaggedValue> &exceptionInfo)
764{
765    JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
766    JSHandle<JSTaggedValue> nameValue = JSObject::GetProperty(thread, exceptionInfo, nameKey).GetValue();
767    RETURN_IF_ABRUPT_COMPLETION(thread);
768    JSHandle<EcmaString> name = JSTaggedValue::ToString(thread, nameValue);
769    // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
770    if (thread->HasPendingException()) {
771        thread->ClearException();
772        name = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
773    }
774    JSHandle<JSTaggedValue> msgKey = thread->GlobalConstants()->GetHandledMessageString();
775    JSHandle<JSTaggedValue> msgValue = JSObject::GetProperty(thread, exceptionInfo, msgKey).GetValue();
776    RETURN_IF_ABRUPT_COMPLETION(thread);
777    JSHandle<EcmaString> msg = JSTaggedValue::ToString(thread, msgValue);
778    // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
779    if (thread->HasPendingException()) {
780        thread->ClearException();
781        msg = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
782    }
783    JSHandle<JSTaggedValue> stackKey = thread->GlobalConstants()->GetHandledStackString();
784    JSHandle<JSTaggedValue> stackValue = JSObject::GetProperty(thread, exceptionInfo, stackKey).GetValue();
785    RETURN_IF_ABRUPT_COMPLETION(thread);
786    JSHandle<EcmaString> stack = JSTaggedValue::ToString(thread, stackValue);
787    // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
788    if (thread->HasPendingException()) {
789        thread->ClearException();
790        stack = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
791    }
792
793    CString nameBuffer = ConvertToString(*name);
794    CString msgBuffer = ConvertToString(*msg);
795    CString stackBuffer = ConvertToString(*stack);
796    LOG_NO_TAG(ERROR) << panda::ecmascript::previewerTag << nameBuffer << ": " << msgBuffer << "\n"
797                      << (panda::ecmascript::previewerTag.empty()
798                              ? stackBuffer
799                              : std::regex_replace(stackBuffer, std::regex(".+(\n|$)"),
800                                                   panda::ecmascript::previewerTag + "$0"));
801}
802
803bool EcmaContext::HasPendingJob()
804{
805    // This interface only determines whether PromiseJobQueue is empty, rather than ScriptJobQueue.
806    if (UNLIKELY(thread_->HasTerminated())) {
807        return false;
808    }
809    TaggedQueue* promiseQueue = TaggedQueue::Cast(GetMicroJobQueue()->GetPromiseJobQueue().GetTaggedObject());
810    return !promiseQueue->Empty();
811}
812
813bool EcmaContext::ExecutePromisePendingJob()
814{
815    if (isProcessingPendingJob_) {
816        LOG_ECMA(DEBUG) << "EcmaVM::ExecutePromisePendingJob can not reentrant";
817        return false;
818    }
819    if (!thread_->HasPendingException()) {
820        isProcessingPendingJob_ = true;
821        job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
822        if (thread_->HasPendingException()) {
823            JsStackInfo::BuildCrashInfo(thread_);
824        }
825        isProcessingPendingJob_ = false;
826        return true;
827    }
828    return false;
829}
830
831void EcmaContext::ClearBufferData()
832{
833    cachedSharedConstpools_.clear();
834    thread_->SetUnsharedConstpools(reinterpret_cast<uintptr_t>(nullptr));
835}
836
837void EcmaContext::SetGlobalEnv(GlobalEnv *global)
838{
839    // In jsthread iteration, SwitchCurrentContext is called to iterate each context.
840    // If the target context is not fully initialized, the variable "global" will be nullptr.
841    if (global != nullptr) {
842        globalEnv_ = JSTaggedValue(global);
843    }
844}
845
846void EcmaContext::SetMicroJobQueue(job::MicroJobQueue *queue)
847{
848    ASSERT(queue != nullptr);
849    microJobQueue_ = JSTaggedValue(queue);
850}
851
852JSHandle<GlobalEnv> EcmaContext::GetGlobalEnv() const
853{
854    return JSHandle<GlobalEnv>(reinterpret_cast<uintptr_t>(&globalEnv_));
855}
856
857JSHandle<job::MicroJobQueue> EcmaContext::GetMicroJobQueue() const
858{
859    return JSHandle<job::MicroJobQueue>(reinterpret_cast<uintptr_t>(&microJobQueue_));
860}
861
862void EcmaContext::MountContext(JSThread *thread)
863{
864    EcmaContext *context = EcmaContext::CreateAndInitialize(thread);
865    thread->SwitchCurrentContext(context);
866}
867
868void EcmaContext::UnmountContext(JSThread *thread)
869{
870    EcmaContext *context = thread->GetCurrentEcmaContext();
871    thread->PopContext();
872    Destroy(context);
873}
874
875EcmaContext *EcmaContext::CreateAndInitialize(JSThread *thread)
876{
877    EcmaContext *context = EcmaContext::Create(thread);
878    thread->PushContext(context);
879    context->Initialize();
880    return context;
881}
882
883void EcmaContext::CheckAndDestroy(JSThread *thread, EcmaContext *context)
884{
885    if (thread->EraseContext(context)) {
886        Destroy(context);
887        return;
888    }
889    LOG_ECMA(FATAL) << "CheckAndDestroy a nonexistent context.";
890}
891
892void EcmaContext::SetupRegExpResultCache()
893{
894    regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_);
895}
896
897void EcmaContext::SetupRegExpGlobalResult()
898{
899    regexpGlobal_ = builtins::RegExpGlobalResult::CreateGlobalResultTable(thread_);
900}
901
902void EcmaContext::SetupNumberToStringResultCache()
903{
904    numberToStringResultCache_ = builtins::NumberToStringResultCache::CreateCacheTable(thread_);
905}
906
907void EcmaContext::SetupStringSplitResultCache()
908{
909    stringSplitResultCache_ = builtins::StringSplitResultCache::CreateCacheTable(thread_);
910}
911
912void EcmaContext::SetupStringToListResultCache()
913{
914    stringToListResultCache_ = builtins::StringToListResultCache::CreateCacheTable(thread_);
915}
916
917void EcmaContext::Iterate(const RootVisitor &v, const RootRangeVisitor &rv)
918{
919    // visit global Constant
920    globalConst_.VisitRangeSlot(rv);
921
922    v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&globalEnv_)));
923    if (!regexpCache_.IsHole()) {
924        v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&regexpCache_)));
925    }
926    if (!regexpGlobal_.IsHole()) {
927        v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&regexpGlobal_)));
928    }
929    if (!numberToStringResultCache_.IsHole()) {
930        v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&numberToStringResultCache_)));
931    }
932    if (!stringSplitResultCache_.IsHole()) {
933        v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&stringSplitResultCache_)));
934    }
935    if (!stringToListResultCache_.IsHole()) {
936        v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&stringToListResultCache_)));
937    }
938    if (!microJobQueue_.IsHole()) {
939        v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&microJobQueue_)));
940    }
941    if (!pointerToIndexDictionary_.IsHole()) {
942        v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&pointerToIndexDictionary_)));
943    }
944
945    if (functionProtoTransitionTable_) {
946        functionProtoTransitionTable_->Iterate(v);
947    }
948    if (moduleManager_) {
949        moduleManager_->Iterate(v);
950    }
951    if (ptManager_) {
952        ptManager_->Iterate(v);
953    }
954    if (propertiesCache_ != nullptr) {
955        propertiesCache_->Clear();
956    }
957    if (regExpParserCache_ != nullptr) {
958        regExpParserCache_->Clear();
959    }
960    if (!vm_->GetJSOptions().EnableGlobalLeakCheck() && currentHandleStorageIndex_ != -1) {
961        // IterateHandle when disableGlobalLeakCheck.
962        int32_t nid = currentHandleStorageIndex_;
963        for (int32_t i = 0; i <= nid; ++i) {
964            auto node = handleStorageNodes_.at(i);
965            auto start = node->data();
966            auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
967            rv(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
968        }
969    }
970
971    if (sustainingJSHandleList_) {
972        sustainingJSHandleList_->Iterate(rv);
973    }
974
975    if (!joinStack_.empty()) {
976        rv(Root::ROOT_VM, ObjectSlot(ToUintPtr(&joinStack_.front())),
977            ObjectSlot(ToUintPtr(&joinStack_.back()) + JSTaggedValue::TaggedTypeSize()));
978    }
979
980    auto start = ObjectSlot(ToUintPtr(unsharedConstpools_.data()));
981    auto end = ObjectSlot(ToUintPtr(&unsharedConstpools_[UNSHARED_CONSTANTPOOL_COUNT - 1]) +
982        JSTaggedValue::TaggedTypeSize());
983    rv(Root::ROOT_VM, start, end);
984}
985
986size_t EcmaContext::IterateHandle(const RootRangeVisitor &rangeVisitor)
987{
988    // EnableGlobalLeakCheck.
989    size_t handleCount = 0;
990    if (currentHandleStorageIndex_ != -1) {
991        int32_t nid = currentHandleStorageIndex_;
992        for (int32_t i = 0; i <= nid; ++i) {
993            auto node = handleStorageNodes_.at(i);
994            auto start = node->data();
995            auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
996            rangeVisitor(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
997            handleCount += (ToUintPtr(end) - ToUintPtr(start)) / sizeof(JSTaggedType);
998        }
999    }
1000    return handleCount;
1001}
1002
1003uintptr_t *EcmaContext::ExpandHandleStorage()
1004{
1005    uintptr_t *result = nullptr;
1006    int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size()) - 1;
1007    if (currentHandleStorageIndex_ == lastIndex) {
1008        auto n = new std::array<JSTaggedType, NODE_BLOCK_SIZE>();
1009        handleStorageNodes_.push_back(n);
1010        currentHandleStorageIndex_++;
1011        result = reinterpret_cast<uintptr_t *>(&n->data()[0]);
1012        handleScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE];
1013    } else {
1014        currentHandleStorageIndex_++;
1015        auto lastNode = handleStorageNodes_[currentHandleStorageIndex_];
1016        result = reinterpret_cast<uintptr_t *>(&lastNode->data()[0]);
1017        handleScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE];
1018    }
1019
1020    return result;
1021}
1022
1023void EcmaContext::ShrinkHandleStorage(int prevIndex)
1024{
1025    currentHandleStorageIndex_ = prevIndex;
1026    int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size()) - 1;
1027#if ECMASCRIPT_ENABLE_ZAP_MEM
1028    uintptr_t size = ToUintPtr(handleScopeStorageEnd_) - ToUintPtr(handleScopeStorageNext_);
1029    if (currentHandleStorageIndex_ != -1) {
1030        if (memset_s(handleScopeStorageNext_, size, 0, size) != EOK) {
1031            LOG_FULL(FATAL) << "memset_s failed";
1032            UNREACHABLE();
1033        }
1034    }
1035    for (int32_t i = currentHandleStorageIndex_ + 1; i < lastIndex; i++) {
1036        if (memset_s(handleStorageNodes_[i],
1037                     NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0,
1038                     NODE_BLOCK_SIZE * sizeof(JSTaggedType)) !=
1039                     EOK) {
1040            LOG_FULL(FATAL) << "memset_s failed";
1041            UNREACHABLE();
1042        }
1043    }
1044#endif
1045
1046    if (lastIndex > MIN_HANDLE_STORAGE_SIZE && currentHandleStorageIndex_ < MIN_HANDLE_STORAGE_SIZE) {
1047        for (int i = MIN_HANDLE_STORAGE_SIZE; i < lastIndex; i++) {
1048            auto node = handleStorageNodes_.back();
1049            delete node;
1050            handleStorageNodes_.pop_back();
1051        }
1052    }
1053}
1054
1055uintptr_t *EcmaContext::ExpandPrimitiveStorage()
1056{
1057    uintptr_t *result = nullptr;
1058    int32_t lastIndex = static_cast<int32_t>(primitiveStorageNodes_.size()) - 1;
1059    if (currentPrimitiveStorageIndex_ == lastIndex) {
1060        auto n = new std::array<JSTaggedType, NODE_BLOCK_SIZE>();
1061        primitiveStorageNodes_.push_back(n);
1062        currentPrimitiveStorageIndex_++;
1063        result = reinterpret_cast<uintptr_t *>(&n->data()[0]);
1064        primitiveScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE];
1065    } else {
1066        currentPrimitiveStorageIndex_++;
1067        auto lastNode = primitiveStorageNodes_[currentPrimitiveStorageIndex_];
1068        result = reinterpret_cast<uintptr_t *>(&lastNode->data()[0]);
1069        primitiveScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE];
1070    }
1071
1072    return result;
1073}
1074
1075void EcmaContext::ShrinkPrimitiveStorage(int prevIndex)
1076{
1077    currentPrimitiveStorageIndex_ = prevIndex;
1078    int32_t lastIndex = static_cast<int32_t>(primitiveStorageNodes_.size()) - 1;
1079#if ECMASCRIPT_ENABLE_ZAP_MEM
1080    uintptr_t size = ToUintPtr(primitiveScopeStorageEnd_) - ToUintPtr(primitiveScopeStorageNext_);
1081    if (currentPrimitiveStorageIndex_ != -1) {
1082        if (memset_s(primitiveScopeStorageNext_, size, 0, size) != EOK) {
1083            LOG_FULL(FATAL) << "memset_s failed";
1084            UNREACHABLE();
1085        }
1086    }
1087    for (int32_t i = currentPrimitiveStorageIndex_ + 1; i < lastIndex; i++) {
1088        if (memset_s(primitiveStorageNodes_[i],
1089                     NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0,
1090                     NODE_BLOCK_SIZE * sizeof(JSTaggedType)) !=
1091                     EOK) {
1092            LOG_FULL(FATAL) << "memset_s failed";
1093            UNREACHABLE();
1094        }
1095    }
1096#endif
1097
1098    if (lastIndex > MIN_PRIMITIVE_STORAGE_SIZE && currentPrimitiveStorageIndex_ < MIN_PRIMITIVE_STORAGE_SIZE) {
1099        for (int i = MIN_PRIMITIVE_STORAGE_SIZE; i < lastIndex; i++) {
1100            auto node = primitiveStorageNodes_.back();
1101            delete node;
1102            primitiveStorageNodes_.pop_back();
1103        }
1104    }
1105}
1106
1107void EcmaContext::LoadStubFile()
1108{
1109    std::string stubFile = "";
1110    if (vm_->GetJSOptions().WasStubFileSet()) {
1111        stubFile = vm_->GetJSOptions().GetStubFile();
1112    }
1113    aotFileManager_->LoadStubFile(stubFile);
1114}
1115
1116bool EcmaContext::LoadAOTFilesInternal(const std::string& aotFileName)
1117{
1118#ifdef AOT_ESCAPE_ENABLE
1119    std::string bundleName = pgo::PGOProfilerManager::GetInstance()->GetBundleName();
1120    if (AotCrashInfo::GetInstance().IsAotEscapedOrNotInEnableList(vm_, bundleName)) {
1121        return false;
1122    }
1123#endif
1124    std::string anFile = aotFileName + AOTFileManager::FILE_EXTENSION_AN;
1125    if (!aotFileManager_->LoadAnFile(anFile)) {
1126        LOG_ECMA(WARN) << "Load " << anFile << " failed. Destroy aot data and rollback to interpreter";
1127        ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
1128        return false;
1129    }
1130
1131    std::string aiFile = aotFileName + AOTFileManager::FILE_EXTENSION_AI;
1132    if (!aotFileManager_->LoadAiFile(aiFile)) {
1133        LOG_ECMA(WARN) << "Load " << aiFile << " failed. Destroy aot data and rollback to interpreter";
1134        ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
1135        return false;
1136    }
1137    return true;
1138}
1139
1140bool EcmaContext::LoadAOTFiles(const std::string& aotFileName)
1141{
1142    return LoadAOTFilesInternal(aotFileName);
1143}
1144
1145#if defined(CROSS_PLATFORM) && defined(ANDROID_PLATFORM)
1146bool EcmaContext::LoadAOTFiles(const std::string& aotFileName,
1147                               std::function<bool(std::string fileName, uint8_t **buff, size_t *buffSize)> cb)
1148{
1149    aotFileManager_->SetJsAotReader(cb);
1150    return LoadAOTFilesInternal(aotFileName);
1151}
1152#endif
1153
1154void EcmaContext::PrintOptStat()
1155{
1156    if (optCodeProfiler_ != nullptr) {
1157        optCodeProfiler_->PrintAndReset();
1158    }
1159}
1160
1161void EcmaContext::DumpAOTInfo() const
1162{
1163    aotFileManager_->DumpAOTInfo();
1164}
1165
1166bool EcmaContext::JoinStackPushFastPath(JSHandle<JSTaggedValue> receiver)
1167{
1168    if (JSTaggedValue::SameValue(joinStack_[0], JSTaggedValue::Hole())) {
1169        joinStack_[0] = receiver.GetTaggedValue();
1170        return true;
1171    }
1172    return JoinStackPush(receiver);
1173}
1174
1175bool EcmaContext::JoinStackPush(JSHandle<JSTaggedValue> receiver)
1176{
1177    uint32_t capacity = joinStack_.size();
1178    JSTaggedValue receiverValue = receiver.GetTaggedValue();
1179    for (size_t i = 0; i < capacity; ++i) {
1180        if (JSTaggedValue::SameValue(joinStack_[i], JSTaggedValue::Hole())) {
1181            joinStack_[i] = receiverValue;
1182            return true;
1183        }
1184        if (JSTaggedValue::SameValue(joinStack_[i], receiverValue)) {
1185            return false;
1186        }
1187    }
1188    joinStack_.emplace_back(receiverValue);
1189    return true;
1190}
1191
1192void EcmaContext::JoinStackPopFastPath(JSHandle<JSTaggedValue> receiver)
1193{
1194    uint32_t length = joinStack_.size();
1195    if (JSTaggedValue::SameValue(joinStack_[0], receiver.GetTaggedValue()) && length == MIN_JOIN_STACK_SIZE) {
1196        joinStack_[0] = JSTaggedValue::Hole();
1197    } else {
1198        JoinStackPop(receiver);
1199    }
1200}
1201
1202void EcmaContext::JoinStackPop(JSHandle<JSTaggedValue> receiver)
1203{
1204    uint32_t length = joinStack_.size();
1205    for (size_t i = 0; i < length; ++i) {
1206        if (JSTaggedValue::SameValue(joinStack_[i], receiver.GetTaggedValue())) {
1207            if (i == 0 && length > MIN_JOIN_STACK_SIZE) {
1208                joinStack_ = {JSTaggedValue::Hole(), JSTaggedValue::Hole()};
1209                break;
1210            } else {
1211                joinStack_[i] = JSTaggedValue::Hole();
1212                break;
1213            }
1214        }
1215    }
1216}
1217
1218std::tuple<uint64_t, uint8_t *, int, kungfu::CalleeRegAndOffsetVec> EcmaContext::CalCallSiteInfo(
1219    uintptr_t retAddr, bool isDeopt) const
1220{
1221    auto loader = aotFileManager_;
1222    return loader->CalCallSiteInfo(retAddr, isDeopt);
1223}
1224
1225void EcmaContext::AddSustainingJSHandle(SustainingJSHandle *sustainingHandle)
1226{
1227    if (sustainingJSHandleList_) {
1228        sustainingJSHandleList_->AddSustainingJSHandle(sustainingHandle);
1229    }
1230}
1231
1232void EcmaContext::RemoveSustainingJSHandle(SustainingJSHandle *sustainingHandle)
1233{
1234    if (sustainingJSHandleList_) {
1235        sustainingJSHandleList_->RemoveSustainingJSHandle(sustainingHandle);
1236    }
1237}
1238
1239void EcmaContext::ClearKeptObjects()
1240{
1241    if (LIKELY(GetGlobalEnv()->GetTaggedWeakRefKeepObjects().IsUndefined())) {
1242        return;
1243    }
1244    GetGlobalEnv()->SetWeakRefKeepObjects(thread_, JSTaggedValue::Undefined());
1245    hasKeptObjects_ = false;
1246}
1247
1248void EcmaContext::AddToKeptObjects(JSHandle<JSTaggedValue> value)
1249{
1250    if (value->IsInSharedHeap()) {
1251        return;
1252    }
1253
1254    JSHandle<GlobalEnv> globalEnv = GetGlobalEnv();
1255    JSHandle<LinkedHashSet> linkedSet;
1256    if (globalEnv->GetWeakRefKeepObjects()->IsUndefined()) {
1257        linkedSet = LinkedHashSet::Create(thread_);
1258    } else {
1259        linkedSet = JSHandle<LinkedHashSet>(thread_,
1260            LinkedHashSet::Cast(globalEnv->GetWeakRefKeepObjects()->GetTaggedObject()));
1261    }
1262    linkedSet = LinkedHashSet::Add(thread_, linkedSet, value);
1263    globalEnv->SetWeakRefKeepObjects(thread_, linkedSet);
1264    hasKeptObjects_ = true;
1265}
1266}  // namespace panda::ecmascript
1267