1 /**
2 * Copyright (c) 2021-2025 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_class_linker_extension.h"
17
18 #include "include/method.h"
19 #include "include/thread_scopes.h"
20 #include "libpandabase/macros.h"
21 #include "libpandabase/utils/logger.h"
22 #include "plugins/ets/runtime/ets_annotation.h"
23 #include "plugins/ets/runtime/ets_coroutine.h"
24 #include "plugins/ets/runtime/ets_exceptions.h"
25 #include "plugins/ets/runtime/ets_panda_file_items.h"
26 #include "plugins/ets/runtime/ets_vm.h"
27 #include "plugins/ets/runtime/napi/ets_napi_helpers.h"
28 #include "plugins/ets/runtime/types/ets_abc_runtime_linker.h"
29 #include "plugins/ets/runtime/types/ets_method.h"
30 #include "runtime/class_linker_context.h"
31 #include "runtime/include/class_linker_extension.h"
32 #include "runtime/include/class_linker-inl.h"
33 #include "runtime/include/language_context.h"
34 #include "runtime/include/mem/panda_string.h"
35 #include "runtime/include/panda_vm.h"
36 #include "runtime/mem/heap_manager.h"
37
38 namespace ark::ets {
39 namespace {
40 enum class EtsNapiType {
41 GENERIC, // - Switches the coroutine to native mode (GC is allowed)
42 // - Prepends the argument list with two additional arguments (NAPI environment and this / class object)
43
44 FAST, // - Leaves the coroutine in managed mode (GC is not allowed)
45 // - Prepends the argument list with two additional arguments (NAPI environment and this / class object)
46 // - !!! The native function should not make any allocations (GC may be triggered during an allocation)
47
48 CRITICAL // - Leaves the coroutine in managed mode (GC is not allowed)
49 // - Passes the arguments as is (the callee method should be static)
50 // - !!! The native function should not make any allocations (GC may be triggered during an allocation)
51 };
52 } // namespace
53
54 extern "C" void EtsAsyncEntryPoint();
55
GetEtsNapiType(Method * method)56 static EtsNapiType GetEtsNapiType(Method *method)
57 {
58 EtsNapiType napiType = EtsNapiType::GENERIC;
59 const panda_file::File &pf = *method->GetPandaFile();
60 panda_file::MethodDataAccessor mda(pf, method->GetFileId());
61 mda.EnumerateAnnotations([&pf, &napiType](panda_file::File::EntityId annId) {
62 panda_file::AnnotationDataAccessor ada(pf, annId);
63 const char *className = utf::Mutf8AsCString(pf.GetStringData(ada.GetClassId()).data);
64 if (className == panda_file_items::class_descriptors::ANI_UNSAFE_QUICK) {
65 napiType = EtsNapiType::FAST;
66 } else if (className == panda_file_items::class_descriptors::ANI_UNSAFE_DIRECT) {
67 napiType = EtsNapiType::CRITICAL;
68 }
69 });
70 return napiType;
71 }
72
GetClassLinkerErrorDescriptor(ClassLinker::Error error)73 static std::string_view GetClassLinkerErrorDescriptor(ClassLinker::Error error)
74 {
75 switch (error) {
76 case ClassLinker::Error::CLASS_NOT_FOUND:
77 return panda_file_items::class_descriptors::LINKER_CLASS_NOT_FOUND_ERROR;
78 case ClassLinker::Error::FIELD_NOT_FOUND:
79 return panda_file_items::class_descriptors::LINKER_UNRESOLVED_FIELD_ERROR;
80 case ClassLinker::Error::METHOD_NOT_FOUND:
81 return panda_file_items::class_descriptors::LINKER_UNRESOLVED_METHOD_ERROR;
82 case ClassLinker::Error::NO_CLASS_DEF:
83 return panda_file_items::class_descriptors::LINKER_UNRESOLVED_CLASS_ERROR;
84 case ClassLinker::Error::CLASS_CIRCULARITY:
85 return panda_file_items::class_descriptors::LINKER_TYPE_CIRCULARITY_ERROR;
86 case ClassLinker::Error::OVERRIDES_FINAL:
87 case ClassLinker::Error::MULTIPLE_OVERRIDE:
88 case ClassLinker::Error::MULTIPLE_IMPLEMENT:
89 return panda_file_items::class_descriptors::LINKER_METHOD_CONFLICT_ERROR;
90 default:
91 LOG(FATAL, CLASS_LINKER) << "Unhandled class linker error (" << helpers::ToUnderlying(error) << "): ";
92 UNREACHABLE();
93 }
94 }
95
OnError(ClassLinker::Error error,const PandaString & message)96 void EtsClassLinkerExtension::ErrorHandler::OnError(ClassLinker::Error error, const PandaString &message)
97 {
98 ThrowEtsException(EtsCoroutine::GetCurrent(), GetClassLinkerErrorDescriptor(error), message);
99 }
100
InitializeClassRoots()101 void EtsClassLinkerExtension::InitializeClassRoots()
102 {
103 InitializeArrayClassRoot(ClassRoot::ARRAY_CLASS, ClassRoot::CLASS,
104 utf::Mutf8AsCString(langCtx_.GetClassArrayClassDescriptor()));
105
106 InitializePrimitiveClassRoot(ClassRoot::V, panda_file::Type::TypeId::VOID, "V");
107 InitializePrimitiveClassRoot(ClassRoot::U1, panda_file::Type::TypeId::U1, "Z");
108 InitializePrimitiveClassRoot(ClassRoot::I8, panda_file::Type::TypeId::I8, "B");
109 InitializePrimitiveClassRoot(ClassRoot::U8, panda_file::Type::TypeId::U8, "H");
110 InitializePrimitiveClassRoot(ClassRoot::I16, panda_file::Type::TypeId::I16, "S");
111 InitializePrimitiveClassRoot(ClassRoot::U16, panda_file::Type::TypeId::U16, "C");
112 InitializePrimitiveClassRoot(ClassRoot::I32, panda_file::Type::TypeId::I32, "I");
113 InitializePrimitiveClassRoot(ClassRoot::U32, panda_file::Type::TypeId::U32, "U");
114 InitializePrimitiveClassRoot(ClassRoot::I64, panda_file::Type::TypeId::I64, "J");
115 InitializePrimitiveClassRoot(ClassRoot::U64, panda_file::Type::TypeId::U64, "Q");
116 InitializePrimitiveClassRoot(ClassRoot::F32, panda_file::Type::TypeId::F32, "F");
117 InitializePrimitiveClassRoot(ClassRoot::F64, panda_file::Type::TypeId::F64, "D");
118 InitializePrimitiveClassRoot(ClassRoot::TAGGED, panda_file::Type::TypeId::TAGGED, "A");
119
120 InitializeArrayClassRoot(ClassRoot::ARRAY_U1, ClassRoot::U1, "[Z");
121 InitializeArrayClassRoot(ClassRoot::ARRAY_I8, ClassRoot::I8, "[B");
122 InitializeArrayClassRoot(ClassRoot::ARRAY_U8, ClassRoot::U8, "[H");
123 InitializeArrayClassRoot(ClassRoot::ARRAY_I16, ClassRoot::I16, "[S");
124 InitializeArrayClassRoot(ClassRoot::ARRAY_U16, ClassRoot::U16, "[C");
125 InitializeArrayClassRoot(ClassRoot::ARRAY_I32, ClassRoot::I32, "[I");
126 InitializeArrayClassRoot(ClassRoot::ARRAY_U32, ClassRoot::U32, "[U");
127 InitializeArrayClassRoot(ClassRoot::ARRAY_I64, ClassRoot::I64, "[J");
128 InitializeArrayClassRoot(ClassRoot::ARRAY_U64, ClassRoot::U64, "[Q");
129 InitializeArrayClassRoot(ClassRoot::ARRAY_F32, ClassRoot::F32, "[F");
130 InitializeArrayClassRoot(ClassRoot::ARRAY_F64, ClassRoot::F64, "[D");
131 InitializeArrayClassRoot(ClassRoot::ARRAY_TAGGED, ClassRoot::TAGGED, "[A");
132 InitializeArrayClassRoot(ClassRoot::ARRAY_STRING, ClassRoot::STRING,
133 utf::Mutf8AsCString(langCtx_.GetStringArrayClassDescriptor()));
134 }
135
InitializeImpl(bool compressedStringEnabled)136 bool EtsClassLinkerExtension::InitializeImpl(bool compressedStringEnabled)
137 {
138 // NOLINTNEXTLINE(google-build-using-namespace)
139 using namespace panda_file_items::class_descriptors;
140
141 auto *coroutine = ets::EtsCoroutine::GetCurrent();
142 langCtx_ = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
143 ASSERT(coroutine != nullptr);
144 heapManager_ = coroutine->GetVM()->GetHeapManager();
145
146 auto *objectClass = GetClassLinker()->GetClass(langCtx_.GetObjectClassDescriptor(), false, GetBootContext());
147 if (objectClass == nullptr) {
148 LOG(ERROR, CLASS_LINKER) << "Cannot create class '" << langCtx_.GetObjectClassDescriptor() << "'";
149 return false;
150 }
151 SetClassRoot(ClassRoot::OBJECT, objectClass);
152
153 auto *classClass = GetClassLinker()->GetClass(langCtx_.GetClassClassDescriptor(), false, GetBootContext());
154 if (classClass == nullptr) {
155 LOG(ERROR, CLASS_LINKER) << "Cannot create class '" << langCtx_.GetClassClassDescriptor() << "'";
156 return false;
157 }
158 SetClassRoot(ClassRoot::CLASS, classClass);
159
160 EtsClass::FromRuntimeClass(classClass)->AsObject()->GetCoreType()->SetClass(classClass);
161 EtsClass::FromRuntimeClass(objectClass)->AsObject()->GetCoreType()->SetClass(classClass);
162
163 coretypes::String::SetCompressedStringsEnabled(compressedStringEnabled);
164
165 auto *stringClass = GetClassLinker()->GetClass(langCtx_.GetStringClassDescriptor(), false, GetBootContext());
166 if (stringClass == nullptr) {
167 LOG(ERROR, CLASS_LINKER) << "Cannot create class '" << langCtx_.GetStringClassDescriptor() << "'";
168 return false;
169 }
170 SetClassRoot(ClassRoot::STRING, stringClass);
171 stringClass->SetStringClass();
172
173 InitializeClassRoots();
174
175 return true;
176 }
177
InitializeArrayClass(Class * arrayClass,Class * componentClass)178 bool EtsClassLinkerExtension::InitializeArrayClass(Class *arrayClass, Class *componentClass)
179 {
180 ASSERT(IsInitialized());
181
182 ASSERT(!arrayClass->IsInitialized());
183 ASSERT(arrayClass->GetComponentType() == nullptr);
184
185 auto *objectClass = GetClassRoot(ClassRoot::OBJECT);
186 arrayClass->SetBase(objectClass);
187 arrayClass->SetComponentType(componentClass);
188
189 auto accessFlags = componentClass->GetAccessFlags() & ACC_FILE_MASK;
190 accessFlags &= ~ACC_INTERFACE;
191 accessFlags |= ACC_FINAL | ACC_ABSTRACT;
192
193 arrayClass->SetAccessFlags(accessFlags);
194
195 auto objectClassVtable = objectClass->GetVTable();
196 auto arrayClassVtable = arrayClass->GetVTable();
197 for (size_t i = 0; i < objectClassVtable.size(); i++) {
198 arrayClassVtable[i] = objectClassVtable[i];
199 }
200
201 arrayClass->SetState(Class::State::INITIALIZED);
202
203 ASSERT(arrayClass->IsArrayClass()); // After init, we give out a well-formed array class.
204 return true;
205 }
206
InitializeClass(Class * klass)207 bool EtsClassLinkerExtension::InitializeClass(Class *klass)
208 {
209 return InitializeClass(klass, GetErrorHandler());
210 }
211
InitializeClass(Class * klass,ClassLinkerErrorHandler * handler)212 bool EtsClassLinkerExtension::InitializeClass(Class *klass, [[maybe_unused]] ClassLinkerErrorHandler *handler)
213 {
214 ASSERT(IsInitialized());
215 ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
216
217 constexpr uint32_t ETS_ACCESS_FLAGS_MASK = 0xFFFFU;
218
219 EtsClass::FromRuntimeClass(klass)->Initialize(
220 klass->GetBase() != nullptr ? EtsClass::FromRuntimeClass(klass->GetBase()) : nullptr,
221 klass->GetAccessFlags() & ETS_ACCESS_FLAGS_MASK, klass->IsPrimitive(), handler);
222
223 return true;
224 }
225
InitializePrimitiveClass(Class * primitiveClass)226 void EtsClassLinkerExtension::InitializePrimitiveClass(Class *primitiveClass)
227 {
228 ASSERT(IsInitialized());
229
230 ASSERT(!primitiveClass->IsInitialized());
231 ASSERT(primitiveClass->IsPrimitive());
232
233 primitiveClass->SetAccessFlags(ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT);
234 primitiveClass->SetState(Class::State::INITIALIZED);
235 }
236
GetClassVTableSize(ClassRoot root)237 size_t EtsClassLinkerExtension::GetClassVTableSize(ClassRoot root)
238 {
239 ASSERT(IsInitialized());
240
241 switch (root) {
242 case ClassRoot::V:
243 case ClassRoot::U1:
244 case ClassRoot::I8:
245 case ClassRoot::U8:
246 case ClassRoot::I16:
247 case ClassRoot::U16:
248 case ClassRoot::I32:
249 case ClassRoot::U32:
250 case ClassRoot::I64:
251 case ClassRoot::U64:
252 case ClassRoot::F32:
253 case ClassRoot::F64:
254 case ClassRoot::TAGGED:
255 return 0;
256 case ClassRoot::ARRAY_U1:
257 case ClassRoot::ARRAY_I8:
258 case ClassRoot::ARRAY_U8:
259 case ClassRoot::ARRAY_I16:
260 case ClassRoot::ARRAY_U16:
261 case ClassRoot::ARRAY_I32:
262 case ClassRoot::ARRAY_U32:
263 case ClassRoot::ARRAY_I64:
264 case ClassRoot::ARRAY_U64:
265 case ClassRoot::ARRAY_F32:
266 case ClassRoot::ARRAY_F64:
267 case ClassRoot::ARRAY_TAGGED:
268 case ClassRoot::ARRAY_CLASS:
269 case ClassRoot::ARRAY_STRING:
270 return GetArrayClassVTableSize();
271 case ClassRoot::OBJECT:
272 case ClassRoot::STRING:
273 return GetClassRoot(root)->GetVTableSize();
274 case ClassRoot::CLASS:
275 return 0;
276 default: {
277 break;
278 }
279 }
280
281 UNREACHABLE();
282 return 0;
283 }
284
GetClassIMTSize(ClassRoot root)285 size_t EtsClassLinkerExtension::GetClassIMTSize(ClassRoot root)
286 {
287 ASSERT(IsInitialized());
288
289 switch (root) {
290 case ClassRoot::V:
291 case ClassRoot::U1:
292 case ClassRoot::I8:
293 case ClassRoot::U8:
294 case ClassRoot::I16:
295 case ClassRoot::U16:
296 case ClassRoot::I32:
297 case ClassRoot::U32:
298 case ClassRoot::I64:
299 case ClassRoot::U64:
300 case ClassRoot::F32:
301 case ClassRoot::F64:
302 case ClassRoot::TAGGED:
303 return 0;
304 case ClassRoot::ARRAY_U1:
305 case ClassRoot::ARRAY_I8:
306 case ClassRoot::ARRAY_U8:
307 case ClassRoot::ARRAY_I16:
308 case ClassRoot::ARRAY_U16:
309 case ClassRoot::ARRAY_I32:
310 case ClassRoot::ARRAY_U32:
311 case ClassRoot::ARRAY_I64:
312 case ClassRoot::ARRAY_U64:
313 case ClassRoot::ARRAY_F32:
314 case ClassRoot::ARRAY_F64:
315 case ClassRoot::ARRAY_TAGGED:
316 case ClassRoot::ARRAY_CLASS:
317 case ClassRoot::ARRAY_STRING:
318 return GetArrayClassIMTSize();
319 case ClassRoot::OBJECT:
320 case ClassRoot::CLASS:
321 case ClassRoot::STRING:
322 return 0;
323 default: {
324 break;
325 }
326 }
327
328 UNREACHABLE();
329 return 0;
330 }
331
GetClassSize(ClassRoot root)332 size_t EtsClassLinkerExtension::GetClassSize(ClassRoot root)
333 {
334 ASSERT(IsInitialized());
335
336 switch (root) {
337 case ClassRoot::V:
338 case ClassRoot::U1:
339 case ClassRoot::I8:
340 case ClassRoot::U8:
341 case ClassRoot::I16:
342 case ClassRoot::U16:
343 case ClassRoot::I32:
344 case ClassRoot::U32:
345 case ClassRoot::I64:
346 case ClassRoot::U64:
347 case ClassRoot::F32:
348 case ClassRoot::F64:
349 case ClassRoot::TAGGED:
350 return Class::ComputeClassSize(GetClassVTableSize(root), GetClassIMTSize(root), 0, 0, 0, 0, 0, 0);
351 case ClassRoot::ARRAY_U1:
352 case ClassRoot::ARRAY_I8:
353 case ClassRoot::ARRAY_U8:
354 case ClassRoot::ARRAY_I16:
355 case ClassRoot::ARRAY_U16:
356 case ClassRoot::ARRAY_I32:
357 case ClassRoot::ARRAY_U32:
358 case ClassRoot::ARRAY_I64:
359 case ClassRoot::ARRAY_U64:
360 case ClassRoot::ARRAY_F32:
361 case ClassRoot::ARRAY_F64:
362 case ClassRoot::ARRAY_TAGGED:
363 case ClassRoot::ARRAY_CLASS:
364 case ClassRoot::ARRAY_STRING:
365 return GetArrayClassSize();
366 case ClassRoot::OBJECT:
367 case ClassRoot::CLASS:
368 case ClassRoot::STRING:
369 return Class::ComputeClassSize(GetClassVTableSize(root), GetClassIMTSize(root), 0, 0, 0, 0, 0, 0);
370 default: {
371 break;
372 }
373 }
374
375 UNREACHABLE();
376 return 0;
377 }
378
GetArrayClassVTableSize()379 size_t EtsClassLinkerExtension::GetArrayClassVTableSize()
380 {
381 ASSERT(IsInitialized());
382
383 return GetClassVTableSize(ClassRoot::OBJECT);
384 }
385
GetArrayClassIMTSize()386 size_t EtsClassLinkerExtension::GetArrayClassIMTSize()
387 {
388 ASSERT(IsInitialized());
389
390 return GetClassIMTSize(ClassRoot::OBJECT);
391 }
392
GetArrayClassSize()393 size_t EtsClassLinkerExtension::GetArrayClassSize()
394 {
395 ASSERT(IsInitialized());
396
397 return GetClassSize(ClassRoot::OBJECT);
398 }
399
InitializeClass(ObjectHeader * objectHeader,const uint8_t * descriptor,size_t vtableSize,size_t imtSize,size_t size)400 Class *EtsClassLinkerExtension::InitializeClass(ObjectHeader *objectHeader, const uint8_t *descriptor,
401 size_t vtableSize, size_t imtSize, size_t size)
402 {
403 auto managedClass = reinterpret_cast<EtsClass *>(objectHeader);
404 ASSERT(managedClass != nullptr);
405 managedClass->InitClass(descriptor, vtableSize, imtSize, size);
406 auto klass = managedClass->GetRuntimeClass();
407 klass->SetManagedObject(objectHeader);
408 klass->SetSourceLang(GetLanguage());
409
410 AddCreatedClass(klass);
411
412 return klass;
413 }
414
CreateClass(const uint8_t * descriptor,size_t vtableSize,size_t imtSize,size_t size)415 Class *EtsClassLinkerExtension::CreateClass(const uint8_t *descriptor, size_t vtableSize, size_t imtSize, size_t size)
416 {
417 ASSERT(IsInitialized());
418
419 Class *classClassRoot = GetClassRoot(ClassRoot::CLASS);
420 ObjectHeader *classObject;
421 if (UNLIKELY(classClassRoot == nullptr)) {
422 ASSERT(utf::IsEqual(descriptor, langCtx_.GetObjectClassDescriptor()) ||
423 utf::IsEqual(descriptor, langCtx_.GetClassClassDescriptor()));
424 classObject = heapManager_->AllocateNonMovableObject<true>(classClassRoot, EtsClass::GetSize(size));
425 } else {
426 classObject = heapManager_->AllocateNonMovableObject<false>(classClassRoot, EtsClass::GetSize(size));
427 }
428 if (UNLIKELY(classObject == nullptr)) {
429 return nullptr;
430 }
431
432 return InitializeClass(classObject, descriptor, vtableSize, imtSize, size);
433 }
434
CreateClassRoot(const uint8_t * descriptor,ClassRoot root)435 Class *EtsClassLinkerExtension::CreateClassRoot(const uint8_t *descriptor, ClassRoot root)
436 {
437 auto vtableSize = GetClassVTableSize(root);
438 auto imtSize = GetClassIMTSize(root);
439 auto size = GetClassSize(root);
440
441 Class *klass;
442 if (root == ClassRoot::CLASS) {
443 ASSERT(GetClassRoot(ClassRoot::CLASS) == nullptr);
444 auto objectHeader = heapManager_->AllocateNonMovableObject<true>(nullptr, EtsClass::GetSize(size));
445 ASSERT(objectHeader != nullptr);
446
447 klass = InitializeClass(objectHeader, descriptor, vtableSize, imtSize, size);
448 klass->SetObjectSize(EtsClass::GetSize(size));
449 EtsClass::FromRuntimeClass(klass)->AsObject()->SetClass(EtsClass::FromRuntimeClass(klass));
450 } else {
451 klass = CreateClass(descriptor, vtableSize, imtSize, size);
452 }
453
454 ASSERT(klass != nullptr);
455 klass->SetBase(GetClassRoot(ClassRoot::OBJECT));
456 klass->SetState(Class::State::LOADED);
457 klass->SetLoadContext(GetBootContext());
458 GetClassLinker()->AddClassRoot(root, klass);
459 return klass;
460 }
461
FreeClass(Class * klass)462 void EtsClassLinkerExtension::FreeClass(Class *klass)
463 {
464 ASSERT(IsInitialized());
465
466 RemoveCreatedClass(klass);
467 }
468
~EtsClassLinkerExtension()469 EtsClassLinkerExtension::~EtsClassLinkerExtension()
470 {
471 if (!IsInitialized()) {
472 return;
473 }
474
475 FreeLoadedClasses();
476
477 // References from `EtsClassLinkerContext` are removed in their destructors, need to process only boot context.
478 RemoveRefToLinker(GetBootContext());
479 }
480
IsMethodNativeApi(const Method * method) const481 bool EtsClassLinkerExtension::IsMethodNativeApi(const Method *method) const
482 {
483 // intrinsics and async methods are marked as native, but they are not native api calls
484 return method->IsNative() && !method->IsIntrinsic() && !EtsAnnotation::FindAsyncAnnotation(method).IsValid();
485 }
486
CanThrowException(const Method * method) const487 bool EtsClassLinkerExtension::CanThrowException(const Method *method) const
488 {
489 if (!method->IsNative()) {
490 return true;
491 }
492
493 const EtsMethod *etsMethod = EtsMethod::FromRuntimeMethod(method);
494 return !etsMethod->IsCriticalNative();
495 }
496
IsNecessarySwitchThreadState(const Method * method) const497 bool EtsClassLinkerExtension::IsNecessarySwitchThreadState(const Method *method) const
498 {
499 if (!IsMethodNativeApi(method)) {
500 return false;
501 }
502
503 const EtsMethod *etsMethod = EtsMethod::FromRuntimeMethod(method);
504 return !(etsMethod->IsFastNative() || etsMethod->IsCriticalNative());
505 }
506
CanNativeMethodUseObjects(const Method * method) const507 bool EtsClassLinkerExtension::CanNativeMethodUseObjects(const Method *method) const
508 {
509 if (!IsMethodNativeApi(method)) {
510 return false;
511 }
512
513 const EtsMethod *etsMethod = EtsMethod::FromRuntimeMethod(method);
514 return !etsMethod->IsCriticalNative();
515 }
516
GetNativeEntryPointFor(Method * method) const517 const void *EtsClassLinkerExtension::GetNativeEntryPointFor(Method *method) const
518 {
519 panda_file::File::EntityId asyncAnnId = EtsAnnotation::FindAsyncAnnotation(method);
520 if (asyncAnnId.IsValid()) {
521 return reinterpret_cast<const void *>(EtsAsyncEntryPoint);
522 }
523 switch (GetEtsNapiType(method)) {
524 case EtsNapiType::GENERIC: {
525 return napi::GetEtsNapiEntryPoint();
526 }
527 case EtsNapiType::FAST: {
528 auto flags = method->GetAccessFlags();
529 flags |= ACC_FAST_NATIVE;
530 method->SetAccessFlags(flags);
531
532 return napi::GetEtsNapiEntryPoint();
533 }
534 case EtsNapiType::CRITICAL: {
535 auto flags = method->GetAccessFlags();
536 flags |= ACC_CRITICAL_NATIVE;
537 method->SetAccessFlags(flags);
538
539 return napi::GetEtsNapiCriticalEntryPoint();
540 }
541 }
542
543 UNREACHABLE();
544 }
545
FromClassObject(ark::ObjectHeader * obj)546 Class *EtsClassLinkerExtension::FromClassObject(ark::ObjectHeader *obj)
547 {
548 return obj != nullptr ? reinterpret_cast<EtsClass *>(obj)->GetRuntimeClass() : nullptr;
549 }
550
GetClassObjectSizeFromClassSize(uint32_t size)551 size_t EtsClassLinkerExtension::GetClassObjectSizeFromClassSize(uint32_t size)
552 {
553 return EtsClass::GetSize(size);
554 }
555
CacheClass(std::string_view descriptor,bool forceInit)556 Class *EtsClassLinkerExtension::CacheClass(std::string_view descriptor, bool forceInit)
557 {
558 Class *cls = GetClassLinker()->GetClass(utf::CStringAsMutf8(descriptor.data()), false, GetBootContext());
559 if (cls == nullptr) {
560 LOG(ERROR, CLASS_LINKER) << "Cannot create class " << descriptor;
561 return nullptr;
562 }
563 if (forceInit && !GetClassLinker()->InitializeClass(EtsCoroutine::GetCurrent(), cls)) {
564 LOG(ERROR, CLASS_LINKER) << "Cannot initialize class " << descriptor;
565 return nullptr;
566 }
567 return cls;
568 }
569
570 template <typename F>
CacheClass(std::string_view descriptor,F const & setup,bool forceInit)571 Class *EtsClassLinkerExtension::CacheClass(std::string_view descriptor, F const &setup, bool forceInit)
572 {
573 Class *cls = CacheClass(descriptor, forceInit);
574 if (cls != nullptr) {
575 setup(EtsClass::FromRuntimeClass(cls));
576 }
577 return cls;
578 }
579
InitializeBuiltinSpecialClasses()580 void EtsClassLinkerExtension::InitializeBuiltinSpecialClasses()
581 {
582 // CC-OFFNXT(WordsTool.95) false positive
583 // NOLINTNEXTLINE(google-build-using-namespace)
584 using namespace panda_file_items::class_descriptors;
585
586 CacheClass(STRING, [](auto *c) { c->SetValueTyped(); });
587 CacheClass(NULL_VALUE, [](auto *c) {
588 c->SetNullValue();
589 c->SetValueTyped();
590 });
591 auto const setupBoxedFlags = [](EtsClass *c) {
592 c->SetBoxed();
593 c->SetValueTyped();
594 };
595 CacheClass(BOX_BOOLEAN, setupBoxedFlags);
596 CacheClass(BOX_BYTE, setupBoxedFlags);
597 CacheClass(BOX_CHAR, setupBoxedFlags);
598 CacheClass(BOX_SHORT, setupBoxedFlags);
599 CacheClass(BOX_INT, setupBoxedFlags);
600 CacheClass(BOX_LONG, setupBoxedFlags);
601 CacheClass(BOX_FLOAT, setupBoxedFlags);
602 CacheClass(BOX_DOUBLE, setupBoxedFlags);
603 CacheClass(BIG_INT, [](auto *c) {
604 c->SetBigInt();
605 c->SetValueTyped();
606 });
607 CacheClass(FUNCTION, [](auto *c) { c->SetFunction(); });
608 CacheClass(BASE_ENUM, [](auto *c) {
609 c->SetEtsEnum();
610 c->SetValueTyped();
611 });
612
613 CacheClass(FINALIZABLE_WEAK_REF, [](auto *c) {
614 c->SetFinalizeReference();
615 c->SetWeakReference();
616 });
617 CacheClass(WEAK_REF, [](auto *c) { c->SetWeakReference(); });
618 }
619
InitializeBuiltinClasses()620 void EtsClassLinkerExtension::InitializeBuiltinClasses()
621 {
622 // NOLINTNEXTLINE(google-build-using-namespace)
623 using namespace panda_file_items::class_descriptors;
624
625 InitializeBuiltinSpecialClasses();
626
627 plaformTypes_ = PandaUniquePtr<EtsPlatformTypes>(
628 Runtime::GetCurrent()->GetInternalAllocator()->New<EtsPlatformTypes>(EtsCoroutine::GetCurrent()));
629
630 // NOTE (electronick, #15938): Refactor the managed class-related pseudo TLS fields
631 // initialization in MT ManagedThread ctor and EtsCoroutine::Initialize
632 auto coro = EtsCoroutine::GetCurrent();
633 ASSERT(coro != nullptr);
634 coro->SetPromiseClass(GetPlatformTypes()->corePromise->GetRuntimeClass());
635 coro->SetJobClass(GetPlatformTypes()->coreJob->GetRuntimeClass());
636 coro->SetStringClassPtr(GetClassRoot(ClassRoot::STRING));
637 coro->SetArrayU16ClassPtr(GetClassRoot(ClassRoot::ARRAY_U16));
638 coro->SetArrayU8ClassPtr(GetClassRoot(ClassRoot::ARRAY_U8));
639 }
640
GetEtsRuntimeLinker(ClassLinkerContext * ctx)641 static EtsRuntimeLinker *GetEtsRuntimeLinker(ClassLinkerContext *ctx)
642 {
643 ASSERT(ctx != nullptr);
644 auto *ref = ctx->GetRefToLinker();
645 if (ref == nullptr) {
646 return nullptr;
647 }
648 return EtsRuntimeLinker::FromCoreType(PandaEtsVM::GetCurrent()->GetGlobalObjectStorage()->Get(ref));
649 }
650
CreateBootRuntimeLinker(ClassLinkerContext * ctx)651 static EtsRuntimeLinker *CreateBootRuntimeLinker(ClassLinkerContext *ctx)
652 {
653 ASSERT(ctx->IsBootContext());
654 ASSERT(ctx->GetRefToLinker() == nullptr);
655 auto *etsObject = EtsObject::Create(PlatformTypes()->coreBootRuntimeLinker);
656 if (UNLIKELY(etsObject == nullptr)) {
657 LOG(FATAL, CLASS_LINKER) << "Could not allocate BootRuntimeLinker";
658 }
659 auto *runtimeLinker = EtsRuntimeLinker::FromEtsObject(etsObject);
660 ASSERT(runtimeLinker != nullptr);
661 runtimeLinker->SetClassLinkerContext(ctx);
662 return runtimeLinker;
663 }
664
665 /* static */
GetOrCreateEtsRuntimeLinker(ClassLinkerContext * ctx)666 EtsRuntimeLinker *EtsClassLinkerExtension::GetOrCreateEtsRuntimeLinker(ClassLinkerContext *ctx)
667 {
668 ASSERT(ctx != nullptr);
669
670 // CC-OFFNXT(G.CTL.03) false positive
671 while (true) {
672 auto *runtimeLinker = GetEtsRuntimeLinker(ctx);
673 if (runtimeLinker != nullptr) {
674 return runtimeLinker;
675 }
676 // Only BootRuntimeLinker is created after its corresponding context
677 ASSERT(ctx->IsBootContext());
678 runtimeLinker = CreateBootRuntimeLinker(ctx);
679 auto *objectStorage = PandaEtsVM::GetCurrent()->GetGlobalObjectStorage();
680 auto *refToLinker = objectStorage->Add(runtimeLinker->GetCoreType(), mem::Reference::ObjectType::GLOBAL);
681 if (ctx->CompareAndSetRefToLinker(nullptr, refToLinker)) {
682 return runtimeLinker;
683 }
684 objectStorage->Remove(refToLinker);
685 }
686 UNREACHABLE();
687 }
688
689 /* static */
RemoveRefToLinker(ClassLinkerContext * ctx)690 void EtsClassLinkerExtension::RemoveRefToLinker(ClassLinkerContext *ctx)
691 {
692 ASSERT(ctx != nullptr);
693 if (Thread::GetCurrent() == nullptr) {
694 // Do not remove references during runtime destruction
695 return;
696 }
697 auto *ref = ctx->GetRefToLinker();
698 if (ref != nullptr) {
699 auto *etsVm = PandaEtsVM::GetCurrent();
700 ASSERT(etsVm != nullptr);
701 auto *objectStorage = etsVm->GetGlobalObjectStorage();
702 ASSERT(objectStorage != nullptr);
703 objectStorage->Remove(ref);
704 }
705 }
706
CreateApplicationClassLinkerContext(const PandaVector<PandaString> & path)707 ClassLinkerContext *EtsClassLinkerExtension::CreateApplicationClassLinkerContext(const PandaVector<PandaString> &path)
708 {
709 ClassLinkerContext *ctx = PandaEtsVM::GetCurrent()->CreateApplicationRuntimeLinker(path);
710 ASSERT(ctx != nullptr);
711 return ctx;
712 }
713
714 } // namespace ark::ets
715