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