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 
36 namespace panda::ecmascript {
37 using PathHelper = base::PathHelper;
38 
EcmaContext(JSThread *thread)39 EcmaContext::EcmaContext(JSThread *thread)
40     : thread_(thread),
41       vm_(thread->GetEcmaVM()),
42       factory_(vm_->GetFactory()),
43       aotFileManager_(vm_->GetAOTFileManager())
44 {
45 }
46 
47 /* static */
Create(JSThread *thread)48 EcmaContext *EcmaContext::Create(JSThread *thread)
49 {
50     LOG_ECMA(INFO) << "EcmaContext::Create";
51     auto context = new EcmaContext(thread);
52     return context;
53 }
54 
55 // static
Destroy(EcmaContext *context)56 bool EcmaContext::Destroy(EcmaContext *context)
57 {
58     if (context != nullptr) {
59         delete context;
60         context = nullptr;
61         return true;
62     }
63     return false;
64 }
65 
Initialize()66 bool 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 
InitializeEcmaScriptRunStat()117 void 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 
ClearIcuCache(JSThread *thread)148 void 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 
SetRuntimeStatEnable(bool flag)160 void 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 
~EcmaContext()180 EcmaContext::~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 
InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc, JSHandle<JSTaggedValue> &thisArg, const JSPandaFile *jsPandaFile, std::string_view entryPoint, CJSInfo* cjsInfo)277 JSTaggedValue 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 
ExecuteAot(size_t actualNumArgs, JSTaggedType *args, const JSTaggedType *prevFp, bool needPushArgv)285 JSTaggedValue 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 
CommonInvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile, std::string_view entryPoint, JSHandle<JSFunction> &func, bool executeFromJob)300 Expected<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 
InvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile, std::string_view entryPoint, bool executeFromJob)376 Expected<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 
InvokeEcmaEntrypointForHotReload( const JSPandaFile *jsPandaFile, std::string_view entryPoint, bool executeFromJob)412 Expected<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 
CJSExecution(JSHandle<JSFunction> &func, JSHandle<JSTaggedValue> &thisArg, const JSPandaFile *jsPandaFile, std::string_view entryPoint)437 void 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 
LoadProtoTransitionTable(JSTaggedValue constpool)488 void EcmaContext::LoadProtoTransitionTable(JSTaggedValue constpool)
489 {
490     JSTaggedValue protoTransitionTable = ConstantPool::Cast(constpool.GetTaggedObject())->GetProtoTransTableInfo();
491     functionProtoTransitionTable_->UpdateProtoTransitionTable(
492         thread_, JSHandle<PointerToIndexDictionary>(thread_, protoTransitionTable));
493 }
494 
ResetProtoTransitionTableOnConstpool(JSTaggedValue constpool)495 void EcmaContext::ResetProtoTransitionTableOnConstpool(JSTaggedValue constpool)
496 {
497     ConstantPool::Cast(constpool.GetTaggedObject())->SetProtoTransTableInfo(thread_, JSTaggedValue::Undefined());
498 }
499 
500 // just find unshared constpool, not create
FindUnsharedConstpool(JSTaggedValue sharedConstpool)501 JSTaggedValue 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 
FindOrCreateUnsharedConstpool(JSTaggedValue sharedConstpool)511 JSTaggedValue 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 
EraseUnusedConstpool(const JSPandaFile *jsPandaFile, int32_t index, int32_t constpoolIndex)529 void 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 
FindConstpools( const JSPandaFile *jsPandaFile)550 std::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.
FindConstpool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)557 JSTaggedValue 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 
FindConstpool(const JSPandaFile *jsPandaFile, int32_t index)564 JSTaggedValue 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 
FindConstpoolFromContextCache(const JSPandaFile *jsPandaFile, int32_t index)573 JSTaggedValue 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 
HasCachedConstpool(const JSPandaFile *jsPandaFile) const585 bool 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 
AddOrUpdateConstpool(const JSPandaFile *jsPandaFile, JSHandle<ConstantPool> constpool, int32_t index)594 JSHandle<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 
AddContextConstpoolCache(const JSPandaFile *jsPandaFile, JSHandle<ConstantPool> constpool, int32_t index)603 void 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 
SetUnsharedConstpool(JSHandle<ConstantPool> sharedConstpool, JSTaggedValue unsharedConstpool)615 void EcmaContext::SetUnsharedConstpool(JSHandle<ConstantPool> sharedConstpool, JSTaggedValue unsharedConstpool)
616 {
617     int32_t constpoolIndex = sharedConstpool->GetUnsharedConstpoolIndex();
618     SetUnsharedConstpool(constpoolIndex, unsharedConstpool);
619 }
620 
SetUnsharedConstpool(int32_t constpoolIndex, JSTaggedValue unsharedConstpool)621 void 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 
UpdateConstpoolWhenDeserialAI(const std::string& fileName, JSHandle<ConstantPool> aiCP, int32_t index)628 void 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 
FindCachedConstpoolAndLoadAiIfNeeded(const JSPandaFile *jsPandaFile, int32_t index)646 JSTaggedValue 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 
FindOrCreateConstPool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)662 JSHandle<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 
CreateAllConstpool(const JSPandaFile *jsPandaFile)686 void 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 
GetAndClearEcmaUncaughtException() const706 JSHandle<JSTaggedValue> EcmaContext::GetAndClearEcmaUncaughtException() const
707 {
708     JSHandle<JSTaggedValue> exceptionHandle = GetEcmaUncaughtException();
709     thread_->ClearException();  // clear for ohos app
710     return exceptionHandle;
711 }
712 
RelocateConstantString(const JSPandaFile *jsPandaFile)713 void EcmaContext::RelocateConstantString(const JSPandaFile *jsPandaFile)
714 {
715     if (!jsPandaFile->IsFirstMergedAbc()) {
716         return;
717     }
718     vm_->GetEcmaStringTable()->RelocateConstantData(vm_, jsPandaFile);
719 }
720 
GetEcmaUncaughtException() const721 JSHandle<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 
EnableUserUncaughtErrorHandler()730 void EcmaContext::EnableUserUncaughtErrorHandler()
731 {
732     isUncaughtExceptionRegistered_ = true;
733 }
734 
HandleUncaughtException(JSTaggedValue exception)735 void 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 
HandleUncaughtException()753 void EcmaContext::HandleUncaughtException()
754 {
755     if (!thread_->HasPendingException()) {
756         return;
757     }
758     JSTaggedValue exception = thread_->GetException();
759     HandleUncaughtException(exception);
760 }
761 
762 // static
PrintJSErrorInfo(JSThread *thread, const JSHandle<JSTaggedValue> &exceptionInfo)763 void 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 
HasPendingJob()803 bool 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 
ExecutePromisePendingJob()813 bool 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 
ClearBufferData()831 void EcmaContext::ClearBufferData()
832 {
833     cachedSharedConstpools_.clear();
834     thread_->SetUnsharedConstpools(reinterpret_cast<uintptr_t>(nullptr));
835 }
836 
SetGlobalEnv(GlobalEnv *global)837 void 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 
SetMicroJobQueue(job::MicroJobQueue *queue)846 void EcmaContext::SetMicroJobQueue(job::MicroJobQueue *queue)
847 {
848     ASSERT(queue != nullptr);
849     microJobQueue_ = JSTaggedValue(queue);
850 }
851 
GetGlobalEnv() const852 JSHandle<GlobalEnv> EcmaContext::GetGlobalEnv() const
853 {
854     return JSHandle<GlobalEnv>(reinterpret_cast<uintptr_t>(&globalEnv_));
855 }
856 
GetMicroJobQueue() const857 JSHandle<job::MicroJobQueue> EcmaContext::GetMicroJobQueue() const
858 {
859     return JSHandle<job::MicroJobQueue>(reinterpret_cast<uintptr_t>(&microJobQueue_));
860 }
861 
MountContext(JSThread *thread)862 void EcmaContext::MountContext(JSThread *thread)
863 {
864     EcmaContext *context = EcmaContext::CreateAndInitialize(thread);
865     thread->SwitchCurrentContext(context);
866 }
867 
UnmountContext(JSThread *thread)868 void EcmaContext::UnmountContext(JSThread *thread)
869 {
870     EcmaContext *context = thread->GetCurrentEcmaContext();
871     thread->PopContext();
872     Destroy(context);
873 }
874 
CreateAndInitialize(JSThread *thread)875 EcmaContext *EcmaContext::CreateAndInitialize(JSThread *thread)
876 {
877     EcmaContext *context = EcmaContext::Create(thread);
878     thread->PushContext(context);
879     context->Initialize();
880     return context;
881 }
882 
CheckAndDestroy(JSThread *thread, EcmaContext *context)883 void 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 
SetupRegExpResultCache()892 void EcmaContext::SetupRegExpResultCache()
893 {
894     regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_);
895 }
896 
SetupRegExpGlobalResult()897 void EcmaContext::SetupRegExpGlobalResult()
898 {
899     regexpGlobal_ = builtins::RegExpGlobalResult::CreateGlobalResultTable(thread_);
900 }
901 
SetupNumberToStringResultCache()902 void EcmaContext::SetupNumberToStringResultCache()
903 {
904     numberToStringResultCache_ = builtins::NumberToStringResultCache::CreateCacheTable(thread_);
905 }
906 
SetupStringSplitResultCache()907 void EcmaContext::SetupStringSplitResultCache()
908 {
909     stringSplitResultCache_ = builtins::StringSplitResultCache::CreateCacheTable(thread_);
910 }
911 
SetupStringToListResultCache()912 void EcmaContext::SetupStringToListResultCache()
913 {
914     stringToListResultCache_ = builtins::StringToListResultCache::CreateCacheTable(thread_);
915 }
916 
Iterate(const RootVisitor &v, const RootRangeVisitor &rv)917 void 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 
IterateHandle(const RootRangeVisitor &rangeVisitor)986 size_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 
ExpandHandleStorage()1003 uintptr_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 
ShrinkHandleStorage(int prevIndex)1023 void 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 
ExpandPrimitiveStorage()1055 uintptr_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 
ShrinkPrimitiveStorage(int prevIndex)1075 void 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 
LoadStubFile()1107 void EcmaContext::LoadStubFile()
1108 {
1109     std::string stubFile = "";
1110     if (vm_->GetJSOptions().WasStubFileSet()) {
1111         stubFile = vm_->GetJSOptions().GetStubFile();
1112     }
1113     aotFileManager_->LoadStubFile(stubFile);
1114 }
1115 
LoadAOTFilesInternal(const std::string& aotFileName)1116 bool 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 
LoadAOTFiles(const std::string& aotFileName)1140 bool EcmaContext::LoadAOTFiles(const std::string& aotFileName)
1141 {
1142     return LoadAOTFilesInternal(aotFileName);
1143 }
1144 
1145 #if defined(CROSS_PLATFORM) && defined(ANDROID_PLATFORM)
LoadAOTFiles(const std::string& aotFileName, std::function<bool(std::string fileName, uint8_t **buff, size_t *buffSize)> cb)1146 bool 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 
PrintOptStat()1154 void EcmaContext::PrintOptStat()
1155 {
1156     if (optCodeProfiler_ != nullptr) {
1157         optCodeProfiler_->PrintAndReset();
1158     }
1159 }
1160 
DumpAOTInfo() const1161 void EcmaContext::DumpAOTInfo() const
1162 {
1163     aotFileManager_->DumpAOTInfo();
1164 }
1165 
JoinStackPushFastPath(JSHandle<JSTaggedValue> receiver)1166 bool 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 
JoinStackPush(JSHandle<JSTaggedValue> receiver)1175 bool 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 
JoinStackPopFastPath(JSHandle<JSTaggedValue> receiver)1192 void 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 
JoinStackPop(JSHandle<JSTaggedValue> receiver)1202 void 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 
CalCallSiteInfo( uintptr_t retAddr, bool isDeopt) const1218 std::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 
AddSustainingJSHandle(SustainingJSHandle *sustainingHandle)1225 void EcmaContext::AddSustainingJSHandle(SustainingJSHandle *sustainingHandle)
1226 {
1227     if (sustainingJSHandleList_) {
1228         sustainingJSHandleList_->AddSustainingJSHandle(sustainingHandle);
1229     }
1230 }
1231 
RemoveSustainingJSHandle(SustainingJSHandle *sustainingHandle)1232 void EcmaContext::RemoveSustainingJSHandle(SustainingJSHandle *sustainingHandle)
1233 {
1234     if (sustainingJSHandleList_) {
1235         sustainingJSHandleList_->RemoveSustainingJSHandle(sustainingHandle);
1236     }
1237 }
1238 
ClearKeptObjects()1239 void EcmaContext::ClearKeptObjects()
1240 {
1241     if (LIKELY(GetGlobalEnv()->GetTaggedWeakRefKeepObjects().IsUndefined())) {
1242         return;
1243     }
1244     GetGlobalEnv()->SetWeakRefKeepObjects(thread_, JSTaggedValue::Undefined());
1245     hasKeptObjects_ = false;
1246 }
1247 
AddToKeptObjects(JSHandle<JSTaggedValue> value)1248 void 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