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