summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Marko <vmarko@google.com>2024-05-02 10:56:42 +0000
committerVladimĂ­r Marko <vmarko@google.com>2024-05-03 03:44:33 +0000
commitdb15ea652d13811e236e1f12c88807ebfed05da9 (patch)
tree85d01be01468cd684a01dc0ee2b349867f6f2d6a
parentf91d21c0b0e4a03102eaa6cfae0f500f8139bc40 (diff)
downloadart-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.cc21
-rw-r--r--runtime/class_linker.h9
-rw-r--r--runtime/interpreter/active_transaction_checker.h74
-rw-r--r--runtime/interpreter/interpreter_common.cc16
-rw-r--r--runtime/interpreter/interpreter_common.h70
-rw-r--r--runtime/interpreter/interpreter_switch_impl-inl.h14
-rw-r--r--runtime/interpreter/interpreter_switch_impl1.cc2
-rw-r--r--runtime/interpreter/unstarted_runtime.cc74
-rw-r--r--runtime/oat/aot_class_linker.cc18
-rw-r--r--runtime/oat/aot_class_linker.h8
-rw-r--r--runtime/runtime.cc16
-rw-r--r--runtime/runtime.h7
-rw-r--r--runtime/transaction.cc5
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_);
}
}