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