• 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 #ifndef PANDA_VERIFIER_JOB_QUEUE_CACHE_H_
17 #define PANDA_VERIFIER_JOB_QUEUE_CACHE_H_
18 
19 #include "verification/cache/file_entity_cache.h"
20 #include "verification/util/flags.h"
21 #include "verification/util/synchronized.h"
22 #include "verification/util/descriptor_string.h"
23 #include "verification/util/enum_array.h"
24 #include "verification/util/optional_ref.h"
25 
26 #include "runtime/core/core_language_context.h"
27 #include "runtime/include/mem/panda_containers.h"
28 #include "runtime/include/mem/panda_string.h"
29 #include "runtime/include/language_context.h"
30 
31 #include "libpandafile/file_items.h"
32 #include "libpandafile/file.h"
33 #include "libpandafile/method_data_accessor.h"
34 #include "libpandafile/field_data_accessor.h"
35 
36 #include "libpandabase/utils/hash.h"
37 
38 #include <functional>
39 #include <cstdint>
40 #include <variant>
41 #include <optional>
42 
43 namespace panda::verifier {
44 class LibCache {
45 public:
46     using Id = uint64_t;
47 
48     struct LangContext;
49 
50     struct CachedClass;
51     struct CachedMethod;
52     struct CachedField;
53 
54     using CachedClassRef = std::reference_wrapper<CachedClass>;
55     using CachedMethodRef = std::reference_wrapper<CachedMethod>;
56     using CachedFieldRef = std::reference_wrapper<CachedField>;
57 
58     using DescriptorString = panda::verifier::DescriptorString<mode::ExactCmp>;
59 
60     using CachedClassRefOrDescriptor = std::variant<CachedClassRef, DescriptorString>;
61     using CachedMethodRefOrEntityId = std::variant<CachedMethodRef, panda_file::File::EntityId>;
62     using CachedFieldRefOrEntityId = std::variant<CachedFieldRef, panda_file::File::EntityId>;
63 
64     /**
65      * @tparam CachedEntity CachedClass, CachedMethod, or CachedField
66      * @tparam DescriptorOrEntityId DescriptorString or EntityId (depends on CachedEntity)
67      * @tparam RefLambda ReturnType(const CachedEntity &)
68      * @tparam DescriptorOrEntityIdLambda `ReturnType(const DescriptorString &)` or `ReturnType(EntityId)`
69      */
70     template <typename CachedEntity, typename DescriptorOrEntityId, typename RefLambda,
71               typename DescriptorOrEntityIdLambda>
Visit(const std::variant<std::reference_wrapper<CachedEntity>,DescriptorOrEntityId> & item,RefLambda ref_lambda,DescriptorOrEntityIdLambda descr_or_id_lambda)72     static auto Visit(const std::variant<std::reference_wrapper<CachedEntity>, DescriptorOrEntityId> &item,
73                       RefLambda ref_lambda, DescriptorOrEntityIdLambda descr_or_id_lambda)
74     {
75         if (std::holds_alternative<std::reference_wrapper<CachedEntity>>(item)) {
76             return ref_lambda(std::get<std::reference_wrapper<CachedEntity>>(item).get());
77         } else {
78             return descr_or_id_lambda(std::get<DescriptorOrEntityId>(item));
79         }
80     }
81 
82     template <typename CachedEntity, typename DescriptorOrEntityId>
IsRef(const std::variant<std::reference_wrapper<CachedEntity>,DescriptorOrEntityId> & item)83     static bool IsRef(const std::variant<std::reference_wrapper<CachedEntity>, DescriptorOrEntityId> &item)
84     {
85         return std::holds_alternative<std::reference_wrapper<CachedEntity>>(item);
86     }
87 
88     template <typename CachedEntity, typename DescriptorOrEntityId>
GetRef(const std::variant<std::reference_wrapper<CachedEntity>,DescriptorOrEntityId> & item)89     static CachedEntity &GetRef(const std::variant<std::reference_wrapper<CachedEntity>, DescriptorOrEntityId> &item)
90     {
91         ASSERT(IsRef(item));
92         return std::get<std::reference_wrapper<CachedEntity>>(item).get();
93     }
94 
95     // prevent accidental calls where e.g. `const CachedClass &` is implicitly converted to `CachedClassRefOrDescriptor`
96     template <typename T>
97     static T &GetRef(const T &) = delete;
98 
GetDescriptor(const CachedClassRefOrDescriptor & item)99     static const DescriptorString &GetDescriptor(const CachedClassRefOrDescriptor &item)
100     {
101         std::reference_wrapper<const DescriptorString> descriptor_ref = Visit(
102             item, [](const CachedClass &klass) { return std::cref(klass.descriptor); },
103             [](const DescriptorString &descriptor) { return std::cref(descriptor); });
104         return descriptor_ref.get();
105     }
106 
107     inline static const CachedClassRefOrDescriptor InvalidDescriptor = DescriptorString {};
IsValid(const CachedClassRefOrDescriptor & item)108     static bool IsValid(const CachedClassRefOrDescriptor &item)
109     {
110         return Visit(
111             item, []([[maybe_unused]] const CachedClass &klass) { return true; },
112             [](const DescriptorString &descriptor) { return descriptor.IsValid(); });
113     }
114 
GetName(const CachedClassRefOrDescriptor & item)115     static PandaString GetName(const CachedClassRefOrDescriptor &item)
116     {
117         return Visit(
118             item, [](const CachedClass &klass) { return GetName(klass); },
119             [](const DescriptorString &descriptor) { return GetName(descriptor); });
120     }
121 
122     template <typename CachedEntity>
IsLinked(std::reference_wrapper<CachedEntity> item)123     static bool IsLinked(std::reference_wrapper<CachedEntity> item)
124     {
125         return item.get().linked;
126     }
127 
128     template <typename CachedEntity, typename DescriptorOrEntityId>
IsLinked(const std::variant<std::reference_wrapper<CachedEntity>,DescriptorOrEntityId> & item)129     static bool IsLinked(const std::variant<std::reference_wrapper<CachedEntity>, DescriptorOrEntityId> &item)
130     {
131         return IsRef(item) && IsLinked(GetRef(item));
132     }
133 
134     template <typename CachedEntity>
IsLinked(const CachedEntity & item)135     static bool IsLinked(const CachedEntity &item)
136     {
137         return item.linked;
138     }
139 
140     using MethodHash = uint64_t;
141     using FieldHash = uint64_t;
142 
143     using PrimitiveClassesArray =
144         EnumArray<OptionalRef<CachedClass>, panda_file::Type::TypeId, panda_file::Type::TypeId::VOID,
145                   panda_file::Type::TypeId::U1, panda_file::Type::TypeId::U8, panda_file::Type::TypeId::I8,
146                   panda_file::Type::TypeId::U16, panda_file::Type::TypeId::I16, panda_file::Type::TypeId::U32,
147                   panda_file::Type::TypeId::I32, panda_file::Type::TypeId::U64, panda_file::Type::TypeId::I64,
148                   panda_file::Type::TypeId::F32, panda_file::Type::TypeId::F64, panda_file::Type::TypeId::TAGGED>;
149 
150     static PandaString GetName(const CachedClass &);
151     static PandaString GetName(const CachedMethod &);
152     static PandaString GetName(const CachedField &);
153     static PandaString GetName(const DescriptorString &);
154 
155     template <typename T>
GetName(OptionalConstRef<T> item)156     static PandaString GetName(OptionalConstRef<T> item)
157     {
158         if (item.HasRef()) {
159             return GetName(item.Get());
160         }
161 
162         return "NOT_IN_CACHE";
163     }
164 
165     struct CachedClass {
166         enum class Flag {
167             DYNAMIC_CLASS,
168             PUBLIC,
169             FINAL,
170             ANNOTATION,
171             ENUM,
172             ARRAY_CLASS,
173             OBJECT_ARRAY_CLASS,
174             STRING_CLASS,
175             VARIABLESIZE,
176             PRIMITIVE,
177             ABSTRACT,
178             INTERFACE,
179             INSTANTIABLE,
180             OBJECT_CLASS,
181             CLASS_CLASS,
182             PROXY,
183             SUPER,
184             SYNTHETIC
185         };
186         using FlagsValue =
187             FlagsForEnum<unsigned int, Flag, Flag::DYNAMIC_CLASS, Flag::PUBLIC, Flag::FINAL, Flag::ANNOTATION,
188                          Flag::ENUM, Flag::ARRAY_CLASS, Flag::OBJECT_ARRAY_CLASS, Flag::STRING_CLASS,
189                          Flag::VARIABLESIZE, Flag::PRIMITIVE, Flag::ABSTRACT, Flag::INTERFACE, Flag::INSTANTIABLE,
190                          Flag::OBJECT_CLASS, Flag::CLASS_CLASS, Flag::PROXY, Flag::SUPER, Flag::SYNTHETIC>;
191         Id id;
192         DescriptorString descriptor;
193         panda_file::SourceLang source_lang;
194         panda_file::Type::TypeId type_id;
195         PandaVector<CachedClassRefOrDescriptor> ancestors;
196         // special case: invalid descriptor indicates this class is not an array
197         CachedClassRefOrDescriptor array_component;
198         FlagsValue flags;
199         PandaUnorderedMap<MethodHash, CachedMethodRef> methods;
200         PandaUnorderedMap<FieldHash, CachedFieldRef> fields;
201         bool linked;
202         OptionalConstRef<panda_file::File> file;
203         panda_file::File::EntityId file_id;
204 
CachedClassCachedClass205         CachedClass(Id id_, DescriptorString descriptor_, panda_file::SourceLang source_lang_,
206                     panda_file::Type::TypeId type_id_, FlagsValue flags_, OptionalConstRef<panda_file::File> file_,
207                     panda_file::File::EntityId file_id_)
208             : id {id_},
209               descriptor {descriptor_},
210               source_lang {source_lang_},
211               type_id {type_id_},
212               ancestors {},
213               array_component {InvalidDescriptor},
214               flags {flags_},
215               methods {},
216               fields {},
217               linked {false},
218               file {file_},
219               file_id {file_id_}
220         {
221         }
222 
223         // should only be accessed from inside the cache, so there won't be inconsistent copies
224         NO_COPY_SEMANTIC(CachedClass);
225 
GetNameCachedClass226         PandaString GetName() const
227         {
228             return LibCache::GetName(*this);
229         }
230 
GetArrayComponentCachedClass231         OptionalConstRef<CachedClass> GetArrayComponent() const
232         {
233             return Visit(
234                 array_component, [](const CachedClass &klass) { return OptionalConstRef {klass}; },
235                 []([[maybe_unused]] const DescriptorString &component_descriptor) {
236                     ASSERT(!component_descriptor.IsValid() &&
237                            "GetArrayComponent should never be called with unresolved descriptor");
238                     return OptionalConstRef<CachedClass> {};
239                 });
240         }
241 
IsSamePackageCachedClass242         bool IsSamePackage(const CachedClass &that_class) const
243         {
244             // get and compare packet names from the fully qualified class names
245             auto this_name = GetName();
246             auto that_name = that_class.GetName();
247             auto this_name_packet_length = this_name.find_last_of('.');
248             auto that_name_packet_length = that_name.find_last_of('.');
249             if (this_name_packet_length == std::string::npos) {
250                 return that_name_packet_length == std::string::npos;
251             }
252             if (this_name_packet_length != that_name_packet_length) {
253                 return false;
254             }
255             return this_name.compare(0, this_name_packet_length, that_name, 0, this_name_packet_length) == 0;
256         }
257 
258         OptionalRef<CachedMethod> ResolveMethod(MethodHash);
259         OptionalRef<CachedField> ResolveField(FieldHash);
260     };
261 
262     struct CachedCatchBlock {
263         const uint8_t *try_block_start;
264         const uint8_t *try_block_end;
265         // special case: invalid descriptor indicates catch_all section
266         CachedClassRefOrDescriptor exception_type;
267         const uint8_t *handler_bytecode;
268         size_t handler_bytecode_size;
269     };
270 
271     struct Indexes {
272         PandaVector<CachedClassRefOrDescriptor> classes;
273         PandaVector<CachedMethodRefOrEntityId> methods;
274         PandaVector<CachedFieldRefOrEntityId> fields;
275     };
276 
277     struct CachedMethod {
278         enum class Flag {
279             STATIC,
280             PUBLIC,
281             PRIVATE,
282             PROTECTED,
283             NATIVE,
284             INTRINSIC,
285             SYNTHETIC,
286             ABSTRACT,
287             FINAL,
288             SYNCHRONIZED,
289             HAS_SINGLE_IMPLEMENTATION,
290             DEFAULT_INTERFACE_METHOD,
291             CONSTRUCTOR,
292             INSTANCE_CONSTRUCTOR,
293             STATIC_CONSTRUCTOR,
294             ARRAY_CONSTRUCTOR
295         };
296         using FlagsValue =
297             FlagsForEnum<unsigned int, Flag, Flag::STATIC, Flag::PUBLIC, Flag::PRIVATE, Flag::PROTECTED, Flag::NATIVE,
298                          Flag::INTRINSIC, Flag::SYNTHETIC, Flag::ABSTRACT, Flag::FINAL, Flag::SYNCHRONIZED,
299                          Flag::HAS_SINGLE_IMPLEMENTATION, Flag::DEFAULT_INTERFACE_METHOD, Flag::CONSTRUCTOR,
300                          Flag::INSTANCE_CONSTRUCTOR, Flag::STATIC_CONSTRUCTOR, Flag::ARRAY_CONSTRUCTOR>;
301         Id id;
302         MethodHash hash;
303         DescriptorString name;
304         CachedClass &klass;
305         PandaVector<CachedClassRefOrDescriptor> signature;
306         PandaVector<CachedCatchBlock> catch_blocks;
307         Indexes &indexes;
308         size_t num_vregs;
309         size_t num_args;
310         FlagsValue flags;
311         const uint8_t *bytecode;
312         size_t bytecode_size;
313         bool linked;
314         /*
315         Keep here extended verification result in debug mode:
316         in case of verification problems, save bitmap of instructions
317         that were successfully verified with contexts on beginnings of
318         unverified blocks to debug them
319         */
320         OptionalConstRef<panda_file::File> file;
321         panda_file::File::EntityId file_id;
322 
CachedMethodCachedMethod323         CachedMethod(Id id_, MethodHash hash_, DescriptorString name_, CachedClass &klass_, Indexes &indexes_,
324                      FlagsValue flags_, OptionalConstRef<panda_file::File> file_, panda_file::File::EntityId file_id_)
325             : id {id_},
326               hash {hash_},
327               name {name_},
328               klass {klass_},
329               signature {},
330               catch_blocks {},
331               indexes {indexes_},
332               num_vregs {0},
333               num_args {0},
334               flags {flags_},
335               bytecode {nullptr},
336               bytecode_size {0},
337               linked {false},
338               file {file_},
339               file_id {file_id_}
340         {
341         }
342 
343         NO_COPY_SEMANTIC(CachedMethod);
344         CachedMethod(CachedMethod &&) = default;
345         CachedMethod &operator=(CachedMethod &&) = default;
346 
GetNameCachedMethod347         PandaString GetName() const
348         {
349             return LibCache::GetName(*this);
350         }
351 
IsStaticCachedMethod352         bool IsStatic() const
353         {
354             return flags[Flag::STATIC];
355         }
356 
GetSourceLangCachedMethod357         panda_file::SourceLang GetSourceLang() const
358         {
359             return klass.source_lang;
360         }
361     };
362 
363     struct CachedField {
364         enum class Flag { PUBLIC, PRIVATE, PROTECTED, STATIC, VOLATILE, FINAL };
365         using FlagsValue = FlagsForEnum<unsigned int, Flag, Flag::PUBLIC, Flag::PRIVATE, Flag::PROTECTED, Flag::STATIC,
366                                         Flag::VOLATILE, Flag::FINAL>;
367         Id id;
368         FieldHash hash;
369         DescriptorString name;
370         CachedClass &klass;
371         CachedClassRefOrDescriptor type;
372         FlagsValue flags;
373         bool linked;
374         const panda_file::File &file;
375         panda_file::File::EntityId file_id;
376 
CachedFieldCachedField377         CachedField(Id id_, DescriptorString name_, CachedClass &klass_, FlagsValue flags_,
378                     const panda_file::File &file_, panda_file::File::EntityId file_id_)
379             : id {id_},
380               hash {},
381               name {name_},
382               klass {klass_},
383               type {InvalidDescriptor},
384               flags {flags_},
385               linked {false},
386               file {file_},
387               file_id {file_id_}
388         {
389         }
390 
GetNameCachedField391         PandaString GetName() const
392         {
393             return LibCache::GetName(*this);
394         }
395 
GetTypeCachedField396         const CachedClass &GetType() const
397         {
398             ASSERT(IsRef(type));
399             return GetRef(type);
400         }
401 
402         NO_COPY_SEMANTIC(CachedField);
403     };
404 
405     LibCache();
406     LibCache(const LibCache &) = delete;
407     LibCache(LibCache &&) = delete;
408     LibCache &operator=(const LibCache &) = delete;
409     LibCache &operator=(LibCache &&) = delete;
410     ~LibCache() = default;
411 
412     using ClassCache = PandaUnorderedMap<Id, CachedClass>;
413     using MethodCache = PandaUnorderedMap<Id, CachedMethod>;
414     using FieldCache = PandaUnorderedMap<Id, CachedField>;
415     using DescriptorLookup = PandaUnorderedMap<DescriptorString, CachedClassRef>;
416     using FileCache = FileEntityCache<CachedClass, CachedMethod, CachedField>;
417 
418     struct LangContext {
419         ClassCache class_cache;
420         MethodCache method_cache;
421         FieldCache field_cache;
422         PrimitiveClassesArray primitive_classes;
423         DescriptorLookup descr_lookup;
424         FileCache file_cache;
425         PandaUnorderedMap<const panda_file::File::IndexHeader *, Indexes> indexes_cache;
426         DescriptorString string_descr;
427         DescriptorString object_descr;
428         DescriptorString string_array_descr;
429 
GetPrimitiveClassLangContext430         CachedClass &GetPrimitiveClass(panda_file::Type::TypeId type_id)
431         {
432             return primitive_classes[type_id].Get();
433         }
434 
GetPrimitiveClassLangContext435         CachedClass &GetPrimitiveClass(panda_file::Type type)
436         {
437             return GetPrimitiveClass(type.GetId());
438         }
439     };
440 
441     struct Data {
442         EnumArray<LangContext, panda_file::SourceLang, LANG_ENUM_LIST> contexts;
443         PandaUnorderedMap<uint32_t, const panda_file::File *> processed_files;
444     };
445 
446     enum class Access { READ_ONLY, READ_WRITE };
447 
448     template <Access Mode>
449     class FastAPIClass;
450 
451     using SyncData = Synchronized<Data, FastAPIClass<Access::READ_ONLY>, FastAPIClass<Access::READ_WRITE>>;
452 
453     template <Access Mode>
454     class FastAPIClass {
455         SyncData &data_;
FastAPIClass(SyncData & data)456         explicit FastAPIClass(SyncData &data) : data_ {data}
457         {
458             if constexpr (Mode == Access::READ_ONLY) {
459                 data_.ReadLock();
460             } else {
461                 data_.WriteLock();
462             }
463         }
464 
465         friend class LibCache;
466 
467     public:
~FastAPIClass()468         ~FastAPIClass()
469         {
470             data_.Unlock();
471         }
472 
GetContext(panda_file::SourceLang src_lang)473         const LangContext &GetContext(panda_file::SourceLang src_lang) const
474         {
475             return data_.GetObj().contexts[src_lang];
476         }
GetContext(panda_file::SourceLang src_lang)477         LangContext &GetContext(panda_file::SourceLang src_lang)
478         {
479             return data_.GetObj().contexts[src_lang];
480         }
481 
482         // signatures for convenience, see job_fill_gen.h.erb
483         OptionalConstRef<CachedClass> GetStringClass(const CachedMethod &cachedMethod);
484         OptionalConstRef<CachedClass> GetStringArrayClass(panda_file::SourceLang src_lang);
485 
486         void ProcessFile(const panda_file::File &pf);
487 
488         // TODO(romanov) convenience API used as ProcessFiles(class_linker.GetBootPandaFiles()), may replace argument
489         // e.g. with `const PandaVector<panda_file::File> &` when ClassLinker dependency is removed
490         void ProcessFiles(const PandaVector<const panda_file::File *> &pfs);
491 
492         OptionalRef<CachedMethod> GetMethod(panda_file::SourceLang src_lang, Id id, bool reportError = false);
493 
494         template <typename CachedEntity>
495         OptionalConstRef<CachedEntity> GetFromCache(const CachedMethod &cachedMethod, panda_file::File::Index idx);
496 
497         OptionalRef<CachedClass> ResolveAndLink(panda_file::SourceLang src_lang, const DescriptorString &descriptor,
498                                                 bool reportError = false);
499 
500     private:
501         CachedClass &MakeSyntheticClass(panda_file::SourceLang src_lang, const uint8_t *descriptor,
502                                         panda_file::Type::TypeId type_id, uint32_t flags);
503 
504         /**
505          * @tparam SignatureFiller  void(CachedClass &, CachedMethod &)
506          */
507         template <typename SignatureFiller>
508         CachedMethod &MakeSyntheticMethod(CachedClass &cachedClass, const uint8_t *name, bool is_static,
509                                           SignatureFiller sig_filler);
510 
511         CachedClass &AddArray(panda_file::SourceLang src_lang, const uint8_t *descr);
512 
513         void InitializeRootClasses(panda_file::SourceLang src_lang);
514 
515         bool Link(CachedClass *cachedClass, bool reportError = false);
516         bool Link(CachedMethod *cachedMethod, bool reportError = false);
517         bool Link(CachedField *cachedField, bool reportError = false);
518 
519         template <typename CachedEntity>
520         OptionalRef<CachedEntity> GetFromCacheAndLink(PandaUnorderedMap<Id, CachedEntity> *cache, Id id,
521                                                       bool reportError = false)
522         {
523             auto it = cache->find(id);
524             if (it != cache->end() && Link(&(it->second), reportError)) {
525                 return it->second;
526             }
527             return {};
528         }
529 
530         OptionalRef<CachedClass> ResolveByDescriptor(panda_file::SourceLang src_lang,
531                                                      const DescriptorString &descriptor, bool reportError = false);
532 
533         bool ResolveAndLink(panda_file::SourceLang src_lang, CachedClassRefOrDescriptor *ref_or_descriptor,
534                             bool reportError = false);
535 
536         void ProcessClass(const panda_file::File &pf, panda_file::File::EntityId entity_id);
537         CachedMethod &ProcessMethod(CachedClass *cachedClass, const panda_file::File &pf,
538                                     const panda_file::MethodDataAccessor &mda);
539         CachedField &ProcessField(CachedClass *cachedClass, const panda_file::File &pf,
540                                   const panda_file::FieldDataAccessor &fda);
541 
542         template <typename CachedEntity>
543         void TryResolveAndLinkElement(CachedEntity *entity, panda_file::SourceLang src_lang,
544                                       LibCache::CachedClassRefOrDescriptor *ref_or_descriptor, bool reportError = false)
545         {
546             if (!ResolveAndLink(src_lang, ref_or_descriptor, reportError)) {
547                 entity->linked = false;
548             }
549         }
550 
551         OptionalRef<CachedMethod> ResolveMethod(const CachedMethod &, panda_file::File::EntityId id);
552     };
553 
FastAPI()554     FastAPIClass<Access::READ_WRITE> FastAPI()
555     {
556         return {data_};
557     }
558 
FastAPI()559     const FastAPIClass<Access::READ_ONLY> FastAPI() const
560     {
561         return {const_cast<SyncData &>(data_)};
562     }
563 
564 private:
565     SyncData data_;
566 };
567 
568 using CachedClass = LibCache::CachedClass;
569 using CachedMethod = LibCache::CachedMethod;
570 using CachedField = LibCache::CachedField;
571 
572 }  // namespace panda::verifier
573 
574 #endif  // !PANDA_VERIFIER_JOB_QUEUE_CACHE_H_
575