• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "plugins/ets/runtime/ets_vm.h"
17 #include <atomic>
18 
19 #include "compiler/optimizer/ir/runtime_interface.h"
20 #include "include/mem/panda_string.h"
21 #include "libpandabase/macros.h"
22 #include "plugins/ets/runtime/ets_class_linker_extension.h"
23 #include "plugins/ets/runtime/ets_coroutine.h"
24 #include "plugins/ets/runtime/ets_exceptions.h"
25 #include "plugins/ets/runtime/ets_handle.h"
26 #include "plugins/ets/runtime/ets_handle_scope.h"
27 #include "plugins/ets/runtime/ets_panda_file_items.h"
28 #include "plugins/ets/runtime/ets_runtime_interface.h"
29 #include "plugins/ets/runtime/mem/ets_reference_processor.h"
30 #include "plugins/ets/runtime/napi/ets_mangle.h"
31 #include "plugins/ets/runtime/napi/ets_napi_invoke_interface.h"
32 #include "plugins/ets/runtime/types/ets_method.h"
33 #include "plugins/ets/runtime/types/ets_promise.h"
34 #include "plugins/ets/runtime/types/ets_string.h"
35 #include "runtime/compiler.h"
36 #include "runtime/include/runtime.h"
37 #include "runtime/include/thread_scopes.h"
38 #include "runtime/init_icu.h"
39 #include "runtime/coroutines/stackful_coroutine_manager.h"
40 #include "runtime/coroutines/threaded_coroutine_manager.h"
41 #include "runtime/mem/lock_config_helper.h"
42 #include "plugins/ets/stdlib/native/init_native_methods.h"
43 #include "plugins/ets/runtime/types/ets_finalizable_weak_ref_list.h"
44 
45 #include "plugins/ets/runtime/intrinsics/helpers/ets_to_string_cache.h"
46 
47 namespace ark::ets {
48 // Create MemoryManager by RuntimeOptions
CreateMM(Runtime * runtime,const RuntimeOptions & options)49 static mem::MemoryManager *CreateMM(Runtime *runtime, const RuntimeOptions &options)
50 {
51     mem::MemoryManager::HeapOptions heapOptions {
52         nullptr,                                      // is_object_finalizeble_func
53         nullptr,                                      // register_finalize_reference_func
54         options.GetMaxGlobalRefSize(),                // max_global_ref_size
55         options.IsGlobalReferenceSizeCheckEnabled(),  // is_global_reference_size_check_enabled
56         MT_MODE_TASK,                                 // multithreading mode
57         options.IsUseTlabForAllocations(),            // is_use_tlab_for_allocations
58         options.IsStartAsZygote(),                    // is_start_as_zygote
59     };
60 
61     auto ctx = runtime->GetLanguageContext(panda_file::SourceLang::ETS);
62     auto allocator = runtime->GetInternalAllocator();
63 
64     mem::GCTriggerConfig gcTriggerConfig(options, panda_file::SourceLang::ETS);
65 
66     mem::GCSettings gcSettings(options, panda_file::SourceLang::ETS);
67 
68     auto gcType = Runtime::GetGCType(options, panda_file::SourceLang::ETS);
69 
70     return mem::MemoryManager::Create(ctx, allocator, gcType, gcSettings, gcTriggerConfig, heapOptions);
71 }
72 
73 /* static */
CreateTaskManagerIfNeeded(const RuntimeOptions & options)74 bool PandaEtsVM::CreateTaskManagerIfNeeded(const RuntimeOptions &options)
75 {
76     if (options.GetWorkersType() == "taskmanager" && Runtime::GetTaskScheduler() == nullptr) {
77         auto *taskScheduler = taskmanager::TaskScheduler::Create(
78             options.GetTaskmanagerWorkersCount(), taskmanager::StringToTaskTimeStats(options.GetTaskStatsType()));
79         if (taskScheduler == nullptr) {
80             return false;
81         }
82         Runtime::SetTaskScheduler(taskScheduler);
83     }
84     return true;
85 }
86 
87 /* static */
Create(Runtime * runtime,const RuntimeOptions & options)88 Expected<PandaEtsVM *, PandaString> PandaEtsVM::Create(Runtime *runtime, const RuntimeOptions &options)
89 {
90     ASSERT(runtime != nullptr);
91 
92     if (!PandaEtsVM::CreateTaskManagerIfNeeded(options)) {
93         return Unexpected(PandaString("Cannot create TaskManager"));
94     }
95 
96     auto mm = CreateMM(runtime, options);
97     if (mm == nullptr) {
98         return Unexpected(PandaString("Cannot create MemoryManager"));
99     }
100 
101     auto allocator = mm->GetHeapManager()->GetInternalAllocator();
102     auto vm = allocator->New<PandaEtsVM>(runtime, options, mm);
103     if (vm == nullptr) {
104         return Unexpected(PandaString("Cannot create PandaCoreVM"));
105     }
106 
107     auto classLinker = EtsClassLinker::Create(runtime->GetClassLinker());
108     if (!classLinker) {
109         allocator->Delete(vm);
110         mem::MemoryManager::Destroy(mm);
111         return Unexpected(classLinker.Error());
112     }
113     vm->classLinker_ = std::move(classLinker.Value());
114 
115     vm->InitializeGC();
116 
117     std::string icuPath = options.GetIcuDataPath();
118     if (icuPath == "default") {
119         SetIcuDirectory();
120     } else {
121         u_setDataDirectory(icuPath.c_str());
122     }
123 
124     CoroutineManagerConfig cfg {
125         // emulate_js
126         Runtime::GetOptions().IsCoroutineJsMode(plugins::LangToRuntimeType(panda_file::SourceLang::ETS)),
127         // workers_count
128         Runtime::GetOptions().GetCoroutineWorkersCount(plugins::LangToRuntimeType(panda_file::SourceLang::ETS)),
129         // enable perf stats
130         Runtime::GetOptions().IsCoroutineDumpStats(plugins::LangToRuntimeType(panda_file::SourceLang::ETS))};
131     vm->coroutineManager_->Initialize(cfg, runtime, vm);
132 
133     return vm;
134 }
135 
Destroy(PandaEtsVM * vm)136 bool PandaEtsVM::Destroy(PandaEtsVM *vm)
137 {
138     if (vm == nullptr) {
139         return false;
140     }
141 
142     vm->SaveProfileInfo();
143     vm->UninitializeThreads();
144     vm->StopGC();
145 
146     auto runtime = Runtime::GetCurrent();
147     runtime->GetInternalAllocator()->Delete(vm);
148 
149     return true;
150 }
151 
PandaEtsVM(Runtime * runtime,const RuntimeOptions & options,mem::MemoryManager * mm)152 PandaEtsVM::PandaEtsVM(Runtime *runtime, const RuntimeOptions &options, mem::MemoryManager *mm)
153     : EtsVM {napi::GetInvokeInterface()}, runtime_(runtime), mm_(mm)
154 {
155     ASSERT(runtime_ != nullptr);
156     ASSERT(mm_ != nullptr);
157 
158     auto heapManager = mm_->GetHeapManager();
159     auto allocator = heapManager->GetInternalAllocator();
160 
161     runtimeIface_ = allocator->New<EtsRuntimeInterface>();
162     compiler_ = allocator->New<Compiler>(heapManager->GetCodeAllocator(), allocator, options,
163                                          heapManager->GetMemStats(), runtimeIface_);
164     stringTable_ = allocator->New<StringTable>();
165     monitorPool_ = allocator->New<MonitorPool>(allocator);
166     referenceProcessor_ = allocator->New<mem::ets::EtsReferenceProcessor>(mm_->GetGC());
167 
168     auto langStr = plugins::LangToRuntimeType(panda_file::SourceLang::ETS);
169     auto coroType = options.GetCoroutineImpl(langStr);
170     if (coroType == "stackful") {
171         coroutineManager_ = allocator->New<StackfulCoroutineManager>(EtsCoroutine::Create<Coroutine>);
172     } else {
173         coroutineManager_ = allocator->New<ThreadedCoroutineManager>(EtsCoroutine::Create<Coroutine>);
174     }
175     rendezvous_ = allocator->New<Rendezvous>(this);
176 
177     InitializeRandomEngine();
178 }
179 
~PandaEtsVM()180 PandaEtsVM::~PandaEtsVM()
181 {
182     auto allocator = mm_->GetHeapManager()->GetInternalAllocator();
183     ASSERT(allocator != nullptr);
184 
185     allocator->Delete(rendezvous_);
186     allocator->Delete(runtimeIface_);
187     allocator->Delete(coroutineManager_);
188     allocator->Delete(referenceProcessor_);
189     allocator->Delete(monitorPool_);
190     allocator->Delete(stringTable_);
191     allocator->Delete(compiler_);
192 
193     if (destroyExternalData_) {
194         destroyExternalData_(&externalData_);
195     }
196 
197     ASSERT(mm_ != nullptr);
198     mm_->Finalize();
199     mem::MemoryManager::Destroy(mm_);
200 }
201 
GetCurrent()202 PandaEtsVM *PandaEtsVM::GetCurrent()
203 {
204     // Use Thread class for possible to use it from native and manage threads
205     return static_cast<PandaEtsVM *>(Thread::GetCurrent()->GetVM());
206 }
207 
PreallocSpecialReference(PandaEtsVM * vm,mem::Reference * & ref,const char * desc,bool nonMovable=false)208 static void PreallocSpecialReference(PandaEtsVM *vm, mem::Reference *&ref, const char *desc, bool nonMovable = false)
209 {
210     EtsClass *cls = vm->GetClassLinker()->GetClass(desc);
211     if (cls == nullptr) {
212         LOG(FATAL, RUNTIME) << "Cannot find a class for special object " << desc;
213     }
214     EtsObject *obj = nonMovable ? EtsObject::CreateNonMovable(cls) : EtsObject::Create(cls);
215     if (obj == nullptr) {
216         LOG(FATAL, RUNTIME) << "Cannot preallocate a special object " << desc;
217     }
218     ref = vm->GetGlobalObjectStorage()->Add(obj->GetCoreType(), ark::mem::Reference::ObjectType::GLOBAL);
219 }
220 
Initialize()221 bool PandaEtsVM::Initialize()
222 {
223     if (!ark::intrinsics::Initialize(ark::panda_file::SourceLang::ETS)) {
224         LOG(ERROR, RUNTIME) << "Failed to initialize eTS intrinsics";
225         return false;
226     }
227 
228     if (!classLinker_->Initialize()) {
229         LOG(FATAL, ETS) << "Cannot initialize ets class linker";
230     }
231     classLinker_->GetEtsClassLinkerExtension()->InitializeBuiltinClasses();
232 
233     if (Runtime::GetOptions().ShouldLoadBootPandaFiles()) {
234         // NOLINTNEXTLINE(google-build-using-namespace)
235         using namespace panda_file_items::class_descriptors;
236 
237         PreallocSpecialReference(this, oomObjRef_, OUT_OF_MEMORY_ERROR.data());
238         PreallocSpecialReference(this, undefinedObjRef_, INTERNAL_UNDEFINED.data(), true);
239         PreallocSpecialReference(this, finalizableWeakRefList_, FINALIZABLE_WEAK_REF.data());
240 
241         if (Thread::GetCurrent() != nullptr) {
242             ASSERT(GetThreadManager()->GetMainThread() == Thread::GetCurrent());
243             auto *coro = EtsCoroutine::GetCurrent();
244             coro->SetUndefinedObject(GetUndefinedObject());
245 
246             doubleToStringCache_ = DoubleToStringCache::Create(coro);
247             floatToStringCache_ = FloatToStringCache::Create(coro);
248             longToStringCache_ = LongToStringCache::Create(coro);
249         }
250         referenceProcessor_->Initialize();
251     }
252     [[maybe_unused]] bool cachesCreated =
253         (doubleToStringCache_ != nullptr && floatToStringCache_ != nullptr && longToStringCache_ != nullptr);
254     LOG_IF(!cachesCreated, WARNING, ETS) << "Cannot initialize number-to-string caches";
255     LOG_IF(cachesCreated, DEBUG, ETS) << "Initialized number-to-string caches";
256 
257     // Check if Intrinsics/native methods should be initialized, we don't want to attempt to
258     // initialize  native methods in certain scenarios where we don't have ets stdlib at our disposal
259     if (Runtime::GetOptions().ShouldInitializeIntrinsics()) {
260         // NOTE(ksarychev, #18135): Implement napi module registration via loading a separate
261         // library
262         EtsEnv *env = EtsCoroutine::GetCurrent()->GetEtsNapiEnv();
263         ark::ets::stdlib::InitNativeMethods(env);
264     }
265 
266     const auto lang = plugins::LangToRuntimeType(panda_file::SourceLang::ETS);
267     for (const auto &path : Runtime::GetOptions().GetNativeLibraryPath(lang)) {
268         nativeLibraryProvider_.AddLibraryPath(ConvertToString(path));
269     }
270 
271     return true;
272 }
273 
InitializeFinish()274 bool PandaEtsVM::InitializeFinish()
275 {
276     if (Runtime::GetOptions().ShouldLoadBootPandaFiles()) {
277         // Preinitialize StackOverflowError, so we don't need to do this when stack overflow occurred
278         EtsClass *cls = classLinker_->GetClass(panda_file_items::class_descriptors::STACK_OVERFLOW_ERROR.data());
279         if (cls == nullptr) {
280             LOG(FATAL, ETS) << "Cannot preinitialize StackOverflowError";
281             return false;
282         }
283     }
284     return true;
285 }
286 
UninitializeThreads()287 void PandaEtsVM::UninitializeThreads()
288 {
289     // Wait until all threads finish the work
290     coroutineManager_->WaitForDeregistration();
291     coroutineManager_->DestroyMainCoroutine();
292     coroutineManager_->Finalize();
293 }
294 
PreStartup()295 void PandaEtsVM::PreStartup()
296 {
297     ASSERT(mm_ != nullptr);
298 
299     mm_->PreStartup();
300 }
301 
PreZygoteFork()302 void PandaEtsVM::PreZygoteFork()
303 {
304     ASSERT(mm_ != nullptr);
305     ASSERT(compiler_ != nullptr);
306 
307     mm_->PreZygoteFork();
308     compiler_->PreZygoteFork();
309 }
310 
PostZygoteFork()311 void PandaEtsVM::PostZygoteFork()
312 {
313     ASSERT(compiler_ != nullptr);
314     ASSERT(mm_ != nullptr);
315 
316     compiler_->PostZygoteFork();
317     mm_->PostZygoteFork();
318 }
319 
InitializeGC()320 void PandaEtsVM::InitializeGC()
321 {
322     ASSERT(mm_ != nullptr);
323 
324     mm_->InitializeGC(this);
325 }
326 
StartGC()327 void PandaEtsVM::StartGC()
328 {
329     ASSERT(mm_ != nullptr);
330 
331     mm_->StartGC();
332 }
333 
StopGC()334 void PandaEtsVM::StopGC()
335 {
336     ASSERT(mm_ != nullptr);
337 
338     if (GetGC()->IsGCRunning()) {
339         mm_->StopGC();
340     }
341 }
342 
HandleReferences(const GCTask & task,const mem::GC::ReferenceClearPredicateT & pred)343 void PandaEtsVM::HandleReferences(const GCTask &task, const mem::GC::ReferenceClearPredicateT &pred)
344 {
345     ASSERT(mm_ != nullptr);
346 
347     auto gc = mm_->GetGC();
348     ASSERT(gc != nullptr);
349 
350     LOG(DEBUG, REF_PROC) << "Start processing cleared references";
351     gc->ProcessReferences(gc->GetGCPhase(), task, pred);
352     LOG(DEBUG, REF_PROC) << "Finish processing cleared references";
353 
354     GetGlobalObjectStorage()->ClearUnmarkedWeakRefs(gc, pred);
355 }
356 
HandleGCRoutineInMutator()357 void PandaEtsVM::HandleGCRoutineInMutator()
358 {
359     // Handle references only in coroutine
360     ASSERT(Coroutine::GetCurrent() != nullptr);
361     ASSERT(GetMutatorLock()->HasLock());
362     auto coroutine = EtsCoroutine::GetCurrent();
363     [[maybe_unused]] HandleScope<ObjectHeader *> handleScope(coroutine);
364     os::memory::LockHolder lock(finalizationRegistryLock_);
365     if (!registeredFinalizationRegistryInstances_.empty()) {
366         EtsClass *finalizationRegistryClass = registeredFinalizationRegistryInstances_.front()->GetClass();
367         EtsMethod *cleanup = finalizationRegistryClass->GetMethod("cleanup");
368         for (auto *entry : registeredFinalizationRegistryInstances_) {
369             VMHandle<ObjectHeader> handle(coroutine, entry->GetCoreType());
370             Value arg(handle.GetPtr());
371             finalizationRegistryLock_.Unlock();
372             cleanup->GetPandaMethod()->Invoke(coroutine, &arg);
373             finalizationRegistryLock_.Lock();
374             ASSERT(!coroutine->HasPendingException());
375         }
376     }
377     coroutine->GetPandaVM()->CleanFinalizableReferenceList();
378 }
379 
HandleGCFinished()380 void PandaEtsVM::HandleGCFinished() {}
381 
CheckEntrypointSignature(Method * entrypoint)382 bool PandaEtsVM::CheckEntrypointSignature(Method *entrypoint)
383 {
384     ASSERT(entrypoint != nullptr);
385 
386     if (entrypoint->GetNumArgs() == 0) {
387         return true;
388     }
389 
390     if (entrypoint->GetNumArgs() > 1) {
391         return false;
392     }
393 
394     auto *pf = entrypoint->GetPandaFile();
395     ASSERT(pf != nullptr);
396     panda_file::MethodDataAccessor mda(*pf, entrypoint->GetFileId());
397     panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
398 
399     if (pda.GetArgType(0).GetId() != panda_file::Type::TypeId::REFERENCE) {
400         return false;
401     }
402 
403     auto name = pf->GetStringData(pda.GetReferenceType(0));
404     std::string_view expectedName(panda_file_items::class_descriptors::STRING_ARRAY);
405 
406     return utf::IsEqual({name.data, name.utf16Length},
407                         {utf::CStringAsMutf8(expectedName.data()), expectedName.length()});
408 }
409 
CreateArgumentsArray(const std::vector<std::string> & args,PandaEtsVM * etsVm)410 static EtsObjectArray *CreateArgumentsArray(const std::vector<std::string> &args, PandaEtsVM *etsVm)
411 {
412     ASSERT(etsVm != nullptr);
413 
414     const char *classDescripor = panda_file_items::class_descriptors::STRING_ARRAY.data();
415     EtsClass *arrayKlass = etsVm->GetClassLinker()->GetClass(classDescripor);
416     if (arrayKlass == nullptr) {
417         LOG(FATAL, RUNTIME) << "Class " << classDescripor << " not found";
418         return nullptr;
419     }
420 
421     EtsCoroutine *coroutine = EtsCoroutine::GetCurrent();
422     [[maybe_unused]] EtsHandleScope scope(coroutine);
423     EtsObjectArray *etsArray = EtsObjectArray::Create(arrayKlass, args.size());
424     EtsHandle<EtsObjectArray> arrayHandle(coroutine, etsArray);
425 
426     for (size_t i = 0; i < args.size(); i++) {
427         EtsString *str = EtsString::CreateFromMUtf8(args[i].data(), args[i].length());
428         arrayHandle.GetPtr()->Set(i, str->AsObject());
429     }
430 
431     return arrayHandle.GetPtr();
432 }
433 
CreateString(Method * ctor,ObjectHeader * obj)434 coretypes::String *PandaEtsVM::CreateString(Method *ctor, ObjectHeader *obj)
435 {
436     EtsString *str = nullptr;
437     ASSERT(ctor->GetNumArgs() > 0);  // must be at list this argument
438     if (ctor->GetNumArgs() == 1) {
439         str = EtsString::CreateNewEmptyString();
440     } else if (ctor->GetNumArgs() == 2U) {
441         ASSERT(ctor->GetArgType(1).GetId() == panda_file::Type::TypeId::REFERENCE);
442         auto *strData = utf::Mutf8AsCString(ctor->GetRefArgType(1).data);
443         if (std::strcmp("[C", strData) == 0) {
444             auto *array = reinterpret_cast<EtsArray *>(obj);
445             str = EtsString::CreateNewStringFromChars(0, array->GetLength(), array);
446         } else if ((std::strcmp("Lstd/core/String;", strData) == 0)) {
447             str = EtsString::CreateNewStringFromString(reinterpret_cast<EtsString *>(obj));
448         } else {
449             LOG(FATAL, ETS) << "Non-existent ctor";
450         }
451     } else {
452         LOG(FATAL, ETS) << "Must be 1 or 2 ctor args";
453     }
454     return str->GetCoreType();
455 }
456 
InvokeEntrypointImpl(Method * entrypoint,const std::vector<std::string> & args)457 Expected<int, Runtime::Error> PandaEtsVM::InvokeEntrypointImpl(Method *entrypoint, const std::vector<std::string> &args)
458 {
459     ASSERT(Runtime::GetCurrent()->GetLanguageContext(*entrypoint).GetLanguage() == panda_file::SourceLang::ETS);
460 
461     EtsCoroutine *coroutine = EtsCoroutine::GetCurrent();
462     ASSERT(coroutine != nullptr);
463 
464     ScopedManagedCodeThread sj(coroutine);
465     if (!classLinker_->InitializeClass(coroutine, EtsClass::FromRuntimeClass(entrypoint->GetClass()))) {
466         LOG(ERROR, RUNTIME) << "Cannot initialize class '" << entrypoint->GetClass()->GetName() << "'";
467         return Unexpected(Runtime::Error::CLASS_NOT_INITIALIZED);
468     }
469 
470     [[maybe_unused]] EtsHandleScope scope(coroutine);
471     if (entrypoint->GetNumArgs() == 0) {
472         auto v = entrypoint->Invoke(coroutine, nullptr);
473         return v.GetAs<int>();
474     }
475 
476     if (entrypoint->GetNumArgs() == 1) {
477         EtsObjectArray *etsObjectArray = CreateArgumentsArray(args, this);
478         EtsHandle<EtsObjectArray> argsHandle(coroutine, etsObjectArray);
479         Value argVal(argsHandle.GetPtr()->AsObject()->GetCoreType());
480         auto v = entrypoint->Invoke(coroutine, &argVal);
481 
482         return v.GetAs<int>();
483     }
484 
485     // What if entrypoint->GetNumArgs() > 1 ?
486     LOG(ERROR, RUNTIME) << "ets entrypoint has args count more than 1 : " << entrypoint->GetNumArgs();
487     return Unexpected(Runtime::Error::INVALID_ENTRY_POINT);
488 }
489 
GetOOMErrorObject()490 ObjectHeader *PandaEtsVM::GetOOMErrorObject()
491 {
492     auto obj = GetGlobalObjectStorage()->Get(oomObjRef_);
493     ASSERT(obj != nullptr);
494     return obj;
495 }
496 
GetUndefinedObject()497 ObjectHeader *PandaEtsVM::GetUndefinedObject()
498 {
499     auto obj = GetGlobalObjectStorage()->Get(undefinedObjRef_);
500     ASSERT(obj != nullptr);
501     return obj;
502 }
503 
LoadNativeLibrary(EtsEnv * env,const PandaString & name)504 bool PandaEtsVM::LoadNativeLibrary(EtsEnv *env, const PandaString &name)
505 {
506     ASSERT_PRINT(Coroutine::GetCurrent()->IsInNativeCode(), "LoadNativeLibrary must be called at native");
507 
508     if (auto error = nativeLibraryProvider_.LoadLibrary(env, name)) {
509         LOG(ERROR, RUNTIME) << "Cannot load library " << name << ": " << error.value();
510         return false;
511     }
512 
513     return true;
514 }
515 
ResolveNativeMethod(Method * method)516 void PandaEtsVM::ResolveNativeMethod(Method *method)
517 {
518     ASSERT_PRINT(method->IsNative(), "Method should be native");
519     std::string className = utf::Mutf8AsCString(method->GetClassName().data);
520     std::string methodName = utf::Mutf8AsCString(method->GetName().data);
521     std::string name = MangleMethodName(className, methodName);
522     auto ptr = nativeLibraryProvider_.ResolveSymbol(PandaString(name));
523     if (ptr == nullptr) {
524         std::string signature;
525         signature.append(EtsMethod::FromRuntimeMethod(method)->GetMethodSignature(false));
526         name = MangleMethodNameWithSignature(name, signature);
527         ptr = nativeLibraryProvider_.ResolveSymbol(PandaString(name));
528         if (ptr == nullptr) {
529             PandaStringStream ss;
530             ss << "No implementation found for " << method->GetFullName() << ", tried " << name;
531 
532             auto coroutine = EtsCoroutine::GetCurrent();
533             ThrowEtsException(coroutine, panda_file_items::class_descriptors::NO_SUCH_METHOD_ERROR, ss.str());
534             return;
535         }
536     }
537 
538     method->SetNativePointer(ptr);
539 }
540 
PrintExceptionInfo(EtsCoroutine * coro,EtsHandle<EtsObject> exception,PandaStringStream & ss)541 static void PrintExceptionInfo(EtsCoroutine *coro, EtsHandle<EtsObject> exception, PandaStringStream &ss)
542 {
543     auto extension = coro->GetPandaVM()->GetEtsClassLinkerExtension();
544     auto cls = exception->GetClass();
545 
546     PandaVector<uint8_t> strBuf;
547     auto const performCall = [coro, &exception, &strBuf](EtsMethod *method) -> std::optional<std::string_view> {
548         ASSERT(method != nullptr);
549         std::array<Value, 1> args = {Value(exception->GetCoreType())};
550         EtsObject *callRes = EtsObject::FromCoreType(
551             EtsMethod::ToRuntimeMethod(method)->Invoke(coro, args.data()).GetAs<ObjectHeader *>());
552         if (coro->HasPendingException() || callRes == EtsObject::FromCoreType(coro->GetUndefinedObject())) {
553             return std::nullopt;
554         }
555         return EtsString::FromEtsObject(callRes)->ConvertToStringView(&strBuf);
556     };
557 
558     char const *dumperName =
559         extension->GetErrorClass()->IsAssignableFrom(cls->GetRuntimeClass()) ? "<get>stack" : "toString";
560     ss << std::endl << performCall(cls->GetMethod(dumperName)).value_or("exception dump failed");
561 }
562 
HandleUncaughtException()563 void PandaEtsVM::HandleUncaughtException()
564 {
565     auto coro = EtsCoroutine::GetCurrent();
566     ASSERT(coro != nullptr);
567     ScopedManagedCodeThread sj(coro);
568     [[maybe_unused]] EtsHandleScope scope(coro);
569 
570     EtsHandle<EtsObject> exception(coro, EtsObject::FromCoreType(coro->GetException()));
571 
572     PandaStringStream logStream;
573     logStream << "Unhandled exception: " << exception->GetCoreType()->ClassAddr<Class>()->GetName();
574 
575     auto descr = exception->GetClass()->GetDescriptor();
576     if (descr != panda_file_items::class_descriptors::OUT_OF_MEMORY_ERROR &&
577         descr != panda_file_items::class_descriptors::STACK_OVERFLOW_ERROR) {
578         coro->ClearException();
579         PrintExceptionInfo(coro, exception, logStream);
580     }
581     LOG(ERROR, RUNTIME) << logStream.str();
582     // _exit guarantees a safe completion in case of multi-threading as static destructors aren't called
583     _exit(1);
584 }
585 
SweepVmRefs(const GCObjectVisitor & gcObjectVisitor)586 void PandaEtsVM::SweepVmRefs(const GCObjectVisitor &gcObjectVisitor)
587 {
588     PandaVM::SweepVmRefs(gcObjectVisitor);
589     {
590         os::memory::LockHolder lock(finalizationRegistryLock_);
591         auto it = registeredFinalizationRegistryInstances_.begin();
592         while (it != registeredFinalizationRegistryInstances_.end()) {
593             if (gcObjectVisitor((*it)->GetCoreType()) == ObjectStatus::DEAD_OBJECT) {
594                 auto toRemove = it++;
595                 registeredFinalizationRegistryInstances_.erase(toRemove);
596             } else {
597                 ++it;
598             }
599         }
600     }
601 }
602 
HandleEmptyArguments(const PandaVector<Value> & arguments,const GCRootVisitor & visitor,const EtsCoroutine * coroutine)603 void HandleEmptyArguments(const PandaVector<Value> &arguments, const GCRootVisitor &visitor,
604                           const EtsCoroutine *coroutine)
605 {
606     // arguments may be empty in the following cases:
607     // 1. The entrypoint is static and doesn't accept any arguments
608     // 2. The coroutine is launched.
609     // 3. The entrypoint is the main method
610     Method *entrypoint = coroutine->GetManagedEntrypoint();
611     panda_file::ShortyIterator it(entrypoint->GetShorty());
612     size_t argIdx = 0;
613     ++it;  // skip return type
614     if (!entrypoint->IsStatic()) {
615         // handle 'this' argument
616         ASSERT(arguments[argIdx].IsReference());
617         ObjectHeader *arg = arguments[argIdx].GetAs<ObjectHeader *>();
618         ASSERT(arg != nullptr);
619         visitor(mem::GCRoot(mem::RootType::ROOT_THREAD, arg));
620         ++argIdx;
621     }
622     while (it != panda_file::ShortyIterator()) {
623         if ((*it).GetId() == panda_file::Type::TypeId::REFERENCE) {
624             ASSERT(arguments[argIdx].IsReference());
625             ObjectHeader *arg = arguments[argIdx].GetAs<ObjectHeader *>();
626             if (arg != nullptr) {
627                 visitor(mem::GCRoot(mem::RootType::ROOT_THREAD, arg));
628             }
629         }
630         ++it;
631         ++argIdx;
632     }
633 }
634 
VisitVmRoots(const GCRootVisitor & visitor)635 void PandaEtsVM::VisitVmRoots(const GCRootVisitor &visitor)
636 {
637     GetThreadManager()->EnumerateThreads([visitor](ManagedThread *thread) {
638         const auto coroutine = EtsCoroutine::CastFromThread(thread);
639         if (auto etsNapiEnv = coroutine->GetEtsNapiEnv()) {
640             auto etsStorage = etsNapiEnv->GetEtsReferenceStorage();
641             etsStorage->GetAsReferenceStorage()->VisitObjects(visitor, mem::RootType::ROOT_NATIVE_LOCAL);
642         }
643         if (!coroutine->HasManagedEntrypoint()) {
644             return true;
645         }
646         const PandaVector<Value> &arguments = coroutine->GetManagedEntrypointArguments();
647         if (!arguments.empty()) {
648             HandleEmptyArguments(arguments, visitor, coroutine);
649         }
650         return true;
651     });
652     visitor(mem::GCRoot(mem::RootType::ROOT_VM, doubleToStringCache_->GetCoreType()));
653     visitor(mem::GCRoot(mem::RootType::ROOT_VM, floatToStringCache_->GetCoreType()));
654     visitor(mem::GCRoot(mem::RootType::ROOT_VM, longToStringCache_->GetCoreType()));
655 }
656 
657 template <bool REF_CAN_BE_NULL>
UpdateMovedVmRef(Value & ref)658 void PandaEtsVM::UpdateMovedVmRef(Value &ref)
659 {
660     ASSERT(ref.IsReference());
661     ObjectHeader *arg = ref.GetAs<ObjectHeader *>();
662     if constexpr (REF_CAN_BE_NULL) {
663         if (arg == nullptr) {
664             return;
665         }
666     } else {
667         ASSERT(arg != nullptr);
668     }
669     if (arg->IsForwarded()) {
670         ObjectHeader *forwardAddress = mem::GetForwardAddress(arg);
671         ref = Value(forwardAddress);
672         LOG(DEBUG, GC) << "Forward root object: " << arg << " -> " << forwardAddress;
673     }
674 }
675 
UpdateManagedEntrypointArgRefs(EtsCoroutine * coroutine)676 void PandaEtsVM::UpdateManagedEntrypointArgRefs(EtsCoroutine *coroutine)
677 {
678     PandaVector<Value> &arguments = coroutine->GetManagedEntrypointArguments();
679     if (!arguments.empty()) {
680         // arguments may be empty in the following cases:
681         // 1. The entrypoint is static and doesn't accept any arguments
682         // 2. The coroutine is launched.
683         // 3. The entrypoint is the main method
684         Method *entrypoint = coroutine->GetManagedEntrypoint();
685         panda_file::ShortyIterator it(entrypoint->GetShorty());
686         size_t argIdx = 0;
687         ++it;  // skip return type
688         if (!entrypoint->IsStatic()) {
689             // handle 'this' argument
690             UpdateMovedVmRef<false>(arguments[argIdx]);
691             ++argIdx;
692         }
693         while (it != panda_file::ShortyIterator()) {
694             if ((*it).GetId() == panda_file::Type::TypeId::REFERENCE) {
695                 UpdateMovedVmRef<true>(arguments[argIdx]);
696             }
697             ++it;
698             ++argIdx;
699         }
700     }
701 }
702 
UpdateVmRefs()703 void PandaEtsVM::UpdateVmRefs()
704 {
705     GetThreadManager()->EnumerateThreads([](ManagedThread *thread) {
706         auto coroutine = EtsCoroutine::CastFromThread(thread);
707         if (auto etsNapiEnv = coroutine->GetEtsNapiEnv()) {
708             auto etsStorage = etsNapiEnv->GetEtsReferenceStorage();
709             etsStorage->GetAsReferenceStorage()->UpdateMovedRefs();
710         }
711         if (!coroutine->HasManagedEntrypoint()) {
712             return true;
713         }
714         UpdateManagedEntrypointArgRefs(coroutine);
715         return true;
716     });
717 
718     {
719         os::memory::LockHolder lock(finalizationRegistryLock_);
720         for (auto &entry : registeredFinalizationRegistryInstances_) {
721             if ((entry->GetCoreType())->IsForwarded()) {
722                 entry = EtsObject::FromCoreType(ark::mem::GetForwardAddress(entry->GetCoreType()));
723             }
724         }
725     }
726 }
727 
EtsNapiObjectToGlobalReference(ets_object globalRef)728 static mem::Reference *EtsNapiObjectToGlobalReference(ets_object globalRef)
729 {
730     ASSERT(globalRef != nullptr);
731     auto *ref = reinterpret_cast<mem::Reference *>(globalRef);
732     return ref;
733 }
734 
DeleteGlobalRef(ets_object globalRef)735 void PandaEtsVM::DeleteGlobalRef(ets_object globalRef)
736 {
737     auto *ref = EtsNapiObjectToGlobalReference(globalRef);
738     if (!ref->IsGlobal()) {
739         LOG(FATAL, ETS_NAPI) << "Try to remote non-global ref: " << std::hex << ref;
740         return;
741     }
742     GetGlobalObjectStorage()->Remove(ref);
743 }
744 
EtsNapiObjectToWeakReference(ets_weak weakRef)745 static mem::Reference *EtsNapiObjectToWeakReference(ets_weak weakRef)
746 {
747     ASSERT(weakRef != nullptr);
748     auto *ref = reinterpret_cast<mem::Reference *>(weakRef);
749     return ref;
750 }
751 
DeleteWeakGlobalRef(ets_weak weakRef)752 void PandaEtsVM::DeleteWeakGlobalRef(ets_weak weakRef)
753 {
754     auto *ref = EtsNapiObjectToWeakReference(weakRef);
755     if (!ref->IsWeak()) {
756         LOG(FATAL, ETS_NAPI) << "Try to remote non-weak ref: " << std::hex << ref;
757         return;
758     }
759     GetGlobalObjectStorage()->Remove(ref);
760 }
761 
RegisterFinalizationRegistryInstance(EtsObject * instance)762 void PandaEtsVM::RegisterFinalizationRegistryInstance(EtsObject *instance)
763 {
764     os::memory::LockHolder lock(finalizationRegistryLock_);
765     registeredFinalizationRegistryInstances_.push_back(instance);
766 }
767 
RegisterFinalizerForObject(EtsCoroutine * coro,const EtsHandle<EtsObject> & object,void (* finalizer)(void *),void * finalizerArg)768 void PandaEtsVM::RegisterFinalizerForObject(EtsCoroutine *coro, const EtsHandle<EtsObject> &object,
769                                             void (*finalizer)(void *), void *finalizerArg)
770 {
771     ASSERT_MANAGED_CODE();
772     auto *weakRef = EtsFinalizableWeakRef::Create(coro);
773     weakRef->SetFinalizer(finalizer, finalizerArg);
774     weakRef->SetReferent(object.GetPtr());
775     auto *coreList = GetGlobalObjectStorage()->Get(finalizableWeakRefList_);
776     auto *weakRefList = EtsFinalizableWeakRefList::FromCoreType(coreList);
777     os::memory::LockHolder lh(finalizableWeakRefListLock_);
778     weakRefList->Push(coro, weakRef);
779 }
780 
CleanFinalizableReferenceList()781 void PandaEtsVM::CleanFinalizableReferenceList()
782 {
783     auto *coreList = GetGlobalObjectStorage()->Get(finalizableWeakRefList_);
784     auto *weakRefList = EtsFinalizableWeakRefList::FromCoreType(coreList);
785     os::memory::LockHolder lh(finalizableWeakRefListLock_);
786     weakRefList->UnlinkClearedReferences(EtsCoroutine::GetCurrent());
787 }
788 
BeforeShutdown()789 void PandaEtsVM::BeforeShutdown()
790 {
791     ScopedManagedCodeThread managedScope(EtsCoroutine::GetCurrent());
792     auto *coreList = GetGlobalObjectStorage()->Get(finalizableWeakRefList_);
793     auto *weakRefList = EtsFinalizableWeakRefList::FromCoreType(coreList);
794     weakRefList->TraverseAndFinalize();
795 }
796 
797 /* static */
Abort(const char * message)798 void PandaEtsVM::Abort(const char *message /* = nullptr */)
799 {
800     Runtime::Abort(message);
801 }
802 }  // namespace ark::ets
803