• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 "plugins/ets/runtime/ets_vm.h"
17 #include <atomic>
18 
19 #include "compiler/optimizer/ir/runtime_interface.h"
20 #include "include/mem/panda_smart_pointers.h"
21 #include "include/mem/panda_string.h"
22 #include "libpandabase/macros.h"
23 #include "plugins/ets/runtime/ani/ani_vm_api.h"
24 #include "plugins/ets/runtime/ets_class_linker_extension.h"
25 #include "plugins/ets/runtime/ets_coroutine.h"
26 #include "plugins/ets/runtime/ets_exceptions.h"
27 #include "plugins/ets/runtime/ets_handle.h"
28 #include "plugins/ets/runtime/ets_handle_scope.h"
29 #include "plugins/ets/runtime/ets_panda_file_items.h"
30 #include "plugins/ets/runtime/ets_runtime_interface.h"
31 #include "plugins/ets/runtime/mem/ets_reference_processor.h"
32 #include "plugins/ets/runtime/napi/ets_mangle.h"
33 #include "plugins/ets/runtime/napi/ets_napi_invoke_interface.h"
34 #include "plugins/ets/runtime/types/ets_method.h"
35 #include "plugins/ets/runtime/types/ets_promise.h"
36 #include "plugins/ets/runtime/types/ets_job.h"
37 #include "plugins/ets/runtime/types/ets_string.h"
38 #include "plugins/ets/runtime/types/ets_array.h"
39 #include "runtime/compiler.h"
40 #include "runtime/include/runtime.h"
41 #include "runtime/include/thread_scopes.h"
42 #include "runtime/init_icu.h"
43 #include "runtime/coroutines/stackful_coroutine_manager.h"
44 #include "runtime/coroutines/threaded_coroutine_manager.h"
45 #include "runtime/mem/lock_config_helper.h"
46 #include "plugins/ets/stdlib/native/init_native_methods.h"
47 #include "plugins/ets/runtime/types/ets_error.h"
48 #include "plugins/ets/runtime/types/ets_abc_runtime_linker.h"
49 #include "plugins/ets/runtime/types/ets_finalizable_weak_ref_list.h"
50 #include "plugins/ets/runtime/types/ets_escompat_array.h"
51 #include "plugins/ets/runtime/intrinsics/helpers/ets_to_string_cache.h"
52 #include "plugins/ets/runtime/hybrid/mem/static_object_operator.h"
53 
54 #include "plugins/ets/runtime/ets_object_state_table.h"
55 #include "libpandabase/taskmanager/task_manager.h"
56 
57 namespace ark::ets {
58 // Create MemoryManager by RuntimeOptions
CreateMM(Runtime * runtime,const RuntimeOptions & options)59 static mem::MemoryManager *CreateMM(Runtime *runtime, const RuntimeOptions &options)
60 {
61     mem::MemoryManager::HeapOptions heapOptions {
62         nullptr,                                      // is_object_finalizeble_func
63         nullptr,                                      // register_finalize_reference_func
64         options.GetMaxGlobalRefSize(),                // max_global_ref_size
65         options.IsGlobalReferenceSizeCheckEnabled(),  // is_global_reference_size_check_enabled
66         MT_MODE_TASK,                                 // multithreading mode
67         options.IsUseTlabForAllocations(),            // is_use_tlab_for_allocations
68         options.IsStartAsZygote(),                    // is_start_as_zygote
69     };
70 
71     auto ctx = runtime->GetLanguageContext(panda_file::SourceLang::ETS);
72     auto allocator = runtime->GetInternalAllocator();
73 
74     mem::GCTriggerConfig gcTriggerConfig(options, panda_file::SourceLang::ETS);
75 
76     mem::GCSettings gcSettings(options, panda_file::SourceLang::ETS);
77 
78     auto gcType = Runtime::GetGCType(options, panda_file::SourceLang::ETS);
79 
80     mem::StaticObjectOperator::Initialize();
81 
82     return mem::MemoryManager::Create(ctx, allocator, gcType, gcSettings, gcTriggerConfig, heapOptions);
83 }
84 
85 /* static */
CreateTaskManagerIfNeeded(const RuntimeOptions & options)86 bool PandaEtsVM::CreateTaskManagerIfNeeded(const RuntimeOptions &options)
87 {
88     if (options.GetWorkersType() == "taskmanager" && !Runtime::IsTaskManagerUsed()) {
89         taskmanager::TaskManager::Start(options.GetTaskmanagerWorkersCount(),
90                                         taskmanager::StringToTaskTimeStats(options.GetTaskStatsType()));
91         Runtime::SetTaskManagerUsed(true);
92     }
93     return true;
94 }
95 
96 /* static */
Create(Runtime * runtime,const RuntimeOptions & options)97 Expected<PandaEtsVM *, PandaString> PandaEtsVM::Create(Runtime *runtime, const RuntimeOptions &options)
98 {
99     ASSERT(runtime != nullptr);
100 
101     if (!PandaEtsVM::CreateTaskManagerIfNeeded(options)) {
102         return Unexpected(PandaString("Cannot create TaskManager"));
103     }
104 
105     auto mm = CreateMM(runtime, options);
106     if (mm == nullptr) {
107         return Unexpected(PandaString("Cannot create MemoryManager"));
108     }
109 
110     auto allocator = mm->GetHeapManager()->GetInternalAllocator();
111     auto vm = allocator->New<PandaEtsVM>(runtime, options, mm);
112     if (vm == nullptr) {
113         return Unexpected(PandaString("Cannot create PandaCoreVM"));
114     }
115 
116     auto classLinker = EtsClassLinker::Create(runtime->GetClassLinker());
117     if (!classLinker) {
118         allocator->Delete(vm);
119         mem::MemoryManager::Destroy(mm);
120         return Unexpected(classLinker.Error());
121     }
122     vm->classLinker_ = std::move(classLinker.Value());
123 
124     vm->InitializeGC();
125 
126     const auto &icuPath = options.GetIcuDataPath();
127     if (icuPath == "default") {
128         SetIcuDirectory();
129     } else {
130         u_setDataDirectory(icuPath.c_str());
131     }
132 
133     CoroutineManagerConfig cfg {
134         // enable drain queue interface
135         options.IsCoroutineEnableFeaturesAniDrainQueue(plugins::LangToRuntimeType(panda_file::SourceLang::ETS)),
136         // enable migration
137         options.IsCoroutineEnableFeaturesMigration(plugins::LangToRuntimeType(panda_file::SourceLang::ETS)),
138         // enable migrate awakened coroutines
139         options.IsCoroutineEnableFeaturesMigrateAwait(plugins::LangToRuntimeType(panda_file::SourceLang::ETS)),
140         // workers_count
141         options.GetCoroutineWorkersCount(plugins::LangToRuntimeType(panda_file::SourceLang::ETS)),
142         // exclusive workers limit
143         options.GetCoroutineEWorkersLimit(plugins::LangToRuntimeType(panda_file::SourceLang::ETS)),
144         // enable perf stats
145         options.IsCoroutineDumpStats(plugins::LangToRuntimeType(panda_file::SourceLang::ETS))};
146     vm->coroutineManager_->Initialize(cfg, runtime, vm);
147 
148     return vm;
149 }
150 
Destroy(PandaEtsVM * vm)151 bool PandaEtsVM::Destroy(PandaEtsVM *vm)
152 {
153     if (vm == nullptr) {
154         return false;
155     }
156 
157     vm->SaveProfileInfo();
158     vm->UninitializeThreads();
159     vm->StopGC();
160 
161     auto runtime = Runtime::GetCurrent();
162     runtime->GetInternalAllocator()->Delete(vm);
163 
164     return true;
165 }
166 
PandaEtsVM(Runtime * runtime,const RuntimeOptions & options,mem::MemoryManager * mm)167 PandaEtsVM::PandaEtsVM(Runtime *runtime, const RuntimeOptions &options, mem::MemoryManager *mm)
168     : EtsVM {napi::GetInvokeInterface()}, ani_vm {ani::GetVMAPI()}, runtime_(runtime), mm_(mm)
169 {
170     ASSERT(runtime_ != nullptr);
171     ASSERT(mm_ != nullptr);
172 
173     auto heapManager = mm_->GetHeapManager();
174     auto allocator = heapManager->GetInternalAllocator();
175 
176     runtimeIface_ = allocator->New<EtsRuntimeInterface>();
177     compiler_ = allocator->New<Compiler>(heapManager->GetCodeAllocator(), allocator, options,
178                                          heapManager->GetMemStats(), runtimeIface_);
179     stringTable_ = allocator->New<StringTable>();
180     monitorPool_ = allocator->New<MonitorPool>(allocator);
181     finalizationRegistryManager_ = allocator->New<FinalizationRegistryManager>(this);
182     referenceProcessor_ = allocator->New<mem::ets::EtsReferenceProcessor>(mm_->GetGC());
183 
184     auto langStr = plugins::LangToRuntimeType(panda_file::SourceLang::ETS);
185     const auto &coroType = options.GetCoroutineImpl(langStr);
186     if (coroType == "stackful") {
187         coroutineManager_ = allocator->New<StackfulCoroutineManager>(EtsCoroutine::Create<Coroutine>);
188     } else {
189         coroutineManager_ = allocator->New<ThreadedCoroutineManager>(EtsCoroutine::Create<Coroutine>);
190     }
191     rendezvous_ = allocator->New<Rendezvous>(this);
192     objStateTable_ = MakePandaUnique<EtsObjectStateTable>(allocator);
193     InitializeRandomEngine();
194 }
195 
~PandaEtsVM()196 PandaEtsVM::~PandaEtsVM()
197 {
198     auto allocator = mm_->GetHeapManager()->GetInternalAllocator();
199     ASSERT(allocator != nullptr);
200 
201     allocator->Delete(rendezvous_);
202     allocator->Delete(runtimeIface_);
203     allocator->Delete(coroutineManager_);
204     allocator->Delete(referenceProcessor_);
205     allocator->Delete(monitorPool_);
206     allocator->Delete(finalizationRegistryManager_);
207     allocator->Delete(stringTable_);
208     allocator->Delete(compiler_);
209 
210     objStateTable_.reset();
211 
212     ASSERT(mm_ != nullptr);
213     mm_->Finalize();
214     mem::MemoryManager::Destroy(mm_);
215 }
216 
GetCurrent()217 PandaEtsVM *PandaEtsVM::GetCurrent()
218 {
219     // Use Thread class for possible to use it from native and manage threads
220     return static_cast<PandaEtsVM *>(Thread::GetCurrent()->GetVM());
221 }
222 
PreallocSpecialReference(PandaEtsVM * vm,const char * desc,bool nonMovable=false)223 static mem::Reference *PreallocSpecialReference(PandaEtsVM *vm, const char *desc, bool nonMovable = false)
224 {
225     EtsClass *cls = vm->GetClassLinker()->GetClass(desc);
226     if (cls == nullptr) {
227         LOG(FATAL, RUNTIME) << "Cannot find a class for special object " << desc;
228     }
229     EtsObject *obj = nonMovable ? EtsObject::CreateNonMovable(cls) : EtsObject::Create(cls);
230     if (obj == nullptr) {
231         LOG(FATAL, RUNTIME) << "Cannot preallocate a special object " << desc;
232     }
233     return vm->GetGlobalObjectStorage()->Add(obj->GetCoreType(), ark::mem::Reference::ObjectType::GLOBAL);
234 }
235 
PreallocOOMError(PandaEtsVM * vm)236 static mem::Reference *PreallocOOMError(PandaEtsVM *vm)
237 {
238     auto *coro = EtsCoroutine::GetCurrent();
239     ASSERT(coro != nullptr);
240 
241     auto *oom = EtsOutOfMemoryError::Create(coro);
242     if (oom == nullptr) {
243         LOG(FATAL, RUNTIME) << "Cannot preallocate OOM error";
244     }
245 
246     return vm->GetGlobalObjectStorage()->Add(oom->AsObject()->GetCoreType(), ark::mem::Reference::ObjectType::GLOBAL);
247 }
248 
Initialize()249 bool PandaEtsVM::Initialize()
250 {
251     if (!ark::intrinsics::Initialize(ark::panda_file::SourceLang::ETS)) {
252         LOG(ERROR, RUNTIME) << "Failed to initialize eTS intrinsics";
253         return false;
254     }
255 
256     if (!classLinker_->Initialize()) {
257         LOG(FATAL, ETS) << "Cannot initialize ets class linker";
258     }
259     classLinker_->GetEtsClassLinkerExtension()->InitializeBuiltinClasses();
260 
261     if (Runtime::GetOptions().ShouldLoadBootPandaFiles()) {
262         // NOLINTNEXTLINE(google-build-using-namespace)
263         using namespace panda_file_items::class_descriptors;
264 
265         ASSERT(Thread::GetCurrent() != nullptr);
266         ASSERT(GetThreadManager()->GetMainThread() == Thread::GetCurrent());
267         auto *coro = EtsCoroutine::GetCurrent();
268 
269         coro->GetLocalStorage().Set<EtsCoroutine::DataIdx::ETS_PLATFORM_TYPES_PTR>(
270             ToUintPtr(classLinker_->GetEtsClassLinkerExtension()->GetPlatformTypes()));
271         ASSERT(PlatformTypes(coro) != nullptr);
272 
273         // Should be invoked after PlatformTypes is initialized in coroutine.
274         oomObjRef_ = PreallocOOMError(this);
275         nullValueRef_ = PreallocSpecialReference(this, NULL_VALUE.data(), true);
276         finalizableWeakRefList_ = PreallocSpecialReference(this, FINALIZABLE_WEAK_REF.data());
277 
278         coro->SetupNullValue(GetNullValue());
279 
280         if (LIKELY(Runtime::GetOptions().IsUseStringCaches())) {
281             doubleToStringCache_ = DoubleToStringCache::Create(coro);
282             floatToStringCache_ = FloatToStringCache::Create(coro);
283             longToStringCache_ = LongToStringCache::Create(coro);
284         }
285 
286         referenceProcessor_->Initialize();
287     }
288     [[maybe_unused]] bool cachesCreated =
289         (doubleToStringCache_ != nullptr && floatToStringCache_ != nullptr && longToStringCache_ != nullptr);
290     LOG_IF(!cachesCreated, WARNING, ETS) << "Cannot initialize number-to-string caches";
291     LOG_IF(cachesCreated, DEBUG, ETS) << "Initialized number-to-string caches";
292 
293     // Check if Intrinsics/native methods should be initialized, we don't want to attempt to
294     // initialize  native methods in certain scenarios where we don't have ets stdlib at our disposal
295     if (Runtime::GetOptions().ShouldInitializeIntrinsics()) {
296         // NOTE(ksarychev, #18135): Implement napi module registration via loading a separate
297         // library
298         ani_env *env = EtsCoroutine::GetCurrent()->GetEtsNapiEnv();
299         ark::ets::stdlib::InitNativeMethods(env);
300     }
301 
302     const auto lang = plugins::LangToRuntimeType(panda_file::SourceLang::ETS);
303     for (const auto &path : Runtime::GetOptions().GetNativeLibraryPath(lang)) {
304         nativeLibraryProvider_.AddLibraryPath(ConvertToString(path));
305     }
306 
307     return true;
308 }
309 
InitializeFinish()310 bool PandaEtsVM::InitializeFinish()
311 {
312     if (Runtime::GetOptions().ShouldLoadBootPandaFiles()) {
313         // Preinitialize StackOverflowError, so we don't need to do this when stack overflow occurred
314         EtsClass *cls = classLinker_->GetClass(panda_file_items::class_descriptors::STACK_OVERFLOW_ERROR.data());
315         if (cls == nullptr) {
316             LOG(FATAL, ETS) << "Cannot preinitialize StackOverflowError";
317             return false;
318         }
319     }
320     return true;
321 }
322 
UninitializeThreads()323 void PandaEtsVM::UninitializeThreads()
324 {
325     // Wait until all threads finish the work
326     coroutineManager_->WaitForDeregistration();
327     coroutineManager_->DestroyMainCoroutine();
328     coroutineManager_->Finalize();
329 }
330 
PreStartup()331 void PandaEtsVM::PreStartup()
332 {
333     ASSERT(mm_ != nullptr);
334 
335     mm_->PreStartup();
336 }
337 
PreZygoteFork()338 void PandaEtsVM::PreZygoteFork()
339 {
340     ASSERT(mm_ != nullptr);
341     ASSERT(compiler_ != nullptr);
342     ASSERT(coroutineManager_ != nullptr);
343 
344     mm_->PreZygoteFork();
345     compiler_->PreZygoteFork();
346     coroutineManager_->PreZygoteFork();
347     if (taskmanager::TaskManager::IsUsed()) {
348         preForkWorkerCount_ = taskmanager::TaskManager::GetWorkersCount();
349         taskmanager::TaskManager::SetWorkersCount(0U);
350     }
351 }
352 
PostZygoteFork()353 void PandaEtsVM::PostZygoteFork()
354 {
355     ASSERT(compiler_ != nullptr);
356     ASSERT(mm_ != nullptr);
357     ASSERT(coroutineManager_ != nullptr);
358 
359     if (taskmanager::TaskManager::IsUsed()) {
360         taskmanager::TaskManager::SetWorkersCount(preForkWorkerCount_);
361     }
362     coroutineManager_->PostZygoteFork();
363     compiler_->PostZygoteFork();
364     mm_->PostZygoteFork();
365     // Postpone GC on application start-up
366     // Postpone GCEnd method should be called on start-up ending event
367     mm_->GetGC()->PostponeGCStart();
368     PreStartup();
369 }
370 
InitializeGC()371 void PandaEtsVM::InitializeGC()
372 {
373     ASSERT(mm_ != nullptr);
374 
375     mm_->InitializeGC(this);
376 }
377 
StartGC()378 void PandaEtsVM::StartGC()
379 {
380     ASSERT(mm_ != nullptr);
381 
382     mm_->StartGC();
383 }
384 
StopGC()385 void PandaEtsVM::StopGC()
386 {
387     ASSERT(mm_ != nullptr);
388 
389     if (GetGC()->IsGCRunning()) {
390         mm_->StopGC();
391     }
392 }
393 
HandleReferences(const GCTask & task,const mem::GC::ReferenceClearPredicateT & pred)394 void PandaEtsVM::HandleReferences(const GCTask &task, const mem::GC::ReferenceClearPredicateT &pred)
395 {
396     ASSERT(mm_ != nullptr);
397 
398     auto gc = mm_->GetGC();
399     ASSERT(gc != nullptr);
400 
401     LOG(DEBUG, REF_PROC) << "Start processing cleared references";
402     gc->ProcessReferences(gc->GetGCPhase(), task, pred);
403     LOG(DEBUG, REF_PROC) << "Finish processing cleared references";
404 
405     GetGlobalObjectStorage()->ClearUnmarkedWeakRefs(gc, pred);
406 }
407 
HandleGCRoutineInMutator()408 void PandaEtsVM::HandleGCRoutineInMutator()
409 {
410     // Handle references only in coroutine
411     ASSERT(Coroutine::GetCurrent() != nullptr);
412     ASSERT(GetMutatorLock()->HasLock());
413     auto coroutine = EtsCoroutine::GetCurrent();
414     GetFinalizationRegistryManager()->StartCleanupCoroIfNeeded(coroutine);
415     coroutine->GetPandaVM()->CleanFinalizableReferenceList();
416 }
417 
HandleGCFinished()418 void PandaEtsVM::HandleGCFinished() {}
419 
CheckEntrypointSignature(Method * entrypoint)420 bool PandaEtsVM::CheckEntrypointSignature(Method *entrypoint)
421 {
422     ASSERT(entrypoint != nullptr);
423 
424     if (entrypoint->GetReturnType().GetId() != panda_file::Type::TypeId::I32 &&
425         entrypoint->GetReturnType().GetId() != panda_file::Type::TypeId::VOID) {
426         return false;
427     }
428 
429     if (entrypoint->GetNumArgs() == 0) {
430         return true;
431     }
432 
433     if (entrypoint->GetNumArgs() > 1) {
434         return false;
435     }
436 
437     auto *pf = entrypoint->GetPandaFile();
438     ASSERT(pf != nullptr);
439     panda_file::MethodDataAccessor mda(*pf, entrypoint->GetFileId());
440     panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
441 
442     if (pda.GetArgType(0).GetId() != panda_file::Type::TypeId::REFERENCE) {
443         return false;
444     }
445 
446     auto name = pf->GetStringData(pda.GetReferenceType(0));
447     std::string_view expectedName(panda_file_items::class_descriptors::STRING_ARRAY);
448 
449     return utf::IsEqual({name.data, name.utf16Length},
450                         {utf::CStringAsMutf8(expectedName.data()), expectedName.length()});
451 }
452 
CreateArgumentsArray(const std::vector<std::string> & args,PandaEtsVM * etsVm)453 static EtsObjectArray *CreateArgumentsArray(const std::vector<std::string> &args, PandaEtsVM *etsVm)
454 {
455     ASSERT(etsVm != nullptr);
456 
457     const char *classDescripor = panda_file_items::class_descriptors::STRING_ARRAY.data();
458     EtsClass *arrayKlass = etsVm->GetClassLinker()->GetClass(classDescripor);
459     if (arrayKlass == nullptr) {
460         LOG(FATAL, RUNTIME) << "Class " << classDescripor << " not found";
461         return nullptr;
462     }
463 
464     EtsCoroutine *coroutine = EtsCoroutine::GetCurrent();
465     [[maybe_unused]] EtsHandleScope scope(coroutine);
466     EtsObjectArray *etsArray = EtsObjectArray::Create(arrayKlass, args.size());
467     EtsHandle<EtsObjectArray> arrayHandle(coroutine, etsArray);
468 
469     for (size_t i = 0; i < args.size(); i++) {
470         EtsString *str = EtsString::CreateFromMUtf8(args[i].data(), args[i].length());
471         arrayHandle.GetPtr()->Set(i, str->AsObject());
472     }
473 
474     return arrayHandle.GetPtr();
475 }
476 
CreateString(Method * ctor,ObjectHeader * obj)477 coretypes::String *PandaEtsVM::CreateString(Method *ctor, ObjectHeader *obj)
478 {
479     EtsString *str = nullptr;
480     ASSERT(ctor->GetNumArgs() > 0);  // must be at list this argument
481     if (ctor->GetNumArgs() == 1) {
482         str = EtsString::CreateNewEmptyString();
483     } else if (ctor->GetNumArgs() == 2U) {
484         ASSERT(ctor->GetArgType(1).GetId() == panda_file::Type::TypeId::REFERENCE);
485         auto *strData = utf::Mutf8AsCString(ctor->GetRefArgType(1).data);
486         if (std::strcmp("[C", strData) == 0) {
487             auto *array = reinterpret_cast<EtsArray *>(obj);
488             str = EtsString::CreateNewStringFromChars(0, array->GetLength(), array);
489         } else if ((std::strcmp("Lstd/core/String;", strData) == 0)) {
490             str = EtsString::CreateNewStringFromString(reinterpret_cast<EtsString *>(obj));
491         } else {
492             LOG(FATAL, ETS) << "Non-existent ctor";
493         }
494     } else {
495         LOG(FATAL, ETS) << "Must be 1 or 2 ctor args";
496     }
497     return str->GetCoreType();
498 }
499 
InvokeEntrypointImpl(Method * entrypoint,const std::vector<std::string> & args)500 Expected<int, Runtime::Error> PandaEtsVM::InvokeEntrypointImpl(Method *entrypoint, const std::vector<std::string> &args)
501 {
502     ASSERT(Runtime::GetCurrent()->GetLanguageContext(*entrypoint).GetLanguage() == panda_file::SourceLang::ETS);
503 
504     EtsCoroutine *coroutine = EtsCoroutine::GetCurrent();
505     ASSERT(coroutine != nullptr);
506 
507     ScopedManagedCodeThread sj(coroutine);
508     if (!classLinker_->InitializeClass(coroutine, EtsClass::FromRuntimeClass(entrypoint->GetClass()))) {
509         LOG(ERROR, RUNTIME) << "Cannot initialize class '" << entrypoint->GetClass()->GetName() << "'";
510         return Unexpected(Runtime::Error::CLASS_NOT_INITIALIZED);
511     }
512 
513     [[maybe_unused]] EtsHandleScope scope(coroutine);
514     if (entrypoint->GetNumArgs() == 0) {
515         auto v = entrypoint->Invoke(coroutine, nullptr);
516         return v.GetAs<int>();
517     }
518 
519     if (entrypoint->GetNumArgs() == 1) {
520         EtsObjectArray *etsObjectArray = CreateArgumentsArray(args, this);
521         EtsHandle<EtsObjectArray> argsHandle(coroutine, etsObjectArray);
522         Value argVal(argsHandle.GetPtr()->AsObject()->GetCoreType());
523         auto v = entrypoint->Invoke(coroutine, &argVal);
524 
525         return v.GetAs<int>();
526     }
527 
528     // What if entrypoint->GetNumArgs() > 1 ?
529     LOG(ERROR, RUNTIME) << "ets entrypoint has args count more than 1 : " << entrypoint->GetNumArgs();
530     return Unexpected(Runtime::Error::INVALID_ENTRY_POINT);
531 }
532 
GetOOMErrorObject()533 ObjectHeader *PandaEtsVM::GetOOMErrorObject()
534 {
535     auto obj = GetGlobalObjectStorage()->Get(oomObjRef_);
536     ASSERT(obj != nullptr);
537     return obj;
538 }
539 
GetNullValue() const540 ObjectHeader *PandaEtsVM::GetNullValue() const
541 {
542     auto obj = GetGlobalObjectStorage()->Get(nullValueRef_);
543     ASSERT(obj != nullptr);
544     return obj;
545 }
546 
LoadNativeLibrary(EtsEnv * env,const PandaString & name,bool shouldVerifyPermission,const PandaString & fileName)547 bool PandaEtsVM::LoadNativeLibrary(EtsEnv *env, const PandaString &name, bool shouldVerifyPermission,
548                                    const PandaString &fileName)
549 {
550     ASSERT_PRINT(Coroutine::GetCurrent()->IsInNativeCode(), "LoadNativeLibrary must be called at native");
551 
552     if (auto error = nativeLibraryProvider_.LoadLibrary(env, name, shouldVerifyPermission, fileName)) {
553         LOG(ERROR, RUNTIME) << "Cannot load library " << name << ": " << error.value();
554         return false;
555     }
556 
557     return true;
558 }
559 
ResolveNativeMethod(Method * method)560 void PandaEtsVM::ResolveNativeMethod(Method *method)
561 {
562     ASSERT_PRINT(method->IsNative(), "Method should be native");
563     std::string className = utf::Mutf8AsCString(method->GetClassName().data);
564     std::string methodName = utf::Mutf8AsCString(method->GetName().data);
565     std::string name = MangleMethodName(className, methodName);
566     auto ptr = nativeLibraryProvider_.ResolveSymbol(PandaString(name));
567     if (ptr == nullptr) {
568         std::string signature;
569         signature.append(EtsMethod::FromRuntimeMethod(method)->GetMethodSignature(false));
570         name = MangleMethodNameWithSignature(name, signature);
571         ptr = nativeLibraryProvider_.ResolveSymbol(PandaString(name));
572         if (ptr == nullptr) {
573             PandaStringStream ss;
574             ss << "No implementation found for " << method->GetFullName() << ", tried " << name;
575 
576             auto coroutine = EtsCoroutine::GetCurrent();
577             ThrowEtsException(coroutine, panda_file_items::class_descriptors::LINKER_UNRESOLVED_METHOD_ERROR, ss.str());
578             return;
579         }
580     }
581 
582     method->SetNativePointer(ptr);
583 }
584 
PrintExceptionInfo(EtsCoroutine * coro,EtsHandle<EtsObject> exception,PandaStringStream & ss)585 static void PrintExceptionInfo(EtsCoroutine *coro, EtsHandle<EtsObject> exception, PandaStringStream &ss)
586 {
587     auto cls = exception->GetClass();
588 
589     PandaVector<uint8_t> strBuf;
590     auto const performCall = [coro, &exception, &strBuf](EtsMethod *method) -> std::optional<std::string_view> {
591         ASSERT(method != nullptr);
592         std::array<Value, 1> args = {Value(exception->GetCoreType())};
593         EtsObject *callRes = EtsObject::FromCoreType(
594             EtsMethod::ToRuntimeMethod(method)->Invoke(coro, args.data()).GetAs<ObjectHeader *>());
595         if (coro->HasPendingException() || callRes == EtsObject::FromCoreType(coro->GetNullValue())) {
596             return std::nullopt;
597         }
598         return EtsString::FromEtsObject(callRes)->ConvertToStringView(&strBuf);
599     };
600 
601     ss << std::endl
602        << performCall(cls->GetInstanceMethod("toString", ":Lstd/core/String;")).value_or("invoke toString failed");
603     if (PlatformTypes(coro)->escompatError->IsAssignableFrom(cls)) {
604         ss << std::endl
605            << performCall(cls->GetInstanceMethod("<get>stack", ":Lstd/core/String;")).value_or("exception dump failed");
606     }
607 }
608 
HandleUncaughtException()609 [[noreturn]] void PandaEtsVM::HandleUncaughtException()
610 {
611     auto coro = EtsCoroutine::GetCurrent();
612     ASSERT(coro != nullptr);
613     ScopedManagedCodeThread sj(coro);
614     [[maybe_unused]] EtsHandleScope scope(coro);
615 
616     EtsHandle<EtsObject> exception(coro, EtsObject::FromCoreType(coro->GetException()));
617 
618     PandaStringStream logStream;
619     logStream << "Unhandled exception: " << exception->GetCoreType()->ClassAddr<Class>()->GetName();
620 
621     auto descr = exception->GetClass()->GetDescriptor();
622     if (descr != panda_file_items::class_descriptors::OUT_OF_MEMORY_ERROR &&
623         descr != panda_file_items::class_descriptors::STACK_OVERFLOW_ERROR) {
624         coro->ClearException();
625         PrintExceptionInfo(coro, exception, logStream);
626     }
627     LOG(ERROR, RUNTIME) << logStream.str();
628     // _exit guarantees a safe completion in case of multi-threading as static destructors aren't called
629     _exit(1);
630 }
631 
HandleEmptyArguments(const PandaVector<Value> & arguments,const GCRootVisitor & visitor,const EtsCoroutine * coroutine)632 void HandleEmptyArguments(const PandaVector<Value> &arguments, const GCRootVisitor &visitor,
633                           const EtsCoroutine *coroutine)
634 {
635     // arguments may be empty in the following cases:
636     // 1. The entrypoint is static and doesn't accept any arguments
637     // 2. The coroutine is launched.
638     // 3. The entrypoint is the main method
639     Method *entrypoint = coroutine->GetManagedEntrypoint();
640     panda_file::ShortyIterator it(entrypoint->GetShorty());
641     size_t argIdx = 0;
642     ++it;  // skip return type
643     if (!entrypoint->IsStatic()) {
644         // handle 'this' argument
645         ASSERT(arguments[argIdx].IsReference());
646         ObjectHeader *arg = arguments[argIdx].GetAs<ObjectHeader *>();
647         ASSERT(arg != nullptr);
648         visitor(mem::GCRoot(mem::RootType::ROOT_THREAD, arg));
649         ++argIdx;
650     }
651     while (it != panda_file::ShortyIterator()) {
652         if ((*it).GetId() == panda_file::Type::TypeId::REFERENCE) {
653             ASSERT(arguments[argIdx].IsReference());
654             ObjectHeader *arg = arguments[argIdx].GetAs<ObjectHeader *>();
655             if (arg != nullptr) {
656                 visitor(mem::GCRoot(mem::RootType::ROOT_THREAD, arg));
657             }
658         }
659         ++it;
660         ++argIdx;
661     }
662 }
663 
AddRootProvider(mem::RootProvider * provider)664 void PandaEtsVM::AddRootProvider(mem::RootProvider *provider)
665 {
666     os::memory::LockHolder lock(rootProviderlock_);
667     ASSERT(rootProviders_.find(provider) == rootProviders_.end());
668     rootProviders_.insert(provider);
669 }
670 
RemoveRootProvider(mem::RootProvider * provider)671 void PandaEtsVM::RemoveRootProvider(mem::RootProvider *provider)
672 {
673     os::memory::LockHolder lock(rootProviderlock_);
674     ASSERT(rootProviders_.find(provider) != rootProviders_.end());
675     rootProviders_.erase(provider);
676 }
677 
VisitUnhandledObjects(const GCRootVisitor & visitor,PandaUnorderedSet<EtsObject * > & unhandledObjects)678 static void VisitUnhandledObjects(const GCRootVisitor &visitor, PandaUnorderedSet<EtsObject *> &unhandledObjects)
679 {
680     for (auto *obj : unhandledObjects) {
681         visitor(mem::GCRoot(mem::RootType::ROOT_VM, obj->GetCoreType()));
682     }
683 }
684 
VisitVmRoots(const GCRootVisitor & visitor)685 void PandaEtsVM::VisitVmRoots(const GCRootVisitor &visitor)
686 {
687     GetThreadManager()->EnumerateThreads([visitor](ManagedThread *thread) {
688         const auto coroutine = EtsCoroutine::CastFromThread(thread);
689         if (auto etsNapiEnv = coroutine->GetEtsNapiEnv()) {
690             auto etsStorage = etsNapiEnv->GetEtsReferenceStorage();
691             etsStorage->GetAsReferenceStorage()->VisitObjects(visitor, mem::RootType::ROOT_NATIVE_LOCAL);
692         }
693         if (!coroutine->HasManagedEntrypoint()) {
694             return true;
695         }
696         const PandaVector<Value> &arguments = coroutine->GetManagedEntrypointArguments();
697         if (!arguments.empty()) {
698             HandleEmptyArguments(arguments, visitor, coroutine);
699         }
700         return true;
701     });
702     if (LIKELY(Runtime::GetOptions().IsUseStringCaches())) {
703         visitor(mem::GCRoot(mem::RootType::ROOT_VM, doubleToStringCache_->GetCoreType()));
704         visitor(mem::GCRoot(mem::RootType::ROOT_VM, floatToStringCache_->GetCoreType()));
705         visitor(mem::GCRoot(mem::RootType::ROOT_VM, longToStringCache_->GetCoreType()));
706         PlatformTypes(this)->VisitRoots(visitor);
707     }
708     {
709         os::memory::LockHolder lock(rootProviderlock_);
710         for (auto *rootProvider : rootProviders_) {
711             rootProvider->VisitRoots(visitor);
712         }
713     }
714     {
715         os::memory::LockHolder lh(unhandledMutex_);
716         VisitUnhandledObjects(visitor, unhandledFailedJobs_);
717         VisitUnhandledObjects(visitor, unhandledRejectedPromises_);
718     }
719 }
720 
721 template <bool REF_CAN_BE_NULL>
UpdateMovedVmRef(Value & ref,const GCRootUpdater & gcRootUpdater)722 void PandaEtsVM::UpdateMovedVmRef(Value &ref, const GCRootUpdater &gcRootUpdater)
723 {
724     ASSERT(ref.IsReference());
725     ObjectHeader *arg = ref.GetAs<ObjectHeader *>();
726     if constexpr (REF_CAN_BE_NULL) {
727         if (arg == nullptr) {
728             return;
729         }
730     } else {
731         ASSERT(arg != nullptr);
732     }
733 
734     if (gcRootUpdater(&arg)) {
735         ref = Value(arg);
736         LOG(DEBUG, GC) << "Forwarded root object: " << arg;
737     }
738 }
739 
UpdateManagedEntrypointArgRefs(EtsCoroutine * coroutine,const GCRootUpdater & gcRootUpdater)740 void PandaEtsVM::UpdateManagedEntrypointArgRefs(EtsCoroutine *coroutine, const GCRootUpdater &gcRootUpdater)
741 {
742     PandaVector<Value> &arguments = coroutine->GetManagedEntrypointArguments();
743     if (!arguments.empty()) {
744         // arguments may be empty in the following cases:
745         // 1. The entrypoint is static and doesn't accept any arguments
746         // 2. The coroutine is launched.
747         // 3. The entrypoint is the main method
748         Method *entrypoint = coroutine->GetManagedEntrypoint();
749         panda_file::ShortyIterator it(entrypoint->GetShorty());
750         size_t argIdx = 0;
751         ++it;  // skip return type
752         if (!entrypoint->IsStatic()) {
753             // handle 'this' argument
754             UpdateMovedVmRef<false>(arguments[argIdx], gcRootUpdater);
755             ++argIdx;
756         }
757         while (it != panda_file::ShortyIterator()) {
758             if ((*it).GetId() == panda_file::Type::TypeId::REFERENCE) {
759                 UpdateMovedVmRef<true>(arguments[argIdx], gcRootUpdater);
760             }
761             ++it;
762             ++argIdx;
763         }
764     }
765 }
766 
UpdateUnhandledObjects(PandaUnorderedSet<EtsObject * > & unhandledObjects,const GCRootUpdater & gcRootUpdater)767 static void UpdateUnhandledObjects(PandaUnorderedSet<EtsObject *> &unhandledObjects, const GCRootUpdater &gcRootUpdater)
768 {
769     PandaVector<PandaUnorderedSet<EtsObject *>::node_type> movedObjects {};
770     for (auto iter = unhandledObjects.begin(); iter != unhandledObjects.end();) {
771         auto *obj = (*iter)->GetCoreType();
772         // assuming that `gcRootUpdater(&obj) == false` means that `obj` was not modified
773         if (gcRootUpdater(&obj)) {
774             auto oldIter = iter;
775             ++iter;
776             auto node = unhandledObjects.extract(oldIter);
777             node.value() = EtsObject::FromCoreType(obj);
778             movedObjects.push_back(std::move(node));
779         } else {
780             ++iter;
781         }
782     }
783     for (auto &node : movedObjects) {
784         unhandledObjects.insert(std::move(node));
785     }
786 }
787 
UpdateVmRefs(const GCRootUpdater & gcRootUpdater)788 void PandaEtsVM::UpdateVmRefs(const GCRootUpdater &gcRootUpdater)
789 {
790     GetThreadManager()->EnumerateThreads([&gcRootUpdater](ManagedThread *thread) {
791         auto coroutine = EtsCoroutine::CastFromThread(thread);
792         if (auto etsNapiEnv = coroutine->GetEtsNapiEnv()) {
793             auto etsStorage = etsNapiEnv->GetEtsReferenceStorage();
794             etsStorage->GetAsReferenceStorage()->UpdateMovedRefs(gcRootUpdater);
795         }
796         if (!coroutine->HasManagedEntrypoint()) {
797             return true;
798         }
799         UpdateManagedEntrypointArgRefs(coroutine, gcRootUpdater);
800         return true;
801     });
802 
803     objStateTable_->EnumerateObjectStates([&gcRootUpdater](EtsObjectStateInfo *info) {
804         auto *obj = info->GetEtsObject()->GetCoreType();
805         if (gcRootUpdater(&obj)) {
806             info->SetEtsObject(EtsObject::FromCoreType(obj));
807         }
808     });
809 
810     {
811         os::memory::LockHolder lock(rootProviderlock_);
812         for (auto *rootProvider : rootProviders_) {
813             rootProvider->UpdateRefs(gcRootUpdater);
814         }
815     }
816     {
817         os::memory::LockHolder lh(unhandledMutex_);
818         UpdateUnhandledObjects(unhandledFailedJobs_, gcRootUpdater);
819         UpdateUnhandledObjects(unhandledRejectedPromises_, gcRootUpdater);
820     }
821     if (LIKELY(Runtime::GetOptions().IsUseStringCaches())) {
822         auto *asciiCache = PlatformTypes(this)->GetAsciiCacheTable();
823         if (asciiCache != nullptr) {
824             PlatformTypes(this)->UpdateCachesVmRefs(gcRootUpdater);
825         }
826     }
827 }
828 
EtsNapiObjectToGlobalReference(ets_object globalRef)829 static mem::Reference *EtsNapiObjectToGlobalReference(ets_object globalRef)
830 {
831     ASSERT(globalRef != nullptr);
832     auto *ref = reinterpret_cast<mem::Reference *>(globalRef);
833     return ref;
834 }
835 
DeleteGlobalRef(ets_object globalRef)836 void PandaEtsVM::DeleteGlobalRef(ets_object globalRef)
837 {
838     auto *ref = EtsNapiObjectToGlobalReference(globalRef);
839     if (!ref->IsGlobal()) {
840         LOG(FATAL, ETS_NAPI) << "Try to remote non-global ref: " << std::hex << ref;
841         return;
842     }
843     GetGlobalObjectStorage()->Remove(ref);
844 }
845 
EtsNapiObjectToWeakReference(ets_weak weakRef)846 static mem::Reference *EtsNapiObjectToWeakReference(ets_weak weakRef)
847 {
848     ASSERT(weakRef != nullptr);
849     auto *ref = reinterpret_cast<mem::Reference *>(weakRef);
850     return ref;
851 }
852 
DeleteWeakGlobalRef(ets_weak weakRef)853 void PandaEtsVM::DeleteWeakGlobalRef(ets_weak weakRef)
854 {
855     auto *ref = EtsNapiObjectToWeakReference(weakRef);
856     if (!ref->IsWeak()) {
857         LOG(FATAL, ETS_NAPI) << "Try to remote non-weak ref: " << std::hex << ref;
858         return;
859     }
860     GetGlobalObjectStorage()->Remove(ref);
861 }
862 
RegisterFinalizerForObject(EtsCoroutine * coro,const EtsHandle<EtsObject> & object,void (* finalizer)(void *),void * finalizerArg)863 void PandaEtsVM::RegisterFinalizerForObject(EtsCoroutine *coro, const EtsHandle<EtsObject> &object,
864                                             void (*finalizer)(void *), void *finalizerArg)
865 {
866     ASSERT_MANAGED_CODE();
867     auto *weakRef = EtsFinalizableWeakRef::Create(coro);
868     weakRef->SetFinalizer(finalizer, finalizerArg);
869     weakRef->SetReferent(object.GetPtr());
870     auto *coreList = GetGlobalObjectStorage()->Get(finalizableWeakRefList_);
871     auto *weakRefList = EtsFinalizableWeakRefList::FromCoreType(coreList);
872     os::memory::LockHolder lh(finalizableWeakRefListLock_);
873     weakRefList->Push(coro, weakRef);
874 }
875 
CleanFinalizableReferenceList()876 void PandaEtsVM::CleanFinalizableReferenceList()
877 {
878     auto *coreList = GetGlobalObjectStorage()->Get(finalizableWeakRefList_);
879     auto *weakRefList = EtsFinalizableWeakRefList::FromCoreType(coreList);
880     os::memory::LockHolder lh(finalizableWeakRefListLock_);
881     weakRefList->UnlinkClearedReferences(EtsCoroutine::GetCurrent());
882 }
883 
BeforeShutdown()884 void PandaEtsVM::BeforeShutdown()
885 {
886     ScopedManagedCodeThread managedScope(EtsCoroutine::GetCurrent());
887     auto *coreList = GetGlobalObjectStorage()->Get(finalizableWeakRefList_);
888     auto *weakRefList = EtsFinalizableWeakRefList::FromCoreType(coreList);
889     weakRefList->TraverseAndFinalize();
890 }
891 
CreateApplicationRuntimeLinker(const PandaVector<PandaString> & abcFiles)892 ClassLinkerContext *PandaEtsVM::CreateApplicationRuntimeLinker(const PandaVector<PandaString> &abcFiles)
893 {
894     auto *coro = EtsCoroutine::GetCurrent();
895     ASSERT(coro != nullptr);
896 
897     [[maybe_unused]] ScopedManagedCodeThread sj(coro);
898     [[maybe_unused]] EtsHandleScope scope(coro);
899 
900     const auto exceptionHandler = [this, coro]() {
901         ASSERT(coro->HasPendingException());
902         [[maybe_unused]] ScopedNativeCodeThread nj(coro);
903         HandleUncaughtException();
904         UNREACHABLE();
905     };
906 
907     auto *klass = PlatformTypes(this)->coreAbcRuntimeLinker;
908     EtsHandle<EtsAbcRuntimeLinker> linkerHandle(coro, EtsAbcRuntimeLinker::FromEtsObject(EtsObject::Create(klass)));
909 
910     EtsHandle<EtsArrayObject<EtsObject>> pathsHandle(coro, EtsArrayObject<EtsObject>::Create(abcFiles.size()));
911     for (size_t idx = 0; idx < abcFiles.size(); ++idx) {
912         auto *str = EtsString::CreateFromMUtf8(abcFiles[idx].data(), abcFiles[idx].length());
913         if (UNLIKELY(str == nullptr)) {
914             // Handle possible OOM
915             exceptionHandler();
916         }
917         pathsHandle->SetRef(idx, str->AsObject());
918     }
919     std::array args {Value(linkerHandle->GetCoreType()), Value(nullptr), Value(pathsHandle->GetCoreType())};
920 
921     auto *ctor =
922         klass->GetDirectMethod(GetLanguageContext().GetCtorName(), "Lstd/core/RuntimeLinker;Lescompat/Array;:V");
923     ASSERT(ctor != nullptr);
924     ctor->GetPandaMethod()->InvokeVoid(coro, args.data());
925     if (coro->HasPendingException()) {
926         // Print exceptions thrown in constructor (e.g. if file not found) and exit
927         exceptionHandler();
928     }
929 
930     // Save global reference to created application `AbcRuntimeLinker`
931     GetGlobalObjectStorage()->Add(linkerHandle->GetCoreType(), mem::Reference::ObjectType::GLOBAL);
932     // Safe to return a non-managed object
933     return linkerHandle->GetClassLinkerContext();
934 }
935 
AddUnhandledObjectImpl(PandaUnorderedSet<EtsObject * > & unhandledObjects,EtsObject * object)936 void PandaEtsVM::AddUnhandledObjectImpl(PandaUnorderedSet<EtsObject *> &unhandledObjects, EtsObject *object)
937 {
938     os::memory::LockHolder lh(unhandledMutex_);
939     unhandledObjects.insert(object);
940 }
941 
RemoveUnhandledObjectImpl(PandaUnorderedSet<EtsObject * > & unhandledObjects,EtsObject * object)942 void PandaEtsVM::RemoveUnhandledObjectImpl(PandaUnorderedSet<EtsObject *> &unhandledObjects, EtsObject *object)
943 {
944     os::memory::LockHolder lh(unhandledMutex_);
945     unhandledObjects.erase(object);
946 }
947 
948 template <typename T>
CreateEtsObjectArrayFromNativeSet(EtsCoroutine * coro,const PandaUnorderedSet<EtsObject * > & unhandledObjects)949 static EtsArrayObject<EtsObject> *CreateEtsObjectArrayFromNativeSet(
950     EtsCoroutine *coro, const PandaUnorderedSet<EtsObject *> &unhandledObjects)
951 {
952     static_assert(std::is_same_v<T, EtsJob> || std::is_same_v<T, EtsPromise>);
953     const auto objCount = unhandledObjects.size();
954     auto *array = EtsArrayObject<EtsObject>::Create(objCount);
955     ASSERT(array != nullptr);
956     size_t i = 0;
957     for (auto *obj : unhandledObjects) {
958         ASSERT(obj != nullptr);
959         ASSERT(!obj->GetCoreType()->IsForwarded());
960         array->SetRef(i, T::FromEtsObject(obj)->GetValue(coro));
961         ++i;
962     }
963     return array;
964 }
965 
966 template <typename T>
ListUnhandledImpl(EtsClassLinker * etsClassLinker,EtsCoroutine * coro,EtsArrayObject<EtsObject> * errors)967 static void ListUnhandledImpl(EtsClassLinker *etsClassLinker, EtsCoroutine *coro, EtsArrayObject<EtsObject> *errors)
968 {
969     static_assert(std::is_same_v<T, EtsJob> || std::is_same_v<T, EtsPromise>);
970     EtsHandle<EtsArrayObject<EtsObject>> herrors(coro, errors);
971     auto *platformTypes = etsClassLinker->GetEtsClassLinkerExtension()->GetPlatformTypes();
972     ark::Method *method {};
973     if constexpr (std::is_same_v<T, EtsJob>) {
974         method = platformTypes->escompatProcessListUnhandledJobs->GetPandaMethod();
975         LOG(DEBUG, COROUTINES) << "List unhandled failed jobs";
976     } else if (std::is_same_v<T, EtsPromise>) {
977         method = platformTypes->escompatProcessListUnhandledPromises->GetPandaMethod();
978         LOG(DEBUG, COROUTINES) << "List unhandled rejected promises";
979     }
980     ASSERT(method != nullptr);
981     ASSERT(herrors.GetPtr() != nullptr);
982     std::array args = {Value(herrors->GetCoreType())};
983     method->InvokeVoid(coro, args.data());
984     LOG(DEBUG, COROUTINES) << "List unhandled rejections end";
985 }
986 
AddUnhandledFailedJob(EtsJob * job)987 void PandaEtsVM::AddUnhandledFailedJob(EtsJob *job)
988 {
989     AddUnhandledObjectImpl(unhandledFailedJobs_, job->AsObject());
990 }
991 
RemoveUnhandledFailedJob(EtsJob * job)992 void PandaEtsVM::RemoveUnhandledFailedJob(EtsJob *job)
993 {
994     ASSERT(job != nullptr);
995     RemoveUnhandledObjectImpl(unhandledFailedJobs_, job->AsObject());
996 }
997 
ListUnhandledFailedJobs()998 void PandaEtsVM::ListUnhandledFailedJobs()
999 {
1000     os::memory::LockHolder lh(unhandledMutex_);
1001     if (unhandledFailedJobs_.empty()) {
1002         return;
1003     }
1004     auto *coro = EtsCoroutine::GetCurrent();
1005     ASSERT(coro != nullptr);
1006     {
1007         [[maybe_unused]] ScopedManagedCodeThread sj(coro);
1008         [[maybe_unused]] EtsHandleScope scope(coro);
1009 
1010         auto *errors = CreateEtsObjectArrayFromNativeSet<EtsJob>(coro, unhandledFailedJobs_);
1011         ListUnhandledImpl<EtsJob>(GetClassLinker(), coro, errors);
1012         unhandledFailedJobs_.clear();
1013     }
1014     if (coro->HasPendingException()) {
1015         HandleUncaughtException();
1016         UNREACHABLE();
1017     }
1018 }
1019 
AddUnhandledRejectedPromise(EtsPromise * promise)1020 void PandaEtsVM::AddUnhandledRejectedPromise(EtsPromise *promise)
1021 {
1022     AddUnhandledObjectImpl(unhandledRejectedPromises_, promise->AsObject());
1023 }
1024 
RemoveUnhandledRejectedPromise(EtsPromise * promise)1025 void PandaEtsVM::RemoveUnhandledRejectedPromise(EtsPromise *promise)
1026 {
1027     ASSERT(promise != nullptr);
1028     RemoveUnhandledObjectImpl(unhandledRejectedPromises_, promise->AsObject());
1029 }
1030 
ListUnhandledRejectedPromises()1031 void PandaEtsVM::ListUnhandledRejectedPromises()
1032 {
1033     os::memory::LockHolder lh(unhandledMutex_);
1034     if (unhandledRejectedPromises_.empty()) {
1035         return;
1036     }
1037     auto *coro = EtsCoroutine::GetCurrent();
1038     ASSERT(coro != nullptr);
1039     {
1040         [[maybe_unused]] ScopedManagedCodeThread sj(coro);
1041         [[maybe_unused]] EtsHandleScope scope(coro);
1042 
1043         auto *errors = CreateEtsObjectArrayFromNativeSet<EtsPromise>(coro, unhandledRejectedPromises_);
1044         ListUnhandledImpl<EtsPromise>(GetClassLinker(), coro, errors);
1045         unhandledRejectedPromises_.clear();
1046     }
1047     if (coro->HasPendingException()) {
1048         HandleUncaughtException();
1049         UNREACHABLE();
1050     }
1051 }
1052 
1053 /* static */
Abort(const char * message)1054 void PandaEtsVM::Abort(const char *message /* = nullptr */)
1055 {
1056     Runtime::Abort(message);
1057 }
1058 }  // namespace ark::ets
1059