blob: 3f1d5242868428a28ba8016de543afabd07fc786 [file] [log] [blame]
David Brazdil86ea7ee2016-02-16 09:26:07 +00001/*
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
David Sehr312f3b22018-03-19 08:39:26 -070017#ifndef ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_
18#define ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_
David Brazdil86ea7ee2016-02-16 09:26:07 +000019
David Sehr312f3b22018-03-19 08:39:26 -070020#include "base/value_object.h"
David Sehr9e734c72018-01-04 17:56:19 -080021#include "dex/dex_file-inl.h"
22#include "dex/dex_file.h"
23#include "dex/dex_instruction-inl.h"
David Brazdil86ea7ee2016-02-16 09:26:07 +000024
25namespace art {
26
David Brazdil86ea7ee2016-02-16 09:26:07 +000027class 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
104class 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
David Brazdil86ea7ee2016-02-16 09:26:07 +0000138inline 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
David Sehr312f3b22018-03-19 08:39:26 -0700147#endif // ART_LIBDEXFILE_DEX_BYTECODE_UTILS_H_