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