| /* | 
 |  * 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 |