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