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