Home | History | Annotate | Download | only in runtime
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "transaction.h"
     18 
     19 #include <android-base/logging.h>
     20 
     21 #include "base/stl_util.h"
     22 #include "gc/accounting/card_table-inl.h"
     23 #include "gc_root-inl.h"
     24 #include "intern_table.h"
     25 #include "mirror/class-inl.h"
     26 #include "mirror/dex_cache-inl.h"
     27 #include "mirror/object-inl.h"
     28 #include "mirror/object_array-inl.h"
     29 
     30 #include <list>
     31 
     32 namespace art {
     33 
     34 // TODO: remove (only used for debugging purpose).
     35 static constexpr bool kEnableTransactionStats = false;
     36 
     37 Transaction::Transaction()
     38   : log_lock_("transaction log lock", kTransactionLogLock),
     39     aborted_(false),
     40     rolling_back_(false),
     41     strict_(false) {
     42   CHECK(Runtime::Current()->IsAotCompiler());
     43 }
     44 
     45 Transaction::Transaction(bool strict, mirror::Class* root) : Transaction() {
     46   strict_ = strict;
     47   root_ = root;
     48 }
     49 
     50 Transaction::~Transaction() {
     51   if (kEnableTransactionStats) {
     52     MutexLock mu(Thread::Current(), log_lock_);
     53     size_t objects_count = object_logs_.size();
     54     size_t field_values_count = 0;
     55     for (const auto& it : object_logs_) {
     56       field_values_count += it.second.Size();
     57     }
     58     size_t array_count = array_logs_.size();
     59     size_t array_values_count = 0;
     60     for (const auto& it : array_logs_) {
     61       array_values_count += it.second.Size();
     62     }
     63     size_t intern_string_count = intern_string_logs_.size();
     64     size_t resolve_string_count = resolve_string_logs_.size();
     65     LOG(INFO) << "Transaction::~Transaction"
     66               << ": objects_count=" << objects_count
     67               << ", field_values_count=" << field_values_count
     68               << ", array_count=" << array_count
     69               << ", array_values_count=" << array_values_count
     70               << ", intern_string_count=" << intern_string_count
     71               << ", resolve_string_count=" << resolve_string_count;
     72   }
     73 }
     74 
     75 void Transaction::Abort(const std::string& abort_message) {
     76   MutexLock mu(Thread::Current(), log_lock_);
     77   // We may abort more than once if the exception thrown at the time of the
     78   // previous abort has been caught during execution of a class initializer.
     79   // We just keep the message of the first abort because it will cause the
     80   // transaction to be rolled back anyway.
     81   if (!aborted_) {
     82     aborted_ = true;
     83     abort_message_ = abort_message;
     84   }
     85 }
     86 
     87 void Transaction::ThrowAbortError(Thread* self, const std::string* abort_message) {
     88   const bool rethrow = (abort_message == nullptr);
     89   if (kIsDebugBuild && rethrow) {
     90     CHECK(IsAborted()) << "Rethrow " << Transaction::kAbortExceptionDescriptor
     91                        << " while transaction is not aborted";
     92   }
     93   if (rethrow) {
     94     // Rethrow an exception with the earlier abort message stored in the transaction.
     95     self->ThrowNewWrappedException(Transaction::kAbortExceptionSignature,
     96                                    GetAbortMessage().c_str());
     97   } else {
     98     // Throw an exception with the given abort message.
     99     self->ThrowNewWrappedException(Transaction::kAbortExceptionSignature,
    100                                    abort_message->c_str());
    101   }
    102 }
    103 
    104 bool Transaction::IsAborted() {
    105   MutexLock mu(Thread::Current(), log_lock_);
    106   return aborted_;
    107 }
    108 
    109 bool Transaction::IsRollingBack() {
    110   return rolling_back_;
    111 }
    112 
    113 bool Transaction::IsStrict() {
    114   MutexLock mu(Thread::Current(), log_lock_);
    115   return strict_;
    116 }
    117 
    118 const std::string& Transaction::GetAbortMessage() {
    119   MutexLock mu(Thread::Current(), log_lock_);
    120   return abort_message_;
    121 }
    122 
    123 bool Transaction::WriteConstraint(mirror::Object* obj, ArtField* field) {
    124   MutexLock mu(Thread::Current(), log_lock_);
    125   if (strict_  // no constraint for boot image
    126       && field->IsStatic()  // no constraint instance updating
    127       && obj != root_) {  // modifying other classes' static field, fail
    128     return true;
    129   }
    130   return false;
    131 }
    132 
    133 bool Transaction::ReadConstraint(mirror::Object* obj, ArtField* field) {
    134   DCHECK(field->IsStatic());
    135   DCHECK(obj->IsClass());
    136   MutexLock mu(Thread::Current(), log_lock_);
    137   if (!strict_ ||   // no constraint for boot image
    138       obj == root_) {  // self-updating, pass
    139     return false;
    140   }
    141   return true;
    142 }
    143 
    144 void Transaction::RecordWriteFieldBoolean(mirror::Object* obj,
    145                                           MemberOffset field_offset,
    146                                           uint8_t value,
    147                                           bool is_volatile) {
    148   DCHECK(obj != nullptr);
    149   MutexLock mu(Thread::Current(), log_lock_);
    150   ObjectLog& object_log = object_logs_[obj];
    151   object_log.LogBooleanValue(field_offset, value, is_volatile);
    152 }
    153 
    154 void Transaction::RecordWriteFieldByte(mirror::Object* obj,
    155                                        MemberOffset field_offset,
    156                                        int8_t value,
    157                                        bool is_volatile) {
    158   DCHECK(obj != nullptr);
    159   MutexLock mu(Thread::Current(), log_lock_);
    160   ObjectLog& object_log = object_logs_[obj];
    161   object_log.LogByteValue(field_offset, value, is_volatile);
    162 }
    163 
    164 void Transaction::RecordWriteFieldChar(mirror::Object* obj,
    165                                        MemberOffset field_offset,
    166                                        uint16_t value,
    167                                        bool is_volatile) {
    168   DCHECK(obj != nullptr);
    169   MutexLock mu(Thread::Current(), log_lock_);
    170   ObjectLog& object_log = object_logs_[obj];
    171   object_log.LogCharValue(field_offset, value, is_volatile);
    172 }
    173 
    174 
    175 void Transaction::RecordWriteFieldShort(mirror::Object* obj,
    176                                         MemberOffset field_offset,
    177                                         int16_t value,
    178                                         bool is_volatile) {
    179   DCHECK(obj != nullptr);
    180   MutexLock mu(Thread::Current(), log_lock_);
    181   ObjectLog& object_log = object_logs_[obj];
    182   object_log.LogShortValue(field_offset, value, is_volatile);
    183 }
    184 
    185 
    186 void Transaction::RecordWriteField32(mirror::Object* obj,
    187                                      MemberOffset field_offset,
    188                                      uint32_t value,
    189                                      bool is_volatile) {
    190   DCHECK(obj != nullptr);
    191   MutexLock mu(Thread::Current(), log_lock_);
    192   ObjectLog& object_log = object_logs_[obj];
    193   object_log.Log32BitsValue(field_offset, value, is_volatile);
    194 }
    195 
    196 void Transaction::RecordWriteField64(mirror::Object* obj,
    197                                      MemberOffset field_offset,
    198                                      uint64_t value,
    199                                      bool is_volatile) {
    200   DCHECK(obj != nullptr);
    201   MutexLock mu(Thread::Current(), log_lock_);
    202   ObjectLog& object_log = object_logs_[obj];
    203   object_log.Log64BitsValue(field_offset, value, is_volatile);
    204 }
    205 
    206 void Transaction::RecordWriteFieldReference(mirror::Object* obj,
    207                                             MemberOffset field_offset,
    208                                             mirror::Object* value,
    209                                             bool is_volatile) {
    210   DCHECK(obj != nullptr);
    211   MutexLock mu(Thread::Current(), log_lock_);
    212   ObjectLog& object_log = object_logs_[obj];
    213   object_log.LogReferenceValue(field_offset, value, is_volatile);
    214 }
    215 
    216 void Transaction::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) {
    217   DCHECK(array != nullptr);
    218   DCHECK(array->IsArrayInstance());
    219   DCHECK(!array->IsObjectArray());
    220   MutexLock mu(Thread::Current(), log_lock_);
    221   auto it = array_logs_.find(array);
    222   if (it == array_logs_.end()) {
    223     ArrayLog log;
    224     it = array_logs_.emplace(array, std::move(log)).first;
    225   }
    226   it->second.LogValue(index, value);
    227 }
    228 
    229 void Transaction::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache,
    230                                       dex::StringIndex string_idx) {
    231   DCHECK(dex_cache != nullptr);
    232   DCHECK_LT(string_idx.index_, dex_cache->GetDexFile()->NumStringIds());
    233   MutexLock mu(Thread::Current(), log_lock_);
    234   resolve_string_logs_.emplace_back(dex_cache, string_idx);
    235 }
    236 
    237 void Transaction::RecordStrongStringInsertion(ObjPtr<mirror::String> s) {
    238   InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kInsert);
    239   LogInternedString(std::move(log));
    240 }
    241 
    242 void Transaction::RecordWeakStringInsertion(ObjPtr<mirror::String> s) {
    243   InternStringLog log(s, InternStringLog::kWeakString, InternStringLog::kInsert);
    244   LogInternedString(std::move(log));
    245 }
    246 
    247 void Transaction::RecordStrongStringRemoval(ObjPtr<mirror::String> s) {
    248   InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kRemove);
    249   LogInternedString(std::move(log));
    250 }
    251 
    252 void Transaction::RecordWeakStringRemoval(ObjPtr<mirror::String> s) {
    253   InternStringLog log(s, InternStringLog::kWeakString, InternStringLog::kRemove);
    254   LogInternedString(std::move(log));
    255 }
    256 
    257 void Transaction::LogInternedString(InternStringLog&& log) {
    258   Locks::intern_table_lock_->AssertExclusiveHeld(Thread::Current());
    259   MutexLock mu(Thread::Current(), log_lock_);
    260   intern_string_logs_.push_front(std::move(log));
    261 }
    262 
    263 void Transaction::Rollback() {
    264   Thread* self = Thread::Current();
    265   self->AssertNoPendingException();
    266   MutexLock mu1(self, *Locks::intern_table_lock_);
    267   MutexLock mu2(self, log_lock_);
    268   rolling_back_ = true;
    269   CHECK(!Runtime::Current()->IsActiveTransaction());
    270   UndoObjectModifications();
    271   UndoArrayModifications();
    272   UndoInternStringTableModifications();
    273   UndoResolveStringModifications();
    274   rolling_back_ = false;
    275 }
    276 
    277 void Transaction::UndoObjectModifications() {
    278   // TODO we may not need to restore objects allocated during this transaction. Or we could directly
    279   // remove them from the heap.
    280   for (const auto& it : object_logs_) {
    281     it.second.Undo(it.first);
    282   }
    283   object_logs_.clear();
    284 }
    285 
    286 void Transaction::UndoArrayModifications() {
    287   // TODO we may not need to restore array allocated during this transaction. Or we could directly
    288   // remove them from the heap.
    289   for (const auto& it : array_logs_) {
    290     it.second.Undo(it.first);
    291   }
    292   array_logs_.clear();
    293 }
    294 
    295 void Transaction::UndoInternStringTableModifications() {
    296   InternTable* const intern_table = Runtime::Current()->GetInternTable();
    297   // We want to undo each operation from the most recent to the oldest. List has been filled so the
    298   // most recent operation is at list begin so just have to iterate over it.
    299   for (const InternStringLog& string_log : intern_string_logs_) {
    300     string_log.Undo(intern_table);
    301   }
    302   intern_string_logs_.clear();
    303 }
    304 
    305 void Transaction::UndoResolveStringModifications() {
    306   for (ResolveStringLog& string_log : resolve_string_logs_) {
    307     string_log.Undo();
    308   }
    309   resolve_string_logs_.clear();
    310 }
    311 
    312 void Transaction::VisitRoots(RootVisitor* visitor) {
    313   MutexLock mu(Thread::Current(), log_lock_);
    314   visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&root_), RootInfo(kRootUnknown));
    315   VisitObjectLogs(visitor);
    316   VisitArrayLogs(visitor);
    317   VisitInternStringLogs(visitor);
    318   VisitResolveStringLogs(visitor);
    319 }
    320 
    321 void Transaction::VisitObjectLogs(RootVisitor* visitor) {
    322   // List of moving roots.
    323   typedef std::pair<mirror::Object*, mirror::Object*> ObjectPair;
    324   std::list<ObjectPair> moving_roots;
    325 
    326   // Visit roots.
    327   for (auto& it : object_logs_) {
    328     it.second.VisitRoots(visitor);
    329     mirror::Object* old_root = it.first;
    330     mirror::Object* new_root = old_root;
    331     visitor->VisitRoot(&new_root, RootInfo(kRootUnknown));
    332     if (new_root != old_root) {
    333       moving_roots.push_back(std::make_pair(old_root, new_root));
    334     }
    335   }
    336 
    337   // Update object logs with moving roots.
    338   for (const ObjectPair& pair : moving_roots) {
    339     mirror::Object* old_root = pair.first;
    340     mirror::Object* new_root = pair.second;
    341     auto old_root_it = object_logs_.find(old_root);
    342     CHECK(old_root_it != object_logs_.end());
    343     CHECK(object_logs_.find(new_root) == object_logs_.end());
    344     object_logs_.emplace(new_root, std::move(old_root_it->second));
    345     object_logs_.erase(old_root_it);
    346   }
    347 }
    348 
    349 void Transaction::VisitArrayLogs(RootVisitor* visitor) {
    350   // List of moving roots.
    351   typedef std::pair<mirror::Array*, mirror::Array*> ArrayPair;
    352   std::list<ArrayPair> moving_roots;
    353 
    354   for (auto& it : array_logs_) {
    355     mirror::Array* old_root = it.first;
    356     CHECK(!old_root->IsObjectArray());
    357     mirror::Array* new_root = old_root;
    358     visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&new_root), RootInfo(kRootUnknown));
    359     if (new_root != old_root) {
    360       moving_roots.push_back(std::make_pair(old_root, new_root));
    361     }
    362   }
    363 
    364   // Update array logs with moving roots.
    365   for (const ArrayPair& pair : moving_roots) {
    366     mirror::Array* old_root = pair.first;
    367     mirror::Array* new_root = pair.second;
    368     auto old_root_it = array_logs_.find(old_root);
    369     CHECK(old_root_it != array_logs_.end());
    370     CHECK(array_logs_.find(new_root) == array_logs_.end());
    371     array_logs_.emplace(new_root, std::move(old_root_it->second));
    372     array_logs_.erase(old_root_it);
    373   }
    374 }
    375 
    376 void Transaction::VisitInternStringLogs(RootVisitor* visitor) {
    377   for (InternStringLog& log : intern_string_logs_) {
    378     log.VisitRoots(visitor);
    379   }
    380 }
    381 
    382 void Transaction::VisitResolveStringLogs(RootVisitor* visitor) {
    383   for (ResolveStringLog& log : resolve_string_logs_) {
    384     log.VisitRoots(visitor);
    385   }
    386 }
    387 
    388 void Transaction::ObjectLog::LogBooleanValue(MemberOffset offset, uint8_t value, bool is_volatile) {
    389   LogValue(ObjectLog::kBoolean, offset, value, is_volatile);
    390 }
    391 
    392 void Transaction::ObjectLog::LogByteValue(MemberOffset offset, int8_t value, bool is_volatile) {
    393   LogValue(ObjectLog::kByte, offset, value, is_volatile);
    394 }
    395 
    396 void Transaction::ObjectLog::LogCharValue(MemberOffset offset, uint16_t value, bool is_volatile) {
    397   LogValue(ObjectLog::kChar, offset, value, is_volatile);
    398 }
    399 
    400 void Transaction::ObjectLog::LogShortValue(MemberOffset offset, int16_t value, bool is_volatile) {
    401   LogValue(ObjectLog::kShort, offset, value, is_volatile);
    402 }
    403 
    404 void Transaction::ObjectLog::Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile) {
    405   LogValue(ObjectLog::k32Bits, offset, value, is_volatile);
    406 }
    407 
    408 void Transaction::ObjectLog::Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile) {
    409   LogValue(ObjectLog::k64Bits, offset, value, is_volatile);
    410 }
    411 
    412 void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset,
    413                                                mirror::Object* obj,
    414                                                bool is_volatile) {
    415   LogValue(ObjectLog::kReference, offset, reinterpret_cast<uintptr_t>(obj), is_volatile);
    416 }
    417 
    418 void Transaction::ObjectLog::LogValue(ObjectLog::FieldValueKind kind,
    419                                       MemberOffset offset,
    420                                       uint64_t value,
    421                                       bool is_volatile) {
    422   auto it = field_values_.find(offset.Uint32Value());
    423   if (it == field_values_.end()) {
    424     ObjectLog::FieldValue field_value;
    425     field_value.value = value;
    426     field_value.is_volatile = is_volatile;
    427     field_value.kind = kind;
    428     field_values_.emplace(offset.Uint32Value(), std::move(field_value));
    429   }
    430 }
    431 
    432 void Transaction::ObjectLog::Undo(mirror::Object* obj) const {
    433   for (auto& it : field_values_) {
    434     // Garbage collector needs to access object's class and array's length. So we don't rollback
    435     // these values.
    436     MemberOffset field_offset(it.first);
    437     if (field_offset.Uint32Value() == mirror::Class::ClassOffset().Uint32Value()) {
    438       // Skip Object::class field.
    439       continue;
    440     }
    441     if (obj->IsArrayInstance() &&
    442         field_offset.Uint32Value() == mirror::Array::LengthOffset().Uint32Value()) {
    443       // Skip Array::length field.
    444       continue;
    445     }
    446     const FieldValue& field_value = it.second;
    447     UndoFieldWrite(obj, field_offset, field_value);
    448   }
    449 }
    450 
    451 void Transaction::ObjectLog::UndoFieldWrite(mirror::Object* obj,
    452                                             MemberOffset field_offset,
    453                                             const FieldValue& field_value) const {
    454   // TODO We may want to abort a transaction while still being in transaction mode. In this case,
    455   // we'd need to disable the check.
    456   constexpr bool kCheckTransaction = false;
    457   switch (field_value.kind) {
    458     case kBoolean:
    459       if (UNLIKELY(field_value.is_volatile)) {
    460         obj->SetFieldBooleanVolatile<false, kCheckTransaction>(
    461             field_offset,
    462             static_cast<bool>(field_value.value));
    463       } else {
    464         obj->SetFieldBoolean<false, kCheckTransaction>(
    465             field_offset,
    466             static_cast<bool>(field_value.value));
    467       }
    468       break;
    469     case kByte:
    470       if (UNLIKELY(field_value.is_volatile)) {
    471         obj->SetFieldByteVolatile<false, kCheckTransaction>(
    472             field_offset,
    473             static_cast<int8_t>(field_value.value));
    474       } else {
    475         obj->SetFieldByte<false, kCheckTransaction>(
    476             field_offset,
    477             static_cast<int8_t>(field_value.value));
    478       }
    479       break;
    480     case kChar:
    481       if (UNLIKELY(field_value.is_volatile)) {
    482         obj->SetFieldCharVolatile<false, kCheckTransaction>(
    483             field_offset,
    484             static_cast<uint16_t>(field_value.value));
    485       } else {
    486         obj->SetFieldChar<false, kCheckTransaction>(
    487             field_offset,
    488             static_cast<uint16_t>(field_value.value));
    489       }
    490       break;
    491     case kShort:
    492       if (UNLIKELY(field_value.is_volatile)) {
    493         obj->SetFieldShortVolatile<false, kCheckTransaction>(
    494             field_offset,
    495             static_cast<int16_t>(field_value.value));
    496       } else {
    497         obj->SetFieldShort<false, kCheckTransaction>(
    498             field_offset,
    499             static_cast<int16_t>(field_value.value));
    500       }
    501       break;
    502     case k32Bits:
    503       if (UNLIKELY(field_value.is_volatile)) {
    504         obj->SetField32Volatile<false, kCheckTransaction>(
    505             field_offset,
    506             static_cast<uint32_t>(field_value.value));
    507       } else {
    508         obj->SetField32<false, kCheckTransaction>(
    509             field_offset,
    510             static_cast<uint32_t>(field_value.value));
    511       }
    512       break;
    513     case k64Bits:
    514       if (UNLIKELY(field_value.is_volatile)) {
    515         obj->SetField64Volatile<false, kCheckTransaction>(field_offset, field_value.value);
    516       } else {
    517         obj->SetField64<false, kCheckTransaction>(field_offset, field_value.value);
    518       }
    519       break;
    520     case kReference:
    521       if (UNLIKELY(field_value.is_volatile)) {
    522         obj->SetFieldObjectVolatile<false, kCheckTransaction>(
    523             field_offset,
    524             reinterpret_cast<mirror::Object*>(field_value.value));
    525       } else {
    526         obj->SetFieldObject<false, kCheckTransaction>(
    527             field_offset,
    528             reinterpret_cast<mirror::Object*>(field_value.value));
    529       }
    530       break;
    531     default:
    532       LOG(FATAL) << "Unknown value kind " << static_cast<int>(field_value.kind);
    533       break;
    534   }
    535 }
    536 
    537 void Transaction::ObjectLog::VisitRoots(RootVisitor* visitor) {
    538   for (auto& it : field_values_) {
    539     FieldValue& field_value = it.second;
    540     if (field_value.kind == ObjectLog::kReference) {
    541       visitor->VisitRootIfNonNull(reinterpret_cast<mirror::Object**>(&field_value.value),
    542                                   RootInfo(kRootUnknown));
    543     }
    544   }
    545 }
    546 
    547 void Transaction::InternStringLog::Undo(InternTable* intern_table) const {
    548   DCHECK(intern_table != nullptr);
    549   switch (string_op_) {
    550     case InternStringLog::kInsert: {
    551       switch (string_kind_) {
    552         case InternStringLog::kStrongString:
    553           intern_table->RemoveStrongFromTransaction(str_.Read());
    554           break;
    555         case InternStringLog::kWeakString:
    556           intern_table->RemoveWeakFromTransaction(str_.Read());
    557           break;
    558         default:
    559           LOG(FATAL) << "Unknown interned string kind";
    560           break;
    561       }
    562       break;
    563     }
    564     case InternStringLog::kRemove: {
    565       switch (string_kind_) {
    566         case InternStringLog::kStrongString:
    567           intern_table->InsertStrongFromTransaction(str_.Read());
    568           break;
    569         case InternStringLog::kWeakString:
    570           intern_table->InsertWeakFromTransaction(str_.Read());
    571           break;
    572         default:
    573           LOG(FATAL) << "Unknown interned string kind";
    574           break;
    575       }
    576       break;
    577     }
    578     default:
    579       LOG(FATAL) << "Unknown interned string op";
    580       break;
    581   }
    582 }
    583 
    584 void Transaction::InternStringLog::VisitRoots(RootVisitor* visitor) {
    585   str_.VisitRoot(visitor, RootInfo(kRootInternedString));
    586 }
    587 
    588 void Transaction::ResolveStringLog::Undo() const {
    589   dex_cache_.Read()->ClearString(string_idx_);
    590 }
    591 
    592 Transaction::ResolveStringLog::ResolveStringLog(ObjPtr<mirror::DexCache> dex_cache,
    593                                                 dex::StringIndex string_idx)
    594     : dex_cache_(dex_cache),
    595       string_idx_(string_idx) {
    596   DCHECK(dex_cache != nullptr);
    597   DCHECK_LT(string_idx_.index_, dex_cache->GetDexFile()->NumStringIds());
    598 }
    599 
    600 void Transaction::ResolveStringLog::VisitRoots(RootVisitor* visitor) {
    601   dex_cache_.VisitRoot(visitor, RootInfo(kRootVMInternal));
    602 }
    603 
    604 Transaction::InternStringLog::InternStringLog(ObjPtr<mirror::String> s,
    605                                               StringKind kind,
    606                                               StringOp op)
    607     : str_(s),
    608       string_kind_(kind),
    609       string_op_(op) {
    610   DCHECK(s != nullptr);
    611 }
    612 
    613 void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
    614   auto it = array_values_.find(index);
    615   if (it == array_values_.end()) {
    616     array_values_.insert(std::make_pair(index, value));
    617   }
    618 }
    619 
    620 void Transaction::ArrayLog::Undo(mirror::Array* array) const {
    621   DCHECK(array != nullptr);
    622   DCHECK(array->IsArrayInstance());
    623   Primitive::Type type = array->GetClass()->GetComponentType()->GetPrimitiveType();
    624   for (auto it : array_values_) {
    625     UndoArrayWrite(array, type, it.first, it.second);
    626   }
    627 }
    628 
    629 void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array,
    630                                            Primitive::Type array_type,
    631                                            size_t index,
    632                                            uint64_t value) const {
    633   // TODO We may want to abort a transaction while still being in transaction mode. In this case,
    634   // we'd need to disable the check.
    635   constexpr bool kCheckTransaction = false;
    636   switch (array_type) {
    637     case Primitive::kPrimBoolean:
    638       array->AsBooleanArray()->SetWithoutChecks<false, kCheckTransaction>(
    639           index, static_cast<uint8_t>(value));
    640       break;
    641     case Primitive::kPrimByte:
    642       array->AsByteArray()->SetWithoutChecks<false, kCheckTransaction>(
    643           index, static_cast<int8_t>(value));
    644       break;
    645     case Primitive::kPrimChar:
    646       array->AsCharArray()->SetWithoutChecks<false, kCheckTransaction>(
    647           index, static_cast<uint16_t>(value));
    648       break;
    649     case Primitive::kPrimShort:
    650       array->AsShortArray()->SetWithoutChecks<false, kCheckTransaction>(
    651           index, static_cast<int16_t>(value));
    652       break;
    653     case Primitive::kPrimInt:
    654       array->AsIntArray()->SetWithoutChecks<false, kCheckTransaction>(
    655           index, static_cast<int32_t>(value));
    656       break;
    657     case Primitive::kPrimFloat:
    658       array->AsFloatArray()->SetWithoutChecks<false, kCheckTransaction>(
    659           index, static_cast<float>(value));
    660       break;
    661     case Primitive::kPrimLong:
    662       array->AsLongArray()->SetWithoutChecks<false, kCheckTransaction>(
    663           index, static_cast<int64_t>(value));
    664       break;
    665     case Primitive::kPrimDouble:
    666       array->AsDoubleArray()->SetWithoutChecks<false, kCheckTransaction>(
    667           index, static_cast<double>(value));
    668       break;
    669     case Primitive::kPrimNot:
    670       LOG(FATAL) << "ObjectArray should be treated as Object";
    671       break;
    672     default:
    673       LOG(FATAL) << "Unsupported type " << array_type;
    674   }
    675 }
    676 
    677 }  // namespace art
    678