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