1 /* 2 * Copyright (C) 2016 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_LIBDEXFILE_DEX_BYTECODE_UTILS_H_ 18 #define ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_ 19 20 #include "base/value_object.h" 21 #include "dex/dex_file-inl.h" 22 #include "dex/dex_file.h" 23 #include "dex/dex_instruction-inl.h" 24 25 namespace art { 26 27 class DexSwitchTable : public ValueObject { 28 public: 29 DexSwitchTable(const Instruction& instruction, uint32_t dex_pc) 30 : instruction_(instruction), 31 dex_pc_(dex_pc), 32 sparse_(instruction.Opcode() == Instruction::SPARSE_SWITCH) { 33 int32_t table_offset = instruction.VRegB_31t(); 34 const uint16_t* table = reinterpret_cast<const uint16_t*>(&instruction) + table_offset; 35 DCHECK_EQ(table[0], sparse_ ? static_cast<uint16_t>(Instruction::kSparseSwitchSignature) 36 : static_cast<uint16_t>(Instruction::kPackedSwitchSignature)); 37 num_entries_ = table[1]; 38 values_ = reinterpret_cast<const int32_t*>(&table[2]); 39 } 40 41 uint16_t GetNumEntries() const { 42 return num_entries_; 43 } 44 45 void CheckIndex(size_t index) const { 46 if (sparse_) { 47 // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order. 48 DCHECK_LT(index, 2 * static_cast<size_t>(num_entries_)); 49 } else { 50 // In a packed table, we have the starting key and num_entries_ values. 51 DCHECK_LT(index, 1 + static_cast<size_t>(num_entries_)); 52 } 53 } 54 55 int32_t GetEntryAt(size_t index) const { 56 CheckIndex(index); 57 return values_[index]; 58 } 59 60 uint32_t GetDexPcForIndex(size_t index) const { 61 CheckIndex(index); 62 return dex_pc_ + 63 (reinterpret_cast<const int16_t*>(values_ + index) - 64 reinterpret_cast<const int16_t*>(&instruction_)); 65 } 66 67 // Index of the first value in the table. 68 size_t GetFirstValueIndex() const { 69 if (sparse_) { 70 // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order. 71 return num_entries_; 72 } else { 73 // In a packed table, we have the starting key and num_entries_ values. 74 return 1; 75 } 76 } 77 78 bool IsSparse() const { return sparse_; } 79 80 bool ShouldBuildDecisionTree() { 81 return IsSparse() || GetNumEntries() <= kSmallSwitchThreshold; 82 } 83 84 private: 85 const Instruction& instruction_; 86 const uint32_t dex_pc_; 87 88 // Whether this is a sparse-switch table (or a packed-switch one). 89 const bool sparse_; 90 91 // This can't be const as it needs to be computed off of the given instruction, and complicated 92 // expressions in the initializer list seemed very ugly. 93 uint16_t num_entries_; 94 95 const int32_t* values_; 96 97 // The number of entries in a packed switch before we use a jump table or specified 98 // compare/jump series. 99 static constexpr uint16_t kSmallSwitchThreshold = 3; 100 101 DISALLOW_COPY_AND_ASSIGN(DexSwitchTable); 102 }; 103 104 class DexSwitchTableIterator { 105 public: 106 explicit DexSwitchTableIterator(const DexSwitchTable& table) 107 : table_(table), 108 num_entries_(static_cast<size_t>(table_.GetNumEntries())), 109 first_target_offset_(table_.GetFirstValueIndex()), 110 index_(0u) {} 111 112 bool Done() const { return index_ >= num_entries_; } 113 bool IsLast() const { return index_ == num_entries_ - 1; } 114 115 void Advance() { 116 DCHECK(!Done()); 117 index_++; 118 } 119 120 int32_t CurrentKey() const { 121 return table_.IsSparse() ? table_.GetEntryAt(index_) : table_.GetEntryAt(0) + index_; 122 } 123 124 int32_t CurrentTargetOffset() const { 125 return table_.GetEntryAt(index_ + first_target_offset_); 126 } 127 128 uint32_t GetDexPcForCurrentIndex() const { return table_.GetDexPcForIndex(index_); } 129 130 private: 131 const DexSwitchTable& table_; 132 const size_t num_entries_; 133 const size_t first_target_offset_; 134 135 size_t index_; 136 }; 137 138 inline bool IsThrowingDexInstruction(const Instruction& instruction) { 139 // Special-case MONITOR_EXIT which is a throwing instruction but the verifier 140 // guarantees that it will never throw. This is necessary to avoid rejecting 141 // 'synchronized' blocks/methods. 142 return instruction.IsThrow() && instruction.Opcode() != Instruction::MONITOR_EXIT; 143 } 144 145 } // namespace art 146 147 #endif // ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_ 148