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