1 /*
2 * Copyright (c) 2021 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/string_helper.h"
19 #include "ecmascript/builtins.h"
20 #include "ecmascript/builtins/builtins_regexp.h"
21 #include "ecmascript/class_linker/panda_file_translator.h"
22 #include "ecmascript/ecma_macros.h"
23 #include "ecmascript/jspandafile/program_object-inl.h"
24 #include "ecmascript/cpu_profiler/cpu_profiler.h"
25 #include "ecmascript/ecma_module.h"
26 #include "ecmascript/ecma_string_table.h"
27 #include "ecmascript/global_dictionary.h"
28 #include "ecmascript/global_env.h"
29 #include "ecmascript/global_env_constants-inl.h"
30 #include "ecmascript/global_env_constants.h"
31 #include "ecmascript/internal_call_params.h"
32 #include "ecmascript/jobs/micro_job_queue.h"
33 #include "ecmascript/jspandafile/js_pandafile.h"
34 #include "ecmascript/jspandafile/js_pandafile_manager.h"
35 #include "ecmascript/js_arraybuffer.h"
36 #include "ecmascript/js_for_in_iterator.h"
37 #include "ecmascript/js_invoker.h"
38 #include "ecmascript/js_native_pointer.h"
39 #include "ecmascript/js_thread.h"
40 #include "ecmascript/mem/concurrent_marker.h"
41 #include "ecmascript/mem/heap.h"
42 #include "ecmascript/object_factory.h"
43 #include "ecmascript/platform/platform.h"
44 #include "ecmascript/regexp/regexp_parser_cache.h"
45 #include "ecmascript/runtime_call_id.h"
46 #include "ecmascript/runtime_trampolines.h"
47 #include "ecmascript/snapshot/mem/slot_bit.h"
48 #include "ecmascript/snapshot/mem/snapshot.h"
49 #include "ecmascript/snapshot/mem/snapshot_serialize.h"
50 #include "ecmascript/symbol_table.h"
51 #include "ecmascript/tagged_array-inl.h"
52 #include "ecmascript/tagged_dictionary.h"
53 #include "ecmascript/tagged_queue-inl.h"
54 #include "ecmascript/tagged_queue.h"
55 #include "ecmascript/template_map.h"
56 #include "ecmascript/tooling/interface/js_debugger_manager.h"
57 #include "ecmascript/ts_types/ts_loader.h"
58 #include "ecmascript/vmstat/runtime_stat.h"
59 #include "libpandafile/file.h"
60
61 namespace panda::ecmascript {
62 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
63 static const std::string_view ENTRY_POINTER = "_GLOBAL::func_main_0";
64 JSRuntimeOptions EcmaVM::options_; // NOLINT(fuchsia-statically-constructed-objects)
65
66 /* static */
Create(const JSRuntimeOptions & options)67 EcmaVM *EcmaVM::Create(const JSRuntimeOptions &options)
68 {
69 auto runtime = Runtime::GetCurrent();
70 auto vm = runtime->GetInternalAllocator()->New<EcmaVM>(options);
71 if (UNLIKELY(vm == nullptr)) {
72 LOG_ECMA(ERROR) << "Failed to create jsvm";
73 return nullptr;
74 }
75 auto jsThread = JSThread::Create(runtime, vm);
76 vm->thread_ = jsThread;
77 vm->Initialize();
78 return vm;
79 }
80
81 // static
Destroy(PandaVM * vm)82 bool EcmaVM::Destroy(PandaVM *vm)
83 {
84 if (vm != nullptr) {
85 auto runtime = Runtime::GetCurrent();
86 runtime->GetInternalAllocator()->Delete(vm);
87 return true;
88 }
89 return false;
90 }
91
92 // static
Create(Runtime * runtime)93 Expected<EcmaVM *, CString> EcmaVM::Create(Runtime *runtime)
94 {
95 EcmaVM *vm = runtime->GetInternalAllocator()->New<EcmaVM>();
96 auto jsThread = ecmascript::JSThread::Create(runtime, vm);
97 vm->thread_ = jsThread;
98 return vm;
99 }
100
101 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
EcmaVM()102 EcmaVM::EcmaVM() : EcmaVM(EcmaVM::GetJSOptions())
103 {
104 isTestMode_ = true;
105 }
106
EcmaVM(JSRuntimeOptions options)107 EcmaVM::EcmaVM(JSRuntimeOptions options)
108 : stringTable_(new EcmaStringTable(this)),
109 nativeAreaAllocator_(std::make_unique<NativeAreaAllocator>()),
110 heapRegionAllocator_(std::make_unique<HeapRegionAllocator>()),
111 chunk_(nativeAreaAllocator_.get()),
112 arrayBufferDataList_(&chunk_),
113 frameworkProgramMethods_(&chunk_),
114 nativeMethodMaps_(&chunk_)
115 {
116 options_ = std::move(options);
117 icEnable_ = options_.IsIcEnable();
118 optionalLogEnabled_ = options_.IsEnableOptionalLog();
119 snapshotSerializeEnable_ = options_.IsSnapshotSerializeEnabled();
120 if (!snapshotSerializeEnable_) {
121 snapshotDeserializeEnable_ = options_.IsSnapshotDeserializeEnabled();
122 }
123 snapshotFileName_ = options_.GetSnapshotFile();
124 frameworkAbcFileName_ = options_.GetFrameworkAbcFile();
125
126 debuggerManager_ = chunk_.New<tooling::JsDebuggerManager>();
127 }
128
Initialize()129 bool EcmaVM::Initialize()
130 {
131 ECMA_BYTRACE_NAME(BYTRACE_TAG_ARK, "EcmaVM::Initialize");
132 Platform::GetCurrentPlatform()->Initialize();
133 RuntimeTrampolines::InitializeRuntimeTrampolines(thread_);
134
135 auto globalConst = const_cast<GlobalEnvConstants *>(thread_->GlobalConstants());
136 regExpParserCache_ = new RegExpParserCache();
137 heap_ = new Heap(this);
138 heap_->Initialize();
139 gcStats_ = chunk_.New<GCStats>(heap_);
140 factory_ = chunk_.New<ObjectFactory>(thread_, heap_);
141 if (UNLIKELY(factory_ == nullptr)) {
142 LOG_ECMA(FATAL) << "alloc factory_ failed";
143 UNREACHABLE();
144 }
145
146 [[maybe_unused]] EcmaHandleScope scope(thread_);
147 if (!snapshotDeserializeEnable_ || !VerifyFilePath(snapshotFileName_)) {
148 LOG_ECMA(DEBUG) << "EcmaVM::Initialize run builtins";
149
150 JSHandle<JSHClass> dynClassClassHandle =
151 factory_->NewEcmaDynClassClass(nullptr, JSHClass::SIZE, JSType::HCLASS);
152 JSHClass *dynclass = reinterpret_cast<JSHClass *>(dynClassClassHandle.GetTaggedValue().GetTaggedObject());
153 dynclass->SetClass(dynclass);
154 JSHandle<JSHClass> globalEnvClass =
155 factory_->NewEcmaDynClass(*dynClassClassHandle, GlobalEnv::SIZE, JSType::GLOBAL_ENV);
156
157 JSHandle<GlobalEnv> globalEnvHandle = factory_->NewGlobalEnv(*globalEnvClass);
158 globalEnv_ = globalEnvHandle.GetTaggedValue();
159 auto globalEnv = GlobalEnv::Cast(globalEnv_.GetTaggedObject());
160
161 // init global env
162 globalConst->InitRootsClass(thread_, *dynClassClassHandle);
163 globalConst->InitGlobalConstant(thread_);
164 globalEnv->SetEmptyArray(thread_, factory_->NewEmptyArray());
165 globalEnv->SetEmptyLayoutInfo(thread_, factory_->CreateLayoutInfo(0));
166 globalEnv->SetRegisterSymbols(thread_, SymbolTable::Create(thread_));
167 globalEnv->SetGlobalRecord(thread_, GlobalDictionary::Create(thread_));
168 JSTaggedValue emptyStr = thread_->GlobalConstants()->GetEmptyString();
169 stringTable_->InternEmptyString(EcmaString::Cast(emptyStr.GetTaggedObject()));
170 globalEnv->SetEmptyTaggedQueue(thread_, factory_->NewTaggedQueue(0));
171 globalEnv->SetTemplateMap(thread_, TemplateMap::Create(thread_));
172 globalEnv->SetRegisterSymbols(GetJSThread(), SymbolTable::Create(GetJSThread()));
173 #ifdef ECMASCRIPT_ENABLE_STUB_AOT
174 std::string moduleFile = options_.GetStubModuleFile();
175 thread_->LoadStubModule(moduleFile.c_str());
176 #endif
177 SetupRegExpResultCache();
178 microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue();
179 {
180 Builtins builtins;
181 builtins.Initialize(globalEnvHandle, thread_);
182 }
183 } else {
184 LOG_ECMA(DEBUG) << "EcmaVM::Initialize run snapshot";
185 SnapShot snapShot(this);
186 std::unique_ptr<const panda_file::File> pf = snapShot.DeserializeGlobalEnvAndProgram(snapshotFileName_);
187 frameworkPandaFile_ = JSPandaFileManager::GetInstance()->CreateJSPandaFile(pf.release(), frameworkAbcFileName_);
188 globalConst->InitGlobalUndefined();
189 }
190
191 thread_->SetGlobalObject(GetGlobalEnv()->GetGlobalObject());
192 moduleManager_ = new ModuleManager(this);
193 tsLoader_ = new TSLoader(this);
194 debuggerManager_->Initialize();
195 InitializeFinish();
196 return true;
197 }
198
InitializeEcmaScriptRunStat()199 void EcmaVM::InitializeEcmaScriptRunStat()
200 {
201 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
202 static const char *runtimeCallerNames[] = {
203 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
204 #define INTERPRETER_CALLER_NAME(name) "Interpreter::" #name,
205 INTERPRETER_CALLER_LIST(INTERPRETER_CALLER_NAME) // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
206 #undef INTERPRETER_CALLER_NAME
207 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
208 #define BUILTINS_API_NAME(class, name) "BuiltinsApi::" #class "_" #name,
209 BUITINS_API_LIST(BUILTINS_API_NAME)
210 #undef BUILTINS_API_NAME
211 #define ABSTRACT_OPERATION_NAME(class, name) "AbstractOperation::" #class "_" #name,
212 ABSTRACT_OPERATION_LIST(ABSTRACT_OPERATION_NAME)
213 #undef ABSTRACT_OPERATION_NAME
214 #define MEM_ALLOCATE_AND_GC_NAME(name) "Memory::" #name,
215 MEM_ALLOCATE_AND_GC_LIST(MEM_ALLOCATE_AND_GC_NAME)
216 #undef MEM_ALLOCATE_AND_GC_NAME
217 };
218 static_assert(sizeof(runtimeCallerNames) == sizeof(const char *) * ecmascript::RUNTIME_CALLER_NUMBER,
219 "Invalid runtime caller number");
220 runtimeStat_ = chunk_.New<EcmaRuntimeStat>(runtimeCallerNames, ecmascript::RUNTIME_CALLER_NUMBER);
221 if (UNLIKELY(runtimeStat_ == nullptr)) {
222 LOG_ECMA(FATAL) << "alloc runtimeStat_ failed";
223 UNREACHABLE();
224 }
225 }
226
SetRuntimeStatEnable(bool flag)227 void EcmaVM::SetRuntimeStatEnable(bool flag)
228 {
229 if (flag) {
230 if (runtimeStat_ == nullptr) {
231 InitializeEcmaScriptRunStat();
232 }
233 } else {
234 if (runtimeStatEnabled_) {
235 runtimeStat_->Print();
236 runtimeStat_->ResetAllCount();
237 }
238 }
239 runtimeStatEnabled_ = flag;
240 }
241
InitializeFinish()242 bool EcmaVM::InitializeFinish()
243 {
244 vmInitialized_ = true;
245 return true;
246 }
247
~EcmaVM()248 EcmaVM::~EcmaVM()
249 {
250 vmInitialized_ = false;
251 Platform::GetCurrentPlatform()->Destroy();
252 ClearNativeMethodsData();
253
254 if (runtimeStat_ != nullptr && runtimeStatEnabled_) {
255 runtimeStat_->Print();
256 }
257
258 // clear c_address: c++ pointer delete
259 ClearBufferData();
260
261 // clear icu cache
262 ClearIcuCache();
263
264 if (gcStats_ != nullptr) {
265 if (options_.IsEnableGCStatsPrint()) {
266 gcStats_->PrintStatisticResult(true);
267 }
268 chunk_.Delete(gcStats_);
269 gcStats_ = nullptr;
270 }
271
272 if (heap_ != nullptr) {
273 heap_->Destroy();
274 delete heap_;
275 heap_ = nullptr;
276 }
277
278 delete regExpParserCache_;
279 regExpParserCache_ = nullptr;
280
281 if (debuggerManager_ != nullptr) {
282 chunk_.Delete(debuggerManager_);
283 debuggerManager_ = nullptr;
284 }
285
286 if (factory_ != nullptr) {
287 chunk_.Delete(factory_);
288 factory_ = nullptr;
289 }
290
291 if (stringTable_ != nullptr) {
292 delete stringTable_;
293 stringTable_ = nullptr;
294 }
295
296 if (runtimeStat_ != nullptr) {
297 chunk_.Delete(runtimeStat_);
298 runtimeStat_ = nullptr;
299 }
300
301 if (moduleManager_ != nullptr) {
302 delete moduleManager_;
303 moduleManager_ = nullptr;
304 }
305
306 if (tsLoader_ != nullptr) {
307 delete tsLoader_;
308 tsLoader_ = nullptr;
309 }
310
311 if (thread_ != nullptr) {
312 delete thread_;
313 thread_ = nullptr;
314 }
315
316 frameworkProgramMethods_.clear();
317 }
318
ExecuteFromPf(const std::string & filename,std::string_view entryPoint,const std::vector<std::string> & args,bool isModule)319 bool EcmaVM::ExecuteFromPf(const std::string &filename, std::string_view entryPoint,
320 const std::vector<std::string> &args, bool isModule)
321 {
322 const JSPandaFile *jsPandaFile = nullptr;
323 if (frameworkPandaFile_ == nullptr || !IsFrameworkPandaFile(filename)) {
324 jsPandaFile = JSPandaFileManager::GetInstance()->LoadPfAbc(filename);
325 if (jsPandaFile == nullptr) {
326 return false;
327 }
328 } else {
329 jsPandaFile = frameworkPandaFile_;
330 }
331 return Execute(jsPandaFile, entryPoint, args);
332 }
333
CollectInfoOfPandaFile(const std::string & filename,std::string_view entryPoint,std::vector<BytecodeTranslationInfo> * infoList,const panda_file::File * & pf)334 bool EcmaVM::CollectInfoOfPandaFile(const std::string &filename, std::string_view entryPoint,
335 std::vector<BytecodeTranslationInfo> *infoList, const panda_file::File *&pf)
336 {
337 const JSPandaFile *jsPandaFile;
338 if (frameworkPandaFile_ == nullptr || !IsFrameworkPandaFile(filename)) {
339 jsPandaFile = JSPandaFileManager::GetInstance()->LoadPfAbc(filename);
340 if (jsPandaFile == nullptr) {
341 return false;
342 }
343 } else {
344 jsPandaFile = frameworkPandaFile_;
345 }
346
347 // Get ClassName and MethodName
348 size_t pos = entryPoint.find_last_of("::");
349 if (pos == std::string_view::npos) {
350 LOG_ECMA(ERROR) << "EntryPoint:" << entryPoint << " is illegal";
351 return false;
352 }
353 CString methodName(entryPoint.substr(pos + 1));
354
355 PandaFileTranslator translator(this, jsPandaFile);
356 translator.TranslateAndCollectPandaFile(methodName, infoList);
357 return true;
358 }
359
ExecuteFromBuffer(const void * buffer,size_t size,std::string_view entryPoint,const std::vector<std::string> & args,const std::string & filename)360 bool EcmaVM::ExecuteFromBuffer(const void *buffer, size_t size, std::string_view entryPoint,
361 const std::vector<std::string> &args, const std::string &filename)
362 {
363 // Get ClassName and MethodName
364 size_t pos = entryPoint.find_last_of("::");
365 if (pos == std::string_view::npos) {
366 LOG_ECMA(ERROR) << "EntryPoint:" << entryPoint << " is illegal";
367 return false;
368 }
369 CString methodName(entryPoint.substr(pos + 1));
370 const JSPandaFile *jsPandaFile = JSPandaFileManager::GetInstance()->LoadBufferAbc(filename, buffer, size);
371 if (jsPandaFile == nullptr) {
372 return false;
373 }
374 InvokeEcmaEntrypoint(jsPandaFile, methodName, args);
375 return true;
376 }
377
Execute(const JSPandaFile * jsPandaFile,std::string_view entryPoint,const std::vector<std::string> & args)378 bool EcmaVM::Execute(const JSPandaFile *jsPandaFile, std::string_view entryPoint, const std::vector<std::string> &args)
379 {
380 // Get ClassName and MethodName
381 size_t pos = entryPoint.find_last_of("::");
382 if (pos == std::string_view::npos) {
383 LOG_ECMA(ERROR) << "EntryPoint:" << entryPoint << " is illegal";
384 return false;
385 }
386 CString methodName(entryPoint.substr(pos + 1));
387 // For Ark application startup
388 InvokeEcmaEntrypoint(jsPandaFile, methodName, args);
389 return true;
390 }
391
GetGlobalEnv() const392 JSHandle<GlobalEnv> EcmaVM::GetGlobalEnv() const
393 {
394 return JSHandle<GlobalEnv>(reinterpret_cast<uintptr_t>(&globalEnv_));
395 }
396
GetMicroJobQueue() const397 JSHandle<job::MicroJobQueue> EcmaVM::GetMicroJobQueue() const
398 {
399 return JSHandle<job::MicroJobQueue>(reinterpret_cast<uintptr_t>(µJobQueue_));
400 }
401
GetMethodForNativeFunction(const void * func)402 JSMethod *EcmaVM::GetMethodForNativeFunction(const void *func)
403 {
404 // signature: any foo(any function_obj, any this)
405 uint32_t accessFlags = ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_NATIVE;
406 uint32_t numArgs = 2; // function object and this
407
408 auto iter = nativeMethodMaps_.find(func);
409 if (iter != nativeMethodMaps_.end()) {
410 return iter->second;
411 }
412 auto method = chunk_.New<JSMethod>(nullptr, panda_file::File::EntityId(0), panda_file::File::EntityId(0),
413 accessFlags, numArgs, nullptr);
414 method->SetNativePointer(const_cast<void *>(func));
415 method->SetNativeBit(true);
416
417 nativeMethodMaps_.emplace(func, method);
418 return method;
419 }
420
FindConstpool(const JSPandaFile * jsPandaFile)421 JSTaggedValue EcmaVM::FindConstpool(const JSPandaFile *jsPandaFile)
422 {
423 auto iter = pandaFileWithConstpool_.find(jsPandaFile);
424 if (iter == pandaFileWithConstpool_.end()) {
425 return JSTaggedValue::Hole();
426 }
427 return iter->second;
428 }
429
SetConstpool(const JSPandaFile * jsPandaFile,JSTaggedValue constpool)430 void EcmaVM::SetConstpool(const JSPandaFile *jsPandaFile, JSTaggedValue constpool)
431 {
432 ASSERT(constpool.IsTaggedArray());
433 ASSERT(pandaFileWithConstpool_.find(jsPandaFile) == pandaFileWithConstpool_.end());
434
435 pandaFileWithConstpool_[jsPandaFile] = constpool;
436 }
437
RedirectMethod(const panda_file::File & pf)438 void EcmaVM::RedirectMethod(const panda_file::File &pf)
439 {
440 for (auto method : frameworkProgramMethods_) {
441 method->SetPandaFile(&pf);
442 }
443 }
444
InvokeEntrypointImpl(Method * entrypoint,const std::vector<std::string> & args)445 Expected<int, Runtime::Error> EcmaVM::InvokeEntrypointImpl(Method *entrypoint, const std::vector<std::string> &args)
446 {
447 // For testcase startup
448 const panda_file::File *file = entrypoint->GetPandaFile();
449 const JSPandaFile *jsPandaFile = JSPandaFileManager::GetInstance()->GetJSPandaFile(file);
450 ASSERT(jsPandaFile != nullptr);
451 return InvokeEcmaEntrypoint(jsPandaFile, utf::Mutf8AsCString(entrypoint->GetName().data), args);
452 }
453
InvokeEcmaEntrypoint(const JSPandaFile * jsPandaFile,const CString & methodName,const std::vector<std::string> & args)454 Expected<int, Runtime::Error> EcmaVM::InvokeEcmaEntrypoint(const JSPandaFile *jsPandaFile,
455 [[maybe_unused]] const CString &methodName,
456 const std::vector<std::string> &args)
457 {
458 [[maybe_unused]] EcmaHandleScope scope(thread_);
459 JSHandle<Program> program;
460 if (snapshotSerializeEnable_) {
461 program = JSPandaFileManager::GetInstance()->GenerateProgram(this, jsPandaFile);
462
463 auto index = jsPandaFile->GetJSPandaFileDesc().find(frameworkAbcFileName_);
464 if (index != CString::npos) {
465 LOG_ECMA(DEBUG) << "snapShot MakeSnapShotProgramObject abc " << jsPandaFile->GetJSPandaFileDesc();
466 SnapShot snapShot(this);
467 snapShot.MakeSnapShotProgramObject(*program, jsPandaFile->GetPandaFile(), snapshotFileName_);
468 }
469 } else {
470 if (jsPandaFile != frameworkPandaFile_) {
471 program = JSPandaFileManager::GetInstance()->GenerateProgram(this, jsPandaFile);
472 } else {
473 program = JSHandle<Program>(thread_, frameworkProgram_);
474 RedirectMethod(*jsPandaFile->GetPandaFile());
475 }
476 }
477
478 if (program.IsEmpty()) {
479 LOG_ECMA(ERROR) << "program is empty, invoke entrypoint failed";
480 return Unexpected(Runtime::Error::PANDA_FILE_LOAD_ERROR);
481 }
482 // for debugger
483 debuggerManager_->GetNotificationManager()->LoadModuleEvent(jsPandaFile->GetJSPandaFileDesc());
484
485 JSHandle<JSFunction> func = JSHandle<JSFunction>(thread_, program->GetMainFunction());
486 JSHandle<JSTaggedValue> global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject();
487 JSHandle<JSTaggedValue> newTarget(thread_, JSTaggedValue::Undefined());
488 JSHandle<TaggedArray> jsargs = factory_->NewTaggedArray(args.size());
489 uint32_t i = 0;
490 for (const std::string &str : args) {
491 JSHandle<JSTaggedValue> strobj(factory_->NewFromStdString(str));
492 jsargs->Set(thread_, i++, strobj);
493 }
494
495 InternalCallParams *params = thread_->GetInternalCallParams();
496 params->MakeArgList(*jsargs);
497 JSRuntimeOptions options = this->GetJSOptions();
498 if (options.IsEnableCpuProfiler()) {
499 CpuProfiler *profiler = CpuProfiler::GetInstance();
500 profiler->CpuProfiler::StartCpuProfiler(this, "");
501 panda::ecmascript::InvokeJsFunction(thread_, func, global, newTarget, params);
502 profiler->CpuProfiler::StopCpuProfiler();
503 } else {
504 panda::ecmascript::InvokeJsFunction(thread_, func, global, newTarget, params);
505 }
506 if (!thread_->HasPendingException()) {
507 job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
508 }
509
510 // print exception information
511 if (thread_->HasPendingException()) {
512 auto exception = thread_->GetException();
513 HandleUncaughtException(exception.GetTaggedObject());
514 }
515 return 0;
516 }
517
IsFrameworkPandaFile(std::string_view filename) const518 bool EcmaVM::IsFrameworkPandaFile(std::string_view filename) const
519 {
520 return filename.size() >= frameworkAbcFileName_.size() &&
521 filename.substr(filename.size() - frameworkAbcFileName_.size()) == frameworkAbcFileName_;
522 }
523
GetAndClearEcmaUncaughtException() const524 JSHandle<JSTaggedValue> EcmaVM::GetAndClearEcmaUncaughtException() const
525 {
526 JSHandle<JSTaggedValue> exceptionHandle = GetEcmaUncaughtException();
527 thread_->ClearException(); // clear for ohos app
528 return exceptionHandle;
529 }
530
GetEcmaUncaughtException() const531 JSHandle<JSTaggedValue> EcmaVM::GetEcmaUncaughtException() const
532 {
533 if (thread_->GetException().IsHole()) {
534 return JSHandle<JSTaggedValue>();
535 }
536 JSHandle<JSTaggedValue> exceptionHandle(thread_, thread_->GetException());
537 return exceptionHandle;
538 }
539
EnableUserUncaughtErrorHandler()540 void EcmaVM::EnableUserUncaughtErrorHandler()
541 {
542 isUncaughtExceptionRegistered_ = true;
543 }
544
HandleUncaughtException(ObjectHeader * exception)545 void EcmaVM::HandleUncaughtException(ObjectHeader *exception)
546 {
547 if (isUncaughtExceptionRegistered_) {
548 return;
549 }
550 [[maybe_unused]] EcmaHandleScope handle_scope(thread_);
551 JSHandle<JSTaggedValue> exceptionHandle(thread_, JSTaggedValue(exception));
552 // if caught exceptionHandle type is JSError
553 thread_->ClearException();
554 if (exceptionHandle->IsJSError()) {
555 PrintJSErrorInfo(exceptionHandle);
556 return;
557 }
558 JSHandle<EcmaString> result = JSTaggedValue::ToString(thread_, exceptionHandle);
559 CString string = ConvertToString(*result);
560 LOG(ERROR, RUNTIME) << string;
561 }
562
PrintJSErrorInfo(const JSHandle<JSTaggedValue> & exceptionInfo)563 void EcmaVM::PrintJSErrorInfo(const JSHandle<JSTaggedValue> &exceptionInfo)
564 {
565 JSHandle<JSTaggedValue> nameKey = thread_->GlobalConstants()->GetHandledNameString();
566 JSHandle<EcmaString> name(JSObject::GetProperty(thread_, exceptionInfo, nameKey).GetValue());
567 JSHandle<JSTaggedValue> msgKey = thread_->GlobalConstants()->GetHandledMessageString();
568 JSHandle<EcmaString> msg(JSObject::GetProperty(thread_, exceptionInfo, msgKey).GetValue());
569 JSHandle<JSTaggedValue> stackKey = thread_->GlobalConstants()->GetHandledStackString();
570 JSHandle<EcmaString> stack(JSObject::GetProperty(thread_, exceptionInfo, stackKey).GetValue());
571
572 CString nameBuffer = ConvertToString(*name);
573 CString msgBuffer = ConvertToString(*msg);
574 CString stackBuffer = ConvertToString(*stack);
575 LOG(ERROR, RUNTIME) << nameBuffer << ": " << msgBuffer << "\n" << stackBuffer;
576 }
577
ProcessNativeDelete(const WeakRootVisitor & v0)578 void EcmaVM::ProcessNativeDelete(const WeakRootVisitor &v0)
579 {
580 auto iter = arrayBufferDataList_.begin();
581 while (iter != arrayBufferDataList_.end()) {
582 JSNativePointer *object = *iter;
583 auto fwd = v0(reinterpret_cast<TaggedObject *>(object));
584 if (fwd == nullptr) {
585 object->Destroy();
586 iter = arrayBufferDataList_.erase(iter);
587 } else {
588 ++iter;
589 }
590 }
591 }
ProcessReferences(const WeakRootVisitor & v0)592 void EcmaVM::ProcessReferences(const WeakRootVisitor &v0)
593 {
594 if (regExpParserCache_ != nullptr) {
595 regExpParserCache_->Clear();
596 }
597
598 // array buffer
599 for (auto iter = arrayBufferDataList_.begin(); iter != arrayBufferDataList_.end();) {
600 JSNativePointer *object = *iter;
601 auto fwd = v0(reinterpret_cast<TaggedObject *>(object));
602 if (fwd == nullptr) {
603 object->Destroy();
604 iter = arrayBufferDataList_.erase(iter);
605 continue;
606 }
607 if (fwd != reinterpret_cast<TaggedObject *>(object)) {
608 *iter = JSNativePointer::Cast(fwd);
609 }
610 ++iter;
611 }
612
613 // framework program
614 if (!frameworkProgram_.IsHole()) {
615 auto fwd = v0(frameworkProgram_.GetTaggedObject());
616 if (fwd == nullptr) {
617 frameworkProgram_ = JSTaggedValue::Undefined();
618 } else if (fwd != frameworkProgram_.GetTaggedObject()) {
619 frameworkProgram_ = JSTaggedValue(fwd);
620 }
621 }
622
623 for (auto iter = pandaFileWithConstpool_.begin(); iter != pandaFileWithConstpool_.end();) {
624 auto object = iter->second;
625 if (object.IsObject()) {
626 TaggedObject *obj = object.GetTaggedObject();
627 auto fwd = v0(obj);
628 if (fwd == nullptr) {
629 iter = pandaFileWithConstpool_.erase(iter);
630 continue;
631 } else if (fwd != obj) {
632 iter->second = JSTaggedValue(fwd);
633 }
634 }
635 ++iter;
636 }
637 }
638
PushToArrayDataList(JSNativePointer * array)639 void EcmaVM::PushToArrayDataList(JSNativePointer *array)
640 {
641 if (std::find(arrayBufferDataList_.begin(), arrayBufferDataList_.end(), array) != arrayBufferDataList_.end()) {
642 return;
643 }
644 arrayBufferDataList_.emplace_back(array);
645 }
646
RemoveArrayDataList(JSNativePointer * array)647 void EcmaVM::RemoveArrayDataList(JSNativePointer *array)
648 {
649 auto iter = std::find(arrayBufferDataList_.begin(), arrayBufferDataList_.end(), array);
650 if (iter != arrayBufferDataList_.end()) {
651 arrayBufferDataList_.erase(iter);
652 }
653 }
654
VerifyFilePath(const CString & filePath) const655 bool EcmaVM::VerifyFilePath(const CString &filePath) const
656 {
657 if (filePath.size() > PATH_MAX) {
658 return false;
659 }
660
661 CVector<char> resolvedPath(PATH_MAX);
662 auto result = realpath(filePath.c_str(), resolvedPath.data());
663 if (result == nullptr) {
664 return false;
665 }
666 std::ifstream file(resolvedPath.data());
667 if (!file.good()) {
668 return false;
669 }
670 file.close();
671 return true;
672 }
673
ClearBufferData()674 void EcmaVM::ClearBufferData()
675 {
676 for (auto iter : arrayBufferDataList_) {
677 iter->Destroy();
678 }
679 arrayBufferDataList_.clear();
680
681 pandaFileWithConstpool_.clear();
682
683 if (frameworkPandaFile_) {
684 delete frameworkPandaFile_;
685 frameworkPandaFile_ = nullptr;
686 }
687 }
688
ExecutePromisePendingJob() const689 bool EcmaVM::ExecutePromisePendingJob() const
690 {
691 thread_local bool isProcessing = false;
692 if (!isProcessing && !thread_->HasPendingException()) {
693 isProcessing = true;
694 job::MicroJobQueue::ExecutePendingJob(thread_, GetMicroJobQueue());
695 isProcessing = false;
696 return true;
697 }
698 return false;
699 }
700
CollectGarbage(TriggerGCType gcType) const701 void EcmaVM::CollectGarbage(TriggerGCType gcType) const
702 {
703 heap_->CollectGarbage(gcType);
704 }
705
StartHeapTracking(HeapTracker * tracker)706 void EcmaVM::StartHeapTracking(HeapTracker *tracker)
707 {
708 heap_->StartHeapTracking(tracker);
709 }
710
StopHeapTracking()711 void EcmaVM::StopHeapTracking()
712 {
713 heap_->StopHeapTracking();
714 }
715
Iterate(const RootVisitor & v)716 void EcmaVM::Iterate(const RootVisitor &v)
717 {
718 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&globalEnv_)));
719 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(µJobQueue_)));
720 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(®expCache_)));
721 moduleManager_->Iterate(v);
722 tsLoader_->Iterate(v);
723 }
724
SetGlobalEnv(GlobalEnv * global)725 void EcmaVM::SetGlobalEnv(GlobalEnv *global)
726 {
727 ASSERT(global != nullptr);
728 globalEnv_ = JSTaggedValue(global);
729 }
730
SetMicroJobQueue(job::MicroJobQueue * queue)731 void EcmaVM::SetMicroJobQueue(job::MicroJobQueue *queue)
732 {
733 ASSERT(queue != nullptr);
734 microJobQueue_ = JSTaggedValue(queue);
735 }
736
GetModuleByName(JSHandle<JSTaggedValue> moduleName)737 JSHandle<JSTaggedValue> EcmaVM::GetModuleByName(JSHandle<JSTaggedValue> moduleName)
738 {
739 // only used in testcase, pandaFileWithProgram_ only one item. this interface will delete
740 const JSPandaFile *currentJSPandaFile = nullptr;
741 JSPandaFileManager::GetInstance()->EnumerateJSPandaFiles([¤tJSPandaFile](const JSPandaFile *jsPandaFile) {
742 currentJSPandaFile = jsPandaFile;
743 return false;
744 });
745 ASSERT(currentJSPandaFile != nullptr);
746 CString currentPathFile = currentJSPandaFile->GetJSPandaFileDesc();
747 CString relativeFile = ConvertToString(EcmaString::Cast(moduleName->GetTaggedObject()));
748
749 // generate full path
750 CString abcPath = moduleManager_->GenerateAmiPath(currentPathFile, relativeFile);
751
752 // Uniform module name
753 JSHandle<EcmaString> abcModuleName = factory_->NewFromString(abcPath);
754
755 JSHandle<JSTaggedValue> module = moduleManager_->GetModule(thread_, JSHandle<JSTaggedValue>::Cast(abcModuleName));
756 if (module->IsUndefined()) {
757 std::string file = base::StringHelper::ToStdString(abcModuleName.GetObject<EcmaString>());
758 std::vector<std::string> argv;
759 ExecuteModule(file, ENTRY_POINTER, argv);
760 module = moduleManager_->GetModule(thread_, JSHandle<JSTaggedValue>::Cast(abcModuleName));
761 }
762 return module;
763 }
764
ExecuteModule(const std::string & moduleFile,std::string_view entryPoint,const std::vector<std::string> & args)765 void EcmaVM::ExecuteModule(const std::string &moduleFile, std::string_view entryPoint,
766 const std::vector<std::string> &args)
767 {
768 moduleManager_->SetCurrentExportModuleName(moduleFile);
769 // Update Current Module
770 EcmaVM::ExecuteFromPf(moduleFile, entryPoint, args, true);
771 // Restore Current Module
772 moduleManager_->RestoreCurrentExportModuleName();
773 }
774
ClearNativeMethodsData()775 void EcmaVM::ClearNativeMethodsData()
776 {
777 for (auto iter : nativeMethodMaps_) {
778 chunk_.Delete(iter.second);
779 }
780 nativeMethodMaps_.clear();
781 }
782
SetupRegExpResultCache()783 void EcmaVM::SetupRegExpResultCache()
784 {
785 regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_);
786 }
787 } // namespace panda::ecmascript
788