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> ¶ms)
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> ¶ms)
945 {
946 Add(ret_type);
947 for (const auto ¶m : 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