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
31namespace panda::panda_file {
32
33class ItemDeduper;
34
35class ItemContainer {
36public:
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
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>
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
115    uint32_t CalculateRoundUpSize(uint32_t before, uint32_t after)
116    {
117        return after - before;
118    }
119
120    std::unordered_map<std::string, StringItem *> *GetStringMap()
121    {
122        return &string_map_;
123    }
124
125    LiteralArrayItem *GetLiteralArrayItem(const std::string &key)
126    {
127        return literalarray_map_.at(key);
128    }
129
130    std::map<std::string, BaseClassItem *> *GetClassMap()
131    {
132        return &class_map_;
133    }
134
135    std::unordered_map<uint32_t, ValueItem *> *GetIntValueMap()
136    {
137        return &int_value_map_;
138    }
139
140    std::unordered_map<uint64_t, ValueItem *> *GetLongValueMap()
141    {
142        return &long_value_map_;
143    }
144
145    std::unordered_map<uint32_t, ValueItem *> *GetFloatValueMap()
146    {
147        return &float_value_map_;
148    }
149
150    std::unordered_map<uint64_t, ValueItem *> *GetDoubleValueMap()
151    {
152        return &double_value_map_;
153    }
154
155    std::unordered_map<BaseItem *, ValueItem *> *GetScalarValueMap()
156    {
157        return &id_value_map_;
158    }
159
160    ProtoItem *GetProtoItem(TypeItem *retType, const std::vector<MethodParamItem> &params)
161    {
162        return proto_map_.at(ProtoKey {retType, params});
163    }
164
165    std::unordered_map<Type::TypeId, PrimitiveTypeItem *> *GetPrimitiveTypeMap()
166    {
167        return &primitive_type_map_;
168    }
169
170    std::list<std::unique_ptr<BaseItem>> &GetItems()
171    {
172        return items_;
173    }
174
175    const std::list<std::unique_ptr<BaseItem>> &GetItems() const
176    {
177        return items_;
178    }
179
180    const std::vector<std::unique_ptr<BaseItem>> &GetForeigtems()
181    {
182        return foreign_items_;
183    }
184
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
203    size_t GetIndexedItemCount() const
204    {
205        return indexed_item_count_;
206    }
207
208    void IncIndexedItemCount()
209    {
210        indexed_item_count_++;
211    }
212
213    static void SetApi(uint8_t api)
214    {
215        ItemContainer::apiVersion = api;
216    }
217
218    static void SetSubApi(std::string subApi)
219    {
220        ItemContainer::subApiVersion = subApi;
221    }
222
223    static std::string GetSubApi()
224    {
225        return ItemContainer::subApiVersion;
226    }
227
228    static uint8_t GetApi()
229    {
230        return ItemContainer::apiVersion;
231    }
232
233    static uint8_t apiVersion;
234    static std::string subApiVersion;
235
236private:
237    template <class T>
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:
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
279        bool Has(IndexedItem *item) const
280        {
281            auto res = index_.find(item);
282            return res != index_.cend();
283        }
284
285        void Remove(IndexedItem *item)
286        {
287            index_.erase(item);
288        }
289
290        size_t GetNumItems() const
291        {
292            return index_.size();
293        }
294
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
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:
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
358        void IncRefCount(LineNumberProgramItem *item)
359        {
360            ASSERT(Has(item));
361            Remove(item);
362            item->IncRefCount();
363            Add(item);
364        }
365
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:
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
403        void SetStart(BaseItem *item)
404        {
405            start_ = item;
406        }
407
408        void SetEnd(BaseItem *item)
409        {
410            end_ = item;
411        }
412
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:
427        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
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
465        IndexHeaderItem *GetCurrentHeader()
466        {
467            return &headers_.back();
468        }
469
470        bool IsEmpty() const
471        {
472            return headers_.empty();
473        }
474
475        size_t GetNumHeaders() const
476        {
477            return headers_.size();
478        }
479
480        void ComputeLayout() override;
481
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
506        size_t GetHash() const
507        {
508            return hash_;
509        }
510
511        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:
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