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