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 LIBPANDAFILE_FILE_ITEM_CONTAINER_H
17 #define LIBPANDAFILE_FILE_ITEM_CONTAINER_H
18 
19 #include "file_items.h"
20 #include "file_writer.h"
21 #include "pgo.h"
22 
23 #include <list>
24 #include <map>
25 #include <memory>
26 #include <set>
27 #include <string>
28 #include <unordered_map>
29 #include <unordered_set>
30 
31 namespace panda::panda_file {
32 
33 class ItemDeduper;
34 
35 class ItemContainer {
36 public:
37     explicit ItemContainer();
38     ~ItemContainer() = default;
39     NO_COPY_SEMANTIC(ItemContainer);
40     NO_MOVE_SEMANTIC(ItemContainer);
41 
42     StringItem *GetOrCreateStringItem(const std::string &str);
43 
44     LiteralArrayItem *GetOrCreateLiteralArrayItem(const std::string &id);
45 
46     ClassItem *GetOrCreateClassItem(const std::string &str);
47 
48     ForeignClassItem *GetOrCreateForeignClassItem(const std::string &str);
49 
50     ScalarValueItem *GetOrCreateIntegerValueItem(uint32_t v);
51 
52     ScalarValueItem *GetOrCreateLongValueItem(uint64_t v);
53 
54     ScalarValueItem *GetOrCreateFloatValueItem(float v);
55 
56     ScalarValueItem *GetOrCreateDoubleValueItem(double v);
57 
58     ScalarValueItem *GetOrCreateIdValueItem(BaseItem *v);
59 
GetOrCreateGlobalClassItem()60     ClassItem *GetOrCreateGlobalClassItem()
61     {
62         return GetOrCreateClassItem("L_GLOBAL;");
63     }
64 
65     ProtoItem *GetOrCreateProtoItem(TypeItem *ret_type, const std::vector<MethodParamItem> &params);
66 
67     PrimitiveTypeItem *GetOrCreatePrimitiveTypeItem(Type type);
68 
69     PrimitiveTypeItem *GetOrCreatePrimitiveTypeItem(Type::TypeId type);
70 
71     LineNumberProgramItem *CreateLineNumberProgramItem();
72 
73     template <class T, class... Args>
CreateItem(Args &&.... args)74     T *CreateItem(Args &&... args)
75     {
76         static_assert(!std::is_same_v<T, StringItem>, "Use GetOrCreateStringItem to create StringItem");
77         static_assert(!std::is_same_v<T, ClassItem>, "Use GetOrCreateClassItem to create ClassItem");
78         static_assert(!std::is_same_v<T, ForeignClassItem>,
79                       "Use GetOrCreateForeignClassItem to create ForeignClassItem");
80         static_assert(!std::is_same_v<T, ValueItem>, "Use GetOrCreateValueItem functions to create ValueItem");
81         static_assert(!std::is_same_v<T, ProtoItem>, "Use GetOrCreateProtoItem to create ValueItem");
82         static_assert(!std::is_same_v<T, LineNumberProgramItem>,
83                       "Use CreateLineNumberProgramItem to create LineNumberProgramItem");
84         static_assert(!std::is_same_v<T, PrimitiveTypeItem>,
85                       "Use GetOrCreatePrimitiveTypeItem to create PrimitiveTypeItem");
86         static_assert(!std::is_same_v<T, MethodItem>, "Use ClassItem instance to create MethodItem");
87         static_assert(!std::is_same_v<T, FieldItem>, "Use ClassItem instance to create FieldItem");
88 
89         std::unique_ptr<T> ptr = nullptr;
90         if constexpr (std::is_same_v<T, ForeignFieldItem> || std::is_same_v<T, ForeignMethodItem> ||
91             std::is_same_v<T, ScalarValueItem> || std::is_same_v<T, ArrayValueItem> ||
92             std::is_same_v<T, LiteralArrayItem>) {
93             ptr = std::make_unique<T>(std::forward<Args>(args)..., this);
94         } else {
95             ptr = std::make_unique<T>(std::forward<Args>(args)...);
96         }
97 
98         auto ret = ptr.get();
99         if (ptr->IsForeign()) {
100             foreign_items_.emplace_back(std::move(ptr));
101         } else {
102             items_.insert(GetInsertPosition<T>(), std::move(ptr));
103         }
104         return ret;
105     }
106 
107     void ReLayout();
108     uint32_t ComputeLayout();
109     bool Write(Writer *writer, bool deduplicateItems = true);
110 
111     std::map<std::string, size_t> GetStat();
112 
113     void DumpItemsStat(std::ostream &os) const;
114 
CalculateRoundUpSize(uint32_t before, uint32_t after)115     uint32_t CalculateRoundUpSize(uint32_t before, uint32_t after)
116     {
117         return after - before;
118     }
119 
GetStringMap()120     std::unordered_map<std::string, StringItem *> *GetStringMap()
121     {
122         return &string_map_;
123     }
124 
GetLiteralArrayItem(const std::string &key)125     LiteralArrayItem *GetLiteralArrayItem(const std::string &key)
126     {
127         return literalarray_map_.at(key);
128     }
129 
GetClassMap()130     std::map<std::string, BaseClassItem *> *GetClassMap()
131     {
132         return &class_map_;
133     }
134 
GetIntValueMap()135     std::unordered_map<uint32_t, ValueItem *> *GetIntValueMap()
136     {
137         return &int_value_map_;
138     }
139 
GetLongValueMap()140     std::unordered_map<uint64_t, ValueItem *> *GetLongValueMap()
141     {
142         return &long_value_map_;
143     }
144 
GetFloatValueMap()145     std::unordered_map<uint32_t, ValueItem *> *GetFloatValueMap()
146     {
147         return &float_value_map_;
148     }
149 
GetDoubleValueMap()150     std::unordered_map<uint64_t, ValueItem *> *GetDoubleValueMap()
151     {
152         return &double_value_map_;
153     }
154 
GetScalarValueMap()155     std::unordered_map<BaseItem *, ValueItem *> *GetScalarValueMap()
156     {
157         return &id_value_map_;
158     }
159 
GetProtoItem(TypeItem *retType, const std::vector<MethodParamItem> &params)160     ProtoItem *GetProtoItem(TypeItem *retType, const std::vector<MethodParamItem> &params)
161     {
162         return proto_map_.at(ProtoKey {retType, params});
163     }
164 
GetPrimitiveTypeMap()165     std::unordered_map<Type::TypeId, PrimitiveTypeItem *> *GetPrimitiveTypeMap()
166     {
167         return &primitive_type_map_;
168     }
169 
GetItems()170     std::list<std::unique_ptr<BaseItem>> &GetItems()
171     {
172         return items_;
173     }
174 
GetItems() const175     const std::list<std::unique_ptr<BaseItem>> &GetItems() const
176     {
177         return items_;
178     }
179 
GetForeigtems()180     const std::vector<std::unique_ptr<BaseItem>> &GetForeigtems()
181     {
182         return foreign_items_;
183     }
184 
GetEndItem()185     BaseItem *GetEndItem()
186     {
187         return end_;
188     }
189 
190     void ReorderItems(panda::panda_file::pgo::ProfileOptimizer *profile_opt);
191 
192     void DeduplicateItems(bool computeLayout = true);
193 
194     void DeduplicateCodeAndDebugInfo();
195 
196     void DeduplicateAnnotations();
197 
198     void DeduplicateLineNumberProgram(DebugInfoItem *item, ItemDeduper *deduper);
199 
200     void DeduplicateDebugInfo(MethodItem *method, ItemDeduper *debug_info_deduper,
201                               ItemDeduper *line_number_program_deduper);
202 
GetIndexedItemCount() const203     size_t GetIndexedItemCount() const
204     {
205         return indexed_item_count_;
206     }
207 
IncIndexedItemCount()208     void IncIndexedItemCount()
209     {
210         indexed_item_count_++;
211     }
212 
SetApi(uint8_t api)213     static void SetApi(uint8_t api)
214     {
215         ItemContainer::apiVersion = api;
216     }
217 
SetSubApi(std::string subApi)218     static void SetSubApi(std::string subApi)
219     {
220         ItemContainer::subApiVersion = subApi;
221     }
222 
GetSubApi()223     static std::string GetSubApi()
224     {
225         return ItemContainer::subApiVersion;
226     }
227 
GetApi()228     static uint8_t GetApi()
229     {
230         return ItemContainer::apiVersion;
231     }
232 
233     static uint8_t apiVersion;
234     static std::string subApiVersion;
235 
236 private:
237     template <class T>
GetInsertPosition()238     auto GetInsertPosition()
239     {
240         if (std::is_same_v<T, CodeItem>) {
241             return code_items_end_;
242         }
243 
244         if (std::is_same_v<T, DebugInfoItem>) {
245             return debug_items_end_;
246         }
247 
248         return items_end_;
249     }
250 
251     class IndexItem : public BaseItem {
252     public:
IndexItem(IndexType type, size_t max_index)253         IndexItem(IndexType type, size_t max_index) : type_(type), max_index_(max_index)
254         {
255             ASSERT(type_ != IndexType::NONE);
256 
257             const auto bc_version = GetVersionByApi(ItemContainer::GetApi(), ItemContainer::GetSubApi());
258             if (bc_version.value().front() >= API_12 && (type == IndexType::FIELD || type == IndexType::PROTO)) {
259                 SetNeedsEmit(false);
260             }
261         }
262 
263         ~IndexItem() override = default;
264 
265         DEFAULT_COPY_SEMANTIC(IndexItem);
266         NO_MOVE_SEMANTIC(IndexItem);
267 
268         size_t Alignment() override
269         {
270             return sizeof(uint32_t);
271         }
272 
273         bool Write(Writer *writer) override;
274 
275         ItemTypes GetItemType() const override;
276 
277         bool Add(IndexedItem *item);
278 
Has(IndexedItem *item) const279         bool Has(IndexedItem *item) const
280         {
281             auto res = index_.find(item);
282             return res != index_.cend();
283         }
284 
Remove(IndexedItem *item)285         void Remove(IndexedItem *item)
286         {
287             index_.erase(item);
288         }
289 
GetNumItems() const290         size_t GetNumItems() const
291         {
292             return index_.size();
293         }
294 
UpdateItems(BaseItem *start, BaseItem *end)295         void UpdateItems(BaseItem *start, BaseItem *end)
296         {
297             size_t i = 0;
298             for (auto *item : index_) {
299                 item->SetIndex(start, end, i++);
300             }
301         }
302 
Reset()303         void Reset()
304         {
305             for (auto *item : index_) {
306                 item->ClearIndexes();
307             }
308         }
309 
310     protected:
311         size_t CalculateSize() const override
312         {
313             if (NeedsEmit()) {
314                 return index_.size() * ID_SIZE;
315             }
316             return 0;
317         }
318 
319     private:
320         struct Comparator {
321             bool operator()(IndexedItem *item1, IndexedItem *item2) const noexcept
322             {
323                 auto index_type = item1->GetIndexType();
324                 if (index_type == IndexType::CLASS) {
325                     auto type_item1 = static_cast<TypeItem *>(item1);
326                     auto type_item2 = static_cast<TypeItem *>(item2);
327                     auto type_id1 = static_cast<size_t>(type_item1->GetType().GetId());
328                     auto type_id2 = static_cast<size_t>(type_item2->GetType().GetId());
329                     if (type_id1 != type_id2) {
330                         return type_id1 < type_id2;
331                     }
332                 }
333 
334                 if (index_type == IndexType::LINE_NUMBER_PROG) {
335                     auto ref_count1 = item1->GetRefCount();
336                     auto ref_count2 = item2->GetRefCount();
337                     if (ref_count1 != ref_count2) {
338                         return ref_count1 > ref_count2;
339                     }
340                 }
341 
342                 return item1->GetIndexedItemCount() < item2->GetIndexedItemCount();
343             }
344         };
345 
346         IndexType type_;
347         size_t max_index_;
348         std::set<IndexedItem *, Comparator> index_;
349     };
350 
351     class LineNumberProgramIndexItem : public IndexItem {
352     public:
LineNumberProgramIndexItem()353         LineNumberProgramIndexItem() : IndexItem(IndexType::LINE_NUMBER_PROG, MAX_INDEX_32) {}
354         ~LineNumberProgramIndexItem() override = default;
355         DEFAULT_COPY_SEMANTIC(LineNumberProgramIndexItem);
356         NO_MOVE_SEMANTIC(LineNumberProgramIndexItem);
357 
IncRefCount(LineNumberProgramItem *item)358         void IncRefCount(LineNumberProgramItem *item)
359         {
360             ASSERT(Has(item));
361             Remove(item);
362             item->IncRefCount();
363             Add(item);
364         }
365 
DecRefCount(LineNumberProgramItem *item)366         void DecRefCount(LineNumberProgramItem *item)
367         {
368             ASSERT(Has(item));
369             Remove(item);
370             item->DecRefCount();
371             Add(item);
372         }
373     };
374 
375     class IndexHeaderItem : public BaseItem {
376     public:
IndexHeaderItem(std::vector<IndexItem *> indexes)377         explicit IndexHeaderItem(std::vector<IndexItem *> indexes) : indexes_(std::move(indexes))
378         {
379             ASSERT(indexes_.size() == INDEX_COUNT_16);
380         }
381 
382         ~IndexHeaderItem() override = default;
383 
384         DEFAULT_COPY_SEMANTIC(IndexHeaderItem);
385         NO_MOVE_SEMANTIC(IndexHeaderItem);
386 
387         size_t Alignment() override
388         {
389             return ID_SIZE;
390         }
391 
392         bool Write(Writer *writer) override;
393 
394         ItemTypes GetItemType() const override
395         {
396             return ItemTypes::INDEX_HEADER;
397         }
398 
399         bool Add(const std::list<IndexedItem *> &items);
400 
401         void Remove(const std::list<IndexedItem *> &items);
402 
SetStart(BaseItem *item)403         void SetStart(BaseItem *item)
404         {
405             start_ = item;
406         }
407 
SetEnd(BaseItem *item)408         void SetEnd(BaseItem *item)
409         {
410             end_ = item;
411         }
412 
UpdateItems()413         void UpdateItems()
414         {
415             for (auto *index : indexes_) {
416                 index->UpdateItems(start_, end_);
417             }
418         }
419 
420     protected:
421         size_t CalculateSize() const override
422         {
423             return sizeof(File::IndexHeader);
424         }
425 
426     private:
IndexGetIndexByType(IndexType type) const427         IndexItem *IndexGetIndexByType(IndexType type) const
428         {
429             auto i = static_cast<size_t>(type);
430             return indexes_[i];
431         }
432 
433         BaseItem *start_ {nullptr};
434         BaseItem *end_ {nullptr};
435         std::vector<IndexItem *> indexes_;
436     };
437 
438     class IndexSectionItem : public BaseItem {
439     public:
440         size_t Alignment() override
441         {
442             return ID_SIZE;
443         }
444 
445         bool Write(Writer *writer) override;
446 
447         ItemTypes GetItemType() const override
448         {
449             return ItemTypes::INDEX_SECTION;
450         }
451 
Reset()452         void Reset()
453         {
454             headers_.clear();
455 
456             for (auto &index : indexes_) {
457                 index.Reset();
458             }
459 
460             indexes_.clear();
461         }
462 
463         void AddHeader();
464 
GetCurrentHeader()465         IndexHeaderItem *GetCurrentHeader()
466         {
467             return &headers_.back();
468         }
469 
IsEmpty() const470         bool IsEmpty() const
471         {
472             return headers_.empty();
473         }
474 
GetNumHeaders() const475         size_t GetNumHeaders() const
476         {
477             return headers_.size();
478         }
479 
480         void ComputeLayout() override;
481 
UpdateItems()482         void UpdateItems()
483         {
484             for (auto &header : headers_) {
485                 header.UpdateItems();
486             }
487         }
488 
489     protected:
490         size_t CalculateSize() const override;
491 
492     private:
493         std::list<IndexHeaderItem> headers_;
494         std::list<IndexItem> indexes_;
495     };
496 
497     class ProtoKey {
498     public:
499         ProtoKey(TypeItem *ret_type, const std::vector<MethodParamItem> &params);
500 
501         ~ProtoKey() = default;
502 
503         DEFAULT_COPY_SEMANTIC(ProtoKey);
504         NO_MOVE_SEMANTIC(ProtoKey);
505 
GetHash() const506         size_t GetHash() const
507         {
508             return hash_;
509         }
510 
operator ==(const ProtoKey &key) const511         bool operator==(const ProtoKey &key) const
512         {
513             return shorty_ == key.shorty_ && ref_types_ == key.ref_types_;
514         }
515 
516     private:
517         void Add(TypeItem *item);
518 
519         size_t hash_;
520         std::string shorty_;
521         std::vector<TypeItem *> ref_types_;
522     };
523 
524     struct ProtoKeyHash {
525         size_t operator()(const ProtoKey &key) const noexcept
526         {
527             return key.GetHash();
528         };
529     };
530 
531     class EndItem : public BaseItem {
532     public:
EndItem()533         EndItem()
534         {
535             SetNeedsEmit(false);
536         }
537 
538         ~EndItem() override = default;
539 
540         DEFAULT_COPY_SEMANTIC(EndItem);
541         NO_MOVE_SEMANTIC(EndItem);
542 
543         size_t CalculateSize() const override
544         {
545             return 0;
546         }
547 
548         bool Write([[maybe_unused]] Writer *writer) override
549         {
550             return true;
551         }
552 
553         ItemTypes GetItemType() const override
554         {
555             return ItemTypes::END_ITEM;
556         }
557     };
558 
559     bool WriteHeader(Writer *writer, ssize_t *checksum_offset);
560 
561     bool WriteItems(Writer *writer);
562 
563     bool WriteHeaderIndexInfo(Writer *writer);
564 
565     void RebuildIndexSection();
566 
567     void RebuildLineNumberProgramIndex();
568 
569     void UpdateOrderIndexes();
570 
571     void AddIndexDependecies(BaseItem *item);
572 
573     void ProcessIndexDependecies(BaseItem *item);
574 
575     size_t GetForeignOffset() const;
576 
577     size_t GetForeignSize() const;
578 
579     std::unordered_map<std::string, StringItem *> string_map_;
580     std::unordered_map<std::string, LiteralArrayItem *> literalarray_map_;
581 
582     std::map<std::string, BaseClassItem *> class_map_;
583 
584     std::unordered_map<uint32_t, ValueItem *> int_value_map_;
585     std::unordered_map<uint64_t, ValueItem *> long_value_map_;
586     // NB! For f32 and f64 value maps we use integral keys
587     // (in fact, bit patterns of corresponding values) to
588     // workaround 0.0 == -0.0 semantics.
589     std::unordered_map<uint32_t, ValueItem *> float_value_map_;
590     std::unordered_map<uint64_t, ValueItem *> double_value_map_;
591     std::unordered_map<BaseItem *, ValueItem *> id_value_map_;
592     std::unordered_map<ProtoKey, ProtoItem *, ProtoKeyHash> proto_map_;
593     std::unordered_map<Type::TypeId, PrimitiveTypeItem *> primitive_type_map_;
594 
595     std::list<std::unique_ptr<BaseItem>> items_;
596 
597     std::vector<std::unique_ptr<BaseItem>> foreign_items_;
598 
599     IndexSectionItem index_section_item_;
600 
601     LineNumberProgramIndexItem line_number_program_index_item_;
602 
603     std::list<std::unique_ptr<BaseItem>>::iterator items_end_;
604     std::list<std::unique_ptr<BaseItem>>::iterator code_items_end_;
605     std::list<std::unique_ptr<BaseItem>>::iterator debug_items_end_;
606 
607     // Get Roundup Alignment Size
608     std::unordered_map<std::string, size_t> items_round_up_size_;
609     uint32_t foreign_item_roundup_size_ {0};
610     uint32_t line_number_item_roundup_size_ {0};
611 
612     BaseItem *end_;
613     size_t indexed_item_count_ {0};
614 };
615 
616 }  // namespace panda::panda_file
617 
618 #endif  // LIBPANDAFILE_FILE_ITEM_CONTAINER_H
619