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