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
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 size_t idx = 0;
363 while (true) {
364 auto res = TryFindMethod(searchIn, fm, relatedItems);
365 if (std::holds_alternative<bool>(res)) {
366 if (std::get<bool>(res)) {
367 return res;
368 }
369 }
370 if (std::holds_alternative<panda_file::MethodItem *>(res)) {
371 return res;
372 }
373 if (idx >= kls->GetInterfaces().size()) {
374 return false;
375 }
376 searchIn = kls->GetInterfaces()[idx++];
377 }
378 return false;
379 }
380
IsSameProto(panda_file::ProtoItem * op1,panda_file::ProtoItem * op2)381 bool Context::IsSameProto(panda_file::ProtoItem *op1, panda_file::ProtoItem *op2)
382 {
383 if (op1->GetRefTypes().size() != op2->GetRefTypes().size()) {
384 return false;
385 }
386
387 if (op1->GetShorty() != op2->GetShorty()) {
388 return false;
389 }
390
391 for (size_t i = 0; i < op1->GetRefTypes().size(); i++) {
392 if (!IsSameType(op2->GetRefTypes()[i], op1->GetRefTypes()[i])) {
393 return false;
394 }
395 }
396
397 return true;
398 }
399
MergeForeignMethod(const panda_file::FileReader * reader,panda_file::ForeignMethodItem * fm)400 void Context::MergeForeignMethod(const panda_file::FileReader *reader, panda_file::ForeignMethodItem *fm)
401 {
402 ASSERT(knownItems_.find(fm) == knownItems_.end());
403 ASSERT(knownItems_.find(fm->GetClassItem()) != knownItems_.end());
404 auto clz = static_cast<panda_file::BaseClassItem *>(knownItems_[fm->GetClassItem()]);
405 std::vector<ark::static_linker::Context::ErrorDetail> details = {{"method", fm}};
406 auto res = TryFindMethod(clz, fm, &details);
407 if (std::holds_alternative<bool>(res) || conf_.remainsPartial.count(GetStr(clz->GetNameItem())) != 0) {
408 if (std::get<bool>(res) || conf_.remainsPartial.count(GetStr(clz->GetNameItem())) != 0) {
409 MergeForeignMethodCreate(reader, clz, fm);
410 } else {
411 Error("Unresolved method", details, reader);
412 }
413 } else {
414 ASSERT(std::holds_alternative<panda_file::MethodItem *>(res));
415 auto meth = std::get<panda_file::MethodItem *>(res);
416 if (meth->GetClassItem() != ClassFromOld(fm->GetClassItem())) {
417 LOG(DEBUG, STATIC_LINKER) << "Resolved method\n"
418 << ErrorToString({"old method", fm}, 1) << '\n'
419 << ErrorToString({"new method", meth}, 1);
420 }
421 knownItems_[fm] = meth;
422 }
423 }
424
MergeForeignMethodCreate(const panda_file::FileReader * reader,panda_file::BaseClassItem * clz,panda_file::ForeignMethodItem * fm)425 void Context::MergeForeignMethodCreate(const panda_file::FileReader *reader, panda_file::BaseClassItem *clz,
426 panda_file::ForeignMethodItem *fm)
427 {
428 auto *fc = static_cast<panda_file::BaseClassItem *>(clz);
429 auto name = StringFromOld(fm->GetNameItem());
430 auto proto = GetProto(fm->GetProto()).first;
431 auto access = fm->GetAccessFlags();
432 auto [iter, was_inserted] = foreignMethods_.emplace(
433 std::piecewise_construct, std::forward_as_tuple(fc, name, proto, access), std::forward_as_tuple(nullptr));
434 if (was_inserted) {
435 iter->second = cont_.CreateItem<panda_file::ForeignMethodItem>(fc, name, proto, access);
436 } else {
437 result_.stats.deduplicatedForeigners++;
438 }
439 auto nfm = iter->second;
440 cameFrom_.emplace(nfm, reader);
441 knownItems_[fm] = nfm;
442 }
443
TryFindField(panda_file::BaseClassItem * klass,const std::string & name,panda_file::TypeItem * expectedType,std::vector<panda_file::FieldItem * > * badCandidates)444 std::variant<std::monostate, panda_file::FieldItem *, panda_file::ForeignClassItem *> Context::TryFindField(
445 panda_file::BaseClassItem *klass, const std::string &name, panda_file::TypeItem *expectedType,
446 std::vector<panda_file::FieldItem *> *badCandidates)
447 {
448 if (klass == nullptr) {
449 return std::monostate {};
450 }
451 if (klass->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
452 return static_cast<panda_file::ForeignClassItem *>(klass);
453 }
454 auto kls = static_cast<panda_file::ClassItem *>(klass);
455 panda_file::FieldItem *newFld = nullptr;
456 kls->VisitFields([&](panda_file::BaseItem *bi) {
457 ASSERT(bi->GetItemType() == panda_file::ItemTypes::FIELD_ITEM);
458 auto fld = static_cast<panda_file::FieldItem *>(bi);
459 if (fld->GetNameItem()->GetData() != name) {
460 return true;
461 }
462 if (fld->GetTypeItem() != expectedType) {
463 if (badCandidates != nullptr) {
464 badCandidates->push_back(fld);
465 }
466 return true;
467 }
468 newFld = fld;
469 return false;
470 });
471 if (newFld != nullptr) {
472 return newFld;
473 }
474 return TryFindField(kls->GetSuperClass(), name, expectedType, badCandidates);
475 }
476
HandleCandidates(const panda_file::FileReader * reader,const std::vector<panda_file::FieldItem * > & candidates,panda_file::ForeignFieldItem * ff)477 void Context::HandleCandidates(const panda_file::FileReader *reader,
478 const std::vector<panda_file::FieldItem *> &candidates, panda_file::ForeignFieldItem *ff)
479 {
480 auto details = std::vector<ErrorDetail> {{"field", ff}};
481 for (const auto &c : candidates) {
482 details.emplace_back("candidate", c);
483 }
484 Error("Unresolved field", details, reader);
485 }
486
MergeForeignField(const panda_file::FileReader * reader,panda_file::ForeignFieldItem * ff)487 void Context::MergeForeignField(const panda_file::FileReader *reader, panda_file::ForeignFieldItem *ff)
488 {
489 ASSERT(knownItems_.find(ff) == knownItems_.end());
490 ASSERT(knownItems_.find(ff->GetClassItem()) != knownItems_.end());
491
492 auto clz = static_cast<panda_file::BaseClassItem *>(knownItems_[ff->GetClassItem()]);
493 if (clz->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM) {
494 MergeForeignFieldCreate(reader, clz, ff);
495 return;
496 }
497
498 ASSERT(clz->GetItemType() == panda_file::ItemTypes::CLASS_ITEM);
499 auto rclz = static_cast<panda_file::ClassItem *>(clz);
500 std::vector<panda_file::FieldItem *> candidates;
501 auto res = TryFindField(rclz, ff->GetNameItem()->GetData(), TypeFromOld(ff->GetTypeItem()), &candidates);
502
503 auto visitor = [&reader, &clz, &ff, &candidates, this](auto el) {
504 using T = decltype(el);
505 if constexpr (std::is_same_v<T, std::monostate>) {
506 if (conf_.remainsPartial.count(GetStr(clz->GetNameItem())) != 0) {
507 MergeForeignFieldCreate(reader, clz, ff);
508 } else {
509 HandleCandidates(reader, candidates, ff);
510 }
511 } else if constexpr (std::is_same_v<T, panda_file::FieldItem *>) {
512 knownItems_[ff] = el;
513 } else {
514 ASSERT((std::is_same_v<T, panda_file::ForeignClassItem *>));
515 // el or klass? propagate field to base or not?
516 auto fieldKlass = el;
517 LOG(DEBUG, STATIC_LINKER) << "Propagating foreign field to base\n"
518 << ErrorToString({"field", ff}, 1) << '\n'
519 << ErrorToString({"new base", fieldKlass}, 1);
520 MergeForeignFieldCreate(reader, fieldKlass, ff);
521 }
522 };
523 std::visit(visitor, res);
524 }
525
MergeForeignFieldCreate(const panda_file::FileReader * reader,panda_file::BaseClassItem * clz,panda_file::ForeignFieldItem * ff)526 void Context::MergeForeignFieldCreate(const panda_file::FileReader *reader, panda_file::BaseClassItem *clz,
527 panda_file::ForeignFieldItem *ff)
528 {
529 auto *fc = static_cast<panda_file::ForeignClassItem *>(clz);
530 auto name = StringFromOld(ff->GetNameItem());
531 auto typ = TypeFromOld(ff->GetTypeItem());
532 auto [iter, was_inserted] = foreignFields_.emplace(std::piecewise_construct, std::forward_as_tuple(fc, name, typ),
533 std::forward_as_tuple(nullptr));
534 if (was_inserted) {
535 iter->second = cont_.CreateItem<panda_file::ForeignFieldItem>(fc, name, typ);
536 } else {
537 result_.stats.deduplicatedForeigners++;
538 }
539
540 auto nff = iter->second;
541 cameFrom_.emplace(nff, reader);
542 knownItems_[ff] = nff;
543 }
544
BreakProto(panda_file::ProtoItem * p)545 std::vector<panda_file::Type> Helpers::BreakProto(panda_file::ProtoItem *p)
546 {
547 auto &shorty = p->GetShorty();
548
549 auto ret = std::vector<panda_file::Type>();
550 ret.reserve(panda_file::SHORTY_ELEM_PER16 * shorty.size());
551
552 // SHORTY
553 size_t numElem = 0;
554 [[maybe_unused]] size_t numRefs = 0;
555 auto fetch = [idx = size_t(0), &shorty]() mutable {
556 ASSERT(idx < shorty.size());
557 return shorty[idx++];
558 };
559
560 auto v = fetch();
561
562 while (v != 0) {
563 auto shift = (numElem % panda_file::SHORTY_ELEM_PER16) * panda_file::SHORTY_ELEM_WIDTH;
564
565 auto elem = (static_cast<decltype(shift)>(v) >> shift) & panda_file::SHORTY_ELEM_MASK;
566
567 if (elem == 0) {
568 break;
569 }
570 auto asId = panda_file::Type::TypeId(elem);
571 ASSERT(asId != panda_file::Type::TypeId::INVALID);
572
573 auto t = panda_file::Type(asId);
574 if (t.IsReference()) {
575 numRefs++;
576 }
577 static_assert(std::is_trivially_copyable_v<decltype(t)>);
578 ret.emplace_back(t);
579
580 numElem++;
581
582 if (numElem % panda_file::SHORTY_ELEM_PER16 == 0) {
583 v = fetch();
584 }
585 }
586
587 ASSERT(numRefs == p->GetRefTypes().size());
588 ASSERT(!ret.empty());
589
590 return ret;
591 }
592
GetProto(panda_file::ProtoItem * p)593 std::pair<panda_file::ProtoItem *, std::vector<panda_file::MethodParamItem>> Context::GetProto(panda_file::ProtoItem *p)
594 {
595 auto &refs = p->GetRefTypes();
596
597 auto typs = Helpers::BreakProto(p);
598
599 panda_file::TypeItem *ret = nullptr;
600 auto params = std::vector<panda_file::MethodParamItem>();
601 params.reserve(typs.size() - 1);
602
603 size_t numRefs = 0;
604
605 for (auto const &t : typs) {
606 panda_file::TypeItem *ti;
607
608 if (t.IsReference()) {
609 ASSERT(numRefs < refs.size());
610 ti = TypeFromOld(refs[numRefs++]);
611 } else {
612 ti = cont_.GetOrCreatePrimitiveTypeItem(t);
613 }
614
615 if (ret == nullptr) {
616 ret = ti;
617 } else {
618 params.emplace_back(ti);
619 }
620 }
621
622 ASSERT(numRefs == refs.size());
623 ASSERT(ret != nullptr);
624
625 auto proto = cont_.GetOrCreateProtoItem(ret, params);
626
627 return std::make_pair(proto, std::move(params));
628 }
629
630 template <typename T, typename Getter, typename Adder>
AddAnnotationImpl(AddAnnotationImplData<T> ad,Getter getter,Adder adder)631 void Context::AddAnnotationImpl(AddAnnotationImplData<T> ad, Getter getter, Adder adder)
632 {
633 const auto &oldAnnotList = DerefPtrRef(getter(ad.oi));
634 for (size_t ind = ad.from; ind < oldAnnotList.size(); ind++) {
635 auto oldAnnot = oldAnnotList[ind];
636 auto mbNewAnnot = AnnotFromOld(oldAnnot);
637 if (std::holds_alternative<panda_file::AnnotationItem *>(mbNewAnnot)) {
638 adder(ad.ni, std::get<panda_file::AnnotationItem *>(mbNewAnnot));
639 return;
640 }
641 const auto &ed = std::get<ErrorDetail>(mbNewAnnot);
642 if (ad.retriesLeft-- == 0) {
643 std::vector<ErrorDetail> details {ErrorDetail {"annotation", oldAnnot}, ed};
644 if constexpr (std::is_base_of_v<panda_file::BaseItem, std::decay_t<T>>) {
645 details.emplace_back("old item", ad.oi);
646 details.emplace_back("new item", ad.ni);
647 }
648 this->Error("can't transfer annotation", details, ad.reader);
649 return;
650 }
651
652 LOG(DEBUG, STATIC_LINKER) << "defer annotation transferring due to" << ErrorToString(ed, 1);
653 ad.from = ind;
654 deferredFailedAnnotations_.emplace_back([=]() { AddAnnotationImpl<T>(ad, getter, adder); });
655 return;
656 }
657 }
658
659 template <typename T>
TransferAnnotations(const panda_file::FileReader * reader,T * ni,T * oi)660 void Context::TransferAnnotations(const panda_file::FileReader *reader, T *ni, T *oi)
661 {
662 AddAnnotationImplData<T> data {reader, ni, oi, 0, 1};
663 // pointers to members break clang-12 on CI
664 AddAnnotationImpl(
665 data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetRuntimeAnnotations()); },
666 [](T *self, panda_file::AnnotationItem *an) { self->AddRuntimeAnnotation(an); });
667 AddAnnotationImpl(
668 data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetAnnotations()); },
669 [](T *self, panda_file::AnnotationItem *an) { self->AddAnnotation(an); });
670 AddAnnotationImpl(
671 data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetRuntimeTypeAnnotations()); },
672 [](T *self, panda_file::AnnotationItem *an) { self->AddRuntimeTypeAnnotation(an); });
673 AddAnnotationImpl(
674 data, [](T *self) -> decltype(auto) { return DerefPtrRef(self->GetTypeAnnotations()); },
675 [](T *self, panda_file::AnnotationItem *an) { self->AddTypeAnnotation(an); });
676 }
677
ArrayValueFromOld(panda_file::ValueItem * oi)678 std::variant<panda_file::ValueItem *, Context::ErrorDetail> Context::ArrayValueFromOld(panda_file::ValueItem *oi)
679 {
680 auto old = oi->GetAsArray();
681 auto its = old->GetItems();
682 for (auto &i : its) {
683 if (i.HasValue<panda_file::BaseItem *>()) {
684 auto vl = ScalarValueIdFromOld(i.GetIdItem());
685 if (std::holds_alternative<ErrorDetail>(vl)) {
686 return std::move(std::get<ErrorDetail>(vl));
687 }
688 i = panda_file::ScalarValueItem(std::get<panda_file::BaseItem *>(vl));
689 }
690 }
691 return cont_.CreateItem<panda_file::ArrayValueItem>(old->GetComponentType(), std::move(its));
692 }
693
ValueFromOld(panda_file::ValueItem * oi)694 std::variant<panda_file::ValueItem *, Context::ErrorDetail> Context::ValueFromOld(panda_file::ValueItem *oi)
695 {
696 using ValueType = panda_file::ValueItem::Type;
697 switch (oi->GetType()) {
698 case ValueType::INTEGER:
699 return cont_.GetOrCreateIntegerValueItem(oi->GetAsScalar()->GetValue<uint32_t>());
700 case ValueType::LONG:
701 return cont_.GetOrCreateLongValueItem(oi->GetAsScalar()->GetValue<uint64_t>());
702 case ValueType::FLOAT:
703 return cont_.GetOrCreateFloatValueItem(oi->GetAsScalar()->GetValue<float>());
704 case ValueType::DOUBLE:
705 return cont_.GetOrCreateDoubleValueItem(oi->GetAsScalar()->GetValue<double>());
706 case ValueType::ID: {
707 auto oldItem = oi->GetAsScalar()->GetIdItem();
708 ASSERT(oldItem != nullptr);
709 auto newItem = ScalarValueIdFromOld(oldItem);
710 if (std::holds_alternative<ErrorDetail>(newItem)) {
711 return std::move(std::get<ErrorDetail>(newItem));
712 }
713 return cont_.GetOrCreateIdValueItem(std::get<panda_file::BaseItem *>(newItem));
714 }
715 case ValueType::ARRAY: {
716 return ArrayValueFromOld(oi);
717 }
718 default:
719 UNREACHABLE();
720 }
721 }
722
AnnotFromOld(panda_file::AnnotationItem * oa)723 std::variant<panda_file::AnnotationItem *, Context::ErrorDetail> Context::AnnotFromOld(panda_file::AnnotationItem *oa)
724 {
725 if (auto iter = knownItems_.find(oa); iter != knownItems_.end()) {
726 return static_cast<panda_file::AnnotationItem *>(iter->second);
727 }
728
729 using Elem = panda_file::AnnotationItem::Elem;
730
731 const auto &oldElems = *oa->GetElements();
732 auto newElems = std::vector<Elem>();
733 newElems.reserve(oldElems.size());
734 for (const auto &oe : oldElems) {
735 auto name = StringFromOld(oe.GetName());
736 auto newVal = ValueFromOld(oe.GetValue());
737 if (std::holds_alternative<ErrorDetail>(newVal)) {
738 return std::move(std::get<ErrorDetail>(newVal));
739 }
740
741 newElems.emplace_back(name, std::get<panda_file::ValueItem *>(newVal));
742 }
743
744 auto clzIt = knownItems_.find(oa->GetClassItem());
745 ASSERT(clzIt != knownItems_.end());
746 ASSERT(clzIt->second->GetItemType() == panda_file::ItemTypes::CLASS_ITEM ||
747 clzIt->second->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM);
748 auto clz = static_cast<panda_file::BaseClassItem *>(clzIt->second);
749
750 auto na = cont_.CreateItem<panda_file::AnnotationItem>(clz, std::move(newElems), oa->GetTags());
751 knownItems_.emplace(oa, na);
752 return na;
753 }
754
ScalarValueIdFromOld(panda_file::BaseItem * oi)755 std::variant<panda_file::BaseItem *, Context::ErrorDetail> Context::ScalarValueIdFromOld(panda_file::BaseItem *oi)
756 {
757 if (auto newItemIt = knownItems_.find(static_cast<panda_file::IndexedItem *>(oi)); newItemIt != knownItems_.end()) {
758 return newItemIt->second;
759 }
760 if (oi->GetItemType() == panda_file::ItemTypes::STRING_ITEM) {
761 return StringFromOld(static_cast<panda_file::StringItem *>(oi));
762 }
763 if (oi->GetItemType() == panda_file::ItemTypes::ANNOTATION_ITEM) {
764 auto oa = static_cast<panda_file::AnnotationItem *>(oi);
765 using ReturnType = decltype(ScalarValueIdFromOld(oi));
766 return std::visit([](auto &&a) -> ReturnType { return std::forward<decltype(a)>(a); }, AnnotFromOld(oa));
767 }
768 return ErrorDetail("id", oi);
769 }
770
Parse()771 void Context::Parse()
772 {
773 for (auto &codeData : codeDatas_) {
774 ProcessCodeData(patcher_, &codeData);
775 }
776 patcher_.ApplyDeps(this);
777 }
778
ComputeLayout()779 void Context::ComputeLayout()
780 {
781 cont_.ComputeLayout();
782 }
783
Patch()784 void Context::Patch()
785 {
786 patcher_.Patch({0, patcher_.GetSize()});
787 }
788
ClassFromOld(panda_file::BaseClassItem * old)789 panda_file::BaseClassItem *Context::ClassFromOld(panda_file::BaseClassItem *old)
790 {
791 if (old == nullptr) {
792 return old;
793 }
794 auto &cm = *cont_.GetClassMap();
795 if (auto iter = cm.find(GetStr(old->GetNameItem())); iter != cm.end()) {
796 return iter->second;
797 }
798 UNREACHABLE();
799 }
800
TypeFromOld(panda_file::TypeItem * old)801 panda_file::TypeItem *Context::TypeFromOld(panda_file::TypeItem *old)
802 {
803 const auto ty = old->GetType();
804 if (ty.IsPrimitive()) {
805 return cont_.GetOrCreatePrimitiveTypeItem(ty.GetId());
806 }
807 ASSERT(old->GetItemType() == panda_file::ItemTypes::CLASS_ITEM ||
808 old->GetItemType() == panda_file::ItemTypes::FOREIGN_CLASS_ITEM);
809 return ClassFromOld(static_cast<panda_file::BaseClassItem *>(old));
810 }
811
GetStr(const panda_file::StringItem * si)812 std::string Context::GetStr(const panda_file::StringItem *si)
813 {
814 return utf::Mutf8AsCString(reinterpret_cast<const uint8_t *>(si->GetData().data()));
815 }
816
StringFromOld(const panda_file::StringItem * s)817 panda_file::StringItem *Context::StringFromOld(const panda_file::StringItem *s)
818 {
819 if (s == nullptr) {
820 return nullptr;
821 }
822 return cont_.GetOrCreateStringItem(GetStr(s));
823 }
824 } // namespace ark::static_linker
825