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