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