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