diff options
author | Sebastien Hertz <shertz@google.com> | 2015-04-02 11:11:19 +0200 |
---|---|---|
committer | Sebastien Hertz <shertz@google.com> | 2015-04-03 09:31:59 +0200 |
commit | 2fd7e69505195cda4caaa3161aaf37315552a698 (patch) | |
tree | 2bd681443ad36a8616f237a12e56ecbdb7054f8e | |
parent | c6e949a6d93fae2351fc59ed825657adee8185dc (diff) | |
download | art-2fd7e69505195cda4caaa3161aaf37315552a698.zip art-2fd7e69505195cda4caaa3161aaf37315552a698.tar.gz art-2fd7e69505195cda4caaa3161aaf37315552a698.tar.bz2 |
Use specific exception class to abort transaction
We used to throw a java.lang.InternalError when aborting a
transaction (when preinitializing image classes at compilation time).
We now use dedicated class dalvik.system.TransactionAbortError that
is only thrown by the compiler to abort a transaction. This class has
constructors taking a java.lang.Throwable "cause" so we can wrap
exceptions causing the transaction to abort (for instance class
java.lang.ClassNotFoundException) and give more information about the
cause of the transaction abort.
Bug: 20019689
Change-Id: I019a72a1c754d8bba6a7ad6bb0f02e4fd6668622
-rw-r--r-- | runtime/class_linker.cc | 2 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.cc | 2 | ||||
-rw-r--r-- | runtime/interpreter/unstarted_runtime.cc | 14 | ||||
-rw-r--r-- | runtime/runtime.cc | 9 | ||||
-rw-r--r-- | runtime/runtime.h | 4 | ||||
-rw-r--r-- | runtime/transaction.cc | 11 | ||||
-rw-r--r-- | runtime/transaction.h | 5 | ||||
-rw-r--r-- | runtime/transaction_test.cc | 8 |
8 files changed, 31 insertions, 24 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index a89196d..be4b9e9 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3411,7 +3411,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, // so we need to throw it again now. VLOG(compiler) << "Return from class initializer of " << PrettyDescriptor(klass.Get()) << " without exception while transaction was aborted: re-throw it now."; - Runtime::Current()->ThrowInternalErrorForAbortedTransaction(self); + Runtime::Current()->ThrowTransactionAbortError(self); mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self); success = false; } else { diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index a310452..582843c 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -475,7 +475,7 @@ void AbortTransaction(Thread* self, const char* fmt, ...) { std::string abort_msg; StringAppendV(&abort_msg, fmt, args); // Throws an exception so we can abort the transaction and rollback every change. - Runtime::Current()->AbortTransactionAndThrowInternalError(self, abort_msg); + Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg); va_end(args); } diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 281f332..2aa4af4 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -37,6 +37,7 @@ #include "mirror/string-inl.h" #include "nth_caller_visitor.h" #include "thread.h" +#include "transaction.h" #include "well_known_classes.h" namespace art { @@ -87,13 +88,14 @@ static void UnstartedRuntimeFindClass(Thread* self, Handle<mirror::String> class // Common helper for class-loading cutouts in an unstarted runtime. We call Runtime methods that // rely on Java code to wrap errors in the correct exception class (i.e., NoClassDefFoundError into // ClassNotFoundException), so need to do the same. The only exception is if the exception is -// actually InternalError. This must not be wrapped, as it signals an initialization abort. +// actually the transaction abort exception. This must not be wrapped, as it signals an +// initialization abort. static void CheckExceptionGenerateClassNotFound(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (self->IsExceptionPending()) { - // If it is not an InternalError, wrap it. + // If it is not the transaction abort exception, wrap it. std::string type(PrettyTypeOf(self->GetException())); - if (type != "java.lang.InternalError") { + if (type != Transaction::kAbortExceptionDescriptor) { self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;", "ClassNotFoundException"); } @@ -502,7 +504,7 @@ static void UnstartedDexCacheGetDexNative( } if (!have_dex) { self->ClearException(); - Runtime::Current()->AbortTransactionAndThrowInternalError(self, "Could not create Dex object"); + Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Could not create Dex object"); } } @@ -570,7 +572,7 @@ static void UnstartedMemoryPeekArray( int64_t address_long = shadow_frame->GetVRegLong(arg_offset); mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset + 2); if (obj == nullptr) { - Runtime::Current()->AbortTransactionAndThrowInternalError(self, "Null pointer in peekArray"); + Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Null pointer in peekArray"); return; } mirror::Array* array = obj->AsArray(); @@ -580,7 +582,7 @@ static void UnstartedMemoryPeekArray( if (offset < 0 || offset + count > array->GetLength()) { std::string error_msg(StringPrintf("Array out of bounds in peekArray: %d/%d vs %d", offset, count, array->GetLength())); - Runtime::Current()->AbortTransactionAndThrowInternalError(self, error_msg.c_str()); + Runtime::Current()->AbortTransactionAndThrowAbortError(self, error_msg.c_str()); return; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b5d2e15..b82e897 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1533,21 +1533,20 @@ bool Runtime::IsTransactionAborted() const { } } -void Runtime::AbortTransactionAndThrowInternalError(Thread* self, - const std::string& abort_message) { +void Runtime::AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) { DCHECK(IsAotCompiler()); DCHECK(IsActiveTransaction()); // Throwing an exception may cause its class initialization. If we mark the transaction // aborted before that, we may warn with a false alarm. Throwing the exception before // marking the transaction aborted avoids that. - preinitialization_transaction_->ThrowInternalError(self, false); + preinitialization_transaction_->ThrowAbortError(self, false); preinitialization_transaction_->Abort(abort_message); } -void Runtime::ThrowInternalErrorForAbortedTransaction(Thread* self) { +void Runtime::ThrowTransactionAbortError(Thread* self) { DCHECK(IsAotCompiler()); DCHECK(IsActiveTransaction()); - preinitialization_transaction_->ThrowInternalError(self, true); + preinitialization_transaction_->ThrowAbortError(self, true); } void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, diff --git a/runtime/runtime.h b/runtime/runtime.h index 64b7183..af3d899 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -468,9 +468,9 @@ class Runtime { void ExitTransactionMode(); bool IsTransactionAborted() const; - void AbortTransactionAndThrowInternalError(Thread* self, const std::string& abort_message) + void AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ThrowInternalErrorForAbortedTransaction(Thread* self) + void ThrowTransactionAbortError(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value, diff --git a/runtime/transaction.cc b/runtime/transaction.cc index 186cfea..5b8d23b 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -60,8 +60,8 @@ Transaction::~Transaction() { void Transaction::Abort(const std::string& abort_message) { MutexLock mu(Thread::Current(), log_lock_); - // We may abort more than once if the java.lang.InternalError thrown at the - // time of the abort has been caught during execution of a class initializer. + // We may abort more than once if the exception thrown at the time of the + // previous abort has been caught during execution of a class initializer. // We just keep the message of the first abort because it will cause the // transaction to be rolled back anyway. if (!aborted_) { @@ -70,16 +70,17 @@ void Transaction::Abort(const std::string& abort_message) { } } -void Transaction::ThrowInternalError(Thread* self, bool rethrow) { +void Transaction::ThrowAbortError(Thread* self, bool rethrow) { if (kIsDebugBuild && rethrow) { - CHECK(IsAborted()) << "Rethrow InternalError while transaction is not aborted"; + CHECK(IsAborted()) << "Rethrow " << Transaction::kAbortExceptionDescriptor + << " while transaction is not aborted"; } std::string abort_msg(GetAbortMessage()); // Temporary workaround for b/20019689. if (self->IsExceptionPending()) { self->ClearException(); } - self->ThrowNewException("Ljava/lang/InternalError;", abort_msg.c_str()); + self->ThrowNewException(Transaction::kAbortExceptionSignature, abort_msg.c_str()); } bool Transaction::IsAborted() { diff --git a/runtime/transaction.h b/runtime/transaction.h index e1b93c9..1419a38 100644 --- a/runtime/transaction.h +++ b/runtime/transaction.h @@ -39,13 +39,16 @@ class InternTable; class Transaction FINAL { public: + static constexpr const char* kAbortExceptionDescriptor = "dalvik.system.TransactionAbortError"; + static constexpr const char* kAbortExceptionSignature = "Ldalvik/system/TransactionAbortError;"; + Transaction(); ~Transaction(); void Abort(const std::string& abort_message) LOCKS_EXCLUDED(log_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ThrowInternalError(Thread* self, bool rethrow) + void ThrowAbortError(Thread* self, bool rethrow) LOCKS_EXCLUDED(log_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsAborted() LOCKS_EXCLUDED(log_lock_); diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index 5db51c8..24ecf6b 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -35,8 +35,9 @@ class TransactionTest : public CommonRuntimeTest { hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader))); ASSERT_TRUE(class_loader.Get() != nullptr); - // Load and initialize java.lang.ExceptionInInitializerError and java.lang.InternalError - // classes so they can be thrown during class initialization if the transaction aborts. + // Load and initialize java.lang.ExceptionInInitializerError and the exception class used + // to abort transaction so they can be thrown during class initialization if the transaction + // aborts. MutableHandle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ExceptionInInitializerError;"))); @@ -44,7 +45,8 @@ class TransactionTest : public CommonRuntimeTest { class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(h_klass->IsInitialized()); - h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;")); + h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), + Transaction::kAbortExceptionSignature)); ASSERT_TRUE(h_klass.Get() != nullptr); class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(h_klass->IsInitialized()); |