diff options
Diffstat (limited to 'compiler/utils/test_dex_file_builder.h')
-rw-r--r-- | compiler/utils/test_dex_file_builder.h | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h new file mode 100644 index 0000000000..ab039aa215 --- /dev/null +++ b/compiler/utils/test_dex_file_builder.h @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ +#define ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ + +#include <cstring> +#include <set> +#include <map> +#include <vector> + +#include "dex_file.h" +#include "utils.h" + +namespace art { + +class TestDexFileBuilder { + public: + TestDexFileBuilder() + : strings_(), types_(), fields_(), protos_(), dex_file_data_() { + } + + void AddString(const std::string& str) { + CHECK(dex_file_data_.empty()); + auto it = strings_.emplace(str, IdxAndDataOffset()).first; + CHECK_LT(it->first.length(), 128u); // Don't allow multi-byte length in uleb128. + } + + void AddType(const std::string& descriptor) { + CHECK(dex_file_data_.empty()); + AddString(descriptor); + types_.emplace(descriptor, 0u); + } + + void AddField(const std::string& class_descriptor, const std::string& type, + const std::string& name) { + CHECK(dex_file_data_.empty()); + AddType(class_descriptor); + AddType(type); + AddString(name); + FieldKey key = { class_descriptor, type, name }; + fields_.emplace(key, 0u); + } + + void AddMethod(const std::string& class_descriptor, const std::string& signature, + const std::string& name) { + CHECK(dex_file_data_.empty()); + AddType(class_descriptor); + AddString(name); + + ProtoKey proto_key = CreateProtoKey(signature); + AddString(proto_key.shorty); + AddType(proto_key.return_type); + for (const auto& arg_type : proto_key.args) { + AddType(arg_type); + } + auto it = protos_.emplace(proto_key, IdxAndDataOffset()).first; + const ProtoKey* proto = &it->first; // Valid as long as the element remains in protos_. + + MethodKey method_key = { + class_descriptor, name, proto + }; + methods_.emplace(method_key, 0u); + } + + // NOTE: The builder holds the actual data, so it must live as long as the dex file. + std::unique_ptr<const DexFile> Build(const std::string& dex_location) { + CHECK(dex_file_data_.empty()); + union { + uint8_t data[sizeof(DexFile::Header)]; + uint64_t force_alignment; + } header_data; + std::memset(header_data.data, 0, sizeof(header_data.data)); + DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data); + std::copy_n(DexFile::kDexMagic, 4u, header->magic_); + std::copy_n(DexFile::kDexMagicVersion, 4u, header->magic_ + 4u); + header->header_size_ = sizeof(header); + header->endian_tag_ = DexFile::kDexEndianConstant; + header->link_size_ = 0u; // Unused. + header->link_off_ = 0u; // Unused. + header->map_off_ = 0u; // Unused. + + uint32_t data_section_size = 0u; + + uint32_t string_ids_offset = sizeof(DexFile::Header); + uint32_t string_idx = 0u; + for (auto& entry : strings_) { + entry.second.idx = string_idx; + string_idx += 1u; + entry.second.data_offset = data_section_size; + data_section_size += entry.first.length() + 1u /* length */ + 1u /* null-terminator */; + } + header->string_ids_size_ = strings_.size(); + header->string_ids_off_ = strings_.empty() ? 0u : string_ids_offset; + + uint32_t type_ids_offset = string_ids_offset + strings_.size() * sizeof(DexFile::StringId); + uint32_t type_idx = 0u; + for (auto& entry : types_) { + entry.second = type_idx; + type_idx += 1u; + } + header->type_ids_size_ = types_.size(); + header->type_ids_off_ = types_.empty() ? 0u : type_ids_offset; + + uint32_t proto_ids_offset = type_ids_offset + types_.size() * sizeof(DexFile::TypeId); + uint32_t proto_idx = 0u; + for (auto& entry : protos_) { + entry.second.idx = proto_idx; + proto_idx += 1u; + size_t num_args = entry.first.args.size(); + if (num_args != 0u) { + entry.second.data_offset = RoundUp(data_section_size, 4u); + data_section_size = entry.second.data_offset + 4u + num_args * sizeof(DexFile::TypeItem); + } else { + entry.second.data_offset = 0u; + } + } + header->proto_ids_size_ = protos_.size(); + header->proto_ids_off_ = protos_.empty() ? 0u : proto_ids_offset; + + uint32_t field_ids_offset = proto_ids_offset + protos_.size() * sizeof(DexFile::ProtoId); + uint32_t field_idx = 0u; + for (auto& entry : fields_) { + entry.second = field_idx; + field_idx += 1u; + } + header->field_ids_size_ = fields_.size(); + header->field_ids_off_ = fields_.empty() ? 0u : field_ids_offset; + + uint32_t method_ids_offset = field_ids_offset + fields_.size() * sizeof(DexFile::FieldId); + uint32_t method_idx = 0u; + for (auto& entry : methods_) { + entry.second = method_idx; + method_idx += 1u; + } + header->method_ids_size_ = methods_.size(); + header->method_ids_off_ = methods_.empty() ? 0u : method_ids_offset; + + // No class defs. + header->class_defs_size_ = 0u; + header->class_defs_off_ = 0u; + + uint32_t data_section_offset = method_ids_offset + methods_.size() * sizeof(DexFile::MethodId); + header->data_size_ = data_section_size; + header->data_off_ = (data_section_size != 0u) ? data_section_offset : 0u; + + uint32_t total_size = data_section_offset + data_section_size; + + dex_file_data_.resize(total_size); + std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); + + for (const auto& entry : strings_) { + CHECK_LT(entry.first.size(), 128u); + uint32_t raw_offset = data_section_offset + entry.second.data_offset; + dex_file_data_[raw_offset] = static_cast<uint8_t>(entry.first.size()); + std::memcpy(&dex_file_data_[raw_offset + 1], entry.first.c_str(), entry.first.size() + 1); + Write32(string_ids_offset + entry.second.idx * sizeof(DexFile::StringId), raw_offset); + } + + for (const auto& entry : types_) { + Write32(type_ids_offset + entry.second * sizeof(DexFile::TypeId), GetStringIdx(entry.first)); + ++type_idx; + } + + for (const auto& entry : protos_) { + size_t num_args = entry.first.args.size(); + uint32_t type_list_offset = + (num_args != 0u) ? data_section_offset + entry.second.data_offset : 0u; + uint32_t raw_offset = proto_ids_offset + entry.second.idx * sizeof(DexFile::ProtoId); + Write32(raw_offset + 0u, GetStringIdx(entry.first.shorty)); + Write16(raw_offset + 4u, GetTypeIdx(entry.first.return_type)); + Write32(raw_offset + 8u, type_list_offset); + if (num_args != 0u) { + CHECK_NE(entry.second.data_offset, 0u); + Write32(type_list_offset, num_args); + for (size_t i = 0; i != num_args; ++i) { + Write16(type_list_offset + 4u + i * sizeof(DexFile::TypeItem), + GetTypeIdx(entry.first.args[i])); + } + } + } + + for (const auto& entry : fields_) { + uint32_t raw_offset = field_ids_offset + entry.second * sizeof(DexFile::FieldId); + Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor)); + Write16(raw_offset + 2u, GetTypeIdx(entry.first.type)); + Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); + } + + for (const auto& entry : methods_) { + uint32_t raw_offset = method_ids_offset + entry.second * sizeof(DexFile::MethodId); + Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor)); + auto it = protos_.find(*entry.first.proto); + CHECK(it != protos_.end()); + Write16(raw_offset + 2u, it->second.idx); + Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); + } + + // Leave checksum and signature as zeros. + + std::string error_msg; + std::unique_ptr<const DexFile> dex_file(DexFile::Open( + &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, &error_msg)); + CHECK(dex_file != nullptr) << error_msg; + return std::move(dex_file); + } + + uint32_t GetStringIdx(const std::string& type) { + auto it = strings_.find(type); + CHECK(it != strings_.end()); + return it->second.idx; + } + + uint32_t GetTypeIdx(const std::string& type) { + auto it = types_.find(type); + CHECK(it != types_.end()); + return it->second; + } + + uint32_t GetFieldIdx(const std::string& class_descriptor, const std::string& type, + const std::string& name) { + FieldKey key = { class_descriptor, type, name }; + auto it = fields_.find(key); + CHECK(it != fields_.end()); + return it->second; + } + + uint32_t GetMethodIdx(const std::string& class_descriptor, const std::string& signature, + const std::string& name) { + ProtoKey proto_key = CreateProtoKey(signature); + MethodKey method_key = { class_descriptor, name, &proto_key }; + auto it = methods_.find(method_key); + CHECK(it != methods_.end()); + return it->second; + } + + private: + struct IdxAndDataOffset { + uint32_t idx; + uint32_t data_offset; + }; + + struct FieldKey { + const std::string class_descriptor; + const std::string type; + const std::string name; + }; + struct FieldKeyComparator { + bool operator()(const FieldKey& lhs, const FieldKey& rhs) const { + if (lhs.class_descriptor != rhs.class_descriptor) { + return lhs.class_descriptor < rhs.class_descriptor; + } + if (lhs.name != rhs.name) { + return lhs.name < rhs.name; + } + return lhs.type < rhs.type; + } + }; + + struct ProtoKey { + std::string shorty; + std::string return_type; + std::vector<std::string> args; + }; + struct ProtoKeyComparator { + bool operator()(const ProtoKey& lhs, const ProtoKey& rhs) const { + if (lhs.return_type != rhs.return_type) { + return lhs.return_type < rhs.return_type; + } + size_t min_args = std::min(lhs.args.size(), rhs.args.size()); + for (size_t i = 0; i != min_args; ++i) { + if (lhs.args[i] != rhs.args[i]) { + return lhs.args[i] < rhs.args[i]; + } + } + return lhs.args.size() < rhs.args.size(); + } + }; + + struct MethodKey { + std::string class_descriptor; + std::string name; + const ProtoKey* proto; + }; + struct MethodKeyComparator { + bool operator()(const MethodKey& lhs, const MethodKey& rhs) const { + if (lhs.class_descriptor != rhs.class_descriptor) { + return lhs.class_descriptor < rhs.class_descriptor; + } + if (lhs.name != rhs.name) { + return lhs.name < rhs.name; + } + return ProtoKeyComparator()(*lhs.proto, *rhs.proto); + } + }; + + ProtoKey CreateProtoKey(const std::string& signature) { + CHECK_EQ(signature[0], '('); + const char* args = signature.c_str() + 1; + const char* args_end = std::strchr(args, ')'); + CHECK(args_end != nullptr); + const char* return_type = args_end + 1; + + ProtoKey key = { + std::string() + ((*return_type == '[') ? 'L' : *return_type), + return_type, + std::vector<std::string>() + }; + while (args != args_end) { + key.shorty += (*args == '[') ? 'L' : *args; + const char* arg_start = args; + while (*args == '[') { + ++args; + } + if (*args == 'L') { + do { + ++args; + CHECK_NE(args, args_end); + } while (*args != ';'); + } + ++args; + key.args.emplace_back(arg_start, args); + } + return key; + } + + void Write32(size_t offset, uint32_t value) { + CHECK_LE(offset + 4u, dex_file_data_.size()); + CHECK_EQ(dex_file_data_[offset + 0], 0u); + CHECK_EQ(dex_file_data_[offset + 1], 0u); + CHECK_EQ(dex_file_data_[offset + 2], 0u); + CHECK_EQ(dex_file_data_[offset + 3], 0u); + dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0); + dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8); + dex_file_data_[offset + 2] = static_cast<uint8_t>(value >> 16); + dex_file_data_[offset + 3] = static_cast<uint8_t>(value >> 24); + } + + void Write16(size_t offset, uint32_t value) { + CHECK_LE(value, 0xffffu); + CHECK_LE(offset + 2u, dex_file_data_.size()); + CHECK_EQ(dex_file_data_[offset + 0], 0u); + CHECK_EQ(dex_file_data_[offset + 1], 0u); + dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0); + dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8); + } + + std::map<std::string, IdxAndDataOffset> strings_; + std::map<std::string, uint32_t> types_; + std::map<FieldKey, uint32_t, FieldKeyComparator> fields_; + std::map<ProtoKey, IdxAndDataOffset, ProtoKeyComparator> protos_; + std::map<MethodKey, uint32_t, MethodKeyComparator> methods_; + + std::vector<uint8_t> dex_file_data_; +}; + +} // namespace art + +#endif // ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ |