• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <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 panda::static_linker {
41 
Merge()42 void Context::Merge()
43 {
44     // types can reference types only
45     // methods can reference types
46     // code items can reference everything
47 
48     // parse regular classes as forward declarations
49     // parse all foreign classes
50     // parse classes with fields and methods
51     // iterate all known indexed entities and create
52     //   appropriate mappings for them
53     //   oldEntity -> newEntity
54 
55     auto &cm = *cont_.GetClassMap();
56 
57     // set offsets of all entities
58     for (auto &reader : readers_) {
59         for (auto [o, i] : *reader.GetItems()) {
60             i->SetOffset(o.GetOffset());
61         }
62     }
63 
64     // add regular classes
65     for (auto &reader : readers_) {
66         auto *ic = reader.GetContainerPtr();
67         auto &classes = *ic->GetClassMap();
68 
69         knownItems_.reserve(reader.GetItems()->size());
70 
71         for (auto [name, i] : classes) {
72             if (i->IsForeign()) {
73                 continue;
74             }
75             ASSERT(name == GetStr(i->GetNameItem()));
76 
77             if (conf_.partial.count(name) == 0) {
78                 if (auto iter = cm.find(name); iter != cm.end()) {
79                     Error("Class redefinition", {ErrorDetail("original", iter->second)}, &reader);
80                 }
81             }
82             auto clz = cont_.GetOrCreateClassItem(name);
83             knownItems_[i] = clz;
84             cameFrom_.emplace(clz, &reader);
85         }
86     }
87 
88     // find all referenced foreign classes
89     for (auto &reader : readers_) {
90         auto *items = reader.GetItems();
91         for (auto &itPair : *items) {
92             auto *item = itPair.second;
93 
94             if (item->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
95                 auto *fc = static_cast<panda_file::ForeignClassItem *>(item);
96                 auto name = GetStr(fc->GetNameItem());
97                 if (auto iter = cm.find(name); iter != cm.end()) {
98                     knownItems_[fc] = iter->second;
99                 } else {
100                     auto clz = cont_.GetOrCreateForeignClassItem(name);
101                     cameFrom_.emplace(clz, &reader);
102                     knownItems_[fc] = clz;
103                 }
104             }
105         }
106     }
107 
108     // fill regular classes
109     for (auto &reader : readers_) {
110         auto *ic = reader.GetContainerPtr();
111         auto &classes = *ic->GetClassMap();
112 
113         for (auto [name, i] : classes) {
114             if (i->IsForeign()) {
115                 continue;
116             }
117             ASSERT(cm.find(name) != cm.end());
118             auto ni = static_cast<panda_file::ClassItem *>(cm[name]);
119             auto oi = static_cast<panda_file::ClassItem *>(i);
120             MergeClass(&reader, ni, oi);
121         }
122     }
123 
124     // scrap all indexable items
125     for (auto &reader : readers_) {
126         auto *items = reader.GetItems();
127         for (auto [offset, item] : *items) {
128             item->SetOffset(offset.GetOffset());
129 
130             switch (item->GetItemType()) {
131                 case panda_file::ItemTypes::FOREIGN_METHOD_ITEM:
132                     MergeForeignMethod(&reader, static_cast<panda_file::ForeignMethodItem *>(item));
133                     break;
134                 case panda_file::ItemTypes::FOREIGN_FIELD_ITEM:
135                     MergeForeignField(&reader, static_cast<panda_file::ForeignFieldItem *>(item));
136                     break;
137                 default:;
138             }
139         }
140     }
141 
142     for (const auto &f : deferredFailedAnnotations_) {
143         f();
144     }
145     deferredFailedAnnotations_.clear();
146 }
147 
MergeClass(const panda_file::FileReader * reader,panda_file::ClassItem * ni,panda_file::ClassItem * oi)148 void Context::MergeClass(const panda_file::FileReader *reader, panda_file::ClassItem *ni, panda_file::ClassItem *oi)
149 {
150     ni->SetAccessFlags(oi->GetAccessFlags());
151     ni->SetPGORank(oi->GetPGORank());
152     ni->SetSourceLang(oi->GetSourceLang());
153     ni->SetSourceFile(StringFromOld(oi->GetSourceFile()));
154 
155     ni->SetSuperClass(ClassFromOld(oi->GetSuperClass()));
156 
157     for (auto iface : oi->GetInterfaces()) {
158         ni->AddInterface(ClassFromOld(iface));
159     }
160 
161     TransferAnnotations(reader, ni, oi);
162 
163     oi->VisitFields([this, ni, reader](panda_file::BaseItem *mi) -> bool {
164         ASSERT(mi->GetItemType() == panda_file::ItemTypes::FIELD_ITEM);
165         MergeField(reader, ni, reinterpret_cast<panda_file::FieldItem *>(mi));
166         return true;
167     });
168 
169     oi->VisitMethods([this, ni, reader](panda_file::BaseItem *mi) -> bool {
170         ASSERT(mi->GetItemType() == panda_file::ItemTypes::METHOD_ITEM);
171         MergeMethod(reader, ni, reinterpret_cast<panda_file::MethodItem *>(mi));
172         return true;
173     });
174 }
175 
MergeField(const panda_file::FileReader * reader,panda_file::ClassItem * clz,panda_file::FieldItem * oi)176 void Context::MergeField(const panda_file::FileReader *reader, panda_file::ClassItem *clz, panda_file::FieldItem *oi)
177 {
178     auto ni = clz->AddField(StringFromOld(oi->GetNameItem()), TypeFromOld(oi->GetTypeItem()), oi->GetAccessFlags());
179     knownItems_[oi] = ni;
180     cameFrom_.emplace(ni, reader);
181 
182     TransferAnnotations(reader, ni, oi);
183 }
184 
MergeMethod(const panda_file::FileReader * reader,panda_file::ClassItem * clz,panda_file::MethodItem * oi)185 void Context::MergeMethod(const panda_file::FileReader *reader, panda_file::ClassItem *clz, panda_file::MethodItem *oi)
186 {
187     auto [proto, params] = GetProto(oi->GetProto());
188     auto &oldParams = oi->GetParams();
189     auto *ni = clz->AddMethod(StringFromOld(oi->GetNameItem()), proto, oi->GetAccessFlags(), params);
190     knownItems_[oi] = ni;
191     cameFrom_.emplace(ni, reader);
192     ni->SetProfileSize(oi->GetProfileSize());
193 
194     for (size_t i = 0; i < oldParams.size(); i++) {
195         TransferAnnotations(reader, &ni->GetParams()[i], &oldParams[i]);
196     }
197 
198     TransferAnnotations(reader, ni, oi);
199 
200     if (ni->HasRuntimeParamAnnotations()) {
201         cont_.CreateItem<panda_file::ParamAnnotationsItem>(ni, true);
202     }
203 
204     if (ni->HasParamAnnotations()) {
205         cont_.CreateItem<panda_file::ParamAnnotationsItem>(ni, false);
206     }
207 
208     auto shouldSave = false;
209     std::vector<uint8_t> *instructions = nullptr;
210 
211     auto patchLnp = false;
212 
213     if (auto odbg = oi->GetDebugInfo(); !conf_.stripDebugInfo && odbg != nullptr) {
214         panda_file::LineNumberProgramItem *lnpItem {};
215 
216         auto oldProgram = odbg->GetLineNumberProgram();
217         if (auto old = knownItems_.find(oldProgram); old != knownItems_.end()) {
218             lnpItem = static_cast<panda_file::LineNumberProgramItem *>(old->second);
219             cont_.IncRefLineNumberProgramItem(lnpItem);
220         } else {
221             lnpItem = cont_.CreateLineNumberProgramItem();
222             knownItems_.emplace(oldProgram, lnpItem);
223 
224             shouldSave = true;
225             result_.stats.debugCount++;
226             patchLnp = true;
227         }
228 
229         auto *ndbg = cont_.CreateItem<panda_file::DebugInfoItem>(lnpItem);
230         ndbg->SetLineNumber(odbg->GetLineNumber());
231         ni->SetDebugInfo(ndbg);
232         for (auto *s : *odbg->GetParameters()) {
233             ndbg->AddParameter(StringFromOld(s));
234         }
235     }
236 
237     if (auto oci = oi->GetCode(); oci != nullptr) {
238         shouldSave = true;
239         result_.stats.codeCount++;
240 
241         auto nci = cont_.CreateItem<panda_file::CodeItem>();
242         ni->SetCode(nci);
243         nci->SetPGORank(oci->GetPGORank());
244         nci->SetNumArgs(oci->GetNumArgs());
245         nci->SetNumVregs(oci->GetNumVregs());
246         *nci->GetInstructions() = *oci->GetInstructions();
247         instructions = nci->GetInstructions();
248         nci->SetNumInstructions(oci->GetNumInstructions());
249 
250         for (const auto &otb : oci->GetTryBlocks()) {
251             auto ncbs = std::vector<panda_file::CodeItem::CatchBlock>();
252             const auto &ocbs = otb.GetCatchBlocks();
253             ncbs.reserve(ocbs.size());
254             for (const auto &ocb : ocbs) {
255                 ASSERT(ocb.GetMethod() == oi);
256                 auto excKlass = ClassFromOld(ocb.GetType());
257                 if (excKlass != nullptr) {
258                     ni->AddIndexDependency(excKlass);
259                 }
260                 ncbs.emplace_back(ni, excKlass, ocb.GetHandlerPc(), ocb.GetCodeSize());
261             }
262             auto ntb = panda_file::CodeItem::TryBlock(otb.GetStartPc(), otb.GetLength(), ncbs);
263             nci->AddTryBlock(ntb);
264         }
265     }
266 
267     if (shouldSave) {
268         codeDatas_.push_back(CodeData {instructions, oi, ni, reader, patchLnp});
269     }
270 }
271 
IsSameType(panda::panda_file::TypeItem * nevv,panda::panda_file::TypeItem * old)272 bool Context::IsSameType(panda::panda_file::TypeItem *nevv, panda::panda_file::TypeItem *old)
273 {
274     if (nevv->GetType().IsPrimitive()) {
275         if (!old->GetType().IsPrimitive()) {
276             return false;
277         }
278         return nevv->GetType() == old->GetType();
279     }
280 
281     auto iter = knownItems_.find(old);
282     return iter != knownItems_.end() && iter->second == nevv;
283 }
284 
TryFindMethod(panda_file::BaseClassItem * klass,panda_file::ForeignMethodItem * fm,std::vector<ErrorDetail> * relatedItems)285 std::variant<bool, panda_file::MethodItem *> Context::TryFindMethod(panda_file::BaseClassItem *klass,
286                                                                     panda_file::ForeignMethodItem *fm,
287                                                                     std::vector<ErrorDetail> *relatedItems)
288 {
289     if (klass == nullptr) {
290         return false;
291     }
292     if (klass->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
293         return true;
294     }
295     ASSERT(klass->GetItemType() == panda_file::ItemTypes::CLASS_ITEM);
296     auto kls = static_cast<panda_file::ClassItem *>(klass);
297     auto op = fm->GetProto();
298 
299     auto [new_meth_beg, new_meth_end] = kls->FindMethod(fm->GetNameItem());
300 
301     for (auto beg = new_meth_beg; beg != new_meth_end; ++beg) {
302         auto nm = beg->get();
303         auto np = nm->GetProto();
304 
305         relatedItems->emplace_back("candidate", nm);
306 
307         if (op->GetRefTypes().size() != np->GetRefTypes().size()) {
308             continue;
309         }
310 
311         if (op->GetShorty() != np->GetShorty()) {
312             continue;
313         }
314 
315         bool ok = true;
316         for (size_t i = 0; i < op->GetRefTypes().size(); i++) {
317             if (!IsSameType(np->GetRefTypes()[i], op->GetRefTypes()[i])) {
318                 ok = false;
319                 break;
320             }
321         }
322 
323         if (ok) {
324             return nm;
325         }
326     }
327 
328     panda_file::BaseClassItem *searchIn = kls->GetSuperClass();
329     size_t idx = 0;
330     while (true) {
331         auto res = TryFindMethod(searchIn, fm, relatedItems);
332         if (std::holds_alternative<bool>(res)) {
333             if (std::get<bool>(res)) {
334                 return res;
335             }
336         }
337         if (std::holds_alternative<panda_file::MethodItem *>(res)) {
338             return res;
339         }
340         if (idx >= kls->GetInterfaces().size()) {
341             return false;
342         }
343         searchIn = kls->GetInterfaces()[idx++];
344     }
345     return false;
346 }
347 
MergeForeignMethod(const panda_file::FileReader * reader,panda_file::ForeignMethodItem * fm)348 void Context::MergeForeignMethod(const panda_file::FileReader *reader, panda_file::ForeignMethodItem *fm)
349 {
350     ASSERT(knownItems_.find(fm) == knownItems_.end());
351     ASSERT(knownItems_.find(fm->GetClassItem()) != knownItems_.end());
352     auto clz = static_cast<panda_file::BaseClassItem *>(knownItems_[fm->GetClassItem()]);
353     std::vector<panda::static_linker::Context::ErrorDetail> details = {{"method", fm}};
354     auto res = TryFindMethod(clz, fm, &details);
355     if (std::holds_alternative<bool>(res) || conf_.remainsPartial.count(GetStr(clz->GetNameItem())) != 0) {
356         if (std::get<bool>(res) || conf_.remainsPartial.count(GetStr(clz->GetNameItem())) != 0) {
357             MergeForeignMethodCreate(reader, clz, fm);
358         } else {
359             Error("Unresolved method", details, reader);
360         }
361     } else {
362         ASSERT(std::holds_alternative<panda_file::MethodItem *>(res));
363         auto meth = std::get<panda_file::MethodItem *>(res);
364         if (meth->GetClassItem() != ClassFromOld(fm->GetClassItem())) {
365             LOG(DEBUG, STATIC_LINKER) << "Resolved method\n"
366                                       << ErrorToString({"old method", fm}, 1) << '\n'
367                                       << ErrorToString({"new method", meth}, 1);
368         }
369         knownItems_[fm] = meth;
370     }
371 }
372 
MergeForeignMethodCreate(const panda_file::FileReader * reader,panda_file::BaseClassItem * clz,panda_file::ForeignMethodItem * fm)373 void Context::MergeForeignMethodCreate(const panda_file::FileReader *reader, panda_file::BaseClassItem *clz,
374                                        panda_file::ForeignMethodItem *fm)
375 {
376     auto *fc = static_cast<panda_file::BaseClassItem *>(clz);
377     auto name = StringFromOld(fm->GetNameItem());
378     auto proto = GetProto(fm->GetProto()).first;
379     auto access = fm->GetAccessFlags();
380     auto [iter, was_inserted] = foreignMethods_.emplace(
381         std::piecewise_construct, std::forward_as_tuple(fc, name, proto, access), std::forward_as_tuple(nullptr));
382     if (was_inserted) {
383         iter->second = cont_.CreateItem<panda_file::ForeignMethodItem>(fc, name, proto, access);
384     } else {
385         result_.stats.deduplicatedForeigners++;
386     }
387     auto nfm = iter->second;
388     cameFrom_.emplace(nfm, reader);
389     knownItems_[fm] = nfm;
390 }
391 
TryFindField(panda_file::BaseClassItem * klass,const std::string & name,panda_file::TypeItem * expectedType,std::vector<panda_file::FieldItem * > * badCandidates)392 std::variant<std::monostate, panda_file::FieldItem *, panda_file::ForeignClassItem *> Context::TryFindField(
393     panda_file::BaseClassItem *klass, const std::string &name, panda_file::TypeItem *expectedType,
394     std::vector<panda_file::FieldItem *> *badCandidates)
395 {
396     if (klass == nullptr) {
397         return std::monostate {};
398     }
399     if (klass->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
400         return static_cast<panda_file::ForeignClassItem *>(klass);
401     }
402     auto kls = static_cast<panda_file::ClassItem *>(klass);
403     panda_file::FieldItem *newFld = nullptr;
404     kls->VisitFields([&](panda_file::BaseItem *bi) {
405         ASSERT(bi->GetItemType() == panda_file::ItemTypes::FIELD_ITEM);
406         auto fld = static_cast<panda_file::FieldItem *>(bi);
407         if (fld->GetNameItem()->GetData() != name) {
408             return true;
409         }
410         if (fld->GetTypeItem() != expectedType) {
411             if (badCandidates != nullptr) {
412                 badCandidates->push_back(fld);
413             }
414             return true;
415         }
416         newFld = fld;
417         return false;
418     });
419     if (newFld != nullptr) {
420         return newFld;
421     }
422     return TryFindField(kls->GetSuperClass(), name, expectedType, badCandidates);
423 }
424 
MergeForeignField(const panda_file::FileReader * reader,panda_file::ForeignFieldItem * ff)425 void Context::MergeForeignField(const panda_file::FileReader *reader, panda_file::ForeignFieldItem *ff)
426 {
427     ASSERT(knownItems_.find(ff) == knownItems_.end());
428     ASSERT(knownItems_.find(ff->GetClassItem()) != knownItems_.end());
429     auto clz = static_cast<panda_file::BaseClassItem *>(knownItems_[ff->GetClassItem()]);
430     if (clz->GetItemType() != panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
431         ASSERT(clz->GetItemType() == panda_file::ItemTypes::CLASS_ITEM);
432         auto rclz = static_cast<panda_file::ClassItem *>(clz);
433         std::vector<panda_file::FieldItem *> candidates;
434         auto res = TryFindField(rclz, ff->GetNameItem()->GetData(), TypeFromOld(ff->GetTypeItem()), &candidates);
435 
436         std::visit(
437             [&](auto el) {
438                 using T = decltype(el);
439                 if constexpr (std::is_same_v<T, std::monostate>) {
440                     if (conf_.remainsPartial.count(GetStr(clz->GetNameItem())) != 0) {
441                         MergeForeignFieldCreate(reader, clz, ff);
442                     } else {
443                         auto details = std::vector<ErrorDetail> {{"field", ff}};
444                         for (const auto &c : candidates) {
445                             details.emplace_back("candidate", c);
446                         }
447                         Error("Unresolved field", details, reader);
448                     }
449                 } else if constexpr (std::is_same_v<T, panda_file::FieldItem *>) {
450                     knownItems_[ff] = el;
451                 } else {
452                     ASSERT((std::is_same_v<T, panda_file::ForeignClassItem *>));
453                     // el or klass? propagate field to base or not?
454                     auto fieldKlass = el;
455                     LOG(DEBUG, STATIC_LINKER) << "Propagating foreign field to base\n"
456                                               << ErrorToString({"field", ff}, 1) << '\n'
457                                               << ErrorToString({"new base", fieldKlass}, 1);
458                     MergeForeignFieldCreate(reader, fieldKlass, ff);
459                 }
460             },
461             res);
462     } else {
463         MergeForeignFieldCreate(reader, clz, ff);
464     }
465 }
466 
MergeForeignFieldCreate(const panda_file::FileReader * reader,panda_file::BaseClassItem * clz,panda_file::ForeignFieldItem * ff)467 void Context::MergeForeignFieldCreate(const panda_file::FileReader *reader, panda_file::BaseClassItem *clz,
468                                       panda_file::ForeignFieldItem *ff)
469 {
470     auto *fc = static_cast<panda_file::ForeignClassItem *>(clz);
471     auto name = StringFromOld(ff->GetNameItem());
472     auto typ = TypeFromOld(ff->GetTypeItem());
473     auto [iter, was_inserted] = foreignFields_.emplace(std::piecewise_construct, std::forward_as_tuple(fc, name, typ),
474                                                        std::forward_as_tuple(nullptr));
475     if (was_inserted) {
476         iter->second = cont_.CreateItem<panda_file::ForeignFieldItem>(fc, name, typ);
477     } else {
478         result_.stats.deduplicatedForeigners++;
479     }
480 
481     auto nff = iter->second;
482     cameFrom_.emplace(nff, reader);
483     knownItems_[ff] = nff;
484 }
485 
BreakProto(panda_file::ProtoItem * p)486 std::vector<panda_file::Type> Helpers::BreakProto(panda_file::ProtoItem *p)
487 {
488     auto &shorty = p->GetShorty();
489 
490     auto ret = std::vector<panda_file::Type>();
491     ret.reserve(panda_file::SHORTY_ELEM_PER16 * shorty.size());
492 
493     // SHORTY
494     size_t numElem = 0;
495     size_t numRefs = 0;
496     auto fetch = [idx = size_t(0), &shorty]() mutable {
497         ASSERT(idx < shorty.size());
498         return shorty[idx++];
499     };
500 
501     auto v = fetch();
502 
503     while (v != 0) {
504         auto shift = (numElem % panda_file::SHORTY_ELEM_PER16) * panda_file::SHORTY_ELEM_WIDTH;
505 
506         auto elem = (static_cast<decltype(shift)>(v) >> shift) & panda_file::SHORTY_ELEM_MASK;
507 
508         if (elem == 0) {
509             break;
510         }
511         auto asId = panda_file::Type::TypeId(elem);
512         ASSERT(asId != panda_file::Type::TypeId::INVALID);
513 
514         auto t = panda_file::Type(asId);
515         if (t.IsReference()) {
516             numRefs++;
517         }
518         static_assert(std::is_trivially_copyable_v<decltype(t)>);
519         ret.emplace_back(t);
520 
521         numElem++;
522 
523         if (numElem % panda_file::SHORTY_ELEM_PER16 == 0) {
524             v = fetch();
525         }
526     }
527 
528     ASSERT(numRefs == p->GetRefTypes().size());
529     ASSERT(!ret.empty());
530 
531     return ret;
532 }
533 
GetProto(panda_file::ProtoItem * p)534 std::pair<panda_file::ProtoItem *, std::vector<panda_file::MethodParamItem>> Context::GetProto(panda_file::ProtoItem *p)
535 {
536     auto &refs = p->GetRefTypes();
537 
538     auto typs = Helpers::BreakProto(p);
539 
540     panda_file::TypeItem *ret = nullptr;
541     auto params = std::vector<panda_file::MethodParamItem>();
542     params.reserve(typs.size() - 1);
543 
544     size_t numRefs = 0;
545 
546     for (auto const &t : typs) {
547         panda_file::TypeItem *ti;
548 
549         if (t.IsReference()) {
550             ASSERT(numRefs < refs.size());
551             ti = TypeFromOld(refs[numRefs++]);
552         } else {
553             ti = cont_.GetOrCreatePrimitiveTypeItem(t);
554         }
555 
556         if (ret == nullptr) {
557             ret = ti;
558         } else {
559             params.emplace_back(ti);
560         }
561     }
562 
563     ASSERT(numRefs == refs.size());
564     ASSERT(ret != nullptr);
565 
566     auto proto = cont_.GetOrCreateProtoItem(ret, params);
567 
568     return std::make_pair(proto, std::move(params));
569 }
570 
571 template <typename T, typename Getter, typename Adder>
AddAnnotationImpl(AddAnnotationImplData<T> ad,Getter getter,Adder adder)572 void Context::AddAnnotationImpl(AddAnnotationImplData<T> ad, Getter getter, Adder adder)
573 {
574     const auto &oldAnnotList = DerefPtrRef(getter(ad.oi));
575     for (size_t ind = ad.from; ind < oldAnnotList.size(); ind++) {
576         auto oldAnnot = oldAnnotList[ind];
577         auto mbNewAnnot = AnnotFromOld(oldAnnot);
578         if (std::holds_alternative<panda_file::AnnotationItem *>(mbNewAnnot)) {
579             adder(ad.ni, std::get<panda_file::AnnotationItem *>(mbNewAnnot));
580             return;
581         }
582         const auto &ed = std::get<ErrorDetail>(mbNewAnnot);
583         if (ad.retriesLeft-- == 0) {
584             std::vector<ErrorDetail> details {ErrorDetail {"annotation", oldAnnot}, ed};
585             if constexpr (std::is_base_of_v<panda_file::BaseItem, std::decay_t<T>>) {
586                 details.emplace_back("old item", ad.oi);
587                 details.emplace_back("new item", ad.ni);
588             }
589             this->Error("can't transfer annotation", details, ad.reader);
590             return;
591         }
592 
593         LOG(DEBUG, STATIC_LINKER) << "defer annotation transferring due to" << ErrorToString(ed, 1);
594         ad.from = ind;
595         deferredFailedAnnotations_.emplace_back([=]() { AddAnnotationImpl<T>(ad, getter, adder); });
596         return;
597     }
598 }
599 
600 template <typename T>
TransferAnnotations(const panda_file::FileReader * reader,T * ni,T * oi)601 void Context::TransferAnnotations(const panda_file::FileReader *reader, T *ni, T *oi)
602 {
603     AddAnnotationImplData<T> data {reader, ni, oi, 0, 1};
604     // pointers to members break clang-12 on CI
605     AddAnnotationImpl(
606         data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetRuntimeAnnotations()); },
607         [](T *self, panda_file::AnnotationItem *an) { self->AddRuntimeAnnotation(an); });
608     AddAnnotationImpl(
609         data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetAnnotations()); },
610         [](T *self, panda_file::AnnotationItem *an) { self->AddAnnotation(an); });
611     AddAnnotationImpl(
612         data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetRuntimeTypeAnnotations()); },
613         [](T *self, panda_file::AnnotationItem *an) { self->AddRuntimeTypeAnnotation(an); });
614     AddAnnotationImpl(
615         data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetTypeAnnotations()); },
616         [](T *self, panda_file::AnnotationItem *an) { self->AddTypeAnnotation(an); });
617 }
618 
AnnotFromOld(panda_file::AnnotationItem * oa)619 std::variant<panda_file::AnnotationItem *, Context::ErrorDetail> Context::AnnotFromOld(panda_file::AnnotationItem *oa)
620 {
621     if (auto iter = knownItems_.find(oa); iter != knownItems_.end()) {
622         return static_cast<panda_file::AnnotationItem *>(iter->second);
623     }
624 
625     using Elem = panda_file::AnnotationItem::Elem;
626 
627     const auto &oldElems = *oa->GetElements();
628     auto newElems = std::vector<Elem>();
629     newElems.reserve(oldElems.size());
630     for (const auto &oe : oldElems) {
631         auto name = StringFromOld(oe.GetName());
632         auto *oldVal = oe.GetValue();
633         panda_file::ValueItem *newVal {};
634 
635         using ValueType = panda_file::ValueItem::Type;
636         switch (oldVal->GetType()) {
637             case ValueType::INTEGER:
638                 newVal = cont_.GetOrCreateIntegerValueItem(oldVal->GetAsScalar()->GetValue<uint32_t>());
639                 break;
640             case ValueType::LONG:
641                 newVal = cont_.GetOrCreateLongValueItem(oldVal->GetAsScalar()->GetValue<uint64_t>());
642                 break;
643             case ValueType::FLOAT:
644                 newVal = cont_.GetOrCreateFloatValueItem(oldVal->GetAsScalar()->GetValue<float>());
645                 break;
646             case ValueType::DOUBLE:
647                 newVal = cont_.GetOrCreateDoubleValueItem(oldVal->GetAsScalar()->GetValue<double>());
648                 break;
649             case ValueType::ID: {
650                 auto oldItem = oldVal->GetAsScalar()->GetIdItem();
651                 ASSERT(oldItem != nullptr);
652                 auto newItem = ScalarValueIdFromOld(oldItem);
653                 if (std::holds_alternative<ErrorDetail>(newItem)) {
654                     return std::move(std::get<ErrorDetail>(newItem));
655                 }
656                 newVal = cont_.GetOrCreateIdValueItem(std::get<panda_file::BaseItem *>(newItem));
657                 break;
658             }
659             case ValueType::ARRAY: {
660                 auto old = oldVal->GetAsArray();
661                 auto its = old->GetItems();
662                 for (auto &i : its) {
663                     if (i.HasValue<panda_file::BaseItem *>()) {
664                         auto vl = ScalarValueIdFromOld(i.GetIdItem());
665                         if (std::holds_alternative<ErrorDetail>(vl)) {
666                             return std::move(std::get<ErrorDetail>(vl));
667                         }
668                         i = panda_file::ScalarValueItem(std::get<panda_file::BaseItem *>(vl));
669                     }
670                 }
671                 newVal = cont_.CreateItem<panda_file::ArrayValueItem>(old->GetComponentType(), std::move(its));
672                 break;
673             }
674             default:
675                 UNREACHABLE();
676         }
677 
678         ASSERT(newVal != nullptr);
679         newElems.emplace_back(name, newVal);
680     }
681 
682     auto clzIt = knownItems_.find(oa->GetClassItem());
683     ASSERT(clzIt != knownItems_.end());
684     ASSERT(clzIt->second->GetItemType() == panda_file::ItemTypes::CLASS_ITEM ||
685            clzIt->second->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM);
686     auto clz = static_cast<panda_file::BaseClassItem *>(clzIt->second);
687 
688     auto na = cont_.CreateItem<panda_file::AnnotationItem>(clz, std::move(newElems), oa->GetTags());
689     knownItems_.emplace(oa, na);
690     return na;
691 }
692 
ScalarValueIdFromOld(panda_file::BaseItem * oi)693 std::variant<panda_file::BaseItem *, Context::ErrorDetail> Context::ScalarValueIdFromOld(panda_file::BaseItem *oi)
694 {
695     if (auto newItemIt = knownItems_.find(static_cast<panda_file::IndexedItem *>(oi)); newItemIt != knownItems_.end()) {
696         return newItemIt->second;
697     }
698     if (oi->GetItemType() == panda_file::ItemTypes::STRING_ITEM) {
699         return StringFromOld(static_cast<panda_file::StringItem *>(oi));
700     }
701     if (oi->GetItemType() == panda_file::ItemTypes::ANNOTATION_ITEM) {
702         auto oa = static_cast<panda_file::AnnotationItem *>(oi);
703         using ReturnType = decltype(ScalarValueIdFromOld(oi));
704         return std::visit([](auto &&a) -> ReturnType { return std::forward<decltype(a)>(a); }, AnnotFromOld(oa));
705     }
706     return ErrorDetail("id", oi);
707 }
708 
Parse()709 void Context::Parse()
710 {
711     for (auto &codeData : codeDatas_) {
712         ProcessCodeData(patcher_, &codeData);
713     }
714     patcher_.ApplyDeps(this);
715 }
716 
ComputeLayout()717 void Context::ComputeLayout()
718 {
719     cont_.ComputeLayout();
720 }
721 
Patch()722 void Context::Patch()
723 {
724     patcher_.Patch({0, patcher_.GetSize()});
725 }
726 
ClassFromOld(panda_file::BaseClassItem * old)727 panda_file::BaseClassItem *Context::ClassFromOld(panda_file::BaseClassItem *old)
728 {
729     if (old == nullptr) {
730         return old;
731     }
732     auto &cm = *cont_.GetClassMap();
733     if (auto iter = cm.find(GetStr(old->GetNameItem())); iter != cm.end()) {
734         return iter->second;
735     }
736     UNREACHABLE();
737 }
738 
TypeFromOld(panda_file::TypeItem * old)739 panda_file::TypeItem *Context::TypeFromOld(panda_file::TypeItem *old)
740 {
741     const auto ty = old->GetType();
742     if (ty.IsPrimitive()) {
743         return cont_.GetOrCreatePrimitiveTypeItem(ty.GetId());
744     }
745     ASSERT(old->GetItemType() == panda_file::ItemTypes::CLASS_ITEM ||
746            old->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM);
747     return ClassFromOld(static_cast<panda_file::BaseClassItem *>(old));
748 }
749 
GetStr(const panda_file::StringItem * si)750 std::string Context::GetStr(const panda_file::StringItem *si)
751 {
752     return utf::Mutf8AsCString(reinterpret_cast<const uint8_t *>(si->GetData().data()));
753 }
754 
StringFromOld(const panda_file::StringItem * s)755 panda_file::StringItem *Context::StringFromOld(const panda_file::StringItem *s)
756 {
757     if (s == nullptr) {
758         return nullptr;
759     }
760     return cont_.GetOrCreateStringItem(GetStr(s));
761 }
762 }  // namespace panda::static_linker
763