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