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