• 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 <tuple>
17 #include <type_traits>
18 #include <variant>
19 
20 #include "libpandafile/file_items.h"
21 #include "libpandafile/file_reader.h"
22 #include "libpandafile/proto_data_accessor-inl.h"
23 #include "libpandabase/utils/utf.h"
24 
25 #include "linker_context.h"
26 
27 namespace {
28 template <typename T>
DerefPtrRef(T && v)29 auto DerefPtrRef(T &&v) -> std::conditional_t<std::is_pointer_v<T>, std::remove_pointer_t<T>, T> &
30 {
31     static_assert(std::is_pointer_v<T> || std::is_reference_v<T>);
32     if constexpr (std::is_pointer_v<T>) {
33         return *v;
34     } else {
35         return v;
36     }
37 }
38 }  // namespace
39 
40 namespace ark::static_linker {
41 
AddItemToKnown(panda_file::BaseItem * item,const std::map<std::string,panda_file::BaseClassItem * > & cm,const panda_file::FileReader & reader)42 void Context::AddItemToKnown(panda_file::BaseItem *item, const std::map<std::string, panda_file::BaseClassItem *> &cm,
43                              const panda_file::FileReader &reader)
44 {
45     auto *fc = static_cast<panda_file::ForeignClassItem *>(item);
46     auto name = GetStr(fc->GetNameItem());
47     if (auto iter = cm.find(name); iter != cm.end()) {
48         knownItems_[fc] = iter->second;
49     } else {
50         auto clz = cont_.GetOrCreateForeignClassItem(name);
51         cameFrom_.emplace(clz, &reader);
52         knownItems_[fc] = clz;
53     }
54 }
55 
MergeItem(panda_file::BaseItem * item,const panda_file::FileReader & reader)56 void Context::MergeItem(panda_file::BaseItem *item, const panda_file::FileReader &reader)
57 {
58     switch (item->GetItemType()) {
59         case panda_file::ItemTypes::FOREIGN_METHOD_ITEM:
60             MergeForeignMethod(&reader, static_cast<panda_file::ForeignMethodItem *>(item));
61             break;
62         case panda_file::ItemTypes::FOREIGN_FIELD_ITEM:
63             MergeForeignField(&reader, static_cast<panda_file::ForeignFieldItem *>(item));
64             break;
65         default:;
66     }
67 }
68 
Merge()69 void Context::Merge()
70 {
71     // types can reference types only
72     // methods can reference types
73     // code items can reference everything
74 
75     // parse regular classes as forward declarations
76     // parse all foreign classes
77     // parse classes with fields and methods
78     // iterate all known indexed entities and create
79     //   appropriate mappings for them
80     //   oldEntity -> newEntity
81 
82     auto &cm = *cont_.GetClassMap();
83 
84     // set offsets of all entities
85     for (auto &reader : readers_) {
86         for (auto [o, i] : *reader.GetItems()) {
87             i->SetOffset(o.GetOffset());
88         }
89     }
90 
91     AddRegularClasses();
92 
93     // find all referenced foreign classes
94     for (auto &reader : readers_) {
95         auto *items = reader.GetItems();
96         for (auto &itPair : *items) {
97             auto *item = itPair.second;
98             if (item->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
99                 AddItemToKnown(item, cm, reader);
100             }
101         }
102     }
103 
104     FillRegularClasses();
105 
106     // scrap all indexable items
107     for (auto &reader : readers_) {
108         auto *items = reader.GetItems();
109         for (auto [offset, item] : *items) {
110             item->SetOffset(offset.GetOffset());
111             MergeItem(item, reader);
112         }
113     }
114 
115     for (const auto &f : deferredFailedAnnotations_) {
116         f();
117     }
118     deferredFailedAnnotations_.clear();
119 }
120 
CheckClassRedifinition(const std::string & name,panda_file::FileReader * reader)121 void Context::CheckClassRedifinition(const std::string &name, panda_file::FileReader *reader)
122 {
123     auto &cm = *cont_.GetClassMap();
124     if (conf_.partial.count(name) == 0) {
125         if (auto iter = cm.find(name); iter != cm.end()) {
126             Error("Class redefinition", {ErrorDetail("original", iter->second)}, reader);
127         }
128     }
129 }
130 
AddRegularClasses()131 void Context::AddRegularClasses()
132 {
133     for (auto &reader : readers_) {
134         auto *ic = reader.GetContainerPtr();
135         auto &classes = *ic->GetClassMap();
136 
137         knownItems_.reserve(reader.GetItems()->size());
138 
139         for (auto [name, i] : classes) {
140             if (i->IsForeign()) {
141                 continue;
142             }
143             ASSERT(name == GetStr(i->GetNameItem()));
144 
145             CheckClassRedifinition(name, &reader);
146 
147             auto clz = cont_.GetOrCreateClassItem(name);
148             knownItems_[i] = clz;
149             cameFrom_.emplace(clz, &reader);
150         }
151     }
152 }
153 
FillRegularClasses()154 void Context::FillRegularClasses()
155 {
156     auto &cm = *cont_.GetClassMap();
157 
158     for (auto &reader : readers_) {
159         auto *ic = reader.GetContainerPtr();
160         auto &classes = *ic->GetClassMap();
161 
162         for (auto [name, i] : classes) {
163             if (i->IsForeign()) {
164                 continue;
165             }
166             ASSERT(cm.find(name) != cm.end());
167             auto ni = static_cast<panda_file::ClassItem *>(cm[name]);
168             auto oi = static_cast<panda_file::ClassItem *>(i);
169             MergeClass(&reader, ni, oi);
170         }
171     }
172 }
173 
MergeClass(const panda_file::FileReader * reader,panda_file::ClassItem * ni,panda_file::ClassItem * oi)174 void Context::MergeClass(const panda_file::FileReader *reader, panda_file::ClassItem *ni, panda_file::ClassItem *oi)
175 {
176     ni->SetAccessFlags(oi->GetAccessFlags());
177     ni->SetPGORank(oi->GetPGORank());
178     ni->SetSourceLang(oi->GetSourceLang());
179     ni->SetSourceFile(StringFromOld(oi->GetSourceFile()));
180 
181     ni->SetSuperClass(ClassFromOld(oi->GetSuperClass()));
182 
183     for (auto iface : oi->GetInterfaces()) {
184         ni->AddInterface(ClassFromOld(iface));
185     }
186 
187     TransferAnnotations(reader, ni, oi);
188 
189     oi->VisitFields([this, ni, reader](panda_file::BaseItem *mi) -> bool {
190         ASSERT(mi->GetItemType() == panda_file::ItemTypes::FIELD_ITEM);
191         MergeField(reader, ni, reinterpret_cast<panda_file::FieldItem *>(mi));
192         return true;
193     });
194 
195     oi->VisitMethods([this, ni, reader](panda_file::BaseItem *mi) -> bool {
196         ASSERT(mi->GetItemType() == panda_file::ItemTypes::METHOD_ITEM);
197         MergeMethod(reader, ni, reinterpret_cast<panda_file::MethodItem *>(mi));
198         return true;
199     });
200 }
201 
MergeField(const panda_file::FileReader * reader,panda_file::ClassItem * clz,panda_file::FieldItem * oi)202 void Context::MergeField(const panda_file::FileReader *reader, panda_file::ClassItem *clz, panda_file::FieldItem *oi)
203 {
204     auto ni = clz->AddField(StringFromOld(oi->GetNameItem()), TypeFromOld(oi->GetTypeItem()), oi->GetAccessFlags());
205     knownItems_[oi] = ni;
206     cameFrom_.emplace(ni, reader);
207 
208     if (oi->GetValue() != nullptr) {
209         auto newVal = ValueFromOld(oi->GetValue());
210         if (std::holds_alternative<ErrorDetail>(newVal)) {
211             Error("can not parse field value",
212                   {std::get<ErrorDetail>(newVal), ErrorDetail {"field", oi}, {"value", oi->GetValue()}});
213         } else {
214             ni->SetValue(std::get<panda_file::ValueItem *>(newVal));
215         }
216     }
217 
218     TransferAnnotations(reader, ni, oi);
219 }
220 
MergeMethod(const panda_file::FileReader * reader,panda_file::ClassItem * clz,panda_file::MethodItem * oi)221 void Context::MergeMethod(const panda_file::FileReader *reader, panda_file::ClassItem *clz, panda_file::MethodItem *oi)
222 {
223     auto [proto, params] = GetProto(oi->GetProto());
224     auto &oldParams = oi->GetParams();
225     auto *ni = clz->AddMethod(StringFromOld(oi->GetNameItem()), proto, oi->GetAccessFlags(), params);
226     knownItems_[oi] = ni;
227     cameFrom_.emplace(ni, reader);
228     ni->SetProfileSize(oi->GetProfileSize());
229 
230     for (size_t i = 0; i < oldParams.size(); i++) {
231         TransferAnnotations(reader, &ni->GetParams()[i], &oldParams[i]);
232     }
233 
234     TransferAnnotations(reader, ni, oi);
235 
236     if (ni->HasRuntimeParamAnnotations()) {
237         cont_.CreateItem<panda_file::ParamAnnotationsItem>(ni, true);
238     }
239 
240     if (ni->HasParamAnnotations()) {
241         cont_.CreateItem<panda_file::ParamAnnotationsItem>(ni, false);
242     }
243 
244     std::vector<uint8_t> *instructions = nullptr;
245 
246     auto [shouldSave, patchLnp] = UpdateDebugInfo(ni, oi);
247 
248     if (auto oci = oi->GetCode(); oci != nullptr) {
249         shouldSave = true;
250         result_.stats.codeCount++;
251 
252         auto nci = cont_.CreateItem<panda_file::CodeItem>();
253         ni->SetCode(nci);
254         nci->SetPGORank(oci->GetPGORank());
255         nci->SetNumArgs(oci->GetNumArgs());
256         nci->SetNumVregs(oci->GetNumVregs());
257         *nci->GetInstructions() = *oci->GetInstructions();
258         instructions = nci->GetInstructions();
259         nci->SetNumInstructions(oci->GetNumInstructions());
260 
261         CreateTryBlocks(ni, nci, oi, oci);
262     }
263 
264     if (shouldSave) {
265         codeDatas_.push_back(CodeData {instructions, oi, ni, reader, patchLnp});
266     }
267 }
268 
UpdateDebugInfo(panda_file::MethodItem * ni,panda_file::MethodItem * oi)269 std::pair<bool, bool> Context::UpdateDebugInfo(panda_file::MethodItem *ni, panda_file::MethodItem *oi)
270 {
271     auto shouldSave = false;
272     auto patchLnp = false;
273 
274     if (auto odbg = oi->GetDebugInfo(); !conf_.stripDebugInfo && odbg != nullptr) {
275         panda_file::LineNumberProgramItem *lnpItem {};
276 
277         auto oldProgram = odbg->GetLineNumberProgram();
278         if (auto old = knownItems_.find(oldProgram); old != knownItems_.end()) {
279             lnpItem = static_cast<panda_file::LineNumberProgramItem *>(old->second);
280             cont_.IncRefLineNumberProgramItem(lnpItem);
281         } else {
282             lnpItem = cont_.CreateLineNumberProgramItem();
283             knownItems_.emplace(oldProgram, lnpItem);
284 
285             shouldSave = true;
286             result_.stats.debugCount++;
287             patchLnp = true;
288         }
289 
290         auto *ndbg = cont_.CreateItem<panda_file::DebugInfoItem>(lnpItem);
291         ndbg->SetLineNumber(odbg->GetLineNumber());
292         ni->SetDebugInfo(ndbg);
293         for (auto *s : *odbg->GetParameters()) {
294             ndbg->AddParameter(StringFromOld(s));
295         }
296     }
297 
298     return {shouldSave, patchLnp};
299 }
300 
CreateTryBlocks(panda_file::MethodItem * ni,panda_file::CodeItem * nci,panda_file::MethodItem * oi,panda_file::CodeItem * oci)301 void Context::CreateTryBlocks(panda_file::MethodItem *ni, panda_file::CodeItem *nci,
302                               [[maybe_unused]] panda_file::MethodItem *oi, panda_file::CodeItem *oci)
303 {
304     for (const auto &otb : oci->GetTryBlocks()) {
305         auto ncbs = std::vector<panda_file::CodeItem::CatchBlock>();
306         const auto &ocbs = otb.GetCatchBlocks();
307         ncbs.reserve(ocbs.size());
308         for (const auto &ocb : ocbs) {
309             ASSERT(ocb.GetMethod() == oi);
310             auto excKlass = ClassFromOld(ocb.GetType());
311             if (excKlass != nullptr) {
312                 ni->AddIndexDependency(excKlass);
313             }
314             ncbs.emplace_back(ni, excKlass, ocb.GetHandlerPc(), ocb.GetCodeSize());
315         }
316         auto ntb = panda_file::CodeItem::TryBlock(otb.GetStartPc(), otb.GetLength(), ncbs);
317         nci->AddTryBlock(ntb);
318     }
319 }
320 
IsSameType(ark::panda_file::TypeItem * nevv,ark::panda_file::TypeItem * old)321 bool Context::IsSameType(ark::panda_file::TypeItem *nevv, ark::panda_file::TypeItem *old)
322 {
323     if (nevv->GetType().IsPrimitive()) {
324         if (!old->GetType().IsPrimitive()) {
325             return false;
326         }
327         return nevv->GetType() == old->GetType();
328     }
329 
330     auto iter = knownItems_.find(old);
331     return iter != knownItems_.end() && iter->second == nevv;
332 }
333 
TryFindMethod(panda_file::BaseClassItem * klass,panda_file::ForeignMethodItem * fm,std::vector<ErrorDetail> * relatedItems)334 std::variant<bool, panda_file::MethodItem *> Context::TryFindMethod(panda_file::BaseClassItem *klass,
335                                                                     panda_file::ForeignMethodItem *fm,
336                                                                     std::vector<ErrorDetail> *relatedItems)
337 {
338     if (klass == nullptr) {
339         return false;
340     }
341     if (klass->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
342         return true;
343     }
344     ASSERT(klass->GetItemType() == panda_file::ItemTypes::CLASS_ITEM);
345     auto kls = static_cast<panda_file::ClassItem *>(klass);
346     auto op = fm->GetProto();
347 
348     auto [new_meth_beg, new_meth_end] = kls->FindMethod(fm->GetNameItem());
349 
350     for (auto beg = new_meth_beg; beg != new_meth_end; ++beg) {
351         auto nm = beg->get();
352         auto np = nm->GetProto();
353 
354         relatedItems->emplace_back("candidate", nm);
355 
356         if (IsSameProto(op, np)) {
357             return nm;
358         }
359     }
360 
361     panda_file::BaseClassItem *searchIn = kls->GetSuperClass();
362     if (klass == searchIn) {
363         LOG(FATAL, STATIC_LINKER) << "TryFindMethod Error: Current class same as SuperClass";
364     }
365     size_t idx = 0;
366     while (true) {
367         auto res = TryFindMethod(searchIn, fm, relatedItems);
368         if (std::holds_alternative<bool>(res)) {
369             if (std::get<bool>(res)) {
370                 return res;
371             }
372         }
373         if (std::holds_alternative<panda_file::MethodItem *>(res)) {
374             return res;
375         }
376         if (idx >= kls->GetInterfaces().size()) {
377             return false;
378         }
379         searchIn = kls->GetInterfaces()[idx++];
380     }
381     return false;
382 }
383 
IsSameProto(panda_file::ProtoItem * op1,panda_file::ProtoItem * op2)384 bool Context::IsSameProto(panda_file::ProtoItem *op1, panda_file::ProtoItem *op2)
385 {
386     if (op1->GetRefTypes().size() != op2->GetRefTypes().size()) {
387         return false;
388     }
389 
390     if (op1->GetShorty() != op2->GetShorty()) {
391         return false;
392     }
393 
394     for (size_t i = 0; i < op1->GetRefTypes().size(); i++) {
395         if (!IsSameType(op2->GetRefTypes()[i], op1->GetRefTypes()[i])) {
396             return false;
397         }
398     }
399 
400     return true;
401 }
402 
MergeForeignMethod(const panda_file::FileReader * reader,panda_file::ForeignMethodItem * fm)403 void Context::MergeForeignMethod(const panda_file::FileReader *reader, panda_file::ForeignMethodItem *fm)
404 {
405     ASSERT(knownItems_.find(fm) == knownItems_.end());
406     ASSERT(knownItems_.find(fm->GetClassItem()) != knownItems_.end());
407     auto clz = static_cast<panda_file::BaseClassItem *>(knownItems_[fm->GetClassItem()]);
408     std::vector<ark::static_linker::Context::ErrorDetail> details = {{"method", fm}};
409     auto res = TryFindMethod(clz, fm, &details);
410     if (std::holds_alternative<bool>(res) || conf_.remainsPartial.count(GetStr(clz->GetNameItem())) != 0) {
411         if (std::get<bool>(res) || conf_.remainsPartial.count(GetStr(clz->GetNameItem())) != 0) {
412             MergeForeignMethodCreate(reader, clz, fm);
413         } else {
414             Error("Unresolved method", details, reader);
415         }
416     } else {
417         ASSERT(std::holds_alternative<panda_file::MethodItem *>(res));
418         auto meth = std::get<panda_file::MethodItem *>(res);
419         if (meth->GetClassItem() != ClassFromOld(fm->GetClassItem())) {
420             LOG(DEBUG, STATIC_LINKER) << "Resolved method\n"
421                                       << ErrorToString({"old method", fm}, 1) << '\n'
422                                       << ErrorToString({"new method", meth}, 1);
423         }
424         knownItems_[fm] = meth;
425     }
426 }
427 
MergeForeignMethodCreate(const panda_file::FileReader * reader,panda_file::BaseClassItem * clz,panda_file::ForeignMethodItem * fm)428 void Context::MergeForeignMethodCreate(const panda_file::FileReader *reader, panda_file::BaseClassItem *clz,
429                                        panda_file::ForeignMethodItem *fm)
430 {
431     auto *fc = static_cast<panda_file::BaseClassItem *>(clz);
432     auto name = StringFromOld(fm->GetNameItem());
433     auto proto = GetProto(fm->GetProto()).first;
434     auto access = fm->GetAccessFlags();
435     auto [iter, was_inserted] = foreignMethods_.emplace(
436         std::piecewise_construct, std::forward_as_tuple(fc, name, proto, access), std::forward_as_tuple(nullptr));
437     if (was_inserted) {
438         iter->second = cont_.CreateItem<panda_file::ForeignMethodItem>(fc, name, proto, access);
439     } else {
440         result_.stats.deduplicatedForeigners++;
441     }
442     auto nfm = iter->second;
443     cameFrom_.emplace(nfm, reader);
444     knownItems_[fm] = nfm;
445 }
446 
TryFindField(panda_file::BaseClassItem * klass,const std::string & name,panda_file::TypeItem * expectedType,std::vector<panda_file::FieldItem * > * badCandidates)447 std::variant<std::monostate, panda_file::FieldItem *, panda_file::ForeignClassItem *> Context::TryFindField(
448     panda_file::BaseClassItem *klass, const std::string &name, panda_file::TypeItem *expectedType,
449     std::vector<panda_file::FieldItem *> *badCandidates)
450 {
451     if (klass == nullptr) {
452         return std::monostate {};
453     }
454     if (klass->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
455         return static_cast<panda_file::ForeignClassItem *>(klass);
456     }
457     auto kls = static_cast<panda_file::ClassItem *>(klass);
458     panda_file::FieldItem *newFld = nullptr;
459     kls->VisitFields([&](panda_file::BaseItem *bi) {
460         ASSERT(bi->GetItemType() == panda_file::ItemTypes::FIELD_ITEM);
461         auto fld = static_cast<panda_file::FieldItem *>(bi);
462         if (fld->GetNameItem()->GetData() != name) {
463             return true;
464         }
465         if (fld->GetTypeItem() != expectedType) {
466             if (badCandidates != nullptr) {
467                 badCandidates->push_back(fld);
468             }
469             return true;
470         }
471         newFld = fld;
472         return false;
473     });
474     if (newFld != nullptr) {
475         return newFld;
476     }
477 
478     if (klass == kls->GetSuperClass()) {
479         LOG(FATAL, STATIC_LINKER) << "TryFindField Error: Current class same as SuperClass";
480     }
481     return TryFindField(kls->GetSuperClass(), name, expectedType, badCandidates);
482 }
483 
HandleCandidates(const panda_file::FileReader * reader,const std::vector<panda_file::FieldItem * > & candidates,panda_file::ForeignFieldItem * ff)484 void Context::HandleCandidates(const panda_file::FileReader *reader,
485                                const std::vector<panda_file::FieldItem *> &candidates, panda_file::ForeignFieldItem *ff)
486 {
487     auto details = std::vector<ErrorDetail> {{"field", ff}};
488     for (const auto &c : candidates) {
489         details.emplace_back("candidate", c);
490     }
491     Error("Unresolved field", details, reader);
492 }
493 
MergeForeignField(const panda_file::FileReader * reader,panda_file::ForeignFieldItem * ff)494 void Context::MergeForeignField(const panda_file::FileReader *reader, panda_file::ForeignFieldItem *ff)
495 {
496     ASSERT(knownItems_.find(ff) == knownItems_.end());
497     ASSERT(knownItems_.find(ff->GetClassItem()) != knownItems_.end());
498 
499     auto clz = static_cast<panda_file::BaseClassItem *>(knownItems_[ff->GetClassItem()]);
500     if (clz->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
501         MergeForeignFieldCreate(reader, clz, ff);
502         return;
503     }
504 
505     ASSERT(clz->GetItemType() == panda_file::ItemTypes::CLASS_ITEM);
506     auto rclz = static_cast<panda_file::ClassItem *>(clz);
507     std::vector<panda_file::FieldItem *> candidates;
508     auto res = TryFindField(rclz, ff->GetNameItem()->GetData(), TypeFromOld(ff->GetTypeItem()), &candidates);
509 
510     auto visitor = [&reader, &clz, &ff, &candidates, this](auto el) {
511         using T = decltype(el);
512         if constexpr (std::is_same_v<T, std::monostate>) {
513             if (conf_.remainsPartial.count(GetStr(clz->GetNameItem())) != 0) {
514                 MergeForeignFieldCreate(reader, clz, ff);
515             } else {
516                 HandleCandidates(reader, candidates, ff);
517             }
518         } else if constexpr (std::is_same_v<T, panda_file::FieldItem *>) {
519             knownItems_[ff] = el;
520         } else {
521             ASSERT((std::is_same_v<T, panda_file::ForeignClassItem *>));
522             // el or klass? propagate field to base or not?
523             auto fieldKlass = el;
524             LOG(DEBUG, STATIC_LINKER) << "Propagating foreign field to base\n"
525                                       << ErrorToString({"field", ff}, 1) << '\n'
526                                       << ErrorToString({"new base", fieldKlass}, 1);
527             MergeForeignFieldCreate(reader, fieldKlass, ff);
528         }
529     };
530     std::visit(visitor, res);
531 }
532 
MergeForeignFieldCreate(const panda_file::FileReader * reader,panda_file::BaseClassItem * clz,panda_file::ForeignFieldItem * ff)533 void Context::MergeForeignFieldCreate(const panda_file::FileReader *reader, panda_file::BaseClassItem *clz,
534                                       panda_file::ForeignFieldItem *ff)
535 {
536     auto *fc = static_cast<panda_file::ForeignClassItem *>(clz);
537     auto name = StringFromOld(ff->GetNameItem());
538     auto typ = TypeFromOld(ff->GetTypeItem());
539     auto [iter, was_inserted] = foreignFields_.emplace(std::piecewise_construct, std::forward_as_tuple(fc, name, typ),
540                                                        std::forward_as_tuple(nullptr));
541     if (was_inserted) {
542         iter->second = cont_.CreateItem<panda_file::ForeignFieldItem>(fc, name, typ, ff->GetAccessFlags());
543     } else {
544         result_.stats.deduplicatedForeigners++;
545     }
546 
547     auto nff = iter->second;
548     cameFrom_.emplace(nff, reader);
549     knownItems_[ff] = nff;
550 }
551 
BreakProto(panda_file::ProtoItem * p)552 std::vector<panda_file::Type> Helpers::BreakProto(panda_file::ProtoItem *p)
553 {
554     auto &shorty = p->GetShorty();
555 
556     auto ret = std::vector<panda_file::Type>();
557     ret.reserve(panda_file::SHORTY_ELEM_PER16 * shorty.size());
558 
559     // SHORTY
560     size_t numElem = 0;
561     [[maybe_unused]] size_t numRefs = 0;
562     auto fetch = [idx = size_t(0), &shorty]() mutable {
563         ASSERT(idx < shorty.size());
564         return shorty[idx++];
565     };
566 
567     auto v = fetch();
568 
569     while (v != 0) {
570         auto shift = (numElem % panda_file::SHORTY_ELEM_PER16) * panda_file::SHORTY_ELEM_WIDTH;
571 
572         auto elem = (static_cast<decltype(shift)>(v) >> shift) & panda_file::SHORTY_ELEM_MASK;
573 
574         if (elem == 0) {
575             break;
576         }
577         auto asId = panda_file::Type::TypeId(elem);
578         ASSERT(asId != panda_file::Type::TypeId::INVALID);
579 
580         auto t = panda_file::Type(asId);
581         if (t.IsReference()) {
582             numRefs++;
583         }
584         static_assert(std::is_trivially_copyable_v<decltype(t)>);
585         ret.emplace_back(t);
586 
587         numElem++;
588 
589         if (numElem % panda_file::SHORTY_ELEM_PER16 == 0) {
590             v = fetch();
591         }
592     }
593 
594     ASSERT(numRefs == p->GetRefTypes().size());
595     ASSERT(!ret.empty());
596 
597     return ret;
598 }
599 
GetProto(panda_file::ProtoItem * p)600 std::pair<panda_file::ProtoItem *, std::vector<panda_file::MethodParamItem>> Context::GetProto(panda_file::ProtoItem *p)
601 {
602     auto &refs = p->GetRefTypes();
603 
604     auto typs = Helpers::BreakProto(p);
605 
606     panda_file::TypeItem *ret = nullptr;
607     auto params = std::vector<panda_file::MethodParamItem>();
608     params.reserve(typs.size() - 1);
609 
610     size_t numRefs = 0;
611 
612     for (auto const &t : typs) {
613         panda_file::TypeItem *ti;
614 
615         if (t.IsReference()) {
616             ASSERT(numRefs < refs.size());
617             ti = TypeFromOld(refs[numRefs++]);
618         } else {
619             ti = cont_.GetOrCreatePrimitiveTypeItem(t);
620         }
621 
622         if (ret == nullptr) {
623             ret = ti;
624         } else {
625             params.emplace_back(ti);
626         }
627     }
628 
629     ASSERT(numRefs == refs.size());
630     ASSERT(ret != nullptr);
631 
632     auto proto = cont_.GetOrCreateProtoItem(ret, params);
633 
634     return std::make_pair(proto, std::move(params));
635 }
636 
637 template <typename T, typename Getter, typename Adder>
AddAnnotationImpl(AddAnnotationImplData<T> ad,Getter getter,Adder adder)638 void Context::AddAnnotationImpl(AddAnnotationImplData<T> ad, Getter getter, Adder adder)
639 {
640     const auto &oldAnnotList = DerefPtrRef(getter(ad.oi));
641     for (size_t ind = ad.from; ind < oldAnnotList.size(); ind++) {
642         auto oldAnnot = oldAnnotList[ind];
643         auto mbNewAnnot = AnnotFromOld(oldAnnot);
644         if (std::holds_alternative<panda_file::AnnotationItem *>(mbNewAnnot)) {
645             adder(ad.ni, std::get<panda_file::AnnotationItem *>(mbNewAnnot));
646             continue;
647         }
648         const auto &ed = std::get<ErrorDetail>(mbNewAnnot);
649         if (ad.retriesLeft-- == 0) {
650             std::vector<ErrorDetail> details {ErrorDetail {"annotation", oldAnnot}, ed};
651             if constexpr (std::is_base_of_v<panda_file::BaseItem, std::decay_t<T>>) {
652                 details.emplace_back("old item", ad.oi);
653                 details.emplace_back("new item", ad.ni);
654             }
655             this->Error("can't transfer annotation", details, ad.reader);
656             return;
657         }
658 
659         LOG(DEBUG, STATIC_LINKER) << "defer annotation transferring due to" << ErrorToString(ed, 1);
660         ad.from = ind;
661         deferredFailedAnnotations_.emplace_back([=]() { AddAnnotationImpl<T>(ad, getter, adder); });
662         return;
663     }
664 }
665 
666 template <typename T>
TransferAnnotations(const panda_file::FileReader * reader,T * ni,T * oi)667 void Context::TransferAnnotations(const panda_file::FileReader *reader, T *ni, T *oi)
668 {
669     AddAnnotationImplData<T> data {reader, ni, oi, 0, 1};
670     // pointers to members break clang-12 on CI
671     AddAnnotationImpl(
672         data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetRuntimeAnnotations()); },
673         [](T *self, panda_file::AnnotationItem *an) { self->AddRuntimeAnnotation(an); });
674     AddAnnotationImpl(
675         data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetAnnotations()); },
676         [](T *self, panda_file::AnnotationItem *an) { self->AddAnnotation(an); });
677     AddAnnotationImpl(
678         data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetRuntimeTypeAnnotations()); },
679         [](T *self, panda_file::AnnotationItem *an) { self->AddRuntimeTypeAnnotation(an); });
680     AddAnnotationImpl(
681         data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetTypeAnnotations()); },
682         [](T *self, panda_file::AnnotationItem *an) { self->AddTypeAnnotation(an); });
683 }
684 
ArrayValueFromOld(panda_file::ValueItem * oi)685 std::variant<panda_file::ValueItem *, Context::ErrorDetail> Context::ArrayValueFromOld(panda_file::ValueItem *oi)
686 {
687     auto old = oi->GetAsArray();
688     auto its = old->GetItems();
689     for (auto &i : its) {
690         if (i.HasValue<panda_file::BaseItem *>()) {
691             auto vl = ScalarValueIdFromOld(i.GetIdItem());
692             if (std::holds_alternative<ErrorDetail>(vl)) {
693                 return std::move(std::get<ErrorDetail>(vl));
694             }
695             i = panda_file::ScalarValueItem(std::get<panda_file::BaseItem *>(vl));
696         }
697     }
698     return cont_.CreateItem<panda_file::ArrayValueItem>(old->GetComponentType(), std::move(its));
699 }
700 
ValueFromOld(panda_file::ValueItem * oi)701 std::variant<panda_file::ValueItem *, Context::ErrorDetail> Context::ValueFromOld(panda_file::ValueItem *oi)
702 {
703     using ValueType = panda_file::ValueItem::Type;
704     switch (oi->GetType()) {
705         case ValueType::INTEGER:
706             return cont_.GetOrCreateIntegerValueItem(oi->GetAsScalar()->GetValue<uint32_t>());
707         case ValueType::LONG:
708             return cont_.GetOrCreateLongValueItem(oi->GetAsScalar()->GetValue<uint64_t>());
709         case ValueType::FLOAT:
710             return cont_.GetOrCreateFloatValueItem(oi->GetAsScalar()->GetValue<float>());
711         case ValueType::DOUBLE:
712             return cont_.GetOrCreateDoubleValueItem(oi->GetAsScalar()->GetValue<double>());
713         case ValueType::ID: {
714             auto oldItem = oi->GetAsScalar()->GetIdItem();
715             ASSERT(oldItem != nullptr);
716             auto newItem = ScalarValueIdFromOld(oldItem);
717             if (std::holds_alternative<ErrorDetail>(newItem)) {
718                 return std::move(std::get<ErrorDetail>(newItem));
719             }
720             return cont_.GetOrCreateIdValueItem(std::get<panda_file::BaseItem *>(newItem));
721         }
722         case ValueType::ARRAY: {
723             return ArrayValueFromOld(oi);
724         }
725         default:
726             UNREACHABLE();
727     }
728 }
729 
AnnotFromOld(panda_file::AnnotationItem * oa)730 std::variant<panda_file::AnnotationItem *, Context::ErrorDetail> Context::AnnotFromOld(panda_file::AnnotationItem *oa)
731 {
732     if (auto iter = knownItems_.find(oa); iter != knownItems_.end()) {
733         return static_cast<panda_file::AnnotationItem *>(iter->second);
734     }
735 
736     using Elem = panda_file::AnnotationItem::Elem;
737 
738     const auto &oldElems = *oa->GetElements();
739     auto newElems = std::vector<Elem>();
740     newElems.reserve(oldElems.size());
741     for (const auto &oe : oldElems) {
742         auto name = StringFromOld(oe.GetName());
743         auto newVal = ValueFromOld(oe.GetValue());
744         if (std::holds_alternative<ErrorDetail>(newVal)) {
745             return std::move(std::get<ErrorDetail>(newVal));
746         }
747 
748         newElems.emplace_back(name, std::get<panda_file::ValueItem *>(newVal));
749     }
750 
751     auto clzIt = knownItems_.find(oa->GetClassItem());
752     ASSERT(clzIt != knownItems_.end());
753     ASSERT(clzIt->second->GetItemType() == panda_file::ItemTypes::CLASS_ITEM ||
754            clzIt->second->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM);
755     auto clz = static_cast<panda_file::BaseClassItem *>(clzIt->second);
756 
757     auto na = cont_.CreateItem<panda_file::AnnotationItem>(clz, std::move(newElems), oa->GetTags());
758     knownItems_.emplace(oa, na);
759     return na;
760 }
761 
ScalarValueIdFromOld(panda_file::BaseItem * oi)762 std::variant<panda_file::BaseItem *, Context::ErrorDetail> Context::ScalarValueIdFromOld(panda_file::BaseItem *oi)
763 {
764     if (auto newItemIt = knownItems_.find(static_cast<panda_file::IndexedItem *>(oi)); newItemIt != knownItems_.end()) {
765         return newItemIt->second;
766     }
767     if (oi->GetItemType() == panda_file::ItemTypes::STRING_ITEM) {
768         return StringFromOld(static_cast<panda_file::StringItem *>(oi));
769     }
770     if (oi->GetItemType() == panda_file::ItemTypes::ANNOTATION_ITEM) {
771         auto oa = static_cast<panda_file::AnnotationItem *>(oi);
772         using ReturnType = decltype(ScalarValueIdFromOld(oi));
773         return std::visit([](auto &&a) -> ReturnType { return std::forward<decltype(a)>(a); }, AnnotFromOld(oa));
774     }
775     return ErrorDetail("id", oi);
776 }
777 
Parse()778 void Context::Parse()
779 {
780     for (auto &codeData : codeDatas_) {
781         ProcessCodeData(patcher_, &codeData);
782     }
783     patcher_.ApplyDeps(this);
784 }
785 
ComputeLayout()786 void Context::ComputeLayout()
787 {
788     cont_.ComputeLayout();
789 }
790 
MethodFind(const std::string & className,const std::string & methodName,std::map<std::string,panda_file::BaseClassItem * > & classesMap)791 bool Context::MethodFind(const std::string &className, const std::string &methodName,
792                          std::map<std::string, panda_file::BaseClassItem *> &classesMap)
793 {
794     auto it = classesMap.find(className);
795     if (it != classesMap.end() && !it->second->IsForeign()) {
796         auto classItem = static_cast<panda_file::ClassItem *>(it->second);
797         auto methodNameItem = std::make_unique<panda_file::StringItem>(methodName);
798         auto range = classItem->FindMethod(methodNameItem.get());
799         if (range.first != range.second) {
800             classItem->SetDependencyMark();
801             return true;
802         }
803     }
804     return false;
805 }
806 
FileFind(const std::string & fileName,std::map<std::string,panda_file::BaseClassItem * > & classesMap)807 bool Context::FileFind(const std::string &fileName, std::map<std::string, panda_file::BaseClassItem *> &classesMap)
808 {
809     bool found = false;
810     auto dotPos = fileName.rfind('.');
811     if (dotPos == std::string::npos) {
812         return found;
813     }
814     auto suffix = fileName.substr(dotPos + 1);
815     if (suffix != "ets" && suffix != "sts") {
816         return found;
817     }
818 
819     auto fileNameFromEntry = fileName.substr(0, dotPos);
820     std::replace(fileNameFromEntry.begin(), fileNameFromEntry.end(), '.', '/');
821     fileNameFromEntry.insert(fileNameFromEntry.begin(), 'L');
822 
823     for (auto &entry : classesMap) {
824         auto classItemName = entry.first;
825         auto lastSlash = classItemName.rfind('/');
826         if (lastSlash != std::string::npos) {
827             auto fileNameFromClass = classItemName.substr(0, lastSlash);
828             if (fileNameFromEntry == fileNameFromClass) {
829                 entry.second->SetDependencyMark();
830                 found = true;
831             }
832         }
833     }
834     return found;
835 }
836 
HandleEntryDependencies()837 bool Context::HandleEntryDependencies()
838 {
839     auto &cm = *cont_.GetClassMap();
840     for (const auto &name : conf_.entryNames) {
841         auto dotPos = name.rfind('.');
842         auto firstSlash = name.find('/');
843         auto lastSlash = name.rfind('/');
844         if (dotPos != std::string::npos && dotPos != 0 && dotPos < name.size() - 1) {
845             // if entry is filename
846             if (FileFind(name, cm)) {
847                 continue;
848             }
849         } else if (firstSlash != std::string::npos && lastSlash != std::string::npos && lastSlash > firstSlash) {
850             // if entry is method
851             auto classNameFromEntry = name.substr(0, lastSlash);
852             classNameFromEntry.insert(classNameFromEntry.begin(), 'L');
853             classNameFromEntry += ";";
854             auto methodNameFromEntry = name.substr(lastSlash + 1);
855             if (MethodFind(classNameFromEntry, methodNameFromEntry, cm)) {
856                 continue;
857             }
858         }
859         // try look up as classname for last try
860         auto className = name + ";";
861         className.insert(className.begin(), 'L');
862         auto it = cm.find(className);
863         if (it != cm.end()) {
864             it->second->SetDependencyMark();
865         } else {
866             Error("Entry not found", {ErrorDetail("Entry", name)});
867             return false;
868         }
869     }
870     return true;
871 }
872 
TryDelete()873 void Context::TryDelete()
874 {
875     if (!conf_.allFileIsEntry && conf_.entryNames.empty()) {
876         return;
877     }
878     for (auto &name : conf_.entryNames) {
879         if (name == "*") {
880             conf_.allFileIsEntry = true;
881         }
882     }
883     auto &cm = *cont_.GetClassMap();
884     if (conf_.allFileIsEntry) {
885         for (auto &entry : cm) {
886             if (entry.second->GetItemType() == panda_file::ItemTypes::CLASS_ITEM) {
887                 entry.second->SetDependencyMark();
888             }
889         }
890     } else {
891         if (!HandleEntryDependencies()) {
892             return;
893         }
894     }
895     // All LiteralarrayMap should be reserved
896     cont_.MarkLiteralarrayMap();
897     patcher_.AddStringDependency();
898     // patches may not be available after items delete, try delete patches before delete items.
899     patcher_.TryDeletePatch();
900     // delete class index map.
901     for (auto it = cm.begin(); it != cm.end();) {
902         if (it->second->GetDependencyMark()) {
903             ++it;
904         } else {
905             it = cm.erase(it);
906         }
907     }
908     cont_.DeleteForeignItems();
909     cont_.DeleteItems();
910 }
911 
Patch()912 void Context::Patch()
913 {
914     patcher_.Patch({0, patcher_.GetSize()});
915 }
916 
ClassFromOld(panda_file::BaseClassItem * old)917 panda_file::BaseClassItem *Context::ClassFromOld(panda_file::BaseClassItem *old)
918 {
919     if (old == nullptr) {
920         return old;
921     }
922     auto &cm = *cont_.GetClassMap();
923     if (auto iter = cm.find(GetStr(old->GetNameItem())); iter != cm.end()) {
924         return iter->second;
925     }
926     UNREACHABLE();
927 }
928 
TypeFromOld(panda_file::TypeItem * old)929 panda_file::TypeItem *Context::TypeFromOld(panda_file::TypeItem *old)
930 {
931     const auto ty = old->GetType();
932     if (ty.IsPrimitive()) {
933         return cont_.GetOrCreatePrimitiveTypeItem(ty.GetId());
934     }
935     ASSERT(old->GetItemType() == panda_file::ItemTypes::CLASS_ITEM ||
936            old->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM);
937     return ClassFromOld(static_cast<panda_file::BaseClassItem *>(old));
938 }
939 
GetStr(const panda_file::StringItem * si)940 std::string Context::GetStr(const panda_file::StringItem *si)
941 {
942     return utf::Mutf8AsCString(reinterpret_cast<const uint8_t *>(si->GetData().data()));
943 }
944 
StringFromOld(const panda_file::StringItem * s)945 panda_file::StringItem *Context::StringFromOld(const panda_file::StringItem *s)
946 {
947     if (s == nullptr) {
948         return nullptr;
949     }
950     return cont_.GetOrCreateStringItem(GetStr(s));
951 }
952 }  // namespace ark::static_linker
953