/*
 * Copyright (C) 2012 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_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
#define ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_

#include "class_linker.h"
#include "class_root.h"
#include "mirror/class-inl.h"
#include "mirror/method_handle_impl.h"
#include "mirror/method_type.h"
#include "mirror/string.h"
#include "mirror/throwable.h"
#include "reg_type.h"
#include "reg_type_cache.h"

namespace art {
namespace verifier {

inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const {
  DCHECK_LT(id, entries_.size());
  const RegType* result = entries_[id];
  DCHECK(result != nullptr);
  return *result;
}

inline const ConstantType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
  // We only expect 0 to be a precise constant.
  DCHECK(value != 0 || precise);
  if (precise && (value >= kMinSmallConstant) && (value <= kMaxSmallConstant)) {
    return *small_precise_constants_[value - kMinSmallConstant];
  }
  return FromCat1NonSmallConstant(value, precise);
}

inline const BooleanType& RegTypeCache::Boolean() {
  return *BooleanType::GetInstance();
}
inline const ByteType& RegTypeCache::Byte() {
  return *ByteType::GetInstance();
}
inline const CharType& RegTypeCache::Char() {
  return *CharType::GetInstance();
}
inline const ShortType& RegTypeCache::Short() {
  return *ShortType::GetInstance();
}
inline const IntegerType& RegTypeCache::Integer() {
  return *IntegerType::GetInstance();
}
inline const FloatType& RegTypeCache::Float() {
  return *FloatType::GetInstance();
}
inline const LongLoType& RegTypeCache::LongLo() {
  return *LongLoType::GetInstance();
}
inline const LongHiType& RegTypeCache::LongHi() {
  return *LongHiType::GetInstance();
}
inline const DoubleLoType& RegTypeCache::DoubleLo() {
  return *DoubleLoType::GetInstance();
}
inline const DoubleHiType& RegTypeCache::DoubleHi() {
  return *DoubleHiType::GetInstance();
}
inline const UndefinedType& RegTypeCache::Undefined() {
  return *UndefinedType::GetInstance();
}
inline const ConflictType& RegTypeCache::Conflict() {
  return *ConflictType::GetInstance();
}
inline const NullType& RegTypeCache::Null() {
  return *NullType::GetInstance();
}

inline const ImpreciseConstType& RegTypeCache::ByteConstant() {
  const ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false);
  DCHECK(result.IsImpreciseConstant());
  return *down_cast<const ImpreciseConstType*>(&result);
}

inline const ImpreciseConstType& RegTypeCache::CharConstant() {
  int32_t jchar_max = static_cast<int32_t>(std::numeric_limits<jchar>::max());
  const ConstantType& result =  FromCat1Const(jchar_max, false);
  DCHECK(result.IsImpreciseConstant());
  return *down_cast<const ImpreciseConstType*>(&result);
}

inline const ImpreciseConstType& RegTypeCache::ShortConstant() {
  const ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::min(), false);
  DCHECK(result.IsImpreciseConstant());
  return *down_cast<const ImpreciseConstType*>(&result);
}

inline const ImpreciseConstType& RegTypeCache::IntConstant() {
  const ConstantType& result = FromCat1Const(std::numeric_limits<jint>::max(), false);
  DCHECK(result.IsImpreciseConstant());
  return *down_cast<const ImpreciseConstType*>(&result);
}

inline const ImpreciseConstType& RegTypeCache::PosByteConstant() {
  const ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::max(), false);
  DCHECK(result.IsImpreciseConstant());
  return *down_cast<const ImpreciseConstType*>(&result);
}

inline const ImpreciseConstType& RegTypeCache::PosShortConstant() {
  const ConstantType& result =  FromCat1Const(std::numeric_limits<jshort>::max(), false);
  DCHECK(result.IsImpreciseConstant());
  return *down_cast<const ImpreciseConstType*>(&result);
}

inline const PreciseReferenceType& RegTypeCache::JavaLangClass() {
  const RegType* result = &FromClass("Ljava/lang/Class;",
                                     GetClassRoot<mirror::Class>(),
                                     /* precise= */ true);
  DCHECK(result->IsPreciseReference());
  return *down_cast<const PreciseReferenceType*>(result);
}

inline const PreciseReferenceType& RegTypeCache::JavaLangString() {
  // String is final and therefore always precise.
  const RegType* result = &FromClass("Ljava/lang/String;",
                                     GetClassRoot<mirror::String>(),
                                     /* precise= */ true);
  DCHECK(result->IsPreciseReference());
  return *down_cast<const PreciseReferenceType*>(result);
}

inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodHandle() {
  const RegType* result = &FromClass("Ljava/lang/invoke/MethodHandle;",
                                     GetClassRoot<mirror::MethodHandle>(),
                                     /* precise= */ true);
  DCHECK(result->IsPreciseReference());
  return *down_cast<const PreciseReferenceType*>(result);
}

inline const PreciseReferenceType& RegTypeCache::JavaLangInvokeMethodType() {
  const RegType* result = &FromClass("Ljava/lang/invoke/MethodType;",
                                     GetClassRoot<mirror::MethodType>(),
                                     /* precise= */ true);
  DCHECK(result->IsPreciseReference());
  return *down_cast<const PreciseReferenceType*>(result);
}

inline const RegType&  RegTypeCache::JavaLangThrowable(bool precise) {
  const RegType* result = &FromClass("Ljava/lang/Throwable;",
                                     GetClassRoot<mirror::Throwable>(),
                                     precise);
  if (precise) {
    DCHECK(result->IsPreciseReference());
    return *down_cast<const PreciseReferenceType*>(result);
  } else {
    DCHECK(result->IsReference());
    return *down_cast<const ReferenceType*>(result);
  }
}

inline const RegType& RegTypeCache::JavaLangObject(bool precise) {
  const RegType* result = &FromClass("Ljava/lang/Object;", GetClassRoot<mirror::Object>(), precise);
  if (precise) {
    DCHECK(result->IsPreciseReference());
    return *down_cast<const PreciseReferenceType*>(result);
  } else {
    DCHECK(result->IsReference());
    return *down_cast<const ReferenceType*>(result);
  }
}

template <class RegTypeType>
inline RegTypeType& RegTypeCache::AddEntry(RegTypeType* new_entry) {
  DCHECK(new_entry != nullptr);
  entries_.push_back(new_entry);
  if (new_entry->HasClass()) {
    ObjPtr<mirror::Class> klass = new_entry->GetClass();
    DCHECK(!klass->IsPrimitive());
    klass_entries_.push_back(std::make_pair(GcRoot<mirror::Class>(klass), new_entry));
  }
  return *new_entry;
}

}  // namespace verifier
}  // namespace art
#endif  // ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
