| /* |
| * 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. |
| */ |
| |
| #include "reg_type.h" |
| |
| |
| #include "base/casts.h" |
| #include "dex_file-inl.h" |
| #include "mirror/class.h" |
| #include "mirror/class-inl.h" |
| #include "mirror/object-inl.h" |
| #include "mirror/object_array-inl.h" |
| #include "object_utils.h" |
| #include "reg_type_cache-inl.h" |
| #include "scoped_thread_state_change.h" |
| |
| #include <limits> |
| #include <sstream> |
| |
| namespace art { |
| namespace verifier { |
| |
| UndefinedType* UndefinedType::instance_ = NULL; |
| ConflictType* ConflictType::instance_ = NULL; |
| BooleanType* BooleanType::instance = NULL; |
| ByteType* ByteType::instance_ = NULL; |
| ShortType* ShortType::instance_ = NULL; |
| CharType* CharType::instance_ = NULL; |
| FloatType* FloatType::instance_ = NULL; |
| LongLoType* LongLoType::instance_ = NULL; |
| LongHiType* LongHiType::instance_ = NULL; |
| DoubleLoType* DoubleLoType::instance_ = NULL; |
| DoubleHiType* DoubleHiType::instance_ = NULL; |
| IntegerType* IntegerType::instance_ = NULL; |
| |
| int32_t RegType::ConstantValue() const { |
| ScopedObjectAccess soa(Thread::Current()); |
| LOG(FATAL) << "Unexpected call to ConstantValue: " << *this; |
| return 0; |
| } |
| |
| int32_t RegType::ConstantValueLo() const { |
| ScopedObjectAccess soa(Thread::Current()); |
| LOG(FATAL) << "Unexpected call to ConstantValueLo: " << *this; |
| return 0; |
| } |
| |
| int32_t RegType::ConstantValueHi() const { |
| ScopedObjectAccess soa(Thread::Current()); |
| LOG(FATAL) << "Unexpected call to ConstantValueHi: " << *this; |
| return 0; |
| } |
| |
| PrimitiveType::PrimitiveType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) |
| : RegType(klass, descriptor, cache_id) { |
| CHECK(klass != NULL); |
| CHECK(!descriptor.empty()); |
| } |
| |
| Cat1Type::Cat1Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) |
| : PrimitiveType(klass, descriptor, cache_id) { |
| } |
| |
| Cat2Type::Cat2Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) |
| : PrimitiveType(klass, descriptor, cache_id) { |
| } |
| |
| std::string PreciseConstType::Dump() const { |
| std::stringstream result; |
| uint32_t val = ConstantValue(); |
| if (val == 0) { |
| CHECK(IsPreciseConstant()); |
| result << "Zero/null"; |
| } else { |
| result << "Precise "; |
| if (IsConstantShort()) { |
| result << StringPrintf("Constant: %d", val); |
| } else { |
| result << StringPrintf("Constant: 0x%x", val); |
| } |
| } |
| return result.str(); |
| } |
| |
| std::string BooleanType::Dump() const { |
| return "Boolean"; |
| } |
| |
| std::string ConflictType::Dump() const { |
| return "Conflict"; |
| } |
| |
| std::string ByteType::Dump() const { |
| return "Byte"; |
| } |
| |
| std::string ShortType::Dump() const { |
| return "Short"; |
| } |
| |
| std::string CharType::Dump() const { |
| return "Char"; |
| } |
| |
| std::string FloatType::Dump() const { |
| return "Float"; |
| } |
| |
| std::string LongLoType::Dump() const { |
| return "Long (Low Half)"; |
| } |
| |
| std::string LongHiType::Dump() const { |
| return "Long (High Half)"; |
| } |
| |
| std::string DoubleLoType::Dump() const { |
| return "Double (Low Half)"; |
| } |
| |
| std::string DoubleHiType::Dump() const { |
| return "Double (High Half)"; |
| } |
| |
| std::string IntegerType::Dump() const { |
| return "Integer"; |
| } |
| |
| DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new DoubleHiType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| DoubleHiType* DoubleHiType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void DoubleHiType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new DoubleLoType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| DoubleLoType* DoubleLoType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void DoubleLoType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| LongLoType* LongLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new LongLoType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| LongHiType* LongHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new LongHiType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| LongHiType* LongHiType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void LongHiType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| LongLoType* LongLoType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void LongLoType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| FloatType* FloatType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new FloatType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| FloatType* FloatType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void FloatType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| CharType* CharType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new CharType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| CharType* CharType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void CharType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| ShortType* ShortType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new ShortType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| ShortType* ShortType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void ShortType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| ByteType* ByteType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new ByteType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| ByteType* ByteType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void ByteType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| IntegerType* IntegerType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new IntegerType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| IntegerType* IntegerType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void IntegerType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| ConflictType* ConflictType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new ConflictType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| ConflictType* ConflictType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void ConflictType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| BooleanType* BooleanType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (BooleanType::instance == NULL) { |
| instance = new BooleanType(klass, descriptor, cache_id); |
| } |
| return BooleanType::instance; |
| } |
| |
| BooleanType* BooleanType::GetInstance() { |
| CHECK(BooleanType::instance != NULL); |
| return BooleanType::instance; |
| } |
| |
| void BooleanType::Destroy() { |
| if (BooleanType::instance != NULL) { |
| delete instance; |
| instance = NULL; |
| } |
| } |
| |
| std::string UndefinedType::Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return "Undefined"; |
| } |
| |
| UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) { |
| if (instance_ == NULL) { |
| instance_ = new UndefinedType(klass, descriptor, cache_id); |
| } |
| return instance_; |
| } |
| |
| UndefinedType* UndefinedType::GetInstance() { |
| CHECK(instance_ != NULL); |
| return instance_; |
| } |
| |
| void UndefinedType::Destroy() { |
| if (instance_ != NULL) { |
| delete instance_; |
| instance_ = NULL; |
| } |
| } |
| |
| PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const std::string& descriptor, |
| uint16_t cache_id) |
| : RegType(klass, descriptor, cache_id) { |
| DCHECK(klass->IsInstantiable()); |
| } |
| |
| std::string UnresolvedMergedType::Dump() const { |
| std::stringstream result; |
| std::set<uint16_t> types = GetMergedTypes(); |
| result << "UnresolvedMergedReferences("; |
| auto it = types.begin(); |
| result << reg_type_cache_->GetFromId(*it).Dump(); |
| for (++it; it != types.end(); ++it) { |
| result << ", "; |
| result << reg_type_cache_->GetFromId(*it).Dump(); |
| } |
| result << ")"; |
| return result.str(); |
| } |
| |
| std::string UnresolvedSuperClass::Dump() const { |
| std::stringstream result; |
| uint16_t super_type_id = GetUnresolvedSuperClassChildId(); |
| result << "UnresolvedSuperClass(" << reg_type_cache_->GetFromId(super_type_id).Dump() << ")"; |
| return result.str(); |
| } |
| |
| std::string UnresolvedReferenceType::Dump() const { |
| std::stringstream result; |
| result << "Unresolved Reference" << ": " << PrettyDescriptor(GetDescriptor()); |
| return result.str(); |
| } |
| |
| std::string UnresolvedUninitializedRefType::Dump() const { |
| std::stringstream result; |
| result << "Unresolved And Uninitialized Reference" << ": " << PrettyDescriptor(GetDescriptor()); |
| result << " Allocation PC: " << GetAllocationPc(); |
| return result.str(); |
| } |
| |
| std::string UnresolvedUninitializedThisRefType::Dump() const { |
| std::stringstream result; |
| result << "Unresolved And Uninitialized This Reference" << PrettyDescriptor(GetDescriptor()); |
| return result.str(); |
| } |
| |
| std::string ReferenceType::Dump() const { |
| std::stringstream result; |
| result << "Reference" << ": " << PrettyDescriptor(GetClass()); |
| return result.str(); |
| } |
| |
| std::string PreciseReferenceType::Dump() const { |
| std::stringstream result; |
| result << "Precise Reference" << ": "<< PrettyDescriptor(GetClass()); |
| return result.str(); |
| } |
| |
| std::string UninitializedReferenceType::Dump() const { |
| std::stringstream result; |
| result << "Uninitialized Reference" << ": " << PrettyDescriptor(GetClass()); |
| result << " Allocation PC: " << GetAllocationPc(); |
| return result.str(); |
| } |
| |
| std::string UninitializedThisReferenceType::Dump() const { |
| std::stringstream result; |
| result << "Uninitialized This Reference" << ": " << PrettyDescriptor(GetClass()); |
| result << "Allocation PC: " << GetAllocationPc(); |
| return result.str(); |
| } |
| |
| std::string ImpreciseConstType::Dump() const { |
| std::stringstream result; |
| uint32_t val = ConstantValue(); |
| if (val == 0) { |
| result << "Zero/null"; |
| } else { |
| result << "Imprecise "; |
| if (IsConstantShort()) { |
| result << StringPrintf("Constant: %d", val); |
| } else { |
| result << StringPrintf("Constant: 0x%x", val); |
| } |
| } |
| return result.str(); |
| } |
| std::string PreciseConstLoType::Dump() const { |
| std::stringstream result; |
| |
| int32_t val = ConstantValueLo(); |
| result << "Precise "; |
| if (val >= std::numeric_limits<jshort>::min() && |
| val <= std::numeric_limits<jshort>::max()) { |
| result << StringPrintf("Low-half Constant: %d", val); |
| } else { |
| result << StringPrintf("Low-half Constant: 0x%x", val); |
| } |
| return result.str(); |
| } |
| |
| std::string ImpreciseConstLoType::Dump() const { |
| std::stringstream result; |
| |
| int32_t val = ConstantValueLo(); |
| result << "Imprecise "; |
| if (val >= std::numeric_limits<jshort>::min() && |
| val <= std::numeric_limits<jshort>::max()) { |
| result << StringPrintf("Low-half Constant: %d", val); |
| } else { |
| result << StringPrintf("Low-half Constant: 0x%x", val); |
| } |
| return result.str(); |
| } |
| |
| std::string PreciseConstHiType::Dump() const { |
| std::stringstream result; |
| int32_t val = ConstantValueHi(); |
| result << "Precise "; |
| if (val >= std::numeric_limits<jshort>::min() && |
| val <= std::numeric_limits<jshort>::max()) { |
| result << StringPrintf("High-half Constant: %d", val); |
| } else { |
| result << StringPrintf("High-half Constant: 0x%x", val); |
| } |
| return result.str(); |
| } |
| |
| std::string ImpreciseConstHiType::Dump() const { |
| std::stringstream result; |
| int32_t val = ConstantValueHi(); |
| result << "Imprecise "; |
| if (val >= std::numeric_limits<jshort>::min() && |
| val <= std::numeric_limits<jshort>::max()) { |
| result << StringPrintf("High-half Constant: %d", val); |
| } else { |
| result << StringPrintf("High-half Constant: 0x%x", val); |
| } |
| return result.str(); |
| } |
| |
| ConstantType::ConstantType(uint32_t constant, uint16_t cache_id) |
| : RegType(NULL, "", cache_id), constant_(constant) { |
| } |
| |
| const RegType& UndefinedType::Merge(const RegType& incoming_type, RegTypeCache* reg_types) const |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| if (incoming_type.IsUndefined()) { |
| return *this; // Undefined MERGE Undefined => Undefined |
| } |
| return reg_types->Conflict(); |
| } |
| |
| const RegType& RegType::HighHalf(RegTypeCache* cache) const { |
| DCHECK(IsLowHalf()); |
| if (IsLongLo()) { |
| return cache->LongHi(); |
| } else if (IsDoubleLo()) { |
| return cache->DoubleHi(); |
| } else { |
| DCHECK(IsImpreciseConstantLo()); |
| return cache->FromCat2ConstHi(ConstantValue(), false); |
| } |
| } |
| |
| Primitive::Type RegType::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 (IsDoubleLo()) { |
| return Primitive::kPrimDouble; |
| } else { |
| DCHECK(IsLongTypes()); |
| return Primitive::kPrimLong; |
| } |
| } |
| |
| bool UninitializedType::IsUninitializedTypes() const { |
| return true; |
| } |
| |
| bool UninitializedType::IsNonZeroReferenceTypes() const { |
| return true; |
| } |
| |
| bool UnresolvedType::IsNonZeroReferenceTypes() const { |
| return true; |
| } |
| std::set<uint16_t> UnresolvedMergedType::GetMergedTypes() const { |
| std::pair<uint16_t, uint16_t> refs = GetTopMergedTypes(); |
| const RegType& _left(reg_type_cache_->GetFromId(refs.first)); |
| RegType& __left(const_cast<RegType&>(_left)); |
| UnresolvedMergedType* left = down_cast<UnresolvedMergedType*>(&__left); |
| |
| RegType& _right( |
| const_cast<RegType&>(reg_type_cache_->GetFromId(refs.second))); |
| UnresolvedMergedType* right = down_cast<UnresolvedMergedType*>(&_right); |
| |
| std::set<uint16_t> types; |
| if (left->IsUnresolvedMergedReference()) { |
| types = left->GetMergedTypes(); |
| } else { |
| types.insert(refs.first); |
| } |
| if (right->IsUnresolvedMergedReference()) { |
| std::set<uint16_t> right_types = right->GetMergedTypes(); |
| types.insert(right_types.begin(), right_types.end()); |
| } else { |
| types.insert(refs.second); |
| } |
| if (kIsDebugBuild) { |
| for (const auto& type : types) { |
| CHECK(!reg_type_cache_->GetFromId(type).IsUnresolvedMergedReference()); |
| } |
| } |
| return types; |
| } |
| |
| const RegType& RegType::GetSuperClass(RegTypeCache* cache) const { |
| if (!IsUnresolvedTypes()) { |
| mirror::Class* super_klass = GetClass()->GetSuperClass(); |
| if (super_klass != NULL) { |
| // A super class of a precise type isn't precise as a precise type indicates the register |
| // holds exactly that type. |
| return cache->FromClass(ClassHelper(super_klass).GetDescriptor(), super_klass, false); |
| } else { |
| return cache->Zero(); |
| } |
| } else { |
| if (!IsUnresolvedMergedReference() && !IsUnresolvedSuperClass() && |
| GetDescriptor()[0] == '[') { |
| // Super class of all arrays is Object. |
| return cache->JavaLangObject(true); |
| } else { |
| return cache->FromUnresolvedSuperClass(*this); |
| } |
| } |
| } |
| |
| bool RegType::CanAccess(const RegType& other) const { |
| if (Equals(other)) { |
| return true; // Trivial accessibility. |
| } else { |
| bool this_unresolved = IsUnresolvedTypes(); |
| bool other_unresolved = other.IsUnresolvedTypes(); |
| if (!this_unresolved && !other_unresolved) { |
| return GetClass()->CanAccess(other.GetClass()); |
| } else if (!other_unresolved) { |
| return other.GetClass()->IsPublic(); // Be conservative, only allow if other is public. |
| } else { |
| return false; // More complicated test not possible on unresolved types, be conservative. |
| } |
| } |
| } |
| |
| bool RegType::CanAccessMember(mirror::Class* klass, uint32_t access_flags) const { |
| if ((access_flags & kAccPublic) != 0) { |
| return true; |
| } |
| if (!IsUnresolvedTypes()) { |
| return GetClass()->CanAccessMember(klass, access_flags); |
| } else { |
| return false; // More complicated test not possible on unresolved types, be conservative. |
| } |
| } |
| |
| bool RegType::IsObjectArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) { |
| // Primitive arrays will always resolve |
| DCHECK(descriptor_[1] == 'L' || descriptor_[1] == '['); |
| return descriptor_[0] == '['; |
| } else if (HasClass()) { |
| mirror::Class* type = GetClass(); |
| return type->IsArrayClass() && !type->GetComponentType()->IsPrimitive(); |
| } else { |
| return false; |
| } |
| } |
| |
| bool RegType::IsJavaLangObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| return IsReference() && GetClass()->IsObjectClass(); |
| } |
| |
| bool RegType::IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) { |
| return descriptor_[0] == '['; |
| } else if (HasClass()) { |
| return GetClass()->IsArrayClass(); |
| } else { |
| return false; |
| } |
| } |
| |
| bool RegType::IsJavaLangObjectArray() const { |
| if (HasClass()) { |
| mirror::Class* type = GetClass(); |
| return type->IsArrayClass() && type->GetComponentType()->IsObjectClass(); |
| } |
| return false; |
| } |
| |
| bool RegType::IsInstantiableTypes() const { |
| return IsUnresolvedTypes() || (IsNonZeroReferenceTypes() && GetClass()->IsInstantiable()); |
| } |
| |
| ImpreciseConstType::ImpreciseConstType(uint32_t constat, uint16_t cache_id) |
| : ConstantType(constat, cache_id) { |
| } |
| |
| static bool AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| if (lhs.Equals(rhs)) { |
| return true; |
| } else { |
| if (lhs.IsBoolean()) { |
| return rhs.IsBooleanTypes(); |
| } else if (lhs.IsByte()) { |
| return rhs.IsByteTypes(); |
| } else if (lhs.IsShort()) { |
| return rhs.IsShortTypes(); |
| } else if (lhs.IsChar()) { |
| return rhs.IsCharTypes(); |
| } else if (lhs.IsInteger()) { |
| return rhs.IsIntegralTypes(); |
| } else if (lhs.IsFloat()) { |
| return rhs.IsFloatTypes(); |
| } else if (lhs.IsLongLo()) { |
| return rhs.IsLongTypes(); |
| } else if (lhs.IsDoubleLo()) { |
| return rhs.IsDoubleTypes(); |
| } else { |
| CHECK(lhs.IsReferenceTypes()) |
| << "Unexpected register type in IsAssignableFrom: '" |
| << lhs << "' := '" << rhs << "'"; |
| if (rhs.IsZero()) { |
| return true; // All reference types can be assigned null. |
| } else if (!rhs.IsReferenceTypes()) { |
| return false; // Expect rhs to be a reference type. |
| } else if (lhs.IsJavaLangObject()) { |
| return true; // All reference types can be assigned to Object. |
| } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) { |
| // If we're not strict allow assignment to any interface, see comment in ClassJoin. |
| return true; |
| } else if (lhs.IsJavaLangObjectArray()) { |
| return rhs.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[] |
| } else if (lhs.HasClass() && rhs.HasClass() && |
| lhs.GetClass()->IsAssignableFrom(rhs.GetClass())) { |
| // We're assignable from the Class point-of-view. |
| return true; |
| } else { |
| // Unresolved types are only assignable for null and equality. |
| return false; |
| } |
| } |
| } |
| } |
| |
| bool RegType::IsAssignableFrom(const RegType& src) const { |
| return AssignableFrom(*this, src, false); |
| } |
| |
| bool RegType::IsStrictlyAssignableFrom(const RegType& src) const { |
| return AssignableFrom(*this, src, true); |
| } |
| |
| int32_t ConstantType::ConstantValueLo() const { |
| DCHECK(IsConstantLo()); |
| return constant_; |
| } |
| |
| int32_t ConstantType::ConstantValueHi() const { |
| if (IsConstantHi() || IsPreciseConstantHi() || IsImpreciseConstantHi()) { |
| return constant_; |
| } else { |
| DCHECK(false); |
| return 0; |
| } |
| } |
| |
| static const RegType& SelectNonConstant(const RegType& a, const RegType& b) { |
| return a.IsConstant() ? b : a; |
| } |
| |
| const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_types) const { |
| DCHECK(!Equals(incoming_type)); // Trivial equality handled by caller |
| if (IsConflict()) { |
| return *this; // Conflict MERGE * => Conflict |
| } else if (incoming_type.IsConflict()) { |
| return incoming_type; // * MERGE Conflict => Conflict |
| } else if (IsUndefined() || incoming_type.IsUndefined()) { |
| return reg_types->Conflict(); // Unknown MERGE * => Conflict |
| } else if (IsConstant() && incoming_type.IsConstant()) { |
| int32_t val1 = ConstantValue(); |
| int32_t val2 = incoming_type.ConstantValue(); |
| if (val1 >= 0 && val2 >= 0) { |
| // +ve1 MERGE +ve2 => MAX(+ve1, +ve2) |
| if (val1 >= val2) { |
| if (!IsPreciseConstant()) { |
| return *this; |
| } else { |
| return reg_types->FromCat1Const(val1, false); |
| } |
| } else { |
| if (!incoming_type.IsPreciseConstant()) { |
| return incoming_type; |
| } else { |
| return reg_types->FromCat1Const(val2, false); |
| } |
| } |
| } else if (val1 < 0 && val2 < 0) { |
| // -ve1 MERGE -ve2 => MIN(-ve1, -ve2) |
| if (val1 <= val2) { |
| if (!IsPreciseConstant()) { |
| return *this; |
| } else { |
| return reg_types->FromCat1Const(val1, false); |
| } |
| } else { |
| if (!incoming_type.IsPreciseConstant()) { |
| return incoming_type; |
| } else { |
| return reg_types->FromCat1Const(val2, false); |
| } |
| } |
| } else { |
| // Values are +ve and -ve, choose smallest signed type in which they both fit |
| if (IsConstantByte()) { |
| if (incoming_type.IsConstantByte()) { |
| return reg_types->ByteConstant(); |
| } else if (incoming_type.IsConstantShort()) { |
| return reg_types->ShortConstant(); |
| } else { |
| return reg_types->IntConstant(); |
| } |
| } else if (IsConstantShort()) { |
| if (incoming_type.IsConstantShort()) { |
| return reg_types->ShortConstant(); |
| } else { |
| return reg_types->IntConstant(); |
| } |
| } else { |
| return reg_types->IntConstant(); |
| } |
| } |
| } else if (IsConstantLo() && incoming_type.IsConstantLo()) { |
| int32_t val1 = ConstantValueLo(); |
| int32_t val2 = incoming_type.ConstantValueLo(); |
| return reg_types->FromCat2ConstLo(val1 | val2, false); |
| } else if (IsConstantHi() && incoming_type.IsConstantHi()) { |
| int32_t val1 = ConstantValueHi(); |
| int32_t val2 = incoming_type.ConstantValueHi(); |
| return reg_types->FromCat2ConstHi(val1 | val2, false); |
| } else if (IsIntegralTypes() && incoming_type.IsIntegralTypes()) { |
| if (IsBooleanTypes() && incoming_type.IsBooleanTypes()) { |
| return reg_types->Boolean(); // boolean MERGE boolean => boolean |
| } |
| if (IsByteTypes() && incoming_type.IsByteTypes()) { |
| return reg_types->Byte(); // byte MERGE byte => byte |
| } |
| if (IsShortTypes() && incoming_type.IsShortTypes()) { |
| return reg_types->Short(); // short MERGE short => short |
| } |
| if (IsCharTypes() && incoming_type.IsCharTypes()) { |
| return reg_types->Char(); // char MERGE char => char |
| } |
| return reg_types->Integer(); // int MERGE * => int |
| } else if ((IsFloatTypes() && incoming_type.IsFloatTypes()) || |
| (IsLongTypes() && incoming_type.IsLongTypes()) || |
| (IsLongHighTypes() && incoming_type.IsLongHighTypes()) || |
| (IsDoubleTypes() && incoming_type.IsDoubleTypes()) || |
| (IsDoubleHighTypes() && incoming_type.IsDoubleHighTypes())) { |
| // check constant case was handled prior to entry |
| DCHECK(!IsConstant() || !incoming_type.IsConstant()); |
| // float/long/double MERGE float/long/double_constant => float/long/double |
| return SelectNonConstant(*this, incoming_type); |
| } else if (IsReferenceTypes() && incoming_type.IsReferenceTypes()) { |
| if (IsZero() || incoming_type.IsZero()) { |
| return SelectNonConstant(*this, incoming_type); // 0 MERGE ref => ref |
| } else if (IsJavaLangObject() || incoming_type.IsJavaLangObject()) { |
| return reg_types->JavaLangObject(false); // Object MERGE ref => Object |
| } else if (IsUnresolvedTypes() || incoming_type.IsUnresolvedTypes()) { |
| // We know how to merge an unresolved type with itself, 0 or Object. In this case we |
| // have two sub-classes and don't know how to merge. Create a new string-based unresolved |
| // type that reflects our lack of knowledge and that allows the rest of the unresolved |
| // mechanics to continue. |
| return reg_types->FromUnresolvedMerge(*this, incoming_type); |
| } else if (IsUninitializedTypes() || incoming_type.IsUninitializedTypes()) { |
| // Something that is uninitialized hasn't had its constructor called. Mark any merge |
| // of this type with something that is initialized as conflicting. The cases of a merge |
| // with itself, 0 or Object are handled above. |
| return reg_types->Conflict(); |
| } else { // Two reference types, compute Join |
| mirror::Class* c1 = GetClass(); |
| mirror::Class* c2 = incoming_type.GetClass(); |
| DCHECK(c1 != NULL && !c1->IsPrimitive()); |
| DCHECK(c2 != NULL && !c2->IsPrimitive()); |
| mirror::Class* join_class = ClassJoin(c1, c2); |
| if (c1 == join_class && !IsPreciseReference()) { |
| return *this; |
| } else if (c2 == join_class && !incoming_type.IsPreciseReference()) { |
| return incoming_type; |
| } else { |
| return reg_types->FromClass(ClassHelper(join_class).GetDescriptor(), join_class, false); |
| } |
| } |
| } else { |
| return reg_types->Conflict(); // Unexpected types => Conflict |
| } |
| } |
| |
| // See comment in reg_type.h |
| mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { |
| DCHECK(!s->IsPrimitive()) << PrettyClass(s); |
| DCHECK(!t->IsPrimitive()) << PrettyClass(t); |
| if (s == t) { |
| return s; |
| } else if (s->IsAssignableFrom(t)) { |
| return s; |
| } else if (t->IsAssignableFrom(s)) { |
| return t; |
| } else if (s->IsArrayClass() && t->IsArrayClass()) { |
| mirror::Class* s_ct = s->GetComponentType(); |
| mirror::Class* t_ct = t->GetComponentType(); |
| if (s_ct->IsPrimitive() || t_ct->IsPrimitive()) { |
| // Given the types aren't the same, if either array is of primitive types then the only |
| // common parent is java.lang.Object |
| mirror::Class* result = s->GetSuperClass(); // short-cut to java.lang.Object |
| DCHECK(result->IsObjectClass()); |
| return result; |
| } |
| mirror::Class* common_elem = ClassJoin(s_ct, t_ct); |
| ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); |
| Thread* self = Thread::Current(); |
| SirtRef<mirror::ClassLoader> class_loader(self, s->GetClassLoader()); |
| std::string descriptor("["); |
| descriptor += ClassHelper(common_elem).GetDescriptor(); |
| mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); |
| DCHECK(array_class != NULL); |
| return array_class; |
| } else { |
| size_t s_depth = s->Depth(); |
| size_t t_depth = t->Depth(); |
| // Get s and t to the same depth in the hierarchy |
| if (s_depth > t_depth) { |
| while (s_depth > t_depth) { |
| s = s->GetSuperClass(); |
| s_depth--; |
| } |
| } else { |
| while (t_depth > s_depth) { |
| t = t->GetSuperClass(); |
| t_depth--; |
| } |
| } |
| // Go up the hierarchy until we get to the common parent |
| while (s != t) { |
| s = s->GetSuperClass(); |
| t = t->GetSuperClass(); |
| } |
| return s; |
| } |
| } |
| |
| void RegType::CheckInvariants() const { |
| if (IsConstant() || IsConstantLo() || IsConstantHi()) { |
| CHECK(descriptor_.empty()) << *this; |
| CHECK(klass_ == NULL) << *this; |
| } |
| if (klass_ != NULL) { |
| CHECK(!descriptor_.empty()) << *this; |
| } |
| } |
| |
| void UninitializedThisReferenceType::CheckInvariants() const { |
| CHECK_EQ(GetAllocationPc(), 0U) << *this; |
| } |
| |
| void UnresolvedUninitializedThisRefType::CheckInvariants() const { |
| CHECK_EQ(GetAllocationPc(), 0U) << *this; |
| CHECK(!descriptor_.empty()) << *this; |
| CHECK(klass_ == NULL) << *this; |
| } |
| |
| void UnresolvedUninitializedRefType::CheckInvariants() const { |
| CHECK(!descriptor_.empty()) << *this; |
| CHECK(klass_ == NULL) << *this; |
| } |
| |
| void UnresolvedMergedType::CheckInvariants() const { |
| // Unresolved merged types: merged types should be defined. |
| CHECK(descriptor_.empty()) << *this; |
| CHECK(klass_ == NULL) << *this; |
| CHECK_NE(merged_types_.first, 0U) << *this; |
| CHECK_NE(merged_types_.second, 0U) << *this; |
| } |
| |
| void UnresolvedReferenceType::CheckInvariants() const { |
| CHECK(!descriptor_.empty()) << *this; |
| CHECK(klass_ == NULL) << *this; |
| } |
| |
| void UnresolvedSuperClass::CheckInvariants() const { |
| // Unresolved merged types: merged types should be defined. |
| CHECK(descriptor_.empty()) << *this; |
| CHECK(klass_ == NULL) << *this; |
| CHECK_NE(unresolved_child_id_, 0U) << *this; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const RegType& rhs) { |
| os << rhs.Dump(); |
| return os; |
| } |
| |
| } // namespace verifier |
| } // namespace art |