diff options
author | Vladimir Marko <vmarko@google.com> | 2024-05-02 10:56:42 +0000 |
---|---|---|
committer | VladimĂr Marko <vmarko@google.com> | 2024-05-03 03:44:33 +0000 |
commit | db15ea652d13811e236e1f12c88807ebfed05da9 (patch) | |
tree | 85d01be01468cd684a01dc0ee2b349867f6f2d6a | |
parent | f91d21c0b0e4a03102eaa6cfae0f500f8139bc40 (diff) | |
download | art-db15ea652d13811e236e1f12c88807ebfed05da9.tar.gz |
Refactor transaction checks in switch interpreter.
Prepare for moving the transactional interpreter from
`runtime/` to `dex2oat/` by moving transaction checking code
to a new file `active_transaction_checker.h`, and breaking
unstarted runtime dependency on transaction code by adding
an indirection with a virtual call through `ClassLinker`.
Consistently return the same value from contraint checks.
Previously we were returning the negated result from the
`CheckWrite{,Value}Constraint()` compared to the value we
received from `Transaction::Write{,Value}Constraint()`.
Test: m test-art-host-gtest
Test: testrunner.py --host --interp-ac
Change-Id: I88a168b6c770932d014e3a40486480590cef0401
-rw-r--r-- | runtime/class_linker.cc | 21 | ||||
-rw-r--r-- | runtime/class_linker.h | 9 | ||||
-rw-r--r-- | runtime/interpreter/active_transaction_checker.h | 74 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.cc | 16 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.h | 70 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_switch_impl-inl.h | 14 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_switch_impl1.cc | 2 | ||||
-rw-r--r-- | runtime/interpreter/unstarted_runtime.cc | 74 | ||||
-rw-r--r-- | runtime/oat/aot_class_linker.cc | 18 | ||||
-rw-r--r-- | runtime/oat/aot_class_linker.h | 8 | ||||
-rw-r--r-- | runtime/runtime.cc | 16 | ||||
-rw-r--r-- | runtime/runtime.h | 7 | ||||
-rw-r--r-- | runtime/transaction.cc | 5 |
13 files changed, 240 insertions, 94 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5b00a87217..08cc153d84 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -11166,6 +11166,27 @@ void ClassLinker::SetEnablePublicSdkChecks([[maybe_unused]] bool enabled) { UNREACHABLE(); } +bool ClassLinker::TransactionWriteConstraint( + [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Object> obj) const { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +bool ClassLinker::TransactionWriteValueConstraint( + [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Object> value) const { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +bool ClassLinker::TransactionAllocationConstraint( + [[maybe_unused]] Thread* self, [[maybe_unused]] ObjPtr<mirror::Class> klass) const { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + void ClassLinker::RemoveDexFromCaches(const DexFile& dex_file) { ReaderMutexLock mu(Thread::Current(), *Locks::dex_lock_); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index f2c08b93c5..eabfb4926a 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -890,6 +890,15 @@ class EXPORT ClassLinker { virtual bool DenyAccessBasedOnPublicSdk(const char* type_descriptor) const; // Enable or disable public sdk checks. virtual void SetEnablePublicSdkChecks(bool enabled); + + // Transaction constraint checks for AOT compilation. + virtual bool TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) const + REQUIRES_SHARED(Locks::mutator_lock_); + virtual bool TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) const + REQUIRES_SHARED(Locks::mutator_lock_); + virtual bool TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) const + REQUIRES_SHARED(Locks::mutator_lock_); + void RemoveDexFromCaches(const DexFile& dex_file); ClassTable* GetBootClassTable() REQUIRES_SHARED(Locks::classlinker_classes_lock_) { return boot_class_table_.get(); diff --git a/runtime/interpreter/active_transaction_checker.h b/runtime/interpreter/active_transaction_checker.h new file mode 100644 index 0000000000..867dc251eb --- /dev/null +++ b/runtime/interpreter/active_transaction_checker.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_INTERPRETER_ACTIVE_TRANSACTION_CHECKER_H_ +#define ART_RUNTIME_INTERPRETER_ACTIVE_TRANSACTION_CHECKER_H_ + +#include "base/macros.h" +#include "gc/heap.h" +#include "mirror/object-inl.h" +#include "runtime.h" +#include "transaction.h" + +namespace art HIDDEN { +namespace interpreter { + +class ActiveTransactionChecker { + public: + static inline bool WriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) + REQUIRES_SHARED(Locks::mutator_lock_) { + Runtime* runtime = Runtime::Current(); + if (runtime->GetTransaction()->WriteConstraint(obj)) { + DCHECK(runtime->GetHeap()->ObjectIsInBootImageSpace(obj) || obj->IsClass()); + const char* base_msg = runtime->GetHeap()->ObjectIsInBootImageSpace(obj) + ? "Can't set fields of boot image " + : "Can't set fields of "; + runtime->AbortTransactionAndThrowAbortError(self, base_msg + obj->PrettyTypeOf()); + return true; + } + return false; + } + + static inline bool WriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) + REQUIRES_SHARED(Locks::mutator_lock_) { + Runtime* runtime = Runtime::Current(); + if (runtime->GetTransaction()->WriteValueConstraint(value)) { + DCHECK(value != nullptr); + std::string msg = value->IsClass() + ? "Can't store reference to class " + value->AsClass()->PrettyDescriptor() + : "Can't store reference to instance of " + value->GetClass()->PrettyDescriptor(); + runtime->AbortTransactionAndThrowAbortError(self, msg); + return true; + } + return false; + } + + static inline bool AllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (klass->IsFinalizable()) { + Runtime::Current()->AbortTransactionF(self, + "Allocating finalizable object in transaction: %s", + klass->PrettyDescriptor().c_str()); + return true; + } + return false; + } +}; + +} // namespace interpreter +} // namespace art + +#endif // ART_RUNTIME_INTERPRETER_ACTIVE_TRANSACTION_CHECKER_H_ diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 737e80b8ca..ded500fd78 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -210,22 +210,6 @@ void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shadow_frame) UNREACHABLE(); } -void AbortTransactionF(Thread* self, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - AbortTransactionV(self, fmt, args); - va_end(args); -} - -void AbortTransactionV(Thread* self, const char* fmt, va_list args) { - CHECK(Runtime::Current()->IsActiveTransaction()); - // Constructs abort message. - std::string abort_msg; - android::base::StringAppendV(&abort_msg, fmt, args); - // Throws an exception so we can abort the transaction and rollback every change. - Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg); -} - // START DECLARATIONS : // // These additional declarations are required because clang complains diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index e6548ef82b..d2238d1d23 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -67,6 +67,32 @@ namespace art HIDDEN { namespace interpreter { +// We declare the helper class for transaction checks here but it shall be defined +// only when compiling the transactional interpreter. +class ActiveTransactionChecker; + +// Define the helper class that does not do any transaction checks. +class InactiveTransactionChecker { + public: + ALWAYS_INLINE static bool WriteConstraint([[maybe_unused]] Thread* self, + [[maybe_unused]] ObjPtr<mirror::Object> obj) + REQUIRES_SHARED(Locks::mutator_lock_) { + return false; + } + + ALWAYS_INLINE static bool WriteValueConstraint([[maybe_unused]] Thread* self, + [[maybe_unused]] ObjPtr<mirror::Object> value) + REQUIRES_SHARED(Locks::mutator_lock_) { + return false; + } + + ALWAYS_INLINE static bool AllocationConstraint([[maybe_unused]] Thread* self, + [[maybe_unused]] ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + return false; + } +}; + void ThrowNullPointerExceptionFromInterpreter() REQUIRES_SHARED(Locks::mutator_lock_); @@ -111,13 +137,6 @@ static inline bool DoMonitorCheckOnExit(Thread* self, ShadowFrame* frame) return true; } -void AbortTransactionF(Thread* self, const char* fmt, ...) - __attribute__((__format__(__printf__, 2, 3))) - REQUIRES_SHARED(Locks::mutator_lock_); - -void AbortTransactionV(Thread* self, const char* fmt, va_list args) - REQUIRES_SHARED(Locks::mutator_lock_); - void RecordArrayElementsInTransaction(ObjPtr<mirror::Array> array, int32_t count) REQUIRES_SHARED(Locks::mutator_lock_); @@ -446,34 +465,6 @@ ALWAYS_INLINE bool DoFieldGet(Thread* self, return true; } -static inline bool CheckWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) - REQUIRES_SHARED(Locks::mutator_lock_) { - Runtime* runtime = Runtime::Current(); - if (runtime->GetTransaction()->WriteConstraint(obj)) { - DCHECK(runtime->GetHeap()->ObjectIsInBootImageSpace(obj) || obj->IsClass()); - const char* base_msg = runtime->GetHeap()->ObjectIsInBootImageSpace(obj) - ? "Can't set fields of boot image " - : "Can't set fields of "; - runtime->AbortTransactionAndThrowAbortError(self, base_msg + obj->PrettyTypeOf()); - return false; - } - return true; -} - -static inline bool CheckWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) - REQUIRES_SHARED(Locks::mutator_lock_) { - Runtime* runtime = Runtime::Current(); - if (runtime->GetTransaction()->WriteValueConstraint(value)) { - DCHECK(value != nullptr); - std::string msg = value->IsClass() - ? "Can't store reference to class " + value->AsClass()->PrettyDescriptor() - : "Can't store reference to instance of " + value->GetClass()->PrettyDescriptor(); - runtime->AbortTransactionAndThrowAbortError(self, msg); - return false; - } - return true; -} - // Handles iput-XXX and sput-XXX instructions. // Returns true on success, otherwise throws an exception and returns false. template<FindFieldType find_type, Primitive::Type field_type, bool transaction_active> @@ -523,15 +514,16 @@ ALWAYS_INLINE bool DoFieldPut(Thread* self, obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); } } - if (transaction_active && !CheckWriteConstraint(self, obj)) { + using TransactionChecker = typename std::conditional_t< + transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>; + if (TransactionChecker::WriteConstraint(self, obj)) { return false; } JValue value = GetFieldValue<field_type>(shadow_frame, vregA); - if (transaction_active && - field_type == Primitive::kPrimNot && - !CheckWriteValueConstraint(self, value.GetL())) { + if (field_type == Primitive::kPrimNot && + TransactionChecker::WriteValueConstraint(self, value.GetL())) { return false; } if (should_report) { diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h index 8b23715ba6..0c50238e48 100644 --- a/runtime/interpreter/interpreter_switch_impl-inl.h +++ b/runtime/interpreter/interpreter_switch_impl-inl.h @@ -53,6 +53,9 @@ namespace interpreter { template<bool transaction_active, Instruction::Format kFormat> class InstructionHandler { public: + using TransactionChecker = typename std::conditional_t< + transaction_active, ActiveTransactionChecker, InactiveTransactionChecker>; + #define HANDLER_ATTRIBUTES ALWAYS_INLINE FLATTEN WARN_UNUSED REQUIRES_SHARED(Locks::mutator_lock_) HANDLER_ATTRIBUTES bool CheckTransactionAbort() { @@ -332,7 +335,7 @@ class InstructionHandler { if (UNLIKELY(!array->CheckIsValidIndex(index))) { return false; // Pending exception. } - if (transaction_active && !CheckWriteConstraint(Self(), array)) { + if (TransactionChecker::WriteConstraint(Self(), array)) { return false; } array->template SetWithoutChecks<transaction_active>(index, value); @@ -669,10 +672,7 @@ class InstructionHandler { if (LIKELY(c != nullptr)) { // Don't allow finalizable objects to be allocated during a transaction since these can't // be finalized without a started runtime. - if (transaction_active && c->IsFinalizable()) { - AbortTransactionF(Self(), - "Allocating finalizable object in transaction: %s", - c->PrettyDescriptor().c_str()); + if (TransactionChecker::AllocationConstraint(Self(), c)) { return false; // Pending exception. } gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); @@ -898,8 +898,8 @@ class InstructionHandler { ObjPtr<mirror::Object> val = GetVRegReference(A()); ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>(); if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) { - if (transaction_active && - (!CheckWriteConstraint(Self(), array) || !CheckWriteValueConstraint(Self(), val))) { + if (TransactionChecker::WriteConstraint(Self(), array) || + TransactionChecker::WriteValueConstraint(Self(), val)) { return false; } array->SetWithoutChecks<transaction_active>(index, val); diff --git a/runtime/interpreter/interpreter_switch_impl1.cc b/runtime/interpreter/interpreter_switch_impl1.cc index b9033d926e..e56c58c2bc 100644 --- a/runtime/interpreter/interpreter_switch_impl1.cc +++ b/runtime/interpreter/interpreter_switch_impl1.cc @@ -19,6 +19,8 @@ #include "interpreter_switch_impl-inl.h" +#include "active_transaction_checker.h" + namespace art HIDDEN { namespace interpreter { diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 7309391b32..2bdb5900b8 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -78,9 +78,10 @@ static void AbortTransactionOrFail(Thread* self, const char* fmt, ...) static void AbortTransactionOrFail(Thread* self, const char* fmt, ...) { va_list args; - if (Runtime::Current()->IsActiveTransaction()) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { va_start(args, fmt); - AbortTransactionV(self, fmt, args); + runtime->AbortTransactionV(self, fmt, args); va_end(args); } else { va_start(args, fmt); @@ -176,7 +177,7 @@ static void CheckExceptionGenerateClassNotFound(Thread* self) // initialize a class differently than when executing at run time. // If we're not aborting the transaction yet, abort now. b/183691501 if (!runtime->IsTransactionAborted()) { - AbortTransactionF(self, "ClassNotFoundException"); + runtime->AbortTransactionF(self, "ClassNotFoundException"); } } else { // If not in a transaction, it cannot be the transaction abort exception. Wrap it. @@ -301,12 +302,11 @@ void UnstartedRuntime::UnstartedClassNewInstance( } // If we're in a transaction, class must not be finalizable (it or a superclass has a finalizer). - if (Runtime::Current()->IsActiveTransaction()) { - if (h_klass->IsFinalizable()) { - AbortTransactionF(self, "Class for newInstance is finalizable: '%s'", - h_klass->PrettyClass().c_str()); - return; - } + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction() && + runtime->GetClassLinker()->TransactionAllocationConstraint(self, h_klass.Get())) { + DCHECK(self->IsExceptionPending()); + return; } // There are two situations in which we'll abort this run. @@ -314,7 +314,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( // 2) If we can't find the default constructor. We'll postpone the exception to runtime. // Note that 2) could likely be handled here, but for safety abort the transaction. bool ok = false; - auto* cl = Runtime::Current()->GetClassLinker(); + auto* cl = runtime->GetClassLinker(); if (cl->EnsureInitialized(self, h_klass, true, true)) { ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize()); if (cons != nullptr && ShouldDenyAccessToMember(cons, shadow_frame)) { @@ -771,7 +771,7 @@ void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass( // If we're not aborting the transaction yet, abort now. b/183691501 // See CheckExceptionGenerateClassNotFound() for more detailed explanation. if (!runtime->IsTransactionAborted()) { - AbortTransactionF(self, "ClassNotFoundException"); + runtime->AbortTransactionF(self, "ClassNotFoundException"); } } else { // If not in a transaction, it cannot be the transaction abort exception. Clear it. @@ -855,7 +855,9 @@ void UnstartedRuntime::UnstartedSystemArraycopy(Thread* self, return; } - if (Runtime::Current()->IsActiveTransaction() && !CheckWriteConstraint(self, dst_obj)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction() && + runtime->GetClassLinker()->TransactionWriteConstraint(self, dst_obj)) { DCHECK(self->IsExceptionPending()); return; } @@ -1589,8 +1591,9 @@ void UnstartedRuntime::UnstartedJdkUnsafeCompareAndSwapLong( int64_t newValue = shadow_frame->GetVRegLong(arg_offset + 6); bool success; // Check whether we're in a transaction, call accordingly. - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj)) { DCHECK(self->IsExceptionPending()); return; } @@ -1635,8 +1638,10 @@ void UnstartedRuntime::UnstartedJdkUnsafeCompareAndSwapObject( } bool success; // Check whether we're in a transaction, call accordingly. - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj) || !CheckWriteValueConstraint(self, new_value)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj) || + runtime->GetClassLinker()->TransactionWriteValueConstraint(self, new_value)) { DCHECK(self->IsExceptionPending()); return; } @@ -1682,8 +1687,10 @@ void UnstartedRuntime::UnstartedJdkUnsafePutReferenceVolatile(Thread* self, } int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); mirror::Object* value = shadow_frame->GetVRegReference(arg_offset + 4); - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj) || !CheckWriteValueConstraint(self, value)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj) || + runtime->GetClassLinker()->TransactionWriteValueConstraint(self, value)) { DCHECK(self->IsExceptionPending()); return; } @@ -1707,8 +1714,10 @@ void UnstartedRuntime::UnstartedJdkUnsafePutOrderedObject(Thread* self, int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2); mirror::Object* new_value = shadow_frame->GetVRegReference(arg_offset + 4); std::atomic_thread_fence(std::memory_order_release); - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj) || !CheckWriteValueConstraint(self, new_value)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj) || + runtime->GetClassLinker()->TransactionWriteValueConstraint(self, new_value)) { DCHECK(self->IsExceptionPending()); return; } @@ -2154,8 +2163,9 @@ void UnstartedRuntime::UnstartedJNIJdkUnsafeCompareAndSwapInt( jint expectedValue = args[3]; jint newValue = args[4]; bool success; - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj)) { DCHECK(self->IsExceptionPending()); return; } @@ -2211,8 +2221,10 @@ void UnstartedRuntime::UnstartedJNIJdkUnsafePutReference(Thread* self, } jlong offset = (static_cast<uint64_t>(args[2]) << 32) | args[1]; ObjPtr<mirror::Object> new_value = reinterpret_cast32<mirror::Object*>(args[3]); - if (Runtime::Current()->IsActiveTransaction()) { - if (!CheckWriteConstraint(self, obj) || !CheckWriteValueConstraint(self, new_value)) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + if (runtime->GetClassLinker()->TransactionWriteConstraint(self, obj) || + runtime->GetClassLinker()->TransactionWriteValueConstraint(self, new_value)) { DCHECK(self->IsExceptionPending()); return; } @@ -2420,12 +2432,16 @@ void UnstartedRuntime::Jni(Thread* self, ArtMethod* method, mirror::Object* rece // Clear out the result in case it's not zeroed out. result->SetL(nullptr); (*iter->second)(self, method, receiver, args, result); - } else if (Runtime::Current()->IsActiveTransaction()) { - AbortTransactionF(self, "Attempt to invoke native method in non-started runtime: %s", - ArtMethod::PrettyMethod(method).c_str()); } else { - LOG(FATAL) << "Calling native method " << ArtMethod::PrettyMethod(method) << " in an unstarted " - "non-transactional runtime"; + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + runtime->AbortTransactionF(self, + "Attempt to invoke native method in non-started runtime: %s", + ArtMethod::PrettyMethod(method).c_str()); + } else { + LOG(FATAL) << "Calling native method " << ArtMethod::PrettyMethod(method) + << " in an unstarted non-transactional runtime"; + } } } diff --git a/runtime/oat/aot_class_linker.cc b/runtime/oat/aot_class_linker.cc index 6640214dc0..6d6887595c 100644 --- a/runtime/oat/aot_class_linker.cc +++ b/runtime/oat/aot_class_linker.cc @@ -21,6 +21,7 @@ #include "dex/class_reference.h" #include "gc/heap.h" #include "handle_scope-inl.h" +#include "interpreter/active_transaction_checker.h" #include "mirror/class-inl.h" #include "runtime.h" #include "verifier/verifier_enums.h" @@ -228,4 +229,21 @@ void AotClassLinker::SetEnablePublicSdkChecks(bool enabled) { } } +bool AotClassLinker::TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) const { + DCHECK(Runtime::Current()->IsActiveTransaction()); + return interpreter::ActiveTransactionChecker::WriteConstraint(self, obj); +} + +bool AotClassLinker::TransactionWriteValueConstraint( + Thread* self, ObjPtr<mirror::Object> value) const { + DCHECK(Runtime::Current()->IsActiveTransaction()); + return interpreter::ActiveTransactionChecker::WriteValueConstraint(self, value); +} + +bool AotClassLinker::TransactionAllocationConstraint(Thread* self, + ObjPtr<mirror::Class> klass) const { + DCHECK(Runtime::Current()->IsActiveTransaction()); + return interpreter::ActiveTransactionChecker::AllocationConstraint(self, klass); +} + } // namespace art diff --git a/runtime/oat/aot_class_linker.h b/runtime/oat/aot_class_linker.h index 492674ea10..4396a50efd 100644 --- a/runtime/oat/aot_class_linker.h +++ b/runtime/oat/aot_class_linker.h @@ -48,6 +48,14 @@ class AotClassLinker : public ClassLinker { bool DenyAccessBasedOnPublicSdk([[maybe_unused]] const char* type_descriptor) const override; void SetEnablePublicSdkChecks(bool enabled) override; + // Transaction constraint checks for AOT compilation. + bool TransactionWriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) const override + REQUIRES_SHARED(Locks::mutator_lock_); + bool TransactionWriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) const override + REQUIRES_SHARED(Locks::mutator_lock_); + bool TransactionAllocationConstraint(Thread* self, ObjPtr<mirror::Class> klass) const override + REQUIRES_SHARED(Locks::mutator_lock_); + protected: // Overridden version of PerformClassVerification allows skipping verification if the class was // previously verified but unloaded. diff --git a/runtime/runtime.cc b/runtime/runtime.cc index df66c5e175..3b27fb6faa 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -3011,6 +3011,22 @@ void Runtime::ThrowTransactionAbortError(Thread* self) { GetTransaction()->ThrowAbortError(self, nullptr); } +void Runtime::AbortTransactionF(Thread* self, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + AbortTransactionV(self, fmt, args); + va_end(args); +} + +void Runtime::AbortTransactionV(Thread* self, const char* fmt, va_list args) { + CHECK(IsActiveTransaction()); + // Constructs abort message. + std::string abort_msg; + android::base::StringAppendV(&abort_msg, fmt, args); + // Throws an exception so we can abort the transaction and rollback every change. + AbortTransactionAndThrowAbortError(self, abort_msg); +} + void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value, diff --git a/runtime/runtime.h b/runtime/runtime.h index 1a1c28bff5..a48bb55755 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -646,6 +646,13 @@ class Runtime { void ThrowTransactionAbortError(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + void AbortTransactionF(Thread* self, const char* fmt, ...) + __attribute__((__format__(__printf__, 3, 4))) + REQUIRES_SHARED(Locks::mutator_lock_); + + void AbortTransactionV(Thread* self, const char* fmt, va_list args) + REQUIRES_SHARED(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 b796df620e..d320c4d940 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -141,16 +141,15 @@ bool Transaction::WriteValueConstraint(ObjPtr<mirror::Object> value) const { if (value == nullptr) { return false; // We can always store null values. } - gc::Heap* heap = Runtime::Current()->GetHeap(); if (IsStrict()) { // TODO: Should we restrict writes the same way as for boot image extension? return false; - } else if (heap->GetBootImageSpaces().empty()) { + } else if (heap_->GetBootImageSpaces().empty()) { return false; // No constraints for boot image. } else { // Boot image extension. ObjPtr<mirror::Class> klass = value->IsClass() ? value->AsClass() : value->GetClass(); - return !AotClassLinker::CanReferenceInBootImageExtensionOrAppImage(klass, heap); + return !AotClassLinker::CanReferenceInBootImageExtensionOrAppImage(klass, heap_); } } |