/*
 * Copyright (C) 2017 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.
 */

#ifndef ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_INL_H_
#define ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_INL_H_

#include "code_item_accessors.h"

#include "base/iteration_range.h"
#include "compact_dex_file.h"
#include "dex_file-inl.h"
#include "dex_instruction_iterator.h"
#include "standard_dex_file.h"

// The no ART version is used by binaries that don't include the whole runtime.
namespace art {

inline void CodeItemInstructionAccessor::Init(uint32_t insns_size_in_code_units,
                                              const uint16_t* insns) {
  insns_size_in_code_units_ = insns_size_in_code_units;
  insns_ = insns;
}

template <>
inline void CodeItemInstructionAccessor::Init<CompactDexFile::CodeItem>(
    const CompactDexFile::CodeItem& code_item) {
  uint32_t insns_size_in_code_units;
  code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ true>(
      &insns_size_in_code_units,
      /*registers_size*/ nullptr,
      /*ins_size*/ nullptr,
      /*outs_size*/ nullptr,
      /*tries_size*/ nullptr);
  Init(insns_size_in_code_units, code_item.insns_);
}

template <>
inline void CodeItemInstructionAccessor::Init<StandardDexFile::CodeItem>(
    const StandardDexFile::CodeItem& code_item) {
  Init(code_item.insns_size_in_code_units_, code_item.insns_);
}

inline void CodeItemInstructionAccessor::Init(const DexFile& dex_file,
                                              const dex::CodeItem* code_item) {
  if (code_item != nullptr) {
    DCHECK(dex_file.IsInDataSection(code_item));
    if (dex_file.IsCompactDexFile()) {
      Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
    } else {
      DCHECK(dex_file.IsStandardDexFile());
      Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
    }
  }
}

inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(
    const DexFile& dex_file,
    const dex::CodeItem* code_item) {
  Init(dex_file, code_item);
}

inline DexInstructionIterator CodeItemInstructionAccessor::begin() const {
  return DexInstructionIterator(insns_, 0u);
}

inline DexInstructionIterator CodeItemInstructionAccessor::end() const {
  return DexInstructionIterator(insns_, insns_size_in_code_units_);
}

inline IterationRange<DexInstructionIterator> CodeItemInstructionAccessor::InstructionsFrom(
    uint32_t start_dex_pc) const {
  DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits());
  return {
      DexInstructionIterator(insns_, start_dex_pc),
      DexInstructionIterator(insns_, insns_size_in_code_units_) };
}

template <>
inline void CodeItemDataAccessor::Init<CompactDexFile::CodeItem>(
    const CompactDexFile::CodeItem& code_item) {
  uint32_t insns_size_in_code_units;
  code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ false>(&insns_size_in_code_units,
                                                                &registers_size_,
                                                                &ins_size_,
                                                                &outs_size_,
                                                                &tries_size_);
  CodeItemInstructionAccessor::Init(insns_size_in_code_units, code_item.insns_);
}

template <>
inline void CodeItemDataAccessor::Init<StandardDexFile::CodeItem>(
    const StandardDexFile::CodeItem& code_item) {
  CodeItemInstructionAccessor::Init(code_item);
  registers_size_ = code_item.registers_size_;
  ins_size_ = code_item.ins_size_;
  outs_size_ = code_item.outs_size_;
  tries_size_ = code_item.tries_size_;
}

inline void CodeItemDataAccessor::Init(const DexFile& dex_file,
                                       const dex::CodeItem* code_item) {
  if (code_item != nullptr) {
    if (dex_file.IsCompactDexFile()) {
      Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
    } else {
      DCHECK(dex_file.IsStandardDexFile());
      Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
    }
  }
}

inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile& dex_file,
                                                  const dex::CodeItem* code_item) {
  Init(dex_file, code_item);
}

inline IterationRange<const dex::TryItem*> CodeItemDataAccessor::TryItems() const {
  const dex::TryItem* try_items = DexFile::GetTryItems(end(), 0u);
  return {
    try_items,
    try_items + TriesSize() };
}

inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const {
  return DexFile::GetCatchHandlerData(end(), TriesSize(), offset);
}

inline const dex::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const {
  IterationRange<const dex::TryItem*> try_items(TryItems());
  int32_t index = DexFile::FindTryItem(try_items.begin(),
                                       try_items.end() - try_items.begin(),
                                       try_dex_pc);
  return index != -1 ? &try_items.begin()[index] : nullptr;
}

inline const void* CodeItemDataAccessor::CodeItemDataEnd() const {
  const uint8_t* handler_data = GetCatchHandlerData();

  if (TriesSize() == 0 || handler_data == nullptr) {
    return &end().Inst();
  }
  // Get the start of the handler data.
  const uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data);
  // Manually read each handler.
  for (uint32_t i = 0; i < handlers_size; ++i) {
    int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2;
    if (uleb128_count <= 0) {
      uleb128_count = -uleb128_count + 1;
    }
    for (int32_t j = 0; j < uleb128_count; ++j) {
      DecodeUnsignedLeb128(&handler_data);
    }
  }
  return reinterpret_cast<const void*>(handler_data);
}

template <>
inline void CodeItemDebugInfoAccessor::Init<CompactDexFile::CodeItem>(
    const CompactDexFile::CodeItem& code_item,
    uint32_t dex_method_index) {
  debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset(
      dex_method_index);
  CodeItemDataAccessor::Init(code_item);
}

template <>
inline void CodeItemDebugInfoAccessor::Init<StandardDexFile::CodeItem>(
    const StandardDexFile::CodeItem& code_item,
    uint32_t dex_method_index ATTRIBUTE_UNUSED) {
  debug_info_offset_ = code_item.debug_info_off_;
  CodeItemDataAccessor::Init(code_item);
}

inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file,
                                            const dex::CodeItem* code_item,
                                            uint32_t dex_method_index) {
  if (code_item == nullptr) {
    return;
  }
  dex_file_ = &dex_file;
  if (dex_file.IsCompactDexFile()) {
    Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index);
  } else {
    DCHECK(dex_file.IsStandardDexFile());
    Init(down_cast<const StandardDexFile::CodeItem&>(*code_item), dex_method_index);
  }
}

template<typename NewLocalVisitor>
inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(
    bool is_static,
    uint32_t method_idx,
    const NewLocalVisitor& new_local) const {
  return dex_file_->DecodeDebugLocalInfo(RegistersSize(),
                                         InsSize(),
                                         InsnsSizeInCodeUnits(),
                                         DebugInfoOffset(),
                                         is_static,
                                         method_idx,
                                         new_local);
}

template <typename Visitor>
inline uint32_t CodeItemDebugInfoAccessor::VisitParameterNames(const Visitor& visitor) const {
  const uint8_t* stream = dex_file_->GetDebugInfoStream(DebugInfoOffset());
  return (stream != nullptr) ? DexFile::DecodeDebugInfoParameterNames(&stream, visitor) : 0u;
}

inline bool CodeItemDebugInfoAccessor::GetLineNumForPc(const uint32_t address,
                                                       uint32_t* line_num) const {
  return DecodeDebugPositionInfo([&](const DexFile::PositionInfo& entry) {
    // We know that this callback will be called in ascending address order, so keep going until we
    // find a match or we've just gone past it.
    if (entry.address_ > address) {
      // The line number from the previous positions callback will be the final result.
      return true;
    }
    *line_num = entry.line_;
    return entry.address_ == address;
  });
}

template <typename Visitor>
inline bool CodeItemDebugInfoAccessor::DecodeDebugPositionInfo(const Visitor& visitor) const {
  return dex_file_->DecodeDebugPositionInfo(
      dex_file_->GetDebugInfoStream(DebugInfoOffset()),
      [this](uint32_t idx) {
        return dex_file_->StringDataByIdx(dex::StringIndex(idx));
      },
      visitor);
}

}  // namespace art

#endif  // ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_INL_H_
