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