• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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