• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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/base/path_helper.h"
19 #include "ecmascript/builtins/builtins.h"
20 #include "ecmascript/builtins/builtins_global.h"
21 #include "ecmascript/builtins/builtins_regexp.h"
22 #include "ecmascript/compiler/aot_file/an_file_data_manager.h"
23 #include "ecmascript/compiler/common_stubs.h"
24 #include "ecmascript/ecma_string_table.h"
25 #include "ecmascript/ecma_vm.h"
26 #include "ecmascript/global_env.h"
27 #include "ecmascript/global_env_constants-inl.h"
28 #include "ecmascript/interpreter/interpreter-inl.h"
29 #include "ecmascript/jobs/micro_job_queue.h"
30 #include "ecmascript/jspandafile/js_pandafile.h"
31 #include "ecmascript/jspandafile/js_pandafile_manager.h"
32 #include "ecmascript/jspandafile/program_object.h"
33 #include "ecmascript/js_function.h"
34 #include "ecmascript/js_thread.h"
35 #include "ecmascript/module/module_path_helper.h"
36 #include "ecmascript/object_factory.h"
37 #include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
38 #include "ecmascript/require/js_cjs_module_cache.h"
39 #include "ecmascript/require/js_require_manager.h"
40 #include "ecmascript/snapshot/mem/snapshot.h"
41 
42 namespace panda::ecmascript {
43 using PathHelper = base::PathHelper;
44 
EcmaContext(JSThread * thread)45 EcmaContext::EcmaContext(JSThread *thread)
46     : thread_(thread),
47       vm_(thread->GetEcmaVM()),
48       factory_(vm_->GetFactory())
49 {
50 }
51 
52 /* static */
Create(JSThread * thread)53 EcmaContext *EcmaContext::Create(JSThread *thread)
54 {
55     LOG_ECMA(INFO) << "EcmaContext::Create";
56     auto context = new EcmaContext(thread);
57     if (UNLIKELY(context == nullptr)) {
58         LOG_ECMA(ERROR) << "Failed to create ecma context";
59         return nullptr;
60     }
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     aotFileManager_ = new AOTFileManager(vm_);
81     propertiesCache_ = new PropertiesCache();
82     regExpParserCache_ = new RegExpParserCache();
83 
84     JSHandle<JSHClass> hClassHandle = factory_->InitClassClass();
85 
86     JSHandle<JSHClass> globalEnvClass = factory_->NewEcmaHClass(
87         *hClassHandle,
88         GlobalEnv::SIZE,
89         JSType::GLOBAL_ENV);
90     thread_->SetGlobalConst(&globalConst_);
91     globalConst_.Init(thread_, *hClassHandle);
92     auto arrayHClassIndexMaps = Elements::InitializeHClassMap();
93     thread_->SetArrayHClassIndexMap(arrayHClassIndexMaps);
94 
95     JSHandle<GlobalEnv> globalEnv = factory_->NewGlobalEnv(*globalEnvClass);
96     globalEnv->Init(thread_);
97     globalEnv_ = globalEnv.GetTaggedValue();
98     Builtins builtins;
99     bool builtinsLazyEnabled = vm_->GetJSOptions().IsWorker() && vm_->GetJSOptions().GetEnableBuiltinsLazy();
100     builtins.Initialize(globalEnv, thread_, builtinsLazyEnabled);
101 
102     SetupRegExpResultCache();
103     microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue();
104     moduleManager_ = new ModuleManager(vm_);
105     tsManager_ = new TSManager(vm_);
106     optCodeProfiler_ = new OptCodeProfiler();
107     initialized_ = true;
108     return true;
109 }
110 
InitializeEcmaScriptRunStat()111 void EcmaContext::InitializeEcmaScriptRunStat()
112 {
113     // NOLINTNEXTLINE(modernize-avoid-c-arrays)
114     static const char *runtimeCallerNames[] = {
115 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
116 #define INTERPRETER_CALLER_NAME(name) "Interpreter::" #name,
117     INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_NAME)  // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
118 #undef INTERPRETER_CALLER_NAME
119 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
120 #define BUILTINS_API_NAME(class, name) "BuiltinsApi::" #class "_" #name,
121     BUILTINS_API_LIST(BUILTINS_API_NAME)
122 #undef BUILTINS_API_NAME
123 #define ABSTRACT_OPERATION_NAME(class, name) "AbstractOperation::" #class "_" #name,
124     ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_NAME)
125 #undef ABSTRACT_OPERATION_NAME
126 #define MEM_ALLOCATE_AND_GC_NAME(name) "Memory::" #name,
127     MEM_ALLOCATE_AND_GC_LIST(MEM_ALLOCATE_AND_GC_NAME)
128 #undef MEM_ALLOCATE_AND_GC_NAME
129 #define DEF_RUNTIME_ID(name) "Runtime::" #name,
130     RUNTIME_STUB_WITH_GC_LIST(DEF_RUNTIME_ID)
131 #undef DEF_RUNTIME_ID
132     };
133     static_assert(sizeof(runtimeCallerNames) == sizeof(const char *) * ecmascript::RUNTIME_CALLER_NUMBER,
134                   "Invalid runtime caller number");
135     runtimeStat_ = vm_->GetChunk()->New<EcmaRuntimeStat>(runtimeCallerNames, ecmascript::RUNTIME_CALLER_NUMBER);
136     if (UNLIKELY(runtimeStat_ == nullptr)) {
137         LOG_FULL(FATAL) << "alloc runtimeStat_ failed";
138         UNREACHABLE();
139     }
140 }
141 
SetRuntimeStatEnable(bool flag)142 void EcmaContext::SetRuntimeStatEnable(bool flag)
143 {
144     static uint64_t start = 0;
145     if (flag) {
146         start = PandaRuntimeTimer::Now();
147         if (runtimeStat_ == nullptr) {
148             InitializeEcmaScriptRunStat();
149         }
150     } else {
151         LOG_ECMA(INFO) << "Runtime State duration:" << PandaRuntimeTimer::Now() - start << "(ns)";
152         if (runtimeStat_ != nullptr && runtimeStat_->IsRuntimeStatEnabled()) {
153             runtimeStat_->Print();
154             runtimeStat_->ResetAllCount();
155         }
156     }
157     if (runtimeStat_ != nullptr) {
158         runtimeStat_->SetRuntimeStatEnabled(flag);
159     }
160 }
161 
~EcmaContext()162 EcmaContext::~EcmaContext()
163 {
164     if (runtimeStat_ != nullptr && runtimeStat_->IsRuntimeStatEnabled()) {
165         runtimeStat_->Print();
166     }
167     for (auto n : handleStorageNodes_) {
168         delete n;
169     }
170     handleStorageNodes_.clear();
171     currentHandleStorageIndex_ = -1;
172     handleScopeCount_ = 0;
173     handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr;
174     ClearBufferData();
175     // clear c_address: c++ pointer delete
176     if (!vm_->IsBundlePack()) {
177         std::shared_ptr<JSPandaFile> jsPandaFile =
178             JSPandaFileManager::GetInstance()->FindJSPandaFile(vm_->GetAssetPath());
179         if (jsPandaFile != nullptr) {
180             jsPandaFile->DeleteParsedConstpoolVM(vm_);
181         }
182     }
183     // clear icu cache
184     ClearIcuCache();
185 
186     if (runtimeStat_ != nullptr) {
187         vm_->GetChunk()->Delete(runtimeStat_);
188         runtimeStat_ = nullptr;
189     }
190     if (optCodeProfiler_ != nullptr) {
191         delete optCodeProfiler_;
192         optCodeProfiler_ = nullptr;
193     }
194     if (moduleManager_ != nullptr) {
195         delete moduleManager_;
196         moduleManager_ = nullptr;
197     }
198     if (tsManager_ != nullptr) {
199         delete tsManager_;
200         tsManager_ = nullptr;
201     }
202     if (regExpParserCache_ != nullptr) {
203         delete regExpParserCache_;
204         regExpParserCache_ = nullptr;
205     }
206     if (aotFileManager_ != nullptr) {
207         delete aotFileManager_;
208         aotFileManager_ = nullptr;
209     }
210     if (propertiesCache_ != nullptr) {
211         delete propertiesCache_;
212         propertiesCache_ = nullptr;
213     }
214     // clear join stack
215     joinStack_.clear();
216 }
217 
InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc,JSHandle<JSTaggedValue> & thisArg,const JSPandaFile * jsPandaFile,std::string_view entryPoint,CJSInfo * cjsInfo)218 JSTaggedValue EcmaContext::InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc, JSHandle<JSTaggedValue> &thisArg,
219                                                    const JSPandaFile *jsPandaFile, std::string_view entryPoint,
220                                                    CJSInfo* cjsInfo)
221 {
222     aotFileManager_->SetAOTMainFuncEntry(mainFunc, jsPandaFile, entryPoint);
223     return JSFunction::InvokeOptimizedEntrypoint(thread_, mainFunc, thisArg, entryPoint, cjsInfo);
224 }
225 
ExecuteAot(size_t actualNumArgs,JSTaggedType * args,const JSTaggedType * prevFp,bool needPushUndefined)226 JSTaggedValue EcmaContext::ExecuteAot(size_t actualNumArgs, JSTaggedType *args,
227                                       const JSTaggedType *prevFp, bool needPushUndefined)
228 {
229     INTERPRETER_TRACE(thread_, ExecuteAot);
230     auto entry = thread_->GetRTInterface(kungfu::RuntimeStubCSigns::ID_JSFunctionEntry);
231     // do not modify this log to INFO, this will call many times
232     LOG_ECMA(DEBUG) << "start to execute aot entry: " << (void*)entry;
233     auto res = reinterpret_cast<JSFunctionEntryType>(entry)(thread_->GetGlueAddr(),
234                                                             actualNumArgs,
235                                                             args,
236                                                             reinterpret_cast<uintptr_t>(prevFp),
237                                                             needPushUndefined);
238     return res;
239 }
240 
InvokeEcmaEntrypoint(const JSPandaFile * jsPandaFile,std::string_view entryPoint,bool excuteFromJob)241 Expected<JSTaggedValue, bool> EcmaContext::InvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile,
242                                                                 std::string_view entryPoint, bool excuteFromJob)
243 {
244     [[maybe_unused]] EcmaHandleScope scope(thread_);
245     JSHandle<Program> program = JSPandaFileManager::GetInstance()->GenerateProgram(vm_, jsPandaFile, entryPoint);
246     if (program.IsEmpty()) {
247         LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed";
248         return Unexpected(false);
249     }
250     // for debugger
251     vm_->GetJsDebuggerManager()->GetNotificationManager()->LoadModuleEvent(
252         jsPandaFile->GetJSPandaFileDesc(), entryPoint);
253 
254     JSHandle<JSFunction> func(thread_, program->GetMainFunction());
255     JSHandle<JSTaggedValue> global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject();
256     JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
257     CString entry = entryPoint.data();
258     JSRecordInfo recordInfo;
259     bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo);
260     if (!hasRecord) {
261         CString msg = "cannot find record '" + entry + "', please check the request path.";
262         LOG_FULL(ERROR) << msg;
263         THROW_REFERENCE_ERROR_AND_RETURN(thread_, msg.c_str(), Unexpected(false));
264     }
265     if (jsPandaFile->IsModule(recordInfo)) {
266         global = undefined;
267         CString moduleName = jsPandaFile->GetJSPandaFileDesc();
268         if (!jsPandaFile->IsBundlePack()) {
269             moduleName = entry;
270         }
271         JSHandle<SourceTextModule> module = moduleManager_->HostGetImportedModule(moduleName);
272         func->SetModule(thread_, module);
273     } else {
274         // if it is Cjs at present, the module slot of the function is not used. We borrow it to store the recordName,
275         // which can avoid the problem of larger memory caused by the new slot
276         JSHandle<EcmaString> recordName = factory_->NewFromUtf8(entry);
277         func->SetModule(thread_, recordName);
278     }
279     vm_->CheckStartCpuProfiler();
280 
281     JSTaggedValue result;
282     if (jsPandaFile->IsCjs(recordInfo)) {
283         CJSExecution(func, global, jsPandaFile, entryPoint);
284     } else {
285         if (aotFileManager_->IsLoadMain(jsPandaFile, entry)) {
286             EcmaRuntimeStatScope runtimeStatScope(vm_);
287             result = InvokeEcmaAotEntrypoint(func, global, jsPandaFile, entryPoint);
288         } else {
289             if (thread_->IsPGOProfilerEnable()) {
290                 vm_->GetPGOProfiler()->ProfileCall(JSTaggedValue::VALUE_UNDEFINED, func.GetTaggedType());
291             }
292             EcmaRuntimeCallInfo *info =
293                 EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle<JSTaggedValue>(func), global, undefined, 0);
294             EcmaRuntimeStatScope runtimeStatScope(vm_);
295             EcmaInterpreter::Execute(info);
296         }
297     }
298     if (!thread_->HasPendingException()) {
299         job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
300     }
301 
302     // print exception information
303     if (!excuteFromJob && thread_->HasPendingException()) {
304         HandleUncaughtException(thread_->GetException());
305     }
306     return result;
307 }
308 
CJSExecution(JSHandle<JSFunction> & func,JSHandle<JSTaggedValue> & thisArg,const JSPandaFile * jsPandaFile,std::string_view entryPoint)309 void EcmaContext::CJSExecution(JSHandle<JSFunction> &func, JSHandle<JSTaggedValue> &thisArg,
310                                const JSPandaFile *jsPandaFile, std::string_view entryPoint)
311 {
312     // create "module", "exports", "require", "filename", "dirname"
313     JSHandle<CjsModule> module = factory_->NewCjsModule();
314     JSHandle<JSTaggedValue> require = GetGlobalEnv()->GetCjsRequireFunction();
315     JSHandle<CjsExports> exports = factory_->NewCjsExports();
316     JSMutableHandle<JSTaggedValue> filename(thread_, JSTaggedValue::Undefined());
317     JSMutableHandle<JSTaggedValue> dirname(thread_, JSTaggedValue::Undefined());
318     if (jsPandaFile->IsBundlePack()) {
319         ModulePathHelper::ResolveCurrentPath(thread_, dirname, filename, jsPandaFile);
320     } else {
321         filename.Update(func->GetModule());
322         ASSERT(filename->IsString());
323         CString fullName = ConvertToString(filename.GetTaggedValue());
324         dirname.Update(PathHelper::ResolveDirPath(thread_, fullName));
325     }
326     CJSInfo cjsInfo(module, require, exports, filename, dirname);
327     RequireManager::InitializeCommonJS(thread_, cjsInfo);
328     if (aotFileManager_->IsLoadMain(jsPandaFile, entryPoint.data())) {
329         EcmaRuntimeStatScope runtimeStateScope(vm_);
330         InvokeEcmaAotEntrypoint(func, thisArg, jsPandaFile, entryPoint, &cjsInfo);
331     } else {
332         // Execute main function
333         JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
334         EcmaRuntimeCallInfo *info =
335             EcmaInterpreter::NewRuntimeCallInfo(thread_,
336                                                 JSHandle<JSTaggedValue>(func),
337                                                 thisArg, undefined, 5); // 5 : argument numbers
338         RETURN_IF_ABRUPT_COMPLETION(thread_);
339         if (info == nullptr) {
340             LOG_ECMA(ERROR) << "CJSExecution Stack overflow!";
341             return;
342         }
343         info->SetCallArg(cjsInfo.exportsHdl.GetTaggedValue(),
344             cjsInfo.requireHdl.GetTaggedValue(),
345             cjsInfo.moduleHdl.GetTaggedValue(),
346             cjsInfo.filenameHdl.GetTaggedValue(),
347             cjsInfo.dirnameHdl.GetTaggedValue());
348         EcmaRuntimeStatScope runtimeStatScope(vm_);
349         EcmaInterpreter::Execute(info);
350     }
351     if (!thread_->HasPendingException()) {
352         job::MicroJobQueue::ExecutePendingJob(thread_, thread_->GetCurrentEcmaContext()->GetMicroJobQueue());
353     }
354 
355     if (!thread_->HasPendingException()) {
356         // Collecting module.exports : exports ---> module.exports --->Module._cache
357         RequireManager::CollectExecutedExp(thread_, cjsInfo);
358     }
359 }
360 
HasCachedConstpool(const JSPandaFile * jsPandaFile) const361 bool EcmaContext::HasCachedConstpool(const JSPandaFile *jsPandaFile) const
362 {
363     return cachedConstpools_.find(jsPandaFile) != cachedConstpools_.end();
364 }
365 
FindConstpool(const JSPandaFile * jsPandaFile,int32_t index)366 JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, int32_t index)
367 {
368     auto iter = cachedConstpools_.find(jsPandaFile);
369     if (iter == cachedConstpools_.end()) {
370         return JSTaggedValue::Hole();
371     }
372     auto constpoolIter = iter->second.find(index);
373     if (constpoolIter == iter->second.end()) {
374         return JSTaggedValue::Hole();
375     }
376     return constpoolIter->second;
377 }
378 
FindConstpools(const JSPandaFile * jsPandaFile)379 std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> EcmaContext::FindConstpools(
380     const JSPandaFile *jsPandaFile)
381 {
382     auto iter = cachedConstpools_.find(jsPandaFile);
383     if (iter == cachedConstpools_.end()) {
384         return std::nullopt;
385     }
386     return iter->second;
387 }
388 
389 // For new version instruction.
FindConstpool(const JSPandaFile * jsPandaFile,panda_file::File::EntityId id)390 JSTaggedValue EcmaContext::FindConstpool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
391 {
392     panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
393     int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
394     return FindConstpool(jsPandaFile, index);
395 }
396 
FindOrCreateConstPool(const JSPandaFile * jsPandaFile,panda_file::File::EntityId id)397 JSHandle<ConstantPool> EcmaContext::FindOrCreateConstPool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
398 {
399     panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
400     int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
401     JSTaggedValue constpool = FindConstpool(jsPandaFile, index);
402     if (constpool.IsHole()) {
403         JSHandle<ConstantPool> newConstpool = ConstantPool::CreateConstPool(vm_, jsPandaFile, id);
404         AddConstpool(jsPandaFile, newConstpool.GetTaggedValue(), index);
405         return newConstpool;
406     }
407 
408     return JSHandle<ConstantPool>(thread_, constpool);
409 }
410 
CreateAllConstpool(const JSPandaFile * jsPandaFile)411 void EcmaContext::CreateAllConstpool(const JSPandaFile *jsPandaFile)
412 {
413     auto headers = jsPandaFile->GetPandaFile()->GetIndexHeaders();
414     uint32_t index = 0;
415     for (const auto &header : headers) {
416         auto constpoolSize = header.method_idx_size;
417         JSHandle<ConstantPool> constpool = factory_->NewConstantPool(constpoolSize);
418         constpool->SetJSPandaFile(jsPandaFile);
419         constpool->SetIndexHeader(&header);
420         AddConstpool(jsPandaFile, constpool.GetTaggedValue(), index++);
421     }
422 }
423 
AddConstpool(const JSPandaFile * jsPandaFile,JSTaggedValue constpool,int32_t index)424 void EcmaContext::AddConstpool(const JSPandaFile *jsPandaFile, JSTaggedValue constpool, int32_t index)
425 {
426     ASSERT(constpool.IsConstantPool());
427     if (cachedConstpools_.find(jsPandaFile) == cachedConstpools_.end()) {
428         cachedConstpools_[jsPandaFile] = CMap<int32_t, JSTaggedValue>();
429     }
430     auto &constpoolMap = cachedConstpools_[jsPandaFile];
431     ASSERT(constpoolMap.find(index) == constpoolMap.end());
432     constpoolMap.insert({index, constpool});
433 }
434 
GetAndClearEcmaUncaughtException() const435 JSHandle<JSTaggedValue> EcmaContext::GetAndClearEcmaUncaughtException() const
436 {
437     JSHandle<JSTaggedValue> exceptionHandle = GetEcmaUncaughtException();
438     thread_->ClearException();  // clear for ohos app
439     return exceptionHandle;
440 }
441 
ProcessNativeDelete(const WeakRootVisitor & visitor)442 void EcmaContext::ProcessNativeDelete(const WeakRootVisitor &visitor)
443 {
444     auto iterator = cachedConstpools_.begin();
445     while (iterator != cachedConstpools_.end()) {
446         auto &constpools = iterator->second;
447         auto constpoolIter = constpools.begin();
448         while (constpoolIter != constpools.end()) {
449             JSTaggedValue constpoolVal = constpoolIter->second;
450             if (constpoolVal.IsHeapObject()) {
451                 TaggedObject *obj = constpoolVal.GetTaggedObject();
452                 auto fwd = visitor(obj);
453                 if (fwd == nullptr) {
454                     constpoolIter = constpools.erase(constpoolIter);
455                     continue;
456                 }
457             }
458             ++constpoolIter;
459         }
460         if (constpools.size() == 0) {
461             LOG_ECMA(INFO) << "remove js pandafile by gc, file:" << iterator->first->GetJSPandaFileDesc();
462             JSPandaFileManager::GetInstance()->RemoveJSPandaFileVm(vm_, iterator->first);
463             iterator = cachedConstpools_.erase(iterator);
464         } else {
465             ++iterator;
466         }
467     }
468 }
469 
ProcessReferences(const WeakRootVisitor & visitor)470 void EcmaContext::ProcessReferences(const WeakRootVisitor &visitor)
471 {
472     auto iterator = cachedConstpools_.begin();
473     while (iterator != cachedConstpools_.end()) {
474         auto &constpools = iterator->second;
475         auto constpoolIter = constpools.begin();
476         while (constpoolIter != constpools.end()) {
477             JSTaggedValue constpoolVal = constpoolIter->second;
478             if (constpoolVal.IsHeapObject()) {
479                 TaggedObject *obj = constpoolVal.GetTaggedObject();
480                 auto fwd = visitor(obj);
481                 if (fwd == nullptr) {
482                     constpoolIter = constpools.erase(constpoolIter);
483                     continue;
484                 } else if (fwd != obj) {
485                     constpoolIter->second = JSTaggedValue(fwd);
486                 }
487             }
488             ++constpoolIter;
489         }
490         if (constpools.size() == 0) {
491             LOG_ECMA(INFO) << "remove js pandafile by gc, file:" << iterator->first->GetJSPandaFileDesc();
492             JSPandaFileManager::GetInstance()->RemoveJSPandaFileVm(vm_, iterator->first);
493             iterator = cachedConstpools_.erase(iterator);
494         } else {
495             ++iterator;
496         }
497     }
498 }
499 
GetEcmaUncaughtException() const500 JSHandle<JSTaggedValue> EcmaContext::GetEcmaUncaughtException() const
501 {
502     if (!thread_->HasPendingException()) {
503         return JSHandle<JSTaggedValue>();
504     }
505     JSHandle<JSTaggedValue> exceptionHandle(thread_, thread_->GetException());
506     return exceptionHandle;
507 }
508 
EnableUserUncaughtErrorHandler()509 void EcmaContext::EnableUserUncaughtErrorHandler()
510 {
511     isUncaughtExceptionRegistered_ = true;
512 }
513 
HandleUncaughtException(JSTaggedValue exception)514 void EcmaContext::HandleUncaughtException(JSTaggedValue exception)
515 {
516     if (isUncaughtExceptionRegistered_) {
517         return;
518     }
519     [[maybe_unused]] EcmaHandleScope handleScope(thread_);
520     JSHandle<JSTaggedValue> exceptionHandle(thread_, exception);
521     // if caught exceptionHandle type is JSError
522     thread_->ClearException();
523     if (exceptionHandle->IsJSError()) {
524         PrintJSErrorInfo(thread_, exceptionHandle);
525         return;
526     }
527     JSHandle<EcmaString> result = JSTaggedValue::ToString(thread_, exceptionHandle);
528     CString string = ConvertToString(*result);
529     LOG_NO_TAG(ERROR) << string;
530 }
531 
532 // static
PrintJSErrorInfo(JSThread * thread,const JSHandle<JSTaggedValue> & exceptionInfo)533 void EcmaContext::PrintJSErrorInfo(JSThread *thread, const JSHandle<JSTaggedValue> &exceptionInfo)
534 {
535     JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
536     JSHandle<JSTaggedValue> nameValue = JSObject::GetProperty(thread, exceptionInfo, nameKey).GetValue();
537     JSHandle<EcmaString> name = JSTaggedValue::ToString(thread, nameValue);
538     // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
539     if (thread->HasPendingException()) {
540         thread->ClearException();
541         name = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
542     }
543     JSHandle<JSTaggedValue> msgKey = thread->GlobalConstants()->GetHandledMessageString();
544     JSHandle<JSTaggedValue> msgValue = JSObject::GetProperty(thread, exceptionInfo, msgKey).GetValue();
545     JSHandle<EcmaString> msg = JSTaggedValue::ToString(thread, msgValue);
546     // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
547     if (thread->HasPendingException()) {
548         thread->ClearException();
549         msg = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
550     }
551     JSHandle<JSTaggedValue> stackKey = thread->GlobalConstants()->GetHandledStackString();
552     JSHandle<JSTaggedValue> stackValue = JSObject::GetProperty(thread, exceptionInfo, stackKey).GetValue();
553     JSHandle<EcmaString> stack = JSTaggedValue::ToString(thread, stackValue);
554     // JSTaggedValue::ToString may cause exception. In this case, do not return, use "<error>" instead.
555     if (thread->HasPendingException()) {
556         thread->ClearException();
557         stack = thread->GetEcmaVM()->GetFactory()->NewFromStdString("<error>");
558     }
559 
560     CString nameBuffer = ConvertToString(*name);
561     CString msgBuffer = ConvertToString(*msg);
562     CString stackBuffer = ConvertToString(*stack);
563     LOG_NO_TAG(ERROR) << nameBuffer << ": " << msgBuffer << "\n" << stackBuffer;
564 }
565 
ExecutePromisePendingJob()566 bool EcmaContext::ExecutePromisePendingJob()
567 {
568     if (isProcessingPendingJob_) {
569         LOG_ECMA(DEBUG) << "EcmaVM::ExecutePromisePendingJob can not reentrant";
570         return false;
571     }
572     if (!thread_->HasPendingException()) {
573         isProcessingPendingJob_ = true;
574         job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
575         isProcessingPendingJob_ = false;
576         return true;
577     }
578     return false;
579 }
580 
ClearBufferData()581 void EcmaContext::ClearBufferData()
582 {
583     auto iter = cachedConstpools_.begin();
584     while (iter != cachedConstpools_.end()) {
585         LOG_ECMA(INFO) << "remove js pandafile by vm destruct, file:" << iter->first->GetJSPandaFileDesc();
586         JSPandaFileManager::GetInstance()->RemoveJSPandaFileVm(vm_, iter->first);
587         iter++;
588     }
589     cachedConstpools_.clear();
590 }
591 
SetGlobalEnv(GlobalEnv * global)592 void EcmaContext::SetGlobalEnv(GlobalEnv *global)
593 {
594     ASSERT(global != nullptr);
595     globalEnv_ = JSTaggedValue(global);
596 }
597 
SetMicroJobQueue(job::MicroJobQueue * queue)598 void EcmaContext::SetMicroJobQueue(job::MicroJobQueue *queue)
599 {
600     ASSERT(queue != nullptr);
601     microJobQueue_ = JSTaggedValue(queue);
602 }
603 
GetGlobalEnv() const604 JSHandle<GlobalEnv> EcmaContext::GetGlobalEnv() const
605 {
606     return JSHandle<GlobalEnv>(reinterpret_cast<uintptr_t>(&globalEnv_));
607 }
608 
GetMicroJobQueue() const609 JSHandle<job::MicroJobQueue> EcmaContext::GetMicroJobQueue() const
610 {
611     return JSHandle<job::MicroJobQueue>(reinterpret_cast<uintptr_t>(&microJobQueue_));
612 }
613 
MountContext(JSThread * thread)614 void EcmaContext::MountContext(JSThread *thread)
615 {
616     EcmaContext *context = EcmaContext::CreateAndInitialize(thread);
617     thread->SwitchCurrentContext(context);
618 }
619 
UnmountContext(JSThread * thread)620 void EcmaContext::UnmountContext(JSThread *thread)
621 {
622     EcmaContext *context = thread->GetCurrentEcmaContext();
623     thread->PopContext();
624     Destroy(context);
625 }
626 
CreateAndInitialize(JSThread * thread)627 EcmaContext *EcmaContext::CreateAndInitialize(JSThread *thread)
628 {
629     EcmaContext *context = EcmaContext::Create(thread);
630     thread->PushContext(context);
631     context->Initialize();
632     return context;
633 }
634 
CheckAndDestroy(JSThread * thread,EcmaContext * context)635 void EcmaContext::CheckAndDestroy(JSThread *thread, EcmaContext *context)
636 {
637     if (thread->EraseContext(context)) {
638         Destroy(context);
639         return;
640     }
641     LOG_ECMA(FATAL) << "CheckAndDestroy a nonexistent context.";
642 }
643 
SetupRegExpResultCache()644 void EcmaContext::SetupRegExpResultCache()
645 {
646     regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_);
647 }
648 
Iterate(const RootVisitor & v,const RootRangeVisitor & rv)649 void EcmaContext::Iterate(const RootVisitor &v, const RootRangeVisitor &rv)
650 {
651     // visit global Constant
652     globalConst_.VisitRangeSlot(rv);
653 
654     v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&globalEnv_)));
655     v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&regexpCache_)));
656     v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&microJobQueue_)));
657     if (moduleManager_) {
658         moduleManager_->Iterate(v);
659     }
660     if (tsManager_) {
661         tsManager_->Iterate(v);
662     }
663     if (aotFileManager_) {
664         aotFileManager_->Iterate(v);
665     }
666     if (propertiesCache_ != nullptr) {
667         propertiesCache_->Clear();
668     }
669     if (!vm_->GetJSOptions().EnableGlobalLeakCheck() && currentHandleStorageIndex_ != -1) {
670         // IterateHandle when disableGlobalLeakCheck.
671         int32_t nid = currentHandleStorageIndex_;
672         for (int32_t i = 0; i <= nid; ++i) {
673             auto node = handleStorageNodes_.at(i);
674             auto start = node->data();
675             auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
676             rv(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
677         }
678     }
679 
680     if (!joinStack_.empty()) {
681         rv(Root::ROOT_VM, ObjectSlot(ToUintPtr(&joinStack_.front())),
682             ObjectSlot(ToUintPtr(&joinStack_.back()) + JSTaggedValue::TaggedTypeSize()));
683     }
684 }
685 
IterateHandle(const RootRangeVisitor & rangeVisitor)686 size_t EcmaContext::IterateHandle(const RootRangeVisitor &rangeVisitor)
687 {
688     // EnableGlobalLeakCheck.
689     size_t handleCount = 0;
690     if (currentHandleStorageIndex_ != -1) {
691         int32_t nid = currentHandleStorageIndex_;
692         for (int32_t i = 0; i <= nid; ++i) {
693             auto node = handleStorageNodes_.at(i);
694             auto start = node->data();
695             auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
696             rangeVisitor(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
697             handleCount += (ToUintPtr(end) - ToUintPtr(start)) / sizeof(JSTaggedType);
698         }
699     }
700     return handleCount;
701 }
702 
ExpandHandleStorage()703 uintptr_t *EcmaContext::ExpandHandleStorage()
704 {
705     uintptr_t *result = nullptr;
706     int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size() - 1);
707     if (currentHandleStorageIndex_ == lastIndex) {
708         auto n = new std::array<JSTaggedType, NODE_BLOCK_SIZE>();
709         handleStorageNodes_.push_back(n);
710         currentHandleStorageIndex_++;
711         result = reinterpret_cast<uintptr_t *>(&n->data()[0]);
712         handleScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE];
713     } else {
714         currentHandleStorageIndex_++;
715         auto lastNode = handleStorageNodes_[currentHandleStorageIndex_];
716         result = reinterpret_cast<uintptr_t *>(&lastNode->data()[0]);
717         handleScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE];
718     }
719 
720     return result;
721 }
722 
ShrinkHandleStorage(int prevIndex)723 void EcmaContext::ShrinkHandleStorage(int prevIndex)
724 {
725     currentHandleStorageIndex_ = prevIndex;
726     int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size() - 1);
727 #if ECMASCRIPT_ENABLE_ZAP_MEM
728     uintptr_t size = ToUintPtr(handleScopeStorageEnd_) - ToUintPtr(handleScopeStorageNext_);
729     if (memset_s(handleScopeStorageNext_, size, 0, size) != EOK) {
730         LOG_FULL(FATAL) << "memset_s failed";
731         UNREACHABLE();
732     }
733     for (int32_t i = currentHandleStorageIndex_ + 1; i < lastIndex; i++) {
734         if (memset_s(handleStorageNodes_[i],
735                      NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0,
736                      NODE_BLOCK_SIZE * sizeof(JSTaggedType)) !=
737                      EOK) {
738             LOG_FULL(FATAL) << "memset_s failed";
739             UNREACHABLE();
740         }
741     }
742 #endif
743 
744     if (lastIndex > MIN_HANDLE_STORAGE_SIZE && currentHandleStorageIndex_ < MIN_HANDLE_STORAGE_SIZE) {
745         for (int i = MIN_HANDLE_STORAGE_SIZE; i < lastIndex; i++) {
746             auto node = handleStorageNodes_.back();
747             delete node;
748             handleStorageNodes_.pop_back();
749         }
750     }
751 }
752 
LoadStubFile()753 void EcmaContext::LoadStubFile()
754 {
755     std::string stubFile = vm_->GetJSOptions().GetStubFile();
756     aotFileManager_->LoadStubFile(stubFile);
757 }
758 
LoadAOTFiles(const std::string & aotFileName)759 bool EcmaContext::LoadAOTFiles(const std::string& aotFileName)
760 {
761     std::string anFile = aotFileName + AOTFileManager::FILE_EXTENSION_AN;
762     if (!aotFileManager_->LoadAnFile(anFile)) {
763         LOG_ECMA(ERROR) << "Load " << anFile << " failed. Destroy aot data and rollback to interpreter";
764         ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
765         return false;
766     }
767 
768     std::string aiFile = aotFileName + AOTFileManager::FILE_EXTENSION_AI;
769     if (!aotFileManager_->LoadAiFile(aiFile)) {
770         LOG_ECMA(ERROR) << "Load " << aiFile << " failed. Destroy aot data and rollback to interpreter";
771         ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAnData(anFile);
772         return false;
773     }
774     return true;
775 }
776 
PrintOptStat()777 void EcmaContext::PrintOptStat()
778 {
779     if (optCodeProfiler_ != nullptr) {
780         optCodeProfiler_->PrintAndReset();
781     }
782 }
783 
DumpAOTInfo() const784 void EcmaContext::DumpAOTInfo() const
785 {
786     aotFileManager_->DumpAOTInfo();
787 }
788 
JoinStackPushFastPath(JSHandle<JSTaggedValue> receiver)789 bool EcmaContext::JoinStackPushFastPath(JSHandle<JSTaggedValue> receiver)
790 {
791     if (JSTaggedValue::SameValue(joinStack_[0], JSTaggedValue::Hole())) {
792         joinStack_[0] = receiver.GetTaggedValue();
793         return true;
794     }
795     return JoinStackPush(receiver);
796 }
797 
JoinStackPush(JSHandle<JSTaggedValue> receiver)798 bool EcmaContext::JoinStackPush(JSHandle<JSTaggedValue> receiver)
799 {
800     uint32_t capacity = joinStack_.size();
801     JSTaggedValue receiverValue = receiver.GetTaggedValue();
802     for (size_t i = 0; i < capacity; ++i) {
803         if (JSTaggedValue::SameValue(joinStack_[i], JSTaggedValue::Hole())) {
804             joinStack_[i] = receiverValue;
805             return true;
806         }
807         if (JSTaggedValue::SameValue(joinStack_[i], receiverValue)) {
808             return false;
809         }
810     }
811     joinStack_.emplace_back(receiverValue);
812     return true;
813 }
814 
JoinStackPopFastPath(JSHandle<JSTaggedValue> receiver)815 void EcmaContext::JoinStackPopFastPath(JSHandle<JSTaggedValue> receiver)
816 {
817     uint32_t length = joinStack_.size();
818     if (JSTaggedValue::SameValue(joinStack_[0], receiver.GetTaggedValue()) && length == MIN_JOIN_STACK_SIZE) {
819         joinStack_[0] = JSTaggedValue::Hole();
820     } else {
821         JoinStackPop(receiver);
822     }
823 }
824 
JoinStackPop(JSHandle<JSTaggedValue> receiver)825 void EcmaContext::JoinStackPop(JSHandle<JSTaggedValue> receiver)
826 {
827     uint32_t length = joinStack_.size();
828     for (size_t i = 0; i < length; ++i) {
829         if (JSTaggedValue::SameValue(joinStack_[i], receiver.GetTaggedValue())) {
830             if (i == 0 && length > MIN_JOIN_STACK_SIZE) {
831                 joinStack_ = {JSTaggedValue::Hole(), JSTaggedValue::Hole()};
832                 break;
833             } else {
834                 joinStack_[i] = JSTaggedValue::Hole();
835                 break;
836             }
837         }
838     }
839 }
840 }  // namespace panda::ecmascript
841