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