Home | History | Annotate | Download | only in dex
      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