blob: 3369784bd1a39837a1f32e7525ee5f6b41c3ac2a [file] [log] [blame]
/*
* 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_H_
#define ART_RUNTIME_VERIFIER_REG_TYPE_H_
#include <stdint.h>
#include <limits>
#include <set>
#include <string>
#include "base/arena_object.h"
#include "base/bit_vector.h"
#include "base/locks.h"
#include "base/macros.h"
#include "base/stringpiece.h"
#include "dex/primitive.h"
#include "gc_root.h"
#include "handle_scope.h"
#include "obj_ptr.h"
namespace art {
namespace mirror {
class Class;
class ClassLoader;
} // namespace mirror
class ArenaBitVector;
class ScopedArenaAllocator;
namespace verifier {
class MethodVerifier;
class RegTypeCache;
/*
* RegType holds information about the "type" of data held in a register.
*/
class RegType {
public:
virtual bool IsUndefined() const { return false; }
virtual bool IsConflict() const { return false; }
virtual bool IsBoolean() const { return false; }
virtual bool IsByte() const { return false; }
virtual bool IsChar() const { return false; }
virtual bool IsShort() const { return false; }
virtual bool IsInteger() const { return false; }
virtual bool IsLongLo() const { return false; }
virtual bool IsLongHi() const { return false; }
virtual bool IsFloat() const { return false; }
virtual bool IsDouble() const { return false; }
virtual bool IsDoubleLo() const { return false; }
virtual bool IsDoubleHi() const { return false; }
virtual bool IsUnresolvedReference() const { return false; }
virtual bool IsUninitializedReference() const { return false; }
virtual bool IsUninitializedThisReference() const { return false; }
virtual bool IsUnresolvedAndUninitializedReference() const { return false; }
virtual bool IsUnresolvedAndUninitializedThisReference() const {
return false;
}
virtual bool IsUnresolvedMergedReference() const { return false; }
virtual bool IsUnresolvedSuperClass() const { return false; }
virtual bool IsReference() const { return false; }
virtual bool IsPreciseReference() const { return false; }
virtual bool IsPreciseConstant() const { return false; }
virtual bool IsPreciseConstantLo() const { return false; }
virtual bool IsPreciseConstantHi() const { return false; }
virtual bool IsImpreciseConstantLo() const { return false; }
virtual bool IsImpreciseConstantHi() const { return false; }
virtual bool IsImpreciseConstant() const { return false; }
virtual bool IsConstantTypes() const { return false; }
bool IsConstant() const {
return IsImpreciseConstant() || IsPreciseConstant();
}
bool IsConstantLo() const {
return IsImpreciseConstantLo() || IsPreciseConstantLo();
}
bool IsPrecise() const {
return IsPreciseConstantLo() || IsPreciseConstant() ||
IsPreciseConstantHi();
}
bool IsLongConstant() const { return IsConstantLo(); }
bool IsConstantHi() const {
return (IsPreciseConstantHi() || IsImpreciseConstantHi());
}
bool IsLongConstantHigh() const { return IsConstantHi(); }
virtual bool IsUninitializedTypes() const { return false; }
virtual bool IsUnresolvedTypes() const { return false; }
bool IsLowHalf() const {
return (IsLongLo() || IsDoubleLo() || IsPreciseConstantLo() || IsImpreciseConstantLo());
}
bool IsHighHalf() const {
return (IsLongHi() || IsDoubleHi() || IsPreciseConstantHi() || IsImpreciseConstantHi());
}
bool IsLongOrDoubleTypes() const { return IsLowHalf(); }
// Check this is the low half, and that type_h is its matching high-half.
inline bool CheckWidePair(const RegType& type_h) const {
if (IsLowHalf()) {
return ((IsImpreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
(IsImpreciseConstantLo() && type_h.IsImpreciseConstantHi()) ||
(IsPreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
(IsPreciseConstantLo() && type_h.IsImpreciseConstantHi()) ||
(IsDoubleLo() && type_h.IsDoubleHi()) ||
(IsLongLo() && type_h.IsLongHi()));
}
return false;
}
// The high half that corresponds to this low half
const RegType& HighHalf(RegTypeCache* cache) const
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsConstantBoolean() const;
virtual bool IsConstantChar() const { return false; }
virtual bool IsConstantByte() const { return false; }
virtual bool IsConstantShort() const { return false; }
virtual bool IsOne() const { return false; }
virtual bool IsZero() const { return false; }
virtual bool IsNull() const { return false; }
bool IsReferenceTypes() const {
return IsNonZeroReferenceTypes() || IsZero() || IsNull();
}
bool IsZeroOrNull() const {
return IsZero() || IsNull();
}
virtual bool IsNonZeroReferenceTypes() const { return false; }
bool IsCategory1Types() const {
return IsChar() || IsInteger() || IsFloat() || IsConstant() || IsByte() ||
IsShort() || IsBoolean();
}
bool IsCategory2Types() const {
return IsLowHalf(); // Don't expect explicit testing of high halves
}
bool IsBooleanTypes() const { return IsBoolean() || IsConstantBoolean(); }
bool IsByteTypes() const {
return IsConstantByte() || IsByte() || IsBoolean();
}
bool IsShortTypes() const {
return IsShort() || IsByte() || IsBoolean() || IsConstantShort();
}
bool IsCharTypes() const {
return IsChar() || IsBooleanTypes() || IsConstantChar();
}
bool IsIntegralTypes() const {
return IsInteger() || IsConstant() || IsByte() || IsShort() || IsChar() ||
IsBoolean();
}
// Give the constant value encoded, but this shouldn't be called in the
// general case.
bool IsArrayIndexTypes() const { return IsIntegralTypes(); }
// Float type may be derived from any constant type
bool IsFloatTypes() const { return IsFloat() || IsConstant(); }
bool IsLongTypes() const { return IsLongLo() || IsLongConstant(); }
bool IsLongHighTypes() const {
return (IsLongHi() || IsPreciseConstantHi() || IsImpreciseConstantHi());
}
bool IsDoubleTypes() const { return IsDoubleLo() || IsLongConstant(); }
bool IsDoubleHighTypes() const {
return (IsDoubleHi() || IsPreciseConstantHi() || IsImpreciseConstantHi());
}
virtual bool IsLong() const { return false; }
bool HasClass() const {
bool result = !klass_.IsNull();
DCHECK_EQ(result, HasClassVirtual());
return result;
}
virtual bool HasClassVirtual() const { return false; }
bool IsJavaLangObject() const REQUIRES_SHARED(Locks::mutator_lock_);
virtual bool IsArrayTypes() const REQUIRES_SHARED(Locks::mutator_lock_);
virtual bool IsObjectArrayTypes() const REQUIRES_SHARED(Locks::mutator_lock_);
Primitive::Type GetPrimitiveType() const;
bool IsJavaLangObjectArray() const
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsInstantiableTypes() const REQUIRES_SHARED(Locks::mutator_lock_);
const StringPiece& GetDescriptor() const {
DCHECK(HasClass() ||
(IsUnresolvedTypes() && !IsUnresolvedMergedReference() &&
!IsUnresolvedSuperClass()));
return descriptor_;
}
ObjPtr<mirror::Class> GetClass() const REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!IsUnresolvedReference());
DCHECK(!klass_.IsNull()) << Dump();
DCHECK(HasClass());
return klass_.Read();
}
uint16_t GetId() const { return cache_id_; }
const RegType& GetSuperClass(RegTypeCache* cache) const
REQUIRES_SHARED(Locks::mutator_lock_);
virtual std::string Dump() const
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
// Can this type access other?
bool CanAccess(const RegType& other) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Can this type access a member with the given properties?
bool CanAccessMember(ObjPtr<mirror::Class> klass, uint32_t access_flags) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Can this type be assigned by src?
// Note: Object and interface types may always be assigned to one another, see
// comment on
// ClassJoin.
bool IsAssignableFrom(const RegType& src, MethodVerifier* verifier) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Can this array type potentially be assigned by src.
// This function is necessary as array types are valid even if their components types are not,
// e.g., when they component type could not be resolved. The function will return true iff the
// types are assignable. It will return false otherwise. In case of return=false, soft_error
// will be set to true iff the assignment test failure should be treated as a soft-error, i.e.,
// when both array types have the same 'depth' and the 'final' component types may be assignable
// (both are reference types).
bool CanAssignArray(const RegType& src,
RegTypeCache& reg_types,
Handle<mirror::ClassLoader> class_loader,
MethodVerifier* verifier,
bool* soft_error) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Can this type be assigned by src? Variant of IsAssignableFrom that doesn't
// allow assignment to
// an interface from an Object.
bool IsStrictlyAssignableFrom(const RegType& src, MethodVerifier* verifier) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Are these RegTypes the same?
bool Equals(const RegType& other) const { return GetId() == other.GetId(); }
// Compute the merge of this register from one edge (path) with incoming_type
// from another.
const RegType& Merge(const RegType& incoming_type,
RegTypeCache* reg_types,
MethodVerifier* verifier) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Same as above, but also handles the case where incoming_type == this.
const RegType& SafeMerge(const RegType& incoming_type,
RegTypeCache* reg_types,
MethodVerifier* verifier) const
REQUIRES_SHARED(Locks::mutator_lock_) {
if (Equals(incoming_type)) {
return *this;
}
return Merge(incoming_type, reg_types, verifier);
}
virtual ~RegType() {}
void VisitRoots(RootVisitor* visitor, const RootInfo& root_info) const
REQUIRES_SHARED(Locks::mutator_lock_);
static void* operator new(size_t size) noexcept {
return ::operator new(size);
}
static void* operator new(size_t size, ArenaAllocator* allocator) = delete;
static void* operator new(size_t size, ScopedArenaAllocator* allocator);
enum class AssignmentType {
kBoolean,
kByte,
kShort,
kChar,
kInteger,
kFloat,
kLongLo,
kDoubleLo,
kConflict,
kReference,
kNotAssignable,
};
ALWAYS_INLINE
inline AssignmentType GetAssignmentType() const {
AssignmentType t = GetAssignmentTypeImpl();
if (kIsDebugBuild) {
if (IsBoolean()) {
CHECK(AssignmentType::kBoolean == t);
} else if (IsByte()) {
CHECK(AssignmentType::kByte == t);
} else if (IsShort()) {
CHECK(AssignmentType::kShort == t);
} else if (IsChar()) {
CHECK(AssignmentType::kChar == t);
} else if (IsInteger()) {
CHECK(AssignmentType::kInteger == t);
} else if (IsFloat()) {
CHECK(AssignmentType::kFloat == t);
} else if (IsLongLo()) {
CHECK(AssignmentType::kLongLo == t);
} else if (IsDoubleLo()) {
CHECK(AssignmentType::kDoubleLo == t);
} else if (IsConflict()) {
CHECK(AssignmentType::kConflict == t);
} else if (IsReferenceTypes()) {
CHECK(AssignmentType::kReference == t);
} else {
LOG(FATAL) << "Unreachable";
UNREACHABLE();
}
}
return t;
}
protected:
RegType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: descriptor_(descriptor),
klass_(klass),
cache_id_(cache_id) {}
template <typename Class>
void CheckConstructorInvariants(Class* this_ ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
static_assert(std::is_final<Class>::value, "Class must be final.");
if (kIsDebugBuild) {
CheckInvariants();
}
}
virtual AssignmentType GetAssignmentTypeImpl() const = 0;
const StringPiece descriptor_;
mutable GcRoot<mirror::Class> klass_; // Non-const only due to moving classes.
const uint16_t cache_id_;
friend class RegTypeCache;
private:
virtual void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_);
/*
* A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
* S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
* S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J
* is the deepest (lowest upper bound) parent of S and T).
*
* This operation applies for regular classes and arrays, however, for interface types there
* needn't be a partial ordering on the types. We could solve the problem of a lack of a partial
* order by introducing sets of types, however, the only operation permissible on an interface is
* invoke-interface. In the tradition of Java verifiers [1] we defer the verification of interface
* types until an invoke-interface call on the interface typed reference at runtime and allow
* the perversion of Object being assignable to an interface type (note, however, that we don't
* allow assignment of Object or Interface to any concrete class and are therefore type safe).
*
* Note: This may return null in case of internal errors, e.g., OOME when a new class would have
* to be created but there is no heap space. The exception will stay pending, and it is
* the job of the caller to handle it.
*
* [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy
*/
static ObjPtr<mirror::Class> ClassJoin(ObjPtr<mirror::Class> s, ObjPtr<mirror::Class> t)
REQUIRES_SHARED(Locks::mutator_lock_);
static bool AssignableFrom(const RegType& lhs,
const RegType& rhs,
bool strict,
MethodVerifier* verifier)
REQUIRES_SHARED(Locks::mutator_lock_);
DISALLOW_COPY_AND_ASSIGN(RegType);
};
// Bottom type.
class ConflictType final : public RegType {
public:
bool IsConflict() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
// Get the singleton Conflict instance.
static const ConflictType* GetInstance() PURE;
// Create the singleton instance.
static const ConflictType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
// Destroy the singleton instance.
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kConflict;
}
private:
ConflictType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const ConflictType* instance_;
};
// A variant of the bottom type used to specify an undefined value in the
// incoming registers.
// Merging with UndefinedType yields ConflictType which is the true bottom.
class UndefinedType final : public RegType {
public:
bool IsUndefined() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
// Get the singleton Undefined instance.
static const UndefinedType* GetInstance() PURE;
// Create the singleton instance.
static const UndefinedType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
// Destroy the singleton instance.
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
private:
UndefinedType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const UndefinedType* instance_;
};
class PrimitiveType : public RegType {
public:
PrimitiveType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
bool HasClassVirtual() const override { return true; }
};
class Cat1Type : public PrimitiveType {
public:
Cat1Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
};
class IntegerType final : public Cat1Type {
public:
bool IsInteger() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const IntegerType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const IntegerType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kInteger;
}
private:
IntegerType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const IntegerType* instance_;
};
class BooleanType final : public Cat1Type {
public:
bool IsBoolean() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const BooleanType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const BooleanType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kBoolean;
}
private:
BooleanType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const BooleanType* instance_;
};
class ByteType final : public Cat1Type {
public:
bool IsByte() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const ByteType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const ByteType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kByte;
}
private:
ByteType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const ByteType* instance_;
};
class ShortType final : public Cat1Type {
public:
bool IsShort() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const ShortType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const ShortType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kShort;
}
private:
ShortType(ObjPtr<mirror::Class> klass, const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const ShortType* instance_;
};
class CharType final : public Cat1Type {
public:
bool IsChar() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const CharType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const CharType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kChar;
}
private:
CharType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const CharType* instance_;
};
class FloatType final : public Cat1Type {
public:
bool IsFloat() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const FloatType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const FloatType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kFloat;
}
private:
FloatType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const FloatType* instance_;
};
class Cat2Type : public PrimitiveType {
public:
Cat2Type(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
};
class LongLoType final : public Cat2Type {
public:
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
bool IsLongLo() const override { return true; }
bool IsLong() const override { return true; }
static const LongLoType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const LongLoType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kLongLo;
}
private:
LongLoType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const LongLoType* instance_;
};
class LongHiType final : public Cat2Type {
public:
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
bool IsLongHi() const override { return true; }
static const LongHiType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const LongHiType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
private:
LongHiType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const LongHiType* instance_;
};
class DoubleLoType final : public Cat2Type {
public:
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
bool IsDoubleLo() const override { return true; }
bool IsDouble() const override { return true; }
static const DoubleLoType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const DoubleLoType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kDoubleLo;
}
private:
DoubleLoType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const DoubleLoType* instance_;
};
class DoubleHiType final : public Cat2Type {
public:
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
bool IsDoubleHi() const override { return true; }
static const DoubleHiType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const DoubleHiType* GetInstance() PURE;
static void Destroy();
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
private:
DoubleHiType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const DoubleHiType* instance_;
};
class ConstantType : public RegType {
public:
ConstantType(uint32_t constant, uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(nullptr, "", cache_id), constant_(constant) {
}
// If this is a 32-bit constant, what is the value? This value may be
// imprecise in which case
// the value represents part of the integer range of values that may be held
// in the register.
int32_t ConstantValue() const {
DCHECK(IsConstantTypes());
return constant_;
}
int32_t ConstantValueLo() const {
DCHECK(IsConstantLo());
return constant_;
}
int32_t ConstantValueHi() const {
if (IsConstantHi() || IsPreciseConstantHi() || IsImpreciseConstantHi()) {
return constant_;
} else {
DCHECK(false);
return 0;
}
}
bool IsZero() const override {
return IsPreciseConstant() && ConstantValue() == 0;
}
bool IsOne() const override {
return IsPreciseConstant() && ConstantValue() == 1;
}
bool IsConstantChar() const override {
return IsConstant() && ConstantValue() >= 0 &&
ConstantValue() <= std::numeric_limits<uint16_t>::max();
}
bool IsConstantByte() const override {
return IsConstant() &&
ConstantValue() >= std::numeric_limits<int8_t>::min() &&
ConstantValue() <= std::numeric_limits<int8_t>::max();
}
bool IsConstantShort() const override {
return IsConstant() &&
ConstantValue() >= std::numeric_limits<int16_t>::min() &&
ConstantValue() <= std::numeric_limits<int16_t>::max();
}
bool IsConstantTypes() const override { return true; }
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
private:
const uint32_t constant_;
};
class PreciseConstType final : public ConstantType {
public:
PreciseConstType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: ConstantType(constant, cache_id) {
CheckConstructorInvariants(this);
}
bool IsPreciseConstant() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
};
class PreciseConstLoType final : public ConstantType {
public:
PreciseConstLoType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: ConstantType(constant, cache_id) {
CheckConstructorInvariants(this);
}
bool IsPreciseConstantLo() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
};
class PreciseConstHiType final : public ConstantType {
public:
PreciseConstHiType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: ConstantType(constant, cache_id) {
CheckConstructorInvariants(this);
}
bool IsPreciseConstantHi() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
};
class ImpreciseConstType final : public ConstantType {
public:
ImpreciseConstType(uint32_t constat, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: ConstantType(constat, cache_id) {
CheckConstructorInvariants(this);
}
bool IsImpreciseConstant() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
};
class ImpreciseConstLoType final : public ConstantType {
public:
ImpreciseConstLoType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: ConstantType(constant, cache_id) {
CheckConstructorInvariants(this);
}
bool IsImpreciseConstantLo() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
};
class ImpreciseConstHiType final : public ConstantType {
public:
ImpreciseConstHiType(uint32_t constant, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: ConstantType(constant, cache_id) {
CheckConstructorInvariants(this);
}
bool IsImpreciseConstantHi() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kNotAssignable;
}
};
// Special "null" type that captures the semantics of null / bottom.
class NullType final : public RegType {
public:
bool IsNull() const override {
return true;
}
// Get the singleton Null instance.
static const NullType* GetInstance() PURE;
// Create the singleton instance.
static const NullType* CreateInstance(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static void Destroy();
std::string Dump() const override {
return "null";
}
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kReference;
}
bool IsConstantTypes() const override {
return true;
}
private:
NullType(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
static const NullType* instance_;
};
// Common parent of all uninitialized types. Uninitialized types are created by
// "new" dex
// instructions and must be passed to a constructor.
class UninitializedType : public RegType {
public:
UninitializedType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint32_t allocation_pc,
uint16_t cache_id)
: RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {}
bool IsUninitializedTypes() const override;
bool IsNonZeroReferenceTypes() const override;
uint32_t GetAllocationPc() const {
DCHECK(IsUninitializedTypes());
return allocation_pc_;
}
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kReference;
}
private:
const uint32_t allocation_pc_;
};
// Similar to ReferenceType but not yet having been passed to a constructor.
class UninitializedReferenceType final : public UninitializedType {
public:
UninitializedReferenceType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint32_t allocation_pc,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(klass, descriptor, allocation_pc, cache_id) {
CheckConstructorInvariants(this);
}
bool IsUninitializedReference() const override { return true; }
bool HasClassVirtual() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
};
// Similar to UnresolvedReferenceType but not yet having been passed to a
// constructor.
class UnresolvedUninitializedRefType final : public UninitializedType {
public:
UnresolvedUninitializedRefType(const StringPiece& descriptor,
uint32_t allocation_pc, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(nullptr, descriptor, allocation_pc, cache_id) {
CheckConstructorInvariants(this);
}
bool IsUnresolvedAndUninitializedReference() const override { return true; }
bool IsUnresolvedTypes() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
private:
void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) override;
};
// Similar to UninitializedReferenceType but special case for the this argument
// of a constructor.
class UninitializedThisReferenceType final : public UninitializedType {
public:
UninitializedThisReferenceType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(klass, descriptor, 0, cache_id) {
CheckConstructorInvariants(this);
}
bool IsUninitializedThisReference() const override { return true; }
bool HasClassVirtual() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
private:
void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) override;
};
class UnresolvedUninitializedThisRefType final : public UninitializedType {
public:
UnresolvedUninitializedThisRefType(const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(nullptr, descriptor, 0, cache_id) {
CheckConstructorInvariants(this);
}
bool IsUnresolvedAndUninitializedThisReference() const override { return true; }
bool IsUnresolvedTypes() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
private:
void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) override;
};
// A type of register holding a reference to an Object of type GetClass or a
// sub-class.
class ReferenceType final : public RegType {
public:
ReferenceType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
}
bool IsReference() const override { return true; }
bool IsNonZeroReferenceTypes() const override { return true; }
bool HasClassVirtual() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kReference;
}
};
// A type of register holding a reference to an Object of type GetClass and only
// an object of that
// type.
class PreciseReferenceType final : public RegType {
public:
PreciseReferenceType(ObjPtr<mirror::Class> klass,
const StringPiece& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsPreciseReference() const override { return true; }
bool IsNonZeroReferenceTypes() const override { return true; }
bool HasClassVirtual() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kReference;
}
};
// Common parent of unresolved types.
class UnresolvedType : public RegType {
public:
UnresolvedType(const StringPiece& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(nullptr, descriptor, cache_id) {}
bool IsNonZeroReferenceTypes() const override;
AssignmentType GetAssignmentTypeImpl() const override {
return AssignmentType::kReference;
}
};
// Similar to ReferenceType except the Class couldn't be loaded. Assignability
// and other tests made
// of this type must be conservative.
class UnresolvedReferenceType final : public UnresolvedType {
public:
UnresolvedReferenceType(const StringPiece& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UnresolvedType(descriptor, cache_id) {
CheckConstructorInvariants(this);
}
bool IsUnresolvedReference() const override { return true; }
bool IsUnresolvedTypes() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
private:
void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) override;
};
// Type representing the super-class of an unresolved type.
class UnresolvedSuperClass final : public UnresolvedType {
public:
UnresolvedSuperClass(uint16_t child_id, RegTypeCache* reg_type_cache,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UnresolvedType("", cache_id),
unresolved_child_id_(child_id),
reg_type_cache_(reg_type_cache) {
CheckConstructorInvariants(this);
}
bool IsUnresolvedSuperClass() const override { return true; }
bool IsUnresolvedTypes() const override { return true; }
uint16_t GetUnresolvedSuperClassChildId() const {
DCHECK(IsUnresolvedSuperClass());
return static_cast<uint16_t>(unresolved_child_id_ & 0xFFFF);
}
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
private:
void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) override;
const uint16_t unresolved_child_id_;
const RegTypeCache* const reg_type_cache_;
};
// A merge of unresolved (and resolved) types. If the types were resolved this may be
// Conflict or another known ReferenceType.
class UnresolvedMergedType final : public UnresolvedType {
public:
// Note: the constructor will copy the unresolved BitVector, not use it directly.
UnresolvedMergedType(const RegType& resolved,
const BitVector& unresolved,
const RegTypeCache* reg_type_cache,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
// The resolved part. See description below.
const RegType& GetResolvedPart() const {
return resolved_part_;
}
// The unresolved part.
const BitVector& GetUnresolvedTypes() const {
return unresolved_types_;
}
bool IsUnresolvedMergedReference() const override { return true; }
bool IsUnresolvedTypes() const override { return true; }
bool IsArrayTypes() const override REQUIRES_SHARED(Locks::mutator_lock_);
bool IsObjectArrayTypes() const override REQUIRES_SHARED(Locks::mutator_lock_);
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
private:
void CheckInvariants() const REQUIRES_SHARED(Locks::mutator_lock_) override;
const RegTypeCache* const reg_type_cache_;
// The original implementation of merged types was a binary tree. Collection of the flattened
// types ("leaves") can be expensive, so we store the expanded list now, as two components:
// 1) A resolved component. We use Zero when there is no resolved component, as that will be
// an identity merge.
// 2) A bitvector of the unresolved reference types. A bitvector was chosen with the assumption
// that there should not be too many types in flight in practice. (We also bias the index
// against the index of Zero, which is one of the later default entries in any cache.)
const RegType& resolved_part_;
const BitVector unresolved_types_;
};
std::ostream& operator<<(std::ostream& os, const RegType& rhs)
REQUIRES_SHARED(Locks::mutator_lock_);
} // namespace verifier
} // namespace art
#endif // ART_RUNTIME_VERIFIER_REG_TYPE_H_