• 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 #include "file_item_container.h"
17 
18 namespace panda::panda_file {
19 
20 class ItemDeduper {
21 public:
22     template <class T>
Deduplicate(T * item)23     T *Deduplicate(T *item)
24     {
25         static_assert(std::is_base_of_v<BaseItem, T>);
26 
27         ItemData item_data(item);
28         auto it = items_.find(item_data);
29         if (it == items_.cend()) {
30             items_.insert(item_data);
31             return item;
32         }
33 
34         auto result = static_cast<T *>(it->GetItem());
35         if (item != result) {
36             item->SetNeedsEmit(false);
37         }
38 
39         return result;
40     }
41 
GetUniqueCount() const42     size_t GetUniqueCount() const
43     {
44         return items_.size();
45     }
46 
47 private:
48     class ItemWriter : public Writer {
49     public:
ItemWriter(std::vector<uint8_t> * buf,size_t offset)50         ItemWriter(std::vector<uint8_t> *buf, size_t offset) : buf_(buf), offset_(offset) {}
51         ~ItemWriter() override = default;
52 
53         NO_COPY_SEMANTIC(ItemWriter);
54         NO_MOVE_SEMANTIC(ItemWriter);
55 
WriteByte(uint8_t byte)56         bool WriteByte(uint8_t byte) override
57         {
58             buf_->push_back(byte);
59             ++offset_;
60             return true;
61         }
62 
WriteBytes(const std::vector<uint8_t> & bytes)63         bool WriteBytes(const std::vector<uint8_t> &bytes) override
64         {
65             buf_->insert(buf_->end(), bytes.cbegin(), bytes.cend());
66             offset_ += bytes.size();
67             return true;
68         }
69 
GetOffset() const70         size_t GetOffset() const override
71         {
72             return offset_;
73         }
74 
75     private:
76         std::vector<uint8_t> *buf_;
77         size_t offset_;
78     };
79 
80     class ItemData {
81     public:
ItemData(BaseItem * item)82         explicit ItemData(BaseItem *item) : item_(item)
83         {
84             Initialize();
85         }
86 
87         ~ItemData() = default;
88 
89         DEFAULT_COPY_SEMANTIC(ItemData);
90         NO_MOVE_SEMANTIC(ItemData);
91 
GetItem() const92         BaseItem *GetItem() const
93         {
94             return item_;
95         }
96 
GetHash() const97         uint32_t GetHash() const
98         {
99             ASSERT(IsInitialized());
100             return hash_;
101         }
102 
operator ==(const ItemData & item_data) const103         bool operator==(const ItemData &item_data) const noexcept
104         {
105             ASSERT(IsInitialized());
106             return data_ == item_data.data_;
107         }
108 
109     private:
IsInitialized() const110         bool IsInitialized() const
111         {
112             return !data_.empty();
113         }
114 
Initialize()115         void Initialize()
116         {
117             ASSERT(item_->NeedsEmit());
118 
119             ItemWriter writer(&data_, item_->GetOffset());
120             [[maybe_unused]] auto res = item_->Write(&writer);
121             ASSERT(res);
122             ASSERT(data_.size() == item_->GetSize());
123 
124             hash_ = GetHash32(data_.data(), data_.size());
125         }
126 
127         BaseItem *item_;
128         uint32_t hash_ {0};
129         std::vector<uint8_t> data_;
130     };
131 
132     struct ItemHash {
operator ()panda::panda_file::ItemDeduper::ItemHash133         size_t operator()(const ItemData &item_data) const noexcept
134         {
135             return item_data.GetHash();
136         }
137     };
138 
139     std::unordered_set<ItemData, ItemHash> items_;
140 };
141 
142 template <class T, class C, class I, class P, class E, class... Args>
GetOrInsert(C & map,I & items,const P & pos,const E & key,bool is_foreign,Args &&...args)143 static T *GetOrInsert(C &map, I &items, const P &pos, const E &key, bool is_foreign, Args &&... args)
144 {
145     auto it = map.find(key);
146     if (it != map.cend()) {
147         auto *item = it->second;
148         if (item->IsForeign() == is_foreign) {
149             return static_cast<T *>(item);
150         }
151 
152         UNREACHABLE();
153         return nullptr;
154     }
155 
156     auto ii = items.insert(pos, std::make_unique<T>(std::forward<Args>(args)...));
157     auto *item = static_cast<T *>(ii->get());
158 
159     [[maybe_unused]] auto res = map.insert({key, item});
160     ASSERT(res.second);
161     return item;
162 }
163 
164 /*static*/
165 uint8_t ItemContainer::apiVersion = 0;
166 std::string ItemContainer::subApiVersion = DEFAULT_SUB_API_VERSION;
167 
ItemContainer()168 ItemContainer::ItemContainer()
169 {
170     items_end_ = items_.insert(items_.end(), std::make_unique<EndItem>());
171     code_items_end_ = items_.insert(items_.end(), std::make_unique<EndItem>());
172     debug_items_end_ = items_.insert(items_.end(), std::make_unique<EndItem>());
173     end_ = debug_items_end_->get();
174 }
175 
GetOrCreateClassItem(const std::string & str)176 ClassItem *ItemContainer::GetOrCreateClassItem(const std::string &str)
177 {
178     return GetOrInsert<ClassItem>(class_map_, items_, items_end_, str, false, str, this);
179 }
180 
GetOrCreateForeignClassItem(const std::string & str)181 ForeignClassItem *ItemContainer::GetOrCreateForeignClassItem(const std::string &str)
182 {
183     return GetOrInsert<ForeignClassItem>(class_map_, foreign_items_, foreign_items_.end(), str, true, str, this);
184 }
185 
GetOrCreateStringItem(const std::string & str)186 StringItem *ItemContainer::GetOrCreateStringItem(const std::string &str)
187 {
188     return GetOrInsert<StringItem>(string_map_, items_, items_end_, str, false, str, this);
189 }
190 
GetStringItem(const std::string & str) const191 StringItem *ItemContainer::GetStringItem(const std::string &str) const
192 {
193     auto it1 = string_map_.find(str);
194     if (it1 != string_map_.cend()) {
195         auto *item = it1->second;
196         if (item->IsForeign() == false) {
197             return static_cast<StringItem *>(item);
198         }
199 
200         UNREACHABLE();
201         return nullptr;
202     }
203     UNREACHABLE();
204     return nullptr;
205 }
206 
GetOrCreateLiteralArrayItem(const std::string & id)207 LiteralArrayItem *ItemContainer::GetOrCreateLiteralArrayItem(const std::string &id)
208 {
209     return GetOrInsert<LiteralArrayItem>(literalarray_map_, items_, items_end_, id, false, this);
210 }
211 
GetOrCreateIntegerValueItem(uint32_t v)212 ScalarValueItem *ItemContainer::GetOrCreateIntegerValueItem(uint32_t v)
213 {
214     return GetOrInsert<ScalarValueItem>(int_value_map_, items_, items_end_, v, false, v, this);
215 }
216 
GetOrCreateLongValueItem(uint64_t v)217 ScalarValueItem *ItemContainer::GetOrCreateLongValueItem(uint64_t v)
218 {
219     return GetOrInsert<ScalarValueItem>(long_value_map_, items_, items_end_, v, false, v, this);
220 }
221 
GetOrCreateFloatValueItem(float v)222 ScalarValueItem *ItemContainer::GetOrCreateFloatValueItem(float v)
223 {
224     return GetOrInsert<ScalarValueItem>(float_value_map_, items_, items_end_, bit_cast<uint32_t>(v), false, v, this);
225 }
226 
GetOrCreateDoubleValueItem(double v)227 ScalarValueItem *ItemContainer::GetOrCreateDoubleValueItem(double v)
228 {
229     return GetOrInsert<ScalarValueItem>(double_value_map_, items_, items_end_, bit_cast<uint64_t>(v), false, v, this);
230 }
231 
GetOrCreateIdValueItem(BaseItem * v)232 ScalarValueItem *ItemContainer::GetOrCreateIdValueItem(BaseItem *v)
233 {
234     return GetOrInsert<ScalarValueItem>(id_value_map_, items_, items_end_, v, false, v, this);
235 }
236 
GetOrCreateProtoItem(TypeItem * ret_type,const std::vector<MethodParamItem> & params)237 ProtoItem *ItemContainer::GetOrCreateProtoItem(TypeItem *ret_type, const std::vector<MethodParamItem> &params)
238 {
239     ProtoKey key(ret_type, params);
240     return GetOrInsert<ProtoItem>(proto_map_, items_, items_end_, key, false, ret_type, params, this);
241 }
242 
GetOrCreatePrimitiveTypeItem(Type type)243 PrimitiveTypeItem *ItemContainer::GetOrCreatePrimitiveTypeItem(Type type)
244 {
245     return GetOrCreatePrimitiveTypeItem(type.GetId());
246 }
247 
GetOrCreatePrimitiveTypeItem(Type::TypeId type)248 PrimitiveTypeItem *ItemContainer::GetOrCreatePrimitiveTypeItem(Type::TypeId type)
249 {
250     return GetOrInsert<PrimitiveTypeItem>(primitive_type_map_, items_, items_end_, type, false, type, this);
251 }
252 
CreateLineNumberProgramItem()253 LineNumberProgramItem *ItemContainer::CreateLineNumberProgramItem()
254 {
255     auto it = items_.insert(debug_items_end_, std::make_unique<LineNumberProgramItem>(this));
256     auto *item = static_cast<LineNumberProgramItem *>(it->get());
257     [[maybe_unused]] auto res = line_number_program_index_item_.Add(item);
258     ASSERT(res);
259     return item;
260 }
261 
DeduplicateLineNumberProgram(DebugInfoItem * item,ItemDeduper * deduper)262 void ItemContainer::DeduplicateLineNumberProgram(DebugInfoItem *item, ItemDeduper *deduper)
263 {
264     auto *line_number_program = item->GetLineNumberProgram();
265     auto *deduplicated = deduper->Deduplicate(line_number_program);
266     if (deduplicated != line_number_program) {
267         item->SetLineNumberProgram(deduplicated);
268         line_number_program_index_item_.Remove(line_number_program);
269         line_number_program_index_item_.IncRefCount(deduplicated);
270     }
271 }
272 
DeduplicateDebugInfo(MethodItem * method,ItemDeduper * debug_info_deduper,ItemDeduper * line_number_program_deduper)273 void ItemContainer::DeduplicateDebugInfo(MethodItem *method, ItemDeduper *debug_info_deduper,
274                                          ItemDeduper *line_number_program_deduper)
275 {
276     auto *debug_item = method->GetDebugInfo();
277     if (debug_item == nullptr) {
278         return;
279     }
280 
281     DeduplicateLineNumberProgram(debug_item, line_number_program_deduper);
282 
283     auto *deduplicated = debug_info_deduper->Deduplicate(debug_item);
284     if (deduplicated != debug_item) {
285         method->SetDebugInfo(deduplicated);
286         line_number_program_index_item_.DecRefCount(debug_item->GetLineNumberProgram());
287     }
288 }
289 
DeduplicateCodeAndDebugInfo()290 void ItemContainer::DeduplicateCodeAndDebugInfo()
291 {
292     ItemDeduper line_number_program_deduper;
293     ItemDeduper debug_deduper;
294 
295     for (auto &p : class_map_) {
296         auto *item = p.second;
297         if (item->IsForeign()) {
298             continue;
299         }
300 
301         auto *class_item = static_cast<ClassItem *>(item);
302 
303         class_item->VisitMethods(
304             [this, &debug_deduper, &line_number_program_deduper](BaseItem *param_item) {
305                 auto *method_item = static_cast<MethodItem *>(param_item);
306                 DeduplicateDebugInfo(method_item, &debug_deduper, &line_number_program_deduper);
307                 return true;
308             });
309     }
310 }
311 
DeduplicateAnnotationValue(AnnotationItem * annotation_item,ItemDeduper * deduper)312 static void DeduplicateAnnotationValue(AnnotationItem *annotation_item, ItemDeduper *deduper)
313 {
314     auto *elems = annotation_item->GetElements();
315     const auto &tags = annotation_item->GetTags();
316 
317     for (size_t i = 0; i < elems->size(); i++) {
318         auto tag = tags[i];
319 
320         // try to dedupe only ArrayValueItems
321         switch (tag.GetItem()) {
322             case 'K':
323             case 'L':
324             case 'M':
325             case 'N':
326             case 'O':
327             case 'P':
328             case 'Q':
329             case 'R':
330             case 'S':
331             case 'T':
332             case 'U':
333             case 'V':
334             case 'W':
335             case 'X':
336             case 'Y':
337             case 'Z':
338             case '@':
339                 break;
340             default:
341                 continue;
342         }
343 
344         auto &elem = (*elems)[i];
345         auto *value = elem.GetValue();
346         auto *deduplicated = deduper->Deduplicate(value);
347         if (deduplicated != value) {
348             elem.SetValue(deduplicated);
349         }
350     }
351 }
352 
DeduplicateAnnotations(std::vector<AnnotationItem * > * items,ItemDeduper * annotation_deduper,ItemDeduper * value_deduper)353 static void DeduplicateAnnotations(std::vector<AnnotationItem *> *items, ItemDeduper *annotation_deduper,
354                                    ItemDeduper *value_deduper)
355 {
356     for (auto &item : *items) {
357         DeduplicateAnnotationValue(item, value_deduper);
358         auto *deduplicated = annotation_deduper->Deduplicate(item);
359         if (deduplicated != item) {
360             item = deduplicated;
361         }
362     }
363 }
364 
DeduplicateAnnotations()365 void ItemContainer::DeduplicateAnnotations()
366 {
367     ItemDeduper value_deduper;
368     ItemDeduper annotation_deduper;
369 
370     for (auto &p : class_map_) {
371         auto *item = p.second;
372         if (item->IsForeign()) {
373             continue;
374         }
375 
376         auto *class_item = static_cast<ClassItem *>(item);
377 
378         panda_file::DeduplicateAnnotations(class_item->GetRuntimeAnnotations(), &annotation_deduper, &value_deduper);
379         panda_file::DeduplicateAnnotations(class_item->GetAnnotations(), &annotation_deduper, &value_deduper);
380         panda_file::DeduplicateAnnotations(class_item->GetRuntimeTypeAnnotations(), &annotation_deduper,
381                                            &value_deduper);
382         panda_file::DeduplicateAnnotations(class_item->GetTypeAnnotations(), &annotation_deduper, &value_deduper);
383 
384         class_item->VisitMethods([&annotation_deduper, &value_deduper](BaseItem *param_item) {
385             auto *method_item = static_cast<MethodItem *>(param_item);
386             panda_file::DeduplicateAnnotations(method_item->GetRuntimeAnnotations(), &annotation_deduper,
387                                                &value_deduper);
388             panda_file::DeduplicateAnnotations(method_item->GetAnnotations(), &annotation_deduper, &value_deduper);
389             panda_file::DeduplicateAnnotations(method_item->GetRuntimeTypeAnnotations(), &annotation_deduper,
390                                                &value_deduper);
391             panda_file::DeduplicateAnnotations(method_item->GetTypeAnnotations(), &annotation_deduper, &value_deduper);
392             return true;
393         });
394 
395         class_item->VisitFields([&annotation_deduper, &value_deduper](BaseItem *param_item) {
396             auto *field_item = static_cast<FieldItem *>(param_item);
397             panda_file::DeduplicateAnnotations(field_item->GetRuntimeAnnotations(), &annotation_deduper,
398                                                &value_deduper);
399             panda_file::DeduplicateAnnotations(field_item->GetAnnotations(), &annotation_deduper, &value_deduper);
400             panda_file::DeduplicateAnnotations(field_item->GetRuntimeTypeAnnotations(), &annotation_deduper,
401                                                &value_deduper);
402             panda_file::DeduplicateAnnotations(field_item->GetTypeAnnotations(), &annotation_deduper, &value_deduper);
403             return true;
404         });
405     }
406 }
407 
DeduplicateItems(bool computeLayout)408 void ItemContainer::DeduplicateItems(bool computeLayout)
409 {
410     if (computeLayout) {
411         ComputeLayout();
412     }
413     DeduplicateCodeAndDebugInfo();
414     DeduplicateAnnotations();
415 }
416 
Compare(const std::unique_ptr<BaseItem> & item1,const std::unique_ptr<BaseItem> & item2)417 static bool Compare(const std::unique_ptr<BaseItem> &item1, const std::unique_ptr<BaseItem> &item2)
418 {
419     return item1->GetReLayoutRank() > item2->GetReLayoutRank();
420 }
421 
ReLayout()422 void ItemContainer::ReLayout()
423 {
424     for (auto &item : items_) {
425         if (!item->NeedsEmit()) {
426             continue;
427         }
428 
429         /* Because only class items and func_main_0 will be accessed in runtime's initialization,
430          * the items in abc will be arranged in following order to increase cache hit rate:
431          *     class items -> string items(for field name) -> other items
432          */
433         switch (item->GetItemType()) {
434             case ItemTypes::CLASS_ITEM: {
435                 item->SetReLayoutRank(ItemRank::CLASS_ITEM_RANK);
436                 break;
437             }
438             case ItemTypes::STRING_ITEM: {
439                 item->SetReLayoutRank(ItemRank::STRING_ITEM_RANK);
440                 break;
441             }
442             default: {
443                 break;
444             }
445         }
446     }
447 
448     items_.sort(Compare);
449 }
450 
ComputeLayout()451 uint32_t ItemContainer::ComputeLayout()
452 {
453     const auto bc_version = GetVersionByApi(ItemContainer::GetApi(), ItemContainer::GetSubApi());
454     uint32_t original_offset = 0;
455     uint32_t num_classes = class_map_.size();
456     uint32_t num_literalarrays = literalarray_map_.size();
457     uint32_t class_idx_offset = sizeof(File::Header);
458     uint32_t cur_offset = 0;
459     if (ContainsLiteralArrayInHeader(bc_version.value())) {
460         cur_offset = class_idx_offset + (num_classes + num_literalarrays) * ID_SIZE;
461     } else {
462         cur_offset = class_idx_offset + (num_classes * ID_SIZE);
463     }
464     items_round_up_size_.clear();
465     foreign_item_roundup_size_ = 0;
466 
467     UpdateOrderIndexes();
468 
469     RebuildIndexSection();
470     RebuildLineNumberProgramIndex();
471 
472     index_section_item_.SetOffset(cur_offset);
473     index_section_item_.ComputeLayout();
474     cur_offset += index_section_item_.GetSize();
475 
476     for (auto &item : foreign_items_) {
477         original_offset = cur_offset;
478         cur_offset = RoundUp(cur_offset, item->Alignment());
479         foreign_item_roundup_size_ += CalculateRoundUpSize(original_offset, cur_offset);
480         item->SetOffset(cur_offset);
481         item->ComputeLayout();
482         cur_offset += item->GetSize();
483     }
484 
485     for (auto &item : items_) {
486         const auto &name = item->GetName();
487 
488         if (!item->NeedsEmit()) {
489             continue;
490         }
491 
492         original_offset = cur_offset;
493         cur_offset = RoundUp(cur_offset, item->Alignment());
494         items_round_up_size_[name] += CalculateRoundUpSize(original_offset, cur_offset);
495         item->SetOffset(cur_offset);
496         item->ComputeLayout();
497         cur_offset += item->GetSize();
498     }
499 
500     // Line number program should be last because it's size is known only after deduplication
501     original_offset = cur_offset;
502     cur_offset = RoundUp(cur_offset, line_number_program_index_item_.Alignment());
503     line_number_item_roundup_size_ = CalculateRoundUpSize(original_offset, cur_offset);
504     line_number_program_index_item_.SetOffset(cur_offset);
505     line_number_program_index_item_.ComputeLayout();
506     cur_offset += line_number_program_index_item_.GetSize();
507 
508     end_->SetOffset(cur_offset);
509 
510     return cur_offset;
511 }
512 
RebuildLineNumberProgramIndex()513 void ItemContainer::RebuildLineNumberProgramIndex()
514 {
515     line_number_program_index_item_.Reset();
516     line_number_program_index_item_.UpdateItems(nullptr, nullptr);
517 }
518 
RebuildIndexSection()519 void ItemContainer::RebuildIndexSection()
520 {
521     index_section_item_.Reset();
522 
523     for (auto &item : foreign_items_) {
524         ProcessIndexDependecies(item.get());
525     }
526 
527     for (auto &item : items_) {
528         if (!item->NeedsEmit()) {
529             continue;
530         }
531 
532         ProcessIndexDependecies(item.get());
533     }
534 
535     if (!index_section_item_.IsEmpty()) {
536         index_section_item_.GetCurrentHeader()->SetEnd(end_);
537     }
538 
539     index_section_item_.UpdateItems();
540 }
541 
UpdateOrderIndexes()542 void ItemContainer::UpdateOrderIndexes()
543 {
544     size_t idx = 0;
545 
546     for (auto &item : foreign_items_) {
547         item->SetOrderIndex(idx++);
548         item->Visit([&idx](BaseItem *param_item) {
549             param_item->SetOrderIndex(idx++);
550             return true;
551         });
552     }
553 
554     for (auto &item : items_) {
555         if (!item->NeedsEmit()) {
556             continue;
557         }
558 
559         item->SetOrderIndex(idx++);
560         item->Visit([&idx](BaseItem *param_item) {
561             param_item->SetOrderIndex(idx++);
562             return true;
563         });
564     }
565 
566     end_->SetOrderIndex(idx++);
567 }
568 
ReorderItems(panda::panda_file::pgo::ProfileOptimizer * profile_opt)569 void ItemContainer::ReorderItems(panda::panda_file::pgo::ProfileOptimizer *profile_opt)
570 {
571     profile_opt->ProfileGuidedRelayout(items_);
572 }
573 
AddIndexDependecies(BaseItem * item)574 void ItemContainer::AddIndexDependecies(BaseItem *item)
575 {
576     if (index_section_item_.IsEmpty()) {
577         index_section_item_.AddHeader();
578         index_section_item_.GetCurrentHeader()->SetStart(item);
579     }
580     const auto &item_deps = item->GetIndexDependencies();
581     if (!index_section_item_.GetCurrentHeader()->Add(item_deps)) {
582         index_section_item_.GetCurrentHeader()->SetEnd(item);
583         index_section_item_.AddHeader();
584         index_section_item_.GetCurrentHeader()->SetStart(item);
585         if (!index_section_item_.GetCurrentHeader()->Add(item_deps)) {
586             LOG(FATAL, PANDAFILE) << "Cannot add " << item_deps.size() << " items to index";
587             UNREACHABLE();
588         }
589     }
590     if (item->GetName() == "method_item") {
591         ASSERT(index_section_item_.GetNumHeaders() >= 1);
592         static_cast<BaseMethodItem *>(item)->SetHeaderIndex(index_section_item_.GetNumHeaders() - 1);
593     }
594 }
595 
ProcessIndexDependecies(BaseItem * item)596 void ItemContainer::ProcessIndexDependecies(BaseItem *item)
597 {
598     AddIndexDependecies(item);
599     item->Visit([&](BaseItem *param_item) {
600         AddIndexDependecies(param_item);
601         return true;
602     });
603 }
604 
WriteHeaderIndexInfo(Writer * writer)605 bool ItemContainer::WriteHeaderIndexInfo(Writer *writer)
606 {
607     if (!writer->Write<uint32_t>(class_map_.size())) {
608         return false;
609     }
610 
611     if (!writer->Write<uint32_t>(sizeof(File::Header))) {
612         return false;
613     }
614 
615     if (!writer->Write<uint32_t>(line_number_program_index_item_.GetNumItems())) {
616         return false;
617     }
618 
619     if (!writer->Write<uint32_t>(line_number_program_index_item_.GetOffset())) {
620         return false;
621     }
622 
623     const auto bc_version = GetVersionByApi(ItemContainer::GetApi(), ItemContainer::GetSubApi());
624 
625     uint32_t num_literalarrays = INVALID_INDEX;
626     uint32_t literalarray_idx_offset = INVALID_OFFSET;
627     size_t index_section_off = sizeof(File::Header) + class_map_.size() * ID_SIZE;
628 
629     if (ContainsLiteralArrayInHeader(bc_version.value())) {
630         num_literalarrays = literalarray_map_.size();
631         literalarray_idx_offset = sizeof(File::Header) + class_map_.size() * ID_SIZE;
632         index_section_off = literalarray_idx_offset + num_literalarrays * ID_SIZE;
633     }
634 
635     if (!writer->Write<uint32_t>(num_literalarrays)) {
636         return false;
637     }
638 
639     if (!writer->Write<uint32_t>(literalarray_idx_offset)) {
640         return false;
641     }
642 
643     if (!writer->Write<uint32_t>(index_section_item_.GetNumHeaders())) {
644         return false;
645     }
646 
647     return writer->Write<uint32_t>(index_section_off);
648 }
649 
WriteHeader(Writer * writer,ssize_t * checksum_offset)650 bool ItemContainer::WriteHeader(Writer *writer, ssize_t *checksum_offset)
651 {
652     uint32_t file_size = ComputeLayout();
653     writer->ReserveBufferCapacity(file_size);
654 
655     std::vector<uint8_t> magic;
656     magic.assign(File::MAGIC.cbegin(), File::MAGIC.cend());
657     if (!writer->WriteBytes(magic)) {
658         return false;
659     }
660 
661     *checksum_offset = writer->GetOffset();
662     uint32_t checksum = 0;
663     if (!writer->Write(checksum)) {
664         return false;
665     }
666     writer->CountChecksum(true);
667 
668     const auto bc_version = GetVersionByApi(ItemContainer::GetApi(), ItemContainer::GetSubApi());
669     std::vector<uint8_t> versionVec(std::begin(bc_version.value()), std::end(bc_version.value()));
670 
671     if (!writer->WriteBytes(versionVec)) {
672         return false;
673     }
674 
675     if (!writer->Write(file_size)) {
676         return false;
677     }
678 
679     uint32_t foreign_offset = GetForeignOffset();
680     if (!writer->Write(foreign_offset)) {
681         return false;
682     }
683 
684     uint32_t foreign_size = GetForeignSize();
685     if (!writer->Write(foreign_size)) {
686         return false;
687     }
688 
689     return WriteHeaderIndexInfo(writer);
690 }
691 
WriteItems(Writer * writer)692 bool ItemContainer::WriteItems(Writer *writer)
693 {
694     ASSERT(writer != nullptr);
695     for (auto &item : foreign_items_) {
696         if (!writer->Align(item->Alignment())) {
697             return false;
698         }
699 
700         if (!item->Write(writer)) {
701             return false;
702         }
703     }
704 
705     for (auto &item : items_) {
706         if (!item->NeedsEmit()) {
707             continue;
708         }
709 
710         if (!writer->Align(item->Alignment())) {
711             return false;
712         }
713 
714         if (!item->Write(writer)) {
715             return false;
716         }
717     }
718     return true;
719 }
720 
Write(Writer * writer,bool deduplicateItems)721 bool ItemContainer::Write(Writer *writer, bool deduplicateItems)
722 {
723     if (deduplicateItems) {
724         DeduplicateItems();
725     }
726 
727     ssize_t checksum_offset = -1;
728     if (!WriteHeader(writer, &checksum_offset)) {
729         return false;
730     }
731     ASSERT(checksum_offset != -1);
732 
733     // Write class idx
734 
735     for (auto &entry : class_map_) {
736         if (!writer->Write(entry.second->GetOffset())) {
737             return false;
738         }
739     }
740 
741     // Write literalArray idx
742 
743     const auto bc_version = GetVersionByApi(ItemContainer::GetApi(), ItemContainer::GetSubApi());
744     if (ContainsLiteralArrayInHeader(bc_version.value())) {
745         for (auto &entry : literalarray_map_) {
746             if (!writer->Write(entry.second->GetOffset())) {
747                 return false;
748             }
749         }
750     }
751 
752     // Write index section
753 
754     if (!index_section_item_.Write(writer)) {
755         return false;
756     }
757 
758     if (!WriteItems(writer)) {
759         return false;
760     }
761 
762     if (!writer->Align(line_number_program_index_item_.Alignment())) {
763         return false;
764     }
765 
766     // Write line number program idx
767 
768     if (!line_number_program_index_item_.Write(writer)) {
769         return false;
770     }
771 
772     writer->CountChecksum(false);
773     writer->RewriteChecksum(checksum_offset);
774 
775     return writer->FinishWrite();
776 }
777 
GetStat()778 std::map<std::string, size_t> ItemContainer::GetStat()
779 {
780     std::map<std::string, size_t> stat;
781 
782     DeduplicateItems();
783     ComputeLayout();
784 
785     stat["header_item"] = sizeof(File::Header);
786     stat["class_idx_item"] = class_map_.size() * ID_SIZE;
787     stat["line_number_program_idx_item"] = line_number_program_index_item_.GetNumItems() * ID_SIZE
788                                            + line_number_item_roundup_size_;
789     const auto bc_version = GetVersionByApi(ItemContainer::GetApi(), ItemContainer::GetSubApi());
790     if (ContainsLiteralArrayInHeader(bc_version.value())) {
791         stat["literalarray_idx"] = literalarray_map_.size() * ID_SIZE;
792     } else {
793         stat["literalarray_idx"] = 0;
794     }
795 
796     stat["index_section_item"] = index_section_item_.GetSize();
797     stat["foreign_item"] = GetForeignSize() + foreign_item_roundup_size_;
798 
799     size_t num_ins = 0;
800     size_t codesize = 0;
801     for (auto &item : items_) {
802         if (!item->NeedsEmit()) {
803             continue;
804         }
805 
806         const auto &name = item->GetName();
807         size_t size = item->GetSize();
808         auto it = stat.find(name);
809         if (it != stat.cend()) {
810             stat[name] += size;
811         } else if (size != 0) {
812             stat[name] = size;
813         }
814         if (name == "code_item") {
815             num_ins += static_cast<CodeItem *>(item.get())->GetNumInstructions();
816             codesize += static_cast<CodeItem *>(item.get())->GetCodeSize();
817         }
818     }
819 
820     for (const auto &[name, round_up_size] : items_round_up_size_) {
821         stat[name] += round_up_size;
822     }
823     stat["instructions_number"] = num_ins;
824     stat["codesize"] = codesize;
825 
826     return stat;
827 }
828 
DumpItemsStat(std::ostream & os) const829 void ItemContainer::DumpItemsStat(std::ostream &os) const
830 {
831     struct Stat {
832         size_t n;
833         size_t total_size;
834     };
835 
836     std::map<std::string, Stat> stat;
837 
838     auto collect_stat = [&stat](auto &items) {
839         for (auto &item : items) {
840             if (!item->NeedsEmit()) {
841                 continue;
842             }
843 
844             const auto &name = item->GetName();
845             size_t size = item->GetSize();
846             auto it = stat.find(name);
847             if (it != stat.cend()) {
848                 stat[name].n += 1;
849                 stat[name].total_size += size;
850             } else if (size != 0) {
851                 stat[name] = {1, size};
852             }
853         }
854     };
855 
856     collect_stat(foreign_items_);
857     collect_stat(items_);
858 
859     for (auto &[name, elem] : stat) {
860         os << name << ":" << std::endl;
861         os << "    n          = " << elem.n << std::endl;
862         os << "    total size = " << elem.total_size << std::endl;
863     }
864 }
865 
GetForeignOffset() const866 size_t ItemContainer::GetForeignOffset() const
867 {
868     if (foreign_items_.empty()) {
869         return 0;
870     }
871 
872     return foreign_items_.front()->GetOffset();
873 }
874 
GetForeignSize() const875 size_t ItemContainer::GetForeignSize() const
876 {
877     if (foreign_items_.empty()) {
878         return 0;
879     }
880 
881     size_t begin = foreign_items_.front()->GetOffset();
882     size_t end = foreign_items_.back()->GetOffset() + foreign_items_.back()->GetSize();
883 
884     return end - begin;
885 }
886 
Write(Writer * writer)887 bool ItemContainer::IndexHeaderItem::Write(Writer *writer)
888 {
889     ASSERT(GetOffset() == writer->GetOffset());
890     ASSERT(start_ != nullptr);
891     ASSERT(start_->GetOffset() != 0);
892     ASSERT(end_ != nullptr);
893     ASSERT(end_->GetOffset() != 0);
894 
895     if (!writer->Write<uint32_t>(start_->GetOffset())) {
896         return false;
897     }
898 
899     if (!writer->Write<uint32_t>(end_->GetOffset())) {
900         return false;
901     }
902 
903     for (auto *index_item : indexes_) {
904         if (!index_item->NeedsEmit()) {
905             // reserve [field_idx_size] | [proto_idx_size] field
906             if (!writer->Write<uint32_t>(INVALID_INDEX)) {
907                 return false;
908             }
909             // reserve [field_idx_off] | [proto_idx_off] field
910             if (!writer->Write<uint32_t>(INVALID_OFFSET)) {
911                 return false;
912             }
913         } else {
914             if (!writer->Write<uint32_t>(index_item->GetNumItems())) {
915                 return false;
916             }
917 
918             ASSERT(index_item->GetOffset() != 0);
919             if (!writer->Write<uint32_t>(index_item->GetOffset())) {
920                 return false;
921             }
922         }
923     }
924 
925     return true;
926 }
927 
Add(const std::list<IndexedItem * > & items)928 bool ItemContainer::IndexHeaderItem::Add(const std::list<IndexedItem *> &items)
929 {
930     std::list<IndexedItem *> added_items;
931 
932     for (auto *item : items) {
933         auto type = item->GetIndexType();
934         ASSERT(type != IndexType::NONE);
935 
936         auto *index_item = IndexGetIndexByType(type);
937 
938         if (index_item->Has(item)) {
939             continue;
940         }
941 
942         if (!index_item->Add(item)) {
943             Remove(added_items);
944             return false;
945         }
946 
947         added_items.push_back(item);
948     }
949 
950     return true;
951 }
952 
Remove(const std::list<IndexedItem * > & items)953 void ItemContainer::IndexHeaderItem::Remove(const std::list<IndexedItem *> &items)
954 {
955     for (auto *item : items) {
956         auto type = item->GetIndexType();
957         ASSERT(type != IndexType::NONE);
958 
959         auto *index_item = IndexGetIndexByType(type);
960         index_item->Remove(item);
961     }
962 }
963 
Write(Writer * writer)964 bool ItemContainer::IndexItem::Write(Writer *writer)
965 {
966     ASSERT(GetOffset() == writer->GetOffset());
967 
968     if (NeedsEmit()) {
969         for (auto *item : index_) {
970             if (!writer->Write<uint32_t>(item->GetOffset())) {
971                 return false;
972             }
973         }
974     }
975 
976     return true;
977 }
978 
GetItemType() const979 ItemTypes ItemContainer::IndexItem::GetItemType() const
980 {
981     switch (type_) {
982         case IndexType::CLASS:
983             return ItemTypes::CLASS_INDEX_ITEM;
984         case IndexType::METHOD_STRING_LITERAL:
985             return ItemTypes::METHOD_INDEX_ITEM;
986         case IndexType::FIELD:
987             return ItemTypes::FIELD_INDEX_ITEM;
988         case IndexType::PROTO:
989             return ItemTypes::PROTO_INDEX_ITEM;
990         case IndexType::LINE_NUMBER_PROG:
991             return ItemTypes::LINE_NUMBER_PROGRAM_INDEX_ITEM;
992         default:
993             break;
994     }
995 
996     UNREACHABLE();
997 }
998 
Add(IndexedItem * item)999 bool ItemContainer::IndexItem::Add(IndexedItem *item)
1000 {
1001     auto size = index_.size();
1002     ASSERT(size <= max_index_);
1003 
1004     if (size == max_index_) {
1005         return false;
1006     }
1007 
1008     auto res = index_.insert(item);
1009     ASSERT(res.second);
1010 
1011     return res.second;
1012 }
1013 
AddHeader()1014 void ItemContainer::IndexSectionItem::AddHeader()
1015 {
1016     std::vector<IndexItem *> index_items;
1017     for (size_t i = 0; i < INDEX_COUNT_16; i++) {
1018         auto type = static_cast<IndexType>(i);
1019         indexes_.emplace_back(type, MAX_INDEX_16);
1020         index_items.push_back(&indexes_.back());
1021     }
1022     headers_.emplace_back(index_items);
1023 }
1024 
CalculateSize() const1025 size_t ItemContainer::IndexSectionItem::CalculateSize() const
1026 {
1027     size_t size = headers_.size() * sizeof(File::IndexHeader);
1028     for (auto &index_item : indexes_) {
1029         size += index_item.GetSize();
1030     }
1031     return size;
1032 }
1033 
ComputeLayout()1034 void ItemContainer::IndexSectionItem::ComputeLayout()
1035 {
1036     size_t offset = GetOffset();
1037 
1038     for (auto &header : headers_) {
1039         header.SetOffset(offset);
1040         header.ComputeLayout();
1041         offset += header.GetSize();
1042     }
1043 
1044     for (auto &index : indexes_) {
1045         index.SetOffset(offset);
1046         index.ComputeLayout();
1047         offset += index.GetSize();
1048     }
1049 }
1050 
Write(Writer * writer)1051 bool ItemContainer::IndexSectionItem::Write(Writer *writer)
1052 {
1053     ASSERT(GetOffset() == writer->GetOffset());
1054 
1055     for (auto &header : headers_) {
1056         if (!header.Write(writer)) {
1057             return false;
1058         }
1059     }
1060 
1061     for (auto &index : indexes_) {
1062         if (!index.Write(writer)) {
1063             return false;
1064         }
1065     }
1066 
1067     return true;
1068 }
1069 
ProtoKey(TypeItem * ret_type,const std::vector<MethodParamItem> & params)1070 ItemContainer::ProtoKey::ProtoKey(TypeItem *ret_type, const std::vector<MethodParamItem> &params)
1071 {
1072     Add(ret_type);
1073     for (const auto &param : params) {
1074         Add(param.GetType());
1075     }
1076     size_t shorty_hash = std::hash<std::string>()(shorty_);
1077     size_t ret_type_hash = std::hash<TypeItem *>()(ret_type);
1078     // combine hashes of shorty and ref_types
1079     hash_ = panda::merge_hashes(shorty_hash, ret_type_hash);
1080     // combine hashes of all param types
1081     for (const auto &item : params) {
1082         size_t param_type_hash = std::hash<TypeItem *>()(item.GetType());
1083         hash_ = panda::merge_hashes(hash_, param_type_hash);
1084     }
1085 }
1086 
Add(TypeItem * item)1087 void ItemContainer::ProtoKey::Add(TypeItem *item)
1088 {
1089     auto type = item->GetType();
1090     shorty_.append(Type::GetSignatureByTypeId(type));
1091     if (type.IsReference()) {
1092         ref_types_.push_back(item);
1093     }
1094 }
1095 
1096 }  // namespace panda::panda_file
1097