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