Fix Transaction constraint validation...

... for boot image extensions. Add WriteConstraint checks
to APUT instructions and add necessary WriteConstraint and
WriteValueConstraint checks to UnstartedRuntime.

For strict transactions (app compilation), prevent writing
to boot image objects. However, more work is required for
this use case as the UnstartedRuntime needs a review for
missing ReadConstraint checks and the WriteValueConstraint
may need to be more restrictive.

While the transaction_test is improved to test Transaction
constraints more thoroughly, no regression tests are
provided for the previously missing checks. Such tests are
difficult to write as they would require compilation of
a custom boot image.

Test: Manual; include java.lang.Locale[] in primary boot
      image by patching CompilerDriver::LoadImageClasses(),
          +  if (GetCompilerOptions().IsBootImage()) {
          +    image_classes->insert("[Ljava/util/Locale;");
          +  }
      , and build. This previously aborted in ImageWriter:
          Image object without assigned bin slot: \
          java.util.concurrent.ConcurrentHashMap$Node
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 119800099
Bug: 147596904
Change-Id: Ibfe1b24b10dbd982b4e4ae4d98289e587a842812
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 46bbea3..6756c7b 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -43,9 +43,9 @@
     : log_lock_("transaction log lock", kTransactionLogLock),
       aborted_(false),
       rolling_back_(false),
-      heap_(strict ? nullptr : Runtime::Current()->GetHeap()),
+      heap_(Runtime::Current()->GetHeap()),
+      strict_(strict),
       root_(root) {
-  DCHECK_EQ(strict, IsStrict());
   DCHECK(Runtime::Current()->IsAotCompiler());
 }
 
@@ -117,16 +117,20 @@
   return abort_message_;
 }
 
-bool Transaction::WriteConstraint(Thread* self, ObjPtr<mirror::Object> obj, ArtField* field) {
+bool Transaction::WriteConstraint(Thread* self, ObjPtr<mirror::Object> obj) {
+  DCHECK(obj != nullptr);
   MutexLock mu(self, log_lock_);
-  if (IsStrict()) {
-    return field->IsStatic() &&  // no constraint instance updating
-           obj != root_;  // modifying other classes' static field, fail
-  } else {
-    // For boot image extension, prevent changes in boot image.
-    // For boot image there are no boot image spaces and this returns false.
-    return heap_->ObjectIsInBootImageSpace(obj);
+
+  // Prevent changes in boot image spaces for app or boot image extension.
+  // For boot image there are no boot image spaces and this condition evaluates to false.
+  if (heap_->ObjectIsInBootImageSpace(obj)) {
+    return true;
   }
+
+  // For apps, also prevent writing to other classes.
+  return IsStrict() &&
+         obj->IsClass() &&  // no constraint updating instances or arrays
+         obj != root_;  // modifying other classes' static field, fail
 }
 
 bool Transaction::WriteValueConstraint(Thread* self, ObjPtr<mirror::Object> value) {
@@ -147,8 +151,9 @@
   }
 }
 
-bool Transaction::ReadConstraint(Thread* self, ObjPtr<mirror::Object> obj, ArtField* field) {
-  DCHECK(field->IsStatic());
+bool Transaction::ReadConstraint(Thread* self, ObjPtr<mirror::Object> obj) {
+  // Read constraints are checked only for static field reads as there are
+  // no constraints on reading instance fields and array elements.
   DCHECK(obj->IsClass());
   MutexLock mu(self, log_lock_);
   if (IsStrict()) {