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 "runtime/include/class_linker.h"
17 #include "runtime/bridge/bridge.h"
18 #include "runtime/cha.h"
19 #include "runtime/class_initializer.h"
20 #include "runtime/include/coretypes/array.h"
21 #include "runtime/include/coretypes/string.h"
22 #include "runtime/include/field.h"
23 #include "runtime/include/itable_builder.h"
24 #include "runtime/include/method.h"
25 #include "runtime/include/panda_vm.h"
26 #include "runtime/include/runtime.h"
27 #include "runtime/include/runtime_notification.h"
28 #include "libpandabase/macros.h"
29 #include "libpandabase/mem/mem.h"
30 #include "libpandabase/utils/bit_utils.h"
31 #include "libpandabase/utils/span.h"
32 #include "libpandabase/utils/utf.h"
33 #include "libpandafile/class_data_accessor-inl.h"
34 #include "libpandafile/code_data_accessor-inl.h"
35 #include "libpandafile/field_data_accessor-inl.h"
36 #include "libpandafile/method_data_accessor-inl.h"
37 #include "libpandafile/modifiers.h"
38 #include "libpandafile/panda_cache.h"
39 #include "libpandafile/proto_data_accessor-inl.h"
40 #include "runtime/include/tooling/debug_inf.h"
41 #include "trace/trace.h"
42
43 namespace panda {
44
45 using Type = panda_file::Type;
46 using SourceLang = panda_file::SourceLang;
47
AddPandaFile(std::unique_ptr<const panda_file::File> && pf,ClassLinkerContext * context)48 void ClassLinker::AddPandaFile(std::unique_ptr<const panda_file::File> &&pf, ClassLinkerContext *context)
49 {
50 ASSERT(pf != nullptr);
51
52 const panda_file::File *file = pf.get();
53
54 SCOPED_TRACE_STREAM << __FUNCTION__ << " " << file->GetFilename();
55
56 {
57 os::memory::LockHolder lock {panda_files_lock_};
58 panda_files_.push_back({context, std::forward<std::unique_ptr<const panda_file::File>>(pf)});
59 }
60
61 if (context == nullptr || context->IsBootContext()) {
62 os::memory::LockHolder lock {boot_panda_files_lock_};
63 boot_panda_files_.push_back(file);
64 }
65
66 if (Runtime::GetCurrent()->IsInitialized()) {
67 // LoadModule for initial boot files is called in runtime
68 Runtime::GetCurrent()->GetNotificationManager()->LoadModuleEvent(file->GetFilename());
69 }
70
71 tooling::DebugInf::AddCodeMetaInfo(file);
72 }
73
FreeClassData(Class * class_ptr)74 void ClassLinker::FreeClassData(Class *class_ptr)
75 {
76 Span<Field> fields = class_ptr->GetFields();
77 if (fields.Size() > 0) {
78 allocator_->Free(fields.begin());
79 class_ptr->SetFields(Span<Field>(), 0);
80 }
81 Span<Method> methods = class_ptr->GetMethods();
82 size_t n = methods.Size() + class_ptr->GetNumCopiedMethods();
83 if (n > 0) {
84 mem::InternalAllocatorPtr allocator = Runtime::GetCurrent()->GetInternalAllocator();
85 for (auto &method : methods) {
86 // We create Profiling data in method class via InternalAllocator.
87 // Therefore, we should delete it via InternalAllocator too.
88 allocator->Free(method.GetProfilingData());
89 }
90 allocator_->Free(methods.begin());
91 class_ptr->SetMethods(Span<Method>(), 0, 0);
92 }
93 bool has_own_itable = !class_ptr->IsArrayClass();
94 auto itable = class_ptr->GetITable().Get();
95 if (has_own_itable && !itable.Empty()) {
96 for (size_t i = 0; i < itable.Size(); i++) {
97 Span<Method *> imethods = itable[i].GetMethods();
98 if (!imethods.Empty()) {
99 allocator_->Free(imethods.begin());
100 }
101 }
102 allocator_->Free(itable.begin());
103 class_ptr->SetITable(ITable());
104 }
105 Span<Class *> interfaces = class_ptr->GetInterfaces();
106 if (!interfaces.Empty()) {
107 allocator_->Free(interfaces.begin());
108 class_ptr->SetInterfaces(Span<Class *>());
109 }
110 }
111
FreeClass(Class * class_ptr)112 void ClassLinker::FreeClass(Class *class_ptr)
113 {
114 FreeClassData(class_ptr);
115 GetExtension(class_ptr->GetSourceLang())->FreeClass(class_ptr);
116 }
117
~ClassLinker()118 ClassLinker::~ClassLinker()
119 {
120 for (auto &copied_name : copied_names_) {
121 allocator_->Free(reinterpret_cast<void *>(const_cast<uint8_t *>(copied_name)));
122 }
123 }
124
ClassLinker(mem::InternalAllocatorPtr allocator,std::vector<std::unique_ptr<ClassLinkerExtension>> && extensions)125 ClassLinker::ClassLinker(mem::InternalAllocatorPtr allocator,
126 std::vector<std::unique_ptr<ClassLinkerExtension>> &&extensions)
127 : allocator_(allocator), aot_manager_(MakePandaUnique<AotManager>()), copied_names_(allocator->Adapter())
128 {
129 for (auto &ext : extensions) {
130 extensions_[panda::panda_file::GetLangArrIndex(ext->GetLanguage())] = std::move(ext);
131 }
132 }
133
ResetExtension(panda_file::SourceLang lang)134 void ClassLinker::ResetExtension(panda_file::SourceLang lang)
135 {
136 extensions_[panda::panda_file::GetLangArrIndex(lang)] =
137 Runtime::GetCurrent()->GetLanguageContext(lang).CreateClassLinkerExtension();
138 }
139
140 template <class T, class... Args>
InitializeMemory(T * mem,Args...args)141 static T *InitializeMemory(T *mem, Args... args)
142 {
143 return new (mem) T(std::forward<Args>(args)...);
144 }
145
Initialize(bool compressed_string_enabled)146 bool ClassLinker::Initialize(bool compressed_string_enabled)
147 {
148 if (is_initialized_) {
149 return true;
150 }
151
152 for (auto &ext : extensions_) {
153 if (ext == nullptr) {
154 continue;
155 }
156
157 if (!ext->Initialize(this, compressed_string_enabled)) {
158 return false;
159 }
160 }
161
162 is_initialized_ = true;
163
164 return true;
165 }
166
InitializeRoots(ManagedThread * thread)167 bool ClassLinker::InitializeRoots(ManagedThread *thread)
168 {
169 for (auto &ext : extensions_) {
170 if (ext == nullptr) {
171 continue;
172 }
173
174 if (!ext->InitializeRoots(thread)) {
175 return false;
176 }
177 }
178
179 return true;
180 }
181
182 using ClassEntry = std::pair<panda_file::File::EntityId, const panda_file::File *>;
183 using PandaFiles = PandaVector<const panda_file::File *>;
184
FindClassInPandaFiles(const uint8_t * descriptor,const PandaFiles & panda_files)185 static ClassEntry FindClassInPandaFiles(const uint8_t *descriptor, const PandaFiles &panda_files)
186 {
187 for (auto *pf : panda_files) {
188 auto class_id = pf->GetClassId(descriptor);
189 if (class_id.IsValid() && !pf->IsExternal(class_id)) {
190 return {class_id, pf};
191 }
192 }
193
194 return {};
195 }
196
FindLoadedClass(const uint8_t * descriptor,ClassLinkerContext * context)197 Class *ClassLinker::FindLoadedClass(const uint8_t *descriptor, ClassLinkerContext *context)
198 {
199 ASSERT(context != nullptr);
200 return context->FindClass(descriptor);
201 }
202
203 template <class ClassDataAccessorT>
GetClassSize(ClassDataAccessorT data_accessor,size_t vtable_size,size_t imt_size,size_t * out_num_sfields)204 static size_t GetClassSize(ClassDataAccessorT data_accessor, size_t vtable_size, size_t imt_size,
205 size_t *out_num_sfields)
206 {
207 size_t num_8bit_sfields = 0;
208 size_t num_16bit_sfields = 0;
209 size_t num_32bit_sfields = 0;
210 size_t num_64bit_sfields = 0;
211 size_t num_ref_sfields = 0;
212 size_t num_tagged_sfields = 0;
213
214 size_t num_sfields = 0;
215
216 data_accessor.template EnumerateStaticFieldTypes([&num_8bit_sfields, &num_16bit_sfields, &num_32bit_sfields,
217 &num_64bit_sfields, &num_ref_sfields, &num_tagged_sfields,
218 &num_sfields](Type field_type) {
219 ++num_sfields;
220
221 switch (field_type.GetId()) {
222 case Type::TypeId::U1:
223 case Type::TypeId::I8:
224 case Type::TypeId::U8:
225 ++num_8bit_sfields;
226 break;
227 case Type::TypeId::I16:
228 case Type::TypeId::U16:
229 ++num_16bit_sfields;
230 break;
231 case Type::TypeId::I32:
232 case Type::TypeId::U32:
233 case Type::TypeId::F32:
234 ++num_32bit_sfields;
235 break;
236 case Type::TypeId::I64:
237 case Type::TypeId::U64:
238 case Type::TypeId::F64:
239 ++num_64bit_sfields;
240 break;
241 case Type::TypeId::REFERENCE:
242 ++num_ref_sfields;
243 break;
244 case Type::TypeId::TAGGED:
245 ++num_tagged_sfields;
246 break;
247 default:
248 UNREACHABLE();
249 break;
250 }
251 });
252
253 *out_num_sfields = num_sfields;
254
255 return Class::ComputeClassSize(vtable_size, imt_size, num_8bit_sfields, num_16bit_sfields, num_32bit_sfields,
256 num_64bit_sfields, num_ref_sfields, num_tagged_sfields);
257 }
258
259 class ClassDataAccessorWrapper {
260 public:
ClassDataAccessorWrapper(panda_file::ClassDataAccessor * data_accessor=nullptr)261 explicit ClassDataAccessorWrapper(panda_file::ClassDataAccessor *data_accessor = nullptr)
262 : data_accessor_(data_accessor)
263 {
264 }
265
266 template <class Callback>
EnumerateStaticFieldTypes(const Callback & cb) const267 void EnumerateStaticFieldTypes(const Callback &cb) const
268 {
269 data_accessor_->EnumerateFields([cb](panda_file::FieldDataAccessor &fda) {
270 if (!fda.IsStatic()) {
271 return;
272 }
273
274 cb(Type::GetTypeFromFieldEncoding(fda.GetType()));
275 });
276 }
277
278 ~ClassDataAccessorWrapper() = default;
279
280 DEFAULT_COPY_SEMANTIC(ClassDataAccessorWrapper);
281 DEFAULT_MOVE_SEMANTIC(ClassDataAccessorWrapper);
282
283 private:
284 panda_file::ClassDataAccessor *data_accessor_;
285 };
286
GetClassInfo(panda_file::ClassDataAccessor * data_accessor,Class * base,Span<Class * > interfaces,ClassLinkerContext * context)287 ClassLinker::ClassInfo ClassLinker::GetClassInfo(panda_file::ClassDataAccessor *data_accessor, Class *base,
288 Span<Class *> interfaces, ClassLinkerContext *context)
289 {
290 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(data_accessor);
291
292 auto vtable_builder = ctx.CreateVTableBuilder();
293 auto itable_builder = ctx.CreateITableBuilder();
294 auto imtable_builder = ctx.CreateIMTableBuilder();
295
296 itable_builder->Build(this, base, interfaces, data_accessor->IsInterface());
297 vtable_builder->Build(data_accessor, base, itable_builder->GetITable(), context);
298 imtable_builder->Build(data_accessor, itable_builder->GetITable());
299
300 ClassDataAccessorWrapper data_accessor_wrapper(data_accessor);
301 size_t num_sfields = 0;
302 size_t size = GetClassSize(data_accessor_wrapper, vtable_builder->GetVTableSize(), imtable_builder->GetIMTSize(),
303 &num_sfields);
304
305 return {size, num_sfields, std::move(vtable_builder), std::move(itable_builder), std::move(imtable_builder)};
306 }
307
308 class ClassDataAccessor {
309 public:
ClassDataAccessor(Span<Field> fields)310 explicit ClassDataAccessor(Span<Field> fields) : fields_(fields) {}
311
312 template <class Callback>
EnumerateStaticFieldTypes(const Callback & cb) const313 void EnumerateStaticFieldTypes(const Callback &cb) const
314 {
315 for (const auto &field : fields_) {
316 if (!field.IsStatic()) {
317 continue;
318 }
319
320 cb(field.GetType());
321 }
322 }
323
324 ~ClassDataAccessor() = default;
325
326 DEFAULT_COPY_SEMANTIC(ClassDataAccessor);
327 DEFAULT_MOVE_SEMANTIC(ClassDataAccessor);
328
329 private:
330 Span<Field> fields_;
331 };
332
GetClassInfo(Span<Method> methods,Span<Field> fields,Class * base,Span<Class * > interfaces,bool is_interface)333 ClassLinker::ClassInfo ClassLinker::GetClassInfo(Span<Method> methods, Span<Field> fields, Class *base,
334 Span<Class *> interfaces, bool is_interface)
335 {
336 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*base);
337
338 auto vtable_builder = ctx.CreateVTableBuilder();
339 auto itable_builder = ctx.CreateITableBuilder();
340 auto imtable_builder = ctx.CreateIMTableBuilder();
341
342 itable_builder->Build(this, base, interfaces, is_interface);
343 vtable_builder->Build(methods, base, itable_builder->GetITable(), is_interface);
344 imtable_builder->Build(itable_builder->GetITable(), is_interface);
345
346 ClassDataAccessor data_accessor(fields);
347 size_t num_sfields = 0;
348 size_t size =
349 GetClassSize(data_accessor, vtable_builder->GetVTableSize(), imtable_builder->GetIMTSize(), &num_sfields);
350
351 return {size, num_sfields, std::move(vtable_builder), std::move(itable_builder), std::move(imtable_builder)};
352 }
353
LoadMethod(Method * method,panda_file::MethodDataAccessor * method_data_accessor,Class * klass,const LanguageContext & ctx,const ClassLinkerExtension * ext)354 static void LoadMethod(Method *method, panda_file::MethodDataAccessor *method_data_accessor, Class *klass,
355 const LanguageContext &ctx, const ClassLinkerExtension *ext)
356 {
357 const auto &pf = method_data_accessor->GetPandaFile();
358 panda_file::ProtoDataAccessor pda(pf, method_data_accessor->GetProtoId());
359
360 uint32_t access_flags = method_data_accessor->GetAccessFlags();
361
362 auto *method_name = pf.GetStringData(method_data_accessor->GetNameId()).data;
363 if (utf::IsEqual(method_name, ctx.GetCtorName()) || utf::IsEqual(method_name, ctx.GetCctorName())) {
364 access_flags |= ACC_CONSTRUCTOR;
365 }
366
367 auto code_id = method_data_accessor->GetCodeId();
368 size_t num_args = (method_data_accessor->IsStatic()) ? pda.GetNumArgs() : (pda.GetNumArgs() + 1);
369
370 if (!code_id.has_value()) {
371 InitializeMemory(method, klass, &pf, method_data_accessor->GetMethodId(), panda_file::File::EntityId(0),
372 access_flags, num_args, reinterpret_cast<const uint16_t *>(pda.GetShorty().Data()));
373
374 if (method_data_accessor->IsNative()) {
375 method->SetCompiledEntryPoint(ext->GetNativeEntryPointFor(method));
376 } else {
377 method->SetInterpreterEntryPoint();
378 }
379 } else {
380 InitializeMemory(method, klass, &pf, method_data_accessor->GetMethodId(), code_id.value(), access_flags,
381 num_args, reinterpret_cast<const uint16_t *>(pda.GetShorty().Data()));
382 method->SetCompiledEntryPoint(GetCompiledCodeToInterpreterBridge(method));
383 }
384 }
385
MaybeLinkMethodToAotCode(Method * method,const compiler::AotClass & aot_class,size_t method_index)386 void MaybeLinkMethodToAotCode(Method *method, const compiler::AotClass &aot_class, size_t method_index)
387 {
388 ASSERT(aot_class.IsValid());
389 if (method->IsIntrinsic()) {
390 return;
391 }
392 auto entry = aot_class.FindMethodCodeEntry(method_index);
393 if (entry != nullptr) {
394 method->SetCompiledEntryPoint(entry);
395 LOG(DEBUG, AOT) << "Found AOT entrypoint ["
396 << reinterpret_cast<const void *>(aot_class.FindMethodCodeSpan(method_index).data()) << ":"
397 << reinterpret_cast<const void *>(aot_class.FindMethodCodeSpan(method_index).end())
398 << "] for method: " << method->GetFullName();
399
400 EVENT_AOT_ENTRYPOINT_FOUND(method->GetFullName());
401 ASSERT(aot_class.FindMethodHeader(method_index)->method_id == method->GetFileId().GetOffset());
402 }
403 }
404
LoadMethods(Class * klass,ClassInfo * class_info,panda_file::ClassDataAccessor * data_accessor,ClassLinkerErrorHandler * error_handler)405 bool ClassLinker::LoadMethods(Class *klass, ClassInfo *class_info, panda_file::ClassDataAccessor *data_accessor,
406 [[maybe_unused]] ClassLinkerErrorHandler *error_handler)
407 {
408 uint32_t num_methods = data_accessor->GetMethodsNumber();
409
410 uint32_t num_vmethods = klass->GetNumVirtualMethods();
411 uint32_t num_smethods = num_methods - num_vmethods;
412
413 auto &copied_methods = class_info->vtable_builder->GetCopiedMethods();
414 uint32_t n = num_methods + copied_methods.size();
415
416 if (n == 0) {
417 return true;
418 }
419
420 Span<Method> methods {allocator_->AllocArray<Method>(n), n};
421
422 size_t smethod_idx = num_vmethods;
423 size_t vmethod_idx = 0;
424
425 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
426 auto *ext = GetExtension(ctx);
427 ASSERT(ext != nullptr);
428
429 auto aot_pfile = aot_manager_->FindPandaFile(klass->GetPandaFile()->GetFullFileName());
430 if (aot_pfile != nullptr) {
431 EVENT_AOT_LOADED_FOR_CLASS(PandaString(aot_pfile->GetFileName()), PandaString(klass->GetName()));
432 }
433
434 compiler::AotClass aot_class =
435 (aot_pfile != nullptr) ? aot_pfile->GetClass(klass->GetFileId().GetOffset()) : compiler::AotClass::Invalid();
436
437 size_t method_index = 0;
438 data_accessor->EnumerateMethods([klass, &smethod_idx, &vmethod_idx, &methods, aot_class, ctx, ext,
439 &method_index](panda_file::MethodDataAccessor &method_data_accessor) {
440 Method *method = method_data_accessor.IsStatic() ? &methods[smethod_idx++] : &methods[vmethod_idx++];
441 LoadMethod(method, &method_data_accessor, klass, ctx, ext);
442 if (aot_class.IsValid()) {
443 MaybeLinkMethodToAotCode(method, aot_class, method_index);
444 }
445 // Instead of checking if the method is abstract before every virtual call
446 // the special stub throwing AbstractMethodError is registered as compiled entry point.
447 if (method->IsAbstract()) {
448 method->SetCompiledEntryPoint(GetAbstractMethodStub());
449 }
450 method_index++;
451 });
452
453 for (size_t i = 0; i < copied_methods.size(); i++) {
454 size_t idx = num_methods + i;
455 InitializeMemory(&methods[idx], copied_methods[i].method_);
456 methods[idx].SetIsDefaultInterfaceMethod();
457 if (copied_methods[i].default_abstract_) {
458 methods[idx].SetCompiledEntryPoint(GetAbstractMethodStub());
459 }
460 if (copied_methods[i].default_conflict_) {
461 methods[idx].SetCompiledEntryPoint(GetDefaultConflictMethodStub());
462 }
463 }
464
465 klass->SetMethods(methods, num_vmethods, num_smethods);
466
467 return true;
468 }
469
LoadFields(Class * klass,panda_file::ClassDataAccessor * data_accessor,ClassLinkerErrorHandler * error_handler)470 bool ClassLinker::LoadFields(Class *klass, panda_file::ClassDataAccessor *data_accessor,
471 [[maybe_unused]] ClassLinkerErrorHandler *error_handler)
472 {
473 uint32_t num_fields = data_accessor->GetFieldsNumber();
474 if (num_fields == 0) {
475 return true;
476 }
477
478 uint32_t num_sfields = klass->GetNumStaticFields();
479
480 Span<Field> fields {allocator_->AllocArray<Field>(num_fields), num_fields};
481
482 size_t sfields_idx = 0;
483 size_t ifields_idx = num_sfields;
484 data_accessor->EnumerateFields(
485 [klass, &sfields_idx, &ifields_idx, &fields](panda_file::FieldDataAccessor &field_data_accessor) {
486 Field *field = field_data_accessor.IsStatic() ? &fields[sfields_idx++] : &fields[ifields_idx++];
487 InitializeMemory(field, klass, field_data_accessor.GetFieldId(), field_data_accessor.GetAccessFlags(),
488 panda_file::Type::GetTypeFromFieldEncoding(field_data_accessor.GetType()));
489 });
490
491 klass->SetFields(fields, num_sfields);
492
493 return true;
494 }
495
496 template <bool reverse_layout = false>
LayoutFieldsWithoutAlignment(size_t size,size_t * offset,size_t * space,PandaList<Field * > * fields)497 static void LayoutFieldsWithoutAlignment(size_t size, size_t *offset, size_t *space, PandaList<Field *> *fields)
498 {
499 while ((space == nullptr || *space >= size) && !fields->empty()) {
500 Field *field = fields->front();
501 // NOLINTNEXTLINE(readability-braces-around-statements)
502 if constexpr (reverse_layout) {
503 *offset -= size;
504 field->SetOffset(*offset);
505 // NOLINTNEXTLINE(readability-misleading-indentation)
506 } else {
507 field->SetOffset(*offset);
508 *offset += size;
509 }
510 if (space != nullptr) {
511 *space -= size;
512 }
513 fields->pop_front();
514 }
515 }
516
LayoutReferenceFields(size_t size,size_t * offset,const PandaList<Field * > & fields)517 static uint32_t LayoutReferenceFields(size_t size, size_t *offset, const PandaList<Field *> &fields)
518 {
519 uint32_t volatile_fields_num = 0;
520 // layout volatile fields firstly
521 for (auto *field : fields) {
522 if (field->IsVolatile()) {
523 volatile_fields_num++;
524 field->SetOffset(*offset);
525 *offset += size;
526 }
527 }
528 for (auto *field : fields) {
529 if (!field->IsVolatile()) {
530 field->SetOffset(*offset);
531 *offset += size;
532 }
533 }
534 return volatile_fields_num;
535 }
536
LayoutFieldsInBaseClassPadding(Class * klass,PandaList<Field * > * tagged_fields,PandaList<Field * > * fields64,PandaList<Field * > * fields32,PandaList<Field * > * fields16,PandaList<Field * > * fields8,PandaList<Field * > * ref_fields,bool is_static)537 static size_t LayoutFieldsInBaseClassPadding(Class *klass, PandaList<Field *> *tagged_fields,
538 PandaList<Field *> *fields64, PandaList<Field *> *fields32,
539 PandaList<Field *> *fields16, PandaList<Field *> *fields8,
540 PandaList<Field *> *ref_fields, bool is_static)
541 {
542 constexpr size_t SIZE_64 = sizeof(uint64_t);
543 constexpr size_t SIZE_32 = sizeof(uint32_t);
544 constexpr size_t SIZE_16 = sizeof(uint16_t);
545 constexpr size_t SIZE_8 = sizeof(uint8_t);
546
547 size_t offset;
548
549 if (is_static) {
550 offset = klass->GetStaticFieldsOffset();
551 } else {
552 offset = (klass->GetBase() != nullptr) ? klass->GetBase()->GetObjectSize() : ObjectHeader::ObjectHeaderSize();
553 }
554
555 size_t align_offset = offset;
556 if (!ref_fields->empty()) {
557 align_offset = AlignUp(offset, ClassHelper::OBJECT_POINTER_SIZE);
558 } else if (!(fields64->empty()) || !(tagged_fields->empty())) {
559 align_offset = AlignUp(offset, SIZE_64);
560 } else if (!fields32->empty()) {
561 align_offset = AlignUp(offset, SIZE_32);
562 } else if (!fields16->empty()) {
563 align_offset = AlignUp(offset, SIZE_16);
564 }
565 if (align_offset != offset) {
566 size_t end_offset = align_offset;
567 size_t padding = end_offset - offset;
568 // try to put short fields of child class at end of free space of base class
569 LayoutFieldsWithoutAlignment<true>(SIZE_32, &end_offset, &padding, fields32);
570 LayoutFieldsWithoutAlignment<true>(SIZE_16, &end_offset, &padding, fields16);
571 LayoutFieldsWithoutAlignment<true>(SIZE_8, &end_offset, &padding, fields8);
572 }
573 return align_offset;
574 }
575
LayoutFields(Class * klass,PandaList<Field * > * tagged_fields,PandaList<Field * > * fields64,PandaList<Field * > * fields32,PandaList<Field * > * fields16,PandaList<Field * > * fields8,PandaList<Field * > * ref_fields,bool is_static)576 static size_t LayoutFields(Class *klass, PandaList<Field *> *tagged_fields, PandaList<Field *> *fields64,
577 PandaList<Field *> *fields32, PandaList<Field *> *fields16, PandaList<Field *> *fields8,
578 PandaList<Field *> *ref_fields, bool is_static)
579 {
580 constexpr size_t SIZE_64 = sizeof(uint64_t);
581 constexpr size_t SIZE_32 = sizeof(uint32_t);
582 constexpr size_t SIZE_16 = sizeof(uint16_t);
583 constexpr size_t SIZE_8 = sizeof(uint8_t);
584
585 size_t offset = LayoutFieldsInBaseClassPadding(klass, tagged_fields, fields64, fields32, fields16, fields8,
586 ref_fields, is_static);
587 if (!ref_fields->empty()) {
588 offset = AlignUp(offset, ClassHelper::OBJECT_POINTER_SIZE);
589 klass->SetRefFieldsNum(ref_fields->size(), is_static);
590 klass->SetRefFieldsOffset(offset, is_static);
591 auto volatile_num = LayoutReferenceFields(ClassHelper::OBJECT_POINTER_SIZE, &offset, *ref_fields);
592 klass->SetVolatileRefFieldsNum(volatile_num, is_static);
593 }
594
595 static_assert(coretypes::TaggedValue::TaggedTypeSize() == SIZE_64,
596 "Please fix alignment of the fields of type \"TaggedValue\"");
597 if (!IsAligned<SIZE_64>(offset) && (!fields64->empty() || !tagged_fields->empty())) {
598 size_t padding = AlignUp(offset, SIZE_64) - offset;
599
600 LayoutFieldsWithoutAlignment(SIZE_32, &offset, &padding, fields32);
601 LayoutFieldsWithoutAlignment(SIZE_16, &offset, &padding, fields16);
602 LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8);
603
604 offset += padding;
605 }
606
607 LayoutFieldsWithoutAlignment(coretypes::TaggedValue::TaggedTypeSize(), &offset, nullptr, tagged_fields);
608 LayoutFieldsWithoutAlignment(SIZE_64, &offset, nullptr, fields64);
609
610 if (!IsAligned<SIZE_32>(offset) && !fields32->empty()) {
611 size_t padding = AlignUp(offset, SIZE_32) - offset;
612
613 LayoutFieldsWithoutAlignment(SIZE_16, &offset, &padding, fields16);
614 LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8);
615
616 offset += padding;
617 }
618
619 LayoutFieldsWithoutAlignment(SIZE_32, &offset, nullptr, fields32);
620
621 if (!IsAligned<SIZE_16>(offset) && !fields16->empty()) {
622 size_t padding = AlignUp(offset, SIZE_16) - offset;
623
624 LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8);
625
626 offset += padding;
627 }
628
629 LayoutFieldsWithoutAlignment(SIZE_16, &offset, nullptr, fields16);
630
631 LayoutFieldsWithoutAlignment(SIZE_8, &offset, nullptr, fields8);
632
633 return offset;
634 }
635
636 /* static */
LayoutFields(Class * klass,Span<Field> fields,bool is_static,ClassLinkerErrorHandler * error_handler)637 bool ClassLinker::LayoutFields(Class *klass, Span<Field> fields, bool is_static,
638 [[maybe_unused]] ClassLinkerErrorHandler *error_handler)
639 {
640 PandaList<Field *> tagged_fields;
641 PandaList<Field *> fields64;
642 PandaList<Field *> fields32;
643 PandaList<Field *> fields16;
644 PandaList<Field *> fields8;
645 PandaList<Field *> ref_fields;
646
647 for (auto &field : fields) {
648 auto type = field.GetType();
649
650 if (!type.IsPrimitive()) {
651 ref_fields.push_back(&field);
652 continue;
653 }
654
655 switch (type.GetId()) {
656 case Type::TypeId::U1:
657 case Type::TypeId::I8:
658 case Type::TypeId::U8:
659 fields8.push_back(&field);
660 break;
661 case Type::TypeId::I16:
662 case Type::TypeId::U16:
663 fields16.push_back(&field);
664 break;
665 case Type::TypeId::I32:
666 case Type::TypeId::U32:
667 case Type::TypeId::F32:
668 fields32.push_back(&field);
669 break;
670 case Type::TypeId::I64:
671 case Type::TypeId::U64:
672 case Type::TypeId::F64:
673 fields64.push_back(&field);
674 break;
675 case Type::TypeId::TAGGED:
676 tagged_fields.push_back(&field);
677 break;
678 default:
679 UNREACHABLE();
680 break;
681 }
682 }
683
684 size_t size =
685 panda::LayoutFields(klass, &tagged_fields, &fields64, &fields32, &fields16, &fields8, &ref_fields, is_static);
686
687 if (!is_static && !klass->IsVariableSize()) {
688 klass->SetObjectSize(size);
689 }
690
691 return true;
692 }
693
LinkMethods(Class * klass,ClassInfo * class_info,ClassLinkerErrorHandler * error_handler)694 bool ClassLinker::LinkMethods(Class *klass, ClassInfo *class_info,
695 [[maybe_unused]] ClassLinkerErrorHandler *error_handler)
696 {
697 class_info->vtable_builder->UpdateClass(klass);
698 class_info->itable_builder->Resolve(klass);
699 class_info->itable_builder->UpdateClass(klass);
700 class_info->imtable_builder->UpdateClass(klass);
701
702 return true;
703 }
704
LinkFields(Class * klass,ClassLinkerErrorHandler * error_handler)705 bool ClassLinker::LinkFields(Class *klass, ClassLinkerErrorHandler *error_handler)
706 {
707 if (!LayoutFields(klass, klass->GetStaticFields(), true, error_handler)) {
708 LOG(ERROR, CLASS_LINKER) << "Cannot layout static fields of class '" << klass->GetName() << "'";
709 return false;
710 }
711
712 if (!LayoutFields(klass, klass->GetInstanceFields(), false, error_handler)) {
713 LOG(ERROR, CLASS_LINKER) << "Cannot layout instance fields of class '" << klass->GetName() << "'";
714 return false;
715 }
716
717 return true;
718 }
719
LoadBaseClass(panda_file::ClassDataAccessor * cda,const LanguageContext & ctx,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)720 Class *ClassLinker::LoadBaseClass(panda_file::ClassDataAccessor *cda, const LanguageContext &ctx,
721 ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler)
722 {
723 auto base_class_id = cda->GetSuperClassId();
724 auto *ext = GetExtension(ctx);
725 ASSERT(ext != nullptr);
726 if (base_class_id.GetOffset() == 0) {
727 return ext->GetClassRoot(ClassRoot::OBJECT);
728 }
729
730 auto &pf = cda->GetPandaFile();
731 auto *base_class = ext->GetClass(pf, base_class_id, context, error_handler);
732 if (base_class == nullptr) {
733 LOG(INFO, CLASS_LINKER) << "Cannot find base class '"
734 << utf::Mutf8AsCString(pf.GetStringData(base_class_id).data) << "' of class '"
735 << utf::Mutf8AsCString(pf.GetStringData(cda->GetClassId()).data) << "' in ctx "
736 << context;
737 return nullptr;
738 }
739
740 return base_class;
741 }
742
LoadInterfaces(panda_file::ClassDataAccessor * cda,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)743 std::optional<Span<Class *>> ClassLinker::LoadInterfaces(panda_file::ClassDataAccessor *cda,
744 ClassLinkerContext *context,
745 ClassLinkerErrorHandler *error_handler)
746 {
747 ASSERT(context != nullptr);
748 size_t ifaces_num = cda->GetIfacesNumber();
749
750 if (ifaces_num == 0) {
751 return Span<Class *>(nullptr, ifaces_num);
752 }
753
754 Span<Class *> ifaces {allocator_->AllocArray<Class *>(ifaces_num), ifaces_num};
755
756 for (size_t i = 0; i < ifaces_num; i++) {
757 auto id = cda->GetInterfaceId(i);
758 auto &pf = cda->GetPandaFile();
759 auto *iface = GetClass(pf, id, context, error_handler);
760 if (iface == nullptr) {
761 LOG(INFO, CLASS_LINKER) << "Cannot find interface '" << utf::Mutf8AsCString(pf.GetStringData(id).data)
762 << "' of class '" << utf::Mutf8AsCString(pf.GetStringData(cda->GetClassId()).data)
763 << "' in ctx " << context;
764 ASSERT(!ifaces.Empty());
765 allocator_->Free(ifaces.begin());
766 return {};
767 }
768
769 ifaces[i] = iface;
770 }
771
772 return ifaces;
773 }
774
775 using ClassLoadingSet = std::unordered_set<uint64_t>;
776
777 // This class is required to clear static unordered_set on return
778 class ClassScopeStaticSetAutoCleaner {
779 public:
780 ClassScopeStaticSetAutoCleaner() = default;
ClassScopeStaticSetAutoCleaner(ClassLoadingSet * set_ptr,ClassLoadingSet ** tl_set_ptr)781 explicit ClassScopeStaticSetAutoCleaner(ClassLoadingSet *set_ptr, ClassLoadingSet **tl_set_ptr)
782 : set_ptr_(set_ptr), tl_set_ptr_(tl_set_ptr)
783 {
784 }
~ClassScopeStaticSetAutoCleaner()785 ~ClassScopeStaticSetAutoCleaner()
786 {
787 set_ptr_->clear();
788 if (tl_set_ptr_ != nullptr) {
789 *tl_set_ptr_ = nullptr;
790 }
791 }
792
793 NO_COPY_SEMANTIC(ClassScopeStaticSetAutoCleaner);
794 NO_MOVE_SEMANTIC(ClassScopeStaticSetAutoCleaner);
795
796 private:
797 ClassLoadingSet *set_ptr_;
798 ClassLoadingSet **tl_set_ptr_;
799 };
800
GetClassUniqueHash(uint32_t panda_file_hash,uint32_t class_id)801 static uint64_t GetClassUniqueHash(uint32_t panda_file_hash, uint32_t class_id)
802 {
803 const uint8_t BITS_TO_SHUFFLE = 32;
804 return (static_cast<uint64_t>(panda_file_hash) << BITS_TO_SHUFFLE) | static_cast<uint64_t>(class_id);
805 }
806
LoadClass(panda_file::ClassDataAccessor * class_data_accessor,const uint8_t * descriptor,Class * base_class,Span<Class * > interfaces,ClassLinkerContext * context,ClassLinkerExtension * ext,ClassLinkerErrorHandler * error_handler)807 Class *ClassLinker::LoadClass(panda_file::ClassDataAccessor *class_data_accessor, const uint8_t *descriptor,
808 Class *base_class, Span<Class *> interfaces, ClassLinkerContext *context,
809 ClassLinkerExtension *ext, ClassLinkerErrorHandler *error_handler)
810 {
811 ASSERT(context != nullptr);
812 ClassInfo class_info = GetClassInfo(class_data_accessor, base_class, interfaces, context);
813
814 auto *klass = ext->CreateClass(descriptor, class_info.vtable_builder->GetVTableSize(),
815 class_info.imtable_builder->GetIMTSize(), class_info.size);
816
817 if (UNLIKELY(klass == nullptr)) {
818 return nullptr;
819 }
820
821 klass->SetLoadContext(context);
822 klass->SetBase(base_class);
823 klass->SetInterfaces(interfaces);
824 klass->SetFileId(class_data_accessor->GetClassId());
825 klass->SetPandaFile(&class_data_accessor->GetPandaFile());
826 klass->SetAccessFlags(class_data_accessor->GetAccessFlags());
827
828 auto &pf = class_data_accessor->GetPandaFile();
829 auto class_id = class_data_accessor->GetClassId();
830 klass->SetClassIndex(pf.GetClassIndex(class_id));
831 klass->SetMethodIndex(pf.GetMethodIndex(class_id));
832 klass->SetFieldIndex(pf.GetFieldIndex(class_id));
833
834 klass->SetNumVirtualMethods(class_info.vtable_builder->GetNumVirtualMethods());
835 klass->SetNumCopiedMethods(class_info.vtable_builder->GetCopiedMethods().size());
836 klass->SetNumStaticFields(class_info.num_sfields);
837
838 if (!LoadMethods(klass, &class_info, class_data_accessor, error_handler)) {
839 FreeClass(klass);
840 LOG(ERROR, CLASS_LINKER) << "Cannot load methods of class '" << descriptor << "'";
841 return nullptr;
842 }
843
844 if (!LoadFields(klass, class_data_accessor, error_handler)) {
845 FreeClass(klass);
846 LOG(ERROR, CLASS_LINKER) << "Cannot load fields of class '" << descriptor << "'";
847 return nullptr;
848 }
849
850 if (!LinkMethods(klass, &class_info, error_handler)) {
851 FreeClass(klass);
852 LOG(ERROR, CLASS_LINKER) << "Cannot link methods of class '" << descriptor << "'";
853 return nullptr;
854 }
855
856 if (!LinkFields(klass, error_handler)) {
857 FreeClass(klass);
858 LOG(ERROR, CLASS_LINKER) << "Cannot link fields of class '" << descriptor << "'";
859 return nullptr;
860 }
861
862 return klass;
863 }
864
LoadClass(const panda_file::File * pf,const uint8_t * descriptor,panda_file::SourceLang lang)865 Class *ClassLinker::LoadClass(const panda_file::File *pf, const uint8_t *descriptor, panda_file::SourceLang lang)
866 {
867 panda_file::File::EntityId class_id = pf->GetClassId(descriptor);
868 if (!class_id.IsValid() || pf->IsExternal(class_id)) {
869 return nullptr;
870 }
871 ClassLinkerContext *context = GetExtension(lang)->GetBootContext();
872 return LoadClass(pf, class_id, descriptor, context, nullptr);
873 }
874
LoadClass(const panda_file::File * pf,panda_file::File::EntityId class_id,const uint8_t * descriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler,bool add_to_runtime)875 Class *ClassLinker::LoadClass(const panda_file::File *pf, panda_file::File::EntityId class_id,
876 const uint8_t *descriptor, ClassLinkerContext *context,
877 ClassLinkerErrorHandler *error_handler, bool add_to_runtime /* = true */)
878 {
879 ASSERT(!pf->IsExternal(class_id));
880 ASSERT(context != nullptr);
881 panda_file::ClassDataAccessor class_data_accessor(*pf, class_id);
882 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(&class_data_accessor);
883
884 if (ctx.GetLanguage() != context->GetSourceLang()) {
885 LanguageContext current_ctx = Runtime::GetCurrent()->GetLanguageContext(context->GetSourceLang());
886 PandaStringStream ss;
887 ss << "Cannot load " << ctx << " class " << descriptor << " into " << current_ctx << " context";
888 LOG(ERROR, CLASS_LINKER) << ss.str();
889 OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str());
890 return nullptr;
891 }
892
893 if (!HasExtension(ctx)) {
894 PandaStringStream ss;
895 ss << "Cannot load class '" << descriptor << "' as class linker hasn't " << ctx << " language extension";
896 LOG(ERROR, CLASS_LINKER) << ss.str();
897 OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str());
898 return nullptr;
899 }
900
901 // This set is used to find out if the class is its own superclass
902 ClassLoadingSet loading_set;
903 static thread_local ClassLoadingSet *thread_local_set = nullptr;
904 ClassLoadingSet **thread_local_set_ptr = nullptr;
905 if (thread_local_set == nullptr) {
906 thread_local_set = &loading_set;
907 thread_local_set_ptr = &thread_local_set;
908 }
909 ClassScopeStaticSetAutoCleaner class_set_auto_cleaner_on_return(thread_local_set, thread_local_set_ptr);
910
911 auto *ext = GetExtension(ctx);
912 Class *base_class = nullptr;
913 bool need_load_base = IsInitialized() || !utf::IsEqual(ctx.GetObjectClassDescriptor(), descriptor);
914 if (need_load_base) {
915 uint32_t class_id_int = class_id.GetOffset();
916 uint32_t panda_file_hash = pf->GetFilenameHash();
917 if (!thread_local_set->insert(GetClassUniqueHash(panda_file_hash, class_id_int)).second) {
918 const PandaString &class_name =
919 utf::Mutf8AsCString(pf->GetStringData(class_data_accessor.GetClassId()).data);
920 PandaString msg = "Class or interface \"" + class_name + "\" is its own superclass or superinterface";
921 OnError(error_handler, Error::CLASS_CIRCULARITY, msg);
922 return nullptr;
923 }
924
925 base_class = LoadBaseClass(&class_data_accessor, ctx, context, error_handler);
926 if (base_class == nullptr) {
927 LOG(INFO, CLASS_LINKER) << "Cannot load base class of class '" << descriptor << "'";
928 return nullptr;
929 }
930 }
931
932 auto res = LoadInterfaces(&class_data_accessor, context, error_handler);
933 if (!res) {
934 LOG(INFO, CLASS_LINKER) << "Cannot load interfaces of class '" << descriptor << "'";
935 return nullptr;
936 }
937
938 auto *klass = LoadClass(&class_data_accessor, descriptor, base_class, res.value(), context, ext, error_handler);
939 if (klass == nullptr) {
940 return nullptr;
941 }
942
943 Runtime::GetCurrent()->GetCha()->Update(klass);
944
945 if (LIKELY(ext->CanInitializeClasses())) {
946 ext->InitializeClass(klass);
947 klass->SetState(Class::State::LOADED);
948 }
949
950 if (LIKELY(add_to_runtime)) {
951 Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(klass);
952
953 auto *other_klass = context->InsertClass(klass);
954 if (other_klass != nullptr) {
955 // Someone has created the class in the other thread (increase the critical section?)
956 FreeClass(klass);
957 return other_klass;
958 }
959
960 RemoveCreatedClassInExtension(klass);
961 Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(klass);
962 }
963 return klass;
964 }
965
CopyMutf8String(mem::InternalAllocatorPtr allocator,const uint8_t * descriptor)966 static const uint8_t *CopyMutf8String(mem::InternalAllocatorPtr allocator, const uint8_t *descriptor)
967 {
968 size_t size = utf::Mutf8Size(descriptor) + 1; // + 1 - null terminate
969 auto *ptr = allocator->AllocArray<uint8_t>(size);
970 memcpy_s(ptr, size, descriptor, size);
971 return ptr;
972 }
973
BuildClass(const uint8_t * descriptor,bool need_copy_descriptor,uint32_t access_flags,Span<Method> methods,Span<Field> fields,Class * base_class,Span<Class * > interfaces,ClassLinkerContext * context,bool is_interface)974 Class *ClassLinker::BuildClass(const uint8_t *descriptor, bool need_copy_descriptor, uint32_t access_flags,
975 Span<Method> methods, Span<Field> fields, Class *base_class, Span<Class *> interfaces,
976 ClassLinkerContext *context, bool is_interface)
977 {
978 ASSERT(context != nullptr);
979 if (need_copy_descriptor) {
980 descriptor = CopyMutf8String(allocator_, descriptor);
981 os::memory::LockHolder lock(copied_names_lock_);
982 copied_names_.push_front(descriptor);
983 }
984
985 auto *ext = GetExtension(base_class->GetSourceLang());
986 ASSERT(ext != nullptr);
987
988 ClassInfo class_info = GetClassInfo(methods, fields, base_class, interfaces, is_interface);
989
990 // Need to protect ArenaAllocator and loaded_classes_
991 auto *klass = ext->CreateClass(descriptor, class_info.vtable_builder->GetVTableSize(),
992 class_info.imtable_builder->GetIMTSize(), class_info.size);
993
994 if (UNLIKELY(klass == nullptr)) {
995 return nullptr;
996 }
997
998 klass->SetLoadContext(context);
999 klass->SetBase(base_class);
1000 klass->SetInterfaces(interfaces);
1001 klass->SetAccessFlags(access_flags);
1002
1003 klass->SetNumVirtualMethods(class_info.vtable_builder->GetNumVirtualMethods());
1004 klass->SetNumCopiedMethods(class_info.vtable_builder->GetCopiedMethods().size());
1005 klass->SetNumStaticFields(class_info.num_sfields);
1006
1007 ASSERT(klass->GetNumCopiedMethods() == 0);
1008
1009 size_t num_smethods = methods.size() - klass->GetNumVirtualMethods();
1010 klass->SetMethods(methods, klass->GetNumVirtualMethods(), num_smethods);
1011 klass->SetFields(fields, klass->GetNumStaticFields());
1012
1013 for (auto &method : methods) {
1014 method.SetClass(klass);
1015 }
1016
1017 for (auto &field : fields) {
1018 field.SetClass(klass);
1019 }
1020
1021 if (!LinkMethods(klass, &class_info, ext->GetErrorHandler())) {
1022 LOG(ERROR, CLASS_LINKER) << "Cannot link class methods '" << descriptor << "'";
1023 return nullptr;
1024 }
1025
1026 if (!LinkFields(klass, ext->GetErrorHandler())) {
1027 LOG(ERROR, CLASS_LINKER) << "Cannot link class fields '" << descriptor << "'";
1028 return nullptr;
1029 }
1030
1031 ext->InitializeClass(klass);
1032 klass->SetState(Class::State::LOADED);
1033
1034 Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(klass);
1035
1036 auto *other_klass = context->InsertClass(klass);
1037 if (other_klass != nullptr) {
1038 // Someone has created the class in the other thread (increase the critical section?)
1039 FreeClass(klass);
1040 return other_klass;
1041 }
1042
1043 RemoveCreatedClassInExtension(klass);
1044 Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(klass);
1045
1046 return klass;
1047 }
1048
CreateArrayClass(ClassLinkerExtension * ext,const uint8_t * descriptor,bool need_copy_descriptor,Class * component_class)1049 Class *ClassLinker::CreateArrayClass(ClassLinkerExtension *ext, const uint8_t *descriptor, bool need_copy_descriptor,
1050 Class *component_class)
1051 {
1052 if (need_copy_descriptor) {
1053 descriptor = CopyMutf8String(allocator_, descriptor);
1054 os::memory::LockHolder lock(copied_names_lock_);
1055 copied_names_.push_front(descriptor);
1056 }
1057
1058 auto *array_class = ext->CreateClass(descriptor, ext->GetArrayClassVTableSize(), ext->GetArrayClassIMTSize(),
1059 ext->GetArrayClassSize());
1060
1061 if (UNLIKELY(array_class == nullptr)) {
1062 return nullptr;
1063 }
1064
1065 array_class->SetLoadContext(component_class->GetLoadContext());
1066
1067 ext->InitializeArrayClass(array_class, component_class);
1068
1069 return array_class;
1070 }
1071
LoadArrayClass(const uint8_t * descriptor,bool need_copy_descriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1072 Class *ClassLinker::LoadArrayClass(const uint8_t *descriptor, bool need_copy_descriptor, ClassLinkerContext *context,
1073 ClassLinkerErrorHandler *error_handler)
1074 {
1075 Span<const uint8_t> sp(descriptor, 1);
1076
1077 Class *component_class = GetClass(sp.cend(), need_copy_descriptor, context, error_handler);
1078
1079 if (component_class == nullptr) {
1080 return nullptr;
1081 }
1082
1083 if (UNLIKELY(component_class->GetType().GetId() == panda_file::Type::TypeId::VOID)) {
1084 OnError(error_handler, Error::NO_CLASS_DEF, "Try to create array with void component type");
1085 return nullptr;
1086 }
1087
1088 auto *ext = GetExtension(component_class->GetSourceLang());
1089 ASSERT(ext != nullptr);
1090
1091 auto *component_class_context = component_class->GetLoadContext();
1092 ASSERT(component_class_context != nullptr);
1093 if (component_class_context != context) {
1094 auto *loaded_class = FindLoadedClass(descriptor, component_class_context);
1095 if (loaded_class != nullptr) {
1096 return loaded_class;
1097 }
1098 }
1099
1100 auto *array_class = CreateArrayClass(ext, descriptor, need_copy_descriptor, component_class);
1101
1102 if (UNLIKELY(array_class == nullptr)) {
1103 return nullptr;
1104 }
1105
1106 Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(array_class);
1107
1108 auto *other_klass = component_class_context->InsertClass(array_class);
1109 if (other_klass != nullptr) {
1110 FreeClass(array_class);
1111 return other_klass;
1112 }
1113
1114 RemoveCreatedClassInExtension(array_class);
1115 Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(array_class);
1116
1117 return array_class;
1118 }
1119
PandaFilesToString(const PandaVector<const panda_file::File * > & panda_files)1120 static PandaString PandaFilesToString(const PandaVector<const panda_file::File *> &panda_files)
1121 {
1122 PandaStringStream ss;
1123 ss << "[";
1124
1125 size_t n = panda_files.size();
1126 for (size_t i = 0; i < n; i++) {
1127 ss << panda_files[i]->GetFilename();
1128
1129 if (i < n - 1) {
1130 ss << ", ";
1131 }
1132 }
1133
1134 ss << "]";
1135 return ss.str();
1136 }
1137
GetClass(const uint8_t * descriptor,bool need_copy_descriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1138 Class *ClassLinker::GetClass(const uint8_t *descriptor, bool need_copy_descriptor, ClassLinkerContext *context,
1139 ClassLinkerErrorHandler *error_handler /* = nullptr */)
1140 {
1141 ASSERT(context != nullptr);
1142 ASSERT(!MTManagedThread::ThreadIsMTManagedThread(Thread::GetCurrent()) ||
1143 !PandaVM::GetCurrent()->GetGC()->IsGCRunning() || Locks::mutator_lock->HasLock());
1144
1145 Class *cls = FindLoadedClass(descriptor, context);
1146 if (cls != nullptr) {
1147 return cls;
1148 }
1149
1150 if (ClassHelper::IsArrayDescriptor(descriptor)) {
1151 return LoadArrayClass(descriptor, need_copy_descriptor, context, error_handler);
1152 }
1153
1154 if (context->IsBootContext()) {
1155 panda_file::File::EntityId class_id;
1156 const panda_file::File *panda_file {nullptr};
1157 {
1158 {
1159 os::memory::LockHolder lock {boot_panda_files_lock_};
1160 std::tie(class_id, panda_file) = FindClassInPandaFiles(descriptor, boot_panda_files_);
1161 }
1162
1163 if (!class_id.IsValid()) {
1164 PandaStringStream ss;
1165 {
1166 // can't make a wider scope for lock here - will get recursion
1167 os::memory::LockHolder lock {boot_panda_files_lock_};
1168 ss << "Cannot find class " << descriptor
1169 << " in boot panda files: " << PandaFilesToString(boot_panda_files_);
1170 }
1171 OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str());
1172 return nullptr;
1173 }
1174 }
1175
1176 return LoadClass(panda_file, class_id, panda_file->GetStringData(class_id).data, context, error_handler);
1177 }
1178
1179 return context->LoadClass(descriptor, need_copy_descriptor, error_handler);
1180 }
1181
GetClass(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1182 Class *ClassLinker::GetClass(const panda_file::File &pf, panda_file::File::EntityId id, ClassLinkerContext *context,
1183 ClassLinkerErrorHandler *error_handler /* = nullptr */)
1184 {
1185 ASSERT(context != nullptr);
1186 ASSERT(!MTManagedThread::ThreadIsMTManagedThread(Thread::GetCurrent()) ||
1187 !PandaVM::GetCurrent()->GetGC()->IsGCRunning() || Locks::mutator_lock->HasLock());
1188
1189 Class *cls = pf.GetPandaCache()->GetClassFromCache(id);
1190 if (cls != nullptr) {
1191 return cls;
1192 }
1193 const uint8_t *descriptor = pf.GetStringData(id).data;
1194
1195 cls = FindLoadedClass(descriptor, context);
1196 if (cls != nullptr) {
1197 pf.GetPandaCache()->SetClassCache(id, cls);
1198 return cls;
1199 }
1200
1201 if (ClassHelper::IsArrayDescriptor(descriptor)) {
1202 cls = LoadArrayClass(descriptor, false, context, error_handler);
1203 if (LIKELY(cls != nullptr)) {
1204 pf.GetPandaCache()->SetClassCache(id, cls);
1205 }
1206 return cls;
1207 }
1208
1209 if (context->IsBootContext()) {
1210 const panda_file::File *pf_ptr = nullptr;
1211 panda_file::File::EntityId ext_id;
1212 {
1213 os::memory::LockHolder lock {boot_panda_files_lock_};
1214 std::tie(ext_id, pf_ptr) = FindClassInPandaFiles(descriptor, boot_panda_files_);
1215 }
1216
1217 if (!ext_id.IsValid()) {
1218 PandaStringStream ss;
1219 {
1220 // can't make a wider scope for lock here - will get recursion
1221 os::memory::LockHolder lock {boot_panda_files_lock_};
1222 ss << "Cannot find class " << descriptor
1223 << " in boot panda files: " << PandaFilesToString(boot_panda_files_);
1224 }
1225 OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str());
1226 return nullptr;
1227 }
1228
1229 cls = LoadClass(pf_ptr, ext_id, descriptor, context, error_handler);
1230 if (LIKELY(cls != nullptr)) {
1231 pf.GetPandaCache()->SetClassCache(id, cls);
1232 }
1233 return cls;
1234 }
1235
1236 return context->LoadClass(descriptor, false, error_handler);
1237 }
1238
GetMethod(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1239 Method *ClassLinker::GetMethod(const panda_file::File &pf, panda_file::File::EntityId id,
1240 ClassLinkerContext *context /* = nullptr */,
1241 ClassLinkerErrorHandler *error_handler /* = nullptr */)
1242 {
1243 Method *method = pf.GetPandaCache()->GetMethodFromCache(id);
1244 if (method != nullptr) {
1245 return method;
1246 }
1247 panda_file::MethodDataAccessor method_data_accessor(pf, id);
1248
1249 auto class_id = method_data_accessor.GetClassId();
1250 if (context == nullptr) {
1251 panda_file::ClassDataAccessor class_data_accessor(pf, class_id);
1252 auto lang = class_data_accessor.GetSourceLang();
1253 if (!lang) {
1254 LOG(INFO, CLASS_LINKER) << "Cannot resolve language context for class_id " << class_id << " in file "
1255 << pf.GetFilename();
1256 return nullptr;
1257 }
1258 auto *extension = GetExtension(lang.value());
1259 context = extension->GetBootContext();
1260 }
1261
1262 Class *klass = GetClass(pf, class_id, context, error_handler);
1263
1264 if (klass == nullptr) {
1265 auto class_name = pf.GetStringData(class_id).data;
1266 LOG(INFO, CLASS_LINKER) << "Cannot find class '" << class_name << "' in ctx " << context;
1267 return nullptr;
1268 }
1269 method = GetMethod(klass, method_data_accessor, error_handler);
1270 if (LIKELY(method != nullptr)) {
1271 pf.GetPandaCache()->SetMethodCache(id, method);
1272 }
1273 return method;
1274 }
1275
GetMethod(const Method & caller,panda_file::File::EntityId id,ClassLinkerErrorHandler * error_handler)1276 Method *ClassLinker::GetMethod(const Method &caller, panda_file::File::EntityId id,
1277 ClassLinkerErrorHandler *error_handler /* = nullptr */)
1278 {
1279 auto *pf = caller.GetPandaFile();
1280 Method *method = pf->GetPandaCache()->GetMethodFromCache(id);
1281 if (method != nullptr) {
1282 return method;
1283 }
1284
1285 panda_file::MethodDataAccessor method_data_accessor(*pf, id);
1286 auto class_id = method_data_accessor.GetClassId();
1287
1288 auto *context = caller.GetClass()->GetLoadContext();
1289 auto *ext = GetExtension(caller.GetClass()->GetSourceLang());
1290 Class *klass = ext->GetClass(*pf, class_id, context, error_handler);
1291
1292 if (klass == nullptr) {
1293 auto class_name = pf->GetStringData(class_id).data;
1294 LOG(INFO, CLASS_LINKER) << "Cannot find class '" << class_name << "' in ctx " << context;
1295 return nullptr;
1296 }
1297
1298 method =
1299 GetMethod(klass, method_data_accessor, (error_handler == nullptr) ? ext->GetErrorHandler() : error_handler);
1300 if (LIKELY(method != nullptr)) {
1301 pf->GetPandaCache()->SetMethodCache(id, method);
1302 }
1303 return method;
1304 }
1305
GetMethod(const Class * klass,const panda_file::MethodDataAccessor & method_data_accessor,ClassLinkerErrorHandler * error_handler)1306 Method *ClassLinker::GetMethod(const Class *klass, const panda_file::MethodDataAccessor &method_data_accessor,
1307 ClassLinkerErrorHandler *error_handler)
1308 {
1309 Method *method;
1310 auto id = method_data_accessor.GetMethodId();
1311 const auto &pf = method_data_accessor.GetPandaFile();
1312
1313 bool is_static = method_data_accessor.IsStatic();
1314 if (!method_data_accessor.IsExternal() && klass->GetPandaFile() == &pf) {
1315 if (klass->IsInterface()) {
1316 method = is_static ? klass->GetStaticInterfaceMethod(id) : klass->GetVirtualInterfaceMethod(id);
1317 } else {
1318 method = is_static ? klass->GetStaticClassMethod(id) : klass->GetVirtualClassMethod(id);
1319 }
1320
1321 if (method == nullptr) {
1322 PandaStringStream ss;
1323 ss << "Cannot find method '" << method_data_accessor.GetName().data << "' in class '" << klass->GetName()
1324 << "'";
1325 OnError(error_handler, Error::METHOD_NOT_FOUND, ss.str());
1326 return nullptr;
1327 }
1328
1329 return method;
1330 }
1331
1332 auto name = method_data_accessor.GetName();
1333 Method::Proto proto(pf, method_data_accessor.GetProtoId());
1334 if (klass->IsInterface()) {
1335 method = is_static ? klass->GetStaticInterfaceMethodByName(name, proto)
1336 : klass->GetVirtualInterfaceMethodByName(name, proto);
1337 } else {
1338 method = is_static ? klass->GetStaticClassMethodByName(name, proto)
1339 : klass->GetVirtualClassMethodByName(name, proto);
1340 if (method == nullptr && klass->IsAbstract()) {
1341 method = klass->GetInterfaceMethod(name, proto);
1342 }
1343 }
1344
1345 if (method == nullptr) {
1346 PandaStringStream ss;
1347 ss << "Cannot find method '" << name.data << "' in class '" << klass->GetName() << "'";
1348 OnError(error_handler, Error::METHOD_NOT_FOUND, ss.str());
1349 return nullptr;
1350 }
1351
1352 LOG_IF(method->IsStatic() != method_data_accessor.IsStatic(), FATAL, CLASS_LINKER)
1353 << "Expected ACC_STATIC for method " << name.data << " in class " << klass->GetName()
1354 << " does not match loaded value";
1355
1356 return method;
1357 }
1358
GetFieldById(Class * klass,const panda_file::FieldDataAccessor & field_data_accessor,ClassLinkerErrorHandler * error_handler)1359 Field *ClassLinker::GetFieldById(Class *klass, const panda_file::FieldDataAccessor &field_data_accessor,
1360 ClassLinkerErrorHandler *error_handler)
1361 {
1362 bool is_static = field_data_accessor.IsStatic();
1363 auto &pf = field_data_accessor.GetPandaFile();
1364 auto id = field_data_accessor.GetFieldId();
1365
1366 Field *field = is_static ? klass->FindStaticFieldById(id) : klass->FindInstanceFieldById(id);
1367
1368 if (field == nullptr) {
1369 PandaStringStream ss;
1370 ss << "Cannot find field '" << pf.GetStringData(field_data_accessor.GetNameId()).data << "' in class '"
1371 << klass->GetName() << "'";
1372 OnError(error_handler, Error::FIELD_NOT_FOUND, ss.str());
1373 return nullptr;
1374 }
1375
1376 pf.GetPandaCache()->SetFieldCache(id, field);
1377 return field;
1378 }
1379
GetFieldBySignature(Class * klass,const panda_file::FieldDataAccessor & field_data_accessor,ClassLinkerErrorHandler * error_handler)1380 Field *ClassLinker::GetFieldBySignature(Class *klass, const panda_file::FieldDataAccessor &field_data_accessor,
1381 ClassLinkerErrorHandler *error_handler)
1382 {
1383 auto &pf = field_data_accessor.GetPandaFile();
1384 auto id = field_data_accessor.GetFieldId();
1385 auto field_name = pf.GetStringData(field_data_accessor.GetNameId());
1386 auto field_type = panda_file::Type::GetTypeFromFieldEncoding(field_data_accessor.GetType());
1387 Field *field = klass->FindField([&](const Field &fld) {
1388 if (field_type == fld.GetType() && field_name == fld.GetName()) {
1389 if (!field_type.IsReference()) {
1390 return true;
1391 }
1392
1393 // compare field class type
1394 if (&pf == fld.GetPandaFile() && id == fld.GetFileId()) {
1395 return true;
1396 }
1397 auto type_id = panda_file::FieldDataAccessor::GetTypeId(*fld.GetPandaFile(), fld.GetFileId());
1398 if (pf.GetStringData(panda_file::File::EntityId(field_data_accessor.GetType())) ==
1399 fld.GetPandaFile()->GetStringData(type_id)) {
1400 return true;
1401 }
1402 }
1403 return false;
1404 });
1405
1406 if (field == nullptr) {
1407 PandaStringStream ss;
1408 ss << "Cannot find field '" << field_name.data << "' in class '" << klass->GetName() << "'";
1409 OnError(error_handler, Error::FIELD_NOT_FOUND, ss.str());
1410 return nullptr;
1411 }
1412
1413 pf.GetPandaCache()->SetFieldCache(id, field);
1414 return field;
1415 }
1416
GetField(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1417 Field *ClassLinker::GetField(const panda_file::File &pf, panda_file::File::EntityId id,
1418 ClassLinkerContext *context /* = nullptr */,
1419 ClassLinkerErrorHandler *error_handler /* = nullptr */)
1420 {
1421 Field *field = pf.GetPandaCache()->GetFieldFromCache(id);
1422 if (field != nullptr) {
1423 return field;
1424 }
1425 panda_file::FieldDataAccessor field_data_accessor(pf, id);
1426
1427 Class *klass = GetClass(pf, field_data_accessor.GetClassId(), context, error_handler);
1428
1429 if (klass == nullptr) {
1430 auto class_name = pf.GetStringData(field_data_accessor.GetClassId()).data;
1431 LOG(INFO, CLASS_LINKER) << "Cannot find class '" << class_name << "' in ctx " << context;
1432 return nullptr;
1433 }
1434
1435 if (!field_data_accessor.IsExternal() && klass->GetPandaFile() == &pf) {
1436 field = GetFieldById(klass, field_data_accessor, error_handler);
1437 } else {
1438 field = GetFieldBySignature(klass, field_data_accessor, error_handler);
1439 }
1440 return field;
1441 }
1442
InitializeClass(ManagedThread * thread,Class * klass)1443 bool ClassLinker::InitializeClass(ManagedThread *thread, Class *klass)
1444 {
1445 ASSERT(klass != nullptr);
1446 if (klass->IsInitialized()) {
1447 return true;
1448 }
1449
1450 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
1451 return ctx.InitializeClass(this, thread, klass);
1452 }
1453
NumLoadedClasses()1454 size_t ClassLinker::NumLoadedClasses()
1455 {
1456 size_t sum = 0;
1457
1458 for (auto &ext : extensions_) {
1459 if (ext == nullptr) {
1460 continue;
1461 }
1462
1463 sum += ext->NumLoadedClasses();
1464 }
1465
1466 return sum;
1467 }
1468
VisitLoadedClasses(size_t flag)1469 void ClassLinker::VisitLoadedClasses(size_t flag)
1470 {
1471 for (auto &ext : extensions_) {
1472 if (ext == nullptr) {
1473 continue;
1474 }
1475 ext->VisitLoadedClasses(flag);
1476 }
1477 }
1478
OnError(ClassLinkerErrorHandler * error_handler,ClassLinker::Error error,const PandaString & msg)1479 void ClassLinker::OnError(ClassLinkerErrorHandler *error_handler, ClassLinker::Error error, const PandaString &msg)
1480 {
1481 if (error_handler != nullptr) {
1482 error_handler->OnError(error, msg);
1483 }
1484 }
1485
GetField(const Method & caller,panda_file::File::EntityId id,ClassLinkerErrorHandler * error_handler)1486 Field *ClassLinker::GetField(const Method &caller, panda_file::File::EntityId id,
1487 ClassLinkerErrorHandler *error_handler /* = nullptr */)
1488 {
1489 Field *field = caller.GetPandaFile()->GetPandaCache()->GetFieldFromCache(id);
1490 if (field != nullptr) {
1491 return field;
1492 }
1493 auto *ext = GetExtension(caller.GetClass()->GetSourceLang());
1494 field = GetField(*caller.GetPandaFile(), id, caller.GetClass()->GetLoadContext(),
1495 (error_handler == nullptr) ? ext->GetErrorHandler() : error_handler);
1496 if (LIKELY(field != nullptr)) {
1497 caller.GetPandaFile()->GetPandaCache()->SetFieldCache(id, field);
1498 }
1499 return field;
1500 }
1501
RemoveCreatedClassInExtension(Class * klass)1502 void ClassLinker::RemoveCreatedClassInExtension(Class *klass)
1503 {
1504 if (klass == nullptr) {
1505 return;
1506 }
1507 auto ext = GetExtension(klass->GetSourceLang());
1508 if (ext != nullptr) {
1509 ext->OnClassPrepared(klass);
1510 }
1511 }
1512
1513 } // namespace panda
1514