1 /*
2 * Copyright (c) 2021-2022 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_vm.h"
17
18 #include "ecmascript/base/path_helper.h"
19 #include "ecmascript/base/string_helper.h"
20 #include "ecmascript/builtins/builtins.h"
21 #include "ecmascript/builtins/builtins_ark_tools.h"
22 #include "ecmascript/builtins/builtins_collator.h"
23 #include "ecmascript/builtins/builtins_date_time_format.h"
24 #include "ecmascript/builtins/builtins_global.h"
25 #include "ecmascript/builtins/builtins_number_format.h"
26 #include "ecmascript/builtins/builtins_object.h"
27 #include "ecmascript/builtins/builtins_promise.h"
28 #include "ecmascript/builtins/builtins_promise_handler.h"
29 #include "ecmascript/builtins/builtins_proxy.h"
30 #include "ecmascript/builtins/builtins_regexp.h"
31 #include "ecmascript/compiler/builtins/builtins_call_signature.h"
32 #include "ecmascript/compiler/call_signature.h"
33 #include "ecmascript/compiler/common_stubs.h"
34 #include "ecmascript/compiler/interpreter_stub.h"
35 #include "ecmascript/compiler/rt_call_signature.h"
36 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
37 #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
38 #endif
39 #if !WIN_OR_MAC_OR_IOS_PLATFORM
40 #include "ecmascript/dfx/hprof/heap_profiler.h"
41 #include "ecmascript/dfx/hprof/heap_profiler_interface.h"
42 #endif
43 #include "ecmascript/debugger/js_debugger_manager.h"
44 #include "ecmascript/dfx/vmstat/opt_code_profiler.h"
45 #include "ecmascript/dfx/vmstat/runtime_stat.h"
46 #include "ecmascript/ecma_string_table.h"
47 #include "ecmascript/aot_file_manager.h"
48 #include "ecmascript/global_env.h"
49 #include "ecmascript/global_env_constants-inl.h"
50 #include "ecmascript/global_env_constants.h"
51 #include "ecmascript/interpreter/interpreter-inl.h"
52 #include "ecmascript/jobs/micro_job_queue.h"
53 #include "ecmascript/jspandafile/constpool_value.h"
54 #include "ecmascript/jspandafile/js_pandafile.h"
55 #include "ecmascript/jspandafile/js_pandafile_manager.h"
56 #include "ecmascript/jspandafile/quick_fix_manager.h"
57 #include "ecmascript/jspandafile/module_data_extractor.h"
58 #include "ecmascript/jspandafile/panda_file_translator.h"
59 #include "ecmascript/jspandafile/program_object.h"
60 #include "ecmascript/js_arraybuffer.h"
61 #include "ecmascript/js_for_in_iterator.h"
62 #include "ecmascript/js_native_pointer.h"
63 #include "ecmascript/js_thread.h"
64 #include "ecmascript/mem/concurrent_marker.h"
65 #include "ecmascript/mem/heap.h"
66 #include "ecmascript/mem/gc_stats.h"
67 #include "ecmascript/mem/mem.h"
68 #include "ecmascript/mem/space.h"
69 #include "ecmascript/mem/visitor.h"
70 #include "ecmascript/napi/include/dfx_jsnapi.h"
71 #include "ecmascript/taskpool/task.h"
72 #include "ecmascript/module/js_module_manager.h"
73 #include "ecmascript/object_factory.h"
74 #include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
75 #include "ecmascript/taskpool/taskpool.h"
76 #include "ecmascript/regexp/regexp_parser_cache.h"
77 #include "ecmascript/runtime_call_id.h"
78 #include "ecmascript/snapshot/mem/snapshot_env.h"
79 #include "ecmascript/snapshot/mem/snapshot.h"
80 #include "ecmascript/stubs/runtime_stubs.h"
81 #include "ecmascript/tagged_array-inl.h"
82 #include "ecmascript/tagged_dictionary.h"
83 #include "ecmascript/tagged_queue.h"
84 #include "ecmascript/tagged_queue.h"
85 #include "ecmascript/ts_types/ts_manager.h"
86 #include "ecmascript/require/js_cjs_module_cache.h"
87 #include "ecmascript/require/js_require_manager.h"
88
89 namespace panda::ecmascript {
90 using RandomGenerator = base::RandomGenerator;
91 using PathHelper = base::PathHelper;
92 /* static */
Create(const JSRuntimeOptions & options,EcmaParamConfiguration & config)93 EcmaVM *EcmaVM::Create(const JSRuntimeOptions &options, EcmaParamConfiguration &config)
94 {
95 JSRuntimeOptions newOptions = options;
96 // only define SUPPORT_ENABLE_ASM_INTERP can enable asm-interpreter
97 #if !defined(SUPPORT_ENABLE_ASM_INTERP)
98 newOptions.SetEnableAsmInterpreter(false);
99 #endif
100 auto vm = new EcmaVM(newOptions, config);
101 if (UNLIKELY(vm == nullptr)) {
102 LOG_ECMA(ERROR) << "Failed to create jsvm";
103 return nullptr;
104 }
105 auto jsThread = JSThread::Create(vm);
106 vm->thread_ = jsThread;
107 vm->Initialize();
108 return vm;
109 }
110
111 // static
Destroy(EcmaVM * vm)112 bool EcmaVM::Destroy(EcmaVM *vm)
113 {
114 if (vm != nullptr) {
115 delete vm;
116 vm = nullptr;
117 return true;
118 }
119 return false;
120 }
121
PreFork()122 void EcmaVM::PreFork()
123 {
124 heap_->CompactHeapBeforeFork();
125 heap_->AdjustSpaceSizeForAppSpawn();
126 heap_->GetReadOnlySpace()->SetReadOnly();
127 heap_->DisableParallelGC();
128 }
129
PostFork()130 void EcmaVM::PostFork()
131 {
132 RandomGenerator::InitRandom();
133 heap_->SetHeapMode(HeapMode::SHARE);
134 GetAssociatedJSThread()->SetThreadId();
135 heap_->EnableParallelGC();
136 }
137
EcmaVM(JSRuntimeOptions options,EcmaParamConfiguration config)138 EcmaVM::EcmaVM(JSRuntimeOptions options, EcmaParamConfiguration config)
139 : stringTable_(new EcmaStringTable(this)),
140 nativeAreaAllocator_(std::make_unique<NativeAreaAllocator>()),
141 heapRegionAllocator_(std::make_unique<HeapRegionAllocator>()),
142 chunk_(nativeAreaAllocator_.get()),
143 ecmaParamConfiguration_(std::move(config))
144 {
145 options_ = std::move(options);
146 icEnabled_ = options_.EnableIC();
147 optionalLogEnabled_ = options_.EnableOptionalLog();
148 snapshotFileName_ = options_.GetSnapshotFile().c_str();
149 frameworkAbcFileName_ = options_.GetFrameworkAbcFile().c_str();
150 options_.ParseAsmInterOption();
151 }
152
ResetPGOProfiler()153 void EcmaVM::ResetPGOProfiler()
154 {
155 bool isEnablePGOProfiler = IsEnablePGOProfiler();
156 if (options_.IsWorker()) {
157 isEnablePGOProfiler = PGOProfilerManager::GetInstance()->IsEnable();
158 }
159 if (pgoProfiler_ == nullptr) {
160 pgoProfiler_ = PGOProfilerManager::GetInstance()->Build(this, isEnablePGOProfiler);
161 } else {
162 PGOProfilerManager::GetInstance()->Reset(pgoProfiler_, isEnablePGOProfiler);
163 }
164 thread_->SetPGOProfilerEnable(isEnablePGOProfiler);
165 }
166
IsEnablePGOProfiler() const167 bool EcmaVM::IsEnablePGOProfiler() const
168 {
169 return options_.GetEnableAsmInterpreter() && options_.IsEnablePGOProfiler();
170 }
171
Initialize()172 bool EcmaVM::Initialize()
173 {
174 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "EcmaVM::Initialize");
175 ResetPGOProfiler();
176 Taskpool::GetCurrentTaskpool()->Initialize();
177 #ifndef PANDA_TARGET_WINDOWS
178 RuntimeStubs::Initialize(thread_);
179 #endif
180 auto globalConst = const_cast<GlobalEnvConstants *>(thread_->GlobalConstants());
181 regExpParserCache_ = new RegExpParserCache();
182 heap_ = new Heap(this);
183 heap_->Initialize();
184 gcStats_ = chunk_.New<GCStats>(heap_, options_.GetLongPauseTime());
185 factory_ = chunk_.New<ObjectFactory>(thread_, heap_);
186 if (UNLIKELY(factory_ == nullptr)) {
187 LOG_FULL(FATAL) << "alloc factory_ failed";
188 UNREACHABLE();
189 }
190 debuggerManager_ = chunk_.New<tooling::JsDebuggerManager>(this);
191 [[maybe_unused]] EcmaHandleScope scope(thread_);
192
193 if (!options_.EnableSnapshotDeserialize()) {
194 LOG_ECMA(DEBUG) << "EcmaVM::Initialize run builtins";
195 JSHandle<JSHClass> hClassHandle = factory_->InitClassClass();
196 JSHandle<JSHClass> globalEnvClass = factory_->NewEcmaHClass(*hClassHandle,
197 GlobalEnv::SIZE,
198 JSType::GLOBAL_ENV);
199 globalConst->Init(thread_, *hClassHandle);
200 globalConstInitialized_ = true;
201 JSHandle<GlobalEnv> globalEnv = factory_->NewGlobalEnv(*globalEnvClass);
202 globalEnv->Init(thread_);
203 globalEnv_ = globalEnv.GetTaggedValue();
204 thread_->SetGlueGlobalEnv(reinterpret_cast<GlobalEnv *>(globalEnv.GetTaggedType()));
205 Builtins builtins;
206 builtins.Initialize(globalEnv, thread_);
207 if (!WIN_OR_MAC_OR_IOS_PLATFORM && options_.EnableSnapshotSerialize()) {
208 const CString fileName = "builtins.snapshot";
209 Snapshot snapshot(this);
210 snapshot.SerializeBuiltins(fileName);
211 }
212 } else {
213 const CString fileName = "builtins.snapshot";
214 Snapshot snapshot(this);
215 if (!WIN_OR_MAC_OR_IOS_PLATFORM) {
216 snapshot.Deserialize(SnapshotType::BUILTINS, fileName, true);
217 }
218 globalConst->InitSpecialForSnapshot();
219 Builtins builtins;
220 builtins.InitializeForSnapshot(thread_);
221 globalConstInitialized_ = true;
222 }
223
224 SetupRegExpResultCache();
225 microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue();
226 GenerateInternalNativeMethods();
227 thread_->SetGlobalObject(GetGlobalEnv()->GetGlobalObject());
228 moduleManager_ = new ModuleManager(this);
229 tsManager_ = new TSManager(this);
230 quickFixManager_ = new QuickFixManager();
231 snapshotEnv_ = new SnapshotEnv(this);
232 if (!WIN_OR_MAC_OR_IOS_PLATFORM) {
233 snapshotEnv_->Initialize();
234 }
235 aotFileManager_ = new AOTFileManager(this);
236 if (options_.GetEnableAsmInterpreter()) {
237 LoadStubFile();
238 }
239
240 if (options_.GetEnableAsmInterpreter() && options_.WasAOTOutputFileSet()) {
241 AnFileDataManager::GetInstance()->SetEnable(true);
242 std::string aotFilename = options_.GetAOTOutputFile();
243 LoadAOTFiles(aotFilename);
244 }
245
246 optCodeProfiler_ = new OptCodeProfiler();
247
248 initialized_ = true;
249 return true;
250 }
251
InitializeEcmaScriptRunStat()252 void EcmaVM::InitializeEcmaScriptRunStat()
253 {
254 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
255 static const char *runtimeCallerNames[] = {
256 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
257 #define INTERPRETER_CALLER_NAME(name) "Interpreter::" #name,
258 INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_NAME) // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
259 #undef INTERPRETER_CALLER_NAME
260 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
261 #define BUILTINS_API_NAME(class, name) "BuiltinsApi::" #class "_" #name,
262 BUILTINS_API_LIST(BUILTINS_API_NAME)
263 #undef BUILTINS_API_NAME
264 #define ABSTRACT_OPERATION_NAME(class, name) "AbstractOperation::" #class "_" #name,
265 ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_NAME)
266 #undef ABSTRACT_OPERATION_NAME
267 #define MEM_ALLOCATE_AND_GC_NAME(name) "Memory::" #name,
268 MEM_ALLOCATE_AND_GC_LIST(MEM_ALLOCATE_AND_GC_NAME)
269 #undef MEM_ALLOCATE_AND_GC_NAME
270 #define DEF_RUNTIME_ID(name) "Runtime::" #name,
271 RUNTIME_STUB_WITH_GC_LIST(DEF_RUNTIME_ID)
272 #undef DEF_RUNTIME_ID
273 };
274 static_assert(sizeof(runtimeCallerNames) == sizeof(const char *) * ecmascript::RUNTIME_CALLER_NUMBER,
275 "Invalid runtime caller number");
276 runtimeStat_ = chunk_.New<EcmaRuntimeStat>(runtimeCallerNames, ecmascript::RUNTIME_CALLER_NUMBER);
277 if (UNLIKELY(runtimeStat_ == nullptr)) {
278 LOG_FULL(FATAL) << "alloc runtimeStat_ failed";
279 UNREACHABLE();
280 }
281 }
282
SetRuntimeStatEnable(bool flag)283 void EcmaVM::SetRuntimeStatEnable(bool flag)
284 {
285 static uint64_t start = 0;
286 if (flag) {
287 start = PandaRuntimeTimer::Now();
288 if (runtimeStat_ == nullptr) {
289 InitializeEcmaScriptRunStat();
290 }
291 } else {
292 LOG_ECMA(INFO) << "Runtime State duration:" << PandaRuntimeTimer::Now() - start << "(ns)";
293 if (runtimeStat_->IsRuntimeStatEnabled()) {
294 runtimeStat_->Print();
295 runtimeStat_->ResetAllCount();
296 }
297 }
298 runtimeStat_->SetRuntimeStatEnabled(flag);
299 }
300
~EcmaVM()301 EcmaVM::~EcmaVM()
302 {
303 initialized_ = false;
304 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
305 DFXJSNApi::StopCpuProfilerForFile(this);
306 #endif
307 heap_->WaitAllTasksFinished();
308 Taskpool::GetCurrentTaskpool()->Destroy(thread_->GetThreadId());
309
310 if (runtimeStat_ != nullptr && runtimeStat_->IsRuntimeStatEnabled()) {
311 runtimeStat_->Print();
312 }
313
314 if (optCodeProfiler_ != nullptr) {
315 delete optCodeProfiler_;
316 optCodeProfiler_ = nullptr;
317 }
318
319 // clear c_address: c++ pointer delete
320 ClearBufferData();
321
322 if (!isBundlePack_) {
323 const JSPandaFile *jsPandaFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(assetPath_);
324 if (jsPandaFile != nullptr) {
325 const_cast<JSPandaFile *>(jsPandaFile)->DeleteParsedConstpoolVM(this);
326 }
327 }
328 // clear icu cache
329 ClearIcuCache();
330
331 if (gcStats_ != nullptr) {
332 if (options_.EnableGCStatsPrint()) {
333 gcStats_->PrintStatisticResult(true);
334 }
335 chunk_.Delete(gcStats_);
336 gcStats_ = nullptr;
337 }
338
339 if (heap_ != nullptr) {
340 heap_->Destroy();
341 delete heap_;
342 heap_ = nullptr;
343 }
344
345 if (regExpParserCache_ != nullptr) {
346 delete regExpParserCache_;
347 regExpParserCache_ = nullptr;
348 }
349
350 if (debuggerManager_ != nullptr) {
351 chunk_.Delete(debuggerManager_);
352 debuggerManager_ = nullptr;
353 }
354
355 if (factory_ != nullptr) {
356 chunk_.Delete(factory_);
357 factory_ = nullptr;
358 }
359
360 if (stringTable_ != nullptr) {
361 delete stringTable_;
362 stringTable_ = nullptr;
363 }
364
365 if (runtimeStat_ != nullptr) {
366 chunk_.Delete(runtimeStat_);
367 runtimeStat_ = nullptr;
368 }
369
370 if (moduleManager_ != nullptr) {
371 delete moduleManager_;
372 moduleManager_ = nullptr;
373 }
374
375 if (tsManager_ != nullptr) {
376 delete tsManager_;
377 tsManager_ = nullptr;
378 }
379
380 if (quickFixManager_ != nullptr) {
381 delete quickFixManager_;
382 quickFixManager_ = nullptr;
383 }
384
385 if (snapshotEnv_ != nullptr) {
386 delete snapshotEnv_;
387 snapshotEnv_ = nullptr;
388 }
389
390 if (aotFileManager_ != nullptr) {
391 delete aotFileManager_;
392 aotFileManager_ = nullptr;
393 }
394
395 if (thread_ != nullptr) {
396 delete thread_;
397 thread_ = nullptr;
398 }
399
400 if (pgoProfiler_ != nullptr) {
401 PGOProfilerManager::GetInstance()->Destroy(pgoProfiler_);
402 pgoProfiler_ = nullptr;
403 }
404 }
405
GetGlobalEnv() const406 JSHandle<GlobalEnv> EcmaVM::GetGlobalEnv() const
407 {
408 return JSHandle<GlobalEnv>(reinterpret_cast<uintptr_t>(&globalEnv_));
409 }
410
GetMicroJobQueue() const411 JSHandle<job::MicroJobQueue> EcmaVM::GetMicroJobQueue() const
412 {
413 return JSHandle<job::MicroJobQueue>(reinterpret_cast<uintptr_t>(µJobQueue_));
414 }
415
FindCatchBlock(Method * method,uint32_t pc) const416 bool EcmaVM::FindCatchBlock(Method *method, uint32_t pc) const
417 {
418 uint32_t pcOffset = panda_file::INVALID_OFFSET;
419 if (thread_->IsAsmInterpreter()) {
420 pcOffset = InterpreterAssembly::FindCatchBlock(method, pc);
421 } else {
422 pcOffset = EcmaInterpreter::FindCatchBlock(method, pc);
423 }
424 return pcOffset != panda_file::INVALID_OFFSET;
425 }
426
InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc,JSHandle<JSTaggedValue> & thisArg,const JSPandaFile * jsPandaFile,std::string_view entryPoint)427 JSTaggedValue EcmaVM::InvokeEcmaAotEntrypoint(JSHandle<JSFunction> mainFunc, JSHandle<JSTaggedValue> &thisArg,
428 const JSPandaFile *jsPandaFile, std::string_view entryPoint)
429 {
430 aotFileManager_->SetAOTMainFuncEntry(mainFunc, jsPandaFile, entryPoint);
431 Method *method = mainFunc->GetCallTarget();
432 size_t actualNumArgs = method->GetNumArgs();
433 size_t argsNum = actualNumArgs + NUM_MANDATORY_JSFUNC_ARGS;
434 std::vector<JSTaggedType> args(argsNum, JSTaggedValue::Undefined().GetRawData());
435 args[0] = mainFunc.GetTaggedValue().GetRawData();
436 args[2] = thisArg.GetTaggedValue().GetRawData(); // 2: this
437 const JSTaggedType *prevFp = thread_->GetLastLeaveFrame();
438 // do not modify this log to INFO, this will call many times
439 LOG_ECMA(DEBUG) << "start to execute aot entry: " << entryPoint;
440 JSTaggedValue res = ExecuteAot(actualNumArgs, args.data(), prevFp, OptimizedEntryFrame::CallType::CALL_FUNC);
441 if (thread_->HasPendingException()) {
442 return thread_->GetException();
443 }
444 return res;
445 }
446
ExecuteAot(size_t actualNumArgs,JSTaggedType * args,const JSTaggedType * prevFp,OptimizedEntryFrame::CallType callType)447 JSTaggedValue EcmaVM::ExecuteAot(size_t actualNumArgs, JSTaggedType *args, const JSTaggedType *prevFp,
448 OptimizedEntryFrame::CallType callType)
449 {
450 INTERPRETER_TRACE(thread_, ExecuteAot);
451 auto entry = thread_->GetRTInterface(kungfu::RuntimeStubCSigns::ID_JSFunctionEntry);
452 // do not modify this log to INFO, this will call many times
453 LOG_ECMA(DEBUG) << "start to execute aot entry: " << (void*)entry;
454 auto res = reinterpret_cast<JSFunctionEntryType>(entry)(thread_->GetGlueAddr(),
455 actualNumArgs,
456 args,
457 reinterpret_cast<uintptr_t>(prevFp),
458 static_cast<size_t>(callType));
459 return res;
460 }
461
CheckStartCpuProfiler()462 void EcmaVM::CheckStartCpuProfiler()
463 {
464 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
465 if (profiler_ == nullptr && !options_.IsWorker() &&
466 options_.EnableCpuProfiler() && options_.GetArkBundleName().compare(bundleName_) == 0) {
467 std::string fileName = options_.GetArkBundleName() + ".cpuprofile";
468 if (!builtins::BuiltinsArkTools::CreateFile(fileName)) {
469 LOG_ECMA(ERROR) << "createFile failed " << fileName;
470 } else {
471 DFXJSNApi::StartCpuProfilerForFile(this, fileName);
472 }
473 }
474 #endif
475 }
476
InvokeEcmaEntrypoint(const JSPandaFile * jsPandaFile,std::string_view entryPoint,bool excuteFromJob)477 Expected<JSTaggedValue, bool> EcmaVM::InvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile, std::string_view entryPoint,
478 bool excuteFromJob)
479 {
480 JSTaggedValue result;
481 [[maybe_unused]] EcmaHandleScope scope(thread_);
482 JSHandle<Program> program;
483 if (jsPandaFile != frameworkPandaFile_) {
484 program = JSPandaFileManager::GetInstance()->GenerateProgram(this, jsPandaFile, entryPoint);
485 } else {
486 program = JSHandle<Program>(thread_, frameworkProgram_);
487 frameworkProgram_ = JSTaggedValue::Hole();
488 }
489 if (program.IsEmpty()) {
490 LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed";
491 return Unexpected(false);
492 }
493 // for debugger
494 debuggerManager_->GetNotificationManager()->LoadModuleEvent(jsPandaFile->GetJSPandaFileDesc(), entryPoint);
495
496 JSHandle<JSFunction> func = JSHandle<JSFunction>(thread_, program->GetMainFunction());
497 JSHandle<JSTaggedValue> global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject();
498 if (jsPandaFile->IsModule(thread_, entryPoint.data())) {
499 global = JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
500 CString moduleName = jsPandaFile->GetJSPandaFileDesc();
501 if (!jsPandaFile->IsBundlePack()) {
502 moduleName = entryPoint.data();
503 }
504 JSHandle<SourceTextModule> module = moduleManager_->HostGetImportedModule(moduleName);
505 func->SetModule(thread_, module);
506 } else {
507 // if it is Cjs at present, the module slot of the function is not used. We borrow it to store the recordName,
508 // which can avoid the problem of larger memory caused by the new slot
509 JSHandle<EcmaString> recordName = factory_->NewFromUtf8(entryPoint.data());
510 func->SetModule(thread_, recordName);
511 }
512 CheckStartCpuProfiler();
513
514 if (aotFileManager_->IsLoadMain(jsPandaFile, entryPoint.data())) {
515 thread_->SetPrintBCOffset(true);
516 EcmaRuntimeStatScope runtimeStatScope(this);
517 result = InvokeEcmaAotEntrypoint(func, global, jsPandaFile, entryPoint);
518 } else {
519 if (jsPandaFile->IsCjs(thread_, entryPoint.data())) {
520 if (!thread_->HasPendingException()) {
521 CJSExecution(func, global, jsPandaFile);
522 }
523 } else {
524 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
525 EcmaRuntimeCallInfo *info =
526 EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle<JSTaggedValue>(func), global, undefined, 0);
527 EcmaRuntimeStatScope runtimeStatScope(this);
528 EcmaInterpreter::Execute(info);
529 }
530 }
531 if (!thread_->HasPendingException()) {
532 job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
533 }
534
535 // print exception information
536 if (!excuteFromJob && thread_->HasPendingException()) {
537 auto exception = thread_->GetException();
538 HandleUncaughtException(exception.GetTaggedObject());
539 }
540 return result;
541 }
542
HasCachedConstpool(const JSPandaFile * jsPandaFile) const543 bool EcmaVM::HasCachedConstpool(const JSPandaFile *jsPandaFile) const
544 {
545 return cachedConstpools_.find(jsPandaFile) != cachedConstpools_.end();
546 }
547
FindConstpool(const JSPandaFile * jsPandaFile,int32_t index)548 JSTaggedValue EcmaVM::FindConstpool(const JSPandaFile *jsPandaFile, int32_t index)
549 {
550 auto iter = cachedConstpools_.find(jsPandaFile);
551 if (iter == cachedConstpools_.end()) {
552 return JSTaggedValue::Hole();
553 }
554 auto constpoolIter = iter->second.find(index);
555 if (constpoolIter == iter->second.end()) {
556 return JSTaggedValue::Hole();
557 }
558 return constpoolIter->second;
559 }
560
FindOrCreateConstPool(const JSPandaFile * jsPandaFile,panda_file::File::EntityId id)561 JSHandle<ConstantPool> EcmaVM::FindOrCreateConstPool(const JSPandaFile *jsPandaFile, panda_file::File::EntityId id)
562 {
563 panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id);
564 int32_t index = static_cast<int32_t>(indexAccessor.GetHeaderIndex());
565 JSTaggedValue constpool = FindConstpool(jsPandaFile, index);
566 if (constpool.IsHole()) {
567 JSHandle<ConstantPool> newConstpool = ConstantPool::CreateConstPool(this, jsPandaFile, id);
568 AddConstpool(jsPandaFile, newConstpool.GetTaggedValue(), index);
569 return newConstpool;
570 }
571
572 return JSHandle<ConstantPool>(thread_, constpool);
573 }
574
FindConstpools(const JSPandaFile * jsPandaFile)575 std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> EcmaVM::FindConstpools(
576 const JSPandaFile *jsPandaFile)
577 {
578 auto iter = cachedConstpools_.find(jsPandaFile);
579 if (iter == cachedConstpools_.end()) {
580 return std::nullopt;
581 }
582 return iter->second;
583 }
584
CJSExecution(JSHandle<JSFunction> & func,JSHandle<JSTaggedValue> & thisArg,const JSPandaFile * jsPandaFile)585 void EcmaVM::CJSExecution(JSHandle<JSFunction> &func, JSHandle<JSTaggedValue> &thisArg,
586 const JSPandaFile *jsPandaFile)
587 {
588 // create "module", "exports", "require", "filename", "dirname"
589 JSHandle<CjsModule> module = factory_->NewCjsModule();
590 JSHandle<JSTaggedValue> require = GetGlobalEnv()->GetCjsRequireFunction();
591 JSHandle<CjsExports> exports = factory_->NewCjsExports();
592 JSMutableHandle<JSTaggedValue> filename(thread_, JSTaggedValue::Undefined());
593 JSMutableHandle<JSTaggedValue> dirname(thread_, JSTaggedValue::Undefined());
594 if (jsPandaFile->IsBundlePack()) {
595 PathHelper::ResolveCurrentPath(thread_, dirname, filename, jsPandaFile);
596 } else {
597 filename.Update(JSFunction::Cast(func.GetTaggedValue().GetTaggedObject())->GetModule());
598 ASSERT(filename->IsString());
599 dirname.Update(PathHelper::ResolveDirPath(thread_, filename));
600 }
601 CJSInfo cjsInfo(module, require, exports, filename, dirname);
602 RequireManager::InitializeCommonJS(thread_, cjsInfo);
603
604 // Execute main function
605 JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
606 EcmaRuntimeCallInfo *info =
607 EcmaInterpreter::NewRuntimeCallInfo(thread_,
608 JSHandle<JSTaggedValue>(func),
609 thisArg, undefined, 5); // 5 : argument numbers
610 RETURN_IF_ABRUPT_COMPLETION(thread_);
611 if (info == nullptr) {
612 LOG_ECMA(ERROR) << "CJSExecution Stack overflow!";
613 return;
614 }
615 info->SetCallArg(cjsInfo.exportsHdl.GetTaggedValue(),
616 cjsInfo.requireHdl.GetTaggedValue(),
617 cjsInfo.moduleHdl.GetTaggedValue(),
618 cjsInfo.filenameHdl.GetTaggedValue(),
619 cjsInfo.dirnameHdl.GetTaggedValue());
620 EcmaRuntimeStatScope runtimeStatScope(this);
621 EcmaInterpreter::Execute(info);
622 if (!thread_->HasPendingException()) {
623 job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
624 }
625
626 if (!thread_->HasPendingException()) {
627 // Collecting module.exports : exports ---> module.exports --->Module._cache
628 RequireManager::CollectExecutedExp(thread_, cjsInfo);
629 }
630 }
631
AddConstpool(const JSPandaFile * jsPandaFile,JSTaggedValue constpool,int32_t index)632 void EcmaVM::AddConstpool(const JSPandaFile *jsPandaFile, JSTaggedValue constpool, int32_t index)
633 {
634 ASSERT(constpool.IsConstantPool());
635 if (cachedConstpools_.find(jsPandaFile) == cachedConstpools_.end()) {
636 cachedConstpools_[jsPandaFile] = CMap<int32_t, JSTaggedValue>();
637 }
638 auto &constpoolMap = cachedConstpools_[jsPandaFile];
639 ASSERT(constpoolMap.find(index) == constpoolMap.end());
640
641 constpoolMap.insert({index, constpool});
642 }
643
GetAndClearEcmaUncaughtException() const644 JSHandle<JSTaggedValue> EcmaVM::GetAndClearEcmaUncaughtException() const
645 {
646 JSHandle<JSTaggedValue> exceptionHandle = GetEcmaUncaughtException();
647 thread_->ClearException(); // clear for ohos app
648 return exceptionHandle;
649 }
650
GetEcmaUncaughtException() const651 JSHandle<JSTaggedValue> EcmaVM::GetEcmaUncaughtException() const
652 {
653 if (!thread_->HasPendingException()) {
654 return JSHandle<JSTaggedValue>();
655 }
656 JSHandle<JSTaggedValue> exceptionHandle(thread_, thread_->GetException());
657 return exceptionHandle;
658 }
659
EnableUserUncaughtErrorHandler()660 void EcmaVM::EnableUserUncaughtErrorHandler()
661 {
662 isUncaughtExceptionRegistered_ = true;
663 }
664
HandleUncaughtException(TaggedObject * exception)665 void EcmaVM::HandleUncaughtException(TaggedObject *exception)
666 {
667 if (isUncaughtExceptionRegistered_) {
668 return;
669 }
670 [[maybe_unused]] EcmaHandleScope handleScope(thread_);
671 JSHandle<JSTaggedValue> exceptionHandle(thread_, JSTaggedValue(exception));
672 // if caught exceptionHandle type is JSError
673 thread_->ClearException();
674 if (exceptionHandle->IsJSError()) {
675 PrintJSErrorInfo(exceptionHandle);
676 if (thread_->IsPrintBCOffset() && exceptionBCList_.size() != 0) {
677 for (auto info : exceptionBCList_) {
678 LOG_ECMA(ERROR) << "Exception at function " << info.first << ": " << info.second;
679 }
680 }
681 return;
682 }
683 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread_, exceptionHandle);
684 CString string = ConvertToString(*result);
685 LOG_NO_TAG(ERROR) << string;
686 }
687
PrintJSErrorInfo(const JSHandle<JSTaggedValue> & exceptionInfo)688 void EcmaVM::PrintJSErrorInfo(const JSHandle<JSTaggedValue> &exceptionInfo)
689 {
690 JSHandle<JSTaggedValue> nameKey = thread_->GlobalConstants()->GetHandledNameString();
691 JSHandle<EcmaString> name(JSObject::GetProperty(thread_, exceptionInfo, nameKey).GetValue());
692 JSHandle<JSTaggedValue> msgKey = thread_->GlobalConstants()->GetHandledMessageString();
693 JSHandle<EcmaString> msg(JSObject::GetProperty(thread_, exceptionInfo, msgKey).GetValue());
694 JSHandle<JSTaggedValue> stackKey = thread_->GlobalConstants()->GetHandledStackString();
695 JSHandle<EcmaString> stack(JSObject::GetProperty(thread_, exceptionInfo, stackKey).GetValue());
696
697 CString nameBuffer = ConvertToString(*name);
698 CString msgBuffer = ConvertToString(*msg);
699 CString stackBuffer = ConvertToString(*stack);
700 LOG_NO_TAG(ERROR) << nameBuffer << ": " << msgBuffer << "\n" << stackBuffer;
701 }
702
ProcessNativeDelete(const WeakRootVisitor & visitor)703 void EcmaVM::ProcessNativeDelete(const WeakRootVisitor &visitor)
704 {
705 auto iter = nativePointerList_.begin();
706 while (iter != nativePointerList_.end()) {
707 JSNativePointer *object = *iter;
708 auto fwd = visitor(reinterpret_cast<TaggedObject *>(object));
709 if (fwd == nullptr) {
710 object->Destroy();
711 iter = nativePointerList_.erase(iter);
712 } else {
713 ++iter;
714 }
715 }
716
717 auto iterator = cachedConstpools_.begin();
718 while (iterator != cachedConstpools_.end()) {
719 auto &constpools = iterator->second;
720 auto constpoolIter = constpools.begin();
721 while (constpoolIter != constpools.end()) {
722 JSTaggedValue constpoolVal = constpoolIter->second;
723 if (constpoolVal.IsHeapObject()) {
724 TaggedObject *obj = constpoolVal.GetTaggedObject();
725 auto fwd = visitor(obj);
726 if (fwd == nullptr) {
727 constpoolIter = constpools.erase(constpoolIter);
728 continue;
729 }
730 }
731 ++constpoolIter;
732 }
733 if (constpools.size() == 0) {
734 JSPandaFileManager::RemoveJSPandaFile(const_cast<JSPandaFile *>(iterator->first));
735 iterator = cachedConstpools_.erase(iterator);
736 } else {
737 ++iterator;
738 }
739 }
740 }
ProcessReferences(const WeakRootVisitor & visitor)741 void EcmaVM::ProcessReferences(const WeakRootVisitor &visitor)
742 {
743 if (regExpParserCache_ != nullptr) {
744 regExpParserCache_->Clear();
745 }
746 heap_->ResetNativeBindingSize();
747 // array buffer
748 auto iter = nativePointerList_.begin();
749 while (iter != nativePointerList_.end()) {
750 JSNativePointer *object = *iter;
751 auto fwd = visitor(reinterpret_cast<TaggedObject *>(object));
752 if (fwd == nullptr) {
753 object->Destroy();
754 iter = nativePointerList_.erase(iter);
755 continue;
756 }
757 heap_->IncreaseNativeBindingSize(JSNativePointer::Cast(fwd));
758 if (fwd != reinterpret_cast<TaggedObject *>(object)) {
759 *iter = JSNativePointer::Cast(fwd);
760 }
761 ++iter;
762 }
763
764 // program maps
765 auto iterator = cachedConstpools_.begin();
766 while (iterator != cachedConstpools_.end()) {
767 auto &constpools = iterator->second;
768 auto constpoolIter = constpools.begin();
769 while (constpoolIter != constpools.end()) {
770 JSTaggedValue constpoolVal = constpoolIter->second;
771 if (constpoolVal.IsHeapObject()) {
772 TaggedObject *obj = constpoolVal.GetTaggedObject();
773 auto fwd = visitor(obj);
774 if (fwd == nullptr) {
775 constpoolIter = constpools.erase(constpoolIter);
776 continue;
777 } else if (fwd != obj) {
778 constpoolIter->second = JSTaggedValue(fwd);
779 }
780 }
781 ++constpoolIter;
782 }
783 if (constpools.size() == 0) {
784 JSPandaFileManager::RemoveJSPandaFile(const_cast<JSPandaFile *>(iterator->first));
785 iterator = cachedConstpools_.erase(iterator);
786 } else {
787 ++iterator;
788 }
789 }
790 }
791
PushToNativePointerList(JSNativePointer * array)792 void EcmaVM::PushToNativePointerList(JSNativePointer *array)
793 {
794 nativePointerList_.emplace_back(array);
795 }
796
RemoveFromNativePointerList(JSNativePointer * array)797 void EcmaVM::RemoveFromNativePointerList(JSNativePointer *array)
798 {
799 auto iter = std::find(nativePointerList_.begin(), nativePointerList_.end(), array);
800 if (iter != nativePointerList_.end()) {
801 JSNativePointer *object = *iter;
802 object->Destroy();
803 nativePointerList_.erase(iter);
804 }
805 }
806
ClearBufferData()807 void EcmaVM::ClearBufferData()
808 {
809 for (auto iter : nativePointerList_) {
810 iter->Destroy();
811 }
812 nativePointerList_.clear();
813
814 cachedConstpools_.clear();
815 internalNativeMethods_.clear();
816 WorkerList_.clear();
817 }
818
ExecutePromisePendingJob()819 bool EcmaVM::ExecutePromisePendingJob()
820 {
821 if (isProcessingPendingJob_) {
822 LOG_ECMA(DEBUG) << "EcmaVM::ExecutePromisePendingJob can not reentrant";
823 return false;
824 }
825 if (!thread_->HasPendingException()) {
826 isProcessingPendingJob_ = true;
827 job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
828 isProcessingPendingJob_ = false;
829 return true;
830 }
831 return false;
832 }
833
CollectGarbage(TriggerGCType gcType) const834 void EcmaVM::CollectGarbage(TriggerGCType gcType) const
835 {
836 heap_->CollectGarbage(gcType);
837 }
838
StartHeapTracking(HeapTracker * tracker)839 void EcmaVM::StartHeapTracking(HeapTracker *tracker)
840 {
841 heap_->StartHeapTracking(tracker);
842 }
843
StopHeapTracking()844 void EcmaVM::StopHeapTracking()
845 {
846 heap_->StopHeapTracking();
847 }
848
Iterate(const RootVisitor & v,const RootRangeVisitor & rv)849 void EcmaVM::Iterate(const RootVisitor &v, const RootRangeVisitor &rv)
850 {
851 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&globalEnv_)));
852 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(µJobQueue_)));
853 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(®expCache_)));
854 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&frameworkProgram_)));
855 rv(Root::ROOT_VM, ObjectSlot(ToUintPtr(&internalNativeMethods_.front())),
856 ObjectSlot(ToUintPtr(&internalNativeMethods_.back()) + JSTaggedValue::TaggedTypeSize()));
857 moduleManager_->Iterate(v);
858 tsManager_->Iterate(v);
859 aotFileManager_->Iterate(v);
860 if (!WIN_OR_MAC_OR_IOS_PLATFORM) {
861 snapshotEnv_->Iterate(v);
862 }
863 }
864
SetGlobalEnv(GlobalEnv * global)865 void EcmaVM::SetGlobalEnv(GlobalEnv *global)
866 {
867 ASSERT(global != nullptr);
868 globalEnv_ = JSTaggedValue(global);
869 }
870
SetMicroJobQueue(job::MicroJobQueue * queue)871 void EcmaVM::SetMicroJobQueue(job::MicroJobQueue *queue)
872 {
873 ASSERT(queue != nullptr);
874 microJobQueue_ = JSTaggedValue(queue);
875 }
876
SetupRegExpResultCache()877 void EcmaVM::SetupRegExpResultCache()
878 {
879 regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_);
880 }
881
LoadStubFile()882 void EcmaVM::LoadStubFile()
883 {
884 std::string stubFile = options_.GetStubFile();
885 aotFileManager_->LoadStubFile(stubFile);
886 }
887
LoadAOTFiles(const std::string & aotFileName)888 void EcmaVM::LoadAOTFiles(const std::string& aotFileName)
889 {
890 std::string anFile = aotFileName + AOTFileManager::FILE_EXTENSION_AN;
891 aotFileManager_->LoadAnFile(anFile);
892
893 std::string aiFile = aotFileName + AOTFileManager::FILE_EXTENSION_AI;
894 aotFileManager_->LoadAiFile(aiFile);
895 }
896
897 #if !WIN_OR_MAC_OR_IOS_PLATFORM
DeleteHeapProfile()898 void EcmaVM::DeleteHeapProfile()
899 {
900 if (heapProfile_ == nullptr) {
901 return;
902 }
903 const_cast<NativeAreaAllocator *>(GetNativeAreaAllocator())->Delete(heapProfile_);
904 heapProfile_ = nullptr;
905 }
906
GetOrNewHeapProfile()907 HeapProfilerInterface *EcmaVM::GetOrNewHeapProfile()
908 {
909 if (heapProfile_ != nullptr) {
910 return heapProfile_;
911 }
912 heapProfile_ = const_cast<NativeAreaAllocator *>(GetNativeAreaAllocator())->New<HeapProfiler>(this);
913 ASSERT(heapProfile_ != nullptr);
914 return heapProfile_;
915 }
916 #endif
917
918 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
919 void *EcmaVM::InternalMethodTable[] = {
920 reinterpret_cast<void *>(builtins::BuiltinsGlobal::CallJsBoundFunction),
921 reinterpret_cast<void *>(builtins::BuiltinsGlobal::CallJsProxy),
922 reinterpret_cast<void *>(builtins::BuiltinsObject::CreateDataPropertyOnObjectFunctions),
923 reinterpret_cast<void *>(builtins::BuiltinsCollator::AnonymousCollator),
924 reinterpret_cast<void *>(builtins::BuiltinsDateTimeFormat::AnonymousDateTimeFormat),
925 reinterpret_cast<void *>(builtins::BuiltinsNumberFormat::NumberFormatInternalFormatNumber),
926 reinterpret_cast<void *>(builtins::BuiltinsProxy::InvalidateProxyFunction),
927 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::AsyncAwaitFulfilled),
928 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::AsyncAwaitRejected),
929 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::ResolveElementFunction),
930 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::Resolve),
931 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::Reject),
932 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::Executor),
933 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::AnyRejectElementFunction),
934 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::AllSettledResolveElementFunction),
935 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::AllSettledRejectElementFunction),
936 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::ThenFinally),
937 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::CatchFinally),
938 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::valueThunkFunction),
939 reinterpret_cast<void *>(builtins::BuiltinsPromiseHandler::throwerFunction),
940 reinterpret_cast<void *>(JSAsyncGeneratorObject::ProcessorFulfilledFunc),
941 reinterpret_cast<void *>(JSAsyncGeneratorObject::ProcessorRejectedFunc),
942 reinterpret_cast<void *>(JSAsyncFromSyncIterator::AsyncFromSyncIterUnwarpFunction)
943 };
944
GenerateInternalNativeMethods()945 void EcmaVM::GenerateInternalNativeMethods()
946 {
947 size_t length = static_cast<size_t>(MethodIndex::METHOD_END);
948 for (size_t i = 0; i < length; i++) {
949 uint32_t numArgs = 2; // function object and this
950 auto method = factory_->NewMethod(nullptr);
951 method->SetNativePointer(InternalMethodTable[i]);
952 method->SetNativeBit(true);
953 method->SetNumArgsWithCallField(numArgs);
954 method->SetFunctionKind(FunctionKind::NORMAL_FUNCTION);
955 internalNativeMethods_.emplace_back(method.GetTaggedValue());
956 }
957 }
958
GetMethodByIndex(MethodIndex idx)959 JSTaggedValue EcmaVM::GetMethodByIndex(MethodIndex idx)
960 {
961 auto index = static_cast<uint8_t>(idx);
962 ASSERT(index < internalNativeMethods_.size());
963 return internalNativeMethods_[index];
964 }
965
TriggerConcurrentCallback(JSTaggedValue result,JSTaggedValue hint)966 void EcmaVM::TriggerConcurrentCallback(JSTaggedValue result, JSTaggedValue hint)
967 {
968 if (concurrentCallback_ == nullptr) {
969 LOG_ECMA(INFO) << "Only trigger concurrent callback in taskpool thread";
970 return;
971 }
972
973 if (result.IsJSPromise()) {
974 // Async concurrent will return Promise
975 auto promise = JSPromise::Cast(result.GetTaggedObject());
976 if (promise->GetPromiseState() == PromiseState::PENDING) {
977 LOG_ECMA(ERROR) << "Promise is in pending state, don't return";
978 return;
979 }
980 result = promise->GetPromiseResult();
981 }
982
983 auto ret = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread_, result));
984 auto data = JSNApiHelper::ToLocal<JSValueRef>(JSHandle<JSTaggedValue>(thread_, hint));
985 concurrentCallback_(ret, data, concurrentData_);
986 }
987 } // namespace panda::ecmascript
988