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