blob: dc9a33a691de2dfd24952c3d87b35ea67efea646 [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_SRC_VERIFIER_REG_TYPE_H_
#define ART_SRC_VERIFIER_REG_TYPE_H_
#include "base/macros.h"
#include "primitive.h"
#include "jni.h"
#include <stdint.h>
#include <set>
#include <string>
namespace art {
namespace mirror {
class Class;
} // namespace mirror
namespace verifier {
class RegTypeCache;
/*
* RegType holds information about the "type" of data held in a register.
*/
class RegType {
public:
enum Type {
// A special state that identifies a register as undefined.
kRegTypeUndefined = 0,
// The bottom type, used to denote the type of operations such as returning a void, throwing
// an exception or merging incompatible types, such as an int and a long.
kRegTypeConflict,
kRegTypeBoolean, // Z.
kRegType1nrSTART = kRegTypeBoolean,
kRegTypeIntegralSTART = kRegTypeBoolean,
kRegTypeByte, // B.
kRegTypeShort, // S.
kRegTypeChar, // C.
kRegTypeInteger, // I.
kRegTypeIntegralEND = kRegTypeInteger,
kRegTypeFloat, // F.
kRegType1nrEND = kRegTypeFloat,
kRegTypeLongLo, // J - lower-numbered register; endian-independent.
kRegTypeLongHi,
kRegTypeDoubleLo, // D.
kRegTypeDoubleHi,
kRegTypeLastFixedLocation = kRegTypeDoubleHi,
kRegTypePreciseConst, // 32-bit constant - could be float or int.
kRegTypeImpreciseConst, // 32-bit constant derived value - could be float or int.
kRegTypePreciseConstLo, // Const wide, lower half - could be long or double.
kRegTypePreciseConstHi, // Const wide, upper half - could be long or double.
kRegTypeImpreciseConstLo, // Const derived wide, lower half - could be long or double.
kRegTypeImpreciseConstHi, // Const derived wide, upper half - could be long or double.
kRegTypeUnresolvedReference, // Reference type that couldn't be resolved.
kRegTypeUninitializedReference, // Freshly allocated reference type.
kRegTypeUninitializedThisReference, // Freshly allocated reference passed as "this".
kRegTypeUnresolvedAndUninitializedReference, // Freshly allocated unresolved reference type.
// Freshly allocated unresolved reference passed as "this".
kRegTypeUnresolvedAndUninitializedThisReference,
kRegTypeUnresolvedMergedReference, // Tree of merged references (at least 1 is unresolved).
kRegTypeUnresolvedSuperClass, // Super class of an unresolved type.
kRegTypeReference, // Reference type.
kRegTypePreciseReference, // Precisely the given type.
};
Type GetType() const {
return type_;
}
bool IsUndefined() const { return type_ == kRegTypeUndefined; }
bool IsConflict() const { return type_ == kRegTypeConflict; }
bool IsBoolean() const { return type_ == kRegTypeBoolean; }
bool IsByte() const { return type_ == kRegTypeByte; }
bool IsChar() const { return type_ == kRegTypeChar; }
bool IsShort() const { return type_ == kRegTypeShort; }
bool IsInteger() const { return type_ == kRegTypeInteger; }
bool IsLong() const { return type_ == kRegTypeLongLo; }
bool IsFloat() const { return type_ == kRegTypeFloat; }
bool IsDouble() const { return type_ == kRegTypeDoubleLo; }
bool IsUnresolvedReference() const { return type_ == kRegTypeUnresolvedReference; }
bool IsUninitializedReference() const { return type_ == kRegTypeUninitializedReference; }
bool IsUninitializedThisReference() const { return type_ == kRegTypeUninitializedThisReference; }
bool IsUnresolvedAndUninitializedReference() const {
return type_ == kRegTypeUnresolvedAndUninitializedReference;
}
bool IsUnresolvedAndUninitializedThisReference() const {
return type_ == kRegTypeUnresolvedAndUninitializedThisReference;
}
bool IsUnresolvedMergedReference() const { return type_ == kRegTypeUnresolvedMergedReference; }
bool IsUnresolvedSuperClass() const { return type_ == kRegTypeUnresolvedSuperClass; }
bool IsReference() const { return type_ == kRegTypeReference; }
bool IsPreciseReference() const { return type_ == kRegTypePreciseReference; }
bool IsUninitializedTypes() const {
return IsUninitializedReference() || IsUninitializedThisReference() ||
IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference();
}
bool IsUnresolvedTypes() const {
return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference() ||
IsUnresolvedAndUninitializedThisReference() || IsUnresolvedMergedReference() ||
IsUnresolvedSuperClass();
}
bool IsLowHalf() const {
return type_ == kRegTypeLongLo ||
type_ == kRegTypeDoubleLo ||
type_ == kRegTypePreciseConstLo ||
type_ == kRegTypeImpreciseConstLo;
}
bool IsHighHalf() const {
return type_ == kRegTypeLongHi ||
type_ == kRegTypeDoubleHi ||
type_ == kRegTypePreciseConstHi ||
type_ == kRegTypeImpreciseConstHi;
}
bool IsLongOrDoubleTypes() const { return IsLowHalf(); }
// Check this is the low half, and that type_h is its matching high-half
bool CheckWidePair(const RegType& type_h) const {
if (IsLowHalf()) {
return (type_h.type_ == type_ + 1) ||
(IsPreciseConstantLo() && type_h.IsImpreciseConstantHi()) ||
(IsImpreciseConstantLo() && type_h.IsPreciseConstantHi());
}
return false;
}
// The high half that corresponds to this low half
const RegType& HighHalf(RegTypeCache* cache) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsPreciseConstant() const {
return type_ == kRegTypePreciseConst;
}
bool IsPreciseConstantLo() const {
return type_ == kRegTypePreciseConstLo;
}
bool IsPreciseConstantHi() const {
return type_ == kRegTypePreciseConstHi;
}
bool IsImpreciseConstantLo() const {
return type_ == kRegTypeImpreciseConstLo;
}
bool IsImpreciseConstantHi() const {
return type_ == kRegTypeImpreciseConstHi;
}
bool IsConstant() const {
return type_ == kRegTypePreciseConst || type_ == kRegTypeImpreciseConst;
}
bool IsConstantLo() const {
return type_ == kRegTypePreciseConstLo || type_ == kRegTypeImpreciseConstLo;
}
bool IsLongConstant() const {
return IsConstantLo();
}
bool IsConstantHi() const {
return type_ == kRegTypePreciseConstHi || type_ == kRegTypeImpreciseConstHi;
}
bool IsLongConstantHigh() const {
return IsConstantHi();
}
// 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(IsConstant());
return allocation_pc_or_constant_or_merged_types_;
}
int32_t ConstantValueLo() const {
DCHECK(IsConstantLo());
return allocation_pc_or_constant_or_merged_types_;
}
int32_t ConstantValueHi() const {
DCHECK(IsConstantHi());
return allocation_pc_or_constant_or_merged_types_;
}
bool IsZero() const { return IsPreciseConstant() && ConstantValue() == 0; }
bool IsOne() const { return IsPreciseConstant() && ConstantValue() == 1; }
bool IsConstantBoolean() const {
return IsConstant() && ConstantValue() >= 0 && ConstantValue() <= 1;
}
bool IsConstantByte() const;
bool IsConstantShort() const;
bool IsConstantChar() const;
bool IsReferenceTypes() const {
return IsNonZeroReferenceTypes() || IsZero();
}
bool IsNonZeroReferenceTypes() const {
return IsReference() || IsPreciseReference() || IsUnresolvedReference() ||
IsUninitializedReference() || IsUninitializedThisReference() ||
IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference() ||
IsUnresolvedMergedReference() || IsUnresolvedSuperClass();
}
bool IsCategory1Types() const {
return (type_ >= kRegType1nrSTART && type_ <= kRegType1nrEND) || IsConstant();
}
bool IsCategory2Types() const {
return IsLowHalf(); // Don't expect explicit testing of high halves
}
bool IsBooleanTypes() const { return IsBoolean() || IsConstantBoolean(); }
bool IsByteTypes() const { return IsByte() || IsBoolean() || IsConstantByte(); }
bool IsShortTypes() const { return IsShort() || IsByte() || IsBoolean() || IsConstantShort(); }
bool IsCharTypes() const { return IsChar() || IsBooleanTypes() || IsConstantChar(); }
bool IsIntegralTypes() const {
return (type_ >= kRegTypeIntegralSTART && type_ <= kRegTypeIntegralEND) || IsConstant();
}
bool IsArrayIndexTypes() const { return IsIntegralTypes(); }
// Float type may be derived from any constant type
bool IsFloatTypes() const { return IsFloat() || IsConstant(); }
bool IsLongTypes() const {
return IsLong() || IsLongConstant();
}
bool IsLongHighTypes() const {
return type_ == kRegTypeLongHi ||
type_ == kRegTypePreciseConstHi ||
type_ == kRegTypeImpreciseConstHi;
}
bool IsDoubleTypes() const {
return IsDouble() || IsLongConstant();
}
bool IsDoubleHighTypes() const {
return type_ == kRegTypeDoubleHi ||
type_ == kRegTypePreciseConstHi ||
type_ == kRegTypeImpreciseConstHi;
}
uint32_t GetAllocationPc() const {
DCHECK(IsUninitializedTypes());
return allocation_pc_or_constant_or_merged_types_;
}
bool HasClass() const {
return IsReference() || IsPreciseReference();
}
mirror::Class* GetClass() const {
DCHECK(!IsUnresolvedReference());
DCHECK(klass_ != NULL);
return klass_;
}
bool IsJavaLangObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsObjectArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Primitive::Type GetPrimitiveType() const {
if (IsNonZeroReferenceTypes()) {
return Primitive::kPrimNot;
} else if (IsBooleanTypes()) {
return Primitive::kPrimBoolean;
} else if (IsByteTypes()) {
return Primitive::kPrimByte;
} else if (IsShortTypes()) {
return Primitive::kPrimShort;
} else if (IsCharTypes()) {
return Primitive::kPrimChar;
} else if (IsFloat()) {
return Primitive::kPrimFloat;
} else if (IsIntegralTypes()) {
return Primitive::kPrimInt;
} else if (IsDouble()) {
return Primitive::kPrimDouble;
} else {
DCHECK(IsLongTypes());
return Primitive::kPrimLong;
}
}
bool IsJavaLangObjectArray() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsInstantiableTypes() const;
std::string GetDescriptor() const {
DCHECK(IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass());
return descriptor_;
}
uint16_t GetId() const {
return cache_id_;
}
// The top of a tree of merged types.
std::pair<uint16_t, uint16_t> GetTopMergedTypes() const {
DCHECK(IsUnresolvedMergedReference());
uint16_t type1 = static_cast<uint16_t>(allocation_pc_or_constant_or_merged_types_ & 0xFFFF);
uint16_t type2 = static_cast<uint16_t>(allocation_pc_or_constant_or_merged_types_ >> 16);
return std::pair<uint16_t, uint16_t>(type1, type2);
}
// The complete set of merged types.
std::set<uint16_t> GetMergedTypes(const RegTypeCache* cache) const;
uint16_t GetUnresolvedSuperClassChildId() const {
DCHECK(IsUnresolvedSuperClass());
return static_cast<uint16_t>(allocation_pc_or_constant_or_merged_types_ & 0xFFFF);
}
const RegType& GetSuperClass(RegTypeCache* cache) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
std::string Dump(const RegTypeCache* reg_types = NULL) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Can this type access other?
bool CanAccess(const RegType& other) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Can this type access a member with the given properties?
bool CanAccessMember(mirror::Class* klass, uint32_t access_flags) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Can this type be assigned by src?
bool IsAssignableFrom(const RegType& src) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
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) const
SHARED_LOCKS_REQUIRED(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).
*
* [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy
*/
static mirror::Class* ClassJoin(mirror::Class* s, mirror::Class* t)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
friend class RegTypeCache;
RegType(Type type, mirror::Class* klass,
uint32_t allocation_pc_or_constant_or_merged_types, uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: type_(type), klass_(klass),
allocation_pc_or_constant_or_merged_types_(allocation_pc_or_constant_or_merged_types),
cache_id_(cache_id) {
#ifndef NDEBUG
CheckInvariants();
#endif
}
RegType(Type type, const std::string& descriptor, uint32_t allocation_pc, uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: type_(type),
klass_(NULL),
descriptor_(descriptor),
allocation_pc_or_constant_or_merged_types_(allocation_pc),
cache_id_(cache_id) {
#ifndef NDEBUG
CheckInvariants();
#endif
}
void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const Type type_; // The current type of the register
// For reference types, if known the type of the register...
mirror::Class* klass_;
// ...else a String for the descriptor.
std::string descriptor_;
// Overloaded field that:
// - if IsConstant() holds a 32bit constant value
// - is IsUninitializedReference()/IsUnresolvedAndUninitializedReference() holds the pc the
// instance in the register was being allocated.
const uint32_t allocation_pc_or_constant_or_merged_types_;
// A RegType cache densely encodes types, this is the location in the cache for this type
const uint16_t cache_id_;
DISALLOW_COPY_AND_ASSIGN(RegType);
};
std::ostream& operator<<(std::ostream& os, const RegType& rhs)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
} // namespace verifier
} // namespace art
#endif // ART_SRC_VERIFIER_REG_TYPE_H_