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