• 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 <numeric>
17 #include <sstream>
18 
19 #include "libpandafile/file_items.h"
20 #include "libpandafile/file_reader.h"
21 #include "libpandabase/os/mutex.h"
22 
23 #include "linker.h"
24 #include "linker_context.h"
25 
26 namespace ark::static_linker {
27 
28 namespace {
29 
DemangleName(std::ostream & o,std::string_view s)30 void DemangleName(std::ostream &o, std::string_view s)
31 {
32     if (s.empty()) {
33         return;
34     }
35     if (s.back() == 0) {
36         s = s.substr(0, s.size() - 1);
37     }
38     if (s.empty()) {
39         return;
40     }
41 
42     if (s.front() == '[') {
43         DemangleName(o, s.substr(1));
44         o << "[]";
45         return;
46     }
47 
48     if (s.size() == 1) {
49         auto ty = panda_file::Type::GetTypeIdBySignature(s.front());
50         o << ty;
51         return;
52     }
53 
54     if (s.front() == 'L' && s.back() == ';') {
55         s = s.substr(1, s.size() - 2UL);
56         while (!s.empty()) {
57             const auto to = s.find('/');
58             o << s.substr(0, to);
59             if (to != std::string::npos) {
60                 o << ".";
61                 s = s.substr(to + 1);
62             } else {
63                 break;
64             }
65         }
66         return;
67     }
68 
69     o << "<unknown>";
70 }
71 
72 void ReprItem(std::ostream &o, const panda_file::BaseItem *i);
73 
ReprMethod(std::ostream & o,panda_file::StringItem * name,panda_file::BaseClassItem * clz,panda_file::ProtoItem * p)74 void ReprMethod(std::ostream &o, panda_file::StringItem *name, panda_file::BaseClassItem *clz, panda_file::ProtoItem *p)
75 {
76     auto typs = Helpers::BreakProto(p);
77     auto refs = p->GetRefTypes();
78     size_t numRefs = 0;
79     auto reprType = [&typs, &refs, &numRefs, &o](size_t ii) {
80         auto &t = typs[ii];
81         if (t.IsPrimitive()) {
82             o << t;
83         } else {
84             ReprItem(o, refs[numRefs++]);
85         }
86     };
87     reprType(0);
88     o << " ";
89     ReprItem(o, clz);
90     o << "." << name->GetData();
91     o << "(";
92     for (size_t ii = 1; ii < typs.size(); ii++) {
93         reprType(ii);
94         if (ii + 1 != typs.size()) {
95             o << ", ";
96         }
97     }
98     o << ")";
99 }
100 
ReprValueItem(std::ostream & o,const panda_file::BaseItem * i)101 void ReprValueItem(std::ostream &o, const panda_file::BaseItem *i)
102 {
103     auto j = static_cast<const panda_file::ValueItem *>(i);
104     switch (j->GetType()) {
105         case panda_file::ValueItem::Type::ARRAY: {
106             auto *arr = static_cast<const panda_file::ArrayValueItem *>(j);
107             const auto &its = arr->GetItems();
108             o << "[";
109             for (size_t k = 0; k < its.size(); k++) {
110                 if (k != 0) {
111                     o << ", ";
112                 }
113                 ReprItem(o, &its[k]);
114             }
115             o << "]";
116             break;
117         }
118         case panda_file::ValueItem::Type::INTEGER: {
119             auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
120             o << scalar->GetValue<uint32_t>() << " as int";
121             break;
122         }
123         case panda_file::ValueItem::Type::LONG: {
124             auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
125             o << scalar->GetValue<uint64_t>() << " as long";
126             break;
127         }
128         case panda_file::ValueItem::Type::FLOAT: {
129             auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
130             o << scalar->GetValue<float>() << " as float";
131             break;
132         }
133         case panda_file::ValueItem::Type::DOUBLE: {
134             auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
135             o << scalar->GetValue<double>() << " as double";
136             break;
137         }
138         case panda_file::ValueItem::Type::ID: {
139             auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
140             ReprItem(o, scalar->GetIdItem());
141             break;
142         }
143         default:
144             UNREACHABLE();
145     }
146 }
147 
ReprAnnotationItem(std::ostream & o,const panda_file::BaseItem * i)148 void ReprAnnotationItem(std::ostream &o, const panda_file::BaseItem *i)
149 {
150     auto j = static_cast<const panda_file::AnnotationItem *>(i);
151     ReprItem(o, j->GetClassItem());
152     o << "<";
153     bool first = true;
154     for (auto &a : *j->GetElements()) {
155         if (first) {
156             first = false;
157         } else {
158             o << ", ";
159         }
160         o << a.GetName()->GetData() << "=";
161         ReprItem(o, a.GetValue());
162     }
163     o << ">";
164 }
165 
ReprStringItem(std::ostream & o,const panda_file::BaseItem * i)166 void ReprStringItem(std::ostream &o, const panda_file::BaseItem *i)
167 {
168     auto str = static_cast<const panda_file::StringItem *>(i);
169     auto view = std::string_view(str->GetData());
170     o << '"';
171     while (!view.empty()) {
172         auto pos = view.find_first_of("\"\\\n");
173         o << view.substr(0, pos);
174         if (pos != std::string::npos) {
175             if (view[pos] == '\n') {
176                 o << "\\n";
177             } else {
178                 o << '\\' << view[pos];
179             }
180             view = view.substr(pos + 1);
181         } else {
182             view = "";
183         }
184     }
185     o << '"';
186 }
187 
188 template <typename T>
ReprFieldItem(std::ostream & o,const T * j)189 void ReprFieldItem(std::ostream &o, const T *j)
190 {
191     ReprItem(o, j->GetTypeItem());
192     o << " ";
193     ReprItem(o, j->GetClassItem());
194     o << "." << j->GetNameItem()->GetData();
195 }
196 
197 // CC-OFFNXT(G.FUN.01, huge_method) big switch case
ReprItem(std::ostream & o,const panda_file::BaseItem * i)198 void ReprItem(std::ostream &o, const panda_file::BaseItem *i)
199 {
200     if (i == nullptr) {
201         o << "<null>";
202         return;
203     }
204     switch (i->GetItemType()) {
205         case panda_file::ItemTypes::FOREIGN_CLASS_ITEM: {
206             auto j = static_cast<const panda_file::ForeignClassItem *>(i);
207             DemangleName(o, j->GetNameItemData());
208             break;
209         }
210         case panda_file::ItemTypes::CLASS_ITEM: {
211             auto j = static_cast<const panda_file::ClassItem *>(i);
212             DemangleName(o, j->GetNameItemData());
213             break;
214         }
215         case panda_file::ItemTypes::FOREIGN_FIELD_ITEM: {
216             auto j = static_cast<const panda_file::ForeignFieldItem *>(i);
217             ReprFieldItem(o, j);
218             break;
219         }
220         case panda_file::ItemTypes::FIELD_ITEM: {
221             auto j = static_cast<const panda_file::FieldItem *>(i);
222             ReprFieldItem(o, j);
223             break;
224         }
225         case panda_file::ItemTypes::PRIMITIVE_TYPE_ITEM: {
226             auto j = static_cast<const panda_file::PrimitiveTypeItem *>(i);
227             o << j->GetType();
228             break;
229         }
230         case panda_file::ItemTypes::FOREIGN_METHOD_ITEM: {
231             auto j = static_cast<const panda_file::ForeignMethodItem *>(i);
232             ReprMethod(o, j->GetNameItem(), j->GetClassItem(), j->GetProto());
233             break;
234         }
235         case panda_file::ItemTypes::METHOD_ITEM: {
236             auto j = static_cast<const panda_file::MethodItem *>(i);
237             ReprMethod(o, j->GetNameItem(), j->GetClassItem(), j->GetProto());
238             break;
239         }
240         case panda_file::ItemTypes::ANNOTATION_ITEM: {
241             ReprAnnotationItem(o, i);
242             break;
243         }
244         case panda_file::ItemTypes::VALUE_ITEM: {
245             ReprValueItem(o, i);
246             break;
247         }
248         case panda_file::ItemTypes::STRING_ITEM: {
249             ReprStringItem(o, i);
250             break;
251         }
252         default:
253             o << "<unknown:" << i->GetOffset() << ">";
254     }
255 }
256 }  // namespace
257 
Context(Config conf)258 Context::Context(Config conf) : conf_(std::move(conf)) {}
259 
260 Context::~Context() = default;
261 
Write(const std::string & out)262 void Context::Write(const std::string &out)
263 {
264     auto writer = panda_file::FileWriter(out);
265     if (!writer) {
266         Error("Can't write", {ErrorDetail("file", out)});
267         return;
268     }
269     cont_.Write(&writer, true, false);
270 
271     result_.stats.itemsCount = knownItems_.size();
272     result_.stats.classCount = cont_.GetClassMap()->size();
273 }
274 
Read(const std::vector<std::string> & input)275 void Context::Read(const std::vector<std::string> &input)
276 {
277     for (const auto &f : input) {
278         auto rd = panda_file::File::Open(f);
279         if (rd == nullptr) {
280             Error("Can't open file", {ErrorDetail("location", f)});
281             break;
282         }
283         auto reader = &readers_.emplace_front(std::move(rd));
284         if (!reader->ReadContainer(false)) {
285             Error("can't read container", {}, reader);
286             break;
287         }
288     }
289 
290     ASSERT(input.size() ==
291            std::accumulate(readers_.begin(), readers_.end(), (size_t)0, [](size_t c, const auto &) { return c + 1; }));
292 }
293 
operator <<(std::ostream & o,const static_linker::Context::ErrorToStringWrapper & self)294 std::ostream &operator<<(std::ostream &o, const static_linker::Context::ErrorToStringWrapper &self)
295 {
296     std::fill_n(std::ostream_iterator<char>(o), self.indent_, '\t');
297     o << self.error_.GetName();
298     std::visit(
299         [&](const auto &v) {
300             using T = std::remove_cv_t<std::remove_reference_t<decltype(v)>>;
301             if constexpr (std::is_same_v<T, std::string>) {
302                 if (!v.empty()) {
303                     o << ": " << v;
304                 }
305             } else {
306                 o << ": ";
307                 ReprItem(o, v);
308                 for (auto [beg, end] = self.ctx_->cameFrom_.equal_range(v); beg != end; ++beg) {
309                     o << "\n";
310                     std::fill_n(std::ostream_iterator<char>(o), self.indent_ + 1, '\t');
311                     o << "declared at `" << beg->second->GetFilePtr()->GetFilename() << "`";
312                 }
313             }
314         },
315         self.error_.GetInfo());
316     return o;
317 }
318 
Error(const std::string & msg,const std::vector<ErrorDetail> & details,const panda_file::FileReader * reader)319 void Context::Error(const std::string &msg, const std::vector<ErrorDetail> &details,
320                     const panda_file::FileReader *reader)
321 {
322     auto o = std::stringstream();
323     o << msg;
324 
325     for (const auto &d : details) {
326         o << "\n" << ErrorToString(d, 1);
327     }
328     if (reader != nullptr) {
329         o << "\n\tat `" << reader->GetFilePtr()->GetFilename() << "`";
330     }
331 
332     result_.errors.emplace_back(o.str());
333 }
334 
335 }  // namespace ark::static_linker
336