Relax the IsInstantiable check when converting an uninitialized
reference type. This is a double protection that leads to wrong
behavior as it may trigger an incorrect hard failure.
Bug:
22777307
Change-Id: Ie555b175f78876647fef083369d40bfe3fd7b69a
PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const std::string& descriptor,
uint16_t cache_id)
: RegType(klass, descriptor, cache_id) {
- DCHECK(klass->IsInstantiable());
+ // Note: no check for IsInstantiable() here. We may produce this in case an InstantiationError
+ // would be thrown at runtime, but we need to continue verification and *not* create a
+ // hard failure or abort.
}
std::string UnresolvedMergedType::Dump() const {
}
}
entry = new ReferenceType(klass, "", entries_.size());
- } else if (klass->IsInstantiable()) {
+ } else if (!klass->IsPrimitive()) {
// We're uninitialized because of allocation, look or create a precise type as allocations
// may only create objects of that type.
+ // Note: we do not check whether the given klass is actually instantiable (besides being
+ // primitive), that is, we allow interfaces and abstract classes here. The reasoning is
+ // twofold:
+ // 1) The "new-instance" instruction to generate the uninitialized type will already
+ // queue an instantiation error. This is a soft error that must be thrown at runtime,
+ // and could potentially change if the class is resolved differently at runtime.
+ // 2) Checking whether the klass is instantiable and using conflict may produce a hard
+ // error when the value is used, which leads to a VerifyError, which is not the
+ // correct semantics.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) {
b/22411633 (3)
b/22411633 (4)
b/22411633 (5)
+b/22777307
Done!
--- /dev/null
+.class public LB22777307;
+.super Ljava/lang/Object;
+
+# A static field. That way we can use the reference.
+.field private static sTest:Ljava/lang/Object;
+
+.method public static run()V
+.registers 2
+ # This is a broken new-instance. It needs to throw at runtime, though. This test is here to
+ # ensure we won't produce a VerifyError.
+ # Cloneable was chosen because it's an already existing interface.
+ new-instance v0, Ljava/lang/Cloneable;
+ invoke-direct {v0}, Ljava/lang/Cloneable;-><init>()V
+ sput-object v0, LB22777307;->sTest:Ljava/lang/Object;
+
+ return-void
+
+.end method
new VerifyError(), null));
testCases.add(new TestCase("b/22411633 (5)", "B22411633_5", "run", new Object[] { false },
null, null));
+ testCases.add(new TestCase("b/22777307", "B22777307", "run", null, new InstantiationError(),
+ null));
}
public void runTests() {