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> ¶ms)
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> ¶ms)
976 {
977 Add(ret_type);
978 for (const auto ¶m : 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