ART: Remove VERIFY_ERROR_UNRESOLVED_CATCH
Instead add a parameter to Fail() that defines whether a runtime
exception is expected at that point.
Bug: 121245951
Test: m test-art-host
Change-Id: Id9132eabc26b71a305ccb87f2182b4ae2e5b8c35
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index b644dba..8396fe9 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3829,12 +3829,13 @@
// We need to post a failure. The compiler currently does not handle unreachable
// code correctly.
- Fail(VERIFY_ERROR_UNRESOLVED_CATCH) << "Unresolved catch handler, fail for compiler";
+ Fail(VERIFY_ERROR_NO_CLASS, /*pending_exc=*/ false)
+ << "Unresolved catch handler, fail for compiler";
return std::make_pair(false, unresolved);
}
// Soft-fail, but do not handle this with a synthetic throw.
- Fail(VERIFY_ERROR_UNRESOLVED_CATCH) << "Unresolved catch handler";
+ Fail(VERIFY_ERROR_NO_CLASS, /*pending_exc=*/ false) << "Unresolved catch handler";
if (common_super != nullptr) {
unresolved = &unresolved->Merge(*common_super, ®_types_, this);
}
@@ -5513,70 +5514,72 @@
reg_types_.VisitRoots(visitor, root_info);
}
-std::ostream& MethodVerifier::Fail(VerifyError error) {
+std::ostream& MethodVerifier::Fail(VerifyError error, bool pending_exc) {
// Mark the error type as encountered.
encountered_failure_types_ |= static_cast<uint32_t>(error);
- switch (error) {
- case VERIFY_ERROR_NO_CLASS:
- case VERIFY_ERROR_NO_FIELD:
- case VERIFY_ERROR_NO_METHOD:
- case VERIFY_ERROR_ACCESS_CLASS:
- case VERIFY_ERROR_ACCESS_FIELD:
- case VERIFY_ERROR_ACCESS_METHOD:
- case VERIFY_ERROR_INSTANTIATION:
- case VERIFY_ERROR_CLASS_CHANGE:
- case VERIFY_ERROR_FORCE_INTERPRETER:
- case VERIFY_ERROR_LOCKING:
- if (Runtime::Current()->IsAotCompiler() || !can_load_classes_) {
- // If we're optimistically running verification at compile time, turn NO_xxx, ACCESS_xxx,
- // class change and instantiation errors into soft verification errors so that we re-verify
- // at runtime. We may fail to find or to agree on access because of not yet available class
- // loaders, or class loaders that will differ at runtime. In these cases, we don't want to
- // affect the soundness of the code being compiled. Instead, the generated code runs "slow
- // paths" that dynamically perform the verification and cause the behavior to be that akin
- // to an interpreter.
- error = VERIFY_ERROR_BAD_CLASS_SOFT;
- } else {
- // If we fail again at runtime, mark that this instruction would throw and force this
- // method to be executed using the interpreter with checks.
- flags_.have_pending_runtime_throw_failure_ = true;
+ if (pending_exc) {
+ switch (error) {
+ case VERIFY_ERROR_NO_CLASS:
+ case VERIFY_ERROR_NO_FIELD:
+ case VERIFY_ERROR_NO_METHOD:
+ case VERIFY_ERROR_ACCESS_CLASS:
+ case VERIFY_ERROR_ACCESS_FIELD:
+ case VERIFY_ERROR_ACCESS_METHOD:
+ case VERIFY_ERROR_INSTANTIATION:
+ case VERIFY_ERROR_CLASS_CHANGE:
+ case VERIFY_ERROR_FORCE_INTERPRETER:
+ case VERIFY_ERROR_LOCKING:
+ if (Runtime::Current()->IsAotCompiler() || !can_load_classes_) {
+ // If we're optimistically running verification at compile time, turn NO_xxx, ACCESS_xxx,
+ // class change and instantiation errors into soft verification errors so that we
+ // re-verify at runtime. We may fail to find or to agree on access because of not yet
+ // available class loaders, or class loaders that will differ at runtime. In these cases,
+ // we don't want to affect the soundness of the code being compiled. Instead, the
+ // generated code runs "slow paths" that dynamically perform the verification and cause
+ // the behavior to be that akin to an interpreter.
+ error = VERIFY_ERROR_BAD_CLASS_SOFT;
+ } else {
+ // If we fail again at runtime, mark that this instruction would throw and force this
+ // method to be executed using the interpreter with checks.
+ flags_.have_pending_runtime_throw_failure_ = true;
- // We need to save the work_line if the instruction wasn't throwing before. Otherwise we'll
- // try to merge garbage.
- // Note: this assumes that Fail is called before we do any work_line modifications.
- // Note: this can fail before we touch any instruction, for the signature of a method. So
- // add a check.
- if (work_insn_idx_ < dex::kDexNoIndex) {
- const Instruction& inst = code_item_accessor_.InstructionAt(work_insn_idx_);
- int opcode_flags = Instruction::FlagsOf(inst.Opcode());
+ // We need to save the work_line if the instruction wasn't throwing before. Otherwise
+ // we'll try to merge garbage.
+ // Note: this assumes that Fail is called before we do any work_line modifications.
+ // Note: this can fail before we touch any instruction, for the signature of a method. So
+ // add a check.
+ if (work_insn_idx_ < dex::kDexNoIndex) {
+ const Instruction& inst = code_item_accessor_.InstructionAt(work_insn_idx_);
+ int opcode_flags = Instruction::FlagsOf(inst.Opcode());
- if ((opcode_flags & Instruction::kThrow) == 0 &&
- GetInstructionFlags(work_insn_idx_).IsInTry()) {
- saved_line_->CopyFromLine(work_line_.get());
+ if ((opcode_flags & Instruction::kThrow) == 0 &&
+ GetInstructionFlags(work_insn_idx_).IsInTry()) {
+ saved_line_->CopyFromLine(work_line_.get());
+ }
}
}
- }
- break;
+ break;
- // Indication that verification should be retried at runtime.
- case VERIFY_ERROR_BAD_CLASS_SOFT:
- if (!allow_soft_failures_) {
+ // Indication that verification should be retried at runtime.
+ case VERIFY_ERROR_BAD_CLASS_SOFT:
+ if (!allow_soft_failures_) {
+ flags_.have_pending_hard_failure_ = true;
+ }
+ break;
+
+ // Hard verification failures at compile time will still fail at runtime, so the class is
+ // marked as rejected to prevent it from being compiled.
+ case VERIFY_ERROR_BAD_CLASS_HARD: {
flags_.have_pending_hard_failure_ = true;
+ break;
}
- break;
-
- // Hard verification failures at compile time will still fail at runtime, so the class is
- // marked as rejected to prevent it from being compiled.
- case VERIFY_ERROR_BAD_CLASS_HARD: {
- flags_.have_pending_hard_failure_ = true;
- break;
}
-
- case VERIFY_ERROR_UNRESOLVED_CATCH:
- // Nothing to do, just remember the failure type.
- break;
+ } else if (kIsDebugBuild) {
+ CHECK_NE(error, VERIFY_ERROR_BAD_CLASS_SOFT);
+ CHECK_NE(error, VERIFY_ERROR_BAD_CLASS_HARD);
}
+
failures_.push_back(error);
std::string location(StringPrintf("%s: [0x%X] ", dex_file_->PrettyMethod(dex_method_idx_).c_str(),
work_insn_idx_));
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index e3d0901..2b9a4bc 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -138,7 +138,7 @@
}
// Log a verification failure.
- std::ostream& Fail(VerifyError error);
+ std::ostream& Fail(VerifyError error, bool pending_exc = true);
// Log for verification information.
ScopedNewLine LogVerifyInfo();
diff --git a/runtime/verifier/verifier_enums.h b/runtime/verifier/verifier_enums.h
index 0e96882..96c98f9 100644
--- a/runtime/verifier/verifier_enums.h
+++ b/runtime/verifier/verifier_enums.h
@@ -94,9 +94,6 @@
// (sets a soft fail at compile time).
VERIFY_ERROR_LOCKING = 1 << 11, // Could not guarantee balanced locking. This should be
// punted to the interpreter with access checks.
- VERIFY_ERROR_UNRESOLVED_CATCH = 1 << 12, // Error code necessary to have a synthetic soft fail
- // that is not an exception, to let the compiler know
- // that there is (unreachable) unverified code.
};
std::ostream& operator<<(std::ostream& os, const VerifyError& rhs);