ART: Allow array-ness for unresolved merge types
In case component types aren't resolvable, array types won't be
either. They then may be merged. The merge is still potentially
an array type.
Ensure that merging an unresolved array type with a primitive
array type or a non-array type will be resolved to java.lang.Object.
Added tests.
Bug: 27799205
Change-Id: I9beff75318814dddd842abd64ef9a5d2644d801e
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 537d9c9..9bcbdbd 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2425,6 +2425,10 @@
if (!array_type.IsArrayTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type "
<< array_type;
+ } else if (array_type.IsUnresolvedTypes()) {
+ // If it's an unresolved array type, it must be non-primitive.
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data for array of type "
+ << array_type;
} else {
const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
DCHECK(!component_type.IsConflict());
@@ -4209,6 +4213,7 @@
const RegType& precise_type = reg_types_.FromUninitialized(res_type);
work_line_->SetRegisterType<LockOp::kClear>(this, inst->VRegA_22c(), precise_type);
} else {
+ DCHECK(!res_type.IsUnresolvedMergedReference());
// Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of
// the list and fail. It's legal, if silly, for arg_count to be zero.
const RegType& expected_type = reg_types_.GetComponentType(res_type, GetClassLoader());
@@ -4253,6 +4258,15 @@
}
} else if (!array_type.IsArrayTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
+ } else if (array_type.IsUnresolvedTypes()) {
+ // Unresolved array types must be reference array types.
+ if (is_primitive) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "reference array type " << array_type
+ << " source for category 1 aget";
+ } else {
+ Fail(VERIFY_ERROR_NO_CLASS) << "cannot verify aget for " << array_type
+ << " because of missing class";
+ }
} else {
/* verify the class */
const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
@@ -4363,6 +4377,15 @@
work_line_->VerifyRegisterType(this, inst->VRegA_23x(), *modified_reg_type);
} else if (!array_type.IsArrayTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput";
+ } else if (array_type.IsUnresolvedTypes()) {
+ // Unresolved array types must be reference array types.
+ if (is_primitive) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "put insn has type '" << insn_type
+ << "' but unresolved type '" << array_type << "'";
+ } else {
+ Fail(VERIFY_ERROR_NO_CLASS) << "cannot verify aput for " << array_type
+ << " because of missing class";
+ }
} else {
const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
const uint32_t vregA = inst->VRegA_23x();
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 0894f5d..bd0308b 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -517,9 +517,21 @@
}
}
+bool RegType::IsJavaLangObject() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ return IsReference() && GetClass()->IsObjectClass();
+}
+
bool RegType::IsObjectArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_) {
- if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
- // Primitive arrays will always resolve
+ if (IsUnresolvedTypes()) {
+ DCHECK(!IsUnresolvedMergedReference());
+
+ if (IsUnresolvedSuperClass()) {
+ // Cannot be an array, as the superclass of arrays is java.lang.Object (which cannot be
+ // unresolved).
+ return false;
+ }
+
+ // Primitive arrays will always resolve.
DCHECK(descriptor_[1] == 'L' || descriptor_[1] == '[');
return descriptor_[0] == '[';
} else if (HasClass()) {
@@ -530,12 +542,15 @@
}
}
-bool RegType::IsJavaLangObject() const SHARED_REQUIRES(Locks::mutator_lock_) {
- return IsReference() && GetClass()->IsObjectClass();
-}
-
bool RegType::IsArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_) {
- if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
+ if (IsUnresolvedTypes()) {
+ DCHECK(!IsUnresolvedMergedReference());
+
+ if (IsUnresolvedSuperClass()) {
+ // Cannot be an array, as the superclass of arrays is java.lang.Object (which cannot be
+ // unresolved).
+ return false;
+ }
return descriptor_[0] == '[';
} else if (HasClass()) {
return GetClass()->IsArrayClass();
@@ -793,11 +808,50 @@
}
}
void UnresolvedMergedType::CheckInvariants() const {
+ CHECK(reg_type_cache_ != nullptr);
+
// Unresolved merged types: merged types should be defined.
CHECK(descriptor_.empty()) << *this;
CHECK(klass_.IsNull()) << *this;
+
+ CHECK(!resolved_part_.IsConflict());
CHECK(resolved_part_.IsReferenceTypes());
CHECK(!resolved_part_.IsUnresolvedTypes());
+
+ CHECK(resolved_part_.IsZero() ||
+ !(resolved_part_.IsArrayTypes() && !resolved_part_.IsObjectArrayTypes()));
+
+ CHECK_GT(unresolved_types_.NumSetBits(), 0U);
+ bool unresolved_is_array =
+ reg_type_cache_->GetFromId(unresolved_types_.GetHighestBitSet()).IsArrayTypes();
+ for (uint32_t idx : unresolved_types_.Indexes()) {
+ const RegType& t = reg_type_cache_->GetFromId(idx);
+ CHECK_EQ(unresolved_is_array, t.IsArrayTypes());
+ }
+
+ if (!resolved_part_.IsZero()) {
+ CHECK_EQ(resolved_part_.IsArrayTypes(), unresolved_is_array);
+ }
+}
+
+bool UnresolvedMergedType::IsArrayTypes() const {
+ // For a merge to be an array, both the resolved and the unresolved part need to be object
+ // arrays.
+ // (Note: we encode a missing resolved part [which doesn't need to be an array] as zero.)
+
+ if (!resolved_part_.IsZero() && !resolved_part_.IsArrayTypes()) {
+ return false;
+ }
+
+ // It is enough to check just one of the merged types. Otherwise the merge should have been
+ // collapsed (checked in CheckInvariants on construction).
+ uint32_t idx = unresolved_types_.GetHighestBitSet();
+ const RegType& unresolved = reg_type_cache_->GetFromId(idx);
+ return unresolved.IsArrayTypes();
+}
+bool UnresolvedMergedType::IsObjectArrayTypes() const {
+ // Same as IsArrayTypes, as primitive arrays are always resolved.
+ return IsArrayTypes();
}
void UnresolvedReferenceType::CheckInvariants() const {
@@ -824,6 +878,14 @@
return false;
}
+ if (IsUnresolvedTypes() || src.IsUnresolvedTypes()) {
+ // An unresolved array type means that it's an array of some reference type. Reference arrays
+ // can never be assigned to primitive-type arrays, and vice versa. So it is a soft error if
+ // both arrays are reference arrays, otherwise a hard error.
+ *soft_error = IsObjectArrayTypes() && src.IsObjectArrayTypes();
+ return false;
+ }
+
const RegType& cmp1 = reg_types.GetComponentType(*this, class_loader.Get());
const RegType& cmp2 = reg_types.GetComponentType(src, class_loader.Get());
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 7c7981e..4837490 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -172,8 +172,8 @@
}
virtual bool HasClassVirtual() const { return false; }
bool IsJavaLangObject() const SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsObjectArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_);
+ virtual bool IsArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_);
+ virtual bool IsObjectArrayTypes() const SHARED_REQUIRES(Locks::mutator_lock_);
Primitive::Type GetPrimitiveType() const;
bool IsJavaLangObjectArray() const
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -905,6 +905,9 @@
bool IsUnresolvedTypes() const OVERRIDE { return true; }
+ bool IsArrayTypes() const OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
+ bool IsObjectArrayTypes() const OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
+
std::string Dump() const OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
private:
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index b171b75..208ef51 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -347,29 +347,39 @@
kDefaultArenaBitVectorBytes * kBitsPerByte, // Allocate at least 8 bytes.
true); // Is expandable.
const RegType* left_resolved;
+ bool left_unresolved_is_array;
if (left.IsUnresolvedMergedReference()) {
- const UnresolvedMergedType* left_merge = down_cast<const UnresolvedMergedType*>(&left);
- types.Copy(&left_merge->GetUnresolvedTypes());
- left_resolved = &left_merge->GetResolvedPart();
+ const UnresolvedMergedType& left_merge = *down_cast<const UnresolvedMergedType*>(&left);
+
+ types.Copy(&left_merge.GetUnresolvedTypes());
+ left_resolved = &left_merge.GetResolvedPart();
+ left_unresolved_is_array = left.IsArrayTypes();
} else if (left.IsUnresolvedTypes()) {
types.ClearAllBits();
types.SetBit(left.GetId());
left_resolved = &Zero();
+ left_unresolved_is_array = left.IsArrayTypes();
} else {
types.ClearAllBits();
left_resolved = &left;
+ left_unresolved_is_array = false;
}
const RegType* right_resolved;
+ bool right_unresolved_is_array;
if (right.IsUnresolvedMergedReference()) {
- const UnresolvedMergedType* right_merge = down_cast<const UnresolvedMergedType*>(&right);
- types.Union(&right_merge->GetUnresolvedTypes());
- right_resolved = &right_merge->GetResolvedPart();
+ const UnresolvedMergedType& right_merge = *down_cast<const UnresolvedMergedType*>(&right);
+
+ types.Union(&right_merge.GetUnresolvedTypes());
+ right_resolved = &right_merge.GetResolvedPart();
+ right_unresolved_is_array = right.IsArrayTypes();
} else if (right.IsUnresolvedTypes()) {
types.SetBit(right.GetId());
right_resolved = &Zero();
+ right_unresolved_is_array = right.IsArrayTypes();
} else {
right_resolved = &right;
+ right_unresolved_is_array = false;
}
// Merge the resolved parts. Left and right might be equal, so use SafeMerge.
@@ -379,6 +389,23 @@
return Conflict();
}
+ bool resolved_merged_is_array = resolved_parts_merged.IsArrayTypes();
+ if (left_unresolved_is_array || right_unresolved_is_array || resolved_merged_is_array) {
+ // Arrays involved, see if we need to merge to Object.
+
+ // Is the resolved part a primitive array?
+ if (resolved_merged_is_array && !resolved_parts_merged.IsObjectArrayTypes()) {
+ return JavaLangObject(false /* precise */);
+ }
+
+ // Is any part not an array (but exists)?
+ if ((!left_unresolved_is_array && left_resolved != &left) ||
+ (!right_unresolved_is_array && right_resolved != &right) ||
+ !resolved_merged_is_array) {
+ return JavaLangObject(false /* precise */);
+ }
+ }
+
// Check if entry already exists.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
@@ -584,6 +611,7 @@
if (!array.IsArrayTypes()) {
return Conflict();
} else if (array.IsUnresolvedTypes()) {
+ DCHECK(!array.IsUnresolvedTypes()); // Caller must make sure not to ask for this.
const std::string descriptor(array.GetDescriptor().as_string());
return FromDescriptor(loader, descriptor.c_str() + 1, false);
} else {