/*
 * Copyright (C) 2018 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_CLASS_ACCESSOR_INL_H_
#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_

#include "class_accessor.h"

#include "base/hiddenapi_flags.h"
#include "base/leb128.h"
#include "base/utils.h"
#include "class_iterator.h"
#include "code_item_accessors-inl.h"
#include "dex_file.h"
#include "method_reference.h"

namespace art {

inline ClassAccessor::ClassAccessor(const ClassIteratorData& data)
    : ClassAccessor(data.dex_file_, data.class_def_idx_) {}

inline ClassAccessor::ClassAccessor(const DexFile& dex_file,
                                    const dex::ClassDef& class_def,
                                    bool parse_hiddenapi_class_data)
    : ClassAccessor(dex_file,
                    dex_file.GetClassData(class_def),
                    dex_file.GetIndexForClassDef(class_def),
                    parse_hiddenapi_class_data) {}

inline ClassAccessor::ClassAccessor(const DexFile& dex_file, uint32_t class_def_index)
    : ClassAccessor(dex_file, dex_file.GetClassDef(class_def_index)) {}

inline ClassAccessor::ClassAccessor(const DexFile& dex_file,
                                    const uint8_t* class_data,
                                    uint32_t class_def_index,
                                    bool parse_hiddenapi_class_data)
    : dex_file_(dex_file),
      class_def_index_(class_def_index),
      ptr_pos_(class_data),
      hiddenapi_ptr_pos_(nullptr),
      num_static_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
      num_instance_fields_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
      num_direct_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u),
      num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {
  if (parse_hiddenapi_class_data && class_def_index != DexFile::kDexNoIndex32) {
    const dex::HiddenapiClassData* hiddenapi_class_data = dex_file.GetHiddenapiClassData();
    if (hiddenapi_class_data != nullptr) {
      hiddenapi_ptr_pos_ = hiddenapi_class_data->GetFlagsPointer(class_def_index);
    }
  }
}

inline void ClassAccessor::Method::Read() {
  index_ += DecodeUnsignedLeb128(&ptr_pos_);
  access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
  code_off_ = DecodeUnsignedLeb128(&ptr_pos_);
  if (hiddenapi_ptr_pos_ != nullptr) {
    hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_);
    DCHECK(hiddenapi::ApiList(hiddenapi_flags_).IsValid());
  }
}

inline MethodReference ClassAccessor::Method::GetReference() const {
  return MethodReference(&dex_file_, GetIndex());
}


inline void ClassAccessor::Field::Read() {
  index_ += DecodeUnsignedLeb128(&ptr_pos_);
  access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
  if (hiddenapi_ptr_pos_ != nullptr) {
    hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_);
    DCHECK(hiddenapi::ApiList(hiddenapi_flags_).IsValid());
  }
}

template <typename DataType, typename Visitor>
inline void ClassAccessor::VisitMembers(size_t count,
                                        const Visitor& visitor,
                                        DataType* data) const {
  DCHECK(data != nullptr);
  for ( ; count != 0; --count) {
    data->Read();
    visitor(*data);
  }
}

template <typename StaticFieldVisitor,
          typename InstanceFieldVisitor,
          typename DirectMethodVisitor,
          typename VirtualMethodVisitor>
inline void ClassAccessor::VisitFieldsAndMethods(
    const StaticFieldVisitor& static_field_visitor,
    const InstanceFieldVisitor& instance_field_visitor,
    const DirectMethodVisitor& direct_method_visitor,
    const VirtualMethodVisitor& virtual_method_visitor) const {
  Field field(dex_file_, ptr_pos_, hiddenapi_ptr_pos_);
  VisitMembers(num_static_fields_, static_field_visitor, &field);
  field.NextSection();
  VisitMembers(num_instance_fields_, instance_field_visitor, &field);

  Method method(dex_file_, field.ptr_pos_, field.hiddenapi_ptr_pos_, /*is_static_or_direct*/ true);
  VisitMembers(num_direct_methods_, direct_method_visitor, &method);
  method.NextSection();
  VisitMembers(num_virtual_methods_, virtual_method_visitor, &method);
}

template <typename DirectMethodVisitor,
          typename VirtualMethodVisitor>
inline void ClassAccessor::VisitMethods(const DirectMethodVisitor& direct_method_visitor,
                                        const VirtualMethodVisitor& virtual_method_visitor) const {
  VisitFieldsAndMethods(VoidFunctor(),
                        VoidFunctor(),
                        direct_method_visitor,
                        virtual_method_visitor);
}

template <typename StaticFieldVisitor,
          typename InstanceFieldVisitor>
inline void ClassAccessor::VisitFields(const StaticFieldVisitor& static_field_visitor,
                                       const InstanceFieldVisitor& instance_field_visitor) const {
  VisitFieldsAndMethods(static_field_visitor,
                        instance_field_visitor,
                        VoidFunctor(),
                        VoidFunctor());
}

inline const dex::CodeItem* ClassAccessor::GetCodeItem(const Method& method) const {
  return dex_file_.GetCodeItem(method.GetCodeItemOffset());
}

inline CodeItemInstructionAccessor ClassAccessor::Method::GetInstructions() const {
  return CodeItemInstructionAccessor(dex_file_, dex_file_.GetCodeItem(GetCodeItemOffset()));
}

inline CodeItemDataAccessor ClassAccessor::Method::GetInstructionsAndData() const {
  return CodeItemDataAccessor(dex_file_, dex_file_.GetCodeItem(GetCodeItemOffset()));
}

inline const char* ClassAccessor::GetDescriptor() const {
  return dex_file_.StringByTypeIdx(GetClassIdx());
}

inline const dex::CodeItem* ClassAccessor::Method::GetCodeItem() const {
  return dex_file_.GetCodeItem(code_off_);
}

inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Field>>
    ClassAccessor::GetFieldsInternal(size_t count) const {
  return {
      DataIterator<Field>(dex_file_,
                          0u,
                          num_static_fields_,
                          count,
                          ptr_pos_,
                          hiddenapi_ptr_pos_),
      DataIterator<Field>(dex_file_,
                          count,
                          num_static_fields_,
                          count,
                          // The following pointers are bogus but unused in the `end` iterator.
                          ptr_pos_,
                          hiddenapi_ptr_pos_) };
}

// Return an iteration range for the first <count> methods.
inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Method>>
    ClassAccessor::GetMethodsInternal(size_t count) const {
  // Skip over the fields.
  Field field(dex_file_, ptr_pos_, hiddenapi_ptr_pos_);
  VisitMembers(NumFields(), VoidFunctor(), &field);
  // Return the iterator pair.
  return {
      DataIterator<Method>(dex_file_,
                           0u,
                           num_direct_methods_,
                           count,
                           field.ptr_pos_,
                           field.hiddenapi_ptr_pos_),
      DataIterator<Method>(dex_file_,
                           count,
                           num_direct_methods_,
                           count,
                           // The following pointers are bogus but unused in the `end` iterator.
                           field.ptr_pos_,
                           field.hiddenapi_ptr_pos_) };
}

inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Field>> ClassAccessor::GetFields()
    const {
  return GetFieldsInternal(num_static_fields_ + num_instance_fields_);
}

inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Field>>
    ClassAccessor::GetStaticFields() const {
  return GetFieldsInternal(num_static_fields_);
}


inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Field>>
    ClassAccessor::GetInstanceFields() const {
  IterationRange<ClassAccessor::DataIterator<ClassAccessor::Field>> fields = GetFields();
  // Skip the static fields.
  return { std::next(fields.begin(), NumStaticFields()), fields.end() };
}

inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Method>>
    ClassAccessor::GetMethods() const {
  return GetMethodsInternal(NumMethods());
}

inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Method>>
    ClassAccessor::GetDirectMethods() const {
  return GetMethodsInternal(NumDirectMethods());
}

inline IterationRange<ClassAccessor::DataIterator<ClassAccessor::Method>>
    ClassAccessor::GetVirtualMethods() const {
  IterationRange<DataIterator<Method>> methods = GetMethods();
  // Skip the direct fields.
  return { std::next(methods.begin(), NumDirectMethods()), methods.end() };
}

inline dex::TypeIndex ClassAccessor::GetClassIdx() const {
  return dex_file_.GetClassDef(class_def_index_).class_idx_;
}

inline const dex::ClassDef& ClassAccessor::GetClassDef() const {
  return dex_file_.GetClassDef(GetClassDefIndex());
}

}  // namespace art

#endif  // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_