/** * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LIBPANDAFILE_FILE_ITEM_CONTAINER_H #define LIBPANDAFILE_FILE_ITEM_CONTAINER_H #include "file_items.h" #include "file_writer.h" #include "pgo.h" #include #include #include #include #include #include #include namespace panda::panda_file { class ItemDeduper; class ItemContainer { public: ItemContainer(); ~ItemContainer() = default; NO_COPY_SEMANTIC(ItemContainer); NO_MOVE_SEMANTIC(ItemContainer); StringItem *GetOrCreateStringItem(const std::string &str); LiteralArrayItem *GetOrCreateLiteralArrayItem(const std::string &id); ClassItem *GetOrCreateClassItem(const std::string &str); ForeignClassItem *GetOrCreateForeignClassItem(const std::string &str); ScalarValueItem *GetOrCreateIntegerValueItem(uint32_t v); ScalarValueItem *GetOrCreateLongValueItem(uint64_t v); ScalarValueItem *GetOrCreateFloatValueItem(float v); ScalarValueItem *GetOrCreateDoubleValueItem(double v); ScalarValueItem *GetOrCreateIdValueItem(BaseItem *v); ClassItem *GetOrCreateGlobalClassItem() { return GetOrCreateClassItem("L_GLOBAL;"); } ProtoItem *GetOrCreateProtoItem(TypeItem *ret_type, const std::vector ¶ms); PrimitiveTypeItem *GetOrCreatePrimitiveTypeItem(Type type); PrimitiveTypeItem *GetOrCreatePrimitiveTypeItem(Type::TypeId type); LineNumberProgramItem *CreateLineNumberProgramItem(); template T *CreateItem(Args &&... args) { static_assert(!std::is_same_v, "Use GetOrCreateStringItem to create StringItem"); static_assert(!std::is_same_v, "Use GetOrCreateClassItem to create ClassItem"); static_assert(!std::is_same_v, "Use GetOrCreateForeignClassItem to create ForeignClassItem"); static_assert(!std::is_same_v, "Use GetOrCreateValueItem functions to create ValueItem"); static_assert(!std::is_same_v, "Use GetOrCreateProtoItem to create ValueItem"); static_assert(!std::is_same_v, "Use CreateLineNumberProgramItem to create LineNumberProgramItem"); static_assert(!std::is_same_v, "Use GetOrCreatePrimitiveTypeItem to create PrimitiveTypeItem"); static_assert(!std::is_same_v, "Use ClassItem instance to create MethodItem"); static_assert(!std::is_same_v, "Use ClassItem instance to create FieldItem"); std::unique_ptr ptr = nullptr; if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) { ptr = std::make_unique(std::forward(args)..., this); } else { ptr = std::make_unique(std::forward(args)...); } auto ret = ptr.get(); if (ptr->IsForeign()) { foreign_items_.emplace_back(std::move(ptr)); } else { items_.insert(GetInsertPosition(), std::move(ptr)); } return ret; } uint32_t ComputeLayout(); bool Write(Writer *writer, bool deduplicateItems = true); std::map GetStat(); void DumpItemsStat(std::ostream &os) const; std::unordered_map *GetStringMap() { return &string_map_; } LiteralArrayItem *GetLiteralArrayItem(const std::string &key) { return literalarray_map_.at(key); } std::map *GetClassMap() { return &class_map_; } std::unordered_map *GetIntValueMap() { return &int_value_map_; } std::unordered_map *GetLongValueMap() { return &long_value_map_; } std::unordered_map *GetFloatValueMap() { return &float_value_map_; } std::unordered_map *GetDoubleValueMap() { return &double_value_map_; } std::unordered_map *GetScalarValueMap() { return &id_value_map_; } ProtoItem *GetProtoItem(TypeItem *retType, const std::vector ¶ms) { return proto_map_.at(ProtoKey {retType, params}); } std::unordered_map *GetPrimitiveTypeMap() { return &primitive_type_map_; } std::list> &GetItems() { return items_; } const std::list> &GetItems() const { return items_; } const std::vector> &GetForeigtems() { return foreign_items_; } BaseItem *GetEndItem() { return end_; } void ReorderItems(panda::panda_file::pgo::ProfileOptimizer *profile_opt); void DeduplicateItems(bool computeLayout = true); void DeduplicateCodeAndDebugInfo(); void DeduplicateAnnotations(); void DeduplicateLineNumberProgram(DebugInfoItem *item, ItemDeduper *deduper); void DeduplicateDebugInfo(MethodItem *method, ItemDeduper *debug_info_deduper, ItemDeduper *line_number_program_deduper); size_t GetIndexedItemCount() const { return indexed_item_count_; } void IncIndexedItemCount() { indexed_item_count_++; } private: template auto GetInsertPosition() { if (std::is_same_v) { return code_items_end_; } if (std::is_same_v) { return debug_items_end_; } return items_end_; } class IndexItem : public BaseItem { public: IndexItem(IndexType type, size_t max_index) : type_(type), max_index_(max_index) { ASSERT(type_ != IndexType::NONE); } ~IndexItem() override = default; DEFAULT_COPY_SEMANTIC(IndexItem); NO_MOVE_SEMANTIC(IndexItem); size_t Alignment() override { return sizeof(uint32_t); } bool Write(Writer *writer) override; ItemTypes GetItemType() const override; bool Add(IndexedItem *item); bool Has(IndexedItem *item) const { auto res = index_.find(item); return res != index_.cend(); } void Remove(IndexedItem *item) { index_.erase(item); } size_t GetNumItems() const { return index_.size(); } void UpdateItems(BaseItem *start, BaseItem *end) { size_t i = 0; for (auto *item : index_) { item->SetIndex(start, end, i++); } } void Reset() { for (auto *item : index_) { item->ClearIndexes(); } } protected: size_t CalculateSize() const override { return index_.size() * ID_SIZE; } private: struct Comparator { bool operator()(IndexedItem *item1, IndexedItem *item2) const noexcept { auto index_type = item1->GetIndexType(); if (index_type == IndexType::CLASS) { auto type_item1 = static_cast(item1); auto type_item2 = static_cast(item2); auto type_id1 = static_cast(type_item1->GetType().GetId()); auto type_id2 = static_cast(type_item2->GetType().GetId()); if (type_id1 != type_id2) { return type_id1 < type_id2; } } if (index_type == IndexType::LINE_NUMBER_PROG) { auto ref_count1 = item1->GetRefCount(); auto ref_count2 = item2->GetRefCount(); if (ref_count1 != ref_count2) { return ref_count1 > ref_count2; } } return item1->GetIndexedItemCount() < item2->GetIndexedItemCount(); } }; IndexType type_; size_t max_index_; std::set index_; }; class LineNumberProgramIndexItem : public IndexItem { public: LineNumberProgramIndexItem() : IndexItem(IndexType::LINE_NUMBER_PROG, MAX_INDEX_32) {} ~LineNumberProgramIndexItem() override = default; DEFAULT_COPY_SEMANTIC(LineNumberProgramIndexItem); NO_MOVE_SEMANTIC(LineNumberProgramIndexItem); void IncRefCount(LineNumberProgramItem *item) { ASSERT(Has(item)); Remove(item); item->IncRefCount(); Add(item); } void DecRefCount(LineNumberProgramItem *item) { ASSERT(Has(item)); Remove(item); item->DecRefCount(); Add(item); } }; class IndexHeaderItem : public BaseItem { public: explicit IndexHeaderItem(std::vector indexes) : indexes_(std::move(indexes)) { ASSERT(indexes_.size() == INDEX_COUNT_16); } ~IndexHeaderItem() override = default; DEFAULT_COPY_SEMANTIC(IndexHeaderItem); NO_MOVE_SEMANTIC(IndexHeaderItem); size_t Alignment() override { return ID_SIZE; } bool Write(Writer *writer) override; ItemTypes GetItemType() const override { return ItemTypes::INDEX_HEADER; } bool Add(const std::list &items); void Remove(const std::list &items); void SetStart(BaseItem *item) { start_ = item; } void SetEnd(BaseItem *item) { end_ = item; } void UpdateItems() { for (auto *index : indexes_) { index->UpdateItems(start_, end_); } } protected: size_t CalculateSize() const override { return sizeof(File::IndexHeader); } private: IndexItem *IndexGetIndexByType(IndexType type) const { auto i = static_cast(type); return indexes_[i]; } BaseItem *start_ {nullptr}; BaseItem *end_ {nullptr}; std::vector indexes_; }; class IndexSectionItem : public BaseItem { public: size_t Alignment() override { return ID_SIZE; } bool Write(Writer *writer) override; ItemTypes GetItemType() const override { return ItemTypes::INDEX_SECTION; } void Reset() { headers_.clear(); for (auto &index : indexes_) { index.Reset(); } indexes_.clear(); } void AddHeader(); IndexHeaderItem *GetCurrentHeader() { return &headers_.back(); } bool IsEmpty() const { return headers_.empty(); } size_t GetNumHeaders() const { return headers_.size(); } void ComputeLayout() override; void UpdateItems() { for (auto &header : headers_) { header.UpdateItems(); } } protected: size_t CalculateSize() const override; private: std::list headers_; std::list indexes_; }; class ProtoKey { public: ProtoKey(TypeItem *ret_type, const std::vector ¶ms); ~ProtoKey() = default; DEFAULT_COPY_SEMANTIC(ProtoKey); NO_MOVE_SEMANTIC(ProtoKey); size_t GetHash() const { return hash_; } bool operator==(const ProtoKey &key) const { return shorty_ == key.shorty_ && ref_types_ == key.ref_types_; } private: void Add(TypeItem *item); size_t hash_; std::string shorty_; std::vector ref_types_; }; struct ProtoKeyHash { size_t operator()(const ProtoKey &key) const noexcept { return key.GetHash(); }; }; class EndItem : public BaseItem { public: EndItem() { SetNeedsEmit(false); } ~EndItem() override = default; DEFAULT_COPY_SEMANTIC(EndItem); NO_MOVE_SEMANTIC(EndItem); size_t CalculateSize() const override { return 0; } bool Write([[maybe_unused]] Writer *writer) override { return true; } ItemTypes GetItemType() const override { return ItemTypes::END_ITEM; } }; bool WriteHeader(Writer *writer, ssize_t *checksum_offset); bool WriteHeaderIndexInfo(Writer *writer); void RebuildIndexSection(); void RebuildLineNumberProgramIndex(); void UpdateOrderIndexes(); void AddIndexDependecies(BaseItem *item); void ProcessIndexDependecies(BaseItem *item); size_t GetForeignOffset() const; size_t GetForeignSize() const; std::unordered_map string_map_; std::unordered_map literalarray_map_; std::map class_map_; std::unordered_map int_value_map_; std::unordered_map long_value_map_; // NB! For f32 and f64 value maps we use integral keys // (in fact, bit patterns of corresponding values) to // workaround 0.0 == -0.0 semantics. std::unordered_map float_value_map_; std::unordered_map double_value_map_; std::unordered_map id_value_map_; std::unordered_map proto_map_; std::unordered_map primitive_type_map_; std::list> items_; std::vector> foreign_items_; IndexSectionItem index_section_item_; LineNumberProgramIndexItem line_number_program_index_item_; std::list>::iterator items_end_; std::list>::iterator code_items_end_; std::list>::iterator debug_items_end_; BaseItem *end_; size_t indexed_item_count_ {0}; }; } // namespace panda::panda_file #endif // LIBPANDAFILE_FILE_ITEM_CONTAINER_H