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 "cache.h"
17
18 #include "file_items.h"
19 #include "macros.h"
20 #include "runtime/include/runtime.h"
21 #include "runtime/include/class.h"
22 #include "runtime/include/method.h"
23 #include "runtime/include/field.h"
24 #include "runtime/include/class_helper.h"
25 #include "runtime/include/language_context.h"
26 #include "libpandabase/utils/logger.h"
27 #include "libpandabase/utils/utf.h"
28 #include "libpandabase/utils/hash.h"
29 #include "libpandafile/method_data_accessor-inl.h"
30 #include "libpandafile/class_data_accessor.h"
31 #include "libpandafile/class_data_accessor-inl.h"
32 #include "libpandafile/code_data_accessor.h"
33 #include "libpandafile/code_data_accessor-inl.h"
34 #include "libpandafile/field_data_accessor-inl.h"
35 #include "libpandafile/proto_data_accessor.h"
36 #include "libpandafile/proto_data_accessor-inl.h"
37 #include "libpandafile/modifiers.h"
38 #include "plugins.h"
39 #include "verifier_messages.h"
40 #include "file_items.h"
41
42 namespace panda::verifier {
43
44 using FastAPIClassRW = LibCache::FastAPIClass<LibCache::Access::READ_WRITE>;
45 using FastAPIClassRO = LibCache::FastAPIClass<LibCache::Access::READ_ONLY>;
46
GetName(const LibCache::CachedClass & cachedClass)47 PandaString LibCache::GetName(const LibCache::CachedClass &cachedClass)
48 {
49 if (cachedClass.type_id == panda_file::Type::TypeId::REFERENCE) {
50 return ClassHelper::GetName<PandaString>(cachedClass.descriptor);
51 }
52 return {ClassHelper::GetPrimitiveTypeStr(cachedClass.type_id)};
53 }
54
GetName(const DescriptorString & descriptor)55 PandaString LibCache::GetName(const DescriptorString &descriptor)
56 {
57 return ClassHelper::GetName<PandaString>(descriptor);
58 }
59
GetName(const LibCache::CachedMethod & cachedMethod)60 PandaString LibCache::GetName(const LibCache::CachedMethod &cachedMethod)
61 {
62 PandaString str = GetName(cachedMethod.klass);
63 str += "::";
64 str += utf::Mutf8AsCString(cachedMethod.name);
65 str += " : ";
66 size_t idx = 0;
67 for (const auto &arg : cachedMethod.signature) {
68 if (idx > 1) {
69 str += ", ";
70 }
71 str += GetName(arg);
72 if (idx == 0) {
73 str += "(";
74 }
75 ++idx;
76 }
77 str += ")";
78 return str;
79 }
80
GetName(const LibCache::CachedField & cachedField)81 PandaString LibCache::GetName(const LibCache::CachedField &cachedField)
82 {
83 auto str = GetName(cachedField.klass);
84 str += ".";
85 str += utf::Mutf8AsCString(cachedField.name);
86 str += " : ";
87 str += GetName(cachedField.type);
88 return str;
89 }
90
91 template <>
92 bool FastAPIClassRW::Link(LibCache::CachedClass *cachedClass, bool reportError);
93
94 template <>
95 bool FastAPIClassRW::Link(LibCache::CachedMethod *cachedMethod, bool reportError);
96
97 template <>
98 bool FastAPIClassRW::Link(LibCache::CachedField *cachedField, bool reportError);
99
100 template <>
GetMethod(panda_file::SourceLang src_lang,LibCache::Id id,bool reportError)101 OptionalRef<LibCache::CachedMethod> FastAPIClassRW::GetMethod(panda_file::SourceLang src_lang, LibCache::Id id,
102 bool reportError)
103 {
104 return GetFromCacheAndLink(&GetContext(src_lang).method_cache, id, reportError);
105 }
106
107 namespace {
108
StringDataToString(panda_file::File::StringData sd)109 std::string StringDataToString(panda_file::File::StringData sd)
110 {
111 std::string res {reinterpret_cast<char *>(const_cast<uint8_t *>(sd.data))};
112 return res;
113 }
114
GetClassFlags(uint32_t raw_flags)115 LibCache::CachedClass::FlagsValue GetClassFlags(uint32_t raw_flags)
116 {
117 LibCache::CachedClass::FlagsValue flags;
118 flags[LibCache::CachedClass::Flag::PUBLIC] = (raw_flags & ACC_PUBLIC) != 0;
119 flags[LibCache::CachedClass::Flag::FINAL] = (raw_flags & ACC_FINAL) != 0;
120 flags[LibCache::CachedClass::Flag::ANNOTATION] = (raw_flags & ACC_ANNOTATION) != 0;
121 flags[LibCache::CachedClass::Flag::ENUM] = (raw_flags & ACC_ENUM) != 0;
122
123 flags[LibCache::CachedClass::Flag::ABSTRACT] = (raw_flags & ACC_ABSTRACT) != 0;
124 flags[LibCache::CachedClass::Flag::INTERFACE] = (raw_flags & ACC_INTERFACE) != 0;
125
126 return flags;
127 }
128
GetMethodFlags(const panda_file::File & file,const panda_file::MethodDataAccessor & mda,panda_file::SourceLang src_lang)129 LibCache::CachedMethod::FlagsValue GetMethodFlags(const panda_file::File &file,
130 const panda_file::MethodDataAccessor &mda,
131 panda_file::SourceLang src_lang)
132 {
133 LibCache::CachedMethod::FlagsValue flags;
134
135 flags[LibCache::CachedMethod::Flag::STATIC] = mda.IsStatic();
136 flags[LibCache::CachedMethod::Flag::NATIVE] = mda.IsNative();
137 flags[LibCache::CachedMethod::Flag::PUBLIC] = mda.IsPublic();
138 flags[LibCache::CachedMethod::Flag::PRIVATE] = mda.IsPrivate();
139 flags[LibCache::CachedMethod::Flag::PROTECTED] = mda.IsProtected();
140 flags[LibCache::CachedMethod::Flag::SYNTHETIC] = mda.IsSynthetic();
141 flags[LibCache::CachedMethod::Flag::ABSTRACT] = mda.IsAbstract();
142 flags[LibCache::CachedMethod::Flag::FINAL] = mda.IsFinal();
143
144 const auto method_name_raw = StringDataToString(file.GetStringData(mda.GetNameId()));
145 std::string ctor_name = panda::panda_file::GetCtorName(src_lang);
146 std::string static_ctor_name = panda::panda_file::GetCctorName(src_lang);
147
148 flags[LibCache::CachedMethod::Flag::CONSTRUCTOR] = (method_name_raw == ctor_name);
149 flags[LibCache::CachedMethod::Flag::STATIC_CONSTRUCTOR] = (method_name_raw == static_ctor_name);
150
151 return flags;
152 }
153
GetFieldFlags(const panda_file::FieldDataAccessor & fda)154 LibCache::CachedField::FlagsValue GetFieldFlags(const panda_file::FieldDataAccessor &fda)
155 {
156 LibCache::CachedField::FlagsValue flags;
157 flags[LibCache::CachedField::Flag::STATIC] = fda.IsStatic();
158 flags[LibCache::CachedField::Flag::VOLATILE] = fda.IsVolatile();
159 flags[LibCache::CachedField::Flag::PUBLIC] = fda.IsPublic();
160 flags[LibCache::CachedField::Flag::PROTECTED] = fda.IsProtected();
161 flags[LibCache::CachedField::Flag::FINAL] = fda.IsFinal();
162 flags[LibCache::CachedField::Flag::PRIVATE] = fda.IsPrivate();
163 return flags;
164 }
165
LogConflictingClassDefinitions(const LibCache::CachedClass & cachedClass,const LibCache::CachedClass & already_cached_class)166 void LogConflictingClassDefinitions(const LibCache::CachedClass &cachedClass,
167 const LibCache::CachedClass &already_cached_class)
168 {
169 auto get_file = [](const LibCache::CachedClass &cached_class1) {
170 if (auto file = cached_class1.file; file.HasRef()) {
171 PandaString result {"in "};
172 result.append(file->GetFullFileName());
173 return result;
174 }
175 return PandaString {"as a synthetic class"};
176 };
177
178 auto &options = Runtime::GetCurrent()->GetVerificationOptions().Debug.GetMethodOptions().GetOptions("default");
179 options.Msg(VerifierMessage::ConflictingClassDefinitions).IfNotHidden([&]() {
180 LOG_VERIFIER_CONFLICTING_CLASS_DEFINITIONS(LibCache::GetName(cachedClass), get_file(already_cached_class),
181 get_file(cachedClass));
182 });
183 }
184
185 } // namespace
186
187 template <>
MakeSyntheticClass(panda_file::SourceLang src_lang,const uint8_t * descriptor,panda_file::Type::TypeId type_id,uint32_t flags)188 LibCache::CachedClass &FastAPIClassRW::MakeSyntheticClass(panda_file::SourceLang src_lang, const uint8_t *descriptor,
189 panda_file::Type::TypeId type_id, uint32_t flags)
190 {
191 auto &data = GetContext(src_lang);
192
193 auto id = Class::CalcUniqId(descriptor);
194
195 auto emplace_result =
196 data.class_cache.try_emplace(id, id, descriptor, src_lang, type_id, GetClassFlags(flags),
197 OptionalConstRef<panda_file::File> {}, panda_file::File::EntityId {});
198 ASSERT(emplace_result.second);
199 CachedClass &cachedClass = emplace_result.first->second;
200
201 auto descr_emplace_result = data.descr_lookup.try_emplace(cachedClass.descriptor, std::ref(cachedClass));
202 if (descr_emplace_result.second) {
203 LOG(DEBUG, VERIFIER) << "CACHE: Added synthetic class " << GetName(cachedClass) << std::hex << " with id 0x"
204 << id;
205 } else {
206 // See the comment on the other `if (descr_emplace_result.second)`
207 LibCache::CachedClass &already_cached_class = descr_emplace_result.first->second.get();
208 LogConflictingClassDefinitions(cachedClass, already_cached_class);
209 // do not return already_cached_class, it might give wrong results in InitializeRoots
210 }
211
212 return cachedClass;
213 }
214
215 static LibCache::MethodHash CalcMethodHash(const LibCache::CachedMethod &cachedMethod);
216
217 template <>
218 template <typename SignatureFiller>
MakeSyntheticMethod(LibCache::CachedClass & cachedClass,const uint8_t * name,bool is_static,SignatureFiller sig_filler)219 LibCache::CachedMethod &FastAPIClassRW::MakeSyntheticMethod(LibCache::CachedClass &cachedClass, const uint8_t *name,
220 bool is_static, SignatureFiller sig_filler)
221 {
222 auto id = Method::CalcUniqId(cachedClass.descriptor, name);
223
224 static Indexes empty_index;
225
226 // TODO(romanov) ideally we want not to construct cachedMethod here but directly in the try_emplace call, like in
227 // MakeSyntheticClass. Currently done this way because id gets modified after construction, so we don't know the key
228 // to use for try_emplace
229 CachedMethod cachedMethod {id, 0, name, cachedClass, empty_index, {}, {}, {}};
230
231 sig_filler(cachedClass, cachedMethod);
232 cachedMethod.flags[CachedMethod::Flag::STATIC] = is_static;
233 cachedMethod.hash = CalcMethodHash(cachedMethod);
234 auto &data = GetContext(cachedClass.source_lang);
235 const auto MASK = 0xFFFFULL;
236 const auto SHIFT = 0x8ULL;
237 cachedMethod.id += ((cachedMethod.hash & MASK) << SHIFT) + cachedMethod.num_args;
238 auto &result = data.method_cache.try_emplace(cachedMethod.id, std::move(cachedMethod)).first->second;
239 cachedClass.methods.insert_or_assign(result.hash, std::ref(result));
240 LOG(DEBUG, VERIFIER) << "CACHE: Added synthetic method '" << GetName(result) << std::hex << " with id 0x"
241 << result.id << ", hash 0x" << result.hash;
242 return result;
243 }
244
245 template <>
AddArray(panda_file::SourceLang src_lang,const uint8_t * descr)246 LibCache::CachedClass &FastAPIClassRW::AddArray(panda_file::SourceLang src_lang, const uint8_t *descr)
247 {
248 auto &data = GetContext(src_lang);
249 auto &array =
250 MakeSyntheticClass(src_lang, descr, panda_file::Type::TypeId::REFERENCE, ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT);
251 array.flags[CachedClass::Flag::ARRAY_CLASS] = true;
252 array.ancestors.push_back(data.object_descr);
253 // add Clonable in ancestors for java
254 // add Serializeable in ancestors for java
255 auto comp_descr = DescriptorString(ClassHelper::GetComponentDescriptor(descr));
256 array.array_component = comp_descr;
257 if (comp_descr.GetLength() > 1) {
258 array.flags[CachedClass::Flag::OBJECT_ARRAY_CLASS] = true;
259 }
260 const uint8_t *ctor_name = panda::plugins::GetLanguageContextBase(src_lang)->GetCtorName();
261 CachedClassRefOrDescriptor i32_class {data.GetPrimitiveClass(panda_file::Type::TypeId::I32)};
262 for (size_t dimensions = 0; dimensions <= ClassHelper::GetDimensionality(descr); ++dimensions) {
263 auto &ctor = MakeSyntheticMethod(array, ctor_name, true, [&](CachedClass &c, CachedMethod &cm) {
264 cm.num_args = dimensions;
265 // method return type first
266 cm.signature.push_back(std::ref(c));
267 cm.signature.insert(cm.signature.end(), dimensions, i32_class);
268 });
269 ctor.flags[CachedMethod::Flag::ARRAY_CONSTRUCTOR] = true;
270 }
271 return array;
272 }
273
274 template <>
InitializeRootClasses(panda_file::SourceLang lang)275 void FastAPIClassRW::InitializeRootClasses(panda_file::SourceLang lang)
276 {
277 const LanguageContextBase *ctx = panda::plugins::GetLanguageContextBase(lang);
278 DescriptorString obj_descriptor = ctx->GetObjectClassDescriptor();
279
280 auto &data = GetContext(lang);
281
282 data.object_descr = obj_descriptor;
283 data.string_descr = ctx->GetStringClassDescriptor();
284 data.string_array_descr = ctx->GetStringArrayClassDescriptor();
285
286 VerificationInitAPI v_api = ctx->GetVerificationInitAPI();
287
288 // primitive
289 for (panda_file::Type::TypeId type_id : v_api.primitive_roots_for_verification) {
290 auto &C = MakeSyntheticClass(lang, ClassHelper::GetPrimitiveTypeDescriptorStr(type_id), type_id,
291 ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT);
292 C.flags[CachedClass::Flag::PRIMITIVE] = true;
293 data.primitive_classes[type_id] = C;
294 }
295
296 for (const uint8_t *el : v_api.array_elements_for_verification) {
297 AddArray(lang, el);
298 }
299
300 // object
301 if (v_api.is_need_object_synthetic_class) {
302 MakeSyntheticClass(lang, obj_descriptor, panda_file::Type::TypeId::REFERENCE,
303 ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT);
304 }
305
306 if (v_api.is_need_string_synthetic_class) {
307 auto &Str = MakeSyntheticClass(lang, data.string_descr, panda_file::Type::TypeId::REFERENCE,
308 ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT);
309 Str.ancestors.push_back(obj_descriptor);
310 }
311
312 if (v_api.is_need_class_synthetic_class) {
313 auto &Class = MakeSyntheticClass(lang, ctx->GetClassClassDescriptor(), panda_file::Type::TypeId::REFERENCE,
314 ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT);
315 Class.ancestors.push_back(obj_descriptor);
316 }
317 }
318
LibCache()319 LibCache::LibCache()
320 {
321 static bool initialized = false;
322 if (initialized) {
323 return;
324 }
325
326 FastAPIClassRW FAPI = FastAPI();
327 for (panda::panda_file::SourceLang lang : panda::panda_file::LANG_ITERATOR) {
328 FAPI.InitializeRootClasses(lang);
329 }
330
331 initialized = true;
332 }
333
334 template <typename Handler>
CalcMethodHash(const uint8_t * name,Handler && handler)335 static LibCache::MethodHash CalcMethodHash(const uint8_t *name, Handler &&handler)
336 {
337 uint64_t name_hash = StdHash(LibCache::DescriptorString {name});
338 uint64_t sig_hash = FNV_INITIAL_SEED;
339 auto hash_str = [&sig_hash](const LibCache::DescriptorString &descr) {
340 sig_hash = PseudoFnvHashItem(StdHash(descr), sig_hash);
341 };
342 handler(hash_str);
343 auto constexpr SHIFT = 32U;
344 return (name_hash << SHIFT) | sig_hash;
345 }
346
CalcMethodHash(const panda_file::File & pf,const panda_file::MethodDataAccessor & mda)347 static LibCache::MethodHash CalcMethodHash(const panda_file::File &pf, const panda_file::MethodDataAccessor &mda)
348 {
349 return CalcMethodHash(pf.GetStringData(mda.GetNameId()).data, [&](auto hash_str) {
350 if (mda.IsStatic()) {
351 hash_str(utf::CStringAsMutf8("static"));
352 }
353 const_cast<panda_file::MethodDataAccessor &>(mda).EnumerateTypesInProto(
354 [&](auto type, auto class_file_id) {
355 if (type.GetId() == panda_file::Type::TypeId::REFERENCE) {
356 auto string_data = pf.GetStringData(class_file_id).data;
357 hash_str(string_data);
358 } else {
359 hash_str(ClassHelper::GetPrimitiveTypeDescriptorStr(type.GetId()));
360 }
361 },
362 // skip 'this' parameter of method
363 true);
364 });
365 }
366
CalcMethodHash(const LibCache::CachedMethod & cachedMethod)367 static LibCache::MethodHash CalcMethodHash(const LibCache::CachedMethod &cachedMethod)
368 {
369 return CalcMethodHash(cachedMethod.name, [&](auto hash_str) {
370 bool is_static = cachedMethod.flags[LibCache::CachedMethod::Flag::STATIC];
371 if (is_static) {
372 hash_str(utf::CStringAsMutf8("static"));
373 }
374 size_t idx = 0;
375 for (const auto &arg : cachedMethod.signature) {
376 if (idx == 1) {
377 // skip 'this' parameter of method
378 ++idx;
379 continue;
380 }
381 hash_str(LibCache::GetDescriptor(arg));
382 if (!is_static) {
383 idx++;
384 }
385 }
386 });
387 }
388
CalcFieldNameAndTypeHash(const panda_file::File & pf,const panda_file::FieldDataAccessor & fda)389 static LibCache::FieldHash CalcFieldNameAndTypeHash(const panda_file::File &pf,
390 const panda_file::FieldDataAccessor &fda)
391 {
392 uint64_t name_hash = PseudoFnvHashString(pf.GetStringData(fda.GetNameId()).data);
393
394 auto type = panda_file::Type::GetTypeFromFieldEncoding(fda.GetType());
395
396 uint64_t type_hash;
397 if (type.GetId() != panda_file::Type::TypeId::REFERENCE) {
398 type_hash = PseudoFnvHashItem(ClassHelper::GetPrimitiveTypeDescriptorChar(type.GetId()));
399 } else {
400 auto type_class_id = panda_file::File::EntityId(fda.GetType());
401 const uint8_t *descr = pf.GetStringData(type_class_id).data;
402 type_hash = PseudoFnvHashString(descr);
403 }
404
405 auto constexpr SHIFT = 32U;
406
407 uint64_t hash = (name_hash << SHIFT) | type_hash;
408
409 return hash;
410 }
411
TryResolveMethod(LibCache::CachedClass * klass,LibCache::MethodHash method_hash)412 static OptionalRef<LibCache::CachedMethod> TryResolveMethod(LibCache::CachedClass *klass,
413 LibCache::MethodHash method_hash)
414 {
415 auto it = klass->methods.find(method_hash);
416 if (it == klass->methods.end()) {
417 // search in ancestors
418 for (auto &ancestor : klass->ancestors) {
419 ASSERT(LibCache::IsRef(ancestor));
420 auto method_ref = TryResolveMethod(&LibCache::GetRef(ancestor), method_hash);
421 if (method_ref.HasRef()) {
422 // place found method in topmost methods table
423 // to speedup future lookups
424 // Think about global cache: (class_id, method_hash) -> method_ref,
425 // it may be more effectife in terms of mem consumption, but less
426 // effective in terms of cpu d-cache locality
427 klass->methods.insert_or_assign(method_hash, *method_ref);
428 return method_ref;
429 }
430 }
431 return {};
432 }
433
434 return it->second.get();
435 }
436
ResolveMethod(LibCache::MethodHash method_hash)437 OptionalRef<LibCache::CachedMethod> LibCache::CachedClass::ResolveMethod(LibCache::MethodHash method_hash)
438 {
439 // assumption: ResolveMethod is called on linked class
440 ASSERT(linked);
441 auto method_ref = TryResolveMethod(this, method_hash);
442 if (!method_ref.HasRef()) {
443 LOG(WARNING, VERIFIER) << std::hex << "Failed to resolve method with hash 0x" << method_hash << " in class "
444 << GetName();
445 }
446
447 return method_ref;
448 }
449
TryResolveField(LibCache::CachedClass * klass,LibCache::FieldHash field_hash)450 static OptionalRef<LibCache::CachedField> TryResolveField(LibCache::CachedClass *klass, LibCache::FieldHash field_hash)
451 {
452 auto it = klass->fields.find(field_hash);
453 if (it == klass->fields.end()) {
454 // search in ancestors
455 for (auto &ancestor : klass->ancestors) {
456 ASSERT(LibCache::IsRef(ancestor));
457 auto field_ref = TryResolveField(&LibCache::GetRef(ancestor), field_hash);
458 if (field_ref.HasRef()) {
459 // see comment in ResolveMethod(MethodHash)
460 klass->fields.insert_or_assign(field_hash, *field_ref);
461 return field_ref;
462 }
463 }
464 return {};
465 }
466
467 return it->second.get();
468 }
469
ResolveField(LibCache::FieldHash field_hash)470 OptionalRef<LibCache::CachedField> LibCache::CachedClass::ResolveField(LibCache::FieldHash field_hash)
471 {
472 // assumption: ResolveField is called on linked class
473 ASSERT(linked);
474
475 auto field_ref = TryResolveField(this, field_hash);
476 if (!field_ref.HasRef()) {
477 LOG(WARNING, VERIFIER) << std::hex << "Failed to resolve field with hash 0x" << field_hash << " in class "
478 << GetName();
479 }
480
481 return field_ref;
482 }
483
InitializeIndex(LibCache::Indexes * indexes,LibCache::LangContext * data,const panda_file::File & pf,const panda_file::File::IndexHeader * header)484 static void InitializeIndex(LibCache::Indexes *indexes, LibCache::LangContext *data, const panda_file::File &pf,
485 const panda_file::File::IndexHeader *header)
486 {
487 auto map_span_to_vector = [](auto *vector, auto entity_id_span, auto func) {
488 // all vectors we pass must be empty
489 ASSERT(vector->empty());
490 vector->reserve(entity_id_span.Size());
491 for (auto id : entity_id_span) {
492 vector->emplace_back(func(id));
493 }
494 vector->shrink_to_fit();
495 };
496
497 // extracted to a variable to avoid a CodeCheck bug
498 auto id_to_ref_or_descriptor = [&](auto class_id) -> LibCache::CachedClassRefOrDescriptor {
499 auto type = panda_file::Type::GetTypeFromFieldEncoding(class_id.GetOffset());
500
501 if (type.IsReference()) {
502 return DescriptorString {pf.GetStringData(class_id).data};
503 }
504
505 return data->GetPrimitiveClass(type);
506 };
507
508 map_span_to_vector(&(indexes->classes), pf.GetClassIndex(header), id_to_ref_or_descriptor);
509 auto id_to_ref_or_id = [](auto entity_id) { return entity_id; };
510 map_span_to_vector(&(indexes->methods), pf.GetMethodIndex(header), id_to_ref_or_id);
511 map_span_to_vector(&(indexes->fields), pf.GetFieldIndex(header), id_to_ref_or_id);
512 }
513
InitializeSignature(LibCache::CachedMethod * cachedMethod,const panda_file::MethodDataAccessor & mda,LibCache::LangContext * data)514 static void InitializeSignature(LibCache::CachedMethod *cachedMethod, const panda_file::MethodDataAccessor &mda,
515 LibCache::LangContext *data)
516 {
517 auto &pf = cachedMethod->file.Get();
518
519 const_cast<panda_file::MethodDataAccessor &>(mda).EnumerateTypesInProto([&](auto type, auto class_file_id) {
520 auto type_id = type.GetId();
521 if (type_id == panda_file::Type::TypeId::REFERENCE) {
522 DescriptorString descr {pf.GetStringData(class_file_id).data};
523 cachedMethod->signature.push_back(descr);
524 } else {
525 cachedMethod->signature.push_back(data->GetPrimitiveClass(type_id));
526 }
527 });
528 }
529
InitializeTryBlock(const panda_file::CodeDataAccessor::TryBlock & try_block,LibCache::CachedMethod * cachedMethod)530 static bool InitializeTryBlock(const panda_file::CodeDataAccessor::TryBlock &try_block,
531 LibCache::CachedMethod *cachedMethod)
532 {
533 auto try_block_start = reinterpret_cast<const uint8_t *>(reinterpret_cast<uintptr_t>(cachedMethod->bytecode) +
534 static_cast<uintptr_t>(try_block.GetStartPc()));
535 auto try_block_end = reinterpret_cast<const uint8_t *>(reinterpret_cast<uintptr_t>(try_block_start) +
536 static_cast<uintptr_t>(try_block.GetLength()));
537 const_cast<panda_file::CodeDataAccessor::TryBlock &>(try_block).EnumerateCatchBlocks([&](const auto &catch_block) {
538 auto handler_pc_ptr = reinterpret_cast<const uint8_t *>(reinterpret_cast<uintptr_t>(cachedMethod->bytecode) +
539 static_cast<uintptr_t>(catch_block.GetHandlerPc()));
540 auto &cached_catch_block = cachedMethod->catch_blocks.emplace_back(LibCache::CachedCatchBlock {
541 try_block_start, try_block_end, LibCache::InvalidDescriptor, handler_pc_ptr, catch_block.GetCodeSize()});
542 auto type_idx = catch_block.GetTypeIdx();
543 if (type_idx != panda_file::INVALID_INDEX) {
544 auto &class_index = cachedMethod->indexes.classes;
545 if (type_idx < class_index.size()) {
546 cached_catch_block.exception_type = class_index[type_idx];
547 } else {
548 LOG(WARNING, VERIFIER) << "Exception type out of bounds in " << cachedMethod->GetName()
549 << " class index, index " << type_idx << ", size " << class_index.size();
550 }
551 }
552 return true;
553 });
554 return true;
555 }
556
InitializeCode(LibCache::CachedMethod * cachedMethod,const panda_file::MethodDataAccessor & mda)557 static void InitializeCode(LibCache::CachedMethod *cachedMethod, const panda_file::MethodDataAccessor &mda)
558 {
559 auto code_id = const_cast<panda_file::MethodDataAccessor &>(mda).GetCodeId();
560 if (!code_id) {
561 cachedMethod->num_vregs = 0;
562 cachedMethod->num_args = 0;
563 cachedMethod->bytecode = nullptr;
564 cachedMethod->bytecode_size = 0;
565 return;
566 }
567
568 auto &pf = cachedMethod->file.Get();
569 panda_file::CodeDataAccessor cda {pf, *code_id};
570 cachedMethod->num_vregs = cda.GetNumVregs();
571 cachedMethod->num_args = cda.GetNumArgs();
572 cachedMethod->bytecode = cda.GetInstructions();
573 cachedMethod->bytecode_size = cda.GetCodeSize();
574 cda.EnumerateTryBlocks([&](const auto &try_block) { return InitializeTryBlock(try_block, cachedMethod); });
575 cachedMethod->catch_blocks.shrink_to_fit();
576 }
577
578 template <>
ProcessMethod(LibCache::CachedClass * cachedClass,const panda_file::File & pf,const panda_file::MethodDataAccessor & mda)579 LibCache::CachedMethod &FastAPIClassRW::ProcessMethod(LibCache::CachedClass *cachedClass, const panda_file::File &pf,
580 const panda_file::MethodDataAccessor &mda)
581 {
582 auto file_id = mda.GetMethodId();
583
584 auto id = Method::CalcUniqId(&pf, file_id);
585
586 panda_file::SourceLang src_lang =
587 const_cast<panda_file::MethodDataAccessor &>(mda).GetSourceLang().value_or(cachedClass->source_lang);
588
589 auto &data = GetContext(src_lang);
590
591 auto cached_method_ref = GetFromCacheAndLink(&data.method_cache, id);
592
593 if (cached_method_ref.HasRef()) {
594 return *cached_method_ref;
595 }
596
597 const panda_file::File::IndexHeader *header = pf.GetIndexHeader(file_id);
598 auto [iter, is_new] = data.indexes_cache.try_emplace(header);
599 Indexes &indexes = iter->second;
600 if (is_new) {
601 InitializeIndex(&indexes, &data, pf, header);
602 }
603
604 auto emplace_result =
605 data.method_cache.try_emplace(id,
606 // CachedMethod constructor args
607 id, CalcMethodHash(pf, mda), pf.GetStringData(mda.GetNameId()).data, *cachedClass,
608 indexes, GetMethodFlags(pf, mda, src_lang), pf, file_id);
609 ASSERT(emplace_result.second);
610 CachedMethod &cachedMethod = emplace_result.first->second;
611
612 InitializeSignature(&cachedMethod, mda, &data);
613 InitializeCode(&cachedMethod, mda);
614
615 data.file_cache.AddToCache(pf, file_id, cachedMethod);
616
617 LOG(DEBUG, VERIFIER) << "CACHE: Added method '" << GetName(cachedMethod) << std::hex << "' with id 0x" << id
618 << ", hash 0x" << cachedMethod.hash;
619
620 return cachedMethod;
621 }
622
623 template <>
ProcessField(LibCache::CachedClass * cachedClass,const panda_file::File & pf,const panda_file::FieldDataAccessor & fda)624 LibCache::CachedField &FastAPIClassRW::ProcessField(LibCache::CachedClass *cachedClass, const panda_file::File &pf,
625 const panda_file::FieldDataAccessor &fda)
626 {
627 auto file_id = fda.GetFieldId();
628
629 auto id = Field::CalcUniqId(&pf, file_id);
630
631 auto src_lang = cachedClass->source_lang;
632
633 auto &data = GetContext(src_lang);
634
635 OptionalRef<CachedField> cached_field_ref = GetFromCacheAndLink(&data.field_cache, id);
636
637 if (cached_field_ref.HasRef()) {
638 return *cached_field_ref;
639 }
640
641 auto emplace_result = data.field_cache.try_emplace(id,
642 // CachedField constructor args
643 id, pf.GetStringData(fda.GetNameId()).data, *cachedClass,
644 GetFieldFlags(fda), pf, file_id);
645 ASSERT(emplace_result.second);
646 CachedField &cachedField = emplace_result.first->second;
647
648 auto type = panda_file::Type::GetTypeFromFieldEncoding(fda.GetType());
649
650 // NB! keep hashing in sync with CalcFieldNameAndTypeHash
651 uint64_t name_hash = PseudoFnvHashString(cachedField.name);
652
653 uint64_t type_hash;
654
655 if (type.GetId() != panda_file::Type::TypeId::REFERENCE) {
656 cachedField.type = data.GetPrimitiveClass(type);
657 type_hash = PseudoFnvHashItem(ClassHelper::GetPrimitiveTypeDescriptorChar(type.GetId()));
658 } else {
659 auto type_class_id = panda_file::File::EntityId(fda.GetType());
660 const uint8_t *descr = pf.GetStringData(type_class_id).data;
661 cachedField.type = DescriptorString(descr);
662 type_hash = PseudoFnvHashString(descr);
663 }
664
665 auto constexpr SHIFT = 32U;
666
667 cachedField.hash = (name_hash << SHIFT) | type_hash;
668
669 LOG(DEBUG, VERIFIER) << "CACHE: Added field '" << GetName(cachedField) << std::hex << "' with id 0x" << id
670 << ", hash 0x" << cachedField.hash;
671
672 data.file_cache.AddToCache(pf, file_id, cachedField);
673
674 return cachedField;
675 }
676
AddAncestors(LibCache::CachedClass * cachedClass,panda_file::ClassDataAccessor * cda,const LibCache::LangContext & data)677 static void AddAncestors(LibCache::CachedClass *cachedClass, panda_file::ClassDataAccessor *cda,
678 const LibCache::LangContext &data)
679 {
680 auto &pf = cda->GetPandaFile();
681
682 auto add_ancestor = [&](auto entity_id) {
683 DescriptorString descr =
684 entity_id.GetOffset() == 0 ? data.object_descr : DescriptorString {pf.GetStringData(entity_id).data};
685 if (descr != cachedClass->descriptor) {
686 cachedClass->ancestors.emplace_back(descr);
687 }
688 };
689
690 cda->EnumerateInterfaces(add_ancestor);
691
692 auto super_class_id = cda->GetSuperClassId();
693
694 add_ancestor(super_class_id);
695 }
696
697 template <>
ProcessClass(const panda_file::File & pf,panda_file::File::EntityId entity_id)698 void FastAPIClassRW::ProcessClass(const panda_file::File &pf, panda_file::File::EntityId entity_id)
699 {
700 auto id = Class::CalcUniqId(&pf, entity_id);
701
702 panda_file::ClassDataAccessor cda {pf, entity_id};
703
704 panda_file::SourceLang src_lang = cda.GetSourceLang().value_or(panda_file::SourceLang::PANDA_ASSEMBLY);
705
706 auto &data = GetContext(src_lang);
707
708 auto cached_class_ref = GetFromCacheAndLink(&data.class_cache, id);
709
710 if (cached_class_ref.HasRef()) {
711 return;
712 }
713
714 auto emplace_result =
715 data.class_cache.try_emplace(id, id, cda.GetDescriptor(), src_lang, panda_file::Type::TypeId::REFERENCE,
716 GetClassFlags(cda.GetAccessFlags()), pf, entity_id);
717 ASSERT(emplace_result.second);
718 CachedClass &cachedClass = emplace_result.first->second;
719
720 cachedClass.ancestors.reserve(cda.GetIfacesNumber() + 1);
721
722 AddAncestors(&cachedClass, &cda, data);
723
724 cachedClass.methods.reserve(cda.GetMethodsNumber());
725 cachedClass.fields.reserve(cda.GetFieldsNumber());
726
727 cda.EnumerateMethods([&](const panda_file::MethodDataAccessor &mda) {
728 if (LIKELY(!pf.IsExternal(mda.GetMethodId()))) {
729 auto &cachedMethod = ProcessMethod(&cachedClass, pf, mda);
730 cachedClass.methods.insert_or_assign(cachedMethod.hash, cachedMethod);
731 }
732 });
733
734 cda.EnumerateFields([&](const panda_file::FieldDataAccessor &fda) {
735 if (LIKELY(!pf.IsExternal(fda.GetFieldId()))) {
736 auto &cachedField = ProcessField(&cachedClass, pf, fda);
737 cachedClass.fields.insert_or_assign(cachedField.hash, cachedField);
738 }
739 });
740
741 data.file_cache.AddToCache(pf, entity_id, cachedClass);
742
743 auto descr_emplace_result = data.descr_lookup.try_emplace(cachedClass.descriptor, std::ref(cachedClass));
744 if (descr_emplace_result.second) {
745 LOG(DEBUG, VERIFIER) << "CACHE: Added class '" << GetName(cachedClass) << std::hex << "' with id 0x" << id;
746 } else {
747 // TODO(romanov) Not sure the new definition can't be accessed, which is desirable; to fix this we could
748 // add OptionalRef<CachedClass> first_definition to CachedClass, and make sure everything is looked up there
749 // first
750 LibCache::CachedClass &already_cached_class = descr_emplace_result.first->second.get();
751 LogConflictingClassDefinitions(cachedClass, already_cached_class);
752 }
753 }
754
755 template <>
ResolveByDescriptor(panda_file::SourceLang src_lang,const DescriptorString & descriptor,bool reportError)756 OptionalRef<LibCache::CachedClass> FastAPIClassRW::ResolveByDescriptor(panda_file::SourceLang src_lang,
757 const DescriptorString &descriptor,
758 bool reportError)
759 {
760 auto &data = GetContext(src_lang);
761
762 const auto it = data.descr_lookup.find(descriptor);
763 if (it != data.descr_lookup.cend()) {
764 LOG(DEBUG, VERIFIER) << "CACHE: Descriptor " << descriptor << " resolved for language " << src_lang;
765 return it->second.get();
766 }
767
768 // check if it is an array descr
769 if (ClassHelper::IsArrayDescriptor(descriptor)) {
770 return AddArray(src_lang, descriptor);
771 }
772
773 reportError
774 ? LOG(WARNING, VERIFIER)
775 : LOG(ERROR, VERIFIER) << "CACHE: Descriptor " << descriptor << " not resolved for language " << src_lang;
776 return {};
777 }
778
779 template <>
ResolveAndLink(panda_file::SourceLang src_lang,const DescriptorString & descriptor,bool reportError)780 OptionalRef<LibCache::CachedClass> FastAPIClassRW::ResolveAndLink(panda_file::SourceLang src_lang,
781 const DescriptorString &descriptor, bool reportError)
782 {
783 auto resolved = ResolveByDescriptor(src_lang, descriptor, reportError);
784 if (resolved.HasRef()) {
785 if (Link(resolved.AsPointer())) {
786 return *resolved;
787 }
788 }
789 return {};
790 }
791
792 template <>
793 bool FastAPIClassRW::ResolveAndLink(panda_file::SourceLang src_lang,
794 LibCache::CachedClassRefOrDescriptor *ref_or_descriptor, bool reportError);
795
796 template <>
Link(LibCache::CachedClass * cachedClass,bool reportError)797 bool FastAPIClassRW::Link(LibCache::CachedClass *cachedClass, bool reportError)
798 {
799 if (IsLinked(*cachedClass)) {
800 return true;
801 }
802
803 cachedClass->linked = true;
804 auto src_lang = cachedClass->source_lang;
805
806 for (auto &ancestor : cachedClass->ancestors) {
807 TryResolveAndLinkElement(cachedClass, src_lang, &ancestor, reportError);
808 }
809
810 if (cachedClass->flags[CachedClass::Flag::ARRAY_CLASS]) {
811 auto &array_component = cachedClass->array_component;
812 ASSERT(GetDescriptor(array_component).IsValid());
813 TryResolveAndLinkElement(cachedClass, src_lang, &array_component, reportError);
814 }
815
816 return IsLinked(*cachedClass);
817 }
818
819 template <>
Link(LibCache::CachedMethod * cachedMethod,bool reportError)820 bool FastAPIClassRW::Link(LibCache::CachedMethod *cachedMethod, bool reportError)
821 {
822 if (IsLinked(*cachedMethod)) {
823 return true;
824 }
825
826 auto &class_ref = cachedMethod->klass;
827 if (!Link(&class_ref)) {
828 return false;
829 }
830
831 auto src_lang = class_ref.source_lang;
832
833 cachedMethod->linked = true;
834
835 for (CachedClassRefOrDescriptor &arg : cachedMethod->signature) {
836 TryResolveAndLinkElement(cachedMethod, src_lang, &arg, reportError);
837 }
838
839 for (auto &catch_block : cachedMethod->catch_blocks) {
840 auto exc_type = catch_block.exception_type;
841 if (IsLinked(exc_type)) {
842 continue;
843 }
844 // the catch-all case, nothing to resolve
845 if (!IsValid(exc_type)) {
846 continue;
847 }
848 TryResolveAndLinkElement(cachedMethod, src_lang, &exc_type, reportError);
849 }
850
851 return IsLinked(*cachedMethod);
852 }
853
854 template <>
Link(LibCache::CachedField * cachedField,bool reportError)855 bool FastAPIClassRW::Link(LibCache::CachedField *cachedField, [[maybe_unused]] bool reportError)
856 {
857 if (IsLinked(*cachedField)) {
858 return true;
859 }
860
861 auto &class_ref = cachedField->klass;
862 if (!Link(&class_ref)) {
863 return false;
864 }
865
866 bool resolved = ResolveAndLink(class_ref.source_lang, &(cachedField->type));
867 if (resolved) {
868 cachedField->linked = true;
869 return true;
870 }
871
872 return false;
873 }
874
875 template <>
ResolveAndLink(panda_file::SourceLang src_lang,LibCache::CachedClassRefOrDescriptor * ref_or_descriptor,bool reportError)876 bool FastAPIClassRW::ResolveAndLink(panda_file::SourceLang src_lang,
877 LibCache::CachedClassRefOrDescriptor *ref_or_descriptor, bool reportError)
878 {
879 return Visit(
880 *ref_or_descriptor, [this](CachedClass &klass) { return Link(&klass); },
881 [&](const DescriptorString &descriptor) {
882 // logic here is similar to the other ResolveAndLink overload, but we don't combine them due to assigning
883 // *ref_or_descriptor even if it can't be linked
884 auto resolved = ResolveByDescriptor(src_lang, descriptor, reportError);
885 if (resolved.HasRef()) {
886 *ref_or_descriptor = *resolved;
887 return Link(resolved.AsPointer());
888 }
889 return false;
890 });
891 }
892
893 template <>
894 template <>
GetFromCache(const LibCache::CachedMethod & cachedMethod,panda_file::File::Index idx)895 OptionalConstRef<LibCache::CachedClass> FastAPIClassRW::GetFromCache<LibCache::CachedClass>(
896 const LibCache::CachedMethod &cachedMethod, panda_file::File::Index idx)
897 {
898 auto &index = cachedMethod.indexes.classes;
899 if (idx >= index.size()) {
900 LOG(WARNING, VERIFIER) << "Class index out of bounds for cached method " << cachedMethod.GetName() << ", index "
901 << idx << ", size " << index.size();
902 return {};
903 }
904
905 auto &item = index[idx];
906
907 if (ResolveAndLink(cachedMethod.GetSourceLang(), &item)) {
908 return GetRef(item);
909 }
910
911 return {};
912 }
913
914 template <>
ResolveMethod(const LibCache::CachedMethod & cachedMethod,panda_file::File::EntityId id)915 OptionalRef<LibCache::CachedMethod> FastAPIClassRW::ResolveMethod(const LibCache::CachedMethod &cachedMethod,
916 panda_file::File::EntityId id)
917 {
918 const panda_file::File &file = *cachedMethod.file;
919 panda_file::MethodDataAccessor mda {file, id};
920
921 DescriptorString descr {file.GetStringData(mda.GetClassId()).data};
922
923 auto src_lang = cachedMethod.GetSourceLang();
924
925 panda_file::SourceLang method_src_lang = mda.GetSourceLang().value_or(src_lang);
926
927 auto class_ref = ResolveAndLink(method_src_lang, descr);
928
929 if (!class_ref.HasRef()) {
930 LOG(WARNING, VERIFIER) << "Failed to resolve or link class by descriptor " << descr << " for language "
931 << method_src_lang;
932 return {};
933 }
934
935 auto &data = GetContext(src_lang);
936
937 MethodHash method_hash = CalcMethodHash(file, mda);
938
939 auto resolved_method = class_ref->ResolveMethod(method_hash);
940
941 if (!resolved_method.HasRef()) {
942 LOG(DEBUG, VERIFIER) << std::hex << "CACHE: cannot resolve method with hash 0x" << method_hash << " in "
943 << class_ref->GetName();
944 return {};
945 }
946
947 LOG(DEBUG, VERIFIER) << std::hex << "CACHE: method with hash 0x" << method_hash << " in " << class_ref->GetName()
948 << "found: " << resolved_method->GetName();
949
950 if (!Link(resolved_method.AsPointer())) {
951 LOG(WARNING, VERIFIER) << "Failed to link " << resolved_method->GetName();
952 return {};
953 }
954
955 data.file_cache.AddToCache(file, id, *resolved_method);
956 return resolved_method;
957 }
958
959 template <>
960 template <>
GetFromCache(const LibCache::CachedMethod & cachedMethod,panda_file::File::Index idx)961 OptionalConstRef<LibCache::CachedMethod> FastAPIClassRW::GetFromCache<LibCache::CachedMethod>(
962 const LibCache::CachedMethod &cachedMethod, panda_file::File::Index idx)
963 {
964 auto &index = cachedMethod.indexes.methods;
965 if (idx >= index.size()) {
966 LOG(WARNING, VERIFIER) << "Method index out of bounds for cached method " << cachedMethod.GetName()
967 << ", index " << idx << ", size " << index.size();
968 return {};
969 }
970 auto &item = index[idx];
971
972 using ReturnType = OptionalConstRef<CachedMethod>;
973 return Visit(
974 item, [](const CachedMethod &already_cached) -> ReturnType { return already_cached; },
975 [&](const panda_file::File::EntityId id) -> ReturnType {
976 auto src_lang = cachedMethod.GetSourceLang();
977 auto &data = GetContext(src_lang);
978 auto method_ref = data.file_cache.GetCached<CachedMethod>(*cachedMethod.file, id);
979
980 if (method_ref.HasRef()) {
981 if (!Link(method_ref.AsPointer())) {
982 return {};
983 }
984 item = *method_ref;
985 return method_ref;
986 }
987
988 auto res = ResolveMethod(cachedMethod, id);
989 if (res.HasRef()) {
990 item = res.AsWrapper();
991 }
992 return res;
993 });
994 }
995
996 template <>
997 template <>
GetFromCache(const LibCache::CachedMethod & cachedMethod,panda_file::File::Index idx)998 OptionalConstRef<LibCache::CachedField> FastAPIClassRW::GetFromCache<LibCache::CachedField>(
999 const LibCache::CachedMethod &cachedMethod, panda_file::File::Index idx)
1000 {
1001 auto &index = cachedMethod.indexes.fields;
1002 if (idx >= index.size()) {
1003 LOG(WARNING, VERIFIER) << "Field index out of bounds for cached method " << cachedMethod.GetName() << ", index "
1004 << idx << ", size " << index.size();
1005 return {};
1006 }
1007 auto &item = index[idx];
1008
1009 using ReturnType = OptionalConstRef<CachedField>;
1010 return Visit(
1011 item, [](const CachedField &already_cached) -> ReturnType { return already_cached; },
1012 [&](const panda_file::File::EntityId id) -> ReturnType {
1013 auto src_lang = cachedMethod.GetSourceLang();
1014 const panda_file::File &file = *cachedMethod.file;
1015
1016 auto &data = GetContext(src_lang);
1017
1018 auto field_ref = data.file_cache.GetCached<CachedField>(file, id);
1019
1020 if (field_ref.HasRef()) {
1021 if (!Link(field_ref.AsPointer())) {
1022 return {};
1023 }
1024 item = field_ref.AsWrapper();
1025 return field_ref;
1026 }
1027
1028 panda_file::FieldDataAccessor fda {file, id};
1029
1030 DescriptorString descr {file.GetStringData(fda.GetClassId()).data};
1031
1032 auto class_ref = ResolveAndLink(src_lang, descr);
1033
1034 if (!class_ref.HasRef()) {
1035 LOG(WARNING, VERIFIER) << "Failed to resolve or link class by descriptor " << descr << " for language "
1036 << src_lang;
1037 return {};
1038 }
1039
1040 FieldHash field_hash = CalcFieldNameAndTypeHash(file, fda);
1041
1042 auto resolved_field = class_ref->ResolveField(field_hash);
1043
1044 if (!resolved_field.HasRef()) {
1045 LOG(DEBUG, VERIFIER) << std::hex << "CACHE: cannot resolve field with hash 0x" << field_hash << " in "
1046 << class_ref->GetName();
1047 return {};
1048 }
1049
1050 LOG(DEBUG, VERIFIER) << std::hex << "CACHE: field with hash 0x" << field_hash << " in "
1051 << class_ref->GetName() << " found: " << resolved_field->GetName();
1052
1053 if (!Link(resolved_field.AsPointer())) {
1054 LOG(WARNING, VERIFIER) << "Failed to link " << resolved_field->GetName();
1055 return {};
1056 }
1057
1058 data.file_cache.AddToCache(file, id, *resolved_field);
1059 item = resolved_field.AsWrapper();
1060 return resolved_field;
1061 });
1062 }
1063
1064 template <>
GetStringClass(const LibCache::CachedMethod & cachedMethod)1065 OptionalConstRef<LibCache::CachedClass> FastAPIClassRW::GetStringClass(const LibCache::CachedMethod &cachedMethod)
1066 {
1067 panda_file::SourceLang src_lang = cachedMethod.GetSourceLang();
1068 auto descriptor = GetContext(src_lang).string_descr;
1069 return ResolveAndLink(src_lang, descriptor);
1070 }
1071
1072 template <>
GetStringArrayClass(panda_file::SourceLang src_lang)1073 OptionalConstRef<LibCache::CachedClass> FastAPIClassRW::GetStringArrayClass(panda_file::SourceLang src_lang)
1074 {
1075 auto descriptor = GetContext(src_lang).string_array_descr;
1076 return ResolveAndLink(src_lang, descriptor);
1077 }
1078
1079 template <>
ProcessFile(const panda_file::File & pf)1080 void FastAPIClassRW::ProcessFile(const panda_file::File &pf)
1081 {
1082 auto &processed_files = data_.GetObj().processed_files;
1083 auto [iter, emplaced] = processed_files.try_emplace(pf.GetUniqId(), &pf);
1084 if (!emplaced) {
1085 const std::string &old_path = iter->second->GetFullFileName();
1086 const std::string &new_path = pf.GetFullFileName();
1087
1088 if (old_path == new_path) {
1089 LOG(INFO, VERIFIER) << "CACHE: " << new_path << " is already processed, skipping";
1090 } else {
1091 LOG(ERROR, VERIFIER) << "CACHE: Two Panda files at '" << old_path << "' and '" << new_path
1092 << "' have the same filename/header hash, ignoring the second one";
1093 }
1094
1095 return;
1096 }
1097
1098 LOG(INFO, VERIFIER) << "CACHE: Processing " << pf.GetFullFileName();
1099 auto classes_indexes = pf.GetClasses();
1100 for (auto idx : classes_indexes) {
1101 panda_file::File::EntityId entity_id {idx};
1102 if (!pf.IsExternal(entity_id)) {
1103 ProcessClass(pf, entity_id);
1104 }
1105 }
1106 }
1107
1108 template <>
ProcessFiles(const PandaVector<const panda_file::File * > & pfs)1109 void FastAPIClassRW::ProcessFiles(const PandaVector<const panda_file::File *> &pfs)
1110 {
1111 for (const auto &pf : pfs) {
1112 ProcessFile(*pf);
1113 }
1114 }
1115
1116 } // namespace panda::verifier
1117