| /* |
| * Copyright (C) 2016 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. |
| */ |
| |
| #include "dex_to_dex_decompiler.h" |
| |
| #include <android-base/logging.h> |
| |
| #include "base/macros.h" |
| #include "base/mutex.h" |
| #include "dex/bytecode_utils.h" |
| #include "dex/code_item_accessors-inl.h" |
| #include "dex/dex_file-inl.h" |
| #include "dex/dex_instruction-inl.h" |
| #include "quicken_info.h" |
| |
| namespace art { |
| namespace optimizer { |
| |
| class DexDecompiler { |
| public: |
| DexDecompiler(const DexFile& dex_file, |
| const dex::CodeItem& code_item, |
| const ArrayRef<const uint8_t>& quickened_info, |
| bool decompile_return_instruction) |
| : code_item_accessor_(dex_file, &code_item), |
| quicken_info_(quickened_info), |
| decompile_return_instruction_(decompile_return_instruction) {} |
| |
| bool Decompile(); |
| |
| private: |
| void DecompileInstanceFieldAccess(Instruction* inst, Instruction::Code new_opcode) { |
| uint16_t index = NextIndex(); |
| inst->SetOpcode(new_opcode); |
| inst->SetVRegC_22c(index); |
| } |
| |
| void DecompileInvokeVirtual(Instruction* inst, Instruction::Code new_opcode, bool is_range) { |
| const uint16_t index = NextIndex(); |
| inst->SetOpcode(new_opcode); |
| if (is_range) { |
| inst->SetVRegB_3rc(index); |
| } else { |
| inst->SetVRegB_35c(index); |
| } |
| } |
| |
| void DecompileNop(Instruction* inst) { |
| const uint16_t reference_index = NextIndex(); |
| if (reference_index == DexFile::kDexNoIndex16) { |
| // This means it was a normal nop and not a check-cast. |
| return; |
| } |
| const uint16_t type_index = NextIndex(); |
| inst->SetOpcode(Instruction::CHECK_CAST); |
| inst->SetVRegA_21c(reference_index); |
| inst->SetVRegB_21c(type_index); |
| } |
| |
| uint16_t NextIndex() { |
| DCHECK_LT(quicken_index_, quicken_info_.NumIndices()); |
| const uint16_t ret = quicken_info_.GetData(quicken_index_); |
| quicken_index_++; |
| return ret; |
| } |
| |
| const CodeItemInstructionAccessor code_item_accessor_; |
| const QuickenInfoTable quicken_info_; |
| const bool decompile_return_instruction_; |
| |
| size_t quicken_index_ = 0u; |
| |
| DISALLOW_COPY_AND_ASSIGN(DexDecompiler); |
| }; |
| |
| bool DexDecompiler::Decompile() { |
| // We need to iterate over the code item, and not over the quickening data, |
| // because the RETURN_VOID quickening is not encoded in the quickening data. Because |
| // unquickening is a rare need and not performance sensitive, it is not worth the |
| // added storage to also add the RETURN_VOID quickening in the quickened data. |
| for (const DexInstructionPcPair& pair : code_item_accessor_) { |
| Instruction* inst = const_cast<Instruction*>(&pair.Inst()); |
| |
| switch (inst->Opcode()) { |
| case Instruction::RETURN_VOID_NO_BARRIER: |
| if (decompile_return_instruction_) { |
| inst->SetOpcode(Instruction::RETURN_VOID); |
| } |
| break; |
| |
| case Instruction::NOP: |
| if (quicken_info_.NumIndices() > 0) { |
| // Only try to decompile NOP if there are more than 0 indices. Not having |
| // any index happens when we unquicken a code item that only has |
| // RETURN_VOID_NO_BARRIER as quickened instruction. |
| DecompileNop(inst); |
| } |
| break; |
| |
| case Instruction::IGET_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IGET); |
| break; |
| |
| case Instruction::IGET_WIDE_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IGET_WIDE); |
| break; |
| |
| case Instruction::IGET_OBJECT_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IGET_OBJECT); |
| break; |
| |
| case Instruction::IGET_BOOLEAN_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IGET_BOOLEAN); |
| break; |
| |
| case Instruction::IGET_BYTE_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IGET_BYTE); |
| break; |
| |
| case Instruction::IGET_CHAR_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IGET_CHAR); |
| break; |
| |
| case Instruction::IGET_SHORT_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IGET_SHORT); |
| break; |
| |
| case Instruction::IPUT_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IPUT); |
| break; |
| |
| case Instruction::IPUT_BOOLEAN_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IPUT_BOOLEAN); |
| break; |
| |
| case Instruction::IPUT_BYTE_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IPUT_BYTE); |
| break; |
| |
| case Instruction::IPUT_CHAR_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IPUT_CHAR); |
| break; |
| |
| case Instruction::IPUT_SHORT_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IPUT_SHORT); |
| break; |
| |
| case Instruction::IPUT_WIDE_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IPUT_WIDE); |
| break; |
| |
| case Instruction::IPUT_OBJECT_QUICK: |
| DecompileInstanceFieldAccess(inst, Instruction::IPUT_OBJECT); |
| break; |
| |
| case Instruction::INVOKE_VIRTUAL_QUICK: |
| DecompileInvokeVirtual(inst, Instruction::INVOKE_VIRTUAL, false); |
| break; |
| |
| case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: |
| DecompileInvokeVirtual(inst, Instruction::INVOKE_VIRTUAL_RANGE, true); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| if (quicken_index_ != quicken_info_.NumIndices()) { |
| if (quicken_index_ == 0) { |
| LOG(WARNING) << "Failed to use any value in quickening info," |
| << " potentially due to duplicate methods."; |
| } else { |
| LOG(FATAL) << "Failed to use all values in quickening info." |
| << " Actual: " << std::hex << quicken_index_ |
| << " Expected: " << quicken_info_.NumIndices(); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool ArtDecompileDEX(const DexFile& dex_file, |
| const dex::CodeItem& code_item, |
| const ArrayRef<const uint8_t>& quickened_info, |
| bool decompile_return_instruction) { |
| if (quickened_info.size() == 0 && !decompile_return_instruction) { |
| return true; |
| } |
| DexDecompiler decompiler(dex_file, code_item, quickened_info, decompile_return_instruction); |
| return decompiler.Decompile(); |
| } |
| |
| } // namespace optimizer |
| } // namespace art |