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