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