test_dex_file_builder.h revision 41b175aba41c9365a1c53b8a1afbd17129c87c14
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_
18#define ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_
19
20#include <cstring>
21#include <set>
22#include <map>
23#include <vector>
24
25#include "base/bit_utils.h"
26#include "base/logging.h"
27#include "dex_file.h"
28
29namespace art {
30
31class TestDexFileBuilder {
32 public:
33  TestDexFileBuilder()
34      : strings_(), types_(), fields_(), protos_(), dex_file_data_() {
35  }
36
37  void AddString(const std::string& str) {
38    CHECK(dex_file_data_.empty());
39    auto it = strings_.emplace(str, IdxAndDataOffset()).first;
40    CHECK_LT(it->first.length(), 128u);  // Don't allow multi-byte length in uleb128.
41  }
42
43  void AddType(const std::string& descriptor) {
44    CHECK(dex_file_data_.empty());
45    AddString(descriptor);
46    types_.emplace(descriptor, 0u);
47  }
48
49  void AddField(const std::string& class_descriptor, const std::string& type,
50                const std::string& name) {
51    CHECK(dex_file_data_.empty());
52    AddType(class_descriptor);
53    AddType(type);
54    AddString(name);
55    FieldKey key = { class_descriptor, type, name };
56    fields_.emplace(key, 0u);
57  }
58
59  void AddMethod(const std::string& class_descriptor, const std::string& signature,
60                 const std::string& name) {
61    CHECK(dex_file_data_.empty());
62    AddType(class_descriptor);
63    AddString(name);
64
65    ProtoKey proto_key = CreateProtoKey(signature);
66    AddString(proto_key.shorty);
67    AddType(proto_key.return_type);
68    for (const auto& arg_type : proto_key.args) {
69      AddType(arg_type);
70    }
71    auto it = protos_.emplace(proto_key, IdxAndDataOffset()).first;
72    const ProtoKey* proto = &it->first;  // Valid as long as the element remains in protos_.
73
74    MethodKey method_key = {
75        class_descriptor, name, proto
76    };
77    methods_.emplace(method_key, 0u);
78  }
79
80  // NOTE: The builder holds the actual data, so it must live as long as the dex file.
81  std::unique_ptr<const DexFile> Build(const std::string& dex_location) {
82    CHECK(dex_file_data_.empty());
83    union {
84      uint8_t data[sizeof(DexFile::Header)];
85      uint64_t force_alignment;
86    } header_data;
87    std::memset(header_data.data, 0, sizeof(header_data.data));
88    DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data);
89    std::copy_n(DexFile::kDexMagic, 4u, header->magic_);
90    std::copy_n(DexFile::kDexMagicVersion, 4u, header->magic_ + 4u);
91    header->header_size_ = sizeof(header);
92    header->endian_tag_ = DexFile::kDexEndianConstant;
93    header->link_size_ = 0u;  // Unused.
94    header->link_off_ = 0u;  // Unused.
95    header->map_off_ = 0u;  // Unused.
96
97    uint32_t data_section_size = 0u;
98
99    uint32_t string_ids_offset = sizeof(DexFile::Header);
100    uint32_t string_idx = 0u;
101    for (auto& entry : strings_) {
102      entry.second.idx = string_idx;
103      string_idx += 1u;
104      entry.second.data_offset = data_section_size;
105      data_section_size += entry.first.length() + 1u /* length */ + 1u /* null-terminator */;
106    }
107    header->string_ids_size_ = strings_.size();
108    header->string_ids_off_ = strings_.empty() ? 0u : string_ids_offset;
109
110    uint32_t type_ids_offset = string_ids_offset + strings_.size() * sizeof(DexFile::StringId);
111    uint32_t type_idx = 0u;
112    for (auto& entry : types_) {
113      entry.second = type_idx;
114      type_idx += 1u;
115    }
116    header->type_ids_size_ = types_.size();
117    header->type_ids_off_ = types_.empty() ? 0u : type_ids_offset;
118
119    uint32_t proto_ids_offset = type_ids_offset + types_.size() * sizeof(DexFile::TypeId);
120    uint32_t proto_idx = 0u;
121    for (auto& entry : protos_) {
122      entry.second.idx = proto_idx;
123      proto_idx += 1u;
124      size_t num_args = entry.first.args.size();
125      if (num_args != 0u) {
126        entry.second.data_offset = RoundUp(data_section_size, 4u);
127        data_section_size = entry.second.data_offset + 4u + num_args * sizeof(DexFile::TypeItem);
128      } else {
129        entry.second.data_offset = 0u;
130      }
131    }
132    header->proto_ids_size_ = protos_.size();
133    header->proto_ids_off_ = protos_.empty() ? 0u : proto_ids_offset;
134
135    uint32_t field_ids_offset = proto_ids_offset + protos_.size() * sizeof(DexFile::ProtoId);
136    uint32_t field_idx = 0u;
137    for (auto& entry : fields_) {
138      entry.second = field_idx;
139      field_idx += 1u;
140    }
141    header->field_ids_size_ = fields_.size();
142    header->field_ids_off_ = fields_.empty() ? 0u : field_ids_offset;
143
144    uint32_t method_ids_offset = field_ids_offset + fields_.size() * sizeof(DexFile::FieldId);
145    uint32_t method_idx = 0u;
146    for (auto& entry : methods_) {
147      entry.second = method_idx;
148      method_idx += 1u;
149    }
150    header->method_ids_size_ = methods_.size();
151    header->method_ids_off_ = methods_.empty() ? 0u : method_ids_offset;
152
153    // No class defs.
154    header->class_defs_size_ = 0u;
155    header->class_defs_off_ = 0u;
156
157    uint32_t data_section_offset = method_ids_offset + methods_.size() * sizeof(DexFile::MethodId);
158    header->data_size_ = data_section_size;
159    header->data_off_ = (data_section_size != 0u) ? data_section_offset : 0u;
160
161    uint32_t total_size = data_section_offset + data_section_size;
162
163    dex_file_data_.resize(total_size);
164    std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header));
165
166    for (const auto& entry : strings_) {
167      CHECK_LT(entry.first.size(), 128u);
168      uint32_t raw_offset = data_section_offset + entry.second.data_offset;
169      dex_file_data_[raw_offset] = static_cast<uint8_t>(entry.first.size());
170      std::memcpy(&dex_file_data_[raw_offset + 1], entry.first.c_str(), entry.first.size() + 1);
171      Write32(string_ids_offset + entry.second.idx * sizeof(DexFile::StringId), raw_offset);
172    }
173
174    for (const auto& entry : types_) {
175      Write32(type_ids_offset + entry.second * sizeof(DexFile::TypeId), GetStringIdx(entry.first));
176      ++type_idx;
177    }
178
179    for (const auto& entry : protos_) {
180      size_t num_args = entry.first.args.size();
181      uint32_t type_list_offset =
182          (num_args != 0u) ? data_section_offset + entry.second.data_offset : 0u;
183      uint32_t raw_offset = proto_ids_offset + entry.second.idx * sizeof(DexFile::ProtoId);
184      Write32(raw_offset + 0u, GetStringIdx(entry.first.shorty));
185      Write16(raw_offset + 4u, GetTypeIdx(entry.first.return_type));
186      Write32(raw_offset + 8u, type_list_offset);
187      if (num_args != 0u) {
188        CHECK_NE(entry.second.data_offset, 0u);
189        Write32(type_list_offset, num_args);
190        for (size_t i = 0; i != num_args; ++i) {
191          Write16(type_list_offset + 4u + i * sizeof(DexFile::TypeItem),
192                  GetTypeIdx(entry.first.args[i]));
193        }
194      }
195    }
196
197    for (const auto& entry : fields_) {
198      uint32_t raw_offset = field_ids_offset + entry.second * sizeof(DexFile::FieldId);
199      Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor));
200      Write16(raw_offset + 2u, GetTypeIdx(entry.first.type));
201      Write32(raw_offset + 4u, GetStringIdx(entry.first.name));
202    }
203
204    for (const auto& entry : methods_) {
205      uint32_t raw_offset = method_ids_offset + entry.second * sizeof(DexFile::MethodId);
206      Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor));
207      auto it = protos_.find(*entry.first.proto);
208      CHECK(it != protos_.end());
209      Write16(raw_offset + 2u, it->second.idx);
210      Write32(raw_offset + 4u, GetStringIdx(entry.first.name));
211    }
212
213    // Leave checksum and signature as zeros.
214
215    std::string error_msg;
216    std::unique_ptr<const DexFile> dex_file(DexFile::Open(
217        &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, &error_msg));
218    CHECK(dex_file != nullptr) << error_msg;
219    return std::move(dex_file);
220  }
221
222  uint32_t GetStringIdx(const std::string& type) {
223    auto it = strings_.find(type);
224    CHECK(it != strings_.end());
225    return it->second.idx;
226  }
227
228  uint32_t GetTypeIdx(const std::string& type) {
229    auto it = types_.find(type);
230    CHECK(it != types_.end());
231    return it->second;
232  }
233
234  uint32_t GetFieldIdx(const std::string& class_descriptor, const std::string& type,
235                       const std::string& name) {
236    FieldKey key = { class_descriptor, type, name };
237    auto it = fields_.find(key);
238    CHECK(it != fields_.end());
239    return it->second;
240  }
241
242  uint32_t GetMethodIdx(const std::string& class_descriptor, const std::string& signature,
243                        const std::string& name) {
244    ProtoKey proto_key = CreateProtoKey(signature);
245    MethodKey method_key = { class_descriptor, name, &proto_key };
246    auto it = methods_.find(method_key);
247    CHECK(it != methods_.end());
248    return it->second;
249  }
250
251 private:
252  struct IdxAndDataOffset {
253    uint32_t idx;
254    uint32_t data_offset;
255  };
256
257  struct FieldKey {
258    const std::string class_descriptor;
259    const std::string type;
260    const std::string name;
261  };
262  struct FieldKeyComparator {
263    bool operator()(const FieldKey& lhs, const FieldKey& rhs) const {
264      if (lhs.class_descriptor != rhs.class_descriptor) {
265        return lhs.class_descriptor < rhs.class_descriptor;
266      }
267      if (lhs.name != rhs.name) {
268        return lhs.name < rhs.name;
269      }
270      return lhs.type < rhs.type;
271    }
272  };
273
274  struct ProtoKey {
275    std::string shorty;
276    std::string return_type;
277    std::vector<std::string> args;
278  };
279  struct ProtoKeyComparator {
280    bool operator()(const ProtoKey& lhs, const ProtoKey& rhs) const {
281      if (lhs.return_type != rhs.return_type) {
282        return lhs.return_type < rhs.return_type;
283      }
284      size_t min_args = std::min(lhs.args.size(), rhs.args.size());
285      for (size_t i = 0; i != min_args; ++i) {
286        if (lhs.args[i] != rhs.args[i]) {
287          return lhs.args[i] < rhs.args[i];
288        }
289      }
290      return lhs.args.size() < rhs.args.size();
291    }
292  };
293
294  struct MethodKey {
295    std::string class_descriptor;
296    std::string name;
297    const ProtoKey* proto;
298  };
299  struct MethodKeyComparator {
300    bool operator()(const MethodKey& lhs, const MethodKey& rhs) const {
301      if (lhs.class_descriptor != rhs.class_descriptor) {
302        return lhs.class_descriptor < rhs.class_descriptor;
303      }
304      if (lhs.name != rhs.name) {
305        return lhs.name < rhs.name;
306      }
307      return ProtoKeyComparator()(*lhs.proto, *rhs.proto);
308    }
309  };
310
311  ProtoKey CreateProtoKey(const std::string& signature) {
312    CHECK_EQ(signature[0], '(');
313    const char* args = signature.c_str() + 1;
314    const char* args_end = std::strchr(args, ')');
315    CHECK(args_end != nullptr);
316    const char* return_type = args_end + 1;
317
318    ProtoKey key = {
319        std::string() + ((*return_type == '[') ? 'L' : *return_type),
320        return_type,
321        std::vector<std::string>()
322    };
323    while (args != args_end) {
324      key.shorty += (*args == '[') ? 'L' : *args;
325      const char* arg_start = args;
326      while (*args == '[') {
327        ++args;
328      }
329      if (*args == 'L') {
330        do {
331          ++args;
332          CHECK_NE(args, args_end);
333        } while (*args != ';');
334      }
335      ++args;
336      key.args.emplace_back(arg_start, args);
337    }
338    return key;
339  }
340
341  void Write32(size_t offset, uint32_t value) {
342    CHECK_LE(offset + 4u, dex_file_data_.size());
343    CHECK_EQ(dex_file_data_[offset + 0], 0u);
344    CHECK_EQ(dex_file_data_[offset + 1], 0u);
345    CHECK_EQ(dex_file_data_[offset + 2], 0u);
346    CHECK_EQ(dex_file_data_[offset + 3], 0u);
347    dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0);
348    dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8);
349    dex_file_data_[offset + 2] = static_cast<uint8_t>(value >> 16);
350    dex_file_data_[offset + 3] = static_cast<uint8_t>(value >> 24);
351  }
352
353  void Write16(size_t offset, uint32_t value) {
354    CHECK_LE(value, 0xffffu);
355    CHECK_LE(offset + 2u, dex_file_data_.size());
356    CHECK_EQ(dex_file_data_[offset + 0], 0u);
357    CHECK_EQ(dex_file_data_[offset + 1], 0u);
358    dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0);
359    dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8);
360  }
361
362  std::map<std::string, IdxAndDataOffset> strings_;
363  std::map<std::string, uint32_t> types_;
364  std::map<FieldKey, uint32_t, FieldKeyComparator> fields_;
365  std::map<ProtoKey, IdxAndDataOffset, ProtoKeyComparator> protos_;
366  std::map<MethodKey, uint32_t, MethodKeyComparator> methods_;
367
368  std::vector<uint8_t> dex_file_data_;
369};
370
371}  // namespace art
372
373#endif  // ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_
374