ART: Fix exception stash in stack dump

It is the executing thread that needs its exception stashed. This
may be different from the dumped thread, which, in case it is not
the same thread, the verifier does not care about.

Use RAII for stashing. Make the code simpler by removing the flag
and instead use a non-null exception object as flag.

Bug: 133764636
Test: m test-art-host
Change-Id: I2b6a44f41880c96a0cf9bb343db22963dca0a758
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 1ad5b7e..6c1f71c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2133,23 +2133,25 @@
   // assumption that there is no exception pending on entry. Thus, stash any pending exception.
   // Thread::Current() instead of this in case a thread is dumping the stack of another suspended
   // thread.
-  StackHandleScope<1> scope(Thread::Current());
-  Handle<mirror::Throwable> exc;
-  bool have_exception = false;
-  if (IsExceptionPending()) {
-    exc = scope.NewHandle(GetException());
-    const_cast<Thread*>(this)->ClearException();
-    have_exception = true;
-  }
+  struct ScopedExceptionStorage {
+    ScopedExceptionStorage() : scope(Thread::Current()) {
+      exc = scope.NewHandle(scope.Self()->GetException());
+      scope.Self()->ClearException();
+    }
+    ~ScopedExceptionStorage() {
+      if (exc != nullptr) {
+        scope.Self()->SetException(exc.Get());
+      }
+    }
+    StackHandleScope<1> scope;
+    Handle<mirror::Throwable> exc;
+  };
+  ScopedExceptionStorage ses;
 
   std::unique_ptr<Context> context(Context::Create());
   StackDumpVisitor dumper(os, const_cast<Thread*>(this), context.get(),
                           !tls32_.throwing_OutOfMemoryError, check_suspended, dump_locks);
   dumper.WalkStack();
-
-  if (have_exception) {
-    const_cast<Thread*>(this)->SetException(exc.Get());
-  }
 }
 
 void Thread::DumpStack(std::ostream& os,