• 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 
ReprItem(std::ostream & o,const panda_file::BaseItem * i)101 void ReprItem(std::ostream &o, const panda_file::BaseItem *i)
102 {
103     if (i == nullptr) {
104         o << "<null>";
105         return;
106     }
107     switch (i->GetItemType()) {
108         case panda_file::ItemTypes::FOREIGN_CLASS_ITEM: {
109             auto j = static_cast<const panda_file::ForeignClassItem *>(i);
110             DemangleName(o, j->GetNameItemData());
111             break;
112         }
113         case panda_file::ItemTypes::CLASS_ITEM: {
114             auto j = static_cast<const panda_file::ClassItem *>(i);
115             DemangleName(o, j->GetNameItemData());
116             break;
117         }
118         case panda_file::ItemTypes::FOREIGN_FIELD_ITEM: {
119             auto j = static_cast<const panda_file::ForeignFieldItem *>(i);
120             ReprItem(o, j->GetTypeItem());
121             o << " ";
122             ReprItem(o, j->GetClassItem());
123             o << "." << j->GetNameItem()->GetData();
124             break;
125         }
126         case panda_file::ItemTypes::FIELD_ITEM: {
127             auto j = static_cast<const panda_file::FieldItem *>(i);
128             ReprItem(o, j->GetTypeItem());
129             o << " ";
130             ReprItem(o, j->GetClassItem());
131             o << "." << j->GetNameItem()->GetData();
132             break;
133         }
134         case panda_file::ItemTypes::PRIMITIVE_TYPE_ITEM: {
135             auto j = static_cast<const panda_file::PrimitiveTypeItem *>(i);
136             o << j->GetType();
137             break;
138         }
139         case panda_file::ItemTypes::FOREIGN_METHOD_ITEM: {
140             auto j = static_cast<const panda_file::ForeignMethodItem *>(i);
141             ReprMethod(o, j->GetNameItem(), j->GetClassItem(), j->GetProto());
142             break;
143         }
144         case panda_file::ItemTypes::METHOD_ITEM: {
145             auto j = static_cast<const panda_file::MethodItem *>(i);
146             ReprMethod(o, j->GetNameItem(), j->GetClassItem(), j->GetProto());
147             break;
148         }
149         case panda_file::ItemTypes::ANNOTATION_ITEM: {
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             break;
165         }
166         case panda_file::ItemTypes::VALUE_ITEM: {
167             auto j = static_cast<const panda_file::ValueItem *>(i);
168             switch (j->GetType()) {
169                 case panda_file::ValueItem::Type::ARRAY: {
170                     auto *arr = static_cast<const panda_file::ArrayValueItem *>(j);
171                     const auto &its = arr->GetItems();
172                     o << "[";
173                     for (size_t k = 0; k < its.size(); k++) {
174                         if (k != 0) {
175                             o << ", ";
176                         }
177                         ReprItem(o, &its[k]);
178                     }
179                     o << "]";
180                     break;
181                 }
182                 case panda_file::ValueItem::Type::INTEGER: {
183                     auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
184                     o << scalar->GetValue<uint32_t>() << " as int";
185                     break;
186                 }
187                 case panda_file::ValueItem::Type::LONG: {
188                     auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
189                     o << scalar->GetValue<uint64_t>() << " as long";
190                     break;
191                 }
192                 case panda_file::ValueItem::Type::FLOAT: {
193                     auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
194                     o << scalar->GetValue<float>() << " as float";
195                     break;
196                 }
197                 case panda_file::ValueItem::Type::DOUBLE: {
198                     auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
199                     o << scalar->GetValue<double>() << " as double";
200                     break;
201                 }
202                 case panda_file::ValueItem::Type::ID: {
203                     auto *scalar = static_cast<const panda_file::ScalarValueItem *>(j);
204                     ReprItem(o, scalar->GetIdItem());
205                     break;
206                 }
207                 default:
208                     UNREACHABLE();
209             }
210             break;
211         }
212         case panda_file::ItemTypes::STRING_ITEM: {
213             auto str = static_cast<const panda_file::StringItem *>(i);
214             auto view = std::string_view(str->GetData());
215             o << '"';
216             while (!view.empty()) {
217                 auto pos = view.find_first_of("\"\\\n");
218                 o << view.substr(0, pos);
219                 if (pos != std::string::npos) {
220                     if (view[pos] == '\n') {
221                         o << "\\n";
222                     } else {
223                         o << '\\' << view[pos];
224                     }
225                     view = view.substr(pos + 1);
226                 } else {
227                     view = "";
228                 }
229             }
230             o << '"';
231             break;
232         }
233         default:
234             o << "<unknown:" << i->GetOffset() << ">";
235     }
236 }
237 }  // namespace
238 
Context(Config conf)239 Context::Context(Config conf) : conf_(std::move(conf)) {}
240 
241 Context::~Context() = default;
242 
Write(const std::string & out)243 void Context::Write(const std::string &out)
244 {
245     auto writer = panda_file::FileWriter(out);
246     if (!writer) {
247         Error("Can't write", {ErrorDetail("file", out)});
248         return;
249     }
250     cont_.Write(&writer, true, false);
251 
252     result_.stats.itemsCount = knownItems_.size();
253     result_.stats.classCount = cont_.GetClassMap()->size();
254 }
255 
Read(const std::vector<std::string> & input)256 void Context::Read(const std::vector<std::string> &input)
257 {
258     for (const auto &f : input) {
259         auto rd = panda_file::File::Open(f);
260         if (rd == nullptr) {
261             Error("Can't open file", {ErrorDetail("location", f)});
262             break;
263         }
264         auto reader = &readers_.emplace_front(std::move(rd));
265         if (!reader->ReadContainer(false)) {
266             Error("can't read container", {}, reader);
267             break;
268         }
269     }
270 
271     ASSERT(input.size() ==
272            std::accumulate(readers_.begin(), readers_.end(), (size_t)0, [](size_t c, const auto &) { return c + 1; }));
273 }
274 
operator <<(std::ostream & o,const static_linker::Context::ErrorToStringWrapper & self)275 std::ostream &operator<<(std::ostream &o, const static_linker::Context::ErrorToStringWrapper &self)
276 {
277     std::fill_n(std::ostream_iterator<char>(o), self.indent_, '\t');
278     o << self.error_.GetName();
279     std::visit(
280         [&](const auto &v) {
281             using T = std::remove_cv_t<std::remove_reference_t<decltype(v)>>;
282             if constexpr (std::is_same_v<T, std::string>) {
283                 if (!v.empty()) {
284                     o << ": " << v;
285                 }
286             } else {
287                 o << ": ";
288                 ReprItem(o, v);
289                 for (auto [beg, end] = self.ctx_->cameFrom_.equal_range(v); beg != end; ++beg) {
290                     o << "\n";
291                     std::fill_n(std::ostream_iterator<char>(o), self.indent_ + 1, '\t');
292                     o << "declared at `" << beg->second->GetFilePtr()->GetFilename() << "`";
293                 }
294             }
295         },
296         self.error_.GetInfo());
297     return o;
298 }
299 
Error(const std::string & msg,const std::vector<ErrorDetail> & details,const panda_file::FileReader * reader)300 void Context::Error(const std::string &msg, const std::vector<ErrorDetail> &details,
301                     const panda_file::FileReader *reader)
302 {
303     auto o = std::stringstream();
304     o << msg;
305 
306     for (const auto &d : details) {
307         o << "\n" << ErrorToString(d, 1);
308     }
309     if (reader != nullptr) {
310         o << "\n\tat `" << reader->GetFilePtr()->GetFilename() << "`";
311     }
312 
313     result_.errors.emplace_back(o.str());
314 }
315 
316 }  // namespace ark::static_linker
317