Merge "Revert "Invoke dex2oat explictly before dalvikvm""
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 5b331bc..1a2b9cd 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -300,6 +300,13 @@
     log_verbosity.oat = true;
     EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
   }
+
+  {
+    const char* log_args = "-verbose:dex";
+    LogVerbosity log_verbosity = LogVerbosity();
+    log_verbosity.dex = true;
+    EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+  }
 }  // TEST_F
 
 // TODO: Enable this b/19274810
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index cd19fa4..e33a207 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -656,6 +656,8 @@
         log_verbosity.systrace_lock_logging = true;
       } else if (verbose_options[j] == "agents") {
         log_verbosity.agents = true;
+      } else if (verbose_options[j] == "dex") {
+        log_verbosity.dex = true;
       } else {
         return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
       }
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index aa529f8..d0f66e2 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -121,14 +121,15 @@
   enum class Type : uint8_t {
     kMethod,
     kCall,
-    kCallRelative,     // NOTE: Actual patching is instruction_set-dependent.
+    kCallRelative,            // NOTE: Actual patching is instruction_set-dependent.
     kType,
-    kTypeRelative,     // NOTE: Actual patching is instruction_set-dependent.
-    kTypeBssEntry,     // NOTE: Actual patching is instruction_set-dependent.
+    kTypeRelative,            // NOTE: Actual patching is instruction_set-dependent.
+    kTypeBssEntry,            // NOTE: Actual patching is instruction_set-dependent.
     kString,
-    kStringRelative,   // NOTE: Actual patching is instruction_set-dependent.
-    kStringBssEntry,   // NOTE: Actual patching is instruction_set-dependent.
-    kDexCacheArray,    // NOTE: Actual patching is instruction_set-dependent.
+    kStringRelative,          // NOTE: Actual patching is instruction_set-dependent.
+    kStringBssEntry,          // NOTE: Actual patching is instruction_set-dependent.
+    kDexCacheArray,           // NOTE: Actual patching is instruction_set-dependent.
+    kBakerReadBarrierBranch,  // NOTE: Actual patching is instruction_set-dependent.
   };
 
   static LinkerPatch MethodPatch(size_t literal_offset,
@@ -215,13 +216,21 @@
                                         const DexFile* target_dex_file,
                                         uint32_t pc_insn_offset,
                                         uint32_t element_offset) {
-    DCHECK(IsUint<32>(element_offset));
     LinkerPatch patch(literal_offset, Type::kDexCacheArray, target_dex_file);
     patch.pc_insn_offset_ = pc_insn_offset;
     patch.element_offset_ = element_offset;
     return patch;
   }
 
+  static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset,
+                                                 uint32_t custom_value1 = 0u,
+                                                 uint32_t custom_value2 = 0u) {
+    LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, nullptr);
+    patch.baker_custom_value1_ = custom_value1;
+    patch.baker_custom_value2_ = custom_value2;
+    return patch;
+  }
+
   LinkerPatch(const LinkerPatch& other) = default;
   LinkerPatch& operator=(const LinkerPatch& other) = default;
 
@@ -241,6 +250,7 @@
       case Type::kStringRelative:
       case Type::kStringBssEntry:
       case Type::kDexCacheArray:
+      case Type::kBakerReadBarrierBranch:
         return true;
       default:
         return false;
@@ -301,6 +311,16 @@
     return pc_insn_offset_;
   }
 
+  uint32_t GetBakerCustomValue1() const {
+    DCHECK(patch_type_ == Type::kBakerReadBarrierBranch);
+    return baker_custom_value1_;
+  }
+
+  uint32_t GetBakerCustomValue2() const {
+    DCHECK(patch_type_ == Type::kBakerReadBarrierBranch);
+    return baker_custom_value2_;
+  }
+
  private:
   LinkerPatch(size_t literal_offset, Type patch_type, const DexFile* target_dex_file)
       : target_dex_file_(target_dex_file),
@@ -314,6 +334,7 @@
   }
 
   const DexFile* target_dex_file_;
+  // TODO: Clean up naming. Some patched locations are literals but others are not.
   uint32_t literal_offset_ : 24;  // Method code size up to 16MiB.
   Type patch_type_ : 8;
   union {
@@ -322,10 +343,12 @@
     uint32_t type_idx_;         // Type index for Type patches.
     uint32_t string_idx_;       // String index for String patches.
     uint32_t element_offset_;   // Element offset in the dex cache arrays.
+    uint32_t baker_custom_value1_;
     static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators");
     static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators");
     static_assert(sizeof(string_idx_) == sizeof(cmp1_), "needed by relational operators");
     static_assert(sizeof(element_offset_) == sizeof(cmp1_), "needed by relational operators");
+    static_assert(sizeof(baker_custom_value1_) == sizeof(cmp1_), "needed by relational operators");
   };
   union {
     // Note: To avoid uninitialized padding on 64-bit systems, we use `size_t` for `cmp2_`.
@@ -334,7 +357,9 @@
     // Literal offset of the insn loading PC (same as literal_offset if it's the same insn,
     // may be different if the PC-relative addressing needs multiple insns).
     uint32_t pc_insn_offset_;
+    uint32_t baker_custom_value2_;
     static_assert(sizeof(pc_insn_offset_) <= sizeof(cmp2_), "needed by relational operators");
+    static_assert(sizeof(baker_custom_value2_) <= sizeof(cmp2_), "needed by relational operators");
   };
 
   friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index 2471f79..f55d5a6 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -24,6 +24,118 @@
 namespace art {
 namespace linker {
 
+class ArmBaseRelativePatcher::ThunkData {
+ public:
+  ThunkData(std::vector<uint8_t> code, uint32_t max_next_offset)
+      : code_(code),
+        offsets_(),
+        max_next_offset_(max_next_offset),
+        pending_offset_(0u) {
+    DCHECK(NeedsNextThunk());  // The data is constructed only when we expect to need the thunk.
+  }
+
+  ThunkData(ThunkData&& src) = default;
+
+  size_t CodeSize() const {
+    return code_.size();
+  }
+
+  ArrayRef<const uint8_t> GetCode() const {
+    return ArrayRef<const uint8_t>(code_);
+  }
+
+  bool NeedsNextThunk() const {
+    return max_next_offset_ != 0u;
+  }
+
+  uint32_t MaxNextOffset() const {
+    DCHECK(NeedsNextThunk());
+    return max_next_offset_;
+  }
+
+  void ClearMaxNextOffset() {
+    DCHECK(NeedsNextThunk());
+    max_next_offset_ = 0u;
+  }
+
+  void SetMaxNextOffset(uint32_t max_next_offset) {
+    DCHECK(!NeedsNextThunk());
+    max_next_offset_ = max_next_offset;
+  }
+
+  // Adjust the MaxNextOffset() down if needed to fit the code before the next thunk.
+  // Returns true if it was adjusted, false if the old value was kept.
+  bool MakeSpaceBefore(const ThunkData& next_thunk, size_t alignment) {
+    DCHECK(NeedsNextThunk());
+    DCHECK(next_thunk.NeedsNextThunk());
+    DCHECK_ALIGNED_PARAM(MaxNextOffset(), alignment);
+    DCHECK_ALIGNED_PARAM(next_thunk.MaxNextOffset(), alignment);
+    if (next_thunk.MaxNextOffset() - CodeSize() < MaxNextOffset()) {
+      max_next_offset_ = RoundDown(next_thunk.MaxNextOffset() - CodeSize(), alignment);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  uint32_t ReserveOffset(size_t offset) {
+    DCHECK(NeedsNextThunk());
+    DCHECK_LE(offset, max_next_offset_);
+    max_next_offset_ = 0u;  // The reserved offset should satisfy all pending references.
+    offsets_.push_back(offset);
+    return offset + CodeSize();
+  }
+
+  bool HasReservedOffset() const {
+    return !offsets_.empty();
+  }
+
+  uint32_t LastReservedOffset() const {
+    DCHECK(HasReservedOffset());
+    return offsets_.back();
+  }
+
+  bool HasPendingOffset() const {
+    return pending_offset_ != offsets_.size();
+  }
+
+  uint32_t GetPendingOffset() const {
+    DCHECK(HasPendingOffset());
+    return offsets_[pending_offset_];
+  }
+
+  void MarkPendingOffsetAsWritten() {
+    DCHECK(HasPendingOffset());
+    ++pending_offset_;
+  }
+
+  bool HasWrittenOffset() const {
+    return pending_offset_ != 0u;
+  }
+
+  uint32_t LastWrittenOffset() const {
+    DCHECK(HasWrittenOffset());
+    return offsets_[pending_offset_ - 1u];
+  }
+
+ private:
+  std::vector<uint8_t> code_;       // The code of the thunk.
+  std::vector<uint32_t> offsets_;   // Offsets at which the thunk needs to be written.
+  uint32_t max_next_offset_;        // The maximum offset at which the next thunk can be placed.
+  uint32_t pending_offset_;         // The index of the next offset to write.
+};
+
+class ArmBaseRelativePatcher::PendingThunkComparator {
+ public:
+  bool operator()(const ThunkData* lhs, const ThunkData* rhs) const {
+    DCHECK(lhs->HasPendingOffset());
+    DCHECK(rhs->HasPendingOffset());
+    // The top of the heap is defined to contain the highest element and we want to pick
+    // the thunk with the smallest pending offset, so use the reverse ordering, i.e. ">".
+    return lhs->GetPendingOffset() > rhs->GetPendingOffset();
+  }
+};
+
 uint32_t ArmBaseRelativePatcher::ReserveSpace(uint32_t offset,
                                               const CompiledMethod* compiled_method,
                                               MethodReference method_ref) {
@@ -31,151 +143,305 @@
 }
 
 uint32_t ArmBaseRelativePatcher::ReserveSpaceEnd(uint32_t offset) {
-  uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
-  bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset,
-                                                MethodReference(nullptr, 0u),
-                                                aligned_offset);
-  if (needs_thunk) {
-    // All remaining patches will be handled by this thunk.
-    DCHECK(!unprocessed_patches_.empty());
-    DCHECK_LE(aligned_offset - unprocessed_patches_.front().second, max_positive_displacement_);
-    unprocessed_patches_.clear();
-
-    thunk_locations_.push_back(aligned_offset);
-    offset = aligned_offset + thunk_code_.size();
+  // For multi-oat compilations (boot image), ReserveSpaceEnd() is called for each oat file.
+  // Since we do not know here whether this is the last file or whether the next opportunity
+  // to place thunk will be soon enough, we need to reserve all needed thunks now. Code for
+  // subsequent oat files can still call back to them.
+  if (!unprocessed_method_call_patches_.empty()) {
+    ResolveMethodCalls(offset, MethodReference(nullptr, DexFile::kDexNoIndex));
   }
+  for (ThunkData* data : unreserved_thunks_) {
+    uint32_t thunk_offset = CompiledCode::AlignCode(offset, instruction_set_);
+    offset = data->ReserveOffset(thunk_offset);
+  }
+  unreserved_thunks_.clear();
+  // We also need to delay initiating the pending_thunks_ until the call to WriteThunks().
+  // Check that the `pending_thunks_.capacity()` indicates that no WriteThunks() has taken place.
+  DCHECK_EQ(pending_thunks_.capacity(), 0u);
   return offset;
 }
 
 uint32_t ArmBaseRelativePatcher::WriteThunks(OutputStream* out, uint32_t offset) {
-  if (current_thunk_to_write_ == thunk_locations_.size()) {
-    return offset;
+  if (pending_thunks_.capacity() == 0u) {
+    if (thunks_.empty()) {
+      return offset;
+    }
+    // First call to WriteThunks(), prepare the thunks for writing.
+    pending_thunks_.reserve(thunks_.size());
+    for (auto& entry : thunks_) {
+      ThunkData* data = &entry.second;
+      if (data->HasPendingOffset()) {
+        pending_thunks_.push_back(data);
+      }
+    }
+    std::make_heap(pending_thunks_.begin(), pending_thunks_.end(), PendingThunkComparator());
   }
   uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
-  if (UNLIKELY(aligned_offset == thunk_locations_[current_thunk_to_write_])) {
-    ++current_thunk_to_write_;
+  while (!pending_thunks_.empty() &&
+         pending_thunks_.front()->GetPendingOffset() == aligned_offset) {
+    // Write alignment bytes and code.
     uint32_t aligned_code_delta = aligned_offset - offset;
-    if (aligned_code_delta != 0u && !WriteCodeAlignment(out, aligned_code_delta)) {
+    if (aligned_code_delta != 0u && UNLIKELY(!WriteCodeAlignment(out, aligned_code_delta))) {
       return 0u;
     }
-    if (UNLIKELY(!WriteRelCallThunk(out, ArrayRef<const uint8_t>(thunk_code_)))) {
+    if (UNLIKELY(!WriteThunk(out, pending_thunks_.front()->GetCode()))) {
       return 0u;
     }
-    offset = aligned_offset + thunk_code_.size();
+    offset = aligned_offset + pending_thunks_.front()->CodeSize();
+    // Mark the thunk as written at the pending offset and update the `pending_thunks_` heap.
+    std::pop_heap(pending_thunks_.begin(), pending_thunks_.end(), PendingThunkComparator());
+    pending_thunks_.back()->MarkPendingOffsetAsWritten();
+    if (pending_thunks_.back()->HasPendingOffset()) {
+      std::push_heap(pending_thunks_.begin(), pending_thunks_.end(), PendingThunkComparator());
+    } else {
+      pending_thunks_.pop_back();
+    }
+    aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
   }
+  DCHECK(pending_thunks_.empty() || pending_thunks_.front()->GetPendingOffset() > aligned_offset);
   return offset;
 }
 
 ArmBaseRelativePatcher::ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
-                                               InstructionSet instruction_set,
-                                               std::vector<uint8_t> thunk_code,
-                                               uint32_t max_positive_displacement,
-                                               uint32_t max_negative_displacement)
-    : provider_(provider), instruction_set_(instruction_set), thunk_code_(thunk_code),
-      max_positive_displacement_(max_positive_displacement),
-      max_negative_displacement_(max_negative_displacement),
-      thunk_locations_(), current_thunk_to_write_(0u), unprocessed_patches_() {
+                                               InstructionSet instruction_set)
+    : provider_(provider),
+      instruction_set_(instruction_set),
+      thunks_(),
+      unprocessed_method_call_patches_(),
+      method_call_thunk_(nullptr),
+      pending_thunks_() {
+}
+
+ArmBaseRelativePatcher::~ArmBaseRelativePatcher() {
+  // All work done by member destructors.
 }
 
 uint32_t ArmBaseRelativePatcher::ReserveSpaceInternal(uint32_t offset,
                                                       const CompiledMethod* compiled_method,
                                                       MethodReference method_ref,
                                                       uint32_t max_extra_space) {
-  uint32_t quick_code_size = compiled_method->GetQuickCode().size();
-  uint32_t quick_code_offset = compiled_method->AlignCode(offset + sizeof(OatQuickMethodHeader));
-  uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
-  // Adjust for extra space required by the subclass.
-  next_aligned_offset = compiled_method->AlignCode(next_aligned_offset + max_extra_space);
-  // TODO: ignore unprocessed patches targeting this method if they can reach quick_code_offset.
-  // We need the MethodReference for that.
-  if (!unprocessed_patches_.empty() &&
-      next_aligned_offset - unprocessed_patches_.front().second > max_positive_displacement_) {
-    bool needs_thunk = ReserveSpaceProcessPatches(quick_code_offset,
-                                                  method_ref,
-                                                  next_aligned_offset);
-    if (needs_thunk) {
-      // A single thunk will cover all pending patches.
-      unprocessed_patches_.clear();
-      uint32_t thunk_location = CompiledMethod::AlignCode(offset, instruction_set_);
-      thunk_locations_.push_back(thunk_location);
-      offset = thunk_location + thunk_code_.size();
+  // Adjust code size for extra space required by the subclass.
+  uint32_t max_code_size = compiled_method->GetQuickCode().size() + max_extra_space;
+  uint32_t code_offset;
+  uint32_t next_aligned_offset;
+  while (true) {
+    code_offset = compiled_method->AlignCode(offset + sizeof(OatQuickMethodHeader));
+    next_aligned_offset = compiled_method->AlignCode(code_offset + max_code_size);
+    if (unreserved_thunks_.empty() ||
+        unreserved_thunks_.front()->MaxNextOffset() >= next_aligned_offset) {
+      break;
+    }
+    ThunkData* thunk = unreserved_thunks_.front();
+    if (thunk == method_call_thunk_) {
+      ResolveMethodCalls(code_offset, method_ref);
+      // This may have changed `method_call_thunk_` data, so re-check if we need to reserve.
+      if (unreserved_thunks_.empty() ||
+          unreserved_thunks_.front()->MaxNextOffset() >= next_aligned_offset) {
+        break;
+      }
+      // We need to process the new `front()` whether it's still the `method_call_thunk_` or not.
+      thunk = unreserved_thunks_.front();
+    }
+    unreserved_thunks_.pop_front();
+    uint32_t thunk_offset = CompiledCode::AlignCode(offset, instruction_set_);
+    offset = thunk->ReserveOffset(thunk_offset);
+    if (thunk == method_call_thunk_) {
+      // All remaining method call patches will be handled by this thunk.
+      DCHECK(!unprocessed_method_call_patches_.empty());
+      DCHECK_LE(thunk_offset - unprocessed_method_call_patches_.front().GetPatchOffset(),
+                MaxPositiveDisplacement(ThunkType::kMethodCall));
+      unprocessed_method_call_patches_.clear();
     }
   }
-  for (const LinkerPatch& patch : compiled_method->GetPatches()) {
-    if (patch.GetType() == LinkerPatch::Type::kCallRelative) {
-      unprocessed_patches_.emplace_back(patch.TargetMethod(),
-                                        quick_code_offset + patch.LiteralOffset());
-    }
-  }
+
+  // Process patches and check that adding thunks for the current method did not push any
+  // thunks (previously existing or newly added) before `next_aligned_offset`. This is
+  // essentially a check that we never compile a method that's too big. The calls or branches
+  // from the method should be able to reach beyond the end of the method and over any pending
+  // thunks. (The number of different thunks should be relatively low and their code short.)
+  ProcessPatches(compiled_method, code_offset);
+  CHECK(unreserved_thunks_.empty() ||
+        unreserved_thunks_.front()->MaxNextOffset() >= next_aligned_offset);
+
   return offset;
 }
 
-uint32_t ArmBaseRelativePatcher::CalculateDisplacement(uint32_t patch_offset,
-                                                       uint32_t target_offset) {
+uint32_t ArmBaseRelativePatcher::CalculateMethodCallDisplacement(uint32_t patch_offset,
+                                                                 uint32_t target_offset) {
+  DCHECK(method_call_thunk_ != nullptr);
   // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
   uint32_t displacement = target_offset - patch_offset;
+  uint32_t max_positive_displacement = MaxPositiveDisplacement(ThunkType::kMethodCall);
+  uint32_t max_negative_displacement = MaxNegativeDisplacement(ThunkType::kMethodCall);
   // NOTE: With unsigned arithmetic we do mean to use && rather than || below.
-  if (displacement > max_positive_displacement_ && displacement < -max_negative_displacement_) {
+  if (displacement > max_positive_displacement && displacement < -max_negative_displacement) {
     // Unwritten thunks have higher offsets, check if it's within range.
-    DCHECK(current_thunk_to_write_ == thunk_locations_.size() ||
-           thunk_locations_[current_thunk_to_write_] > patch_offset);
-    if (current_thunk_to_write_ != thunk_locations_.size() &&
-        thunk_locations_[current_thunk_to_write_] - patch_offset < max_positive_displacement_) {
-      displacement = thunk_locations_[current_thunk_to_write_] - patch_offset;
+    DCHECK(!method_call_thunk_->HasPendingOffset() ||
+           method_call_thunk_->GetPendingOffset() > patch_offset);
+    if (method_call_thunk_->HasPendingOffset() &&
+        method_call_thunk_->GetPendingOffset() - patch_offset <= max_positive_displacement) {
+      displacement = method_call_thunk_->GetPendingOffset() - patch_offset;
     } else {
       // We must have a previous thunk then.
-      DCHECK_NE(current_thunk_to_write_, 0u);
-      DCHECK_LT(thunk_locations_[current_thunk_to_write_ - 1], patch_offset);
-      displacement = thunk_locations_[current_thunk_to_write_ - 1] - patch_offset;
-      DCHECK(displacement >= -max_negative_displacement_);
+      DCHECK(method_call_thunk_->HasWrittenOffset());
+      DCHECK_LT(method_call_thunk_->LastWrittenOffset(), patch_offset);
+      displacement = method_call_thunk_->LastWrittenOffset() - patch_offset;
+      DCHECK_GE(displacement, -max_negative_displacement);
     }
   }
   return displacement;
 }
 
-bool ArmBaseRelativePatcher::ReserveSpaceProcessPatches(uint32_t quick_code_offset,
-                                                        MethodReference method_ref,
-                                                        uint32_t next_aligned_offset) {
-  // Process as many patches as possible, stop only on unresolved targets or calls too far back.
-  while (!unprocessed_patches_.empty()) {
-    MethodReference patch_ref = unprocessed_patches_.front().first;
-    uint32_t patch_offset = unprocessed_patches_.front().second;
-    DCHECK(thunk_locations_.empty() || thunk_locations_.back() <= patch_offset);
-    if (patch_ref.dex_file == method_ref.dex_file &&
-        patch_ref.dex_method_index == method_ref.dex_method_index) {
-      DCHECK_GT(quick_code_offset, patch_offset);
-      if (quick_code_offset - patch_offset > max_positive_displacement_) {
-        return true;
-      }
-    } else {
-      auto result = provider_->FindMethodOffset(patch_ref);
-      if (!result.first) {
-        // If still unresolved, check if we have a thunk within range.
-        if (thunk_locations_.empty() ||
-            patch_offset - thunk_locations_.back() > max_negative_displacement_) {
-          // No thunk in range, we need a thunk if the next aligned offset
-          // is out of range, or if we're at the end of all code.
-          return (next_aligned_offset - patch_offset > max_positive_displacement_) ||
-              (quick_code_offset == next_aligned_offset);  // End of code.
-        }
+uint32_t ArmBaseRelativePatcher::GetThunkTargetOffset(const ThunkKey& key, uint32_t patch_offset) {
+  auto it = thunks_.find(key);
+  CHECK(it != thunks_.end());
+  const ThunkData& data = it->second;
+  if (data.HasWrittenOffset()) {
+    uint32_t offset = data.LastWrittenOffset();
+    DCHECK_LT(offset, patch_offset);
+    if (patch_offset - offset <= MaxNegativeDisplacement(key.GetType())) {
+      return offset;
+    }
+  }
+  DCHECK(data.HasPendingOffset());
+  uint32_t offset = data.GetPendingOffset();
+  DCHECK_GT(offset, patch_offset);
+  DCHECK_LE(offset - patch_offset, MaxPositiveDisplacement(key.GetType()));
+  return offset;
+}
+
+void ArmBaseRelativePatcher::ProcessPatches(const CompiledMethod* compiled_method,
+                                            uint32_t code_offset) {
+  for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+    uint32_t patch_offset = code_offset + patch.LiteralOffset();
+    ThunkType key_type = static_cast<ThunkType>(-1);
+    ThunkData* old_data = nullptr;
+    if (patch.GetType() == LinkerPatch::Type::kCallRelative) {
+      key_type = ThunkType::kMethodCall;
+      unprocessed_method_call_patches_.emplace_back(patch_offset, patch.TargetMethod());
+      if (method_call_thunk_ == nullptr) {
+        ThunkKey key(key_type, ThunkParams{{ 0u, 0u }});  // NOLINT(whitespace/braces)
+        uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key_type);
+        auto it = thunks_.Put(key, ThunkData(CompileThunk(key), max_next_offset));
+        method_call_thunk_ = &it->second;
+        AddUnreservedThunk(method_call_thunk_);
       } else {
-        uint32_t target_offset = result.second - CompiledCode::CodeDelta(instruction_set_);
-        if (target_offset >= patch_offset) {
-          DCHECK_LE(target_offset - patch_offset, max_positive_displacement_);
-        } else {
-          // When calling back, check if we have a thunk that's closer than the actual target.
-          if (!thunk_locations_.empty()) {
-            target_offset = std::max(target_offset, thunk_locations_.back());
-          }
-          if (patch_offset - target_offset > max_negative_displacement_) {
-            return true;
-          }
+        old_data = method_call_thunk_;
+      }
+    } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) {
+      ThunkKey key = GetBakerReadBarrierKey(patch);
+      key_type = key.GetType();
+      auto lb = thunks_.lower_bound(key);
+      if (lb == thunks_.end() || thunks_.key_comp()(key, lb->first)) {
+        uint32_t max_next_offset = CalculateMaxNextOffset(patch_offset, key_type);
+        auto it = thunks_.PutBefore(lb, key, ThunkData(CompileThunk(key), max_next_offset));
+        AddUnreservedThunk(&it->second);
+      } else {
+        old_data = &lb->second;
+      }
+    }
+    if (old_data != nullptr) {
+      // Shared path where an old thunk may need an update.
+      DCHECK(key_type != static_cast<ThunkType>(-1));
+      DCHECK(!old_data->HasReservedOffset() || old_data->LastReservedOffset() < patch_offset);
+      if (old_data->NeedsNextThunk()) {
+        // Patches for a method are ordered by literal offset, so if we still need to place
+        // this thunk for a previous patch, that thunk shall be in range for this patch.
+        DCHECK_LE(old_data->MaxNextOffset(), CalculateMaxNextOffset(patch_offset, key_type));
+      } else {
+        if (!old_data->HasReservedOffset() ||
+            patch_offset - old_data->LastReservedOffset() > MaxNegativeDisplacement(key_type)) {
+          old_data->SetMaxNextOffset(CalculateMaxNextOffset(patch_offset, key_type));
+          AddUnreservedThunk(old_data);
         }
       }
     }
-    unprocessed_patches_.pop_front();
   }
-  return false;
+}
+
+void ArmBaseRelativePatcher::AddUnreservedThunk(ThunkData* data) {
+  DCHECK(data->NeedsNextThunk());
+  size_t index = unreserved_thunks_.size();
+  while (index != 0u && data->MaxNextOffset() < unreserved_thunks_[index - 1u]->MaxNextOffset()) {
+    --index;
+  }
+  unreserved_thunks_.insert(unreserved_thunks_.begin() + index, data);
+  // We may need to update the max next offset(s) if the thunk code would not fit.
+  size_t alignment = GetInstructionSetAlignment(instruction_set_);
+  if (index + 1u != unreserved_thunks_.size()) {
+    // Note: Ignore the return value as we need to process previous thunks regardless.
+    data->MakeSpaceBefore(*unreserved_thunks_[index + 1u], alignment);
+  }
+  // Make space for previous thunks. Once we find a pending thunk that does
+  // not need an adjustment, we can stop.
+  while (index != 0u && unreserved_thunks_[index - 1u]->MakeSpaceBefore(*data, alignment)) {
+    --index;
+    data = unreserved_thunks_[index];
+  }
+}
+
+void ArmBaseRelativePatcher::ResolveMethodCalls(uint32_t quick_code_offset,
+                                                MethodReference method_ref) {
+  DCHECK(!unreserved_thunks_.empty());
+  DCHECK(!unprocessed_method_call_patches_.empty());
+  DCHECK(method_call_thunk_ != nullptr);
+  uint32_t max_positive_displacement = MaxPositiveDisplacement(ThunkType::kMethodCall);
+  uint32_t max_negative_displacement = MaxNegativeDisplacement(ThunkType::kMethodCall);
+  // Process as many patches as possible, stop only on unresolved targets or calls too far back.
+  while (!unprocessed_method_call_patches_.empty()) {
+    MethodReference target_method = unprocessed_method_call_patches_.front().GetTargetMethod();
+    uint32_t patch_offset = unprocessed_method_call_patches_.front().GetPatchOffset();
+    DCHECK(!method_call_thunk_->HasReservedOffset() ||
+           method_call_thunk_->LastReservedOffset() <= patch_offset);
+    if (!method_call_thunk_->HasReservedOffset() ||
+        patch_offset - method_call_thunk_->LastReservedOffset() > max_negative_displacement) {
+      // No previous thunk in range, check if we can reach the target directly.
+      if (target_method.dex_file == method_ref.dex_file &&
+          target_method.dex_method_index == method_ref.dex_method_index) {
+        DCHECK_GT(quick_code_offset, patch_offset);
+        if (quick_code_offset - patch_offset > max_positive_displacement) {
+          break;
+        }
+      } else {
+        auto result = provider_->FindMethodOffset(target_method);
+        if (!result.first) {
+          break;
+        }
+        uint32_t target_offset = result.second - CompiledCode::CodeDelta(instruction_set_);
+        if (target_offset >= patch_offset) {
+          DCHECK_LE(target_offset - patch_offset, max_positive_displacement);
+        } else if (patch_offset - target_offset > max_negative_displacement) {
+          break;
+        }
+      }
+    }
+    unprocessed_method_call_patches_.pop_front();
+  }
+  if (!unprocessed_method_call_patches_.empty()) {
+    // Try to adjust the max next offset in `method_call_thunk_`. Do this conservatively only if
+    // the thunk shall be at the end of the `unreserved_thunks_` to avoid dealing with overlaps.
+    uint32_t new_max_next_offset =
+        unprocessed_method_call_patches_.front().GetPatchOffset() + max_positive_displacement;
+    if (new_max_next_offset >
+        unreserved_thunks_.back()->MaxNextOffset() + unreserved_thunks_.back()->CodeSize()) {
+      method_call_thunk_->ClearMaxNextOffset();
+      method_call_thunk_->SetMaxNextOffset(new_max_next_offset);
+      if (method_call_thunk_ != unreserved_thunks_.back()) {
+        RemoveElement(unreserved_thunks_, method_call_thunk_);
+        unreserved_thunks_.push_back(method_call_thunk_);
+      }
+    }
+  } else {
+    // We have resolved all method calls, we do not need a new thunk anymore.
+    method_call_thunk_->ClearMaxNextOffset();
+    RemoveElement(unreserved_thunks_, method_call_thunk_);
+  }
+}
+
+inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_offset,
+                                                               ThunkType type) {
+  return RoundDown(patch_offset + MaxPositiveDisplacement(type),
+                   GetInstructionSetAlignment(instruction_set_));
 }
 
 }  // namespace linker
diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h
index 25fd35e..2cb1b6c 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.h
+++ b/compiler/linker/arm/relative_patcher_arm_base.h
@@ -18,9 +18,11 @@
 #define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
 
 #include <deque>
+#include <vector>
 
 #include "linker/relative_patcher.h"
 #include "method_reference.h"
+#include "safe_map.h"
 
 namespace art {
 namespace linker {
@@ -35,32 +37,138 @@
 
  protected:
   ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
-                         InstructionSet instruction_set,
-                         std::vector<uint8_t> thunk_code,
-                         uint32_t max_positive_displacement,
-                         uint32_t max_negative_displacement);
+                         InstructionSet instruction_set);
+  ~ArmBaseRelativePatcher();
+
+  enum class ThunkType {
+    kMethodCall,              // Method call thunk.
+    kBakerReadBarrierField,   // Baker read barrier, load field or array element at known offset.
+    kBakerReadBarrierRoot,    // Baker read barrier, GC root load.
+  };
+
+  struct BakerReadBarrierOffsetParams {
+    uint32_t holder_reg;      // Holder object for reading lock word.
+    uint32_t base_reg;        // Base register, different from holder for large offset.
+                              // If base differs from holder, it should be a pre-defined
+                              // register to limit the number of thunks we need to emit.
+                              // The offset is retrieved using introspection.
+  };
+
+  struct BakerReadBarrierRootParams {
+    uint32_t root_reg;        // The register holding the GC root.
+    uint32_t dummy;
+  };
+
+  struct RawThunkParams {
+    uint32_t first;
+    uint32_t second;
+  };
+
+  union ThunkParams {
+    RawThunkParams raw_params;
+    BakerReadBarrierOffsetParams offset_params;
+    BakerReadBarrierRootParams root_params;
+  };
+
+  class ThunkKey {
+   public:
+    ThunkKey(ThunkType type, ThunkParams params) : type_(type), params_(params) { }
+
+    ThunkType GetType() const {
+      return type_;
+    }
+
+    BakerReadBarrierOffsetParams GetOffsetParams() const {
+      DCHECK(type_ == ThunkType::kBakerReadBarrierField);
+      return params_.offset_params;
+    }
+
+    BakerReadBarrierRootParams GetRootParams() const {
+      DCHECK(type_ == ThunkType::kBakerReadBarrierRoot);
+      return params_.root_params;
+    }
+
+    RawThunkParams GetRawParams() const {
+      return params_.raw_params;
+    }
+
+   private:
+    ThunkType type_;
+    ThunkParams params_;
+  };
+
+  class ThunkKeyCompare {
+   public:
+    bool operator()(const ThunkKey& lhs, const ThunkKey& rhs) const {
+      if (lhs.GetType() != rhs.GetType()) {
+        return lhs.GetType() < rhs.GetType();
+      }
+      if (lhs.GetRawParams().first != rhs.GetRawParams().first) {
+        return lhs.GetRawParams().first < rhs.GetRawParams().first;
+      }
+      return lhs.GetRawParams().second < rhs.GetRawParams().second;
+    }
+  };
 
   uint32_t ReserveSpaceInternal(uint32_t offset,
                                 const CompiledMethod* compiled_method,
                                 MethodReference method_ref,
                                 uint32_t max_extra_space);
-  uint32_t CalculateDisplacement(uint32_t patch_offset, uint32_t target_offset);
+  uint32_t GetThunkTargetOffset(const ThunkKey& key, uint32_t patch_offset);
+
+  uint32_t CalculateMethodCallDisplacement(uint32_t patch_offset,
+                                           uint32_t target_offset);
+
+  virtual ThunkKey GetBakerReadBarrierKey(const LinkerPatch& patch) = 0;
+  virtual std::vector<uint8_t> CompileThunk(const ThunkKey& key) = 0;
+  virtual uint32_t MaxPositiveDisplacement(ThunkType type) = 0;
+  virtual uint32_t MaxNegativeDisplacement(ThunkType type) = 0;
 
  private:
-  bool ReserveSpaceProcessPatches(uint32_t quick_code_offset, MethodReference method_ref,
-                                  uint32_t next_aligned_offset);
+  class ThunkData;
+
+  void ProcessPatches(const CompiledMethod* compiled_method, uint32_t code_offset);
+  void AddUnreservedThunk(ThunkData* data);
+
+  void ResolveMethodCalls(uint32_t quick_code_offset, MethodReference method_ref);
+
+  uint32_t CalculateMaxNextOffset(uint32_t patch_offset, ThunkType type);
 
   RelativePatcherTargetProvider* const provider_;
   const InstructionSet instruction_set_;
-  const std::vector<uint8_t> thunk_code_;
-  const uint32_t max_positive_displacement_;
-  const uint32_t max_negative_displacement_;
-  std::vector<uint32_t> thunk_locations_;
-  size_t current_thunk_to_write_;
 
-  // ReserveSpace() tracks unprocessed patches.
-  typedef std::pair<MethodReference, uint32_t> UnprocessedPatch;
-  std::deque<UnprocessedPatch> unprocessed_patches_;
+  // The data for all thunks.
+  // SafeMap<> nodes don't move after being inserted, so we can use direct pointers to the data.
+  using ThunkMap = SafeMap<ThunkKey, ThunkData, ThunkKeyCompare>;
+  ThunkMap thunks_;
+
+  // ReserveSpace() tracks unprocessed method call patches. These may be resolved later.
+  class UnprocessedMethodCallPatch {
+   public:
+    UnprocessedMethodCallPatch(uint32_t patch_offset, MethodReference target_method)
+        : patch_offset_(patch_offset), target_method_(target_method) { }
+
+    uint32_t GetPatchOffset() const {
+      return patch_offset_;
+    }
+
+    MethodReference GetTargetMethod() const {
+      return target_method_;
+    }
+
+   private:
+    uint32_t patch_offset_;
+    MethodReference target_method_;
+  };
+  std::deque<UnprocessedMethodCallPatch> unprocessed_method_call_patches_;
+  // Once we have compiled a method call thunk, cache pointer to the data.
+  ThunkData* method_call_thunk_;
+
+  // Thunks
+  std::deque<ThunkData*> unreserved_thunks_;
+
+  class PendingThunkComparator;
+  std::vector<ThunkData*> pending_thunks_;  // Heap with the PendingThunkComparator.
 
   friend class Arm64RelativePatcherTest;
   friend class Thumb2RelativePatcherTest;
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index fa49fc4..1a5d79c 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -23,9 +23,17 @@
 namespace art {
 namespace linker {
 
+// PC displacement from patch location; Thumb2 PC is always at instruction address + 4.
+static constexpr int32_t kPcDisplacement = 4;
+
+// Maximum positive and negative displacement for method call measured from the patch location.
+// (Signed 25 bit displacement with the last bit 0 has range [-2^24, 2^24-2] measured from
+// the Thumb2 PC pointing right after the BL, i.e. 4 bytes later than the patch location.)
+constexpr uint32_t kMaxMethodCallPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement;
+constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 24) - kPcDisplacement;
+
 Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherTargetProvider* provider)
-    : ArmBaseRelativePatcher(provider, kThumb2, CompileThunkCode(),
-                             kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
+    : ArmBaseRelativePatcher(provider, kThumb2) {
 }
 
 void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code,
@@ -36,7 +44,7 @@
   DCHECK_EQ(literal_offset & 1u, 0u);
   DCHECK_EQ(patch_offset & 1u, 0u);
   DCHECK_EQ(target_offset & 1u, 1u);  // Thumb2 mode bit.
-  uint32_t displacement = CalculateDisplacement(patch_offset, target_offset & ~1u);
+  uint32_t displacement = CalculateMethodCallDisplacement(patch_offset, target_offset & ~1u);
   displacement -= kPcDisplacement;  // The base PC is at the end of the 4-byte patch.
   DCHECK_EQ(displacement & 1u, 0u);
   DCHECK((displacement >> 24) == 0u || (displacement >> 24) == 255u);  // 25-bit signed.
@@ -76,7 +84,20 @@
   SetInsn32(code, literal_offset, insn);
 }
 
-std::vector<uint8_t> Thumb2RelativePatcher::CompileThunkCode() {
+void Thumb2RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                                        const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                                        uint32_t patch_offset ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "UNIMPLEMENTED";
+}
+
+ArmBaseRelativePatcher::ThunkKey Thumb2RelativePatcher::GetBakerReadBarrierKey(
+    const LinkerPatch& patch ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "UNIMPLEMENTED";
+  UNREACHABLE();
+}
+
+std::vector<uint8_t> Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) {
+  DCHECK(key.GetType() == ThunkType::kMethodCall);
   // The thunk just uses the entry point in the ArtMethod. This works even for calls
   // to the generic JNI and interpreter trampolines.
   ArenaPool pool;
@@ -93,6 +114,16 @@
   return thunk_code;
 }
 
+uint32_t Thumb2RelativePatcher::MaxPositiveDisplacement(ThunkType type) {
+  DCHECK(type == ThunkType::kMethodCall);
+  return kMaxMethodCallPositiveDisplacement;
+}
+
+uint32_t Thumb2RelativePatcher::MaxNegativeDisplacement(ThunkType type) {
+  DCHECK(type == ThunkType::kMethodCall);
+  return kMaxMethodCallNegativeDisplacement;
+}
+
 void Thumb2RelativePatcher::SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) {
   DCHECK_LE(offset + 4u, code->size());
   DCHECK_EQ(offset & 1u, 0u);
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
index d85739c..ab37802 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.h
+++ b/compiler/linker/arm/relative_patcher_thumb2.h
@@ -34,24 +34,24 @@
                                 const LinkerPatch& patch,
                                 uint32_t patch_offset,
                                 uint32_t target_offset) OVERRIDE;
+  void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                   const LinkerPatch& patch,
+                                   uint32_t patch_offset) OVERRIDE;
+
+ protected:
+  ThunkKey GetBakerReadBarrierKey(const LinkerPatch& patch) OVERRIDE;
+  std::vector<uint8_t> CompileThunk(const ThunkKey& key) OVERRIDE;
+  uint32_t MaxPositiveDisplacement(ThunkType type) OVERRIDE;
+  uint32_t MaxNegativeDisplacement(ThunkType type) OVERRIDE;
 
  private:
-  static std::vector<uint8_t> CompileThunkCode();
-
   void SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value);
   static uint32_t GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset);
 
   template <typename Vector>
   static uint32_t GetInsn32(Vector* code, uint32_t offset);
 
-  // PC displacement from patch location; Thumb2 PC is always at instruction address + 4.
-  static constexpr int32_t kPcDisplacement = 4;
-
-  // Maximum positive and negative displacement measured from the patch location.
-  // (Signed 25 bit displacement with the last bit 0 has range [-2^24, 2^24-2] measured from
-  // the Thumb2 PC pointing right after the BL, i.e. 4 bytes later than the patch location.)
-  static constexpr uint32_t kMaxPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement;
-  static constexpr uint32_t kMaxNegativeDisplacement = (1u << 24) - kPcDisplacement;
+  friend class Thumb2RelativePatcherTest;
 
   DISALLOW_COPY_AND_ASSIGN(Thumb2RelativePatcher);
 };
diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc
index eace3d4..f08270d 100644
--- a/compiler/linker/arm/relative_patcher_thumb2_test.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc
@@ -63,7 +63,7 @@
     const uint32_t method2_size = (method3_offset - sizeof(OatQuickMethodHeader) - method2_offset);
     std::vector<uint8_t> method2_raw_code(method2_size);
     ArrayRef<const uint8_t> method2_code(method2_raw_code);
-    AddCompiledMethod(MethodRef(2u), method2_code, ArrayRef<const LinkerPatch>());
+    AddCompiledMethod(MethodRef(2u), method2_code);
 
     AddCompiledMethod(MethodRef(3u), method3_code, method3_patches);
 
@@ -80,7 +80,7 @@
     } else {
       uint32_t thunk_end =
           CompiledCode::AlignCode(method3_offset - sizeof(OatQuickMethodHeader), kThumb2) +
-          ThunkSize();
+          MethodCallThunkSize();
       uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end);
       CHECK_EQ(result3.second, header_offset + sizeof(OatQuickMethodHeader) + 1 /* thumb mode */);
       return true;   // Thunk present.
@@ -94,24 +94,30 @@
     return result.second - 1 /* thumb mode */;
   }
 
-  uint32_t ThunkSize() {
-    return static_cast<Thumb2RelativePatcher*>(patcher_.get())->thunk_code_.size();
+  std::vector<uint8_t> CompileMethodCallThunk() {
+    ArmBaseRelativePatcher::ThunkKey key(
+        ArmBaseRelativePatcher::ThunkType::kMethodCall,
+        ArmBaseRelativePatcher::ThunkParams{{ 0, 0 }});  // NOLINT(whitespace/braces)
+    return static_cast<Thumb2RelativePatcher*>(patcher_.get())->CompileThunk(key);
+  }
+
+  uint32_t MethodCallThunkSize() {
+    return CompileMethodCallThunk().size();
   }
 
   bool CheckThunk(uint32_t thunk_offset) {
-    Thumb2RelativePatcher* patcher = static_cast<Thumb2RelativePatcher*>(patcher_.get());
-    ArrayRef<const uint8_t> expected_code(patcher->thunk_code_);
+    const std::vector<uint8_t> expected_code = CompileMethodCallThunk();
     if (output_.size() < thunk_offset + expected_code.size()) {
       LOG(ERROR) << "output_.size() == " << output_.size() << " < "
           << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
       return false;
     }
     ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
-    if (linked_code == expected_code) {
+    if (linked_code == ArrayRef<const uint8_t>(expected_code)) {
       return true;
     }
     // Log failure info.
-    DumpDiff(expected_code, linked_code);
+    DumpDiff(ArrayRef<const uint8_t>(expected_code), linked_code);
     return false;
   }
 
@@ -357,9 +363,10 @@
   uint32_t method3_offset = GetMethodOffset(3u);
   ASSERT_TRUE(IsAligned<kArmAlignment>(method3_offset));
   uint32_t method3_header_offset = method3_offset - sizeof(OatQuickMethodHeader);
+  uint32_t thunk_size = MethodCallThunkSize();
   uint32_t thunk_offset =
-      RoundDown(method3_header_offset - ThunkSize(), GetInstructionSetAlignment(kThumb2));
-  DCHECK_EQ(thunk_offset + ThunkSize() + CodeAlignmentSize(thunk_offset + ThunkSize()),
+      RoundDown(method3_header_offset - thunk_size, GetInstructionSetAlignment(kThumb2));
+  DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
             method3_header_offset);
   ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset));
   uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */);
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 9ddf200..53797d2 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -16,11 +16,17 @@
 
 #include "linker/arm64/relative_patcher_arm64.h"
 
+#include "arch/arm64/asm_support_arm64.h"
 #include "arch/arm64/instruction_set_features_arm64.h"
 #include "art_method.h"
+#include "base/bit_utils.h"
 #include "compiled_method.h"
 #include "driver/compiler_driver.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "linker/output_stream.h"
+#include "lock_word.h"
+#include "mirror/object.h"
+#include "mirror/array-inl.h"
 #include "oat.h"
 #include "oat_quick_method_header.h"
 #include "utils/arm64/assembler_arm64.h"
@@ -30,17 +36,52 @@
 
 namespace {
 
+// Maximum positive and negative displacement for method call measured from the patch location.
+// (Signed 28 bit displacement with the last two bits 0 has range [-2^27, 2^27-4] measured from
+// the ARM64 PC pointing to the BL.)
+constexpr uint32_t kMaxMethodCallPositiveDisplacement = (1u << 27) - 4u;
+constexpr uint32_t kMaxMethodCallNegativeDisplacement = (1u << 27);
+
+// Maximum positive and negative displacement for a conditional branch measured from the patch
+// location. (Signed 21 bit displacement with the last two bits 0 has range [-2^20, 2^20-4]
+// measured from the ARM64 PC pointing to the B.cond.)
+constexpr uint32_t kMaxBcondPositiveDisplacement = (1u << 20) - 4u;
+constexpr uint32_t kMaxBcondNegativeDisplacement = (1u << 20);
+
+// The ADRP thunk for erratum 843419 is 2 instructions, i.e. 8 bytes.
+constexpr uint32_t kAdrpThunkSize = 8u;
+
 inline bool IsAdrpPatch(const LinkerPatch& patch) {
-  return (patch.IsPcRelative() && patch.GetType() != LinkerPatch::Type::kCallRelative) &&
-      patch.LiteralOffset() == patch.PcInsnOffset();
+  switch (patch.GetType()) {
+    case LinkerPatch::Type::kMethod:
+    case LinkerPatch::Type::kCall:
+    case LinkerPatch::Type::kCallRelative:
+    case LinkerPatch::Type::kType:
+    case LinkerPatch::Type::kString:
+    case LinkerPatch::Type::kBakerReadBarrierBranch:
+      return false;
+    case LinkerPatch::Type::kTypeRelative:
+    case LinkerPatch::Type::kTypeBssEntry:
+    case LinkerPatch::Type::kStringRelative:
+    case LinkerPatch::Type::kStringBssEntry:
+    case LinkerPatch::Type::kDexCacheArray:
+      return patch.LiteralOffset() == patch.PcInsnOffset();
+  }
+}
+
+inline uint32_t MaxExtraSpace(size_t num_adrp, size_t code_size) {
+  if (num_adrp == 0u) {
+    return 0u;
+  }
+  uint32_t alignment_bytes = CompiledMethod::AlignCode(code_size, kArm64) - code_size;
+  return kAdrpThunkSize * num_adrp + alignment_bytes;
 }
 
 }  // anonymous namespace
 
 Arm64RelativePatcher::Arm64RelativePatcher(RelativePatcherTargetProvider* provider,
                                            const Arm64InstructionSetFeatures* features)
-    : ArmBaseRelativePatcher(provider, kArm64, CompileThunkCode(),
-                             kMaxPositiveDisplacement, kMaxNegativeDisplacement),
+    : ArmBaseRelativePatcher(provider, kArm64),
       fix_cortex_a53_843419_(features->NeedFixCortexA53_843419()),
       reserved_adrp_thunks_(0u),
       processed_adrp_thunks_(0u) {
@@ -74,7 +115,9 @@
       ++num_adrp;
     }
   }
-  offset = ReserveSpaceInternal(offset, compiled_method, method_ref, kAdrpThunkSize * num_adrp);
+  ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
+  uint32_t max_extra_space = MaxExtraSpace(num_adrp, code.size());
+  offset = ReserveSpaceInternal(offset, compiled_method, method_ref, max_extra_space);
   if (num_adrp == 0u) {
     return offset;
   }
@@ -82,7 +125,6 @@
   // Now that we have the actual offset where the code will be placed, locate the ADRP insns
   // that actually require the thunk.
   uint32_t quick_code_offset = compiled_method->AlignCode(offset + sizeof(OatQuickMethodHeader));
-  ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
   uint32_t thunk_offset = compiled_method->AlignCode(quick_code_offset + code.size());
   DCHECK(compiled_method != nullptr);
   for (const LinkerPatch& patch : compiled_method->GetPatches()) {
@@ -146,7 +188,7 @@
   DCHECK_EQ(literal_offset & 3u, 0u);
   DCHECK_EQ(patch_offset & 3u, 0u);
   DCHECK_EQ(target_offset & 3u, 0u);
-  uint32_t displacement = CalculateDisplacement(patch_offset, target_offset & ~1u);
+  uint32_t displacement = CalculateMethodCallDisplacement(patch_offset, target_offset & ~1u);
   DCHECK_EQ(displacement & 3u, 0u);
   DCHECK((displacement >> 27) == 0u || (displacement >> 27) == 31u);  // 28-bit signed.
   uint32_t insn = (displacement & 0x0fffffffu) >> 2;
@@ -253,15 +295,184 @@
   }
 }
 
-std::vector<uint8_t> Arm64RelativePatcher::CompileThunkCode() {
-  // The thunk just uses the entry point in the ArtMethod. This works even for calls
-  // to the generic JNI and interpreter trampolines.
+void Arm64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                                       const LinkerPatch& patch,
+                                                       uint32_t patch_offset) {
+  DCHECK_ALIGNED(patch_offset, 4u);
+  uint32_t literal_offset = patch.LiteralOffset();
+  DCHECK_ALIGNED(literal_offset, 4u);
+  DCHECK_LT(literal_offset, code->size());
+  uint32_t insn = GetInsn(code, literal_offset);
+  DCHECK_EQ(insn & 0xffffffe0u, 0xb5000000);  // CBNZ Xt, +0 (unpatched)
+  ThunkKey key = GetBakerReadBarrierKey(patch);
+  if (kIsDebugBuild) {
+    // Check that the next instruction matches the expected LDR.
+    switch (key.GetType()) {
+      case ThunkType::kBakerReadBarrierField: {
+        DCHECK_GE(code->size() - literal_offset, 8u);
+        uint32_t next_insn = GetInsn(code, literal_offset + 4u);
+        // LDR (immediate) with correct base_reg.
+        CheckValidReg(next_insn & 0x1fu);  // Check destination register.
+        CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (key.GetOffsetParams().base_reg << 5));
+        break;
+      }
+      case ThunkType::kBakerReadBarrierRoot: {
+        DCHECK_GE(literal_offset, 4u);
+        uint32_t prev_insn = GetInsn(code, literal_offset - 4u);
+        // LDR (immediate) with correct root_reg.
+        CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | key.GetRootParams().root_reg);
+        break;
+      }
+      default:
+        LOG(FATAL) << "Unexpected type: " << static_cast<uint32_t>(key.GetType());
+        UNREACHABLE();
+    }
+  }
+  uint32_t target_offset = GetThunkTargetOffset(key, patch_offset);
+  DCHECK_ALIGNED(target_offset, 4u);
+  uint32_t disp = target_offset - patch_offset;
+  DCHECK((disp >> 20) == 0u || (disp >> 20) == 4095u);  // 21-bit signed.
+  insn |= (disp << (5 - 2)) & 0x00ffffe0u;              // Shift bits 2-20 to 5-23.
+  SetInsn(code, literal_offset, insn);
+}
+
+ArmBaseRelativePatcher::ThunkKey Arm64RelativePatcher::GetBakerReadBarrierKey(
+    const LinkerPatch& patch) {
+  DCHECK_EQ(patch.GetType(), LinkerPatch::Type::kBakerReadBarrierBranch);
+  uint32_t value = patch.GetBakerCustomValue1();
+  BakerReadBarrierKind type = BakerReadBarrierKindField::Decode(value);
+  ThunkParams params;
+  switch (type) {
+    case BakerReadBarrierKind::kField:
+      params.offset_params.base_reg = BakerReadBarrierFirstRegField::Decode(value);
+      CheckValidReg(params.offset_params.base_reg);
+      params.offset_params.holder_reg = BakerReadBarrierSecondRegField::Decode(value);
+      CheckValidReg(params.offset_params.holder_reg);
+      break;
+    case BakerReadBarrierKind::kGcRoot:
+      params.root_params.root_reg = BakerReadBarrierFirstRegField::Decode(value);
+      CheckValidReg(params.root_params.root_reg);
+      params.root_params.dummy = 0u;
+      DCHECK_EQ(BakerReadBarrierSecondRegField::Decode(value), kInvalidEncodedReg);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected type: " << static_cast<uint32_t>(type);
+      UNREACHABLE();
+  }
+  constexpr uint8_t kTypeTranslationOffset = 1u;
+  static_assert(static_cast<uint32_t>(BakerReadBarrierKind::kField) + kTypeTranslationOffset ==
+                static_cast<uint32_t>(ThunkType::kBakerReadBarrierField),
+                "Thunk type translation check.");
+  static_assert(static_cast<uint32_t>(BakerReadBarrierKind::kGcRoot) + kTypeTranslationOffset ==
+                static_cast<uint32_t>(ThunkType::kBakerReadBarrierRoot),
+                "Thunk type translation check.");
+  return ThunkKey(static_cast<ThunkType>(static_cast<uint32_t>(type) + kTypeTranslationOffset),
+                  params);
+}
+
+#define __ assembler.GetVIXLAssembler()->
+
+static void EmitGrayCheckAndFastPath(arm64::Arm64Assembler& assembler,
+                                     vixl::aarch64::Register base_reg,
+                                     vixl::aarch64::MemOperand& lock_word,
+                                     vixl::aarch64::Label* slow_path) {
+  using namespace vixl::aarch64;  // NOLINT(build/namespaces)
+  // Load the lock word containing the rb_state.
+  __ Ldr(ip0.W(), lock_word);
+  // Given the numeric representation, it's enough to check the low bit of the rb_state.
+  static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+  static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
+  __ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path);
+  static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == -4, "Check field LDR offset");
+  static_assert(BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == -4, "Check array LDR offset");
+  __ Sub(lr, lr, 4);  // Adjust the return address one instruction back to the LDR.
+  // Introduce a dependency on the lock_word including rb_state,
+  // to prevent load-load reordering, and without using
+  // a memory barrier (which would be more expensive).
+  __ Add(base_reg, base_reg, Operand(vixl::aarch64::ip0, LSR, 32));
+  __ Br(lr);          // And return back to the function.
+  // Note: The fake dependency is unnecessary for the slow path.
+}
+
+std::vector<uint8_t> Arm64RelativePatcher::CompileThunk(const ThunkKey& key) {
+  using namespace vixl::aarch64;  // NOLINT(build/namespaces)
   ArenaPool pool;
   ArenaAllocator arena(&pool);
   arm64::Arm64Assembler assembler(&arena);
-  Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-      kArm64PointerSize).Int32Value());
-  assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0));
+
+  switch (key.GetType()) {
+    case ThunkType::kMethodCall: {
+      // The thunk just uses the entry point in the ArtMethod. This works even for calls
+      // to the generic JNI and interpreter trampolines.
+      Offset offset(ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+          kArm64PointerSize).Int32Value());
+      assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0));
+      break;
+    }
+    case ThunkType::kBakerReadBarrierField: {
+      // Check if the holder is gray and, if not, add fake dependency to the base register
+      // and return to the LDR instruction to load the reference. Otherwise, use introspection
+      // to load the reference and call the entrypoint (in IP1) that performs further checks
+      // on the reference and marks it if needed.
+      auto holder_reg = Register::GetXRegFromCode(key.GetOffsetParams().holder_reg);
+      auto base_reg = Register::GetXRegFromCode(key.GetOffsetParams().base_reg);
+      UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
+      temps.Exclude(ip0, ip1);
+      // If base_reg differs from holder_reg, the offset was too large and we must have
+      // emitted an explicit null check before the load. Otherwise, we need to null-check
+      // the holder as we do not necessarily do that check before going to the thunk.
+      vixl::aarch64::Label throw_npe;
+      if (holder_reg.Is(base_reg)) {
+        __ Cbz(holder_reg.W(), &throw_npe);
+      }
+      vixl::aarch64::Label slow_path;
+      MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
+      EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path);
+      __ Bind(&slow_path);
+      MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET);
+      __ Ldr(ip0.W(), ldr_address);         // Load the LDR (immediate) unsigned offset.
+      __ Ubfx(ip0, ip0, 10, 12);            // Extract the offset.
+      __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2));   // Load the reference.
+      __ Br(ip1);                           // Jump to the entrypoint.
+      if (holder_reg.Is(base_reg)) {
+        // Add null check slow path. The stack map is at the address pointed to by LR.
+        __ Bind(&throw_npe);
+        int32_t offset = GetThreadOffset<kArm64PointerSize>(kQuickThrowNullPointer).Int32Value();
+        __ Ldr(ip0, MemOperand(vixl::aarch64::x19, offset));
+        __ Br(ip0);
+      }
+      break;
+    }
+    case ThunkType::kBakerReadBarrierRoot: {
+      // Check if the reference needs to be marked and if so (i.e. not null, not marked yet
+      // and it does not have a forwarding address), call the correct introspection entrypoint;
+      // otherwise return the reference (or the extracted forwarding address).
+      // There is no gray bit check for GC roots.
+      auto root_reg = Register::GetWRegFromCode(key.GetRootParams().root_reg);
+      UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
+      temps.Exclude(ip0, ip1);
+      vixl::aarch64::Label return_label, not_marked, forwarding_address;
+      __ Cbz(root_reg, &return_label);
+      MemOperand lock_word(root_reg.X(), mirror::Object::MonitorOffset().Int32Value());
+      __ Ldr(ip0.W(), lock_word);
+      __ Tbz(ip0.W(), LockWord::kMarkBitStateShift, &not_marked);
+      __ Bind(&return_label);
+      __ Br(lr);
+      __ Bind(&not_marked);
+      __ Tst(ip0.W(), Operand(ip0.W(), LSL, 1));
+      __ B(&forwarding_address, mi);
+      // Adjust the art_quick_read_barrier_mark_introspection address in IP1 to
+      // art_quick_read_barrier_mark_introspection_gc_roots.
+      __ Add(ip1, ip1, Operand(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET));
+      __ Mov(ip0.W(), root_reg);
+      __ Br(ip1);
+      __ Bind(&forwarding_address);
+      __ Lsl(root_reg, ip0.W(), LockWord::kForwardingAddressShift);
+      __ Br(lr);
+      break;
+    }
+  }
+
   // Ensure we emit the literal pool.
   assembler.FinalizeCode();
   std::vector<uint8_t> thunk_code(assembler.CodeSize());
@@ -270,6 +481,28 @@
   return thunk_code;
 }
 
+#undef __
+
+uint32_t Arm64RelativePatcher::MaxPositiveDisplacement(ThunkType type) {
+  switch (type) {
+    case ThunkType::kMethodCall:
+      return kMaxMethodCallPositiveDisplacement;
+    case ThunkType::kBakerReadBarrierField:
+    case ThunkType::kBakerReadBarrierRoot:
+      return kMaxBcondPositiveDisplacement;
+  }
+}
+
+uint32_t Arm64RelativePatcher::MaxNegativeDisplacement(ThunkType type) {
+  switch (type) {
+    case ThunkType::kMethodCall:
+      return kMaxMethodCallNegativeDisplacement;
+    case ThunkType::kBakerReadBarrierField:
+    case ThunkType::kBakerReadBarrierRoot:
+      return kMaxBcondNegativeDisplacement;
+  }
+}
+
 uint32_t Arm64RelativePatcher::PatchAdrp(uint32_t adrp, uint32_t disp) {
   return (adrp & 0x9f00001fu) |  // Clear offset bits, keep ADRP with destination reg.
       // Bottom 12 bits are ignored, the next 2 lowest bits are encoded in bits 29-30.
diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h
index a4a8018..7887cea 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.h
+++ b/compiler/linker/arm64/relative_patcher_arm64.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_LINKER_ARM64_RELATIVE_PATCHER_ARM64_H_
 
 #include "base/array_ref.h"
+#include "base/bit_field.h"
 #include "linker/arm/relative_patcher_arm_base.h"
 
 namespace art {
@@ -25,6 +26,27 @@
 
 class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher {
  public:
+  enum class BakerReadBarrierKind : uint8_t {
+    kField,   // Field get or array get with constant offset (i.e. constant index).
+    kGcRoot,  // GC root load.
+    kLast
+  };
+
+  static uint32_t EncodeBakerReadBarrierFieldData(uint32_t base_reg, uint32_t holder_reg) {
+    CheckValidReg(base_reg);
+    CheckValidReg(holder_reg);
+    return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kField) |
+           BakerReadBarrierFirstRegField::Encode(base_reg) |
+           BakerReadBarrierSecondRegField::Encode(holder_reg);
+  }
+
+  static uint32_t EncodeBakerReadBarrierGcRootData(uint32_t root_reg) {
+    CheckValidReg(root_reg);
+    return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kGcRoot) |
+           BakerReadBarrierFirstRegField::Encode(root_reg) |
+           BakerReadBarrierSecondRegField::Encode(kInvalidEncodedReg);
+  }
+
   Arm64RelativePatcher(RelativePatcherTargetProvider* provider,
                        const Arm64InstructionSetFeatures* features);
 
@@ -41,9 +63,33 @@
                                 const LinkerPatch& patch,
                                 uint32_t patch_offset,
                                 uint32_t target_offset) OVERRIDE;
+  void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                   const LinkerPatch& patch,
+                                   uint32_t patch_offset) OVERRIDE;
+
+ protected:
+  static constexpr uint32_t kInvalidEncodedReg = /* sp/zr is invalid */ 31u;
+
+  ThunkKey GetBakerReadBarrierKey(const LinkerPatch& patch) OVERRIDE;
+  std::vector<uint8_t> CompileThunk(const ThunkKey& key) OVERRIDE;
+  uint32_t MaxPositiveDisplacement(ThunkType type) OVERRIDE;
+  uint32_t MaxNegativeDisplacement(ThunkType type) OVERRIDE;
 
  private:
-  static std::vector<uint8_t> CompileThunkCode();
+  static constexpr size_t kBitsForBakerReadBarrierKind =
+      MinimumBitsToStore(static_cast<size_t>(BakerReadBarrierKind::kLast));
+  static constexpr size_t kBitsForRegister = 5u;
+  using BakerReadBarrierKindField =
+      BitField<BakerReadBarrierKind, 0, kBitsForBakerReadBarrierKind>;
+  using BakerReadBarrierFirstRegField =
+      BitField<uint32_t, kBitsForBakerReadBarrierKind, kBitsForRegister>;
+  using BakerReadBarrierSecondRegField =
+      BitField<uint32_t, kBitsForBakerReadBarrierKind + kBitsForRegister, kBitsForRegister>;
+
+  static void CheckValidReg(uint32_t reg) {
+    DCHECK(reg < 30u && reg != 16u && reg != 17u);
+  }
+
   static uint32_t PatchAdrp(uint32_t adrp, uint32_t disp);
 
   static bool NeedsErratum843419Thunk(ArrayRef<const uint8_t> code, uint32_t literal_offset,
@@ -54,15 +100,6 @@
   template <typename Alloc>
   static uint32_t GetInsn(std::vector<uint8_t, Alloc>* code, uint32_t offset);
 
-  // Maximum positive and negative displacement measured from the patch location.
-  // (Signed 28 bit displacement with the last bit 0 has range [-2^27, 2^27-4] measured from
-  // the ARM64 PC pointing to the BL.)
-  static constexpr uint32_t kMaxPositiveDisplacement = (1u << 27) - 4u;
-  static constexpr uint32_t kMaxNegativeDisplacement = (1u << 27);
-
-  // The ADRP thunk for erratum 843419 is 2 instructions, i.e. 8 bytes.
-  static constexpr uint32_t kAdrpThunkSize = 8u;
-
   const bool fix_cortex_a53_843419_;
   // Map original patch_offset to thunk offset.
   std::vector<std::pair<uint32_t, uint32_t>> adrp_thunk_locations_;
@@ -70,6 +107,8 @@
   size_t processed_adrp_thunks_;
   std::vector<uint8_t> current_method_thunks_;
 
+  friend class Arm64RelativePatcherTest;
+
   DISALLOW_COPY_AND_ASSIGN(Arm64RelativePatcher);
 };
 
diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc
index 9932c79..b4d35ab 100644
--- a/compiler/linker/arm64/relative_patcher_arm64_test.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+#include "base/casts.h"
 #include "linker/relative_patcher_test.h"
 #include "linker/arm64/relative_patcher_arm64.h"
+#include "lock_word.h"
+#include "mirror/object.h"
 #include "oat_quick_method_header.h"
 
 namespace art {
@@ -32,6 +35,9 @@
   static const uint8_t kNopRawCode[];
   static const ArrayRef<const uint8_t> kNopCode;
 
+  // NOP instruction.
+  static constexpr uint32_t kNopInsn = 0xd503201f;
+
   // All branches can be created from kBlPlus0 or kBPlus0 by adding the low 26 bits.
   static constexpr uint32_t kBlPlus0 = 0x94000000u;
   static constexpr uint32_t kBPlus0 = 0x14000000u;
@@ -40,7 +46,7 @@
   static constexpr uint32_t kBlPlusMax = 0x95ffffffu;
   static constexpr uint32_t kBlMinusMax = 0x96000000u;
 
-  // LDR immediate, 32-bit.
+  // LDR immediate, unsigned offset.
   static constexpr uint32_t kLdrWInsn = 0xb9400000u;
 
   // ADD/ADDS/SUB/SUBS immediate, 64-bit.
@@ -61,6 +67,34 @@
   static constexpr uint32_t kLdrWSpRelInsn = 0xb94003edu;
   static constexpr uint32_t kLdrXSpRelInsn = 0xf94003edu;
 
+  // CBNZ x17, +0. Bits 5-23 are a placeholder for target offset from PC in units of 4-bytes.
+  static constexpr uint32_t kCbnzIP1Plus0Insn = 0xb5000011;
+
+  void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
+    CHECK_LE(pos, code->size());
+    const uint8_t insn_code[] = {
+        static_cast<uint8_t>(insn),
+        static_cast<uint8_t>(insn >> 8),
+        static_cast<uint8_t>(insn >> 16),
+        static_cast<uint8_t>(insn >> 24),
+    };
+    static_assert(sizeof(insn_code) == 4u, "Invalid sizeof(insn_code).");
+    code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
+  }
+
+  void PushBackInsn(std::vector<uint8_t>* code, uint32_t insn) {
+    InsertInsn(code, code->size(), insn);
+  }
+
+  std::vector<uint8_t> RawCode(std::initializer_list<uint32_t> insns) {
+    std::vector<uint8_t> raw_code;
+    raw_code.reserve(insns.size() * 4u);
+    for (uint32_t insn : insns) {
+      PushBackInsn(&raw_code, insn);
+    }
+    return raw_code;
+  }
+
   uint32_t Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
                                  const ArrayRef<const LinkerPatch>& method1_patches,
                                  const ArrayRef<const uint8_t>& last_method_code,
@@ -93,8 +127,7 @@
       uint32_t chunk_code_size =
           chunk_size - CodeAlignmentSize(chunk_start) - sizeof(OatQuickMethodHeader);
       gap_code.resize(chunk_code_size, 0u);
-      AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code),
-                        ArrayRef<const LinkerPatch>());
+      AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code));
       method_idx += 1u;
       chunk_start += chunk_size;
       chunk_size = kSmallChunkSize;  // For all but the first chunk.
@@ -112,7 +145,7 @@
     // There may be a thunk before method2.
     if (last_result.second != last_method_offset) {
       // Thunk present. Check that there's only one.
-      uint32_t thunk_end = CompiledCode::AlignCode(gap_end, kArm64) + ThunkSize();
+      uint32_t thunk_end = CompiledCode::AlignCode(gap_end, kArm64) + MethodCallThunkSize();
       uint32_t header_offset = thunk_end + CodeAlignmentSize(thunk_end);
       CHECK_EQ(last_result.second, header_offset + sizeof(OatQuickMethodHeader));
     }
@@ -126,37 +159,49 @@
     return result.second;
   }
 
-  uint32_t ThunkSize() {
-    return static_cast<Arm64RelativePatcher*>(patcher_.get())->thunk_code_.size();
+  std::vector<uint8_t> CompileMethodCallThunk() {
+    ArmBaseRelativePatcher::ThunkKey key(
+        ArmBaseRelativePatcher::ThunkType::kMethodCall,
+        ArmBaseRelativePatcher::ThunkParams{{ 0, 0 }});  // NOLINT(whitespace/braces)
+    return down_cast<Arm64RelativePatcher*>(patcher_.get())->CompileThunk(key);
+  }
+
+  uint32_t MethodCallThunkSize() {
+    return CompileMethodCallThunk().size();
   }
 
   bool CheckThunk(uint32_t thunk_offset) {
-    Arm64RelativePatcher* patcher = static_cast<Arm64RelativePatcher*>(patcher_.get());
-    ArrayRef<const uint8_t> expected_code(patcher->thunk_code_);
+    const std::vector<uint8_t> expected_code = CompileMethodCallThunk();
     if (output_.size() < thunk_offset + expected_code.size()) {
       LOG(ERROR) << "output_.size() == " << output_.size() << " < "
           << "thunk_offset + expected_code.size() == " << (thunk_offset + expected_code.size());
       return false;
     }
     ArrayRef<const uint8_t> linked_code(&output_[thunk_offset], expected_code.size());
-    if (linked_code == expected_code) {
+    if (linked_code == ArrayRef<const uint8_t>(expected_code)) {
       return true;
     }
     // Log failure info.
-    DumpDiff(expected_code, linked_code);
+    DumpDiff(ArrayRef<const uint8_t>(expected_code), linked_code);
     return false;
   }
 
+  std::vector<uint8_t> GenNops(size_t num_nops) {
+    std::vector<uint8_t> result;
+    result.reserve(num_nops * 4u + 4u);
+    for (size_t i = 0; i != num_nops; ++i) {
+      PushBackInsn(&result, kNopInsn);
+    }
+    return result;
+  }
+
   std::vector<uint8_t> GenNopsAndBl(size_t num_nops, uint32_t bl) {
     std::vector<uint8_t> result;
     result.reserve(num_nops * 4u + 4u);
     for (size_t i = 0; i != num_nops; ++i) {
-      result.insert(result.end(), kNopCode.begin(), kNopCode.end());
+      PushBackInsn(&result, kNopInsn);
     }
-    result.push_back(static_cast<uint8_t>(bl));
-    result.push_back(static_cast<uint8_t>(bl >> 8));
-    result.push_back(static_cast<uint8_t>(bl >> 16));
-    result.push_back(static_cast<uint8_t>(bl >> 24));
+    PushBackInsn(&result, bl);
     return result;
   }
 
@@ -167,7 +212,7 @@
     std::vector<uint8_t> result;
     result.reserve(num_nops * 4u + 8u);
     for (size_t i = 0; i != num_nops; ++i) {
-      result.insert(result.end(), kNopCode.begin(), kNopCode.end());
+      PushBackInsn(&result, kNopInsn);
     }
     CHECK_ALIGNED(method_offset, 4u);
     CHECK_ALIGNED(target_offset, 4u);
@@ -188,14 +233,8 @@
         ((disp & 0xffffc000) >> (14 - 5)) |   // immhi = (disp >> 14) is at bit 5,
         // We take the sign bit from the disp, limiting disp to +- 2GiB.
         ((disp & 0x80000000) >> (31 - 23));   // sign bit in immhi is at bit 23.
-    result.push_back(static_cast<uint8_t>(adrp));
-    result.push_back(static_cast<uint8_t>(adrp >> 8));
-    result.push_back(static_cast<uint8_t>(adrp >> 16));
-    result.push_back(static_cast<uint8_t>(adrp >> 24));
-    result.push_back(static_cast<uint8_t>(use_insn));
-    result.push_back(static_cast<uint8_t>(use_insn >> 8));
-    result.push_back(static_cast<uint8_t>(use_insn >> 16));
-    result.push_back(static_cast<uint8_t>(use_insn >> 24));
+    PushBackInsn(&result, adrp);
+    PushBackInsn(&result, use_insn);
     return result;
   }
 
@@ -208,7 +247,7 @@
   void TestNopsAdrpLdr(size_t num_nops, uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
     dex_cache_arrays_begin_ = dex_cache_arrays_begin;
     auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u);  // Unpatched.
-    LinkerPatch patches[] = {
+    const LinkerPatch patches[] = {
         LinkerPatch::DexCacheArrayPatch(num_nops * 4u     , nullptr, num_nops * 4u, element_offset),
         LinkerPatch::DexCacheArrayPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, element_offset),
     };
@@ -233,7 +272,7 @@
     constexpr uint32_t kStringIndex = 1u;
     string_index_to_offset_map_.Put(kStringIndex, string_offset);
     auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u);  // Unpatched.
-    LinkerPatch patches[] = {
+    const LinkerPatch patches[] = {
         LinkerPatch::RelativeStringPatch(num_nops * 4u     , nullptr, num_nops * 4u, kStringIndex),
         LinkerPatch::RelativeStringPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, kStringIndex),
     };
@@ -247,16 +286,6 @@
     EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
   }
 
-  void InsertInsn(std::vector<uint8_t>* code, size_t pos, uint32_t insn) {
-    CHECK_LE(pos, code->size());
-    const uint8_t insn_code[] = {
-        static_cast<uint8_t>(insn), static_cast<uint8_t>(insn >> 8),
-        static_cast<uint8_t>(insn >> 16), static_cast<uint8_t>(insn >> 24),
-    };
-    static_assert(sizeof(insn_code) == 4u, "Invalid sizeof(insn_code).");
-    code->insert(code->begin() + pos, insn_code, insn_code + sizeof(insn_code));
-  }
-
   void PrepareNopsAdrpInsn2Ldr(size_t num_nops,
                                uint32_t insn2,
                                uint32_t dex_cache_arrays_begin,
@@ -264,7 +293,7 @@
     dex_cache_arrays_begin_ = dex_cache_arrays_begin;
     auto code = GenNopsAndAdrpLdr(num_nops, 0u, 0u);  // Unpatched.
     InsertInsn(&code, num_nops * 4u + 4u, insn2);
-    LinkerPatch patches[] = {
+    const LinkerPatch patches[] = {
         LinkerPatch::DexCacheArrayPatch(num_nops * 4u     , nullptr, num_nops * 4u, element_offset),
         LinkerPatch::DexCacheArrayPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, element_offset),
     };
@@ -279,7 +308,7 @@
     string_index_to_offset_map_.Put(kStringIndex, string_offset);
     auto code = GenNopsAndAdrpAdd(num_nops, 0u, 0u);  // Unpatched.
     InsertInsn(&code, num_nops * 4u + 4u, insn2);
-    LinkerPatch patches[] = {
+    const LinkerPatch patches[] = {
         LinkerPatch::RelativeStringPatch(num_nops * 4u     , nullptr, num_nops * 4u, kStringIndex),
         LinkerPatch::RelativeStringPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, kStringIndex),
     };
@@ -329,7 +358,7 @@
     InsertInsn(&expected_thunk_code, 4u, b_in);
     ASSERT_EQ(expected_thunk_code.size(), 8u);
 
-    uint32_t thunk_size = ThunkSize();
+    uint32_t thunk_size = MethodCallThunkSize();
     ASSERT_EQ(thunk_offset + thunk_size, output_.size());
     ASSERT_EQ(thunk_size, expected_thunk_code.size());
     ArrayRef<const uint8_t> thunk_code(&output_[thunk_offset], thunk_size);
@@ -433,6 +462,33 @@
     uint32_t insn2 = sprel_ldr_insn | ((sprel_disp_in_load_units & 0xfffu) << 10);
     TestAdrpInsn2Add(insn2, adrp_offset, has_thunk, string_offset);
   }
+
+  std::vector<uint8_t> CompileBakerOffsetThunk(uint32_t base_reg, uint32_t holder_reg) {
+    const LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
+        0u, Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg));
+    auto* patcher = down_cast<Arm64RelativePatcher*>(patcher_.get());
+    ArmBaseRelativePatcher::ThunkKey key = patcher->GetBakerReadBarrierKey(patch);
+    return patcher->CompileThunk(key);
+  }
+
+  std::vector<uint8_t> CompileBakerGcRootThunk(uint32_t root_reg) {
+    LinkerPatch patch = LinkerPatch::BakerReadBarrierBranchPatch(
+        0u, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg));
+    auto* patcher = down_cast<Arm64RelativePatcher*>(patcher_.get());
+    ArmBaseRelativePatcher::ThunkKey key = patcher->GetBakerReadBarrierKey(patch);
+    return patcher->CompileThunk(key);
+  }
+
+  uint32_t GetOutputInsn(uint32_t offset) {
+    CHECK_LE(offset, output_.size());
+    CHECK_GE(output_.size() - offset, 4u);
+    return (static_cast<uint32_t>(output_[offset]) << 0) |
+           (static_cast<uint32_t>(output_[offset + 1]) << 8) |
+           (static_cast<uint32_t>(output_[offset + 2]) << 16) |
+           (static_cast<uint32_t>(output_[offset + 3]) << 24);
+  }
+
+  void TestBakerField(uint32_t offset, uint32_t root_reg);
 };
 
 const uint8_t Arm64RelativePatcherTest::kCallRawCode[] = {
@@ -458,24 +514,22 @@
 };
 
 TEST_F(Arm64RelativePatcherTestDefault, CallSelf) {
-  LinkerPatch patches[] = {
+  const LinkerPatch patches[] = {
       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
   };
   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
   Link();
 
-  static const uint8_t expected_code[] = {
-      0x00, 0x00, 0x00, 0x94
-  };
+  const std::vector<uint8_t> expected_code = RawCode({kBlPlus0});
   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
 }
 
 TEST_F(Arm64RelativePatcherTestDefault, CallOther) {
-  LinkerPatch method1_patches[] = {
+  const LinkerPatch method1_patches[] = {
       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
   };
   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
-  LinkerPatch method2_patches[] = {
+  const LinkerPatch method2_patches[] = {
       LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
   };
   AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
@@ -486,9 +540,7 @@
   uint32_t diff_after = method2_offset - method1_offset;
   CHECK_ALIGNED(diff_after, 4u);
   ASSERT_LT(diff_after >> 2, 1u << 8);  // Simple encoding, (diff_after >> 2) fits into 8 bits.
-  static const uint8_t method1_expected_code[] = {
-      static_cast<uint8_t>(diff_after >> 2), 0x00, 0x00, 0x94
-  };
+  const std::vector<uint8_t> method1_expected_code = RawCode({kBlPlus0 + (diff_after >> 2)});
   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
   uint32_t diff_before = method1_offset - method2_offset;
   CHECK_ALIGNED(diff_before, 4u);
@@ -498,7 +550,7 @@
 }
 
 TEST_F(Arm64RelativePatcherTestDefault, CallTrampoline) {
-  LinkerPatch patches[] = {
+  const LinkerPatch patches[] = {
       LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
   };
   AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
@@ -518,7 +570,7 @@
   constexpr uint32_t bl_offset_in_last_method = 1u * 4u;  // After NOPs.
   ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
   ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
-  LinkerPatch last_method_patches[] = {
+  const LinkerPatch last_method_patches[] = {
       LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, missing_method_index),
   };
 
@@ -551,7 +603,7 @@
   ArrayRef<const uint8_t> method1_code(method1_raw_code);
   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
   uint32_t expected_last_method_idx = 65;  // Based on 2MiB chunks in Create2MethodsWithGap().
-  LinkerPatch method1_patches[] = {
+  const LinkerPatch method1_patches[] = {
       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx),
   };
 
@@ -577,7 +629,7 @@
   constexpr uint32_t bl_offset_in_last_method = 0u * 4u;  // After NOPs.
   ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
   ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
-  LinkerPatch last_method_patches[] = {
+  const LinkerPatch last_method_patches[] = {
       LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
   };
 
@@ -603,7 +655,7 @@
   ArrayRef<const uint8_t> method1_code(method1_raw_code);
   ASSERT_EQ(bl_offset_in_method1 + 4u, method1_code.size());
   uint32_t expected_last_method_idx = 65;  // Based on 2MiB chunks in Create2MethodsWithGap().
-  LinkerPatch method1_patches[] = {
+  const LinkerPatch method1_patches[] = {
       LinkerPatch::RelativeCodePatch(bl_offset_in_method1, nullptr, expected_last_method_idx),
   };
 
@@ -620,9 +672,10 @@
   uint32_t last_method_offset = GetMethodOffset(last_method_idx);
   ASSERT_TRUE(IsAligned<kArm64Alignment>(last_method_offset));
   uint32_t last_method_header_offset = last_method_offset - sizeof(OatQuickMethodHeader);
+  uint32_t thunk_size = MethodCallThunkSize();
   uint32_t thunk_offset =
-      RoundDown(last_method_header_offset - ThunkSize(), GetInstructionSetAlignment(kArm64));
-  DCHECK_EQ(thunk_offset + ThunkSize() + CodeAlignmentSize(thunk_offset + ThunkSize()),
+      RoundDown(last_method_header_offset - thunk_size, GetInstructionSetAlignment(kArm64));
+  DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
             last_method_header_offset);
   uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1);
   CHECK_ALIGNED(diff, 4u);
@@ -637,7 +690,7 @@
   constexpr uint32_t bl_offset_in_last_method = 1u * 4u;  // After NOPs.
   ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
   ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
-  LinkerPatch last_method_patches[] = {
+  const LinkerPatch last_method_patches[] = {
       LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, 1u),
   };
 
@@ -832,5 +885,383 @@
 
 TEST_FOR_OFFSETS(LDRX_SPREL_ADD_TEST, 0, 8)
 
+void Arm64RelativePatcherTest::TestBakerField(uint32_t offset, uint32_t root_reg) {
+  uint32_t valid_regs[] = {
+      0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
+      10, 11, 12, 13, 14, 15,         18, 19,  // IP0 and IP1 are reserved.
+      20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+      // LR and SP/ZR are reserved.
+  };
+  DCHECK_ALIGNED(offset, 4u);
+  DCHECK_LT(offset, 16 * KB);
+  constexpr size_t kMethodCodeSize = 8u;
+  constexpr size_t kLiteralOffset = 0u;
+  uint32_t method_idx = 0u;
+  for (uint32_t base_reg : valid_regs) {
+    for (uint32_t holder_reg : valid_regs) {
+      uint32_t ldr = kLdrWInsn | (offset << (10 - 2)) | (base_reg << 5) | root_reg;
+      const std::vector<uint8_t> raw_code = RawCode({kCbnzIP1Plus0Insn, ldr});
+      ASSERT_EQ(kMethodCodeSize, raw_code.size());
+      ArrayRef<const uint8_t> code(raw_code);
+      uint32_t encoded_data =
+          Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(base_reg, holder_reg);
+      const LinkerPatch patches[] = {
+          LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset, encoded_data),
+      };
+      ++method_idx;
+      AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
+    }
+  }
+  Link();
+
+  // All thunks are at the end.
+  uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
+  method_idx = 0u;
+  for (uint32_t base_reg : valid_regs) {
+    for (uint32_t holder_reg : valid_regs) {
+      ++method_idx;
+      uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset);
+      uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
+      uint32_t ldr = kLdrWInsn | (offset << (10 - 2)) | (base_reg << 5) | root_reg;
+      const std::vector<uint8_t> expected_code = RawCode({cbnz, ldr});
+      ASSERT_EQ(kMethodCodeSize, expected_code.size());
+      ASSERT_TRUE(
+          CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
+
+      std::vector<uint8_t> expected_thunk = CompileBakerOffsetThunk(base_reg, holder_reg);
+      ASSERT_GT(output_.size(), thunk_offset);
+      ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
+      ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
+                                             expected_thunk.size());
+      if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
+        DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
+        ASSERT_TRUE(false);
+      }
+
+      size_t gray_check_offset = thunk_offset;
+      if (holder_reg == base_reg) {
+        // Verify that the null-check CBZ uses the correct register, i.e. holder_reg.
+        ASSERT_GE(output_.size() - gray_check_offset, 4u);
+        ASSERT_EQ(0x34000000 | holder_reg, GetOutputInsn(thunk_offset) & 0xff00001f);
+        gray_check_offset +=4u;
+      }
+      // Verify that the lock word for gray bit check is loaded from the holder address.
+      static constexpr size_t kGrayCheckInsns = 5;
+      ASSERT_GE(output_.size() - gray_check_offset, 4u * kGrayCheckInsns);
+      const uint32_t load_lock_word =
+          kLdrWInsn |
+          (mirror::Object::MonitorOffset().Uint32Value() << (10 - 2)) |
+          (holder_reg << 5) |
+          /* ip0 */ 16;
+      EXPECT_EQ(load_lock_word, GetOutputInsn(gray_check_offset));
+      // Verify the gray bit check.
+      const uint32_t check_gray_bit_witout_offset =
+          0x37000000 | (LockWord::kReadBarrierStateShift << 19) | /* ip0 */ 16;
+      EXPECT_EQ(check_gray_bit_witout_offset, GetOutputInsn(gray_check_offset + 4u) & 0xfff8001f);
+      // Verify the fake dependency.
+      const uint32_t fake_dependency =
+          0x8b408000 |              // ADD Xd, Xn, Xm, LSR 32
+          (/* ip0 */ 16 << 16) |    // Xm = ip0
+          (base_reg << 5) |         // Xn = base_reg
+          base_reg;                 // Xd = base_reg
+      EXPECT_EQ(fake_dependency, GetOutputInsn(gray_check_offset + 12u));
+      // Do not check the rest of the implementation.
+
+      // The next thunk follows on the next aligned offset.
+      thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
+    }
+  }
+}
+
+#define TEST_BAKER_FIELD(offset, root_reg)    \
+  TEST_F(Arm64RelativePatcherTestDefault,     \
+    BakerOffset##offset##_##root_reg) {       \
+    TestBakerField(offset, root_reg);         \
+  }
+
+TEST_BAKER_FIELD(/* offset */ 0, /* root_reg */ 0)
+TEST_BAKER_FIELD(/* offset */ 8, /* root_reg */ 15)
+TEST_BAKER_FIELD(/* offset */ 0x3ffc, /* root_reg */ 29)
+
+TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddle) {
+  // One thunk in the middle with maximum distance branches to it from both sides.
+  // Use offset = 0, base_reg = 0, root_reg = 0, the LDR is simply `kLdrWInsn`.
+  constexpr uint32_t kLiteralOffset1 = 4;
+  const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn});
+  ArrayRef<const uint8_t> code1(raw_code1);
+  uint32_t encoded_data =
+      Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0);
+  const LinkerPatch patches1[] = {
+      LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
+  };
+  AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
+
+  // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
+  // allows the branch to reach that thunk.
+  size_t filler1_size =
+      1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
+  std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
+  ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
+  AddCompiledMethod(MethodRef(2u), filler1_code);
+
+  // Enforce thunk reservation with a tiny method.
+  AddCompiledMethod(MethodRef(3u), kNopCode);
+
+  // Allow reaching the thunk from the very beginning of a method 1MiB away. Backward branch
+  // reaches the full 1MiB. Things to subtract:
+  //   - thunk size and method 3 pre-header, rounded up (padding in between if needed)
+  //   - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
+  //   - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
+  size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
+  size_t filler2_size =
+      1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
+             - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
+             - sizeof(OatQuickMethodHeader);
+  std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
+  ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
+  AddCompiledMethod(MethodRef(4u), filler2_code);
+
+  constexpr uint32_t kLiteralOffset2 = 0;
+  const std::vector<uint8_t> raw_code2 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn});
+  ArrayRef<const uint8_t> code2(raw_code2);
+  const LinkerPatch patches2[] = {
+      LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
+  };
+  AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
+
+  Link();
+
+  uint32_t first_method_offset = GetMethodOffset(1u);
+  uint32_t last_method_offset = GetMethodOffset(5u);
+  EXPECT_EQ(2 * MB, last_method_offset - first_method_offset);
+
+  const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0;
+  const uint32_t cbnz_max_backward = kCbnzIP1Plus0Insn | 0x00800000;
+  const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn});
+  const std::vector<uint8_t> expected_code2 = RawCode({cbnz_max_backward, kLdrWInsn});
+  ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
+  ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
+}
+
+TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkBeforeFiller) {
+  // Based on the first part of BakerOffsetThunkInTheMiddle but the CBNZ is one instruction
+  // earlier, so the thunk is emitted before the filler.
+  // Use offset = 0, base_reg = 0, root_reg = 0, the LDR is simply `kLdrWInsn`.
+  constexpr uint32_t kLiteralOffset1 = 0;
+  const std::vector<uint8_t> raw_code1 = RawCode({kCbnzIP1Plus0Insn, kLdrWInsn, kNopInsn});
+  ArrayRef<const uint8_t> code1(raw_code1);
+  uint32_t encoded_data =
+      Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0);
+  const LinkerPatch patches1[] = {
+      LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
+  };
+  AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
+
+  // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
+  // allows the branch to reach that thunk.
+  size_t filler1_size =
+      1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
+  std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
+  ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
+  AddCompiledMethod(MethodRef(2u), filler1_code);
+
+  Link();
+
+  const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64Alignment) - kLiteralOffset1;
+  const uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
+  const std::vector<uint8_t> expected_code1 = RawCode({cbnz, kLdrWInsn, kNopInsn});
+  ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
+}
+
+TEST_F(Arm64RelativePatcherTestDefault, BakerOffsetThunkInTheMiddleUnreachableFromLast) {
+  // Based on the BakerOffsetThunkInTheMiddle but the CBNZ in the last method is preceded
+  // by NOP and cannot reach the thunk in the middle, so we emit an extra thunk at the end.
+  // Use offset = 0, base_reg = 0, root_reg = 0, the LDR is simply `kLdrWInsn`.
+  constexpr uint32_t kLiteralOffset1 = 4;
+  const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn});
+  ArrayRef<const uint8_t> code1(raw_code1);
+  uint32_t encoded_data =
+      Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(/* base_reg */ 0, /* holder_reg */ 0);
+  const LinkerPatch patches1[] = {
+      LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset1, encoded_data),
+  };
+  AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(patches1));
+
+  // Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
+  // allows the branch to reach that thunk.
+  size_t filler1_size =
+      1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
+  std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
+  ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
+  AddCompiledMethod(MethodRef(2u), filler1_code);
+
+  // Enforce thunk reservation with a tiny method.
+  AddCompiledMethod(MethodRef(3u), kNopCode);
+
+  // If not for the extra NOP, this would allow reaching the thunk from the very beginning
+  // of a method 1MiB away. Backward branch reaches the full 1MiB. Things to subtract:
+  //   - thunk size and method 3 pre-header, rounded up (padding in between if needed)
+  //   - method 3 code and method 4 pre-header, rounded up (padding in between if needed)
+  //   - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
+  size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
+  size_t filler2_size =
+      1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
+             - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
+             - sizeof(OatQuickMethodHeader);
+  std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
+  ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
+  AddCompiledMethod(MethodRef(4u), filler2_code);
+
+  // Extra NOP compared to BakerOffsetThunkInTheMiddle.
+  constexpr uint32_t kLiteralOffset2 = 4;
+  const std::vector<uint8_t> raw_code2 = RawCode({kNopInsn, kCbnzIP1Plus0Insn, kLdrWInsn});
+  ArrayRef<const uint8_t> code2(raw_code2);
+  const LinkerPatch patches2[] = {
+      LinkerPatch::BakerReadBarrierBranchPatch(kLiteralOffset2, encoded_data),
+  };
+  AddCompiledMethod(MethodRef(5u), code2, ArrayRef<const LinkerPatch>(patches2));
+
+  Link();
+
+  const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0;
+  const uint32_t cbnz_last_offset = RoundUp(raw_code2.size(), kArm64Alignment) - kLiteralOffset2;
+  const uint32_t cbnz_last = kCbnzIP1Plus0Insn | (cbnz_last_offset << (5 - 2));
+  const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn});
+  const std::vector<uint8_t> expected_code2 = RawCode({kNopInsn, cbnz_last, kLdrWInsn});
+  ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
+  ASSERT_TRUE(CheckLinkedMethod(MethodRef(5), ArrayRef<const uint8_t>(expected_code2)));
+}
+
+TEST_F(Arm64RelativePatcherTestDefault, BakerRootGcRoot) {
+  uint32_t valid_regs[] = {
+      0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
+      10, 11, 12, 13, 14, 15,         18, 19,  // IP0 and IP1 are reserved.
+      20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+      // LR and SP/ZR are reserved.
+  };
+  constexpr size_t kMethodCodeSize = 8u;
+  constexpr size_t kLiteralOffset = 4u;
+  uint32_t method_idx = 0u;
+  for (uint32_t root_reg : valid_regs) {
+    ++method_idx;
+    uint32_t ldr = kLdrWInsn | (/* offset */ 8 << (10 - 2)) | (/* base_reg */ 0 << 5) | root_reg;
+    const std::vector<uint8_t> raw_code = RawCode({ldr, kCbnzIP1Plus0Insn});
+    ASSERT_EQ(kMethodCodeSize, raw_code.size());
+    ArrayRef<const uint8_t> code(raw_code);
+    const LinkerPatch patches[] = {
+        LinkerPatch::BakerReadBarrierBranchPatch(
+            kLiteralOffset, Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg)),
+    };
+    AddCompiledMethod(MethodRef(method_idx), code, ArrayRef<const LinkerPatch>(patches));
+  }
+  Link();
+
+  // All thunks are at the end.
+  uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
+  method_idx = 0u;
+  for (uint32_t root_reg : valid_regs) {
+    ++method_idx;
+    uint32_t cbnz_offset = thunk_offset - (GetMethodOffset(method_idx) + kLiteralOffset);
+    uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
+    uint32_t ldr = kLdrWInsn | (/* offset */ 8 << (10 - 2)) | (/* base_reg */ 0 << 5) | root_reg;
+    const std::vector<uint8_t> expected_code = RawCode({ldr, cbnz});
+    ASSERT_EQ(kMethodCodeSize, expected_code.size());
+    EXPECT_TRUE(CheckLinkedMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(expected_code)));
+
+    std::vector<uint8_t> expected_thunk = CompileBakerGcRootThunk(root_reg);
+    ASSERT_GT(output_.size(), thunk_offset);
+    ASSERT_GE(output_.size() - thunk_offset, expected_thunk.size());
+    ArrayRef<const uint8_t> compiled_thunk(output_.data() + thunk_offset,
+                                           expected_thunk.size());
+    if (ArrayRef<const uint8_t>(expected_thunk) != compiled_thunk) {
+      DumpDiff(ArrayRef<const uint8_t>(expected_thunk), compiled_thunk);
+      ASSERT_TRUE(false);
+    }
+
+    // Verify that the fast-path null-check CBZ uses the correct register, i.e. root_reg.
+    ASSERT_GE(output_.size() - thunk_offset, 4u);
+    ASSERT_EQ(0x34000000 | root_reg, GetOutputInsn(thunk_offset) & 0xff00001f);
+    // Do not check the rest of the implementation.
+
+    // The next thunk follows on the next aligned offset.
+    thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
+  }
+}
+
+TEST_F(Arm64RelativePatcherTestDefault, BakerAndMethodCallInteraction) {
+  // During development, there was a `DCHECK_LE(MaxNextOffset(), next_thunk.MaxNextOffset());`
+  // in `ArmBaseRelativePatcher::ThunkData::MakeSpaceBefore()` which does not necessarily
+  // hold when we're reserving thunks of different sizes. This test exposes the situation
+  // by using Baker thunks and a method call thunk.
+
+  // Add a method call patch that can reach to method 1 offset + 128MiB.
+  uint32_t method_idx = 0u;
+  constexpr size_t kMethodCallLiteralOffset = 4u;
+  constexpr uint32_t kMissingMethodIdx = 2u;
+  const std::vector<uint8_t> raw_code1 = RawCode({kNopInsn, kBlPlus0});
+  const LinkerPatch method1_patches[] = {
+      LinkerPatch::RelativeCodePatch(kMethodCallLiteralOffset, nullptr, 2u),
+  };
+  ArrayRef<const uint8_t> code1(raw_code1);
+  ++method_idx;
+  AddCompiledMethod(MethodRef(1u), code1, ArrayRef<const LinkerPatch>(method1_patches));
+
+  // Skip kMissingMethodIdx.
+  ++method_idx;
+  ASSERT_EQ(kMissingMethodIdx, method_idx);
+  // Add a method with the right size that the method code for the next one starts 1MiB
+  // after code for method 1.
+  size_t filler_size =
+      1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
+             - sizeof(OatQuickMethodHeader);
+  std::vector<uint8_t> filler_code = GenNops(filler_size / 4u);
+  ++method_idx;
+  AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
+  // Add 126 methods with 1MiB code+header, making the code for the next method start 1MiB
+  // before the currently scheduled MaxNextOffset() for the method call thunk.
+  for (uint32_t i = 0; i != 126; ++i) {
+    filler_size = 1 * MB - sizeof(OatQuickMethodHeader);
+    filler_code = GenNops(filler_size / 4u);
+    ++method_idx;
+    AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(filler_code));
+  }
+
+  // Add 2 Baker GC root patches to the last method, one that would allow the thunk at
+  // 1MiB + kArm64Alignment, i.e. kArm64Alignment after the method call thunk, and the
+  // second that needs it kArm64Alignment after that. Given the size of the GC root thunk
+  // is more than the space required by the method call thunk plus kArm64Alignment,
+  // this pushes the first GC root thunk's pending MaxNextOffset() before the method call
+  // thunk's pending MaxNextOffset() which needs to be adjusted.
+  ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64Alignment) + kArm64Alignment,
+            CompileBakerGcRootThunk(/* root_reg */ 0).size());
+  static_assert(kArm64Alignment == 16, "Code below assumes kArm64Alignment == 16");
+  constexpr size_t kBakerLiteralOffset1 = 4u + kArm64Alignment;
+  constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64Alignment;
+  // Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | root_reg`.
+  const uint32_t ldr1 = kLdrWInsn | /* root_reg */ 1;
+  const uint32_t ldr2 = kLdrWInsn | /* root_reg */ 2;
+  const std::vector<uint8_t> last_method_raw_code = RawCode({
+      kNopInsn, kNopInsn, kNopInsn, kNopInsn,   // Padding before first GC root read barrier.
+      ldr1, kCbnzIP1Plus0Insn,                  // First GC root LDR with read barrier.
+      kNopInsn, kNopInsn,                       // Padding before second GC root read barrier.
+      ldr2, kCbnzIP1Plus0Insn,                  // Second GC root LDR with read barrier.
+  });
+  uint32_t encoded_data1 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 1);
+  uint32_t encoded_data2 = Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(/* root_reg */ 2);
+  const LinkerPatch last_method_patches[] = {
+      LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset1, encoded_data1),
+      LinkerPatch::BakerReadBarrierBranchPatch(kBakerLiteralOffset2, encoded_data2),
+  };
+  ++method_idx;
+  AddCompiledMethod(MethodRef(method_idx),
+                    ArrayRef<const uint8_t>(last_method_raw_code),
+                    ArrayRef<const LinkerPatch>(last_method_patches));
+
+  // The main purpose of the test is to check that Link() does not cause a crash.
+  Link();
+
+  ASSERT_EQ(127 * MB, GetMethodOffset(method_idx) - GetMethodOffset(1u));
+}
+
 }  // namespace linker
 }  // namespace art
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc
index fe5f9a9..8da530f 100644
--- a/compiler/linker/mips/relative_patcher_mips.cc
+++ b/compiler/linker/mips/relative_patcher_mips.cc
@@ -117,5 +117,11 @@
   (*code)[literal_low_offset + 1] = static_cast<uint8_t>(diff >> 8);
 }
 
+void MipsRelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                                      const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                                      uint32_t patch_offset ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "UNIMPLEMENTED";
+}
+
 }  // namespace linker
 }  // namespace art
diff --git a/compiler/linker/mips/relative_patcher_mips.h b/compiler/linker/mips/relative_patcher_mips.h
index 4ff2f2f..852a345 100644
--- a/compiler/linker/mips/relative_patcher_mips.h
+++ b/compiler/linker/mips/relative_patcher_mips.h
@@ -41,6 +41,9 @@
                                 const LinkerPatch& patch,
                                 uint32_t patch_offset,
                                 uint32_t target_offset) OVERRIDE;
+  void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                   const LinkerPatch& patch,
+                                   uint32_t patch_offset) OVERRIDE;
 
  private:
   // We'll maximize the range of a single load instruction for dex cache array accesses
diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/compiler/linker/mips64/relative_patcher_mips64.cc
index c479716..3488d6d 100644
--- a/compiler/linker/mips64/relative_patcher_mips64.cc
+++ b/compiler/linker/mips64/relative_patcher_mips64.cc
@@ -107,5 +107,11 @@
   (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8);
 }
 
+void Mips64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                                        const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                                        uint32_t patch_offset ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "UNIMPLEMENTED";
+}
+
 }  // namespace linker
 }  // namespace art
diff --git a/compiler/linker/mips64/relative_patcher_mips64.h b/compiler/linker/mips64/relative_patcher_mips64.h
index 8ef8ceb..f478d7f 100644
--- a/compiler/linker/mips64/relative_patcher_mips64.h
+++ b/compiler/linker/mips64/relative_patcher_mips64.h
@@ -39,6 +39,9 @@
                                 const LinkerPatch& patch,
                                 uint32_t patch_offset,
                                 uint32_t target_offset) OVERRIDE;
+  void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                   const LinkerPatch& patch,
+                                   uint32_t patch_offset) OVERRIDE;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Mips64RelativePatcher);
diff --git a/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h
index dbda03f..247b290 100644
--- a/compiler/linker/multi_oat_relative_patcher.h
+++ b/compiler/linker/multi_oat_relative_patcher.h
@@ -112,6 +112,13 @@
     relative_patcher_->PatchPcRelativeReference(code, patch, patch_offset, target_offset);
   }
 
+  void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                   const LinkerPatch& patch,
+                                   uint32_t patch_offset) {
+    patch_offset += adjustment_;
+    relative_patcher_->PatchBakerReadBarrierBranch(code, patch, patch_offset);
+  }
+
   // Wrappers around RelativePatcher for statistics retrieval.
   uint32_t CodeAlignmentSize() const;
   uint32_t RelativeCallThunksSize() const;
diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc
index 92a96a0..951588a 100644
--- a/compiler/linker/multi_oat_relative_patcher_test.cc
+++ b/compiler/linker/multi_oat_relative_patcher_test.cc
@@ -63,7 +63,7 @@
       if (next_write_call_thunk_ != 0u) {
         offset += next_write_call_thunk_;
         std::vector<uint8_t> thunk(next_write_call_thunk_, 'c');
-        bool success = WriteRelCallThunk(out, ArrayRef<const uint8_t>(thunk));
+        bool success = WriteThunk(out, ArrayRef<const uint8_t>(thunk));
         CHECK(success);
         next_write_call_thunk_ = 0u;
       }
@@ -95,6 +95,12 @@
       last_target_offset_ = target_offset;
     }
 
+    void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                     const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                     uint32_t patch_offset ATTRIBUTE_UNUSED) {
+      LOG(FATAL) << "UNIMPLEMENTED";
+    }
+
     uint32_t last_reserve_offset_ = 0u;
     MethodReference last_reserve_method_ = kNullMethodRef;
     uint32_t next_reserve_adjustment_ = 0u;
diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc
index f1538b1..ee49453 100644
--- a/compiler/linker/relative_patcher.cc
+++ b/compiler/linker/relative_patcher.cc
@@ -75,6 +75,12 @@
       LOG(FATAL) << "Unexpected relative dex cache array patch.";
     }
 
+    void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                     const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                     uint32_t patch_offset ATTRIBUTE_UNUSED) {
+      LOG(FATAL) << "Unexpected baker read barrier branch patch.";
+    }
+
    private:
     DISALLOW_COPY_AND_ASSIGN(RelativePatcherNone);
   };
@@ -127,7 +133,7 @@
   return true;
 }
 
-bool RelativePatcher::WriteRelCallThunk(OutputStream* out, const ArrayRef<const uint8_t>& thunk) {
+bool RelativePatcher::WriteThunk(OutputStream* out, const ArrayRef<const uint8_t>& thunk) {
   if (UNLIKELY(!out->WriteFully(thunk.data(), thunk.size()))) {
     return false;
   }
diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h
index 15e955b..38c8228 100644
--- a/compiler/linker/relative_patcher.h
+++ b/compiler/linker/relative_patcher.h
@@ -109,6 +109,11 @@
                                         uint32_t patch_offset,
                                         uint32_t target_offset) = 0;
 
+  // Patch a branch to a Baker read barrier thunk.
+  virtual void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                           const LinkerPatch& patch,
+                                           uint32_t patch_offset) = 0;
+
  protected:
   RelativePatcher()
       : size_code_alignment_(0u),
@@ -117,7 +122,7 @@
   }
 
   bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
-  bool WriteRelCallThunk(OutputStream* out, const ArrayRef<const uint8_t>& thunk);
+  bool WriteThunk(OutputStream* out, const ArrayRef<const uint8_t>& thunk);
   bool WriteMiscThunk(OutputStream* out, const ArrayRef<const uint8_t>& thunk);
 
  private:
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index 908cb41..d9a87a0 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -76,9 +76,10 @@
     return MethodReference(nullptr, method_idx);
   }
 
-  void AddCompiledMethod(MethodReference method_ref,
-                         const ArrayRef<const uint8_t>& code,
-                         const ArrayRef<const LinkerPatch>& patches) {
+  void AddCompiledMethod(
+      MethodReference method_ref,
+      const ArrayRef<const uint8_t>& code,
+      const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) {
     compiled_method_refs_.push_back(method_ref);
     compiled_methods_.emplace_back(new CompiledMethod(
         &driver_,
@@ -169,6 +170,10 @@
                                                patch,
                                                offset + patch.LiteralOffset(),
                                                target_offset);
+          } else if (patch.GetType() == LinkerPatch::Type::kBakerReadBarrierBranch) {
+            patcher_->PatchBakerReadBarrierBranch(&patched_code_,
+                                                  patch,
+                                                  offset + patch.LiteralOffset());
           } else {
             LOG(FATAL) << "Bad patch type. " << patch.GetType();
             UNREACHABLE();
diff --git a/compiler/linker/x86/relative_patcher_x86.cc b/compiler/linker/x86/relative_patcher_x86.cc
index 768d31a..6967b0b 100644
--- a/compiler/linker/x86/relative_patcher_x86.cc
+++ b/compiler/linker/x86/relative_patcher_x86.cc
@@ -56,5 +56,11 @@
   (*code)[literal_offset + 3u] = static_cast<uint8_t>(diff >> 24);
 }
 
+void X86RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                                     const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                                     uint32_t patch_offset ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "UNIMPLEMENTED";
+}
+
 }  // namespace linker
 }  // namespace art
diff --git a/compiler/linker/x86/relative_patcher_x86.h b/compiler/linker/x86/relative_patcher_x86.h
index fbf9ad4..63a8338 100644
--- a/compiler/linker/x86/relative_patcher_x86.h
+++ b/compiler/linker/x86/relative_patcher_x86.h
@@ -30,6 +30,9 @@
                                 const LinkerPatch& patch,
                                 uint32_t patch_offset,
                                 uint32_t target_offset) OVERRIDE;
+  void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                   const LinkerPatch& patch,
+                                   uint32_t patch_offset) OVERRIDE;
 };
 
 }  // namespace linker
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/compiler/linker/x86_64/relative_patcher_x86_64.cc
index 2ff6930..156ece9 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64.cc
+++ b/compiler/linker/x86_64/relative_patcher_x86_64.cc
@@ -34,5 +34,11 @@
   reinterpret_cast<unaligned_int32_t*>(&(*code)[patch.LiteralOffset()])[0] = displacement;
 }
 
+void X86_64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+                                                        const LinkerPatch& patch ATTRIBUTE_UNUSED,
+                                                        uint32_t patch_offset ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "UNIMPLEMENTED";
+}
+
 }  // namespace linker
 }  // namespace art
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.h b/compiler/linker/x86_64/relative_patcher_x86_64.h
index 11bb6d5..4f3ec49 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64.h
+++ b/compiler/linker/x86_64/relative_patcher_x86_64.h
@@ -30,6 +30,9 @@
                                 const LinkerPatch& patch,
                                 uint32_t patch_offset,
                                 uint32_t target_offset) OVERRIDE;
+  void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
+                                   const LinkerPatch& patch,
+                                   uint32_t patch_offset) OVERRIDE;
 };
 
 }  // namespace linker
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 105db1d..1781643 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1348,6 +1348,12 @@
                 PatchObjectAddress(&patched_code_, literal_offset, type);
                 break;
               }
+              case LinkerPatch::Type::kBakerReadBarrierBranch: {
+                writer_->relative_patcher_->PatchBakerReadBarrierBranch(&patched_code_,
+                                                                        patch,
+                                                                        offset_ + literal_offset);
+                break;
+              }
               default: {
                 DCHECK(false) << "Unexpected linker patch type: " << patch.GetType();
                 break;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 794e05c..4955562 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -16,6 +16,7 @@
 
 #include "code_generator_arm64.h"
 
+#include "arch/arm64/asm_support_arm64.h"
 #include "arch/arm64/instruction_set_features_arm64.h"
 #include "art_method.h"
 #include "code_generator_utils.h"
@@ -25,6 +26,7 @@
 #include "gc/accounting/card_table.h"
 #include "intrinsics.h"
 #include "intrinsics_arm64.h"
+#include "linker/arm64/relative_patcher_arm64.h"
 #include "mirror/array-inl.h"
 #include "mirror/class-inl.h"
 #include "offsets.h"
@@ -68,6 +70,7 @@
 using helpers::OutputCPURegister;
 using helpers::OutputFPRegister;
 using helpers::OutputRegister;
+using helpers::QRegisterFrom;
 using helpers::RegisterFrom;
 using helpers::StackOperandFrom;
 using helpers::VIXLRegCodeFromART;
@@ -80,6 +83,27 @@
 // generates less code/data with a small num_entries.
 static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
 
+// Reference load (except object array loads) is using LDR Wt, [Xn, #offset] which can handle
+// offset < 16KiB. For offsets >= 16KiB, the load shall be emitted as two or more instructions.
+// For the Baker read barrier implementation using link-generated thunks we need to split
+// the offset explicitly.
+constexpr uint32_t kReferenceLoadMinFarOffset = 16 * KB;
+
+// Flags controlling the use of link-time generated thunks for Baker read barriers.
+// Not yet implemented for heap poisoning.
+constexpr bool kBakerReadBarrierLinkTimeThunksEnableForFields = !kPoisonHeapReferences;
+constexpr bool kBakerReadBarrierLinkTimeThunksEnableForGcRoots = !kPoisonHeapReferences;
+
+// Some instructions have special requirements for a temporary, for example
+// LoadClass/kBssEntry and LoadString/kBssEntry for Baker read barrier require
+// temp that's not an R0 (to avoid an extra move) and Baker read barrier field
+// loads with large offsets need a fixed register to limit the number of link-time
+// thunks we generate. For these and similar cases, we want to reserve a specific
+// register that's neither callee-save nor an argument register. We choose x15.
+inline Location FixedTempLocation() {
+  return Location::RegisterLocation(x15.GetCode());
+}
+
 inline Condition ARM64Condition(IfCondition cond) {
   switch (cond) {
     case kCondEQ: return eq;
@@ -297,23 +321,22 @@
     constexpr bool call_saves_everything_except_r0_ip0 = (!kUseReadBarrier || kUseBakerReadBarrier);
     CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
 
-    // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the page address of
-    // the entry which is in a scratch register. Make sure it's not used for saving/restoring
-    // registers. Exclude the scratch register also for non-Baker read barrier for simplicity.
+    InvokeRuntimeCallingConvention calling_convention;
+    // For HLoadClass/kBssEntry/kSaveEverything, the page address of the entry is in a temp
+    // register, make sure it's not clobbered by the call or by saving/restoring registers.
     DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
     bool is_load_class_bss_entry =
         (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
-    UseScratchRegisterScope temps(arm64_codegen->GetVIXLAssembler());
     if (is_load_class_bss_entry) {
-      // This temp is a scratch register.
       DCHECK(bss_entry_temp_.IsValid());
-      temps.Exclude(bss_entry_temp_);
+      DCHECK(!bss_entry_temp_.Is(calling_convention.GetRegisterAt(0)));
+      DCHECK(
+          !UseScratchRegisterScope(arm64_codegen->GetVIXLAssembler()).IsAvailable(bss_entry_temp_));
     }
 
     __ Bind(GetEntryLabel());
     SaveLiveRegisters(codegen, locations);
 
-    InvokeRuntimeCallingConvention calling_convention;
     dex::TypeIndex type_index = cls_->GetTypeIndex();
     __ Mov(calling_convention.GetRegisterAt(0).W(), type_index.index_);
     QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
@@ -386,14 +409,15 @@
     DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
     CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
 
-    // temp_ is a scratch register. Make sure it's not used for saving/restoring registers.
-    UseScratchRegisterScope temps(arm64_codegen->GetVIXLAssembler());
-    temps.Exclude(temp_);
+    InvokeRuntimeCallingConvention calling_convention;
+    // Make sure `temp_` is not clobbered by the call or by saving/restoring registers.
+    DCHECK(temp_.IsValid());
+    DCHECK(!temp_.Is(calling_convention.GetRegisterAt(0)));
+    DCHECK(!UseScratchRegisterScope(arm64_codegen->GetVIXLAssembler()).IsAvailable(temp_));
 
     __ Bind(GetEntryLabel());
     SaveLiveRegisters(codegen, locations);
 
-    InvokeRuntimeCallingConvention calling_convention;
     const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
     __ Mov(calling_convention.GetRegisterAt(0).W(), string_index.index_);
     arm64_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
@@ -1415,6 +1439,7 @@
                                graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       type_bss_entry_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+      baker_read_barrier_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       jit_string_patches_(StringReferenceValueComparator(),
                           graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
       jit_class_patches_(TypeReferenceValueComparator(),
@@ -1459,9 +1484,12 @@
 }
 
 Location ParallelMoveResolverARM64::AllocateScratchLocationFor(Location::Kind kind) {
-  DCHECK(kind == Location::kRegister || kind == Location::kFpuRegister ||
-         kind == Location::kStackSlot || kind == Location::kDoubleStackSlot);
-  kind = (kind == Location::kFpuRegister) ? Location::kFpuRegister : Location::kRegister;
+  DCHECK(kind == Location::kRegister || kind == Location::kFpuRegister
+         || kind == Location::kStackSlot || kind == Location::kDoubleStackSlot
+         || kind == Location::kSIMDStackSlot);
+  kind = (kind == Location::kFpuRegister || kind == Location::kSIMDStackSlot)
+      ? Location::kFpuRegister
+      : Location::kRegister;
   Location scratch = GetScratchLocation(kind);
   if (!scratch.Equals(Location::NoLocation())) {
     return scratch;
@@ -1471,7 +1499,9 @@
     scratch = LocationFrom(vixl_temps_.AcquireX());
   } else {
     DCHECK(kind == Location::kFpuRegister);
-    scratch = LocationFrom(vixl_temps_.AcquireD());
+    scratch = LocationFrom(codegen_->GetGraph()->HasSIMD()
+        ? vixl_temps_.AcquireVRegisterOfSize(kQRegSize)
+        : vixl_temps_.AcquireD());
   }
   AddScratchLocation(scratch);
   return scratch;
@@ -1482,7 +1512,7 @@
     vixl_temps_.Release(XRegisterFrom(loc));
   } else {
     DCHECK(loc.IsFpuRegister());
-    vixl_temps_.Release(DRegisterFrom(loc));
+    vixl_temps_.Release(codegen_->GetGraph()->HasSIMD() ? QRegisterFrom(loc) : DRegisterFrom(loc));
   }
   RemoveScratchLocation(loc);
 }
@@ -1745,6 +1775,8 @@
     if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
       DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
       __ Ldr(dst, StackOperandFrom(source));
+    } else if (source.IsSIMDStackSlot()) {
+      __ Ldr(QRegisterFrom(destination), StackOperandFrom(source));
     } else if (source.IsConstant()) {
       DCHECK(CoherentConstantAndType(source, dst_type));
       MoveConstant(dst, source.GetConstant());
@@ -1767,7 +1799,29 @@
         __ Fmov(RegisterFrom(destination, dst_type), FPRegisterFrom(source, source_type));
       } else {
         DCHECK(destination.IsFpuRegister());
-        __ Fmov(FPRegister(dst), FPRegisterFrom(source, dst_type));
+        if (GetGraph()->HasSIMD()) {
+          __ Mov(QRegisterFrom(destination), QRegisterFrom(source));
+        } else {
+          __ Fmov(FPRegister(dst), FPRegisterFrom(source, dst_type));
+        }
+      }
+    }
+  } else if (destination.IsSIMDStackSlot()) {
+    if (source.IsFpuRegister()) {
+      __ Str(QRegisterFrom(source), StackOperandFrom(destination));
+    } else {
+      DCHECK(source.IsSIMDStackSlot());
+      UseScratchRegisterScope temps(GetVIXLAssembler());
+      if (GetVIXLAssembler()->GetScratchFPRegisterList()->IsEmpty()) {
+        Register temp = temps.AcquireX();
+        __ Ldr(temp, MemOperand(sp, source.GetStackIndex()));
+        __ Str(temp, MemOperand(sp, destination.GetStackIndex()));
+        __ Ldr(temp, MemOperand(sp, source.GetStackIndex() + kArm64WordSize));
+        __ Str(temp, MemOperand(sp, destination.GetStackIndex() + kArm64WordSize));
+      } else {
+        FPRegister temp = temps.AcquireVRegisterOfSize(kQRegSize);
+        __ Ldr(temp, StackOperandFrom(source));
+        __ Str(temp, StackOperandFrom(destination));
       }
     }
   } else {  // The destination is not a register. It must be a stack slot.
@@ -2206,7 +2260,8 @@
   }
 }
 
-void LocationsBuilderARM64::HandleFieldGet(HInstruction* instruction) {
+void LocationsBuilderARM64::HandleFieldGet(HInstruction* instruction,
+                                           const FieldInfo& field_info) {
   DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
 
   bool object_field_get_with_read_barrier =
@@ -2220,7 +2275,17 @@
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
     // We need a temporary register for the read barrier marking slow
     // path in CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier.
-    locations->AddTemp(Location::RequiresRegister());
+    if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
+        !Runtime::Current()->UseJitCompilation() &&
+        !field_info.IsVolatile()) {
+      // If link-time thunks for the Baker read barrier are enabled, for AOT
+      // non-volatile loads we need a temporary only if the offset is too big.
+      if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
+        locations->AddTemp(FixedTempLocation());
+      }
+    } else {
+      locations->AddTemp(Location::RequiresRegister());
+    }
   }
   locations->SetInAt(0, Location::RequiresRegister());
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
@@ -2249,7 +2314,8 @@
     // Object FieldGet with Baker's read barrier case.
     // /* HeapReference<Object> */ out = *(base + offset)
     Register base = RegisterFrom(base_loc, Primitive::kPrimNot);
-    Register temp = WRegisterFrom(locations->GetTemp(0));
+    Location maybe_temp =
+        (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location::NoLocation();
     // Note that potential implicit null checks are handled in this
     // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier call.
     codegen_->GenerateFieldLoadWithBakerReadBarrier(
@@ -2257,7 +2323,7 @@
         out,
         base,
         offset,
-        temp,
+        maybe_temp,
         /* needs_null_check */ true,
         field_info.IsVolatile());
   } else {
@@ -2642,7 +2708,21 @@
     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers.
     // We need a temporary register for the read barrier marking slow
     // path in CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier.
-    locations->AddTemp(Location::RequiresRegister());
+    if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
+        !Runtime::Current()->UseJitCompilation() &&
+        instruction->GetIndex()->IsConstant()) {
+      // Array loads with constant index are treated as field loads.
+      // If link-time thunks for the Baker read barrier are enabled, for AOT
+      // constant index loads we need a temporary only if the offset is too big.
+      uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
+      uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
+      offset += index << Primitive::ComponentSizeShift(Primitive::kPrimNot);
+      if (offset >= kReferenceLoadMinFarOffset) {
+        locations->AddTemp(FixedTempLocation());
+      }
+    } else {
+      locations->AddTemp(Location::RequiresRegister());
+    }
   }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
@@ -2678,11 +2758,25 @@
 
   if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
     // Object ArrayGet with Baker's read barrier case.
-    Register temp = WRegisterFrom(locations->GetTemp(0));
     // Note that a potential implicit null check is handled in the
     // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call.
-    codegen_->GenerateArrayLoadWithBakerReadBarrier(
-        instruction, out, obj.W(), offset, index, temp, /* needs_null_check */ true);
+    if (index.IsConstant()) {
+      // Array load with a constant index can be treated as a field load.
+      offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type);
+      Location maybe_temp =
+          (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location::NoLocation();
+      codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
+                                                      out,
+                                                      obj.W(),
+                                                      offset,
+                                                      maybe_temp,
+                                                      /* needs_null_check */ true,
+                                                      /* use_load_acquire */ false);
+    } else {
+      Register temp = WRegisterFrom(locations->GetTemp(0));
+      codegen_->GenerateArrayLoadWithBakerReadBarrier(
+          instruction, out, obj.W(), offset, index, temp, /* needs_null_check */ true);
+    }
   } else {
     // General case.
     MemOperand source = HeapOperand(obj);
@@ -3712,7 +3806,7 @@
 }
 
 void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  HandleFieldGet(instruction);
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
 }
 
 void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -4514,6 +4608,11 @@
   return NewPcRelativePatch(dex_file, element_offset, adrp_label, &pc_relative_dex_cache_patches_);
 }
 
+vixl::aarch64::Label* CodeGeneratorARM64::NewBakerReadBarrierPatch(uint32_t custom_data) {
+  baker_read_barrier_patches_.emplace_back(custom_data);
+  return &baker_read_barrier_patches_.back().label;
+}
+
 vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativePatch(
     const DexFile& dex_file,
     uint32_t offset_or_index,
@@ -4612,7 +4711,8 @@
       pc_relative_string_patches_.size() +
       boot_image_type_patches_.size() +
       pc_relative_type_patches_.size() +
-      type_bss_entry_patches_.size();
+      type_bss_entry_patches_.size() +
+      baker_read_barrier_patches_.size();
   linker_patches->reserve(size);
   for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) {
     linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(info.label.GetLocation(),
@@ -4646,6 +4746,10 @@
                                                      target_type.dex_file,
                                                      target_type.type_index.index_));
   }
+  for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
+    linker_patches->push_back(LinkerPatch::BakerReadBarrierBranchPatch(info.label.GetLocation(),
+                                                                       info.custom_data));
+  }
   DCHECK_EQ(size, linker_patches->size());
 }
 
@@ -4758,8 +4862,7 @@
   if (cls->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
     if (!kUseReadBarrier || kUseBakerReadBarrier) {
       // Rely on the type resolution or initialization and marking to save everything we need.
-      // Note that IP0 may be clobbered by saving/restoring the live register (only one thanks
-      // to the custom calling convention) or by marking, so we shall use IP1.
+      locations->AddTemp(FixedTempLocation());
       RegisterSet caller_saves = RegisterSet::Empty();
       InvokeRuntimeCallingConvention calling_convention;
       caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
@@ -4836,11 +4939,7 @@
       // Add ADRP with its PC-relative Class .bss entry patch.
       const DexFile& dex_file = cls->GetDexFile();
       dex::TypeIndex type_index = cls->GetTypeIndex();
-      // We can go to slow path even with non-zero reference and in that case marking
-      // can clobber IP0, so we need to use IP1 which shall be preserved.
-      bss_entry_temp = ip1;
-      UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
-      temps.Exclude(bss_entry_temp);
+      bss_entry_temp = XRegisterFrom(cls->GetLocations()->GetTemp(0));
       bss_entry_adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index);
       codegen_->EmitAdrpPlaceholder(bss_entry_adrp_label, bss_entry_temp);
       // Add LDR with its PC-relative Class patch.
@@ -4947,8 +5046,7 @@
     if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) {
       if (!kUseReadBarrier || kUseBakerReadBarrier) {
         // Rely on the pResolveString and marking to save everything we need.
-        // Note that IP0 may be clobbered by saving/restoring the live register (only one thanks
-        // to the custom calling convention) or by marking, so we shall use IP1.
+        locations->AddTemp(FixedTempLocation());
         RegisterSet caller_saves = RegisterSet::Empty();
         InvokeRuntimeCallingConvention calling_convention;
         caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
@@ -4999,11 +5097,7 @@
       const DexFile& dex_file = load->GetDexFile();
       const dex::StringIndex string_index = load->GetStringIndex();
       DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
-      // We could use IP0 as the marking shall not clobber IP0 if the reference is null and
-      // that's when we need the slow path. But let's not rely on such details and use IP1.
-      Register temp = ip1;
-      UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
-      temps.Exclude(temp);
+      Register temp = XRegisterFrom(load->GetLocations()->GetTemp(0));
       vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index);
       codegen_->EmitAdrpPlaceholder(adrp_label, temp);
       // Add LDR with its PC-relative String patch.
@@ -5438,7 +5532,7 @@
 }
 
 void LocationsBuilderARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
-  HandleFieldGet(instruction);
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
 }
 
 void InstructionCodeGeneratorARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
@@ -5747,7 +5841,6 @@
   Register out_reg = RegisterFrom(out, type);
   if (read_barrier_option == kWithReadBarrier) {
     CHECK(kEmitCompilerReadBarrier);
-    Register temp_reg = RegisterFrom(maybe_temp, type);
     if (kUseBakerReadBarrier) {
       // Load with fast path based Baker's read barrier.
       // /* HeapReference<Object> */ out = *(out + offset)
@@ -5755,7 +5848,7 @@
                                                       out,
                                                       out_reg,
                                                       offset,
-                                                      temp_reg,
+                                                      maybe_temp,
                                                       /* needs_null_check */ false,
                                                       /* use_load_acquire */ false);
     } else {
@@ -5763,6 +5856,7 @@
       // Save the value of `out` into `maybe_temp` before overwriting it
       // in the following move operation, as we will need it for the
       // read barrier below.
+      Register temp_reg = RegisterFrom(maybe_temp, type);
       __ Mov(temp_reg, out_reg);
       // /* HeapReference<Object> */ out = *(out + offset)
       __ Ldr(out_reg, HeapOperand(out_reg, offset));
@@ -5790,13 +5884,12 @@
     CHECK(kEmitCompilerReadBarrier);
     if (kUseBakerReadBarrier) {
       // Load with fast path based Baker's read barrier.
-      Register temp_reg = RegisterFrom(maybe_temp, type);
       // /* HeapReference<Object> */ out = *(obj + offset)
       codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
                                                       out,
                                                       obj_reg,
                                                       offset,
-                                                      temp_reg,
+                                                      maybe_temp,
                                                       /* needs_null_check */ false,
                                                       /* use_load_acquire */ false);
     } else {
@@ -5827,52 +5920,97 @@
     if (kUseBakerReadBarrier) {
       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
       // Baker's read barrier are used.
-      //
-      // Note that we do not actually check the value of
-      // `GetIsGcMarking()` to decide whether to mark the loaded GC
-      // root or not.  Instead, we load into `temp` the read barrier
-      // mark entry point corresponding to register `root`. If `temp`
-      // is null, it means that `GetIsGcMarking()` is false, and vice
-      // versa.
-      //
-      //   temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-      //   GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
-      //   if (temp != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
-      //     // Slow path.
-      //     root = temp(root);  // root = ReadBarrier::Mark(root);  // Runtime entry point call.
-      //   }
+      if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots &&
+          !Runtime::Current()->UseJitCompilation()) {
+        // Note that we do not actually check the value of `GetIsGcMarking()`
+        // to decide whether to mark the loaded GC root or not.  Instead, we
+        // load into `temp` the read barrier mark introspection entrypoint.
+        // If `temp` is null, it means that `GetIsGcMarking()` is false, and
+        // vice versa.
+        //
+        // We use link-time generated thunks for the slow path. That thunk
+        // checks the reference and jumps to the entrypoint if needed.
+        //
+        //     temp = Thread::Current()->pReadBarrierMarkIntrospection
+        //     lr = &return_address;
+        //     GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
+        //     if (temp != nullptr) {
+        //        goto gc_root_thunk<root_reg>(lr)
+        //     }
+        //   return_address:
 
-      // Slow path marking the GC root `root`. The entrypoint will already be loaded in `temp`.
-      Register temp = lr;
-      SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(
-          instruction, root, /* entrypoint */ LocationFrom(temp));
-      codegen_->AddSlowPath(slow_path);
+        UseScratchRegisterScope temps(GetVIXLAssembler());
+        DCHECK(temps.IsAvailable(ip0));
+        DCHECK(temps.IsAvailable(ip1));
+        temps.Exclude(ip0, ip1);
+        uint32_t custom_data =
+            linker::Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg.GetCode());
+        vixl::aarch64::Label* cbnz_label = codegen_->NewBakerReadBarrierPatch(custom_data);
 
-      // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
-      const int32_t entry_point_offset =
-          CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(root.reg());
-      // Loading the entrypoint does not require a load acquire since it is only changed when
-      // threads are suspended or running a checkpoint.
-      __ Ldr(temp, MemOperand(tr, entry_point_offset));
-
-      // /* GcRoot<mirror::Object> */ root = *(obj + offset)
-      if (fixup_label == nullptr) {
-        __ Ldr(root_reg, MemOperand(obj, offset));
+        // ip1 = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection.
+        DCHECK_EQ(ip0.GetCode(), 16u);
+        const int32_t entry_point_offset =
+            CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ip0.GetCode());
+        __ Ldr(ip1, MemOperand(tr, entry_point_offset));
+        EmissionCheckScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize);
+        vixl::aarch64::Label return_address;
+        __ adr(lr, &return_address);
+        if (fixup_label != nullptr) {
+          __ Bind(fixup_label);
+        }
+        static_assert(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET == -8,
+                      "GC root LDR must be 2 instruction (8B) before the return address label.");
+        __ ldr(root_reg, MemOperand(obj.X(), offset));
+        __ Bind(cbnz_label);
+        __ cbnz(ip1, static_cast<int64_t>(0));  // Placeholder, patched at link-time.
+        __ Bind(&return_address);
       } else {
-        codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj);
-      }
-      static_assert(
-          sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
-          "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
-          "have different sizes.");
-      static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
-                    "art::mirror::CompressedReference<mirror::Object> and int32_t "
-                    "have different sizes.");
+        // Note that we do not actually check the value of
+        // `GetIsGcMarking()` to decide whether to mark the loaded GC
+        // root or not.  Instead, we load into `temp` the read barrier
+        // mark entry point corresponding to register `root`. If `temp`
+        // is null, it means that `GetIsGcMarking()` is false, and vice
+        // versa.
+        //
+        //   temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+        //   GcRoot<mirror::Object> root = *(obj+offset);  // Original reference load.
+        //   if (temp != nullptr) {  // <=> Thread::Current()->GetIsGcMarking()
+        //     // Slow path.
+        //     root = temp(root);  // root = ReadBarrier::Mark(root);  // Runtime entry point call.
+        //   }
 
-      // The entrypoint is null when the GC is not marking, this prevents one load compared to
-      // checking GetIsGcMarking.
-      __ Cbnz(temp, slow_path->GetEntryLabel());
-      __ Bind(slow_path->GetExitLabel());
+        // Slow path marking the GC root `root`. The entrypoint will already be loaded in `temp`.
+        Register temp = lr;
+        SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(
+            instruction, root, /* entrypoint */ LocationFrom(temp));
+        codegen_->AddSlowPath(slow_path);
+
+        // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+        const int32_t entry_point_offset =
+            CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(root.reg());
+        // Loading the entrypoint does not require a load acquire since it is only changed when
+        // threads are suspended or running a checkpoint.
+        __ Ldr(temp, MemOperand(tr, entry_point_offset));
+
+        // /* GcRoot<mirror::Object> */ root = *(obj + offset)
+        if (fixup_label == nullptr) {
+          __ Ldr(root_reg, MemOperand(obj, offset));
+        } else {
+          codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj);
+        }
+        static_assert(
+            sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
+            "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
+            "have different sizes.");
+        static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
+                      "art::mirror::CompressedReference<mirror::Object> and int32_t "
+                      "have different sizes.");
+
+        // The entrypoint is null when the GC is not marking, this prevents one load compared to
+        // checking GetIsGcMarking.
+        __ Cbnz(temp, slow_path->GetEntryLabel());
+        __ Bind(slow_path->GetExitLabel());
+      }
     } else {
       // GC root loaded through a slow path for read barriers other
       // than Baker's.
@@ -5902,13 +6040,76 @@
                                                                Location ref,
                                                                Register obj,
                                                                uint32_t offset,
-                                                               Register temp,
+                                                               Location maybe_temp,
                                                                bool needs_null_check,
                                                                bool use_load_acquire) {
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
 
+  if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
+      !use_load_acquire &&
+      !Runtime::Current()->UseJitCompilation()) {
+    // Note that we do not actually check the value of `GetIsGcMarking()`
+    // to decide whether to mark the loaded GC root or not.  Instead, we
+    // load into `temp` the read barrier mark introspection entrypoint.
+    // If `temp` is null, it means that `GetIsGcMarking()` is false, and
+    // vice versa.
+    //
+    // We use link-time generated thunks for the slow path. That thunk checks
+    // the holder and jumps to the entrypoint if needed. If the holder is not
+    // gray, it creates a fake dependency and returns to the LDR instruction.
+    //
+    //     temp = Thread::Current()->pReadBarrierMarkIntrospection
+    //     lr = &return_address;
+    //     if (temp != nullptr) {
+    //        goto field_thunk<holder_reg, base_reg>(lr)
+    //     }
+    //   not_gray_return_address:
+    //     // Original reference load. If the offset is too large to fit
+    //     // into LDR, we use an adjusted base register here.
+    //     GcRoot<mirror::Object> root = *(obj+offset);
+    //   gray_return_address:
+
+    DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
+    Register base = obj;
+    if (offset >= kReferenceLoadMinFarOffset) {
+      DCHECK(maybe_temp.IsRegister());
+      base = WRegisterFrom(maybe_temp);
+      static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
+      __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
+      offset &= (kReferenceLoadMinFarOffset - 1u);
+    }
+    UseScratchRegisterScope temps(GetVIXLAssembler());
+    DCHECK(temps.IsAvailable(ip0));
+    DCHECK(temps.IsAvailable(ip1));
+    temps.Exclude(ip0, ip1);
+    uint32_t custom_data = linker::Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(
+        base.GetCode(),
+        obj.GetCode());
+    vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data);
+
+    // ip1 = Thread::Current()->pReadBarrierMarkReg16, i.e. pReadBarrierMarkIntrospection.
+    DCHECK_EQ(ip0.GetCode(), 16u);
+    const int32_t entry_point_offset =
+        CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ip0.GetCode());
+    __ Ldr(ip1, MemOperand(tr, entry_point_offset));
+    EmissionCheckScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize);
+    vixl::aarch64::Label return_address;
+    __ adr(lr, &return_address);
+    __ Bind(cbnz_label);
+    __ cbnz(ip1, static_cast<int64_t>(0));  // Placeholder, patched at link-time.
+    static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == -4,
+                  "Field LDR must be 1 instruction (4B) before the return address label.");
+    __ ldr(RegisterFrom(ref, Primitive::kPrimNot), MemOperand(base.X(), offset));
+    if (needs_null_check) {
+      MaybeRecordImplicitNullCheck(instruction);
+    }
+    __ Bind(&return_address);
+    return;
+  }
+
   // /* HeapReference<Object> */ ref = *(obj + offset)
+  Register temp = WRegisterFrom(maybe_temp);
   Location no_index = Location::NoLocation();
   size_t no_scale_factor = 0u;
   GenerateReferenceLoadWithBakerReadBarrier(instruction,
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 10d8b84..58feea2 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -351,7 +351,7 @@
  private:
   void HandleBinaryOp(HBinaryOperation* instr);
   void HandleFieldSet(HInstruction* instruction);
-  void HandleFieldGet(HInstruction* instruction);
+  void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
   void HandleInvoke(HInvoke* instr);
   void HandleCondition(HCondition* instruction);
   void HandleShift(HBinaryOperation* instr);
@@ -412,8 +412,9 @@
   }
 
   size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
-    // Allocated in D registers, which are word sized.
-    return kArm64WordSize;
+    return GetGraph()->HasSIMD()
+        ? 2 * kArm64WordSize   // 16 bytes == 2 arm64 words for each spill
+        : 1 * kArm64WordSize;  //  8 bytes == 1 arm64 words for each spill
   }
 
   uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
@@ -578,6 +579,10 @@
       uint32_t element_offset,
       vixl::aarch64::Label* adrp_label = nullptr);
 
+  // Add a new baker read barrier patch and return the label to be bound
+  // before the CBNZ instruction.
+  vixl::aarch64::Label* NewBakerReadBarrierPatch(uint32_t custom_data);
+
   vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageStringLiteral(
       const DexFile& dex_file,
       dex::StringIndex string_index);
@@ -609,7 +614,7 @@
                                              Location ref,
                                              vixl::aarch64::Register obj,
                                              uint32_t offset,
-                                             vixl::aarch64::Register temp,
+                                             Location maybe_temp,
                                              bool needs_null_check,
                                              bool use_load_acquire);
   // Fast path implementation of ReadBarrier::Barrier for a heap
@@ -737,6 +742,13 @@
     vixl::aarch64::Label* pc_insn_label;
   };
 
+  struct BakerReadBarrierPatchInfo {
+    explicit BakerReadBarrierPatchInfo(uint32_t data) : label(), custom_data(data) { }
+
+    vixl::aarch64::Label label;
+    uint32_t custom_data;
+  };
+
   vixl::aarch64::Label* NewPcRelativePatch(const DexFile& dex_file,
                                            uint32_t offset_or_index,
                                            vixl::aarch64::Label* adrp_label,
@@ -776,6 +788,8 @@
   ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
   // PC-relative type patch info for kBssEntry.
   ArenaDeque<PcRelativePatchInfo> type_bss_entry_patches_;
+  // Baker read barrier patch info.
+  ArenaDeque<BakerReadBarrierPatchInfo> baker_read_barrier_patches_;
 
   // Patches for string literals in JIT compiled code.
   StringToLiteralMap jit_string_patches_;
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index cce412b..b6678b0 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -3634,8 +3634,8 @@
           } else {
             DCHECK(in.IsConstant());
             DCHECK(in.GetConstant()->IsLongConstant());
-            int32_t value = Int32ConstantFrom(in);
-            __ Mov(OutputRegister(conversion), value);
+            int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
+            __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
           }
           break;
 
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 287891f..aa030b2 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -2509,8 +2509,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
         __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset, null_checker);
       } else {
-        __ Sll(TMP, index.AsRegister<Register>(), TIMES_2);
-        __ Addu(TMP, obj, TMP);
+        __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_2, TMP);
         __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
       }
       break;
@@ -2557,13 +2556,11 @@
           __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
           __ B(&done);
           __ Bind(&uncompressed_load);
-          __ Sll(TMP, index_reg, TIMES_2);
-          __ Addu(TMP, obj, TMP);
+          __ ShiftAndAdd(TMP, index_reg, obj, TIMES_2, TMP);
           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
           __ Bind(&done);
         } else {
-          __ Sll(TMP, index_reg, TIMES_2);
-          __ Addu(TMP, obj, TMP);
+          __ ShiftAndAdd(TMP, index_reg, obj, TIMES_2, TMP);
           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
         }
       }
@@ -2578,8 +2575,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
         __ LoadFromOffset(kLoadWord, out, obj, offset, null_checker);
       } else {
-        __ Sll(TMP, index.AsRegister<Register>(), TIMES_4);
-        __ Addu(TMP, obj, TMP);
+        __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
         __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
       }
       break;
@@ -2613,8 +2609,7 @@
           // reference, if heap poisoning is enabled).
           codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
         } else {
-          __ Sll(TMP, index.AsRegister<Register>(), TIMES_4);
-          __ Addu(TMP, obj, TMP);
+          __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
           __ LoadFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
           // If read barriers are enabled, emit read barriers other than
           // Baker's using a slow path (and also unpoison the loaded
@@ -2637,8 +2632,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
         __ LoadFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
       } else {
-        __ Sll(TMP, index.AsRegister<Register>(), TIMES_8);
-        __ Addu(TMP, obj, TMP);
+        __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_8, TMP);
         __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
       }
       break;
@@ -2651,8 +2645,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
         __ LoadSFromOffset(out, obj, offset, null_checker);
       } else {
-        __ Sll(TMP, index.AsRegister<Register>(), TIMES_4);
-        __ Addu(TMP, obj, TMP);
+        __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_4, TMP);
         __ LoadSFromOffset(out, TMP, data_offset, null_checker);
       }
       break;
@@ -2665,8 +2658,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
         __ LoadDFromOffset(out, obj, offset, null_checker);
       } else {
-        __ Sll(TMP, index.AsRegister<Register>(), TIMES_8);
-        __ Addu(TMP, obj, TMP);
+        __ ShiftAndAdd(TMP, index.AsRegister<Register>(), obj, TIMES_8, TMP);
         __ LoadDFromOffset(out, TMP, data_offset, null_checker);
       }
       break;
@@ -2779,8 +2771,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2;
       } else {
-        __ Sll(base_reg, index.AsRegister<Register>(), TIMES_2);
-        __ Addu(base_reg, obj, base_reg);
+        __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_2, base_reg);
       }
       if (value_location.IsConstant()) {
         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
@@ -2797,8 +2788,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
       } else {
-        __ Sll(base_reg, index.AsRegister<Register>(), TIMES_4);
-        __ Addu(base_reg, obj, base_reg);
+        __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
       }
       if (value_location.IsConstant()) {
         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
@@ -2817,8 +2807,7 @@
         if (index.IsConstant()) {
           data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
         } else {
-          __ Sll(base_reg, index.AsRegister<Register>(), TIMES_4);
-          __ Addu(base_reg, obj, base_reg);
+          __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
         }
         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
         DCHECK_EQ(value, 0);
@@ -2848,8 +2837,7 @@
           if (index.IsConstant()) {
             data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
           } else {
-            __ Sll(base_reg, index.AsRegister<Register>(), TIMES_4);
-            __ Addu(base_reg, obj, base_reg);
+            __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
           }
           __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
           __ B(&done);
@@ -2907,8 +2895,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
       } else {
-        __ Sll(base_reg, index.AsRegister<Register>(), TIMES_4);
-        __ Addu(base_reg, obj, base_reg);
+        __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
       }
       __ StoreToOffset(kStoreWord, source, base_reg, data_offset);
 
@@ -2933,8 +2920,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
       } else {
-        __ Sll(base_reg, index.AsRegister<Register>(), TIMES_8);
-        __ Addu(base_reg, obj, base_reg);
+        __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_8, base_reg);
       }
       if (value_location.IsConstant()) {
         int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
@@ -2951,8 +2937,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
       } else {
-        __ Sll(base_reg, index.AsRegister<Register>(), TIMES_4);
-        __ Addu(base_reg, obj, base_reg);
+        __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_4, base_reg);
       }
       if (value_location.IsConstant()) {
         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
@@ -2969,8 +2954,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
       } else {
-        __ Sll(base_reg, index.AsRegister<Register>(), TIMES_8);
-        __ Addu(base_reg, obj, base_reg);
+        __ ShiftAndAdd(base_reg, index.AsRegister<Register>(), obj, TIMES_8, base_reg);
       }
       if (value_location.IsConstant()) {
         int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
@@ -6351,8 +6335,7 @@
       Register index_reg = index.IsRegisterPair()
           ? index.AsRegisterPairLow<Register>()
           : index.AsRegister<Register>();
-      __ Sll(TMP, index_reg, scale_factor);
-      __ Addu(TMP, obj, TMP);
+      __ ShiftAndAdd(TMP, index_reg, obj, scale_factor, TMP);
       __ LoadFromOffset(kLoadWord, ref_reg, TMP, offset);
     }
   } else {
@@ -8446,8 +8429,7 @@
   // We are in the range of the table.
   // Load the target address from the jump table, indexing by the value.
   __ LoadLabelAddress(AT, constant_area, table->GetLabel());
-  __ Sll(TMP, TMP, 2);
-  __ Addu(TMP, TMP, AT);
+  __ ShiftAndAdd(TMP, TMP, AT, 2, TMP);
   __ Lw(TMP, TMP, 0);
   // Compute the absolute target address by adding the table start address
   // (the table contains offsets to targets relative to its start).
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 78b31e9..19250c6 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -2059,8 +2059,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
         __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset, null_checker);
       } else {
-        __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2);
-        __ Daddu(TMP, obj, TMP);
+        __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_2);
         __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker);
       }
       break;
@@ -2107,13 +2106,11 @@
           __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
           __ Bc(&done);
           __ Bind(&uncompressed_load);
-          __ Dsll(TMP, index_reg, TIMES_2);
-          __ Daddu(TMP, obj, TMP);
+          __ Dlsa(TMP, index_reg, obj, TIMES_2);
           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
           __ Bind(&done);
         } else {
-          __ Dsll(TMP, index_reg, TIMES_2);
-          __ Daddu(TMP, obj, TMP);
+          __ Dlsa(TMP, index_reg, obj, TIMES_2);
           __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
         }
       }
@@ -2129,8 +2126,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
         __ LoadFromOffset(load_type, out, obj, offset, null_checker);
       } else {
-        __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4);
-        __ Daddu(TMP, obj, TMP);
+        __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_4);
         __ LoadFromOffset(load_type, out, TMP, data_offset, null_checker);
       }
       break;
@@ -2164,8 +2160,7 @@
           // reference, if heap poisoning is enabled).
           codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
         } else {
-          __ Sll(TMP, index.AsRegister<GpuRegister>(), TIMES_4);
-          __ Addu(TMP, obj, TMP);
+          __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_4);
           __ LoadFromOffset(kLoadUnsignedWord, out, TMP, data_offset, null_checker);
           // If read barriers are enabled, emit read barriers other than
           // Baker's using a slow path (and also unpoison the loaded
@@ -2188,8 +2183,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
         __ LoadFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
       } else {
-        __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8);
-        __ Daddu(TMP, obj, TMP);
+        __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_8);
         __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
       }
       break;
@@ -2202,8 +2196,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
         __ LoadFpuFromOffset(kLoadWord, out, obj, offset, null_checker);
       } else {
-        __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4);
-        __ Daddu(TMP, obj, TMP);
+        __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_4);
         __ LoadFpuFromOffset(kLoadWord, out, TMP, data_offset, null_checker);
       }
       break;
@@ -2216,8 +2209,7 @@
             (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
         __ LoadFpuFromOffset(kLoadDoubleword, out, obj, offset, null_checker);
       } else {
-        __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8);
-        __ Daddu(TMP, obj, TMP);
+        __ Dlsa(TMP, index.AsRegister<GpuRegister>(), obj, TIMES_8);
         __ LoadFpuFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker);
       }
       break;
@@ -2330,8 +2322,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2;
       } else {
-        __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_2);
-        __ Daddu(base_reg, obj, base_reg);
+        __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_2);
       }
       if (value_location.IsConstant()) {
         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
@@ -2348,8 +2339,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
       } else {
-        __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_4);
-        __ Daddu(base_reg, obj, base_reg);
+        __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
       }
       if (value_location.IsConstant()) {
         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
@@ -2368,8 +2358,7 @@
         if (index.IsConstant()) {
           data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
         } else {
-          __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_4);
-          __ Daddu(base_reg, obj, base_reg);
+          __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
         }
         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
         DCHECK_EQ(value, 0);
@@ -2399,8 +2388,7 @@
           if (index.IsConstant()) {
             data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
           } else {
-            __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_4);
-            __ Daddu(base_reg, obj, base_reg);
+            __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
           }
           __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker);
           __ Bc(&done);
@@ -2458,8 +2446,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
       } else {
-        __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_4);
-        __ Daddu(base_reg, obj, base_reg);
+        __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
       }
       __ StoreToOffset(kStoreWord, source, base_reg, data_offset);
 
@@ -2484,8 +2471,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
       } else {
-        __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_8);
-        __ Daddu(base_reg, obj, base_reg);
+        __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_8);
       }
       if (value_location.IsConstant()) {
         int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
@@ -2502,8 +2488,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4;
       } else {
-        __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_4);
-        __ Daddu(base_reg, obj, base_reg);
+        __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_4);
       }
       if (value_location.IsConstant()) {
         int32_t value = CodeGenerator::GetInt32ValueOf(value_location.GetConstant());
@@ -2520,8 +2505,7 @@
       if (index.IsConstant()) {
         data_offset += index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8;
       } else {
-        __ Dsll(base_reg, index.AsRegister<GpuRegister>(), TIMES_8);
-        __ Daddu(base_reg, obj, base_reg);
+        __ Dlsa(base_reg, index.AsRegister<GpuRegister>(), obj, TIMES_8);
       }
       if (value_location.IsConstant()) {
         int64_t value = CodeGenerator::GetInt64ValueOf(value_location.GetConstant());
@@ -4447,8 +4431,11 @@
       __ LoadFromOffset(kLoadUnsignedWord, ref_reg, obj, computed_offset);
     } else {
       GpuRegister index_reg = index.AsRegister<GpuRegister>();
-      __ Dsll(TMP, index_reg, scale_factor);
-      __ Daddu(TMP, obj, TMP);
+      if (scale_factor == TIMES_1) {
+        __ Daddu(TMP, index_reg, obj);
+      } else {
+        __ Dlsa(TMP, index_reg, obj, scale_factor);
+      }
       __ LoadFromOffset(kLoadUnsignedWord, ref_reg, TMP, offset);
     }
   } else {
@@ -6203,8 +6190,7 @@
   // We are in the range of the table.
   // Load the target address from the jump table, indexing by the value.
   __ LoadLabelAddress(AT, table->GetLabel());
-  __ Sll(TMP, TMP, 2);
-  __ Daddu(TMP, TMP, AT);
+  __ Dlsa(TMP, TMP, AT, 2);
   __ Lw(TMP, TMP, 0);
   // Compute the absolute target address by adding the table start address
   // (the table contains offsets to targets relative to its start).
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index f4874fe..0923920 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -22,7 +22,7 @@
 namespace art {
 namespace arm64 {
 
-using helpers::DRegisterFrom;
+using helpers::VRegisterFrom;
 using helpers::HeapOperand;
 using helpers::InputRegisterAt;
 using helpers::Int64ConstantFrom;
@@ -38,10 +38,12 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetOut(Location::RequiresFpuRegister());
       break;
     case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
       locations->SetInAt(0, Location::RequiresFpuRegister());
       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
       break;
@@ -53,25 +55,33 @@
 
 void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Dup(dst.V8B(), InputRegisterAt(instruction, 0));
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Dup(dst.V16B(), InputRegisterAt(instruction, 0));
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Dup(dst.V4H(), InputRegisterAt(instruction, 0));
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Dup(dst.V8H(), InputRegisterAt(instruction, 0));
       break;
     case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Dup(dst.V4S(), InputRegisterAt(instruction, 0));
+      break;
+    case Primitive::kPrimLong:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Dup(dst.V2S(), InputRegisterAt(instruction, 0));
+      __ Dup(dst.V2D(), XRegisterFrom(locations->InAt(0)));
       break;
     case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Dup(dst.V4S(), VRegisterFrom(locations->InAt(0)).V4S(), 0);
+      break;
+    case Primitive::kPrimDouble:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Dup(dst.V2S(), DRegisterFrom(locations->InAt(0)).V2S(), 0);
+      __ Dup(dst.V2D(), VRegisterFrom(locations->InAt(0)).V2D(), 0);
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -109,7 +119,9 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
       locations->SetInAt(0, Location::RequiresFpuRegister());
       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
       break;
@@ -125,13 +137,13 @@
 
 void InstructionCodeGeneratorARM64::VisitVecCnv(HVecCnv* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister src = DRegisterFrom(locations->InAt(0));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister src = VRegisterFrom(locations->InAt(0));
+  VRegister dst = VRegisterFrom(locations->Out());
   Primitive::Type from = instruction->GetInputType();
   Primitive::Type to = instruction->GetResultType();
   if (from == Primitive::kPrimInt && to == Primitive::kPrimFloat) {
-    DCHECK_EQ(2u, instruction->GetVectorLength());
-    __ Scvtf(dst.V2S(), src.V2S());
+    DCHECK_EQ(4u, instruction->GetVectorLength());
+    __ Scvtf(dst.V4S(), src.V4S());
   } else {
     LOG(FATAL) << "Unsupported SIMD type";
   }
@@ -143,25 +155,33 @@
 
 void InstructionCodeGeneratorARM64::VisitVecNeg(HVecNeg* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister src = DRegisterFrom(locations->InAt(0));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister src = VRegisterFrom(locations->InAt(0));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Neg(dst.V8B(), src.V8B());
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Neg(dst.V16B(), src.V16B());
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Neg(dst.V4H(), src.V4H());
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Neg(dst.V8H(), src.V8H());
       break;
     case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Neg(dst.V4S(), src.V4S());
+      break;
+    case Primitive::kPrimLong:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Neg(dst.V2S(), src.V2S());
+      __ Neg(dst.V2D(), src.V2D());
       break;
     case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Fneg(dst.V4S(), src.V4S());
+      break;
+    case Primitive::kPrimDouble:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Fneg(dst.V2S(), src.V2S());
+      __ Fneg(dst.V2D(), src.V2D());
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -175,25 +195,33 @@
 
 void InstructionCodeGeneratorARM64::VisitVecAbs(HVecAbs* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister src = DRegisterFrom(locations->InAt(0));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister src = VRegisterFrom(locations->InAt(0));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Abs(dst.V8B(), src.V8B());
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Abs(dst.V16B(), src.V16B());
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Abs(dst.V4H(), src.V4H());
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Abs(dst.V8H(), src.V8H());
       break;
     case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Abs(dst.V4S(), src.V4S());
+      break;
+    case Primitive::kPrimLong:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Abs(dst.V2S(), src.V2S());
+      __ Abs(dst.V2D(), src.V2D());
       break;
     case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Fabs(dst.V4S(), src.V4S());
+      break;
+    case Primitive::kPrimDouble:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Fabs(dst.V2S(), src.V2S());
+      __ Fabs(dst.V2D(), src.V2D());
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -206,19 +234,20 @@
 
 void InstructionCodeGeneratorARM64::VisitVecNot(HVecNot* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister src = DRegisterFrom(locations->InAt(0));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister src = VRegisterFrom(locations->InAt(0));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:  // special case boolean-not
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Movi(dst.V8B(), 1);
-      __ Eor(dst.V8B(), dst.V8B(), src.V8B());
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Movi(dst.V16B(), 1);
+      __ Eor(dst.V16B(), dst.V16B(), src.V16B());
       break;
     case Primitive::kPrimByte:
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
-      __ Not(dst.V8B(), src.V8B());  // lanes do not matter
+    case Primitive::kPrimLong:
+      __ Not(dst.V16B(), src.V16B());  // lanes do not matter
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -235,7 +264,9 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
       locations->SetInAt(0, Location::RequiresFpuRegister());
       locations->SetInAt(1, Location::RequiresFpuRegister());
       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
@@ -252,26 +283,34 @@
 
 void InstructionCodeGeneratorARM64::VisitVecAdd(HVecAdd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister rhs = DRegisterFrom(locations->InAt(1));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister rhs = VRegisterFrom(locations->InAt(1));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Add(dst.V8B(), lhs.V8B(), rhs.V8B());
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Add(dst.V16B(), lhs.V16B(), rhs.V16B());
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Add(dst.V4H(), lhs.V4H(), rhs.V4H());
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Add(dst.V8H(), lhs.V8H(), rhs.V8H());
       break;
     case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Add(dst.V4S(), lhs.V4S(), rhs.V4S());
+      break;
+    case Primitive::kPrimLong:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Add(dst.V2S(), lhs.V2S(), rhs.V2S());
+      __ Add(dst.V2D(), lhs.V2D(), rhs.V2D());
       break;
     case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Fadd(dst.V4S(), lhs.V4S(), rhs.V4S());
+      break;
+    case Primitive::kPrimDouble:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Fadd(dst.V2S(), lhs.V2S(), rhs.V2S());
+      __ Fadd(dst.V2D(), lhs.V2D(), rhs.V2D());
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -285,26 +324,34 @@
 
 void InstructionCodeGeneratorARM64::VisitVecSub(HVecSub* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister rhs = DRegisterFrom(locations->InAt(1));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister rhs = VRegisterFrom(locations->InAt(1));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Sub(dst.V8B(), lhs.V8B(), rhs.V8B());
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Sub(dst.V16B(), lhs.V16B(), rhs.V16B());
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Sub(dst.V4H(), lhs.V4H(), rhs.V4H());
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Sub(dst.V8H(), lhs.V8H(), rhs.V8H());
       break;
     case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Sub(dst.V4S(), lhs.V4S(), rhs.V4S());
+      break;
+    case Primitive::kPrimLong:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Sub(dst.V2S(), lhs.V2S(), rhs.V2S());
+      __ Sub(dst.V2D(), lhs.V2D(), rhs.V2D());
       break;
     case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Fsub(dst.V4S(), lhs.V4S(), rhs.V4S());
+      break;
+    case Primitive::kPrimDouble:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Fsub(dst.V2S(), lhs.V2S(), rhs.V2S());
+      __ Fsub(dst.V2D(), lhs.V2D(), rhs.V2D());
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -318,26 +365,30 @@
 
 void InstructionCodeGeneratorARM64::VisitVecMul(HVecMul* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister rhs = DRegisterFrom(locations->InAt(1));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister rhs = VRegisterFrom(locations->InAt(1));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Mul(dst.V8B(), lhs.V8B(), rhs.V8B());
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Mul(dst.V16B(), lhs.V16B(), rhs.V16B());
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Mul(dst.V4H(), lhs.V4H(), rhs.V4H());
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Mul(dst.V8H(), lhs.V8H(), rhs.V8H());
       break;
     case Primitive::kPrimInt:
-      DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Mul(dst.V2S(), lhs.V2S(), rhs.V2S());
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Mul(dst.V4S(), lhs.V4S(), rhs.V4S());
       break;
     case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Fmul(dst.V4S(), lhs.V4S(), rhs.V4S());
+      break;
+    case Primitive::kPrimDouble:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Fmul(dst.V2S(), lhs.V2S(), rhs.V2S());
+      __ Fmul(dst.V2D(), lhs.V2D(), rhs.V2D());
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -351,13 +402,17 @@
 
 void InstructionCodeGeneratorARM64::VisitVecDiv(HVecDiv* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister rhs = DRegisterFrom(locations->InAt(1));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister rhs = VRegisterFrom(locations->InAt(1));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Fdiv(dst.V4S(), lhs.V4S(), rhs.V4S());
+      break;
+    case Primitive::kPrimDouble:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Fdiv(dst.V2S(), lhs.V2S(), rhs.V2S());
+      __ Fdiv(dst.V2D(), lhs.V2D(), rhs.V2D());
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -371,17 +426,19 @@
 
 void InstructionCodeGeneratorARM64::VisitVecAnd(HVecAnd* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister rhs = DRegisterFrom(locations->InAt(1));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister rhs = VRegisterFrom(locations->InAt(1));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
-      __ And(dst.V8B(), lhs.V8B(), rhs.V8B());  // lanes do not matter
+    case Primitive::kPrimDouble:
+      __ And(dst.V16B(), lhs.V16B(), rhs.V16B());  // lanes do not matter
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -403,17 +460,19 @@
 
 void InstructionCodeGeneratorARM64::VisitVecOr(HVecOr* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister rhs = DRegisterFrom(locations->InAt(1));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister rhs = VRegisterFrom(locations->InAt(1));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
-      __ Orr(dst.V8B(), lhs.V8B(), rhs.V8B());  // lanes do not matter
+    case Primitive::kPrimDouble:
+      __ Orr(dst.V16B(), lhs.V16B(), rhs.V16B());  // lanes do not matter
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -427,17 +486,19 @@
 
 void InstructionCodeGeneratorARM64::VisitVecXor(HVecXor* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister rhs = DRegisterFrom(locations->InAt(1));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister rhs = VRegisterFrom(locations->InAt(1));
+  VRegister dst = VRegisterFrom(locations->Out());
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
-      __ Eor(dst.V8B(), lhs.V8B(), rhs.V8B());  // lanes do not matter
+    case Primitive::kPrimDouble:
+      __ Eor(dst.V16B(), lhs.V16B(), rhs.V16B());  // lanes do not matter
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -453,6 +514,7 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
       locations->SetInAt(0, Location::RequiresFpuRegister());
       locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
       locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
@@ -469,22 +531,26 @@
 
 void InstructionCodeGeneratorARM64::VisitVecShl(HVecShl* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister dst = VRegisterFrom(locations->Out());
   int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Shl(dst.V8B(), lhs.V8B(), value);
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Shl(dst.V16B(), lhs.V16B(), value);
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Shl(dst.V4H(), lhs.V4H(), value);
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Shl(dst.V8H(), lhs.V8H(), value);
       break;
     case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Shl(dst.V4S(), lhs.V4S(), value);
+      break;
+    case Primitive::kPrimLong:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Shl(dst.V2S(), lhs.V2S(), value);
+      __ Shl(dst.V2D(), lhs.V2D(), value);
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -498,22 +564,26 @@
 
 void InstructionCodeGeneratorARM64::VisitVecShr(HVecShr* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister dst = VRegisterFrom(locations->Out());
   int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Sshr(dst.V8B(), lhs.V8B(), value);
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Sshr(dst.V16B(), lhs.V16B(), value);
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Sshr(dst.V4H(), lhs.V4H(), value);
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Sshr(dst.V8H(), lhs.V8H(), value);
       break;
     case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Sshr(dst.V4S(), lhs.V4S(), value);
+      break;
+    case Primitive::kPrimLong:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Sshr(dst.V2S(), lhs.V2S(), value);
+      __ Sshr(dst.V2D(), lhs.V2D(), value);
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -527,22 +597,26 @@
 
 void InstructionCodeGeneratorARM64::VisitVecUShr(HVecUShr* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  FPRegister lhs = DRegisterFrom(locations->InAt(0));
-  FPRegister dst = DRegisterFrom(locations->Out());
+  VRegister lhs = VRegisterFrom(locations->InAt(0));
+  VRegister dst = VRegisterFrom(locations->Out());
   int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Ushr(dst.V8B(), lhs.V8B(), value);
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Ushr(dst.V16B(), lhs.V16B(), value);
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Ushr(dst.V4H(), lhs.V4H(), value);
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Ushr(dst.V8H(), lhs.V8H(), value);
       break;
     case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Ushr(dst.V4S(), lhs.V4S(), value);
+      break;
+    case Primitive::kPrimLong:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Ushr(dst.V2S(), lhs.V2S(), value);
+      __ Ushr(dst.V2D(), lhs.V2D(), value);
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -561,7 +635,9 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
       if (is_load) {
@@ -613,22 +689,27 @@
 void InstructionCodeGeneratorARM64::VisitVecLoad(HVecLoad* instruction) {
   Location reg_loc = Location::NoLocation();
   MemOperand mem = CreateVecMemRegisters(instruction, &reg_loc, /*is_load*/ true);
-  FPRegister reg = DRegisterFrom(reg_loc);
+  VRegister reg = VRegisterFrom(reg_loc);
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ Ld1(reg.V8B(), mem);
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Ld1(reg.V16B(), mem);
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ Ld1(reg.V4H(), mem);
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Ld1(reg.V8H(), mem);
       break;
     case Primitive::kPrimInt:
     case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Ld1(reg.V4S(), mem);
+      break;
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ Ld1(reg.V2S(), mem);
+      __ Ld1(reg.V2D(), mem);
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -643,22 +724,27 @@
 void InstructionCodeGeneratorARM64::VisitVecStore(HVecStore* instruction) {
   Location reg_loc = Location::NoLocation();
   MemOperand mem = CreateVecMemRegisters(instruction, &reg_loc, /*is_load*/ false);
-  FPRegister reg = DRegisterFrom(reg_loc);
+  VRegister reg = VRegisterFrom(reg_loc);
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ St1(reg.V8B(), mem);
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ St1(reg.V16B(), mem);
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ St1(reg.V4H(), mem);
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ St1(reg.V8H(), mem);
       break;
     case Primitive::kPrimInt:
     case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ St1(reg.V4S(), mem);
+      break;
+    case Primitive::kPrimLong:
+    case Primitive::kPrimDouble:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ St1(reg.V2S(), mem);
+      __ St1(reg.V2D(), mem);
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index f8bbf68..4ba5c55 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -769,6 +769,45 @@
   InternalCodeAllocator code_allocator;
   codegen.Finalize(&code_allocator);
 }
+
+// Check that ParallelMoveResolver works fine for ARM64 for both cases when SIMD is on and off.
+TEST_F(CodegenTest, ARM64ParallelMoveResolverSIMD) {
+  std::unique_ptr<const Arm64InstructionSetFeatures> features(
+      Arm64InstructionSetFeatures::FromCppDefines());
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = CreateGraph(&allocator);
+  arm64::CodeGeneratorARM64 codegen(graph, *features.get(), CompilerOptions());
+
+  codegen.Initialize();
+
+  graph->SetHasSIMD(true);
+  for (int i = 0; i < 2; i++) {
+    HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena());
+    move->AddMove(Location::SIMDStackSlot(0),
+                  Location::SIMDStackSlot(257),
+                  Primitive::kPrimDouble,
+                  nullptr);
+    move->AddMove(Location::SIMDStackSlot(257),
+                  Location::SIMDStackSlot(0),
+                  Primitive::kPrimDouble,
+                  nullptr);
+    move->AddMove(Location::FpuRegisterLocation(0),
+                  Location::FpuRegisterLocation(1),
+                  Primitive::kPrimDouble,
+                  nullptr);
+    move->AddMove(Location::FpuRegisterLocation(1),
+                  Location::FpuRegisterLocation(0),
+                  Primitive::kPrimDouble,
+                  nullptr);
+    codegen.GetMoveResolver()->EmitNativeCode(move);
+    graph->SetHasSIMD(false);
+  }
+
+  InternalCodeAllocator code_allocator;
+  codegen.Finalize(&code_allocator);
+}
+
 #endif
 
 #ifdef ART_ENABLE_CODEGEN_mips
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index d3f431e..721f74e 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -92,6 +92,16 @@
   return vixl::aarch64::FPRegister::GetDRegFromCode(location.reg());
 }
 
+inline vixl::aarch64::FPRegister QRegisterFrom(Location location) {
+  DCHECK(location.IsFpuRegister()) << location;
+  return vixl::aarch64::FPRegister::GetQRegFromCode(location.reg());
+}
+
+inline vixl::aarch64::FPRegister VRegisterFrom(Location location) {
+  DCHECK(location.IsFpuRegister()) << location;
+  return vixl::aarch64::FPRegister::GetVRegFromCode(location.reg());
+}
+
 inline vixl::aarch64::FPRegister SRegisterFrom(Location location) {
   DCHECK(location.IsFpuRegister()) << location;
   return vixl::aarch64::FPRegister::GetSRegFromCode(location.reg());
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 423fd3c..47bcb5d 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2507,9 +2507,11 @@
     // We use a block to end the scratch scope before the write barrier, thus
     // freeing the temporary registers so they can be used in `MarkGCCard`.
     UseScratchRegisterScope temps(masm);
+    Location temp3_loc;  // Used only for Baker read barrier.
     Register temp3;
     if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-      temp3 = WRegisterFrom(locations->GetTemp(2));
+      temp3_loc = locations->GetTemp(2);
+      temp3 = WRegisterFrom(temp3_loc);
     } else {
       temp3 = temps.AcquireW();
     }
@@ -2527,7 +2529,7 @@
                                                           temp1_loc,
                                                           src.W(),
                                                           class_offset,
-                                                          temp2,
+                                                          temp3_loc,
                                                           /* needs_null_check */ false,
                                                           /* use_load_acquire */ false);
           // Bail out if the source is not a non primitive array.
@@ -2536,7 +2538,7 @@
                                                           temp1_loc,
                                                           temp1,
                                                           component_offset,
-                                                          temp2,
+                                                          temp3_loc,
                                                           /* needs_null_check */ false,
                                                           /* use_load_acquire */ false);
           __ Cbz(temp1, intrinsic_slow_path->GetEntryLabel());
@@ -2553,7 +2555,7 @@
                                                         temp1_loc,
                                                         dest.W(),
                                                         class_offset,
-                                                        temp2,
+                                                        temp3_loc,
                                                         /* needs_null_check */ false,
                                                         /* use_load_acquire */ false);
 
@@ -2570,7 +2572,7 @@
                                                           temp2_loc,
                                                           temp1,
                                                           component_offset,
-                                                          temp3,
+                                                          temp3_loc,
                                                           /* needs_null_check */ false,
                                                           /* use_load_acquire */ false);
           __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
@@ -2589,7 +2591,7 @@
                                                         temp2_loc,
                                                         src.W(),
                                                         class_offset,
-                                                        temp3,
+                                                        temp3_loc,
                                                         /* needs_null_check */ false,
                                                         /* use_load_acquire */ false);
         // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
@@ -2603,7 +2605,7 @@
                                                           temp1_loc,
                                                           temp1,
                                                           component_offset,
-                                                          temp2,
+                                                          temp3_loc,
                                                           /* needs_null_check */ false,
                                                           /* use_load_acquire */ false);
           // /* HeapReference<Class> */ temp1 = temp1->super_class_
@@ -2687,7 +2689,7 @@
                                                         temp1_loc,
                                                         src.W(),
                                                         class_offset,
-                                                        temp2,
+                                                        temp3_loc,
                                                         /* needs_null_check */ false,
                                                         /* use_load_acquire */ false);
         // /* HeapReference<Class> */ temp2 = temp1->component_type_
@@ -2695,7 +2697,7 @@
                                                         temp2_loc,
                                                         temp1,
                                                         component_offset,
-                                                        temp3,
+                                                        temp3_loc,
                                                         /* needs_null_check */ false,
                                                         /* use_load_acquire */ false);
         __ Cbz(temp2, intrinsic_slow_path->GetEntryLabel());
@@ -2755,9 +2757,17 @@
         // Make sure `tmp` is not IP0, as it is clobbered by
         // ReadBarrierMarkRegX entry points in
         // ReadBarrierSystemArrayCopySlowPathARM64.
+        DCHECK(temps.IsAvailable(ip0));
         temps.Exclude(ip0);
         Register tmp = temps.AcquireW();
         DCHECK_NE(LocationFrom(tmp).reg(), IP0);
+        // Put IP0 back in the pool so that VIXL has at least one
+        // scratch register available to emit macro-instructions (note
+        // that IP1 is already used for `tmp`). Indeed some
+        // macro-instructions used in GenSystemArrayCopyAddresses
+        // (invoked hereunder) may require a scratch register (for
+        // instance to emit a load with a large constant offset).
+        temps.Include(ip0);
 
         // /* int32_t */ monitor = src->monitor_
         __ Ldr(tmp, HeapOperand(src.W(), monitor_offset));
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index b67793c..41df56b 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2701,12 +2701,7 @@
 
   // Calculate destination address.
   __ Addiu(dstPtr, dstObj, data_offset);
-  if (IsR6()) {
-    __ Lsa(dstPtr, dstBegin, dstPtr, char_shift);
-  } else {
-    __ Sll(AT, dstBegin, char_shift);
-    __ Addu(dstPtr, dstPtr, AT);
-  }
+  __ ShiftAndAdd(dstPtr, dstBegin, dstPtr, char_shift);
 
   if (mirror::kUseStringCompression) {
     MipsLabel uncompressed_copy, compressed_loop;
@@ -2734,12 +2729,7 @@
 
   // Calculate source address.
   __ Addiu(srcPtr, srcObj, value_offset);
-  if (IsR6()) {
-    __ Lsa(srcPtr, srcBegin, srcPtr, char_shift);
-  } else {
-    __ Sll(AT, srcBegin, char_shift);
-    __ Addu(srcPtr, srcPtr, AT);
-  }
+  __ ShiftAndAdd(srcPtr, srcBegin, srcPtr, char_shift);
 
   __ Bind(&loop);
   __ Lh(AT, srcPtr, 0);
@@ -2752,6 +2742,397 @@
   __ Bind(&done);
 }
 
+static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kCallOnMainOnly,
+                                                           kIntrinsified);
+  InvokeRuntimeCallingConvention calling_convention;
+
+  locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimDouble));
+}
+
+static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kCallOnMainOnly,
+                                                           kIntrinsified);
+  InvokeRuntimeCallingConvention calling_convention;
+
+  locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+  locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
+  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimDouble));
+}
+
+static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorMIPS* codegen, QuickEntrypointEnum entry) {
+  LocationSummary* locations = invoke->GetLocations();
+  FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
+  DCHECK_EQ(in, F12);
+  FRegister out = locations->Out().AsFpuRegister<FRegister>();
+  DCHECK_EQ(out, F0);
+
+  codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
+}
+
+static void GenFPFPToFPCall(HInvoke* invoke,
+                            CodeGeneratorMIPS* codegen,
+                            QuickEntrypointEnum entry) {
+  LocationSummary* locations = invoke->GetLocations();
+  FRegister in0 = locations->InAt(0).AsFpuRegister<FRegister>();
+  DCHECK_EQ(in0, F12);
+  FRegister in1 = locations->InAt(1).AsFpuRegister<FRegister>();
+  DCHECK_EQ(in1, F14);
+  FRegister out = locations->Out().AsFpuRegister<FRegister>();
+  DCHECK_EQ(out, F0);
+
+  codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
+}
+
+// static double java.lang.Math.cos(double a)
+void IntrinsicLocationsBuilderMIPS::VisitMathCos(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathCos(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCos);
+}
+
+// static double java.lang.Math.sin(double a)
+void IntrinsicLocationsBuilderMIPS::VisitMathSin(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathSin(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickSin);
+}
+
+// static double java.lang.Math.acos(double a)
+void IntrinsicLocationsBuilderMIPS::VisitMathAcos(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathAcos(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAcos);
+}
+
+// static double java.lang.Math.asin(double a)
+void IntrinsicLocationsBuilderMIPS::VisitMathAsin(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathAsin(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAsin);
+}
+
+// static double java.lang.Math.atan(double a)
+void IntrinsicLocationsBuilderMIPS::VisitMathAtan(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathAtan(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAtan);
+}
+
+// static double java.lang.Math.atan2(double y, double x)
+void IntrinsicLocationsBuilderMIPS::VisitMathAtan2(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathAtan2(HInvoke* invoke) {
+  GenFPFPToFPCall(invoke, codegen_, kQuickAtan2);
+}
+
+// static double java.lang.Math.cbrt(double a)
+void IntrinsicLocationsBuilderMIPS::VisitMathCbrt(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathCbrt(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCbrt);
+}
+
+// static double java.lang.Math.cosh(double x)
+void IntrinsicLocationsBuilderMIPS::VisitMathCosh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathCosh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCosh);
+}
+
+// static double java.lang.Math.exp(double a)
+void IntrinsicLocationsBuilderMIPS::VisitMathExp(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathExp(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickExp);
+}
+
+// static double java.lang.Math.expm1(double x)
+void IntrinsicLocationsBuilderMIPS::VisitMathExpm1(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathExpm1(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickExpm1);
+}
+
+// static double java.lang.Math.hypot(double x, double y)
+void IntrinsicLocationsBuilderMIPS::VisitMathHypot(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathHypot(HInvoke* invoke) {
+  GenFPFPToFPCall(invoke, codegen_, kQuickHypot);
+}
+
+// static double java.lang.Math.log(double a)
+void IntrinsicLocationsBuilderMIPS::VisitMathLog(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathLog(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickLog);
+}
+
+// static double java.lang.Math.log10(double x)
+void IntrinsicLocationsBuilderMIPS::VisitMathLog10(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathLog10(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickLog10);
+}
+
+// static double java.lang.Math.nextAfter(double start, double direction)
+void IntrinsicLocationsBuilderMIPS::VisitMathNextAfter(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathNextAfter(HInvoke* invoke) {
+  GenFPFPToFPCall(invoke, codegen_, kQuickNextAfter);
+}
+
+// static double java.lang.Math.sinh(double x)
+void IntrinsicLocationsBuilderMIPS::VisitMathSinh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathSinh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickSinh);
+}
+
+// static double java.lang.Math.tan(double a)
+void IntrinsicLocationsBuilderMIPS::VisitMathTan(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathTan(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickTan);
+}
+
+// static double java.lang.Math.tanh(double x)
+void IntrinsicLocationsBuilderMIPS::VisitMathTanh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathTanh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickTanh);
+}
+
+// static void java.lang.System.arraycopy(Object src, int srcPos,
+//                                        Object dest, int destPos,
+//                                        int length)
+void IntrinsicLocationsBuilderMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) {
+  HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
+  HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant();
+  HIntConstant* length = invoke->InputAt(4)->AsIntConstant();
+
+  // As long as we are checking, we might as well check to see if the src and dest
+  // positions are >= 0.
+  if ((src_pos != nullptr && src_pos->GetValue() < 0) ||
+      (dest_pos != nullptr && dest_pos->GetValue() < 0)) {
+    // We will have to fail anyways.
+    return;
+  }
+
+  // And since we are already checking, check the length too.
+  if (length != nullptr) {
+    int32_t len = length->GetValue();
+    if (len < 0) {
+      // Just call as normal.
+      return;
+    }
+  }
+
+  // Okay, it is safe to generate inline code.
+  LocationSummary* locations =
+      new (arena_) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified);
+  // arraycopy(Object src, int srcPos, Object dest, int destPos, int length).
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3)));
+  locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4)));
+
+  locations->AddTemp(Location::RequiresRegister());
+  locations->AddTemp(Location::RequiresRegister());
+  locations->AddTemp(Location::RequiresRegister());
+}
+
+// Utility routine to verify that "length(input) - pos >= length"
+static void EnoughItems(MipsAssembler* assembler,
+                        Register length_input_minus_pos,
+                        Location length,
+                        SlowPathCodeMIPS* slow_path) {
+  if (length.IsConstant()) {
+    int32_t length_constant = length.GetConstant()->AsIntConstant()->GetValue();
+
+    if (IsInt<16>(length_constant)) {
+      __ Slti(TMP, length_input_minus_pos, length_constant);
+      __ Bnez(TMP, slow_path->GetEntryLabel());
+    } else {
+      __ LoadConst32(TMP, length_constant);
+      __ Blt(length_input_minus_pos, TMP, slow_path->GetEntryLabel());
+    }
+  } else {
+    __ Blt(length_input_minus_pos, length.AsRegister<Register>(), slow_path->GetEntryLabel());
+  }
+}
+
+static void CheckPosition(MipsAssembler* assembler,
+                          Location pos,
+                          Register input,
+                          Location length,
+                          SlowPathCodeMIPS* slow_path,
+                          bool length_is_input_length = false) {
+  // Where is the length in the Array?
+  const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
+
+  // Calculate length(input) - pos.
+  if (pos.IsConstant()) {
+    int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue();
+    if (pos_const == 0) {
+      if (!length_is_input_length) {
+        // Check that length(input) >= length.
+        __ LoadFromOffset(kLoadWord, AT, input, length_offset);
+        EnoughItems(assembler, AT, length, slow_path);
+      }
+    } else {
+      // Check that (length(input) - pos) >= zero.
+      __ LoadFromOffset(kLoadWord, AT, input, length_offset);
+      DCHECK_GT(pos_const, 0);
+      __ Addiu32(AT, AT, -pos_const, TMP);
+      __ Bltz(AT, slow_path->GetEntryLabel());
+
+      // Verify that (length(input) - pos) >= length.
+      EnoughItems(assembler, AT, length, slow_path);
+    }
+  } else if (length_is_input_length) {
+    // The only way the copy can succeed is if pos is zero.
+    Register pos_reg = pos.AsRegister<Register>();
+    __ Bnez(pos_reg, slow_path->GetEntryLabel());
+  } else {
+    // Verify that pos >= 0.
+    Register pos_reg = pos.AsRegister<Register>();
+    __ Bltz(pos_reg, slow_path->GetEntryLabel());
+
+    // Check that (length(input) - pos) >= zero.
+    __ LoadFromOffset(kLoadWord, AT, input, length_offset);
+    __ Subu(AT, AT, pos_reg);
+    __ Bltz(AT, slow_path->GetEntryLabel());
+
+    // Verify that (length(input) - pos) >= length.
+    EnoughItems(assembler, AT, length, slow_path);
+  }
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitSystemArrayCopyChar(HInvoke* invoke) {
+  MipsAssembler* assembler = GetAssembler();
+  LocationSummary* locations = invoke->GetLocations();
+
+  Register src = locations->InAt(0).AsRegister<Register>();
+  Location src_pos = locations->InAt(1);
+  Register dest = locations->InAt(2).AsRegister<Register>();
+  Location dest_pos = locations->InAt(3);
+  Location length = locations->InAt(4);
+
+  MipsLabel loop;
+
+  Register dest_base = locations->GetTemp(0).AsRegister<Register>();
+  Register src_base = locations->GetTemp(1).AsRegister<Register>();
+  Register count = locations->GetTemp(2).AsRegister<Register>();
+
+  SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke);
+  codegen_->AddSlowPath(slow_path);
+
+  // Bail out if the source and destination are the same (to handle overlap).
+  __ Beq(src, dest, slow_path->GetEntryLabel());
+
+  // Bail out if the source is null.
+  __ Beqz(src, slow_path->GetEntryLabel());
+
+  // Bail out if the destination is null.
+  __ Beqz(dest, slow_path->GetEntryLabel());
+
+  // Load length into register for count.
+  if (length.IsConstant()) {
+    __ LoadConst32(count, length.GetConstant()->AsIntConstant()->GetValue());
+  } else {
+    // If the length is negative, bail out.
+    // We have already checked in the LocationsBuilder for the constant case.
+    __ Bltz(length.AsRegister<Register>(), slow_path->GetEntryLabel());
+
+    __ Move(count, length.AsRegister<Register>());
+  }
+
+  // Validity checks: source.
+  CheckPosition(assembler, src_pos, src, Location::RegisterLocation(count), slow_path);
+
+  // Validity checks: dest.
+  CheckPosition(assembler, dest_pos, dest, Location::RegisterLocation(count), slow_path);
+
+  // If count is zero, we're done.
+  __ Beqz(count, slow_path->GetExitLabel());
+
+  // Okay, everything checks out.  Finally time to do the copy.
+  // Check assumption that sizeof(Char) is 2 (used in scaling below).
+  const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+  DCHECK_EQ(char_size, 2u);
+
+  const size_t char_shift = Primitive::ComponentSizeShift(Primitive::kPrimChar);
+
+  const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+  // Calculate source and destination addresses.
+  if (src_pos.IsConstant()) {
+    int32_t src_pos_const = src_pos.GetConstant()->AsIntConstant()->GetValue();
+
+    __ Addiu32(src_base, src, data_offset + char_size * src_pos_const, TMP);
+  } else {
+    __ Addiu32(src_base, src, data_offset, TMP);
+    __ ShiftAndAdd(src_base, src_pos.AsRegister<Register>(), src_base, char_shift);
+  }
+  if (dest_pos.IsConstant()) {
+    int32_t dest_pos_const = dest_pos.GetConstant()->AsIntConstant()->GetValue();
+
+    __ Addiu32(dest_base, dest, data_offset + char_size * dest_pos_const, TMP);
+  } else {
+    __ Addiu32(dest_base, dest, data_offset, TMP);
+    __ ShiftAndAdd(dest_base, dest_pos.AsRegister<Register>(), dest_base, char_shift);
+  }
+
+  __ Bind(&loop);
+  __ Lh(TMP, src_base, 0);
+  __ Addiu(src_base, src_base, char_size);
+  __ Addiu(count, count, -1);
+  __ Sh(TMP, dest_base, 0);
+  __ Addiu(dest_base, dest_base, char_size);
+  __ Bnez(count, &loop);
+
+  __ Bind(slow_path->GetExitLabel());
+}
+
 // Unimplemented intrinsics.
 
 UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil)
@@ -2763,27 +3144,8 @@
 UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
 
 UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
 
-UNIMPLEMENTED_INTRINSIC(MIPS, MathCos)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathSin)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathAcos)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathAsin)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathAtan)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathAtan2)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathCbrt)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathCosh)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathExp)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathExpm1)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathHypot)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathLog)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathLog10)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathNextAfter)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathSinh)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathTan)
-UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh)
-
 UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter);
 UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferAppend);
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index c2518a7..82d0567 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2171,28 +2171,210 @@
   GenLowestOneBit(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
 }
 
+static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kCallOnMainOnly,
+                                                           kIntrinsified);
+  InvokeRuntimeCallingConvention calling_convention;
+
+  locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimDouble));
+}
+
+static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kCallOnMainOnly,
+                                                           kIntrinsified);
+  InvokeRuntimeCallingConvention calling_convention;
+
+  locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+  locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
+  locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimDouble));
+}
+
+static void GenFPToFPCall(HInvoke* invoke,
+                          CodeGeneratorMIPS64* codegen,
+                          QuickEntrypointEnum entry) {
+  LocationSummary* locations = invoke->GetLocations();
+  FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
+  DCHECK_EQ(in, F12);
+  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
+  DCHECK_EQ(out, F0);
+
+  codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
+}
+
+static void GenFPFPToFPCall(HInvoke* invoke,
+                            CodeGeneratorMIPS64* codegen,
+                            QuickEntrypointEnum entry) {
+  LocationSummary* locations = invoke->GetLocations();
+  FpuRegister in0 = locations->InAt(0).AsFpuRegister<FpuRegister>();
+  DCHECK_EQ(in0, F12);
+  FpuRegister in1 = locations->InAt(1).AsFpuRegister<FpuRegister>();
+  DCHECK_EQ(in1, F13);
+  FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
+  DCHECK_EQ(out, F0);
+
+  codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc());
+}
+
+// static double java.lang.Math.cos(double a)
+void IntrinsicLocationsBuilderMIPS64::VisitMathCos(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathCos(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCos);
+}
+
+// static double java.lang.Math.sin(double a)
+void IntrinsicLocationsBuilderMIPS64::VisitMathSin(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathSin(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickSin);
+}
+
+// static double java.lang.Math.acos(double a)
+void IntrinsicLocationsBuilderMIPS64::VisitMathAcos(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathAcos(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAcos);
+}
+
+// static double java.lang.Math.asin(double a)
+void IntrinsicLocationsBuilderMIPS64::VisitMathAsin(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathAsin(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAsin);
+}
+
+// static double java.lang.Math.atan(double a)
+void IntrinsicLocationsBuilderMIPS64::VisitMathAtan(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathAtan(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAtan);
+}
+
+// static double java.lang.Math.atan2(double y, double x)
+void IntrinsicLocationsBuilderMIPS64::VisitMathAtan2(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathAtan2(HInvoke* invoke) {
+  GenFPFPToFPCall(invoke, codegen_, kQuickAtan2);
+}
+
+// static double java.lang.Math.cbrt(double a)
+void IntrinsicLocationsBuilderMIPS64::VisitMathCbrt(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathCbrt(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCbrt);
+}
+
+// static double java.lang.Math.cosh(double x)
+void IntrinsicLocationsBuilderMIPS64::VisitMathCosh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathCosh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCosh);
+}
+
+// static double java.lang.Math.exp(double a)
+void IntrinsicLocationsBuilderMIPS64::VisitMathExp(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathExp(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickExp);
+}
+
+// static double java.lang.Math.expm1(double x)
+void IntrinsicLocationsBuilderMIPS64::VisitMathExpm1(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathExpm1(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickExpm1);
+}
+
+// static double java.lang.Math.hypot(double x, double y)
+void IntrinsicLocationsBuilderMIPS64::VisitMathHypot(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathHypot(HInvoke* invoke) {
+  GenFPFPToFPCall(invoke, codegen_, kQuickHypot);
+}
+
+// static double java.lang.Math.log(double a)
+void IntrinsicLocationsBuilderMIPS64::VisitMathLog(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathLog(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickLog);
+}
+
+// static double java.lang.Math.log10(double x)
+void IntrinsicLocationsBuilderMIPS64::VisitMathLog10(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathLog10(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickLog10);
+}
+
+// static double java.lang.Math.nextAfter(double start, double direction)
+void IntrinsicLocationsBuilderMIPS64::VisitMathNextAfter(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathNextAfter(HInvoke* invoke) {
+  GenFPFPToFPCall(invoke, codegen_, kQuickNextAfter);
+}
+
+// static double java.lang.Math.sinh(double x)
+void IntrinsicLocationsBuilderMIPS64::VisitMathSinh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathSinh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickSinh);
+}
+
+// static double java.lang.Math.tan(double a)
+void IntrinsicLocationsBuilderMIPS64::VisitMathTan(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathTan(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickTan);
+}
+
+// static double java.lang.Math.tanh(double x)
+void IntrinsicLocationsBuilderMIPS64::VisitMathTanh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitMathTanh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickTanh);
+}
+
 UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
 
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathCos)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathSin)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathAcos)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathAsin)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathAtan)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathAtan2)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathCbrt)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathCosh)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathExp)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathExpm1)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathHypot)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathLog)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathLog10)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathNextAfter)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathSinh)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathTan)
-UNIMPLEMENTED_INTRINSIC(MIPS64, MathTanh)
-
 UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter);
 UNIMPLEMENTED_INTRINSIC(MIPS64, StringBufferAppend);
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 8eeff1f..8e88c1e 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -623,6 +623,12 @@
     }
     return true;
   } else if (instruction->IsArrayGet()) {
+    // Strings are different, with a different offset to the actual data
+    // and some compressed to save memory. For now, all cases are rejected
+    // to avoid the complexity.
+    if (instruction->AsArrayGet()->IsStringCharAt()) {
+      return false;
+    }
     // Accept a right-hand-side array base[index] for
     // (1) exact matching vector type,
     // (2) loop-invariant base,
@@ -770,21 +776,25 @@
       return false;
     case kArm64:
       // Allow vectorization for all ARM devices, because Android assumes that
-      // ARMv8 AArch64 always supports advanced SIMD. For now, only D registers
-      // (64-bit vectors) not Q registers (128-bit vectors).
+      // ARMv8 AArch64 always supports advanced SIMD.
       switch (type) {
         case Primitive::kPrimBoolean:
         case Primitive::kPrimByte:
           *restrictions |= kNoDiv | kNoAbs;
-          return TrySetVectorLength(8);
+          return TrySetVectorLength(16);
         case Primitive::kPrimChar:
         case Primitive::kPrimShort:
           *restrictions |= kNoDiv | kNoAbs;
-          return TrySetVectorLength(4);
+          return TrySetVectorLength(8);
         case Primitive::kPrimInt:
           *restrictions |= kNoDiv;
+          return TrySetVectorLength(4);
+        case Primitive::kPrimLong:
+          *restrictions |= kNoDiv | kNoMul;
           return TrySetVectorLength(2);
         case Primitive::kPrimFloat:
+          return TrySetVectorLength(4);
+        case Primitive::kPrimDouble:
           return TrySetVectorLength(2);
         default:
           return false;
@@ -1072,7 +1082,10 @@
     HInstruction* s = block->GetFirstInstruction();
     if (s != nullptr && s->IsSuspendCheck()) {
       HInstruction* c = s->GetNext();
-      if (c != nullptr && c->IsCondition() && c->GetUses().HasExactlyOneElement()) {
+      if (c != nullptr &&
+          c->IsCondition() &&
+          c->GetUses().HasExactlyOneElement() &&  // only used for termination
+          !c->HasEnvironmentUses()) {  // unlikely, but not impossible
         HInstruction* i = c->GetNext();
         if (i != nullptr && i->IsIf() && i->InputAt(0) == c) {
           iset_->insert(c);
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 2e2231b..a99d02d 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -635,6 +635,7 @@
   DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast<Register>(pos + size - 1), pos, 0x04), rd, rd, rt);
 }
 
+// TODO: This instruction is available in both R6 and MSA and it should be used when available.
 void MipsAssembler::Lsa(Register rd, Register rs, Register rt, int saPlusOne) {
   CHECK(IsR6());
   CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne;
@@ -642,6 +643,24 @@
   DsFsmInstrRrr(EmitR(0x0, rs, rt, rd, sa, 0x05), rd, rs, rt);
 }
 
+void MipsAssembler::ShiftAndAdd(Register dst,
+                                Register src_idx,
+                                Register src_base,
+                                int shamt,
+                                Register tmp) {
+  CHECK(0 <= shamt && shamt <= 4) << shamt;
+  CHECK_NE(src_base, tmp);
+  if (shamt == TIMES_1) {
+    // Catch the special case where the shift amount is zero (0).
+    Addu(dst, src_base, src_idx);
+  } else if (IsR6()) {
+    Lsa(dst, src_idx, src_base, shamt);
+  } else {
+    Sll(tmp, src_idx, shamt);
+    Addu(dst, src_base, tmp);
+  }
+}
+
 void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
   DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs);
 }
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 1a5a23d..463daeb 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -263,6 +263,7 @@
   void Ext(Register rd, Register rt, int pos, int size);  // R2+
   void Ins(Register rd, Register rt, int pos, int size);  // R2+
   void Lsa(Register rd, Register rs, Register rt, int saPlusOne);  // R6
+  void ShiftAndAdd(Register dst, Register src_idx, Register src_base, int shamt, Register tmp = AT);
 
   void Lb(Register rt, Register rs, uint16_t imm16);
   void Lh(Register rt, Register rs, uint16_t imm16);
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index b4ea20b..81566c4 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2931,7 +2931,7 @@
   // time (bug 10645725) unless we're a debug build or running on valgrind. Note: The Dex2Oat class
   // should not destruct the runtime in this case.
   if (!art::kIsDebugBuild && (RUNNING_ON_MEMORY_TOOL == 0)) {
-    exit(result);
+    _exit(result);
   }
   return result;
 }
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index e26d051..4b65c52 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -14,6 +14,7 @@
 
 art_cc_defaults {
     name: "libart-dexlayout-defaults",
+    defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
         "dexlayout.cc",
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 4905b5c..6bd9da8 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -496,8 +496,8 @@
       dex_file.GetClassAnnotationSet(disk_annotations_item);
   AnnotationSetItem* class_annotation = nullptr;
   if (class_set_item != nullptr) {
-    uint32_t offset = disk_annotations_item->class_annotations_off_;
-    class_annotation = CreateAnnotationSetItem(dex_file, class_set_item, offset);
+    uint32_t item_offset = disk_annotations_item->class_annotations_off_;
+    class_annotation = CreateAnnotationSetItem(dex_file, class_set_item, item_offset);
   }
   const DexFile::FieldAnnotationsItem* fields =
       dex_file.GetFieldAnnotations(disk_annotations_item);
@@ -784,7 +784,7 @@
   std::function<uint32_t(const dex_ir::Collections&)> offset_fn;
 };
 
-static const std::vector<FileSectionDescriptor> kFileSectionDescriptors = {
+static const FileSectionDescriptor kFileSectionDescriptors[] = {
   {
     "Header",
     DexFile::kDexTypeHeaderItem,
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
index 3edf051..688201b 100644
--- a/dexlayout/dexdiag.cc
+++ b/dexlayout/dexdiag.cc
@@ -21,9 +21,12 @@
 
 #include <iostream>
 #include <memory>
+#include <vector>
 
 #include "android-base/stringprintf.h"
 
+#include "base/stringpiece.h"
+
 #include "dex_file.h"
 #include "dex_ir.h"
 #include "dex_ir_builder.h"
@@ -37,9 +40,13 @@
 
 static constexpr size_t kLineLength = 32;
 
-static bool g_show_key = false;
 static bool g_verbose = false;
-static bool g_show_statistics = false;
+
+// The width needed to print a file page offset (32-bit).
+static constexpr int kPageCountWidth =
+    static_cast<int>(std::numeric_limits<uint32_t>::digits10);
+// Display the sections.
+static constexpr char kSectionHeader[] = "Section name";
 
 struct DexSectionInfo {
  public:
@@ -88,12 +95,73 @@
   DISALLOW_COPY_AND_ASSIGN(PageCount);
 };
 
-static void PrintLetterKey() {
-  std::cout << "letter section_type" << std::endl;
-  for (const auto& p : kDexSectionInfoMap) {
-    const DexSectionInfo& section_info = p.second;
-    std::cout << section_info.letter << "      " << section_info.name.c_str() << std::endl;
+class Printer {
+ public:
+  Printer() : section_header_width_(ComputeHeaderWidth()) {
   }
+
+  void PrintHeader() const {
+    std::cout << StringPrintf("%-*s %*s %*s %% of   %% of",
+                              section_header_width_,
+                              kSectionHeader,
+                              kPageCountWidth,
+                              "resident",
+                              kPageCountWidth,
+                              "total"
+                              )
+              << std::endl;
+    std::cout << StringPrintf("%-*s %*s %*s sect.  total",
+                              section_header_width_,
+                              "",
+                              kPageCountWidth,
+                              "pages",
+                              kPageCountWidth,
+                              "pages")
+              << std::endl;
+  }
+
+  void PrintOne(const char* name,
+                size_t resident,
+                size_t mapped,
+                double percent_of_section,
+                double percent_of_total) const {
+    // 6.2 is sufficient to print 0-100% with two decimal places of accuracy.
+    std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f",
+                              section_header_width_,
+                              name,
+                              kPageCountWidth,
+                              resident,
+                              kPageCountWidth,
+                              mapped,
+                              percent_of_section,
+                              percent_of_total)
+              << std::endl;
+  }
+
+  void PrintSkipLine() const { std::cout << std::endl; }
+
+  // Computes the width of the section header column in the table (for fixed formatting).
+  static int ComputeHeaderWidth() {
+    int header_width = 0;
+    for (const auto& pair : kDexSectionInfoMap) {
+      const DexSectionInfo& section_info = pair.second;
+      header_width = std::max(header_width, static_cast<int>(section_info.name.length()));
+    }
+    return header_width;
+  }
+
+ private:
+  const int section_header_width_;
+};
+
+static void PrintLetterKey() {
+  std::cout << "L pagetype" << std::endl;
+  for (const auto& pair : kDexSectionInfoMap) {
+    const DexSectionInfo& section_info = pair.second;
+    std::cout << section_info.letter << " " << section_info.name.c_str() << std::endl;
+  }
+  std::cout << "* (Executable page resident)" << std::endl;
+  std::cout << ". (Mapped page not resident)" << std::endl;
 }
 
 static char PageTypeChar(uint16_t type) {
@@ -150,7 +218,8 @@
 static void DisplayDexStatistics(size_t start,
                                  size_t end,
                                  const PageCount& resident_pages,
-                                 const std::vector<dex_ir::DexFileSection>& sections) {
+                                 const std::vector<dex_ir::DexFileSection>& sections,
+                                 Printer* printer) {
   // Compute the total possible sizes for sections.
   PageCount mapped_pages;
   DCHECK_GE(end, start);
@@ -162,34 +231,7 @@
     mapped_pages.Increment(FindSectionTypeForPage(page, sections));
   }
   size_t total_resident_pages = 0;
-  // Compute the width of the section header column in the table (for fixed formatting).
-  int section_header_width = 0;
-  for (const auto& section_info : kDexSectionInfoMap) {
-    section_header_width = std::max(section_header_width,
-                                    static_cast<int>(section_info.second.name.length()));
-  }
-  // The width needed to print a file page offset (32-bit).
-  static constexpr int kPageCountWidth =
-      static_cast<int>(std::numeric_limits<uint32_t>::digits10);
-  // Display the sections.
-  static constexpr char kSectionHeader[] = "Section name";
-  std::cout << StringPrintf("%-*s %*s %*s %% of   %% of",
-                            section_header_width,
-                            kSectionHeader,
-                            kPageCountWidth,
-                            "resident",
-                            kPageCountWidth,
-                            "total"
-                            )
-            << std::endl;
-  std::cout << StringPrintf("%-*s %*s %*s sect.  total",
-                            section_header_width,
-                            "",
-                            kPageCountWidth,
-                            "pages",
-                            kPageCountWidth,
-                            "pages")
-            << std::endl;
+  printer->PrintHeader();
   for (size_t i = sections.size(); i > 0; --i) {
     const dex_ir::DexFileSection& section = sections[i - 1];
     const uint16_t type = section.type;
@@ -199,35 +241,27 @@
     if (mapped_pages.Get(type) > 0) {
       percent_resident = 100.0 * pages_resident / mapped_pages.Get(type);
     }
-    // 6.2 is sufficient to print 0-100% with two decimal places of accuracy.
-    std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f",
-                              section_header_width,
-                              section_info.name.c_str(),
-                              kPageCountWidth,
-                              pages_resident,
-                              kPageCountWidth,
-                              mapped_pages.Get(type),
-                              percent_resident,
-                              100.0 * pages_resident / total_mapped_pages)
-              << std::endl;
+    printer->PrintOne(section_info.name.c_str(),
+                      pages_resident,
+                      mapped_pages.Get(type),
+                      percent_resident,
+                      100.0 * pages_resident / total_mapped_pages);
     total_resident_pages += pages_resident;
   }
-  std::cout << StringPrintf("%-*s %*zd %*zd        %6.2f",
-                            section_header_width,
-                            "GRAND TOTAL",
-                            kPageCountWidth,
-                            total_resident_pages,
-                            kPageCountWidth,
-                            total_mapped_pages,
-                            100.0 * total_resident_pages / total_mapped_pages)
-            << std::endl
-            << std::endl;
+  double percent_of_total = 100.0 * total_resident_pages / total_mapped_pages;
+  printer->PrintOne("GRAND TOTAL",
+                    total_resident_pages,
+                    total_mapped_pages,
+                    percent_of_total,
+                    percent_of_total);
+  printer->PrintSkipLine();
 }
 
 static void ProcessOneDexMapping(uint64_t* pagemap,
                                  uint64_t map_start,
                                  const DexFile* dex_file,
-                                 uint64_t vdex_start) {
+                                 uint64_t vdex_start,
+                                 Printer* printer) {
   uint64_t dex_file_start = reinterpret_cast<uint64_t>(dex_file->Begin());
   size_t dex_file_size = dex_file->Size();
   if (dex_file_start < vdex_start) {
@@ -256,12 +290,10 @@
   }
   PageCount section_resident_pages;
   ProcessPageMap(pagemap, start_page, end_page, sections, &section_resident_pages);
-  if (g_show_statistics) {
-    DisplayDexStatistics(start_page, end_page, section_resident_pages, sections);
-  }
+  DisplayDexStatistics(start_page, end_page, section_resident_pages, sections, printer);
 }
 
-static bool DisplayMappingIfFromVdexFile(pm_map_t* map) {
+static bool DisplayMappingIfFromVdexFile(pm_map_t* map, Printer* printer) {
   // Confirm that the map is from a vdex file.
   static const char* suffixes[] = { ".vdex" };
   std::string vdex_name;
@@ -284,9 +316,9 @@
                                                 &error_msg /*out*/));
   if (vdex == nullptr) {
     std::cerr << "Could not open vdex file "
-              << vdex_name.c_str()
+              << vdex_name
               << ": error "
-              << error_msg.c_str()
+              << error_msg
               << std::endl;
     return false;
   }
@@ -294,9 +326,9 @@
   std::vector<std::unique_ptr<const DexFile>> dex_files;
   if (!vdex->OpenAllDexFiles(&dex_files, &error_msg)) {
     std::cerr << "Dex files could not be opened for "
-              << vdex_name.c_str()
+              << vdex_name
               << ": error "
-              << error_msg.c_str()
+              << error_msg
               << std::endl;
   }
   // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
@@ -315,18 +347,91 @@
     ProcessOneDexMapping(pagemap,
                          pm_map_start(map),
                          dex_file.get(),
-                         reinterpret_cast<uint64_t>(vdex->Begin()));
+                         reinterpret_cast<uint64_t>(vdex->Begin()),
+                         printer);
   }
   free(pagemap);
   return true;
 }
 
+static void ProcessOneOatMapping(uint64_t* pagemap, size_t size, Printer* printer) {
+  size_t resident_page_count = 0;
+  for (size_t page = 0; page < size; ++page) {
+    char type_char = '.';
+    if (PM_PAGEMAP_PRESENT(pagemap[page])) {
+      ++resident_page_count;
+      type_char = '*';
+    }
+    if (g_verbose) {
+      std::cout << type_char;
+      if (page % kLineLength == kLineLength - 1) {
+        std::cout << std::endl;
+      }
+    }
+  }
+  if (g_verbose) {
+    if (size % kLineLength != 0) {
+      std::cout << std::endl;
+    }
+  }
+  double percent_of_total = 100.0 * resident_page_count / size;
+  printer->PrintHeader();
+  printer->PrintOne("EXECUTABLE", resident_page_count, size, percent_of_total, percent_of_total);
+  printer->PrintSkipLine();
+}
+
+static bool DisplayMappingIfFromOatFile(pm_map_t* map, Printer* printer) {
+  // Confirm that the map is from a vdex file.
+  static const char* suffixes[] = { ".odex", ".oat" };
+  std::string vdex_name;
+  bool found = false;
+  for (size_t j = 0; j < sizeof(suffixes) / sizeof(suffixes[0]); ++j) {
+    if (strstr(pm_map_name(map), suffixes[j]) != nullptr) {
+      vdex_name = pm_map_name(map);
+      found = true;
+      break;
+    }
+  }
+  if (!found) {
+    return true;
+  }
+  // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
+  uint64_t* pagemap;
+  size_t len;
+  if (pm_map_pagemap(map, &pagemap, &len) != 0) {
+    std::cerr << "Error creating pagemap." << std::endl;
+    return false;
+  }
+  // Process the dex files.
+  std::cout << "MAPPING "
+            << pm_map_name(map)
+            << StringPrintf(": %zx-%zx", pm_map_start(map), pm_map_end(map))
+            << std::endl;
+  ProcessOneOatMapping(pagemap, len, printer);
+  free(pagemap);
+  return true;
+}
+
+static bool FilterByNameContains(const std::string& mapped_file_name,
+                                 const std::vector<std::string>& name_filters) {
+  // If no filters were set, everything matches.
+  if (name_filters.empty()) {
+    return true;
+  }
+  for (const auto& name_contains : name_filters) {
+    if (mapped_file_name.find(name_contains) != std::string::npos) {
+      return true;
+    }
+  }
+  return false;
+}
 
 static void Usage(const char* cmd) {
-  std::cerr << "Usage: " << cmd << " [-k] [-s] [-v] pid" << std::endl
-            << "    -k Shows a key to verbose display characters." << std::endl
-            << "    -s Shows section statistics for individual dex files." << std::endl
-            << "    -v Verbosely displays resident pages for dex files." << std::endl;
+  std::cerr << "Usage: " << cmd << " [options] pid" << std::endl
+            << "    --contains=<string>:  Display sections containing string." << std::endl
+            << "    --help:               Shows this message." << std::endl
+            << "    --verbose:            Makes displays verbose." << std::endl;
+  PrintLetterKey();
 }
 
 static int DexDiagMain(int argc, char* argv[]) {
@@ -335,14 +440,18 @@
     return EXIT_FAILURE;
   }
 
+  std::vector<std::string> name_filters;
   // TODO: add option to track usage by class name, etc.
   for (int i = 1; i < argc - 1; ++i) {
-    if (strcmp(argv[i], "-k") == 0) {
-      g_show_key = true;
-    } else if (strcmp(argv[i], "-s") == 0) {
-      g_show_statistics = true;
-    } else if (strcmp(argv[i], "-v") == 0) {
+    const StringPiece option(argv[i]);
+    if (option == "--help") {
+      Usage(argv[0]);
+      return EXIT_SUCCESS;
+    } else if (option == "--verbose") {
       g_verbose = true;
+    } else if (option.starts_with("--contains=")) {
+      std::string contains(option.substr(strlen("--contains=")).data());
+      name_filters.push_back(contains);
     } else {
       Usage(argv[0]);
       return EXIT_FAILURE;
@@ -387,16 +496,21 @@
   }
 
   // Process the mappings that are due to DEX files.
+  Printer printer;
   for (size_t i = 0; i < num_maps; ++i) {
-    if (!DisplayMappingIfFromVdexFile(maps[i])) {
+    std::string mapped_file_name = pm_map_name(maps[i]);
+    // Filter by name contains options (if any).
+    if (!FilterByNameContains(mapped_file_name, name_filters)) {
+      continue;
+    }
+    if (!DisplayMappingIfFromVdexFile(maps[i], &printer)) {
+      return EXIT_FAILURE;
+    } else if (!DisplayMappingIfFromOatFile(maps[i], &printer)) {
       return EXIT_FAILURE;
     }
   }
 
-  if (g_show_key) {
-    PrintLetterKey();
-  }
-  return 0;
+  return EXIT_SUCCESS;
 }
 
 }  // namespace art
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 0536f322..344d735 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -393,6 +393,7 @@
       index = dec_insn->VRegB();
       secondary_index = dec_insn->VRegH();
       width = 4;
+      break;
     default:
       break;
   }  // switch
@@ -1816,7 +1817,15 @@
       output_location += "/" + dex_file_location + ".new";
     }
     new_file.reset(OS::CreateEmptyFile(output_location.c_str()));
-    ftruncate(new_file->Fd(), header_->FileSize());
+    if (new_file == nullptr) {
+      LOG(ERROR) << "Could not create dex writer output file: " << output_location;
+      return;
+    }
+    if (ftruncate(new_file->Fd(), header_->FileSize()) != 0) {
+      LOG(ERROR) << "Could not grow dex writer output file: " << output_location;;
+      new_file->Erase();
+      return;
+    }
     mem_map_.reset(MemMap::MapFile(header_->FileSize(), PROT_READ | PROT_WRITE, MAP_SHARED,
         new_file->Fd(), 0, /*low_4gb*/ false, output_location.c_str(), &error_msg));
   } else {
@@ -1825,7 +1834,7 @@
   }
   if (mem_map_ == nullptr) {
     LOG(ERROR) << "Could not create mem map for dex writer output: " << error_msg;
-    if (new_file.get() != nullptr) {
+    if (new_file != nullptr) {
       new_file->Erase();
     }
     return;
diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h
index 5e7b51d..cfcd6a7 100644
--- a/runtime/arch/arm64/asm_support_arm64.h
+++ b/runtime/arch/arm64/asm_support_arm64.h
@@ -24,4 +24,18 @@
 #define FRAME_SIZE_SAVE_REFS_AND_ARGS 224
 #define FRAME_SIZE_SAVE_EVERYTHING 512
 
+// The offset from art_quick_read_barrier_mark_introspection to the array switch cases,
+// i.e. art_quick_read_barrier_mark_introspection_arrays.
+#define BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET 0x100
+// The offset from art_quick_read_barrier_mark_introspection to the GC root entrypoint,
+// i.e. art_quick_read_barrier_mark_introspection_gc_roots.
+#define BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET 0x300
+
+// The offset of the reference load LDR from the return address in LR for field loads.
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET -4
+// The offset of the reference load LDR from the return address in LR for array loads.
+#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET -4
+// The offset of the reference load LDR from the return address in LR for GC root loads.
+#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET -8
+
 #endif  // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 6add107..bc7bcb1 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -17,6 +17,7 @@
 #include <math.h>
 #include <string.h>
 
+#include "arch/arm64/asm_support_arm64.h"
 #include "entrypoints/jni/jni_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
@@ -70,6 +71,10 @@
 extern "C" mirror::Object* art_quick_read_barrier_mark_reg28(mirror::Object*);
 extern "C" mirror::Object* art_quick_read_barrier_mark_reg29(mirror::Object*);
 
+extern "C" mirror::Object* art_quick_read_barrier_mark_introspection(mirror::Object*);
+extern "C" mirror::Object* art_quick_read_barrier_mark_introspection_arrays(mirror::Object*);
+extern "C" mirror::Object* art_quick_read_barrier_mark_introspection_gc_roots(mirror::Object*);
+
 void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_marking) {
   // ARM64 is the architecture with the largest number of core
   // registers (32) that supports the read barrier configuration.
@@ -109,6 +114,21 @@
   qpoints->pReadBarrierMarkReg27 = is_marking ? art_quick_read_barrier_mark_reg27 : nullptr;
   qpoints->pReadBarrierMarkReg28 = is_marking ? art_quick_read_barrier_mark_reg28 : nullptr;
   qpoints->pReadBarrierMarkReg29 = is_marking ? art_quick_read_barrier_mark_reg29 : nullptr;
+
+  // Check that array switch cases are at appropriate offsets from the introspection entrypoint.
+  DCHECK_ALIGNED(art_quick_read_barrier_mark_introspection, 512u);
+  intptr_t array_diff =
+      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_arrays) -
+      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
+  DCHECK_EQ(BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET, array_diff);
+  // Check that the GC root entrypoint is at appropriate offset from the introspection entrypoint.
+  intptr_t gc_roots_diff =
+      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_gc_roots) -
+      reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
+  DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET, gc_roots_diff);
+  // The register 16, i.e. IP0, is reserved, so there is no art_quick_read_barrier_mark_reg16.
+  // We're using the entry to hold a pointer to the introspection entrypoint instead.
+  qpoints->pReadBarrierMarkReg16 = is_marking ? art_quick_read_barrier_mark_introspection : nullptr;
 }
 
 void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index b2bbd0d..c7fa7f5 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2494,6 +2494,240 @@
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg28, w28, x28
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, w29, x29
 
+
+.macro SELECT_X_OR_W_FOR_MACRO macro_to_use, x, w, xreg
+    .if \xreg
+      \macro_to_use \x
+    .else
+      \macro_to_use \w
+    .endif
+.endm
+
+.macro FOR_REGISTERS macro_for_register, macro_for_reserved_register, xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x0, w0, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x1, w1, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x2, w2, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x3, w3, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x4, w4, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x5, w5, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x6, w6, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x7, w7, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x8, w8, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x9, w9, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x10, w10, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x11, w11, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x12, w12, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x13, w13, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x14, w14, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x15, w15, \xreg
+    \macro_for_reserved_register  // IP0 is reserved
+    \macro_for_reserved_register  // IP1 is reserved
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x18, w18, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x19, w19, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x20, w20, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x21, w21, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x22, w22, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x23, w23, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x24, w24, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x25, w25, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x26, w26, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x27, w27, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x28, w28, \xreg
+    SELECT_X_OR_W_FOR_MACRO \macro_for_register, x29, w29, \xreg
+    \macro_for_reserved_register  // lr is reserved
+    \macro_for_reserved_register  // sp is reserved
+.endm
+
+.macro FOR_XREGISTERS macro_for_register, macro_for_reserved_register
+    FOR_REGISTERS \macro_for_register, \macro_for_reserved_register, /* xreg */ 1
+.endm
+
+.macro FOR_WREGISTERS macro_for_register, macro_for_reserved_register
+    FOR_REGISTERS \macro_for_register, \macro_for_reserved_register, /* xreg */ 0
+.endm
+
+.macro BRK0_BRK0
+    brk 0
+    brk 0
+.endm
+
+#if BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET != BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET
+#error "Array and field introspection code sharing requires same LDR offset."
+#endif
+.macro INTROSPECTION_ARRAY_LOAD index_reg
+    ldr   wIP0, [xIP0, \index_reg, lsl #2]
+    b     art_quick_read_barrier_mark_introspection
+.endm
+
+.macro MOV_WIP0_TO_WREG_AND_BL_LR reg
+    mov   \reg, wIP0
+    br    lr  // Do not use RET as we do not enter the entrypoint with "BL".
+.endm
+
+.macro READ_BARRIER_MARK_INTROSPECTION_SLOW_PATH ldr_offset
+    /*
+     * Allocate 44 stack slots * 8 = 352 bytes:
+     * - 19 slots for core registers X0-15, X18-X19, LR
+     * - 1 slot padding
+     * - 24 slots for floating-point registers D0-D7 and D16-D31
+     */
+    // Save all potentially live caller-save core registers.
+    SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 352
+    SAVE_TWO_REGS  x2,  x3, 16
+    SAVE_TWO_REGS  x4,  x5, 32
+    SAVE_TWO_REGS  x6,  x7, 48
+    SAVE_TWO_REGS  x8,  x9, 64
+    SAVE_TWO_REGS x10, x11, 80
+    SAVE_TWO_REGS x12, x13, 96
+    SAVE_TWO_REGS x14, x15, 112
+    SAVE_TWO_REGS x18, x19, 128       // Skip x16, x17, i.e. IP0, IP1.
+    SAVE_REG      xLR,      144       // Save return address, skip padding at 152.
+    // Save all potentially live caller-save floating-point registers.
+    stp   d0, d1,   [sp, #160]
+    stp   d2, d3,   [sp, #176]
+    stp   d4, d5,   [sp, #192]
+    stp   d6, d7,   [sp, #208]
+    stp   d16, d17, [sp, #224]
+    stp   d18, d19, [sp, #240]
+    stp   d20, d21, [sp, #256]
+    stp   d22, d23, [sp, #272]
+    stp   d24, d25, [sp, #288]
+    stp   d26, d27, [sp, #304]
+    stp   d28, d29, [sp, #320]
+    stp   d30, d31, [sp, #336]
+
+    mov   x0, xIP0
+    bl    artReadBarrierMark          // artReadBarrierMark(obj)
+    mov   xIP0, x0
+
+    // Restore core regs, except x0 and x1 as the return register switch case
+    // address calculation is smoother with an extra register.
+    RESTORE_TWO_REGS  x2,  x3, 16
+    RESTORE_TWO_REGS  x4,  x5, 32
+    RESTORE_TWO_REGS  x6,  x7, 48
+    RESTORE_TWO_REGS  x8,  x9, 64
+    RESTORE_TWO_REGS x10, x11, 80
+    RESTORE_TWO_REGS x12, x13, 96
+    RESTORE_TWO_REGS x14, x15, 112
+    RESTORE_TWO_REGS x18, x19, 128    // Skip x16, x17, i.e. IP0, IP1.
+    RESTORE_REG      xLR,      144    // Restore return address.
+    // Save all potentially live caller-save floating-point registers.
+    stp   d0, d1,   [sp, #160]
+    stp   d2, d3,   [sp, #176]
+    stp   d4, d5,   [sp, #192]
+    stp   d6, d7,   [sp, #208]
+    stp   d16, d17, [sp, #224]
+    stp   d18, d19, [sp, #240]
+    stp   d20, d21, [sp, #256]
+    stp   d22, d23, [sp, #272]
+    stp   d24, d25, [sp, #288]
+    stp   d26, d27, [sp, #304]
+    stp   d28, d29, [sp, #320]
+    stp   d30, d31, [sp, #336]
+
+    ldr   x0, [lr, #\ldr_offset]      // Load the instruction.
+    adr   xIP1, .Lmark_introspection_return_switch
+    bfi   xIP1, x0, #3, #5            // Calculate switch case address.
+    RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 352
+    br    xIP1
+.endm
+
+    /*
+     * Use introspection to load a reference from the same address as the LDR
+     * instruction in generated code would load (unless loaded by the thunk,
+     * see below), call ReadBarrier::Mark() with that reference if needed
+     * and return it in the same register as the LDR instruction would load.
+     *
+     * The entrypoint is called through a thunk that differs across load kinds.
+     * For field and array loads the LDR instruction in generated code follows
+     * the branch to the thunk, i.e. the LDR is at [LR, #-4], and the thunk
+     * knows the holder and performs the gray bit check, returning to the LDR
+     * instruction if the object is not gray, so this entrypoint no longer
+     * needs to know anything about the holder. For GC root loads, the LDR
+     * instruction in generated code precedes the branch to the thunk (i.e.
+     * the LDR is at [LR, #-8]) and the thunk does not do the gray bit check.
+     *
+     * For field accesses and array loads with a constant index the thunk loads
+     * the reference into IP0 using introspection and calls the main entrypoint,
+     * art_quick_read_barrier_mark_introspection.
+     *
+     * For array accesses with non-constant index, the thunk inserts the bits
+     * 16-21 of the LDR instruction to the entrypoint address, effectively
+     * calculating a switch case label based on the index register (bits 16-20)
+     * and adding an extra offset (bit 21 is set) to differentiate from the
+     * main entrypoint, then moves the base register to IP0 and jumps to the
+     * switch case. Therefore we need to align the main entrypoint to 512 bytes,
+     * accounting for a 256-byte offset followed by 32 array entrypoints
+     * starting at art_quick_read_barrier_mark_introspection_arrays, each
+     * containing an LDR (register) and a branch to the main entrypoint.
+     *
+     * For GC root accesses we cannot use the main entrypoint because of the
+     * different offset where the LDR instruction in generated code is located.
+     * To re-use the same entrypoint pointer in generated code, we make sure
+     * that the gc root entrypoint (a copy of the entrypoint with a different
+     * offset for introspection loads) is located at a known offset (768 bytes,
+     * or BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRYPOINT_OFFSET) from the main
+     * entrypoint and the GC root thunk adjusts the entrypoint pointer, moves
+     * the root register to IP0 and jumps to the customized entrypoint,
+     * art_quick_read_barrier_mark_introspection_gc_roots. The thunk also
+     * performs all the fast-path checks, so we need just the slow path.
+     *
+     * The code structure is
+     *   art_quick_read_barrier_mark_introspection:
+     *     Up to 256 bytes for the main entrypoint code.
+     *     Padding to 256 bytes if needed.
+     *   art_quick_read_barrier_mark_introspection_arrays:
+     *     Exactly 256 bytes for array load switch cases (32x2 instructions).
+     *   .Lmark_introspection_return_switch:
+     *     Exactly 256 bytes for return switch cases (32x2 instructions).
+     *   art_quick_read_barrier_mark_introspection_gc_roots:
+     *     GC root entrypoint code.
+     */
+    .balign 512
+ENTRY art_quick_read_barrier_mark_introspection
+    // At this point, IP0 contains the reference, IP1 can be freely used.
+    // If reference is null, just return it in the right register.
+    cbz   wIP0, .Lmark_introspection_return
+    // Use wIP1 as temp and check the mark bit of the reference.
+    ldr   wIP1, [xIP0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
+    tbz   wIP1, #LOCK_WORD_MARK_BIT_SHIFT, .Lmark_introspection_unmarked
+.Lmark_introspection_return:
+    // Without an extra register for the return switch case address calculation,
+    // we exploit the high word of the xIP0 to temporarily store the ref_reg*8,
+    // so the return switch below must move wIP0 instead of xIP0 to the register.
+    ldr   wIP1, [lr, #BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET]  // Load the instruction.
+    bfi   xIP0, xIP1, #(32 + 3), #5   // Extract ref_reg*8 to high word in xIP0.
+    adr   xIP1, .Lmark_introspection_return_switch
+    bfxil xIP1, xIP0, #32, #8         // Calculate return switch case address.
+    br    xIP1
+.Lmark_introspection_unmarked:
+    // Check if the top two bits are one, if this is the case it is a forwarding address.
+    tst   wIP1, wIP1, lsl #1
+    bmi   .Lmark_introspection_forwarding_address
+    READ_BARRIER_MARK_INTROSPECTION_SLOW_PATH BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET
+
+.Lmark_introspection_forwarding_address:
+    // Shift left by the forwarding address shift. This clears out the state bits since they are
+    // in the top 2 bits of the lock word.
+    lsl   wIP0, wIP1, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT
+    b .Lmark_introspection_return
+
+    // We're very close to the alloted 256B for the entrypoint code before the
+    // array switch cases. Should we go a little bit over the limit, we can
+    // move some code after the array switch cases and return switch cases.
+    .balign 256
+    .hidden art_quick_read_barrier_mark_introspection_arrays
+    .global art_quick_read_barrier_mark_introspection_arrays
+art_quick_read_barrier_mark_introspection_arrays:
+    FOR_XREGISTERS INTROSPECTION_ARRAY_LOAD, BRK0_BRK0
+.Lmark_introspection_return_switch:
+    FOR_WREGISTERS MOV_WIP0_TO_WREG_AND_BL_LR, BRK0_BRK0
+    .hidden art_quick_read_barrier_mark_introspection_gc_roots
+    .global art_quick_read_barrier_mark_introspection_gc_roots
+art_quick_read_barrier_mark_introspection_gc_roots:
+    READ_BARRIER_MARK_INTROSPECTION_SLOW_PATH BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET
+END art_quick_read_barrier_mark_introspection
+
 .extern artInvokePolymorphic
 ENTRY art_quick_invoke_polymorphic
     SETUP_SAVE_REFS_AND_ARGS_FRAME                // Save callee saves in case allocation triggers GC.
diff --git a/runtime/arch/mips/entrypoints_direct_mips.h b/runtime/arch/mips/entrypoints_direct_mips.h
index 937cd1e..1020781 100644
--- a/runtime/arch/mips/entrypoints_direct_mips.h
+++ b/runtime/arch/mips/entrypoints_direct_mips.h
@@ -47,7 +47,24 @@
       entrypoint == kQuickCmplFloat ||
       entrypoint == kQuickReadBarrierJni ||
       entrypoint == kQuickReadBarrierSlow ||
-      entrypoint == kQuickReadBarrierForRootSlow;
+      entrypoint == kQuickReadBarrierForRootSlow ||
+      entrypoint == kQuickCos ||
+      entrypoint == kQuickSin ||
+      entrypoint == kQuickAcos ||
+      entrypoint == kQuickAsin ||
+      entrypoint == kQuickAtan ||
+      entrypoint == kQuickAtan2 ||
+      entrypoint == kQuickCbrt ||
+      entrypoint == kQuickCosh ||
+      entrypoint == kQuickExp ||
+      entrypoint == kQuickExpm1 ||
+      entrypoint == kQuickHypot ||
+      entrypoint == kQuickLog ||
+      entrypoint == kQuickLog10 ||
+      entrypoint == kQuickNextAfter ||
+      entrypoint == kQuickSinh ||
+      entrypoint == kQuickTan ||
+      entrypoint == kQuickTanh;
 }
 
 }  // namespace art
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 2349620..434e33c 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -312,6 +312,42 @@
   qpoints->pUshrLong = art_quick_ushr_long;
   static_assert(!IsDirectEntrypoint(kQuickUshrLong), "Non-direct C stub marked direct.");
 
+  // More math.
+  qpoints->pCos = cos;
+  static_assert(IsDirectEntrypoint(kQuickCos), "Direct C stub marked non-direct.");
+  qpoints->pSin = sin;
+  static_assert(IsDirectEntrypoint(kQuickSin), "Direct C stub marked non-direct.");
+  qpoints->pAcos = acos;
+  static_assert(IsDirectEntrypoint(kQuickAcos), "Direct C stub marked non-direct.");
+  qpoints->pAsin = asin;
+  static_assert(IsDirectEntrypoint(kQuickAsin), "Direct C stub marked non-direct.");
+  qpoints->pAtan = atan;
+  static_assert(IsDirectEntrypoint(kQuickAtan), "Direct C stub marked non-direct.");
+  qpoints->pAtan2 = atan2;
+  static_assert(IsDirectEntrypoint(kQuickAtan2), "Direct C stub marked non-direct.");
+  qpoints->pCbrt = cbrt;
+  static_assert(IsDirectEntrypoint(kQuickCbrt), "Direct C stub marked non-direct.");
+  qpoints->pCosh = cosh;
+  static_assert(IsDirectEntrypoint(kQuickCosh), "Direct C stub marked non-direct.");
+  qpoints->pExp = exp;
+  static_assert(IsDirectEntrypoint(kQuickExp), "Direct C stub marked non-direct.");
+  qpoints->pExpm1 = expm1;
+  static_assert(IsDirectEntrypoint(kQuickExpm1), "Direct C stub marked non-direct.");
+  qpoints->pHypot = hypot;
+  static_assert(IsDirectEntrypoint(kQuickHypot), "Direct C stub marked non-direct.");
+  qpoints->pLog = log;
+  static_assert(IsDirectEntrypoint(kQuickLog), "Direct C stub marked non-direct.");
+  qpoints->pLog10 = log10;
+  static_assert(IsDirectEntrypoint(kQuickLog10), "Direct C stub marked non-direct.");
+  qpoints->pNextAfter = nextafter;
+  static_assert(IsDirectEntrypoint(kQuickNextAfter), "Direct C stub marked non-direct.");
+  qpoints->pSinh = sinh;
+  static_assert(IsDirectEntrypoint(kQuickSinh), "Direct C stub marked non-direct.");
+  qpoints->pTan = tan;
+  static_assert(IsDirectEntrypoint(kQuickTan), "Direct C stub marked non-direct.");
+  qpoints->pTanh = tanh;
+  static_assert(IsDirectEntrypoint(kQuickTanh), "Direct C stub marked non-direct.");
+
   // Intrinsics
   qpoints->pIndexOf = art_quick_indexof;
   static_assert(!IsDirectEntrypoint(kQuickIndexOf), "Non-direct C stub marked direct.");
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
index 66405cb..f8242ae 100644
--- a/runtime/arch/mips64/entrypoints_init_mips64.cc
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -137,6 +137,25 @@
   qpoints->pShrLong = nullptr;
   qpoints->pUshrLong = nullptr;
 
+  // More math.
+  qpoints->pCos = cos;
+  qpoints->pSin = sin;
+  qpoints->pAcos = acos;
+  qpoints->pAsin = asin;
+  qpoints->pAtan = atan;
+  qpoints->pAtan2 = atan2;
+  qpoints->pCbrt = cbrt;
+  qpoints->pCosh = cosh;
+  qpoints->pExp = exp;
+  qpoints->pExpm1 = expm1;
+  qpoints->pHypot = hypot;
+  qpoints->pLog = log;
+  qpoints->pLog10 = log10;
+  qpoints->pNextAfter = nextafter;
+  qpoints->pSinh = sinh;
+  qpoints->pTan = tan;
+  qpoints->pTanh = tanh;
+
   // Intrinsics
   qpoints->pIndexOf = art_quick_indexof;
   qpoints->pStringCompareTo = art_quick_string_compareto;
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index e763e43..935fd81 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -78,6 +78,7 @@
   "RegAllocator ",
   "RegAllocVldt ",
   "StackMapStm  ",
+  "VectorNode   ",
   "CodeGen      ",
   "Assembler    ",
   "ParallelMove ",
@@ -150,8 +151,15 @@
 #if __clang_major__ >= 4
 #pragma GCC diagnostic ignored "-Winstantiation-after-specialization"
 #endif
-// Explicitly instantiate the used implementation.
-template class ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations>;
+// We're going to use ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations> which needs
+// to be explicitly instantiated if kArenaAllocatorCountAllocations is true. Explicit
+// instantiation of the specialization ArenaAllocatorStatsImpl<false> does not do anything
+// but requires the warning "-Winstantiation-after-specialization" to be turned off.
+//
+// To avoid bit-rot of the ArenaAllocatorStatsImpl<true>, instantiate it also in debug builds
+// (but keep the unnecessary code out of release builds) as we do not usually compile with
+// kArenaAllocatorCountAllocations set to true.
+template class ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations || kIsDebugBuild>;
 #pragma GCC diagnostic pop
 
 void ArenaAllocatorMemoryTool::DoMakeDefined(void* ptr, size_t size) {
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index a173ac2..7a9184e 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -56,6 +56,7 @@
   bool image;
   bool systrace_lock_logging;  // Enabled with "-verbose:sys-locks".
   bool agents;
+  bool dex;  // Some dex access output etc.
 };
 
 // Global log verbosity setting, initialized by InitLogging.
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index 44a84c8..08b370e 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -89,13 +89,14 @@
     // Check if a bad Mutex of this level or lower is held.
     bool bad_mutexes_held = false;
     for (int i = level_; i >= 0; --i) {
-      BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i));
-      if (UNLIKELY(held_mutex != nullptr)) {
+      LockLevel lock_level_i = static_cast<LockLevel>(i);
+      BaseMutex* held_mutex = self->GetHeldMutex(lock_level_i);
+      if (UNLIKELY(held_mutex != nullptr) && lock_level_i != kAbortLock) {
         LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" "
-                   << "(level " << LockLevel(i) << " - " << i
+                   << "(level " << lock_level_i << " - " << i
                    << ") while locking \"" << name_ << "\" "
                    << "(level " << level_ << " - " << static_cast<int>(level_) << ")";
-        if (i > kAbortLock) {
+        if (lock_level_i > kAbortLock) {
           // Only abort in the check below if this is more than abort level lock.
           bad_mutexes_held = true;
         }
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 868d8df..039b60a 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -2453,7 +2453,7 @@
   bool needs_resume;
   {
     MutexLock mu2(soa.Self(), *Locks::thread_suspend_count_lock_);
-    needs_resume = thread->GetSuspendCount() > 0;
+    needs_resume = thread->GetDebugSuspendCount() > 0;
   }
   if (needs_resume) {
     Runtime::Current()->GetThreadList()->Resume(thread, true);
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index e884e39..41db4d8 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -59,11 +59,17 @@
 }
 
 inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const {
+  if (!idx.IsValid()) {
+    return nullptr;
+  }
   const TypeId& type_id = GetTypeId(idx);
   return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length);
 }
 
 inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx) const {
+  if (!idx.IsValid()) {
+    return nullptr;
+  }
   const TypeId& type_id = GetTypeId(idx);
   return StringDataByIdx(type_id.descriptor_idx_);
 }
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 85100ae..688f95b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -590,6 +590,10 @@
 
 void DexFile::InitializeSectionsFromMapList() {
   const MapList* map_list = reinterpret_cast<const MapList*>(begin_ + header_->map_off_);
+  if (header_->map_off_ == 0 || header_->map_off_ > size_) {
+    // Bad offset. The dex file verifier runs after this method and will reject the file.
+    return;
+  }
   const size_t count = map_list->size_;
 
   size_t map_limit = header_->map_off_ + count * sizeof(MapItem);
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 9131715..f811287 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -161,6 +161,16 @@
   "ACACAAALABgAAAAAAAAAAACgge4CAABjbGFzc2VzLmRleFVUBQADAWPlV3V4CwABBOQDAQAEiBMA"
   "AFBLBQYAAAAAAwADAPUAAABkBAAAAAA=";
 
+static const char kRawDexBadMapOffset[] =
+  "ZGV4CjAzNQAZKGSz85r+tXJ1I24FYi+FpQtWbXtelAmoAQAAcAAAAHhWNBIAAAAAAAAAAEAwIBAF"
+  "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADcAAAAzAAAAOQA"
+  "AADsAAAA9AAAAPkAAAANAQAAAgAAAAMAAAAEAAAABAAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAA"
+  "AAAAAAABAAAAAQAAAAAAAAABAAAAAAAAABUBAAAAAAAAAQABAAEAAAAQAQAABAAAAHAQAQAAAA4A"
+  "Bjxpbml0PgAGQS5qYXZhAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgABAAcOAAAAAQAAgYAE"
+  "zAEACwAAAAAAAAABAAAAAAAAAAEAAAAFAAAAcAAAAAIAAAADAAAAhAAAAAMAAAABAAAAkAAAAAUA"
+  "AAACAAAAnAAAAAYAAAABAAAArAAAAAEgAAABAAAAzAAAAAIgAAAFAAAA5AAAAAMgAAABAAAAEAEA"
+  "AAAgAAABAAAAFQEAAAAQAAABAAAAIAEAAA==";
+
 static void DecodeAndWriteDexFile(const char* base64, const char* location) {
   // decode base64
   CHECK(base64 != nullptr);
@@ -212,7 +222,8 @@
 
 static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base64,
                                                                 const char* location,
-                                                                uint32_t location_checksum) {
+                                                                uint32_t location_checksum,
+                                                                bool expect_success) {
   CHECK(base64 != nullptr);
   std::vector<uint8_t> dex_bytes = DecodeBase64Vec(base64);
   CHECK_NE(dex_bytes.size(), 0u);
@@ -232,7 +243,11 @@
                                                         /* verify */ true,
                                                         /* verify_checksum */ true,
                                                         &error_message));
-  CHECK(dex_file != nullptr) << error_message;
+  if (expect_success) {
+    CHECK(dex_file != nullptr) << error_message;
+  } else {
+    CHECK(dex_file == nullptr) << "Expected dex file open to fail.";
+  }
   return dex_file;
 }
 
@@ -282,7 +297,7 @@
 TEST_F(DexFileTest, HeaderInMemory) {
   ScratchFile tmp;
   std::unique_ptr<const DexFile> raw =
-      OpenDexFileInMemoryBase64(kRawDex, tmp.GetFilename().c_str(), 0x00d87910U);
+      OpenDexFileInMemoryBase64(kRawDex, tmp.GetFilename().c_str(), 0x00d87910U, true);
   ValidateDexFileHeader(std::move(raw));
 }
 
@@ -569,4 +584,18 @@
   EXPECT_EQ(dex_files.size(), 3u);
 }
 
+TEST_F(DexFileTest, OpenDexBadMapOffset) {
+  ScratchFile tmp;
+  std::unique_ptr<const DexFile> raw =
+      OpenDexFileInMemoryBase64(kRawDexBadMapOffset, tmp.GetFilename().c_str(), 0xb3642819U, false);
+  EXPECT_EQ(raw, nullptr);
+}
+
+TEST_F(DexFileTest, GetStringWithNoIndex) {
+  ScratchFile tmp;
+  std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str()));
+  dex::TypeIndex idx;
+  EXPECT_EQ(raw->StringByTypeIdx(idx), nullptr);
+}
+
 }  // namespace art
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 25073a8..354ae20 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1075,7 +1075,7 @@
               << called_method.dex_file->PrettyMethod(called_method.dex_method_index);
         }
       } else {
-        VLOG(oat) << "Accessed dex file for invoke " << invoke_type << " "
+        VLOG(dex) << "Accessed dex file for invoke " << invoke_type << " "
                   << called_method.dex_method_index;
       }
     } else {
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 24ba52f..bcf5008 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -1108,7 +1108,9 @@
     space::RegionSpace* region_space = collector->RegionSpace();
     CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
     VerifyNoFromSpaceRefsFieldVisitor visitor(collector);
-    obj->VisitReferences(visitor, visitor);
+    obj->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
+        visitor,
+        visitor);
     if (kUseBakerReadBarrier) {
       CHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::WhiteState())
           << "obj=" << obj << " non-white rb_state " << obj->GetReadBarrierState();
@@ -1232,7 +1234,9 @@
     CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
     collector->AssertToSpaceInvariant(nullptr, MemberOffset(0), obj);
     AssertToSpaceInvariantFieldVisitor visitor(collector);
-    obj->VisitReferences(visitor, visitor);
+    obj->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
+        visitor,
+        visitor);
   }
 
  private:
@@ -1471,8 +1475,7 @@
     // Add to the live bytes per unevacuated from space. Note this code is always run by the
     // GC-running thread (no synchronization required).
     DCHECK(region_space_bitmap_->Test(to_ref));
-    // Disable the read barrier in SizeOf for performance, which is safe.
-    size_t obj_size = to_ref->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>();
+    size_t obj_size = to_ref->SizeOf<kDefaultVerifyFlags>();
     size_t alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
     region_space_->AddLiveBytes(to_ref, alloc_size);
   }
@@ -1714,16 +1717,19 @@
 }
 
 // Assert the to-space invariant.
-void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset,
+void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj,
+                                               MemberOffset offset,
                                                mirror::Object* ref) {
-  CHECK(heap_->collector_type_ == kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
+  CHECK_EQ(heap_->collector_type_, kCollectorTypeCC);
   if (is_asserting_to_space_invariant_) {
-    if (region_space_->IsInToSpace(ref)) {
+    using RegionType = space::RegionSpace::RegionType;
+    space::RegionSpace::RegionType type = region_space_->GetRegionType(ref);
+    if (type == RegionType::kRegionTypeToSpace) {
       // OK.
       return;
-    } else if (region_space_->IsInUnevacFromSpace(ref)) {
+    } else if (type == RegionType::kRegionTypeUnevacFromSpace) {
       CHECK(IsMarkedInUnevacFromSpace(ref)) << ref;
-    } else if (region_space_->IsInFromSpace(ref)) {
+    } else if (UNLIKELY(type == RegionType::kRegionTypeFromSpace)) {
       // Not OK. Do extra logging.
       if (obj != nullptr) {
         LogFromSpaceRefHolder(obj, offset);
@@ -1888,10 +1894,10 @@
   explicit RefFieldsVisitor(ConcurrentCopying* collector)
       : collector_(collector) {}
 
-  void operator()(ObjPtr<mirror::Object> obj, MemberOffset offset, bool /* is_static */)
+  void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */)
       const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES_SHARED(Locks::heap_bitmap_lock_) {
-    collector_->Process(obj.Ptr(), offset);
+    collector_->Process(obj, offset);
   }
 
   void operator()(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> ref) const
@@ -2064,7 +2070,7 @@
     AssertToSpaceInvariant(nullptr, MemberOffset(0), java_lang_Object.Ptr());
     CHECK_EQ(byte_size, (java_lang_Object->GetObjectSize<kVerifyNone, kWithoutReadBarrier>()));
     dummy_obj->SetClass(java_lang_Object.Ptr());
-    CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone, kWithoutReadBarrier>()));
+    CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone>()));
   } else {
     // Use an int array.
     dummy_obj->SetClass(int_array_class);
@@ -2075,7 +2081,7 @@
     CHECK_EQ(dummy_arr->GetLength(), length)
         << "byte_size=" << byte_size << " length=" << length
         << " component_size=" << component_size << " data_offset=" << data_offset;
-    CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone, kWithoutReadBarrier>()))
+    CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone>()))
         << "byte_size=" << byte_size << " length=" << length
         << " component_size=" << component_size << " data_offset=" << data_offset;
   }
@@ -2142,10 +2148,10 @@
 
 mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref) {
   DCHECK(region_space_->IsInFromSpace(from_ref));
-  // No read barrier to avoid nested RB that might violate the to-space
-  // invariant. Note that from_ref is a from space ref so the SizeOf()
-  // call will access the from-space meta objects, but it's ok and necessary.
-  size_t obj_size = from_ref->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>();
+  // There must not be a read barrier to avoid nested RB that might violate the to-space invariant.
+  // Note that from_ref is a from space ref so the SizeOf() call will access the from-space meta
+  // objects, but it's ok and necessary.
+  size_t obj_size = from_ref->SizeOf<kDefaultVerifyFlags>();
   size_t region_space_alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
   size_t region_space_bytes_allocated = 0U;
   size_t non_moving_space_bytes_allocated = 0U;
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index c1c1cad..c35ec7c 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -29,6 +29,7 @@
     case kGcCauseBackground: return "Background";
     case kGcCauseExplicit: return "Explicit";
     case kGcCauseForNativeAlloc: return "NativeAlloc";
+    case kGcCauseForNativeAllocBackground: return "NativeAllocBackground";
     case kGcCauseCollectorTransition: return "CollectorTransition";
     case kGcCauseDisableMovingGc: return "DisableMovingGc";
     case kGcCauseHomogeneousSpaceCompact: return "HomogeneousSpaceCompact";
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index eb27547..41c8943 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -33,6 +33,8 @@
   kGcCauseExplicit,
   // GC triggered for a native allocation.
   kGcCauseForNativeAlloc,
+  // Background GC triggered for a native allocation.
+  kGcCauseForNativeAllocBackground,
   // GC triggered for a collector transition.
   kGcCauseCollectorTransition,
   // Not a real GC cause, used when we disable moving GC (currently for GetPrimitiveArrayCritical).
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index f04bc89..4a25610 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2337,9 +2337,7 @@
     size_t bin_size = object_addr - context->prev_;
     // Add the bin consisting of the end of the previous object to the start of the current object.
     collector->AddBin(bin_size, context->prev_);
-    // Turn off read barrier. ZygoteCompactingCollector doesn't use it (even in the CC build.)
-    context->prev_ = object_addr + RoundUp(obj->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>(),
-                                           kObjectAlignment);
+    context->prev_ = object_addr + RoundUp(obj->SizeOf<kDefaultVerifyFlags>(), kObjectAlignment);
   }
 
   void AddBin(size_t size, uintptr_t position) {
@@ -2359,8 +2357,7 @@
 
   virtual mirror::Object* MarkNonForwardedObject(mirror::Object* obj)
       REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
-    // Turn off read barrier. ZygoteCompactingCollector doesn't use it (even in the CC build.)
-    size_t obj_size = obj->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>();
+    size_t obj_size = obj->SizeOf<kDefaultVerifyFlags>();
     size_t alloc_size = RoundUp(obj_size, kObjectAlignment);
     mirror::Object* forward_address;
     // Find the smallest bin which we can move obj in.
@@ -2542,6 +2539,13 @@
     // to large objects.
     mod_union_table->SetCards();
   } else {
+    // Make sure to clear the zygote space cards so that we don't dirty pages in the next GC. There
+    // may be dirty cards from the zygote compaction or reference processing. These cards are not
+    // necessary to have marked since the zygote space may not refer to any objects not in the
+    // zygote or image spaces at this point.
+    mod_union_table->ProcessCards();
+    mod_union_table->ClearTable();
+
     // For CC we never collect zygote large objects. This means we do not need to set the cards for
     // the zygote mod-union table and we can also clear all of the existing image mod-union tables.
     // The existing mod-union tables are only for image spaces and may only reference zygote and
@@ -3682,20 +3686,21 @@
                                             ObjPtr<mirror::Object>* obj) {
   StackHandleScope<1> hs(self);
   HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
-  RequestConcurrentGC(self, force_full);
+  RequestConcurrentGC(self, kGcCauseBackground, force_full);
 }
 
 class Heap::ConcurrentGCTask : public HeapTask {
  public:
-  ConcurrentGCTask(uint64_t target_time, bool force_full)
-      : HeapTask(target_time), force_full_(force_full) { }
+  ConcurrentGCTask(uint64_t target_time, GcCause cause, bool force_full)
+      : HeapTask(target_time), cause_(cause), force_full_(force_full) {}
   virtual void Run(Thread* self) OVERRIDE {
     gc::Heap* heap = Runtime::Current()->GetHeap();
-    heap->ConcurrentGC(self, force_full_);
+    heap->ConcurrentGC(self, cause_, force_full_);
     heap->ClearConcurrentGCRequest();
   }
 
  private:
+  const GcCause cause_;
   const bool force_full_;  // If true, force full (or partial) collection.
 };
 
@@ -3709,18 +3714,19 @@
   concurrent_gc_pending_.StoreRelaxed(false);
 }
 
-void Heap::RequestConcurrentGC(Thread* self, bool force_full) {
+void Heap::RequestConcurrentGC(Thread* self, GcCause cause, bool force_full) {
   if (CanAddHeapTask(self) &&
       concurrent_gc_pending_.CompareExchangeStrongSequentiallyConsistent(false, true)) {
     task_processor_->AddTask(self, new ConcurrentGCTask(NanoTime(),  // Start straight away.
+                                                        cause,
                                                         force_full));
   }
 }
 
-void Heap::ConcurrentGC(Thread* self, bool force_full) {
+void Heap::ConcurrentGC(Thread* self, GcCause cause, bool force_full) {
   if (!Runtime::Current()->IsShuttingDown(self)) {
     // Wait for any GCs currently running to finish.
-    if (WaitForGcToComplete(kGcCauseBackground, self) == collector::kGcTypeNone) {
+    if (WaitForGcToComplete(cause, self) == collector::kGcTypeNone) {
       // If the we can't run the GC type we wanted to run, find the next appropriate one and try that
       // instead. E.g. can't do partial, so do full instead.
       collector::GcType next_gc_type = next_gc_type_;
@@ -3728,13 +3734,11 @@
       if (force_full && next_gc_type == collector::kGcTypeSticky) {
         next_gc_type = NonStickyGcType();
       }
-      if (CollectGarbageInternal(next_gc_type, kGcCauseBackground, false) ==
-          collector::kGcTypeNone) {
+      if (CollectGarbageInternal(next_gc_type, cause, false) == collector::kGcTypeNone) {
         for (collector::GcType gc_type : gc_plan_) {
           // Attempt to run the collector, if we succeed, we are done.
           if (gc_type > next_gc_type &&
-              CollectGarbageInternal(gc_type, kGcCauseBackground, false) !=
-                  collector::kGcTypeNone) {
+              CollectGarbageInternal(gc_type, cause, false) != collector::kGcTypeNone) {
             break;
           }
         }
@@ -3936,7 +3940,7 @@
     // Trigger another GC because there have been enough native bytes
     // allocated since the last GC.
     if (IsGcConcurrent()) {
-      RequestConcurrentGC(ThreadForEnv(env), /*force_full*/true);
+      RequestConcurrentGC(ThreadForEnv(env), kGcCauseForNativeAllocBackground, /*force_full*/true);
     } else {
       CollectGarbageInternal(NonStickyGcType(), kGcCauseForNativeAlloc, false);
     }
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 1a782b4..241d84c 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -330,7 +330,7 @@
 
   // Does a concurrent GC, should only be called by the GC daemon thread
   // through runtime.
-  void ConcurrentGC(Thread* self, bool force_full)
+  void ConcurrentGC(Thread* self, GcCause cause, bool force_full)
       REQUIRES(!Locks::runtime_shutdown_lock_, !*gc_complete_lock_, !*pending_task_lock_);
 
   // Implements VMDebug.countInstancesOfClass and JDWP VM_InstanceCount.
@@ -743,7 +743,8 @@
   void RequestTrim(Thread* self) REQUIRES(!*pending_task_lock_);
 
   // Request asynchronous GC.
-  void RequestConcurrentGC(Thread* self, bool force_full) REQUIRES(!*pending_task_lock_);
+  void RequestConcurrentGC(Thread* self, GcCause cause, bool force_full)
+      REQUIRES(!*pending_task_lock_);
 
   // Whether or not we may use a garbage collector, used so that we only create collectors we need.
   bool MayUseCollector(CollectorType type) const;
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index 515a6fd..6d426c2 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -72,6 +72,11 @@
   bitmap->Set(fake_end_of_heap_object);
 }
 
+TEST_F(HeapTest, DumpGCPerformanceOnShutdown) {
+  Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ false);
+  Runtime::Current()->SetDumpGCPerformanceOnShutdown(true);
+}
+
 class ZygoteHeapTest : public CommonRuntimeTest {
   void SetUpRuntimeOptions(RuntimeOptions* options) {
     CommonRuntimeTest::SetUpRuntimeOptions(options);
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index bd7c4ad..b6f8a17 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -925,8 +925,17 @@
     case mirror::MethodHandle::kInstancePut: {
       size_t obj_reg = is_range ? first_arg : args[0];
       size_t value_reg = is_range ? (first_arg + 1) : args[1];
-      JValue value = GetValueFromShadowFrame(shadow_frame, field_type, value_reg);
-      if (do_conversions && !ConvertArgumentValue(callsite_type, handle_type, 1, &value)) {
+      const size_t kPTypeIndex = 1;
+      // Use ptypes instead of field type since we may be unboxing a reference for a primitive
+      // field. The field type is incorrect for this case.
+      JValue value = GetValueFromShadowFrame(
+          shadow_frame,
+          callsite_type->GetPTypes()->Get(kPTypeIndex)->GetPrimitiveType(),
+          value_reg);
+      if (do_conversions && !ConvertArgumentValue(callsite_type,
+                                                  handle_type,
+                                                  kPTypeIndex,
+                                                  &value)) {
         DCHECK(self->IsExceptionPending());
         return false;
       }
@@ -940,8 +949,17 @@
         return false;
       }
       size_t value_reg = is_range ? first_arg : args[0];
-      JValue value = GetValueFromShadowFrame(shadow_frame, field_type, value_reg);
-      if (do_conversions && !ConvertArgumentValue(callsite_type, handle_type, 0, &value)) {
+      const size_t kPTypeIndex = 0;
+      // Use ptypes instead of field type since we may be unboxing a reference for a primitive
+      // field. The field type is incorrect for this case.
+      JValue value = GetValueFromShadowFrame(
+          shadow_frame,
+          callsite_type->GetPTypes()->Get(kPTypeIndex)->GetPrimitiveType(),
+          value_reg);
+      if (do_conversions && !ConvertArgumentValue(callsite_type,
+                                                  handle_type,
+                                                  kPTypeIndex,
+                                                  &value)) {
         DCHECK(self->IsExceptionPending());
         return false;
       }
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 811f1ea..f83645e 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -513,8 +513,11 @@
   return GetClass<kVerifyFlags>()->IsPhantomReferenceClass();
 }
 
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
 inline size_t Object::SizeOf() {
+  // Read barrier is never required for SizeOf since objects sizes are constant. Reading from-space
+  // values is OK because of that.
+  static constexpr ReadBarrierOption kReadBarrierOption = kWithoutReadBarrier;
   size_t result;
   constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
   if (IsArrayInstance<kVerifyFlags, kReadBarrierOption>()) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index f7ab26d..417a22d 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -128,8 +128,7 @@
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   ALWAYS_INLINE bool InstanceOf(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
-           ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   size_t SizeOf() REQUIRES_SHARED(Locks::mutator_lock_);
 
   Object* Clone(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_)
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 11f8505..34bbf32 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -252,7 +252,7 @@
 }
 
 static void VMRuntime_concurrentGC(JNIEnv* env, jobject) {
-  Runtime::Current()->GetHeap()->ConcurrentGC(ThreadForEnv(env), true);
+  Runtime::Current()->GetHeap()->ConcurrentGC(ThreadForEnv(env), gc::kGcCauseBackground, true);
 }
 
 static void VMRuntime_requestHeapTrim(JNIEnv* env, jobject) {
@@ -260,7 +260,9 @@
 }
 
 static void VMRuntime_requestConcurrentGC(JNIEnv* env, jobject) {
-  Runtime::Current()->GetHeap()->RequestConcurrentGC(ThreadForEnv(env), true);
+  Runtime::Current()->GetHeap()->RequestConcurrentGC(ThreadForEnv(env),
+                                                     gc::kGcCauseBackground,
+                                                     true);
 }
 
 static void VMRuntime_startHeapTaskProcessor(JNIEnv* env, jobject) {
diff --git a/runtime/oat.h b/runtime/oat.h
index faa0129..58ea91b 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '1', '1', '7', '\0' };  // Read barriers on MIPS.
+  static constexpr uint8_t kOatVersion[] = { '1', '1', '8', '\0' };  // ARM64 Read barriers thunks.
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index d04dbbe..a950980 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -482,6 +482,8 @@
   }
 
   // Now drain the queue.
+  bool has_duplicates = false;
+  error_msg->clear();
   while (!queue.empty()) {
     // Modifying the top element is only safe if we pop right after.
     DexFileAndClassPair compare_pop(queue.top());
@@ -493,12 +495,15 @@
       if (strcmp(compare_pop.GetCachedDescriptor(), top.GetCachedDescriptor()) == 0) {
         // Same descriptor. Check whether it's crossing old-oat-files to new-oat-files.
         if (compare_pop.FromLoadedOat() != top.FromLoadedOat()) {
-          *error_msg =
-              StringPrintf("Found duplicated class when checking oat files: '%s' in %s and %s",
+          error_msg->append(
+              StringPrintf("Found duplicated class when checking oat files: '%s' in %s and %s\n",
                            compare_pop.GetCachedDescriptor(),
                            compare_pop.GetDexFile()->GetLocation().c_str(),
-                           top.GetDexFile()->GetLocation().c_str());
-          return true;
+                           top.GetDexFile()->GetLocation().c_str()));
+          if (!VLOG_IS_ON(oat)) {
+            return true;
+          }
+          has_duplicates = true;
         }
         queue.pop();
         AddNext(top, queue);
@@ -510,7 +515,7 @@
     AddNext(compare_pop, queue);
   }
 
-  return false;
+  return has_duplicates;
 }
 
 // Check for class-def collisions in dex files.
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 1ddbb86..233b45c 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -131,6 +131,9 @@
   unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
   ArtJvmTiEnv* last_env = nullptr;
   for (ArtJvmTiEnv* env : envs) {
+    if (env == nullptr) {
+      continue;
+    }
     if (ShouldDispatch<kEvent>(env, thread)) {
       jint new_len = 0;
       unsigned char* new_data = nullptr;
@@ -171,7 +174,9 @@
 template <ArtJvmtiEvent kEvent, typename ...Args>
 inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const {
   for (ArtJvmTiEnv* env : envs) {
-    DispatchEvent<kEvent, Args...>(env, thread, args...);
+    if (env != nullptr) {
+      DispatchEvent<kEvent, Args...>(env, thread, args...);
+    }
   }
 }
 
@@ -253,6 +258,9 @@
 inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
   bool union_value = false;
   for (const ArtJvmTiEnv* stored_env : envs) {
+    if (stored_env == nullptr) {
+      continue;
+    }
     union_value |= stored_env->event_masks.global_event_mask.Test(event);
     union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
     if (union_value) {
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 34492a9..521494a 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -141,13 +141,21 @@
 }
 
 void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) {
-  envs.push_back(env);
+  // Since we never shrink this array we might as well try to fill gaps.
+  auto it = std::find(envs.begin(), envs.end(), nullptr);
+  if (it != envs.end()) {
+    *it = env;
+  } else {
+    envs.push_back(env);
+  }
 }
 
 void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) {
+  // Since we might be currently iterating over the envs list we cannot actually erase elements.
+  // Instead we will simply replace them with 'nullptr' and skip them manually.
   auto it = std::find(envs.begin(), envs.end(), env);
   if (it != envs.end()) {
-    envs.erase(it);
+    *it = nullptr;
     for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal);
          i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal);
          ++i) {
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index ae8bf0f..b9e3cf0 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -202,6 +202,8 @@
   void HandleEventType(ArtJvmtiEvent event, bool enable);
 
   // List of all JvmTiEnv objects that have been created, in their creation order.
+  // NB Some elements might be null representing envs that have been deleted. They should be skipped
+  // anytime this list is used.
   std::vector<ArtJvmTiEnv*> envs;
 
   // A union of all enabled events, anywhere.
diff --git a/runtime/openjdkjvmti/ti_class_definition.cc b/runtime/openjdkjvmti/ti_class_definition.cc
index 153692b..0671105 100644
--- a/runtime/openjdkjvmti/ti_class_definition.cc
+++ b/runtime/openjdkjvmti/ti_class_definition.cc
@@ -102,7 +102,9 @@
       } else if (orig_dex->IsDexCache()) {
         dex_file = orig_dex->AsDexCache()->GetDexFile();
       } else {
-        DCHECK_EQ(orig_dex->GetClass()->GetPrimitiveType(), art::Primitive::kPrimLong);
+        DCHECK(orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"))
+            << "Expected java/lang/Long but found object of type "
+            << orig_dex->GetClass()->PrettyClass();
         art::ObjPtr<art::mirror::Class> prim_long_class(
             art::Runtime::Current()->GetClassLinker()->GetClassRoot(
                 art::ClassLinker::kPrimitiveLong));
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 49d9aca..7fc5104 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -888,8 +888,8 @@
       bool add_to_worklist = ReportRoot(root_obj, info);
       // We use visited_ to mark roots already so we do not need another set.
       if (visited_->find(root_obj) == visited_->end()) {
-        visited_->insert(root_obj);
         if (add_to_worklist) {
+          visited_->insert(root_obj);
           worklist_->push_back(root_obj);
         }
       }
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index bc73029..01bf21d 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -61,8 +61,13 @@
 
   art::ScopedObjectAccess soa(art::Thread::Current());
   if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
-    // This isn't specified as an error case, so return 0.
-    *size_ptr = 0;
+    // Use the shorty.
+    art::ArtMethod* base_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
+    size_t arg_count = art::ArtMethod::NumArgRegisters(base_method->GetShorty());
+    if (!base_method->IsStatic()) {
+      arg_count++;
+    }
+    *size_ptr = static_cast<jint>(arg_count);
     return ERR(NONE);
   }
 
@@ -203,9 +208,9 @@
 
   art::ScopedObjectAccess soa(art::Thread::Current());
   if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
-    // This isn't specified as an error case, so return 0/0.
-    *start_location_ptr = 0;
-    *end_location_ptr = 0;
+    // This isn't specified as an error case, so return -1/-1 as the RI does.
+    *start_location_ptr = -1;
+    *end_location_ptr = -1;
     return ERR(NONE);
   }
 
diff --git a/runtime/openjdkjvmti/ti_properties.cc b/runtime/openjdkjvmti/ti_properties.cc
index 4f4f013..8ee5366 100644
--- a/runtime/openjdkjvmti/ti_properties.cc
+++ b/runtime/openjdkjvmti/ti_properties.cc
@@ -34,8 +34,15 @@
 #include <string.h>
 #include <vector>
 
+#include "jni.h"
+#include "ScopedLocalRef.h"
+#include "ScopedUtfChars.h"
+
 #include "art_jvmti.h"
 #include "runtime.h"
+#include "thread-inl.h"
+#include "ti_phase.h"
+#include "well_known_classes.h"
 
 namespace openjdkjvmti {
 
@@ -145,6 +152,56 @@
   return result;
 }
 
+// See dalvik_system_VMRuntime.cpp.
+static const char* DefaultToDot(const std::string& class_path) {
+  return class_path.empty() ? "." : class_path.c_str();
+}
+
+// Handle kPropertyLibraryPath.
+static jvmtiError GetLibraryPath(jvmtiEnv* env, char** value_ptr) {
+  const std::vector<std::string>& runtime_props = art::Runtime::Current()->GetProperties();
+  for (const std::string& prop_assignment : runtime_props) {
+    size_t assign_pos = prop_assignment.find('=');
+    if (assign_pos != std::string::npos && assign_pos > 0) {
+      if (prop_assignment.substr(0, assign_pos) == kPropertyLibraryPath) {
+        return Copy(env, prop_assignment.substr(assign_pos + 1).c_str(), value_ptr);
+      }
+    }
+  }
+  if (!PhaseUtil::IsLivePhase()) {
+    return ERR(NOT_AVAILABLE);
+  }
+  // We expect this call to be rare. So don't optimize.
+  DCHECK(art::Thread::Current() != nullptr);
+  JNIEnv* jni_env = art::Thread::Current()->GetJniEnv();
+  jmethodID get_prop = jni_env->GetStaticMethodID(art::WellKnownClasses::java_lang_System,
+                                                  "getProperty",
+                                                  "(Ljava/lang/String;)Ljava/lang/String;");
+  CHECK(get_prop != nullptr);
+
+  ScopedLocalRef<jobject> input_str(jni_env, jni_env->NewStringUTF(kPropertyLibraryPath));
+  if (input_str.get() == nullptr) {
+    jni_env->ExceptionClear();
+    return ERR(OUT_OF_MEMORY);
+  }
+
+  ScopedLocalRef<jobject> prop_res(
+      jni_env, jni_env->CallStaticObjectMethod(art::WellKnownClasses::java_lang_System,
+                                               get_prop,
+                                               input_str.get()));
+  if (jni_env->ExceptionCheck() == JNI_TRUE) {
+    jni_env->ExceptionClear();
+    return ERR(INTERNAL);
+  }
+  if (prop_res.get() == nullptr) {
+    *value_ptr = nullptr;
+    return ERR(NONE);
+  }
+
+  ScopedUtfChars chars(jni_env, reinterpret_cast<jstring>(prop_res.get()));
+  return Copy(env, chars.c_str(), value_ptr);
+}
+
 jvmtiError PropertiesUtil::GetSystemProperty(jvmtiEnv* env,
                                              const char* property,
                                              char** value_ptr) {
@@ -153,22 +210,11 @@
   }
 
   if (strcmp(property, kPropertyLibraryPath) == 0) {
-    // TODO: In the live phase, we should probably compare to System.getProperty. java.library.path
-    //       may not be set initially, and is then freely modifiable.
-    const std::vector<std::string>& runtime_props = art::Runtime::Current()->GetProperties();
-    for (const std::string& prop_assignment : runtime_props) {
-      size_t assign_pos = prop_assignment.find('=');
-      if (assign_pos != std::string::npos && assign_pos > 0) {
-        if (prop_assignment.substr(0, assign_pos) == kPropertyLibraryPath) {
-          return Copy(env, prop_assignment.substr(assign_pos + 1).c_str(), value_ptr);
-        }
-      }
-    }
-    return ERR(NOT_AVAILABLE);
+    return GetLibraryPath(env, value_ptr);
   }
 
   if (strcmp(property, kPropertyClassPath) == 0) {
-    return Copy(env, art::Runtime::Current()->GetClassPathString().c_str(), value_ptr);
+    return Copy(env, DefaultToDot(art::Runtime::Current()->GetClassPathString()), value_ptr);
   }
 
   for (size_t i = 0; i != kPropertiesSize; ++i) {
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 0655079..358bb0f 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -1418,14 +1418,18 @@
   art::mirror::Class* klass = GetMirrorClass();
   art::mirror::ClassExt* ext = klass->GetExtData();
   art::mirror::PointerArray* methods = ext->GetObsoleteMethods();
-  int32_t old_length =
-      cur_data->GetOldDexCaches() == nullptr ? 0 : cur_data->GetOldDexCaches()->GetLength();
+  art::mirror::PointerArray* old_methods = cur_data->GetOldObsoleteMethods();
+  int32_t old_length = old_methods == nullptr ? 0 : old_methods->GetLength();
   int32_t expected_length =
       old_length + klass->NumDirectMethods() + klass->NumDeclaredVirtualMethods();
   // Check to make sure we are only undoing this one.
   if (expected_length == methods->GetLength()) {
-    for (int32_t i = old_length; i < expected_length; i++) {
-      if (methods->GetElementPtrSize<art::ArtMethod*>(i, art::kRuntimePointerSize) != nullptr) {
+    for (int32_t i = 0; i < expected_length; i++) {
+      art::ArtMethod* expected = nullptr;
+      if (i < old_length) {
+        expected = old_methods->GetElementPtrSize<art::ArtMethod*>(i, art::kRuntimePointerSize);
+      }
+      if (methods->GetElementPtrSize<art::ArtMethod*>(i, art::kRuntimePointerSize) != expected) {
         // We actually have some new obsolete methods. Just abort since we cannot safely shrink the
         // obsolete methods array.
         return;
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 2b38b2e..c102fb0 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -28,12 +28,15 @@
 
 namespace art {
 
+// Disabled for performance reasons.
+static constexpr bool kCheckDebugDisallowReadBarrierCount = false;
+
 template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kAlwaysUpdateField>
 inline MirrorType* ReadBarrier::Barrier(
     mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) {
   constexpr bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
   if (kUseReadBarrier && with_read_barrier) {
-    if (kIsDebugBuild) {
+    if (kCheckDebugDisallowReadBarrierCount) {
       Thread* const self = Thread::Current();
       if (self != nullptr) {
         CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 48efbe5..a48a58d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -269,13 +269,6 @@
     UnloadNativeBridge();
   }
 
-  if (dump_gc_performance_on_shutdown_) {
-    // This can't be called from the Heap destructor below because it
-    // could call RosAlloc::InspectAll() which needs the thread_list
-    // to be still alive.
-    heap_->DumpGcPerformanceInfo(LOG_STREAM(INFO));
-  }
-
   Thread* self = Thread::Current();
   const bool attach_shutdown_thread = self == nullptr;
   if (attach_shutdown_thread) {
@@ -285,6 +278,13 @@
     LOG(WARNING) << "Current thread not detached in Runtime shutdown";
   }
 
+  if (dump_gc_performance_on_shutdown_) {
+    // This can't be called from the Heap destructor below because it
+    // could call RosAlloc::InspectAll() which needs the thread_list
+    // to be still alive.
+    heap_->DumpGcPerformanceInfo(LOG_STREAM(INFO));
+  }
+
   if (jit_ != nullptr) {
     // Stop the profile saver thread before marking the runtime as shutting down.
     // The saver will try to dump the profiles before being sopped and that
@@ -1722,6 +1722,7 @@
   mirror::MethodHandlesLookup::VisitRoots(visitor);
   mirror::EmulatedStackFrame::VisitRoots(visitor);
   mirror::ClassExt::VisitRoots(visitor);
+  mirror::CallSite::VisitRoots(visitor);
   // Visit all the primitive array types classes.
   mirror::PrimitiveArray<uint8_t>::VisitRoots(visitor);   // BooleanArray
   mirror::PrimitiveArray<int8_t>::VisitRoots(visitor);    // ByteArray
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 92feabb..20db628 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -661,6 +661,10 @@
 
   void InitThreadGroups(Thread* self);
 
+  void SetDumpGCPerformanceOnShutdown(bool value) {
+    dump_gc_performance_on_shutdown_ = value;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 333128b..5c6eead 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -913,7 +913,9 @@
               << " next=" << *cur_quick_frame_;
         }
 
-        cur_depth_++;
+        if (kCount == CountTransitions::kYes || !method->IsRuntimeMethod()) {
+          cur_depth_++;
+        }
         method = *cur_quick_frame_;
       }
     } else if (cur_shadow_frame_ != nullptr) {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 008c388..a8a03c7 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1451,7 +1451,8 @@
       MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
 
       DCHECK_NE(GetState(), ThreadState::kRunnable);
-      CHECK(ModifySuspendCount(self, -1, nullptr, false));
+      bool updated = ModifySuspendCount(self, -1, nullptr, false);
+      DCHECK(updated);
     }
 
     return;  // We're done, break out of the loop.
@@ -1928,6 +1929,23 @@
   Thread::Current()->AssertNoPendingException();
 
   Runtime::Current()->GetClassLinker()->RunRootClinits();
+
+  // The thread counts as started from now on. We need to add it to the ThreadGroup. For regular
+  // threads, this is done in Thread.start() on the Java side.
+  {
+    // This is only ever done once. There's no benefit in caching the method.
+    jmethodID thread_group_add = soa.Env()->GetMethodID(WellKnownClasses::java_lang_ThreadGroup,
+                                                        "add",
+                                                        "(Ljava/lang/Thread;)V");
+    CHECK(thread_group_add != nullptr);
+    ScopedLocalRef<jobject> thread_jobject(
+        soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
+    soa.Env()->CallNonvirtualVoidMethod(runtime->GetMainThreadGroup(),
+                                        WellKnownClasses::java_lang_ThreadGroup,
+                                        thread_group_add,
+                                        thread_jobject.get());
+    Thread::Current()->AssertNoPendingException();
+  }
 }
 
 void Thread::Shutdown() {
diff --git a/runtime/thread.h b/runtime/thread.h
index de0b892..4d5d644 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -244,6 +244,7 @@
                           int delta,
                           AtomicInteger* suspend_barrier,
                           bool for_debugger)
+      WARN_UNUSED
       REQUIRES(Locks::thread_suspend_count_lock_);
 
   bool RequestCheckpoint(Closure* function)
@@ -1276,6 +1277,7 @@
                                   int delta,
                                   AtomicInteger* suspend_barrier,
                                   bool for_debugger)
+      WARN_UNUSED
       REQUIRES(Locks::thread_suspend_count_lock_);
 
   void RunCheckpointFunction();
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 8d72fe8..2e0d866 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -323,7 +323,8 @@
               // Spurious fail, try again.
               continue;
             }
-            thread->ModifySuspendCount(self, +1, nullptr, false);
+            bool updated = thread->ModifySuspendCount(self, +1, nullptr, false);
+            DCHECK(updated);
             suspended_count_modified_threads.push_back(thread);
             break;
           }
@@ -365,7 +366,8 @@
     checkpoint_function->Run(thread);
     {
       MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
-      thread->ModifySuspendCount(self, -1, nullptr, false);
+      bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+      DCHECK(updated);
     }
   }
 
@@ -565,7 +567,8 @@
       if ((state == kWaitingForGcThreadFlip || thread->IsTransitioningToRunnable()) &&
           thread->GetSuspendCount() == 1) {
         // The thread will resume right after the broadcast.
-        thread->ModifySuspendCount(self, -1, nullptr, false);
+        bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+        DCHECK(updated);
         ++runnable_thread_count;
       } else {
         other_threads.push_back(thread);
@@ -598,7 +601,8 @@
     TimingLogger::ScopedTiming split4("ResumeOtherThreads", collector->GetTimings());
     MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
     for (const auto& thread : other_threads) {
-      thread->ModifySuspendCount(self, -1, nullptr, false);
+      bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+      DCHECK(updated);
     }
     Thread::resume_cond_->Broadcast(self);
   }
@@ -708,7 +712,8 @@
         continue;
       }
       VLOG(threads) << "requesting thread suspend: " << *thread;
-      thread->ModifySuspendCount(self, +1, &pending_threads, debug_suspend);
+      bool updated = thread->ModifySuspendCount(self, +1, &pending_threads, debug_suspend);
+      DCHECK(updated);
 
       // Must install the pending_threads counter first, then check thread->IsSuspend() and clear
       // the counter. Otherwise there's a race with Thread::TransitionFromRunnableToSuspended()
@@ -786,7 +791,8 @@
       if (thread == self) {
         continue;
       }
-      thread->ModifySuspendCount(self, -1, nullptr, false);
+      bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+      DCHECK(updated);
     }
 
     // Broadcast a notification to all suspended threads, some or all of
@@ -828,7 +834,8 @@
           << ") thread not within thread list";
       return;
     }
-    thread->ModifySuspendCount(self, -1, nullptr, for_debugger);
+    bool updated = thread->ModifySuspendCount(self, -1, nullptr, for_debugger);
+    DCHECK(updated);
   }
 
   {
@@ -884,7 +891,11 @@
           // If we incremented the suspend count but the thread reset its peer, we need to
           // re-decrement it since it is shutting down and may deadlock the runtime in
           // ThreadList::WaitForOtherNonDaemonThreadsToExit.
-          suspended_thread->ModifySuspendCount(soa.Self(), -1, nullptr, debug_suspension);
+          bool updated = suspended_thread->ModifySuspendCount(soa.Self(),
+                                                              -1,
+                                                              nullptr,
+                                                              debug_suspension);
+          DCHECK(updated);
         }
         ThreadSuspendByPeerWarning(self,
                                    ::android::base::WARNING,
@@ -910,7 +921,8 @@
           }
           CHECK(suspended_thread == nullptr);
           suspended_thread = thread;
-          suspended_thread->ModifySuspendCount(self, +1, nullptr, debug_suspension);
+          bool updated = suspended_thread->ModifySuspendCount(self, +1, nullptr, debug_suspension);
+          DCHECK(updated);
           request_suspension = false;
         } else {
           // If the caller isn't requesting suspension, a suspension should have already occurred.
@@ -942,7 +954,11 @@
                                      peer);
           if (suspended_thread != nullptr) {
             CHECK_EQ(suspended_thread, thread);
-            suspended_thread->ModifySuspendCount(soa.Self(), -1, nullptr, debug_suspension);
+            bool updated = suspended_thread->ModifySuspendCount(soa.Self(),
+                                                                -1,
+                                                                nullptr,
+                                                                debug_suspension);
+            DCHECK(updated);
           }
           *timed_out = true;
           return nullptr;
@@ -1015,7 +1031,8 @@
             // which will allow this thread to be suspended.
             continue;
           }
-          thread->ModifySuspendCount(self, +1, nullptr, debug_suspension);
+          bool updated = thread->ModifySuspendCount(self, +1, nullptr, debug_suspension);
+          DCHECK(updated);
           suspended_thread = thread;
         } else {
           CHECK_EQ(suspended_thread, thread);
@@ -1046,7 +1063,8 @@
                                          "Thread suspension timed out",
                                          thread_id);
           if (suspended_thread != nullptr) {
-            thread->ModifySuspendCount(soa.Self(), -1, nullptr, debug_suspension);
+            bool updated = thread->ModifySuspendCount(soa.Self(), -1, nullptr, debug_suspension);
+            DCHECK(updated);
           }
           *timed_out = true;
           return nullptr;
@@ -1123,7 +1141,8 @@
     // to ensure that we're the only one fiddling with the suspend count
     // though.
     MutexLock mu(self, *Locks::thread_suspend_count_lock_);
-    self->ModifySuspendCount(self, +1, nullptr, true);
+    bool updated = self->ModifySuspendCount(self, +1, nullptr, true);
+    DCHECK(updated);
     CHECK_GT(self->GetSuspendCount(), 0);
 
     VLOG(threads) << *self << " self-suspending (debugger)";
@@ -1207,7 +1226,8 @@
           continue;
         }
         VLOG(threads) << "requesting thread resume: " << *thread;
-        thread->ModifySuspendCount(self, -1, nullptr, true);
+        bool updated = thread->ModifySuspendCount(self, -1, nullptr, true);
+        DCHECK(updated);
       }
     }
   }
@@ -1236,7 +1256,11 @@
       if (thread == self || thread->GetDebugSuspendCount() == 0) {
         continue;
       }
-      thread->ModifySuspendCount(self, -thread->GetDebugSuspendCount(), nullptr, true);
+      bool suspended = thread->ModifySuspendCount(self,
+                                                  -thread->GetDebugSuspendCount(),
+                                                  nullptr,
+                                                  true);
+      DCHECK(suspended);
     }
   }
 
@@ -1293,7 +1317,8 @@
       // daemons.
       CHECK(thread->IsDaemon()) << *thread;
       if (thread != self) {
-        thread->ModifySuspendCount(self, +1, nullptr, false);
+        bool updated = thread->ModifySuspendCount(self, +1, nullptr, false);
+        DCHECK(updated);
         ++daemons_left;
       }
       // We are shutting down the runtime, set the JNI functions of all the JNIEnvs to be
@@ -1352,10 +1377,12 @@
   // Modify suspend count in increments of 1 to maintain invariants in ModifySuspendCount. While
   // this isn't particularly efficient the suspend counts are most commonly 0 or 1.
   for (int delta = debug_suspend_all_count_; delta > 0; delta--) {
-    self->ModifySuspendCount(self, +1, nullptr, true);
+    bool updated = self->ModifySuspendCount(self, +1, nullptr, true);
+    DCHECK(updated);
   }
   for (int delta = suspend_all_count_ - debug_suspend_all_count_; delta > 0; delta--) {
-    self->ModifySuspendCount(self, +1, nullptr, false);
+    bool updated = self->ModifySuspendCount(self, +1, nullptr, false);
+    DCHECK(updated);
   }
   CHECK(!Contains(self));
   list_.push_back(self);
@@ -1450,11 +1477,13 @@
     MutexLock mu(self, *Locks::thread_list_lock_);
     MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
     for (Thread* thread : list_) {
-      thread->ModifySuspendCount(self, +1, nullptr, false);
+      bool suspended = thread->ModifySuspendCount(self, +1, nullptr, false);
+      DCHECK(suspended);
       if (thread == self || thread->IsSuspended()) {
         threads_to_visit.push_back(thread);
       } else {
-        thread->ModifySuspendCount(self, -1, nullptr, false);
+        bool resumed = thread->ModifySuspendCount(self, -1, nullptr, false);
+        DCHECK(resumed);
       }
     }
   }
@@ -1469,7 +1498,8 @@
   {
     MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
     for (Thread* thread : threads_to_visit) {
-      thread->ModifySuspendCount(self, -1, nullptr, false);
+      bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+      DCHECK(updated);
     }
   }
 }
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index cc1b78d..f4799d2 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -23,12 +23,14 @@
 
 #include <dlfcn.h>
 #include <errno.h>
+#include <pthread.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <initializer_list>
+#include <mutex>
 #include <utility>
 
 #include "sigchain.h"
@@ -61,7 +63,6 @@
 //               doesn't have SA_RESTART, and raise the signal to avoid restarting syscalls that are
 //               expected to be interrupted?
 
-
 static void log(const char* format, ...) {
   char buf[256];
   va_list ap;
@@ -91,7 +92,41 @@
 
 static decltype(&sigaction) linked_sigaction;
 static decltype(&sigprocmask) linked_sigprocmask;
-__thread bool handling_signal;
+
+static pthread_key_t GetHandlingSignalKey() {
+  static pthread_key_t key;
+  static std::once_flag once;
+  std::call_once(once, []() {
+    int rc = pthread_key_create(&key, nullptr);
+    if (rc != 0) {
+      fatal("failed to create sigchain pthread key: %s", strerror(rc));
+    }
+  });
+  return key;
+}
+
+static bool GetHandlingSignal() {
+  void* result = pthread_getspecific(GetHandlingSignalKey());
+  return reinterpret_cast<uintptr_t>(result);
+}
+
+static void SetHandlingSignal(bool value) {
+  pthread_setspecific(GetHandlingSignalKey(),
+                      reinterpret_cast<void*>(static_cast<uintptr_t>(value)));
+}
+
+class ScopedHandlingSignal {
+ public:
+  ScopedHandlingSignal() : original_value_(GetHandlingSignal()) {
+  }
+
+  ~ScopedHandlingSignal() {
+    SetHandlingSignal(original_value_);
+  }
+
+ private:
+  bool original_value_;
+};
 
 class SignalChain {
  public:
@@ -164,20 +199,6 @@
 
 static SignalChain chains[_NSIG];
 
-class ScopedFlagRestorer {
- public:
-  explicit ScopedFlagRestorer(bool* flag) : flag_(flag), original_value_(*flag) {
-  }
-
-  ~ScopedFlagRestorer() {
-    *flag_ = original_value_;
-  }
-
- private:
-  bool* flag_;
-  bool original_value_;
-};
-
 class ScopedSignalUnblocker {
  public:
   explicit ScopedSignalUnblocker(const std::initializer_list<int>& signals) {
@@ -202,13 +223,13 @@
 };
 
 void SignalChain::Handler(int signo, siginfo_t* siginfo, void* ucontext_raw) {
-  ScopedFlagRestorer flag(&handling_signal);
+  ScopedHandlingSignal handling_signal;
 
   // Try the special handlers first.
   // If one of them crashes, we'll reenter this handler and pass that crash onto the user handler.
-  if (!handling_signal) {
+  if (!GetHandlingSignal()) {
     ScopedSignalUnblocker unblocked { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }; // NOLINT
-    handling_signal = true;
+    SetHandlingSignal(true);
 
     for (const auto& handler : chains[signo].special_handlers_) {
       if (handler != nullptr && handler(signo, siginfo, ucontext_raw)) {
@@ -306,7 +327,7 @@
 
 extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
   // When inside a signal handler, forward directly to the actual sigprocmask.
-  if (handling_signal) {
+  if (GetHandlingSignal()) {
     return linked_sigprocmask(how, bionic_new_set, bionic_old_set);
   }
 
diff --git a/test/051-thread/expected.txt b/test/051-thread/expected.txt
index 3fc3492..c8af963 100644
--- a/test/051-thread/expected.txt
+++ b/test/051-thread/expected.txt
@@ -12,4 +12,6 @@
 testSetName finished
 testThreadPriorities starting
 testThreadPriorities finished
+Found current Thread in ThreadGroup
+Found expected stack in getAllStackTraces()
 thread test done
diff --git a/test/051-thread/src/Main.java b/test/051-thread/src/Main.java
index 82fc0d4..08cb5de 100644
--- a/test/051-thread/src/Main.java
+++ b/test/051-thread/src/Main.java
@@ -15,6 +15,9 @@
  */
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * Test some basic thread stuff.
@@ -28,6 +31,8 @@
         testSleepZero();
         testSetName();
         testThreadPriorities();
+        testMainThreadGroup();
+        testMainThreadAllStackTraces();
         System.out.println("thread test done");
     }
 
@@ -159,6 +164,49 @@
         System.out.print("testThreadPriorities finished\n");
     }
 
+    private static void testMainThreadGroup() {
+      Thread threads[] = new Thread[10];
+      Thread current = Thread.currentThread();
+      current.getThreadGroup().enumerate(threads);
+
+      for (Thread t : threads) {
+        if (t == current) {
+          System.out.println("Found current Thread in ThreadGroup");
+          return;
+        }
+      }
+      throw new RuntimeException("Did not find main thread: " + Arrays.toString(threads));
+    }
+
+    private static void testMainThreadAllStackTraces() {
+      StackTraceElement[] trace = Thread.getAllStackTraces().get(Thread.currentThread());
+      if (trace == null) {
+        throw new RuntimeException("Did not find main thread: " + Thread.getAllStackTraces());
+      }
+      List<StackTraceElement> list = Arrays.asList(trace);
+      Iterator<StackTraceElement> it = list.iterator();
+      while (it.hasNext()) {
+        StackTraceElement ste = it.next();
+        if (ste.getClassName().equals("Main")) {
+          if (!ste.getMethodName().equals("testMainThreadAllStackTraces")) {
+            throw new RuntimeException(list.toString());
+          }
+
+          StackTraceElement ste2 = it.next();
+          if (!ste2.getClassName().equals("Main")) {
+            throw new RuntimeException(list.toString());
+          }
+          if (!ste2.getMethodName().equals("main")) {
+            throw new RuntimeException(list.toString());
+          }
+
+          System.out.println("Found expected stack in getAllStackTraces()");
+          return;
+        }
+      }
+      throw new RuntimeException(list.toString());
+    }
+
     private static native int getNativePriority();
     private static native boolean supportsThreadPriorities();
 
diff --git a/test/160-read-barrier-stress/expected.txt b/test/160-read-barrier-stress/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/160-read-barrier-stress/expected.txt
diff --git a/test/160-read-barrier-stress/info.txt b/test/160-read-barrier-stress/info.txt
new file mode 100644
index 0000000..505fe33
--- /dev/null
+++ b/test/160-read-barrier-stress/info.txt
@@ -0,0 +1 @@
+Test stressing read barriers for CC GC.
diff --git a/test/160-read-barrier-stress/run b/test/160-read-barrier-stress/run
new file mode 100644
index 0000000..ab82229
--- /dev/null
+++ b/test/160-read-barrier-stress/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+# Limit the Java heap to 16MiB to force more GCs.
+exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/160-read-barrier-stress/src/Main.java b/test/160-read-barrier-stress/src/Main.java
new file mode 100644
index 0000000..7e130ce
--- /dev/null
+++ b/test/160-read-barrier-stress/src/Main.java
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Main {
+    public static void main(String[] args) {
+        // Initialize local variables for comparison.
+        Object f0000 = manyFields.testField0000;
+        Object f1024 = manyFields.testField1024;
+        Object f4444 = manyFields.testField4444;
+        Object f4999 = manyFields.testField4999;
+        // Initialize largeArray for comparison.
+        largeArray[0] = f0000;
+        largeArray[1024] = f1024;
+        largeArray[4444] = f4444;
+        largeArray[4999] = f4999;
+        // Read indexes, they cannot be considered constant because the variables are volatile.
+        int i0 = index0;
+        int i1024 = index1024;
+        int i4444 = index4444;
+        int i4999 = index4999;
+        // Initialize strings, hide this under a condition based on a volatile field.
+        String testString0 = null;
+        String testString1 = null;
+        String testString2 = null;
+        String testString3 = null;
+        if (index0 != 12345678) {
+            // By having this in the const-string instructions in an if-block, we avoid
+            // GVN eliminating identical const-string instructions in the loop below.
+            testString0 = "testString0";
+            testString1 = "testString1";
+            testString2 = "testString2";
+            testString3 = "testString3";
+        }
+
+        // Continually check reads from `manyFields` and `largeArray` while allocating
+        // over 64MiB memory (with heap size limited to 16MiB), ensuring we run GC and
+        // stress the read barrier implementation if concurrent collector is enabled.
+        for (int i = 0; i != 64 * 1024; ++i) {
+            allocateAtLeast1KiB();
+            ManyFields mf = manyFields;  // Load the volatile `manyFields` once on each iteration.
+            Object[] la = largeArray;    // Load the volatile `largeArray` once on each iteration.
+            // Test reference field access.
+            assertSameObject(f0000, mf.testField0000);
+            assertSameObject(f1024, mf.testField1024);
+            assertSameObject(f4444, mf.testField4444);
+            assertSameObject(f4999, mf.testField4999);
+            // Test array access with constant index.
+            assertSameObject(f0000, la[0]);
+            assertSameObject(f1024, la[1024]);
+            assertSameObject(f4444, la[4444]);
+            assertSameObject(f4999, la[4999]);
+            // Test array access with non-constant index.
+            assertSameObject(f0000, la[i0]);
+            assertSameObject(f1024, la[i1024]);
+            assertSameObject(f4444, la[i4444]);
+            assertSameObject(f4999, la[i4999]);
+            // Test GC roots.
+            if (index0 != 12345678) {
+              assertSameObject(testString0, "testString0");
+              assertSameObject(testString1, "testString1");
+              assertSameObject(testString2, "testString2");
+              assertSameObject(testString3, "testString3");
+            }
+            // TODO: Stress GC roots (const-string/const-class, kBssEntry/kReferrersClass).
+        }
+    }
+
+    public static void assertSameObject(Object lhs, Object rhs) {
+        if (lhs != rhs) {
+            throw new Error("Different objects: " + lhs + " and " + rhs);
+        }
+    }
+
+    public static void allocateAtLeast1KiB() {
+        // Give GC more work by allocating Object arrays.
+        memory[allocationIndex] = new Object[1024 / 4];
+        ++allocationIndex;
+        if (allocationIndex == memory.length) {
+            allocationIndex = 0;
+        }
+    }
+
+    // Make these volatile to avoid load elimination.
+    public static volatile ManyFields manyFields = new ManyFields();
+    public static volatile Object[] largeArray = new Object[5000];
+    public static volatile int index0 = 0;
+    public static volatile int index1024 = 1024;
+    public static volatile int index4444 = 4444;
+    public static volatile int index4999 = 4999;
+
+    // We shall retain some allocated memory and release old allocations
+    // so that the GC has something to do.
+    public static Object[] memory = new Object[1024];
+    public static int allocationIndex = 0;
+}
+
+class ManyFields extends ManyFieldsBase3 {
+    public Object testField4000 = new Integer(4000);
+    public Object testField4001 = new Integer(4001);
+    public Object testField4002 = new Integer(4002);
+    public Object testField4003 = new Integer(4003);
+    public Object testField4004 = new Integer(4004);
+    public Object testField4005 = new Integer(4005);
+    public Object testField4006 = new Integer(4006);
+    public Object testField4007 = new Integer(4007);
+    public Object testField4008 = new Integer(4008);
+    public Object testField4009 = new Integer(4009);
+    public Object testField4010 = new Integer(4010);
+    public Object testField4011 = new Integer(4011);
+    public Object testField4012 = new Integer(4012);
+    public Object testField4013 = new Integer(4013);
+    public Object testField4014 = new Integer(4014);
+    public Object testField4015 = new Integer(4015);
+    public Object testField4016 = new Integer(4016);
+    public Object testField4017 = new Integer(4017);
+    public Object testField4018 = new Integer(4018);
+    public Object testField4019 = new Integer(4019);
+    public Object testField4020 = new Integer(4020);
+    public Object testField4021 = new Integer(4021);
+    public Object testField4022 = new Integer(4022);
+    public Object testField4023 = new Integer(4023);
+    public Object testField4024 = new Integer(4024);
+    public Object testField4025 = new Integer(4025);
+    public Object testField4026 = new Integer(4026);
+    public Object testField4027 = new Integer(4027);
+    public Object testField4028 = new Integer(4028);
+    public Object testField4029 = new Integer(4029);
+    public Object testField4030 = new Integer(4030);
+    public Object testField4031 = new Integer(4031);
+    public Object testField4032 = new Integer(4032);
+    public Object testField4033 = new Integer(4033);
+    public Object testField4034 = new Integer(4034);
+    public Object testField4035 = new Integer(4035);
+    public Object testField4036 = new Integer(4036);
+    public Object testField4037 = new Integer(4037);
+    public Object testField4038 = new Integer(4038);
+    public Object testField4039 = new Integer(4039);
+    public Object testField4040 = new Integer(4040);
+    public Object testField4041 = new Integer(4041);
+    public Object testField4042 = new Integer(4042);
+    public Object testField4043 = new Integer(4043);
+    public Object testField4044 = new Integer(4044);
+    public Object testField4045 = new Integer(4045);
+    public Object testField4046 = new Integer(4046);
+    public Object testField4047 = new Integer(4047);
+    public Object testField4048 = new Integer(4048);
+    public Object testField4049 = new Integer(4049);
+    public Object testField4050 = new Integer(4050);
+    public Object testField4051 = new Integer(4051);
+    public Object testField4052 = new Integer(4052);
+    public Object testField4053 = new Integer(4053);
+    public Object testField4054 = new Integer(4054);
+    public Object testField4055 = new Integer(4055);
+    public Object testField4056 = new Integer(4056);
+    public Object testField4057 = new Integer(4057);
+    public Object testField4058 = new Integer(4058);
+    public Object testField4059 = new Integer(4059);
+    public Object testField4060 = new Integer(4060);
+    public Object testField4061 = new Integer(4061);
+    public Object testField4062 = new Integer(4062);
+    public Object testField4063 = new Integer(4063);
+    public Object testField4064 = new Integer(4064);
+    public Object testField4065 = new Integer(4065);
+    public Object testField4066 = new Integer(4066);
+    public Object testField4067 = new Integer(4067);
+    public Object testField4068 = new Integer(4068);
+    public Object testField4069 = new Integer(4069);
+    public Object testField4070 = new Integer(4070);
+    public Object testField4071 = new Integer(4071);
+    public Object testField4072 = new Integer(4072);
+    public Object testField4073 = new Integer(4073);
+    public Object testField4074 = new Integer(4074);
+    public Object testField4075 = new Integer(4075);
+    public Object testField4076 = new Integer(4076);
+    public Object testField4077 = new Integer(4077);
+    public Object testField4078 = new Integer(4078);
+    public Object testField4079 = new Integer(4079);
+    public Object testField4080 = new Integer(4080);
+    public Object testField4081 = new Integer(4081);
+    public Object testField4082 = new Integer(4082);
+    public Object testField4083 = new Integer(4083);
+    public Object testField4084 = new Integer(4084);
+    public Object testField4085 = new Integer(4085);
+    public Object testField4086 = new Integer(4086);
+    public Object testField4087 = new Integer(4087);
+    public Object testField4088 = new Integer(4088);
+    public Object testField4089 = new Integer(4089);
+    public Object testField4090 = new Integer(4090);
+    public Object testField4091 = new Integer(4091);
+    public Object testField4092 = new Integer(4092);
+    public Object testField4093 = new Integer(4093);
+    public Object testField4094 = new Integer(4094);
+    public Object testField4095 = new Integer(4095);
+    public Object testField4096 = new Integer(4096);
+    public Object testField4097 = new Integer(4097);
+    public Object testField4098 = new Integer(4098);
+    public Object testField4099 = new Integer(4099);
+    public Object testField4100 = new Integer(4100);
+    public Object testField4101 = new Integer(4101);
+    public Object testField4102 = new Integer(4102);
+    public Object testField4103 = new Integer(4103);
+    public Object testField4104 = new Integer(4104);
+    public Object testField4105 = new Integer(4105);
+    public Object testField4106 = new Integer(4106);
+    public Object testField4107 = new Integer(4107);
+    public Object testField4108 = new Integer(4108);
+    public Object testField4109 = new Integer(4109);
+    public Object testField4110 = new Integer(4110);
+    public Object testField4111 = new Integer(4111);
+    public Object testField4112 = new Integer(4112);
+    public Object testField4113 = new Integer(4113);
+    public Object testField4114 = new Integer(4114);
+    public Object testField4115 = new Integer(4115);
+    public Object testField4116 = new Integer(4116);
+    public Object testField4117 = new Integer(4117);
+    public Object testField4118 = new Integer(4118);
+    public Object testField4119 = new Integer(4119);
+    public Object testField4120 = new Integer(4120);
+    public Object testField4121 = new Integer(4121);
+    public Object testField4122 = new Integer(4122);
+    public Object testField4123 = new Integer(4123);
+    public Object testField4124 = new Integer(4124);
+    public Object testField4125 = new Integer(4125);
+    public Object testField4126 = new Integer(4126);
+    public Object testField4127 = new Integer(4127);
+    public Object testField4128 = new Integer(4128);
+    public Object testField4129 = new Integer(4129);
+    public Object testField4130 = new Integer(4130);
+    public Object testField4131 = new Integer(4131);
+    public Object testField4132 = new Integer(4132);
+    public Object testField4133 = new Integer(4133);
+    public Object testField4134 = new Integer(4134);
+    public Object testField4135 = new Integer(4135);
+    public Object testField4136 = new Integer(4136);
+    public Object testField4137 = new Integer(4137);
+    public Object testField4138 = new Integer(4138);
+    public Object testField4139 = new Integer(4139);
+    public Object testField4140 = new Integer(4140);
+    public Object testField4141 = new Integer(4141);
+    public Object testField4142 = new Integer(4142);
+    public Object testField4143 = new Integer(4143);
+    public Object testField4144 = new Integer(4144);
+    public Object testField4145 = new Integer(4145);
+    public Object testField4146 = new Integer(4146);
+    public Object testField4147 = new Integer(4147);
+    public Object testField4148 = new Integer(4148);
+    public Object testField4149 = new Integer(4149);
+    public Object testField4150 = new Integer(4150);
+    public Object testField4151 = new Integer(4151);
+    public Object testField4152 = new Integer(4152);
+    public Object testField4153 = new Integer(4153);
+    public Object testField4154 = new Integer(4154);
+    public Object testField4155 = new Integer(4155);
+    public Object testField4156 = new Integer(4156);
+    public Object testField4157 = new Integer(4157);
+    public Object testField4158 = new Integer(4158);
+    public Object testField4159 = new Integer(4159);
+    public Object testField4160 = new Integer(4160);
+    public Object testField4161 = new Integer(4161);
+    public Object testField4162 = new Integer(4162);
+    public Object testField4163 = new Integer(4163);
+    public Object testField4164 = new Integer(4164);
+    public Object testField4165 = new Integer(4165);
+    public Object testField4166 = new Integer(4166);
+    public Object testField4167 = new Integer(4167);
+    public Object testField4168 = new Integer(4168);
+    public Object testField4169 = new Integer(4169);
+    public Object testField4170 = new Integer(4170);
+    public Object testField4171 = new Integer(4171);
+    public Object testField4172 = new Integer(4172);
+    public Object testField4173 = new Integer(4173);
+    public Object testField4174 = new Integer(4174);
+    public Object testField4175 = new Integer(4175);
+    public Object testField4176 = new Integer(4176);
+    public Object testField4177 = new Integer(4177);
+    public Object testField4178 = new Integer(4178);
+    public Object testField4179 = new Integer(4179);
+    public Object testField4180 = new Integer(4180);
+    public Object testField4181 = new Integer(4181);
+    public Object testField4182 = new Integer(4182);
+    public Object testField4183 = new Integer(4183);
+    public Object testField4184 = new Integer(4184);
+    public Object testField4185 = new Integer(4185);
+    public Object testField4186 = new Integer(4186);
+    public Object testField4187 = new Integer(4187);
+    public Object testField4188 = new Integer(4188);
+    public Object testField4189 = new Integer(4189);
+    public Object testField4190 = new Integer(4190);
+    public Object testField4191 = new Integer(4191);
+    public Object testField4192 = new Integer(4192);
+    public Object testField4193 = new Integer(4193);
+    public Object testField4194 = new Integer(4194);
+    public Object testField4195 = new Integer(4195);
+    public Object testField4196 = new Integer(4196);
+    public Object testField4197 = new Integer(4197);
+    public Object testField4198 = new Integer(4198);
+    public Object testField4199 = new Integer(4199);
+    public Object testField4200 = new Integer(4200);
+    public Object testField4201 = new Integer(4201);
+    public Object testField4202 = new Integer(4202);
+    public Object testField4203 = new Integer(4203);
+    public Object testField4204 = new Integer(4204);
+    public Object testField4205 = new Integer(4205);
+    public Object testField4206 = new Integer(4206);
+    public Object testField4207 = new Integer(4207);
+    public Object testField4208 = new Integer(4208);
+    public Object testField4209 = new Integer(4209);
+    public Object testField4210 = new Integer(4210);
+    public Object testField4211 = new Integer(4211);
+    public Object testField4212 = new Integer(4212);
+    public Object testField4213 = new Integer(4213);
+    public Object testField4214 = new Integer(4214);
+    public Object testField4215 = new Integer(4215);
+    public Object testField4216 = new Integer(4216);
+    public Object testField4217 = new Integer(4217);
+    public Object testField4218 = new Integer(4218);
+    public Object testField4219 = new Integer(4219);
+    public Object testField4220 = new Integer(4220);
+    public Object testField4221 = new Integer(4221);
+    public Object testField4222 = new Integer(4222);
+    public Object testField4223 = new Integer(4223);
+    public Object testField4224 = new Integer(4224);
+    public Object testField4225 = new Integer(4225);
+    public Object testField4226 = new Integer(4226);
+    public Object testField4227 = new Integer(4227);
+    public Object testField4228 = new Integer(4228);
+    public Object testField4229 = new Integer(4229);
+    public Object testField4230 = new Integer(4230);
+    public Object testField4231 = new Integer(4231);
+    public Object testField4232 = new Integer(4232);
+    public Object testField4233 = new Integer(4233);
+    public Object testField4234 = new Integer(4234);
+    public Object testField4235 = new Integer(4235);
+    public Object testField4236 = new Integer(4236);
+    public Object testField4237 = new Integer(4237);
+    public Object testField4238 = new Integer(4238);
+    public Object testField4239 = new Integer(4239);
+    public Object testField4240 = new Integer(4240);
+    public Object testField4241 = new Integer(4241);
+    public Object testField4242 = new Integer(4242);
+    public Object testField4243 = new Integer(4243);
+    public Object testField4244 = new Integer(4244);
+    public Object testField4245 = new Integer(4245);
+    public Object testField4246 = new Integer(4246);
+    public Object testField4247 = new Integer(4247);
+    public Object testField4248 = new Integer(4248);
+    public Object testField4249 = new Integer(4249);
+    public Object testField4250 = new Integer(4250);
+    public Object testField4251 = new Integer(4251);
+    public Object testField4252 = new Integer(4252);
+    public Object testField4253 = new Integer(4253);
+    public Object testField4254 = new Integer(4254);
+    public Object testField4255 = new Integer(4255);
+    public Object testField4256 = new Integer(4256);
+    public Object testField4257 = new Integer(4257);
+    public Object testField4258 = new Integer(4258);
+    public Object testField4259 = new Integer(4259);
+    public Object testField4260 = new Integer(4260);
+    public Object testField4261 = new Integer(4261);
+    public Object testField4262 = new Integer(4262);
+    public Object testField4263 = new Integer(4263);
+    public Object testField4264 = new Integer(4264);
+    public Object testField4265 = new Integer(4265);
+    public Object testField4266 = new Integer(4266);
+    public Object testField4267 = new Integer(4267);
+    public Object testField4268 = new Integer(4268);
+    public Object testField4269 = new Integer(4269);
+    public Object testField4270 = new Integer(4270);
+    public Object testField4271 = new Integer(4271);
+    public Object testField4272 = new Integer(4272);
+    public Object testField4273 = new Integer(4273);
+    public Object testField4274 = new Integer(4274);
+    public Object testField4275 = new Integer(4275);
+    public Object testField4276 = new Integer(4276);
+    public Object testField4277 = new Integer(4277);
+    public Object testField4278 = new Integer(4278);
+    public Object testField4279 = new Integer(4279);
+    public Object testField4280 = new Integer(4280);
+    public Object testField4281 = new Integer(4281);
+    public Object testField4282 = new Integer(4282);
+    public Object testField4283 = new Integer(4283);
+    public Object testField4284 = new Integer(4284);
+    public Object testField4285 = new Integer(4285);
+    public Object testField4286 = new Integer(4286);
+    public Object testField4287 = new Integer(4287);
+    public Object testField4288 = new Integer(4288);
+    public Object testField4289 = new Integer(4289);
+    public Object testField4290 = new Integer(4290);
+    public Object testField4291 = new Integer(4291);
+    public Object testField4292 = new Integer(4292);
+    public Object testField4293 = new Integer(4293);
+    public Object testField4294 = new Integer(4294);
+    public Object testField4295 = new Integer(4295);
+    public Object testField4296 = new Integer(4296);
+    public Object testField4297 = new Integer(4297);
+    public Object testField4298 = new Integer(4298);
+    public Object testField4299 = new Integer(4299);
+    public Object testField4300 = new Integer(4300);
+    public Object testField4301 = new Integer(4301);
+    public Object testField4302 = new Integer(4302);
+    public Object testField4303 = new Integer(4303);
+    public Object testField4304 = new Integer(4304);
+    public Object testField4305 = new Integer(4305);
+    public Object testField4306 = new Integer(4306);
+    public Object testField4307 = new Integer(4307);
+    public Object testField4308 = new Integer(4308);
+    public Object testField4309 = new Integer(4309);
+    public Object testField4310 = new Integer(4310);
+    public Object testField4311 = new Integer(4311);
+    public Object testField4312 = new Integer(4312);
+    public Object testField4313 = new Integer(4313);
+    public Object testField4314 = new Integer(4314);
+    public Object testField4315 = new Integer(4315);
+    public Object testField4316 = new Integer(4316);
+    public Object testField4317 = new Integer(4317);
+    public Object testField4318 = new Integer(4318);
+    public Object testField4319 = new Integer(4319);
+    public Object testField4320 = new Integer(4320);
+    public Object testField4321 = new Integer(4321);
+    public Object testField4322 = new Integer(4322);
+    public Object testField4323 = new Integer(4323);
+    public Object testField4324 = new Integer(4324);
+    public Object testField4325 = new Integer(4325);
+    public Object testField4326 = new Integer(4326);
+    public Object testField4327 = new Integer(4327);
+    public Object testField4328 = new Integer(4328);
+    public Object testField4329 = new Integer(4329);
+    public Object testField4330 = new Integer(4330);
+    public Object testField4331 = new Integer(4331);
+    public Object testField4332 = new Integer(4332);
+    public Object testField4333 = new Integer(4333);
+    public Object testField4334 = new Integer(4334);
+    public Object testField4335 = new Integer(4335);
+    public Object testField4336 = new Integer(4336);
+    public Object testField4337 = new Integer(4337);
+    public Object testField4338 = new Integer(4338);
+    public Object testField4339 = new Integer(4339);
+    public Object testField4340 = new Integer(4340);
+    public Object testField4341 = new Integer(4341);
+    public Object testField4342 = new Integer(4342);
+    public Object testField4343 = new Integer(4343);
+    public Object testField4344 = new Integer(4344);
+    public Object testField4345 = new Integer(4345);
+    public Object testField4346 = new Integer(4346);
+    public Object testField4347 = new Integer(4347);
+    public Object testField4348 = new Integer(4348);
+    public Object testField4349 = new Integer(4349);
+    public Object testField4350 = new Integer(4350);
+    public Object testField4351 = new Integer(4351);
+    public Object testField4352 = new Integer(4352);
+    public Object testField4353 = new Integer(4353);
+    public Object testField4354 = new Integer(4354);
+    public Object testField4355 = new Integer(4355);
+    public Object testField4356 = new Integer(4356);
+    public Object testField4357 = new Integer(4357);
+    public Object testField4358 = new Integer(4358);
+    public Object testField4359 = new Integer(4359);
+    public Object testField4360 = new Integer(4360);
+    public Object testField4361 = new Integer(4361);
+    public Object testField4362 = new Integer(4362);
+    public Object testField4363 = new Integer(4363);
+    public Object testField4364 = new Integer(4364);
+    public Object testField4365 = new Integer(4365);
+    public Object testField4366 = new Integer(4366);
+    public Object testField4367 = new Integer(4367);
+    public Object testField4368 = new Integer(4368);
+    public Object testField4369 = new Integer(4369);
+    public Object testField4370 = new Integer(4370);
+    public Object testField4371 = new Integer(4371);
+    public Object testField4372 = new Integer(4372);
+    public Object testField4373 = new Integer(4373);
+    public Object testField4374 = new Integer(4374);
+    public Object testField4375 = new Integer(4375);
+    public Object testField4376 = new Integer(4376);
+    public Object testField4377 = new Integer(4377);
+    public Object testField4378 = new Integer(4378);
+    public Object testField4379 = new Integer(4379);
+    public Object testField4380 = new Integer(4380);
+    public Object testField4381 = new Integer(4381);
+    public Object testField4382 = new Integer(4382);
+    public Object testField4383 = new Integer(4383);
+    public Object testField4384 = new Integer(4384);
+    public Object testField4385 = new Integer(4385);
+    public Object testField4386 = new Integer(4386);
+    public Object testField4387 = new Integer(4387);
+    public Object testField4388 = new Integer(4388);
+    public Object testField4389 = new Integer(4389);
+    public Object testField4390 = new Integer(4390);
+    public Object testField4391 = new Integer(4391);
+    public Object testField4392 = new Integer(4392);
+    public Object testField4393 = new Integer(4393);
+    public Object testField4394 = new Integer(4394);
+    public Object testField4395 = new Integer(4395);
+    public Object testField4396 = new Integer(4396);
+    public Object testField4397 = new Integer(4397);
+    public Object testField4398 = new Integer(4398);
+    public Object testField4399 = new Integer(4399);
+    public Object testField4400 = new Integer(4400);
+    public Object testField4401 = new Integer(4401);
+    public Object testField4402 = new Integer(4402);
+    public Object testField4403 = new Integer(4403);
+    public Object testField4404 = new Integer(4404);
+    public Object testField4405 = new Integer(4405);
+    public Object testField4406 = new Integer(4406);
+    public Object testField4407 = new Integer(4407);
+    public Object testField4408 = new Integer(4408);
+    public Object testField4409 = new Integer(4409);
+    public Object testField4410 = new Integer(4410);
+    public Object testField4411 = new Integer(4411);
+    public Object testField4412 = new Integer(4412);
+    public Object testField4413 = new Integer(4413);
+    public Object testField4414 = new Integer(4414);
+    public Object testField4415 = new Integer(4415);
+    public Object testField4416 = new Integer(4416);
+    public Object testField4417 = new Integer(4417);
+    public Object testField4418 = new Integer(4418);
+    public Object testField4419 = new Integer(4419);
+    public Object testField4420 = new Integer(4420);
+    public Object testField4421 = new Integer(4421);
+    public Object testField4422 = new Integer(4422);
+    public Object testField4423 = new Integer(4423);
+    public Object testField4424 = new Integer(4424);
+    public Object testField4425 = new Integer(4425);
+    public Object testField4426 = new Integer(4426);
+    public Object testField4427 = new Integer(4427);
+    public Object testField4428 = new Integer(4428);
+    public Object testField4429 = new Integer(4429);
+    public Object testField4430 = new Integer(4430);
+    public Object testField4431 = new Integer(4431);
+    public Object testField4432 = new Integer(4432);
+    public Object testField4433 = new Integer(4433);
+    public Object testField4434 = new Integer(4434);
+    public Object testField4435 = new Integer(4435);
+    public Object testField4436 = new Integer(4436);
+    public Object testField4437 = new Integer(4437);
+    public Object testField4438 = new Integer(4438);
+    public Object testField4439 = new Integer(4439);
+    public Object testField4440 = new Integer(4440);
+    public Object testField4441 = new Integer(4441);
+    public Object testField4442 = new Integer(4442);
+    public Object testField4443 = new Integer(4443);
+    public Object testField4444 = new Integer(4444);
+    public Object testField4445 = new Integer(4445);
+    public Object testField4446 = new Integer(4446);
+    public Object testField4447 = new Integer(4447);
+    public Object testField4448 = new Integer(4448);
+    public Object testField4449 = new Integer(4449);
+    public Object testField4450 = new Integer(4450);
+    public Object testField4451 = new Integer(4451);
+    public Object testField4452 = new Integer(4452);
+    public Object testField4453 = new Integer(4453);
+    public Object testField4454 = new Integer(4454);
+    public Object testField4455 = new Integer(4455);
+    public Object testField4456 = new Integer(4456);
+    public Object testField4457 = new Integer(4457);
+    public Object testField4458 = new Integer(4458);
+    public Object testField4459 = new Integer(4459);
+    public Object testField4460 = new Integer(4460);
+    public Object testField4461 = new Integer(4461);
+    public Object testField4462 = new Integer(4462);
+    public Object testField4463 = new Integer(4463);
+    public Object testField4464 = new Integer(4464);
+    public Object testField4465 = new Integer(4465);
+    public Object testField4466 = new Integer(4466);
+    public Object testField4467 = new Integer(4467);
+    public Object testField4468 = new Integer(4468);
+    public Object testField4469 = new Integer(4469);
+    public Object testField4470 = new Integer(4470);
+    public Object testField4471 = new Integer(4471);
+    public Object testField4472 = new Integer(4472);
+    public Object testField4473 = new Integer(4473);
+    public Object testField4474 = new Integer(4474);
+    public Object testField4475 = new Integer(4475);
+    public Object testField4476 = new Integer(4476);
+    public Object testField4477 = new Integer(4477);
+    public Object testField4478 = new Integer(4478);
+    public Object testField4479 = new Integer(4479);
+    public Object testField4480 = new Integer(4480);
+    public Object testField4481 = new Integer(4481);
+    public Object testField4482 = new Integer(4482);
+    public Object testField4483 = new Integer(4483);
+    public Object testField4484 = new Integer(4484);
+    public Object testField4485 = new Integer(4485);
+    public Object testField4486 = new Integer(4486);
+    public Object testField4487 = new Integer(4487);
+    public Object testField4488 = new Integer(4488);
+    public Object testField4489 = new Integer(4489);
+    public Object testField4490 = new Integer(4490);
+    public Object testField4491 = new Integer(4491);
+    public Object testField4492 = new Integer(4492);
+    public Object testField4493 = new Integer(4493);
+    public Object testField4494 = new Integer(4494);
+    public Object testField4495 = new Integer(4495);
+    public Object testField4496 = new Integer(4496);
+    public Object testField4497 = new Integer(4497);
+    public Object testField4498 = new Integer(4498);
+    public Object testField4499 = new Integer(4499);
+    public Object testField4500 = new Integer(4500);
+    public Object testField4501 = new Integer(4501);
+    public Object testField4502 = new Integer(4502);
+    public Object testField4503 = new Integer(4503);
+    public Object testField4504 = new Integer(4504);
+    public Object testField4505 = new Integer(4505);
+    public Object testField4506 = new Integer(4506);
+    public Object testField4507 = new Integer(4507);
+    public Object testField4508 = new Integer(4508);
+    public Object testField4509 = new Integer(4509);
+    public Object testField4510 = new Integer(4510);
+    public Object testField4511 = new Integer(4511);
+    public Object testField4512 = new Integer(4512);
+    public Object testField4513 = new Integer(4513);
+    public Object testField4514 = new Integer(4514);
+    public Object testField4515 = new Integer(4515);
+    public Object testField4516 = new Integer(4516);
+    public Object testField4517 = new Integer(4517);
+    public Object testField4518 = new Integer(4518);
+    public Object testField4519 = new Integer(4519);
+    public Object testField4520 = new Integer(4520);
+    public Object testField4521 = new Integer(4521);
+    public Object testField4522 = new Integer(4522);
+    public Object testField4523 = new Integer(4523);
+    public Object testField4524 = new Integer(4524);
+    public Object testField4525 = new Integer(4525);
+    public Object testField4526 = new Integer(4526);
+    public Object testField4527 = new Integer(4527);
+    public Object testField4528 = new Integer(4528);
+    public Object testField4529 = new Integer(4529);
+    public Object testField4530 = new Integer(4530);
+    public Object testField4531 = new Integer(4531);
+    public Object testField4532 = new Integer(4532);
+    public Object testField4533 = new Integer(4533);
+    public Object testField4534 = new Integer(4534);
+    public Object testField4535 = new Integer(4535);
+    public Object testField4536 = new Integer(4536);
+    public Object testField4537 = new Integer(4537);
+    public Object testField4538 = new Integer(4538);
+    public Object testField4539 = new Integer(4539);
+    public Object testField4540 = new Integer(4540);
+    public Object testField4541 = new Integer(4541);
+    public Object testField4542 = new Integer(4542);
+    public Object testField4543 = new Integer(4543);
+    public Object testField4544 = new Integer(4544);
+    public Object testField4545 = new Integer(4545);
+    public Object testField4546 = new Integer(4546);
+    public Object testField4547 = new Integer(4547);
+    public Object testField4548 = new Integer(4548);
+    public Object testField4549 = new Integer(4549);
+    public Object testField4550 = new Integer(4550);
+    public Object testField4551 = new Integer(4551);
+    public Object testField4552 = new Integer(4552);
+    public Object testField4553 = new Integer(4553);
+    public Object testField4554 = new Integer(4554);
+    public Object testField4555 = new Integer(4555);
+    public Object testField4556 = new Integer(4556);
+    public Object testField4557 = new Integer(4557);
+    public Object testField4558 = new Integer(4558);
+    public Object testField4559 = new Integer(4559);
+    public Object testField4560 = new Integer(4560);
+    public Object testField4561 = new Integer(4561);
+    public Object testField4562 = new Integer(4562);
+    public Object testField4563 = new Integer(4563);
+    public Object testField4564 = new Integer(4564);
+    public Object testField4565 = new Integer(4565);
+    public Object testField4566 = new Integer(4566);
+    public Object testField4567 = new Integer(4567);
+    public Object testField4568 = new Integer(4568);
+    public Object testField4569 = new Integer(4569);
+    public Object testField4570 = new Integer(4570);
+    public Object testField4571 = new Integer(4571);
+    public Object testField4572 = new Integer(4572);
+    public Object testField4573 = new Integer(4573);
+    public Object testField4574 = new Integer(4574);
+    public Object testField4575 = new Integer(4575);
+    public Object testField4576 = new Integer(4576);
+    public Object testField4577 = new Integer(4577);
+    public Object testField4578 = new Integer(4578);
+    public Object testField4579 = new Integer(4579);
+    public Object testField4580 = new Integer(4580);
+    public Object testField4581 = new Integer(4581);
+    public Object testField4582 = new Integer(4582);
+    public Object testField4583 = new Integer(4583);
+    public Object testField4584 = new Integer(4584);
+    public Object testField4585 = new Integer(4585);
+    public Object testField4586 = new Integer(4586);
+    public Object testField4587 = new Integer(4587);
+    public Object testField4588 = new Integer(4588);
+    public Object testField4589 = new Integer(4589);
+    public Object testField4590 = new Integer(4590);
+    public Object testField4591 = new Integer(4591);
+    public Object testField4592 = new Integer(4592);
+    public Object testField4593 = new Integer(4593);
+    public Object testField4594 = new Integer(4594);
+    public Object testField4595 = new Integer(4595);
+    public Object testField4596 = new Integer(4596);
+    public Object testField4597 = new Integer(4597);
+    public Object testField4598 = new Integer(4598);
+    public Object testField4599 = new Integer(4599);
+    public Object testField4600 = new Integer(4600);
+    public Object testField4601 = new Integer(4601);
+    public Object testField4602 = new Integer(4602);
+    public Object testField4603 = new Integer(4603);
+    public Object testField4604 = new Integer(4604);
+    public Object testField4605 = new Integer(4605);
+    public Object testField4606 = new Integer(4606);
+    public Object testField4607 = new Integer(4607);
+    public Object testField4608 = new Integer(4608);
+    public Object testField4609 = new Integer(4609);
+    public Object testField4610 = new Integer(4610);
+    public Object testField4611 = new Integer(4611);
+    public Object testField4612 = new Integer(4612);
+    public Object testField4613 = new Integer(4613);
+    public Object testField4614 = new Integer(4614);
+    public Object testField4615 = new Integer(4615);
+    public Object testField4616 = new Integer(4616);
+    public Object testField4617 = new Integer(4617);
+    public Object testField4618 = new Integer(4618);
+    public Object testField4619 = new Integer(4619);
+    public Object testField4620 = new Integer(4620);
+    public Object testField4621 = new Integer(4621);
+    public Object testField4622 = new Integer(4622);
+    public Object testField4623 = new Integer(4623);
+    public Object testField4624 = new Integer(4624);
+    public Object testField4625 = new Integer(4625);
+    public Object testField4626 = new Integer(4626);
+    public Object testField4627 = new Integer(4627);
+    public Object testField4628 = new Integer(4628);
+    public Object testField4629 = new Integer(4629);
+    public Object testField4630 = new Integer(4630);
+    public Object testField4631 = new Integer(4631);
+    public Object testField4632 = new Integer(4632);
+    public Object testField4633 = new Integer(4633);
+    public Object testField4634 = new Integer(4634);
+    public Object testField4635 = new Integer(4635);
+    public Object testField4636 = new Integer(4636);
+    public Object testField4637 = new Integer(4637);
+    public Object testField4638 = new Integer(4638);
+    public Object testField4639 = new Integer(4639);
+    public Object testField4640 = new Integer(4640);
+    public Object testField4641 = new Integer(4641);
+    public Object testField4642 = new Integer(4642);
+    public Object testField4643 = new Integer(4643);
+    public Object testField4644 = new Integer(4644);
+    public Object testField4645 = new Integer(4645);
+    public Object testField4646 = new Integer(4646);
+    public Object testField4647 = new Integer(4647);
+    public Object testField4648 = new Integer(4648);
+    public Object testField4649 = new Integer(4649);
+    public Object testField4650 = new Integer(4650);
+    public Object testField4651 = new Integer(4651);
+    public Object testField4652 = new Integer(4652);
+    public Object testField4653 = new Integer(4653);
+    public Object testField4654 = new Integer(4654);
+    public Object testField4655 = new Integer(4655);
+    public Object testField4656 = new Integer(4656);
+    public Object testField4657 = new Integer(4657);
+    public Object testField4658 = new Integer(4658);
+    public Object testField4659 = new Integer(4659);
+    public Object testField4660 = new Integer(4660);
+    public Object testField4661 = new Integer(4661);
+    public Object testField4662 = new Integer(4662);
+    public Object testField4663 = new Integer(4663);
+    public Object testField4664 = new Integer(4664);
+    public Object testField4665 = new Integer(4665);
+    public Object testField4666 = new Integer(4666);
+    public Object testField4667 = new Integer(4667);
+    public Object testField4668 = new Integer(4668);
+    public Object testField4669 = new Integer(4669);
+    public Object testField4670 = new Integer(4670);
+    public Object testField4671 = new Integer(4671);
+    public Object testField4672 = new Integer(4672);
+    public Object testField4673 = new Integer(4673);
+    public Object testField4674 = new Integer(4674);
+    public Object testField4675 = new Integer(4675);
+    public Object testField4676 = new Integer(4676);
+    public Object testField4677 = new Integer(4677);
+    public Object testField4678 = new Integer(4678);
+    public Object testField4679 = new Integer(4679);
+    public Object testField4680 = new Integer(4680);
+    public Object testField4681 = new Integer(4681);
+    public Object testField4682 = new Integer(4682);
+    public Object testField4683 = new Integer(4683);
+    public Object testField4684 = new Integer(4684);
+    public Object testField4685 = new Integer(4685);
+    public Object testField4686 = new Integer(4686);
+    public Object testField4687 = new Integer(4687);
+    public Object testField4688 = new Integer(4688);
+    public Object testField4689 = new Integer(4689);
+    public Object testField4690 = new Integer(4690);
+    public Object testField4691 = new Integer(4691);
+    public Object testField4692 = new Integer(4692);
+    public Object testField4693 = new Integer(4693);
+    public Object testField4694 = new Integer(4694);
+    public Object testField4695 = new Integer(4695);
+    public Object testField4696 = new Integer(4696);
+    public Object testField4697 = new Integer(4697);
+    public Object testField4698 = new Integer(4698);
+    public Object testField4699 = new Integer(4699);
+    public Object testField4700 = new Integer(4700);
+    public Object testField4701 = new Integer(4701);
+    public Object testField4702 = new Integer(4702);
+    public Object testField4703 = new Integer(4703);
+    public Object testField4704 = new Integer(4704);
+    public Object testField4705 = new Integer(4705);
+    public Object testField4706 = new Integer(4706);
+    public Object testField4707 = new Integer(4707);
+    public Object testField4708 = new Integer(4708);
+    public Object testField4709 = new Integer(4709);
+    public Object testField4710 = new Integer(4710);
+    public Object testField4711 = new Integer(4711);
+    public Object testField4712 = new Integer(4712);
+    public Object testField4713 = new Integer(4713);
+    public Object testField4714 = new Integer(4714);
+    public Object testField4715 = new Integer(4715);
+    public Object testField4716 = new Integer(4716);
+    public Object testField4717 = new Integer(4717);
+    public Object testField4718 = new Integer(4718);
+    public Object testField4719 = new Integer(4719);
+    public Object testField4720 = new Integer(4720);
+    public Object testField4721 = new Integer(4721);
+    public Object testField4722 = new Integer(4722);
+    public Object testField4723 = new Integer(4723);
+    public Object testField4724 = new Integer(4724);
+    public Object testField4725 = new Integer(4725);
+    public Object testField4726 = new Integer(4726);
+    public Object testField4727 = new Integer(4727);
+    public Object testField4728 = new Integer(4728);
+    public Object testField4729 = new Integer(4729);
+    public Object testField4730 = new Integer(4730);
+    public Object testField4731 = new Integer(4731);
+    public Object testField4732 = new Integer(4732);
+    public Object testField4733 = new Integer(4733);
+    public Object testField4734 = new Integer(4734);
+    public Object testField4735 = new Integer(4735);
+    public Object testField4736 = new Integer(4736);
+    public Object testField4737 = new Integer(4737);
+    public Object testField4738 = new Integer(4738);
+    public Object testField4739 = new Integer(4739);
+    public Object testField4740 = new Integer(4740);
+    public Object testField4741 = new Integer(4741);
+    public Object testField4742 = new Integer(4742);
+    public Object testField4743 = new Integer(4743);
+    public Object testField4744 = new Integer(4744);
+    public Object testField4745 = new Integer(4745);
+    public Object testField4746 = new Integer(4746);
+    public Object testField4747 = new Integer(4747);
+    public Object testField4748 = new Integer(4748);
+    public Object testField4749 = new Integer(4749);
+    public Object testField4750 = new Integer(4750);
+    public Object testField4751 = new Integer(4751);
+    public Object testField4752 = new Integer(4752);
+    public Object testField4753 = new Integer(4753);
+    public Object testField4754 = new Integer(4754);
+    public Object testField4755 = new Integer(4755);
+    public Object testField4756 = new Integer(4756);
+    public Object testField4757 = new Integer(4757);
+    public Object testField4758 = new Integer(4758);
+    public Object testField4759 = new Integer(4759);
+    public Object testField4760 = new Integer(4760);
+    public Object testField4761 = new Integer(4761);
+    public Object testField4762 = new Integer(4762);
+    public Object testField4763 = new Integer(4763);
+    public Object testField4764 = new Integer(4764);
+    public Object testField4765 = new Integer(4765);
+    public Object testField4766 = new Integer(4766);
+    public Object testField4767 = new Integer(4767);
+    public Object testField4768 = new Integer(4768);
+    public Object testField4769 = new Integer(4769);
+    public Object testField4770 = new Integer(4770);
+    public Object testField4771 = new Integer(4771);
+    public Object testField4772 = new Integer(4772);
+    public Object testField4773 = new Integer(4773);
+    public Object testField4774 = new Integer(4774);
+    public Object testField4775 = new Integer(4775);
+    public Object testField4776 = new Integer(4776);
+    public Object testField4777 = new Integer(4777);
+    public Object testField4778 = new Integer(4778);
+    public Object testField4779 = new Integer(4779);
+    public Object testField4780 = new Integer(4780);
+    public Object testField4781 = new Integer(4781);
+    public Object testField4782 = new Integer(4782);
+    public Object testField4783 = new Integer(4783);
+    public Object testField4784 = new Integer(4784);
+    public Object testField4785 = new Integer(4785);
+    public Object testField4786 = new Integer(4786);
+    public Object testField4787 = new Integer(4787);
+    public Object testField4788 = new Integer(4788);
+    public Object testField4789 = new Integer(4789);
+    public Object testField4790 = new Integer(4790);
+    public Object testField4791 = new Integer(4791);
+    public Object testField4792 = new Integer(4792);
+    public Object testField4793 = new Integer(4793);
+    public Object testField4794 = new Integer(4794);
+    public Object testField4795 = new Integer(4795);
+    public Object testField4796 = new Integer(4796);
+    public Object testField4797 = new Integer(4797);
+    public Object testField4798 = new Integer(4798);
+    public Object testField4799 = new Integer(4799);
+    public Object testField4800 = new Integer(4800);
+    public Object testField4801 = new Integer(4801);
+    public Object testField4802 = new Integer(4802);
+    public Object testField4803 = new Integer(4803);
+    public Object testField4804 = new Integer(4804);
+    public Object testField4805 = new Integer(4805);
+    public Object testField4806 = new Integer(4806);
+    public Object testField4807 = new Integer(4807);
+    public Object testField4808 = new Integer(4808);
+    public Object testField4809 = new Integer(4809);
+    public Object testField4810 = new Integer(4810);
+    public Object testField4811 = new Integer(4811);
+    public Object testField4812 = new Integer(4812);
+    public Object testField4813 = new Integer(4813);
+    public Object testField4814 = new Integer(4814);
+    public Object testField4815 = new Integer(4815);
+    public Object testField4816 = new Integer(4816);
+    public Object testField4817 = new Integer(4817);
+    public Object testField4818 = new Integer(4818);
+    public Object testField4819 = new Integer(4819);
+    public Object testField4820 = new Integer(4820);
+    public Object testField4821 = new Integer(4821);
+    public Object testField4822 = new Integer(4822);
+    public Object testField4823 = new Integer(4823);
+    public Object testField4824 = new Integer(4824);
+    public Object testField4825 = new Integer(4825);
+    public Object testField4826 = new Integer(4826);
+    public Object testField4827 = new Integer(4827);
+    public Object testField4828 = new Integer(4828);
+    public Object testField4829 = new Integer(4829);
+    public Object testField4830 = new Integer(4830);
+    public Object testField4831 = new Integer(4831);
+    public Object testField4832 = new Integer(4832);
+    public Object testField4833 = new Integer(4833);
+    public Object testField4834 = new Integer(4834);
+    public Object testField4835 = new Integer(4835);
+    public Object testField4836 = new Integer(4836);
+    public Object testField4837 = new Integer(4837);
+    public Object testField4838 = new Integer(4838);
+    public Object testField4839 = new Integer(4839);
+    public Object testField4840 = new Integer(4840);
+    public Object testField4841 = new Integer(4841);
+    public Object testField4842 = new Integer(4842);
+    public Object testField4843 = new Integer(4843);
+    public Object testField4844 = new Integer(4844);
+    public Object testField4845 = new Integer(4845);
+    public Object testField4846 = new Integer(4846);
+    public Object testField4847 = new Integer(4847);
+    public Object testField4848 = new Integer(4848);
+    public Object testField4849 = new Integer(4849);
+    public Object testField4850 = new Integer(4850);
+    public Object testField4851 = new Integer(4851);
+    public Object testField4852 = new Integer(4852);
+    public Object testField4853 = new Integer(4853);
+    public Object testField4854 = new Integer(4854);
+    public Object testField4855 = new Integer(4855);
+    public Object testField4856 = new Integer(4856);
+    public Object testField4857 = new Integer(4857);
+    public Object testField4858 = new Integer(4858);
+    public Object testField4859 = new Integer(4859);
+    public Object testField4860 = new Integer(4860);
+    public Object testField4861 = new Integer(4861);
+    public Object testField4862 = new Integer(4862);
+    public Object testField4863 = new Integer(4863);
+    public Object testField4864 = new Integer(4864);
+    public Object testField4865 = new Integer(4865);
+    public Object testField4866 = new Integer(4866);
+    public Object testField4867 = new Integer(4867);
+    public Object testField4868 = new Integer(4868);
+    public Object testField4869 = new Integer(4869);
+    public Object testField4870 = new Integer(4870);
+    public Object testField4871 = new Integer(4871);
+    public Object testField4872 = new Integer(4872);
+    public Object testField4873 = new Integer(4873);
+    public Object testField4874 = new Integer(4874);
+    public Object testField4875 = new Integer(4875);
+    public Object testField4876 = new Integer(4876);
+    public Object testField4877 = new Integer(4877);
+    public Object testField4878 = new Integer(4878);
+    public Object testField4879 = new Integer(4879);
+    public Object testField4880 = new Integer(4880);
+    public Object testField4881 = new Integer(4881);
+    public Object testField4882 = new Integer(4882);
+    public Object testField4883 = new Integer(4883);
+    public Object testField4884 = new Integer(4884);
+    public Object testField4885 = new Integer(4885);
+    public Object testField4886 = new Integer(4886);
+    public Object testField4887 = new Integer(4887);
+    public Object testField4888 = new Integer(4888);
+    public Object testField4889 = new Integer(4889);
+    public Object testField4890 = new Integer(4890);
+    public Object testField4891 = new Integer(4891);
+    public Object testField4892 = new Integer(4892);
+    public Object testField4893 = new Integer(4893);
+    public Object testField4894 = new Integer(4894);
+    public Object testField4895 = new Integer(4895);
+    public Object testField4896 = new Integer(4896);
+    public Object testField4897 = new Integer(4897);
+    public Object testField4898 = new Integer(4898);
+    public Object testField4899 = new Integer(4899);
+    public Object testField4900 = new Integer(4900);
+    public Object testField4901 = new Integer(4901);
+    public Object testField4902 = new Integer(4902);
+    public Object testField4903 = new Integer(4903);
+    public Object testField4904 = new Integer(4904);
+    public Object testField4905 = new Integer(4905);
+    public Object testField4906 = new Integer(4906);
+    public Object testField4907 = new Integer(4907);
+    public Object testField4908 = new Integer(4908);
+    public Object testField4909 = new Integer(4909);
+    public Object testField4910 = new Integer(4910);
+    public Object testField4911 = new Integer(4911);
+    public Object testField4912 = new Integer(4912);
+    public Object testField4913 = new Integer(4913);
+    public Object testField4914 = new Integer(4914);
+    public Object testField4915 = new Integer(4915);
+    public Object testField4916 = new Integer(4916);
+    public Object testField4917 = new Integer(4917);
+    public Object testField4918 = new Integer(4918);
+    public Object testField4919 = new Integer(4919);
+    public Object testField4920 = new Integer(4920);
+    public Object testField4921 = new Integer(4921);
+    public Object testField4922 = new Integer(4922);
+    public Object testField4923 = new Integer(4923);
+    public Object testField4924 = new Integer(4924);
+    public Object testField4925 = new Integer(4925);
+    public Object testField4926 = new Integer(4926);
+    public Object testField4927 = new Integer(4927);
+    public Object testField4928 = new Integer(4928);
+    public Object testField4929 = new Integer(4929);
+    public Object testField4930 = new Integer(4930);
+    public Object testField4931 = new Integer(4931);
+    public Object testField4932 = new Integer(4932);
+    public Object testField4933 = new Integer(4933);
+    public Object testField4934 = new Integer(4934);
+    public Object testField4935 = new Integer(4935);
+    public Object testField4936 = new Integer(4936);
+    public Object testField4937 = new Integer(4937);
+    public Object testField4938 = new Integer(4938);
+    public Object testField4939 = new Integer(4939);
+    public Object testField4940 = new Integer(4940);
+    public Object testField4941 = new Integer(4941);
+    public Object testField4942 = new Integer(4942);
+    public Object testField4943 = new Integer(4943);
+    public Object testField4944 = new Integer(4944);
+    public Object testField4945 = new Integer(4945);
+    public Object testField4946 = new Integer(4946);
+    public Object testField4947 = new Integer(4947);
+    public Object testField4948 = new Integer(4948);
+    public Object testField4949 = new Integer(4949);
+    public Object testField4950 = new Integer(4950);
+    public Object testField4951 = new Integer(4951);
+    public Object testField4952 = new Integer(4952);
+    public Object testField4953 = new Integer(4953);
+    public Object testField4954 = new Integer(4954);
+    public Object testField4955 = new Integer(4955);
+    public Object testField4956 = new Integer(4956);
+    public Object testField4957 = new Integer(4957);
+    public Object testField4958 = new Integer(4958);
+    public Object testField4959 = new Integer(4959);
+    public Object testField4960 = new Integer(4960);
+    public Object testField4961 = new Integer(4961);
+    public Object testField4962 = new Integer(4962);
+    public Object testField4963 = new Integer(4963);
+    public Object testField4964 = new Integer(4964);
+    public Object testField4965 = new Integer(4965);
+    public Object testField4966 = new Integer(4966);
+    public Object testField4967 = new Integer(4967);
+    public Object testField4968 = new Integer(4968);
+    public Object testField4969 = new Integer(4969);
+    public Object testField4970 = new Integer(4970);
+    public Object testField4971 = new Integer(4971);
+    public Object testField4972 = new Integer(4972);
+    public Object testField4973 = new Integer(4973);
+    public Object testField4974 = new Integer(4974);
+    public Object testField4975 = new Integer(4975);
+    public Object testField4976 = new Integer(4976);
+    public Object testField4977 = new Integer(4977);
+    public Object testField4978 = new Integer(4978);
+    public Object testField4979 = new Integer(4979);
+    public Object testField4980 = new Integer(4980);
+    public Object testField4981 = new Integer(4981);
+    public Object testField4982 = new Integer(4982);
+    public Object testField4983 = new Integer(4983);
+    public Object testField4984 = new Integer(4984);
+    public Object testField4985 = new Integer(4985);
+    public Object testField4986 = new Integer(4986);
+    public Object testField4987 = new Integer(4987);
+    public Object testField4988 = new Integer(4988);
+    public Object testField4989 = new Integer(4989);
+    public Object testField4990 = new Integer(4990);
+    public Object testField4991 = new Integer(4991);
+    public Object testField4992 = new Integer(4992);
+    public Object testField4993 = new Integer(4993);
+    public Object testField4994 = new Integer(4994);
+    public Object testField4995 = new Integer(4995);
+    public Object testField4996 = new Integer(4996);
+    public Object testField4997 = new Integer(4997);
+    public Object testField4998 = new Integer(4998);
+    public Object testField4999 = new Integer(4999);
+}
diff --git a/test/160-read-barrier-stress/src/ManyFieldsBase0.java b/test/160-read-barrier-stress/src/ManyFieldsBase0.java
new file mode 100644
index 0000000..1b6c2a6
--- /dev/null
+++ b/test/160-read-barrier-stress/src/ManyFieldsBase0.java
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+class ManyFieldsBase0 {
+    public Object testField0000 = new Integer(0);
+    public Object testField0001 = new Integer(1);
+    public Object testField0002 = new Integer(2);
+    public Object testField0003 = new Integer(3);
+    public Object testField0004 = new Integer(4);
+    public Object testField0005 = new Integer(5);
+    public Object testField0006 = new Integer(6);
+    public Object testField0007 = new Integer(7);
+    public Object testField0008 = new Integer(8);
+    public Object testField0009 = new Integer(9);
+    public Object testField0010 = new Integer(10);
+    public Object testField0011 = new Integer(11);
+    public Object testField0012 = new Integer(12);
+    public Object testField0013 = new Integer(13);
+    public Object testField0014 = new Integer(14);
+    public Object testField0015 = new Integer(15);
+    public Object testField0016 = new Integer(16);
+    public Object testField0017 = new Integer(17);
+    public Object testField0018 = new Integer(18);
+    public Object testField0019 = new Integer(19);
+    public Object testField0020 = new Integer(20);
+    public Object testField0021 = new Integer(21);
+    public Object testField0022 = new Integer(22);
+    public Object testField0023 = new Integer(23);
+    public Object testField0024 = new Integer(24);
+    public Object testField0025 = new Integer(25);
+    public Object testField0026 = new Integer(26);
+    public Object testField0027 = new Integer(27);
+    public Object testField0028 = new Integer(28);
+    public Object testField0029 = new Integer(29);
+    public Object testField0030 = new Integer(30);
+    public Object testField0031 = new Integer(31);
+    public Object testField0032 = new Integer(32);
+    public Object testField0033 = new Integer(33);
+    public Object testField0034 = new Integer(34);
+    public Object testField0035 = new Integer(35);
+    public Object testField0036 = new Integer(36);
+    public Object testField0037 = new Integer(37);
+    public Object testField0038 = new Integer(38);
+    public Object testField0039 = new Integer(39);
+    public Object testField0040 = new Integer(40);
+    public Object testField0041 = new Integer(41);
+    public Object testField0042 = new Integer(42);
+    public Object testField0043 = new Integer(43);
+    public Object testField0044 = new Integer(44);
+    public Object testField0045 = new Integer(45);
+    public Object testField0046 = new Integer(46);
+    public Object testField0047 = new Integer(47);
+    public Object testField0048 = new Integer(48);
+    public Object testField0049 = new Integer(49);
+    public Object testField0050 = new Integer(50);
+    public Object testField0051 = new Integer(51);
+    public Object testField0052 = new Integer(52);
+    public Object testField0053 = new Integer(53);
+    public Object testField0054 = new Integer(54);
+    public Object testField0055 = new Integer(55);
+    public Object testField0056 = new Integer(56);
+    public Object testField0057 = new Integer(57);
+    public Object testField0058 = new Integer(58);
+    public Object testField0059 = new Integer(59);
+    public Object testField0060 = new Integer(60);
+    public Object testField0061 = new Integer(61);
+    public Object testField0062 = new Integer(62);
+    public Object testField0063 = new Integer(63);
+    public Object testField0064 = new Integer(64);
+    public Object testField0065 = new Integer(65);
+    public Object testField0066 = new Integer(66);
+    public Object testField0067 = new Integer(67);
+    public Object testField0068 = new Integer(68);
+    public Object testField0069 = new Integer(69);
+    public Object testField0070 = new Integer(70);
+    public Object testField0071 = new Integer(71);
+    public Object testField0072 = new Integer(72);
+    public Object testField0073 = new Integer(73);
+    public Object testField0074 = new Integer(74);
+    public Object testField0075 = new Integer(75);
+    public Object testField0076 = new Integer(76);
+    public Object testField0077 = new Integer(77);
+    public Object testField0078 = new Integer(78);
+    public Object testField0079 = new Integer(79);
+    public Object testField0080 = new Integer(80);
+    public Object testField0081 = new Integer(81);
+    public Object testField0082 = new Integer(82);
+    public Object testField0083 = new Integer(83);
+    public Object testField0084 = new Integer(84);
+    public Object testField0085 = new Integer(85);
+    public Object testField0086 = new Integer(86);
+    public Object testField0087 = new Integer(87);
+    public Object testField0088 = new Integer(88);
+    public Object testField0089 = new Integer(89);
+    public Object testField0090 = new Integer(90);
+    public Object testField0091 = new Integer(91);
+    public Object testField0092 = new Integer(92);
+    public Object testField0093 = new Integer(93);
+    public Object testField0094 = new Integer(94);
+    public Object testField0095 = new Integer(95);
+    public Object testField0096 = new Integer(96);
+    public Object testField0097 = new Integer(97);
+    public Object testField0098 = new Integer(98);
+    public Object testField0099 = new Integer(99);
+    public Object testField0100 = new Integer(100);
+    public Object testField0101 = new Integer(101);
+    public Object testField0102 = new Integer(102);
+    public Object testField0103 = new Integer(103);
+    public Object testField0104 = new Integer(104);
+    public Object testField0105 = new Integer(105);
+    public Object testField0106 = new Integer(106);
+    public Object testField0107 = new Integer(107);
+    public Object testField0108 = new Integer(108);
+    public Object testField0109 = new Integer(109);
+    public Object testField0110 = new Integer(110);
+    public Object testField0111 = new Integer(111);
+    public Object testField0112 = new Integer(112);
+    public Object testField0113 = new Integer(113);
+    public Object testField0114 = new Integer(114);
+    public Object testField0115 = new Integer(115);
+    public Object testField0116 = new Integer(116);
+    public Object testField0117 = new Integer(117);
+    public Object testField0118 = new Integer(118);
+    public Object testField0119 = new Integer(119);
+    public Object testField0120 = new Integer(120);
+    public Object testField0121 = new Integer(121);
+    public Object testField0122 = new Integer(122);
+    public Object testField0123 = new Integer(123);
+    public Object testField0124 = new Integer(124);
+    public Object testField0125 = new Integer(125);
+    public Object testField0126 = new Integer(126);
+    public Object testField0127 = new Integer(127);
+    public Object testField0128 = new Integer(128);
+    public Object testField0129 = new Integer(129);
+    public Object testField0130 = new Integer(130);
+    public Object testField0131 = new Integer(131);
+    public Object testField0132 = new Integer(132);
+    public Object testField0133 = new Integer(133);
+    public Object testField0134 = new Integer(134);
+    public Object testField0135 = new Integer(135);
+    public Object testField0136 = new Integer(136);
+    public Object testField0137 = new Integer(137);
+    public Object testField0138 = new Integer(138);
+    public Object testField0139 = new Integer(139);
+    public Object testField0140 = new Integer(140);
+    public Object testField0141 = new Integer(141);
+    public Object testField0142 = new Integer(142);
+    public Object testField0143 = new Integer(143);
+    public Object testField0144 = new Integer(144);
+    public Object testField0145 = new Integer(145);
+    public Object testField0146 = new Integer(146);
+    public Object testField0147 = new Integer(147);
+    public Object testField0148 = new Integer(148);
+    public Object testField0149 = new Integer(149);
+    public Object testField0150 = new Integer(150);
+    public Object testField0151 = new Integer(151);
+    public Object testField0152 = new Integer(152);
+    public Object testField0153 = new Integer(153);
+    public Object testField0154 = new Integer(154);
+    public Object testField0155 = new Integer(155);
+    public Object testField0156 = new Integer(156);
+    public Object testField0157 = new Integer(157);
+    public Object testField0158 = new Integer(158);
+    public Object testField0159 = new Integer(159);
+    public Object testField0160 = new Integer(160);
+    public Object testField0161 = new Integer(161);
+    public Object testField0162 = new Integer(162);
+    public Object testField0163 = new Integer(163);
+    public Object testField0164 = new Integer(164);
+    public Object testField0165 = new Integer(165);
+    public Object testField0166 = new Integer(166);
+    public Object testField0167 = new Integer(167);
+    public Object testField0168 = new Integer(168);
+    public Object testField0169 = new Integer(169);
+    public Object testField0170 = new Integer(170);
+    public Object testField0171 = new Integer(171);
+    public Object testField0172 = new Integer(172);
+    public Object testField0173 = new Integer(173);
+    public Object testField0174 = new Integer(174);
+    public Object testField0175 = new Integer(175);
+    public Object testField0176 = new Integer(176);
+    public Object testField0177 = new Integer(177);
+    public Object testField0178 = new Integer(178);
+    public Object testField0179 = new Integer(179);
+    public Object testField0180 = new Integer(180);
+    public Object testField0181 = new Integer(181);
+    public Object testField0182 = new Integer(182);
+    public Object testField0183 = new Integer(183);
+    public Object testField0184 = new Integer(184);
+    public Object testField0185 = new Integer(185);
+    public Object testField0186 = new Integer(186);
+    public Object testField0187 = new Integer(187);
+    public Object testField0188 = new Integer(188);
+    public Object testField0189 = new Integer(189);
+    public Object testField0190 = new Integer(190);
+    public Object testField0191 = new Integer(191);
+    public Object testField0192 = new Integer(192);
+    public Object testField0193 = new Integer(193);
+    public Object testField0194 = new Integer(194);
+    public Object testField0195 = new Integer(195);
+    public Object testField0196 = new Integer(196);
+    public Object testField0197 = new Integer(197);
+    public Object testField0198 = new Integer(198);
+    public Object testField0199 = new Integer(199);
+    public Object testField0200 = new Integer(200);
+    public Object testField0201 = new Integer(201);
+    public Object testField0202 = new Integer(202);
+    public Object testField0203 = new Integer(203);
+    public Object testField0204 = new Integer(204);
+    public Object testField0205 = new Integer(205);
+    public Object testField0206 = new Integer(206);
+    public Object testField0207 = new Integer(207);
+    public Object testField0208 = new Integer(208);
+    public Object testField0209 = new Integer(209);
+    public Object testField0210 = new Integer(210);
+    public Object testField0211 = new Integer(211);
+    public Object testField0212 = new Integer(212);
+    public Object testField0213 = new Integer(213);
+    public Object testField0214 = new Integer(214);
+    public Object testField0215 = new Integer(215);
+    public Object testField0216 = new Integer(216);
+    public Object testField0217 = new Integer(217);
+    public Object testField0218 = new Integer(218);
+    public Object testField0219 = new Integer(219);
+    public Object testField0220 = new Integer(220);
+    public Object testField0221 = new Integer(221);
+    public Object testField0222 = new Integer(222);
+    public Object testField0223 = new Integer(223);
+    public Object testField0224 = new Integer(224);
+    public Object testField0225 = new Integer(225);
+    public Object testField0226 = new Integer(226);
+    public Object testField0227 = new Integer(227);
+    public Object testField0228 = new Integer(228);
+    public Object testField0229 = new Integer(229);
+    public Object testField0230 = new Integer(230);
+    public Object testField0231 = new Integer(231);
+    public Object testField0232 = new Integer(232);
+    public Object testField0233 = new Integer(233);
+    public Object testField0234 = new Integer(234);
+    public Object testField0235 = new Integer(235);
+    public Object testField0236 = new Integer(236);
+    public Object testField0237 = new Integer(237);
+    public Object testField0238 = new Integer(238);
+    public Object testField0239 = new Integer(239);
+    public Object testField0240 = new Integer(240);
+    public Object testField0241 = new Integer(241);
+    public Object testField0242 = new Integer(242);
+    public Object testField0243 = new Integer(243);
+    public Object testField0244 = new Integer(244);
+    public Object testField0245 = new Integer(245);
+    public Object testField0246 = new Integer(246);
+    public Object testField0247 = new Integer(247);
+    public Object testField0248 = new Integer(248);
+    public Object testField0249 = new Integer(249);
+    public Object testField0250 = new Integer(250);
+    public Object testField0251 = new Integer(251);
+    public Object testField0252 = new Integer(252);
+    public Object testField0253 = new Integer(253);
+    public Object testField0254 = new Integer(254);
+    public Object testField0255 = new Integer(255);
+    public Object testField0256 = new Integer(256);
+    public Object testField0257 = new Integer(257);
+    public Object testField0258 = new Integer(258);
+    public Object testField0259 = new Integer(259);
+    public Object testField0260 = new Integer(260);
+    public Object testField0261 = new Integer(261);
+    public Object testField0262 = new Integer(262);
+    public Object testField0263 = new Integer(263);
+    public Object testField0264 = new Integer(264);
+    public Object testField0265 = new Integer(265);
+    public Object testField0266 = new Integer(266);
+    public Object testField0267 = new Integer(267);
+    public Object testField0268 = new Integer(268);
+    public Object testField0269 = new Integer(269);
+    public Object testField0270 = new Integer(270);
+    public Object testField0271 = new Integer(271);
+    public Object testField0272 = new Integer(272);
+    public Object testField0273 = new Integer(273);
+    public Object testField0274 = new Integer(274);
+    public Object testField0275 = new Integer(275);
+    public Object testField0276 = new Integer(276);
+    public Object testField0277 = new Integer(277);
+    public Object testField0278 = new Integer(278);
+    public Object testField0279 = new Integer(279);
+    public Object testField0280 = new Integer(280);
+    public Object testField0281 = new Integer(281);
+    public Object testField0282 = new Integer(282);
+    public Object testField0283 = new Integer(283);
+    public Object testField0284 = new Integer(284);
+    public Object testField0285 = new Integer(285);
+    public Object testField0286 = new Integer(286);
+    public Object testField0287 = new Integer(287);
+    public Object testField0288 = new Integer(288);
+    public Object testField0289 = new Integer(289);
+    public Object testField0290 = new Integer(290);
+    public Object testField0291 = new Integer(291);
+    public Object testField0292 = new Integer(292);
+    public Object testField0293 = new Integer(293);
+    public Object testField0294 = new Integer(294);
+    public Object testField0295 = new Integer(295);
+    public Object testField0296 = new Integer(296);
+    public Object testField0297 = new Integer(297);
+    public Object testField0298 = new Integer(298);
+    public Object testField0299 = new Integer(299);
+    public Object testField0300 = new Integer(300);
+    public Object testField0301 = new Integer(301);
+    public Object testField0302 = new Integer(302);
+    public Object testField0303 = new Integer(303);
+    public Object testField0304 = new Integer(304);
+    public Object testField0305 = new Integer(305);
+    public Object testField0306 = new Integer(306);
+    public Object testField0307 = new Integer(307);
+    public Object testField0308 = new Integer(308);
+    public Object testField0309 = new Integer(309);
+    public Object testField0310 = new Integer(310);
+    public Object testField0311 = new Integer(311);
+    public Object testField0312 = new Integer(312);
+    public Object testField0313 = new Integer(313);
+    public Object testField0314 = new Integer(314);
+    public Object testField0315 = new Integer(315);
+    public Object testField0316 = new Integer(316);
+    public Object testField0317 = new Integer(317);
+    public Object testField0318 = new Integer(318);
+    public Object testField0319 = new Integer(319);
+    public Object testField0320 = new Integer(320);
+    public Object testField0321 = new Integer(321);
+    public Object testField0322 = new Integer(322);
+    public Object testField0323 = new Integer(323);
+    public Object testField0324 = new Integer(324);
+    public Object testField0325 = new Integer(325);
+    public Object testField0326 = new Integer(326);
+    public Object testField0327 = new Integer(327);
+    public Object testField0328 = new Integer(328);
+    public Object testField0329 = new Integer(329);
+    public Object testField0330 = new Integer(330);
+    public Object testField0331 = new Integer(331);
+    public Object testField0332 = new Integer(332);
+    public Object testField0333 = new Integer(333);
+    public Object testField0334 = new Integer(334);
+    public Object testField0335 = new Integer(335);
+    public Object testField0336 = new Integer(336);
+    public Object testField0337 = new Integer(337);
+    public Object testField0338 = new Integer(338);
+    public Object testField0339 = new Integer(339);
+    public Object testField0340 = new Integer(340);
+    public Object testField0341 = new Integer(341);
+    public Object testField0342 = new Integer(342);
+    public Object testField0343 = new Integer(343);
+    public Object testField0344 = new Integer(344);
+    public Object testField0345 = new Integer(345);
+    public Object testField0346 = new Integer(346);
+    public Object testField0347 = new Integer(347);
+    public Object testField0348 = new Integer(348);
+    public Object testField0349 = new Integer(349);
+    public Object testField0350 = new Integer(350);
+    public Object testField0351 = new Integer(351);
+    public Object testField0352 = new Integer(352);
+    public Object testField0353 = new Integer(353);
+    public Object testField0354 = new Integer(354);
+    public Object testField0355 = new Integer(355);
+    public Object testField0356 = new Integer(356);
+    public Object testField0357 = new Integer(357);
+    public Object testField0358 = new Integer(358);
+    public Object testField0359 = new Integer(359);
+    public Object testField0360 = new Integer(360);
+    public Object testField0361 = new Integer(361);
+    public Object testField0362 = new Integer(362);
+    public Object testField0363 = new Integer(363);
+    public Object testField0364 = new Integer(364);
+    public Object testField0365 = new Integer(365);
+    public Object testField0366 = new Integer(366);
+    public Object testField0367 = new Integer(367);
+    public Object testField0368 = new Integer(368);
+    public Object testField0369 = new Integer(369);
+    public Object testField0370 = new Integer(370);
+    public Object testField0371 = new Integer(371);
+    public Object testField0372 = new Integer(372);
+    public Object testField0373 = new Integer(373);
+    public Object testField0374 = new Integer(374);
+    public Object testField0375 = new Integer(375);
+    public Object testField0376 = new Integer(376);
+    public Object testField0377 = new Integer(377);
+    public Object testField0378 = new Integer(378);
+    public Object testField0379 = new Integer(379);
+    public Object testField0380 = new Integer(380);
+    public Object testField0381 = new Integer(381);
+    public Object testField0382 = new Integer(382);
+    public Object testField0383 = new Integer(383);
+    public Object testField0384 = new Integer(384);
+    public Object testField0385 = new Integer(385);
+    public Object testField0386 = new Integer(386);
+    public Object testField0387 = new Integer(387);
+    public Object testField0388 = new Integer(388);
+    public Object testField0389 = new Integer(389);
+    public Object testField0390 = new Integer(390);
+    public Object testField0391 = new Integer(391);
+    public Object testField0392 = new Integer(392);
+    public Object testField0393 = new Integer(393);
+    public Object testField0394 = new Integer(394);
+    public Object testField0395 = new Integer(395);
+    public Object testField0396 = new Integer(396);
+    public Object testField0397 = new Integer(397);
+    public Object testField0398 = new Integer(398);
+    public Object testField0399 = new Integer(399);
+    public Object testField0400 = new Integer(400);
+    public Object testField0401 = new Integer(401);
+    public Object testField0402 = new Integer(402);
+    public Object testField0403 = new Integer(403);
+    public Object testField0404 = new Integer(404);
+    public Object testField0405 = new Integer(405);
+    public Object testField0406 = new Integer(406);
+    public Object testField0407 = new Integer(407);
+    public Object testField0408 = new Integer(408);
+    public Object testField0409 = new Integer(409);
+    public Object testField0410 = new Integer(410);
+    public Object testField0411 = new Integer(411);
+    public Object testField0412 = new Integer(412);
+    public Object testField0413 = new Integer(413);
+    public Object testField0414 = new Integer(414);
+    public Object testField0415 = new Integer(415);
+    public Object testField0416 = new Integer(416);
+    public Object testField0417 = new Integer(417);
+    public Object testField0418 = new Integer(418);
+    public Object testField0419 = new Integer(419);
+    public Object testField0420 = new Integer(420);
+    public Object testField0421 = new Integer(421);
+    public Object testField0422 = new Integer(422);
+    public Object testField0423 = new Integer(423);
+    public Object testField0424 = new Integer(424);
+    public Object testField0425 = new Integer(425);
+    public Object testField0426 = new Integer(426);
+    public Object testField0427 = new Integer(427);
+    public Object testField0428 = new Integer(428);
+    public Object testField0429 = new Integer(429);
+    public Object testField0430 = new Integer(430);
+    public Object testField0431 = new Integer(431);
+    public Object testField0432 = new Integer(432);
+    public Object testField0433 = new Integer(433);
+    public Object testField0434 = new Integer(434);
+    public Object testField0435 = new Integer(435);
+    public Object testField0436 = new Integer(436);
+    public Object testField0437 = new Integer(437);
+    public Object testField0438 = new Integer(438);
+    public Object testField0439 = new Integer(439);
+    public Object testField0440 = new Integer(440);
+    public Object testField0441 = new Integer(441);
+    public Object testField0442 = new Integer(442);
+    public Object testField0443 = new Integer(443);
+    public Object testField0444 = new Integer(444);
+    public Object testField0445 = new Integer(445);
+    public Object testField0446 = new Integer(446);
+    public Object testField0447 = new Integer(447);
+    public Object testField0448 = new Integer(448);
+    public Object testField0449 = new Integer(449);
+    public Object testField0450 = new Integer(450);
+    public Object testField0451 = new Integer(451);
+    public Object testField0452 = new Integer(452);
+    public Object testField0453 = new Integer(453);
+    public Object testField0454 = new Integer(454);
+    public Object testField0455 = new Integer(455);
+    public Object testField0456 = new Integer(456);
+    public Object testField0457 = new Integer(457);
+    public Object testField0458 = new Integer(458);
+    public Object testField0459 = new Integer(459);
+    public Object testField0460 = new Integer(460);
+    public Object testField0461 = new Integer(461);
+    public Object testField0462 = new Integer(462);
+    public Object testField0463 = new Integer(463);
+    public Object testField0464 = new Integer(464);
+    public Object testField0465 = new Integer(465);
+    public Object testField0466 = new Integer(466);
+    public Object testField0467 = new Integer(467);
+    public Object testField0468 = new Integer(468);
+    public Object testField0469 = new Integer(469);
+    public Object testField0470 = new Integer(470);
+    public Object testField0471 = new Integer(471);
+    public Object testField0472 = new Integer(472);
+    public Object testField0473 = new Integer(473);
+    public Object testField0474 = new Integer(474);
+    public Object testField0475 = new Integer(475);
+    public Object testField0476 = new Integer(476);
+    public Object testField0477 = new Integer(477);
+    public Object testField0478 = new Integer(478);
+    public Object testField0479 = new Integer(479);
+    public Object testField0480 = new Integer(480);
+    public Object testField0481 = new Integer(481);
+    public Object testField0482 = new Integer(482);
+    public Object testField0483 = new Integer(483);
+    public Object testField0484 = new Integer(484);
+    public Object testField0485 = new Integer(485);
+    public Object testField0486 = new Integer(486);
+    public Object testField0487 = new Integer(487);
+    public Object testField0488 = new Integer(488);
+    public Object testField0489 = new Integer(489);
+    public Object testField0490 = new Integer(490);
+    public Object testField0491 = new Integer(491);
+    public Object testField0492 = new Integer(492);
+    public Object testField0493 = new Integer(493);
+    public Object testField0494 = new Integer(494);
+    public Object testField0495 = new Integer(495);
+    public Object testField0496 = new Integer(496);
+    public Object testField0497 = new Integer(497);
+    public Object testField0498 = new Integer(498);
+    public Object testField0499 = new Integer(499);
+    public Object testField0500 = new Integer(500);
+    public Object testField0501 = new Integer(501);
+    public Object testField0502 = new Integer(502);
+    public Object testField0503 = new Integer(503);
+    public Object testField0504 = new Integer(504);
+    public Object testField0505 = new Integer(505);
+    public Object testField0506 = new Integer(506);
+    public Object testField0507 = new Integer(507);
+    public Object testField0508 = new Integer(508);
+    public Object testField0509 = new Integer(509);
+    public Object testField0510 = new Integer(510);
+    public Object testField0511 = new Integer(511);
+    public Object testField0512 = new Integer(512);
+    public Object testField0513 = new Integer(513);
+    public Object testField0514 = new Integer(514);
+    public Object testField0515 = new Integer(515);
+    public Object testField0516 = new Integer(516);
+    public Object testField0517 = new Integer(517);
+    public Object testField0518 = new Integer(518);
+    public Object testField0519 = new Integer(519);
+    public Object testField0520 = new Integer(520);
+    public Object testField0521 = new Integer(521);
+    public Object testField0522 = new Integer(522);
+    public Object testField0523 = new Integer(523);
+    public Object testField0524 = new Integer(524);
+    public Object testField0525 = new Integer(525);
+    public Object testField0526 = new Integer(526);
+    public Object testField0527 = new Integer(527);
+    public Object testField0528 = new Integer(528);
+    public Object testField0529 = new Integer(529);
+    public Object testField0530 = new Integer(530);
+    public Object testField0531 = new Integer(531);
+    public Object testField0532 = new Integer(532);
+    public Object testField0533 = new Integer(533);
+    public Object testField0534 = new Integer(534);
+    public Object testField0535 = new Integer(535);
+    public Object testField0536 = new Integer(536);
+    public Object testField0537 = new Integer(537);
+    public Object testField0538 = new Integer(538);
+    public Object testField0539 = new Integer(539);
+    public Object testField0540 = new Integer(540);
+    public Object testField0541 = new Integer(541);
+    public Object testField0542 = new Integer(542);
+    public Object testField0543 = new Integer(543);
+    public Object testField0544 = new Integer(544);
+    public Object testField0545 = new Integer(545);
+    public Object testField0546 = new Integer(546);
+    public Object testField0547 = new Integer(547);
+    public Object testField0548 = new Integer(548);
+    public Object testField0549 = new Integer(549);
+    public Object testField0550 = new Integer(550);
+    public Object testField0551 = new Integer(551);
+    public Object testField0552 = new Integer(552);
+    public Object testField0553 = new Integer(553);
+    public Object testField0554 = new Integer(554);
+    public Object testField0555 = new Integer(555);
+    public Object testField0556 = new Integer(556);
+    public Object testField0557 = new Integer(557);
+    public Object testField0558 = new Integer(558);
+    public Object testField0559 = new Integer(559);
+    public Object testField0560 = new Integer(560);
+    public Object testField0561 = new Integer(561);
+    public Object testField0562 = new Integer(562);
+    public Object testField0563 = new Integer(563);
+    public Object testField0564 = new Integer(564);
+    public Object testField0565 = new Integer(565);
+    public Object testField0566 = new Integer(566);
+    public Object testField0567 = new Integer(567);
+    public Object testField0568 = new Integer(568);
+    public Object testField0569 = new Integer(569);
+    public Object testField0570 = new Integer(570);
+    public Object testField0571 = new Integer(571);
+    public Object testField0572 = new Integer(572);
+    public Object testField0573 = new Integer(573);
+    public Object testField0574 = new Integer(574);
+    public Object testField0575 = new Integer(575);
+    public Object testField0576 = new Integer(576);
+    public Object testField0577 = new Integer(577);
+    public Object testField0578 = new Integer(578);
+    public Object testField0579 = new Integer(579);
+    public Object testField0580 = new Integer(580);
+    public Object testField0581 = new Integer(581);
+    public Object testField0582 = new Integer(582);
+    public Object testField0583 = new Integer(583);
+    public Object testField0584 = new Integer(584);
+    public Object testField0585 = new Integer(585);
+    public Object testField0586 = new Integer(586);
+    public Object testField0587 = new Integer(587);
+    public Object testField0588 = new Integer(588);
+    public Object testField0589 = new Integer(589);
+    public Object testField0590 = new Integer(590);
+    public Object testField0591 = new Integer(591);
+    public Object testField0592 = new Integer(592);
+    public Object testField0593 = new Integer(593);
+    public Object testField0594 = new Integer(594);
+    public Object testField0595 = new Integer(595);
+    public Object testField0596 = new Integer(596);
+    public Object testField0597 = new Integer(597);
+    public Object testField0598 = new Integer(598);
+    public Object testField0599 = new Integer(599);
+    public Object testField0600 = new Integer(600);
+    public Object testField0601 = new Integer(601);
+    public Object testField0602 = new Integer(602);
+    public Object testField0603 = new Integer(603);
+    public Object testField0604 = new Integer(604);
+    public Object testField0605 = new Integer(605);
+    public Object testField0606 = new Integer(606);
+    public Object testField0607 = new Integer(607);
+    public Object testField0608 = new Integer(608);
+    public Object testField0609 = new Integer(609);
+    public Object testField0610 = new Integer(610);
+    public Object testField0611 = new Integer(611);
+    public Object testField0612 = new Integer(612);
+    public Object testField0613 = new Integer(613);
+    public Object testField0614 = new Integer(614);
+    public Object testField0615 = new Integer(615);
+    public Object testField0616 = new Integer(616);
+    public Object testField0617 = new Integer(617);
+    public Object testField0618 = new Integer(618);
+    public Object testField0619 = new Integer(619);
+    public Object testField0620 = new Integer(620);
+    public Object testField0621 = new Integer(621);
+    public Object testField0622 = new Integer(622);
+    public Object testField0623 = new Integer(623);
+    public Object testField0624 = new Integer(624);
+    public Object testField0625 = new Integer(625);
+    public Object testField0626 = new Integer(626);
+    public Object testField0627 = new Integer(627);
+    public Object testField0628 = new Integer(628);
+    public Object testField0629 = new Integer(629);
+    public Object testField0630 = new Integer(630);
+    public Object testField0631 = new Integer(631);
+    public Object testField0632 = new Integer(632);
+    public Object testField0633 = new Integer(633);
+    public Object testField0634 = new Integer(634);
+    public Object testField0635 = new Integer(635);
+    public Object testField0636 = new Integer(636);
+    public Object testField0637 = new Integer(637);
+    public Object testField0638 = new Integer(638);
+    public Object testField0639 = new Integer(639);
+    public Object testField0640 = new Integer(640);
+    public Object testField0641 = new Integer(641);
+    public Object testField0642 = new Integer(642);
+    public Object testField0643 = new Integer(643);
+    public Object testField0644 = new Integer(644);
+    public Object testField0645 = new Integer(645);
+    public Object testField0646 = new Integer(646);
+    public Object testField0647 = new Integer(647);
+    public Object testField0648 = new Integer(648);
+    public Object testField0649 = new Integer(649);
+    public Object testField0650 = new Integer(650);
+    public Object testField0651 = new Integer(651);
+    public Object testField0652 = new Integer(652);
+    public Object testField0653 = new Integer(653);
+    public Object testField0654 = new Integer(654);
+    public Object testField0655 = new Integer(655);
+    public Object testField0656 = new Integer(656);
+    public Object testField0657 = new Integer(657);
+    public Object testField0658 = new Integer(658);
+    public Object testField0659 = new Integer(659);
+    public Object testField0660 = new Integer(660);
+    public Object testField0661 = new Integer(661);
+    public Object testField0662 = new Integer(662);
+    public Object testField0663 = new Integer(663);
+    public Object testField0664 = new Integer(664);
+    public Object testField0665 = new Integer(665);
+    public Object testField0666 = new Integer(666);
+    public Object testField0667 = new Integer(667);
+    public Object testField0668 = new Integer(668);
+    public Object testField0669 = new Integer(669);
+    public Object testField0670 = new Integer(670);
+    public Object testField0671 = new Integer(671);
+    public Object testField0672 = new Integer(672);
+    public Object testField0673 = new Integer(673);
+    public Object testField0674 = new Integer(674);
+    public Object testField0675 = new Integer(675);
+    public Object testField0676 = new Integer(676);
+    public Object testField0677 = new Integer(677);
+    public Object testField0678 = new Integer(678);
+    public Object testField0679 = new Integer(679);
+    public Object testField0680 = new Integer(680);
+    public Object testField0681 = new Integer(681);
+    public Object testField0682 = new Integer(682);
+    public Object testField0683 = new Integer(683);
+    public Object testField0684 = new Integer(684);
+    public Object testField0685 = new Integer(685);
+    public Object testField0686 = new Integer(686);
+    public Object testField0687 = new Integer(687);
+    public Object testField0688 = new Integer(688);
+    public Object testField0689 = new Integer(689);
+    public Object testField0690 = new Integer(690);
+    public Object testField0691 = new Integer(691);
+    public Object testField0692 = new Integer(692);
+    public Object testField0693 = new Integer(693);
+    public Object testField0694 = new Integer(694);
+    public Object testField0695 = new Integer(695);
+    public Object testField0696 = new Integer(696);
+    public Object testField0697 = new Integer(697);
+    public Object testField0698 = new Integer(698);
+    public Object testField0699 = new Integer(699);
+    public Object testField0700 = new Integer(700);
+    public Object testField0701 = new Integer(701);
+    public Object testField0702 = new Integer(702);
+    public Object testField0703 = new Integer(703);
+    public Object testField0704 = new Integer(704);
+    public Object testField0705 = new Integer(705);
+    public Object testField0706 = new Integer(706);
+    public Object testField0707 = new Integer(707);
+    public Object testField0708 = new Integer(708);
+    public Object testField0709 = new Integer(709);
+    public Object testField0710 = new Integer(710);
+    public Object testField0711 = new Integer(711);
+    public Object testField0712 = new Integer(712);
+    public Object testField0713 = new Integer(713);
+    public Object testField0714 = new Integer(714);
+    public Object testField0715 = new Integer(715);
+    public Object testField0716 = new Integer(716);
+    public Object testField0717 = new Integer(717);
+    public Object testField0718 = new Integer(718);
+    public Object testField0719 = new Integer(719);
+    public Object testField0720 = new Integer(720);
+    public Object testField0721 = new Integer(721);
+    public Object testField0722 = new Integer(722);
+    public Object testField0723 = new Integer(723);
+    public Object testField0724 = new Integer(724);
+    public Object testField0725 = new Integer(725);
+    public Object testField0726 = new Integer(726);
+    public Object testField0727 = new Integer(727);
+    public Object testField0728 = new Integer(728);
+    public Object testField0729 = new Integer(729);
+    public Object testField0730 = new Integer(730);
+    public Object testField0731 = new Integer(731);
+    public Object testField0732 = new Integer(732);
+    public Object testField0733 = new Integer(733);
+    public Object testField0734 = new Integer(734);
+    public Object testField0735 = new Integer(735);
+    public Object testField0736 = new Integer(736);
+    public Object testField0737 = new Integer(737);
+    public Object testField0738 = new Integer(738);
+    public Object testField0739 = new Integer(739);
+    public Object testField0740 = new Integer(740);
+    public Object testField0741 = new Integer(741);
+    public Object testField0742 = new Integer(742);
+    public Object testField0743 = new Integer(743);
+    public Object testField0744 = new Integer(744);
+    public Object testField0745 = new Integer(745);
+    public Object testField0746 = new Integer(746);
+    public Object testField0747 = new Integer(747);
+    public Object testField0748 = new Integer(748);
+    public Object testField0749 = new Integer(749);
+    public Object testField0750 = new Integer(750);
+    public Object testField0751 = new Integer(751);
+    public Object testField0752 = new Integer(752);
+    public Object testField0753 = new Integer(753);
+    public Object testField0754 = new Integer(754);
+    public Object testField0755 = new Integer(755);
+    public Object testField0756 = new Integer(756);
+    public Object testField0757 = new Integer(757);
+    public Object testField0758 = new Integer(758);
+    public Object testField0759 = new Integer(759);
+    public Object testField0760 = new Integer(760);
+    public Object testField0761 = new Integer(761);
+    public Object testField0762 = new Integer(762);
+    public Object testField0763 = new Integer(763);
+    public Object testField0764 = new Integer(764);
+    public Object testField0765 = new Integer(765);
+    public Object testField0766 = new Integer(766);
+    public Object testField0767 = new Integer(767);
+    public Object testField0768 = new Integer(768);
+    public Object testField0769 = new Integer(769);
+    public Object testField0770 = new Integer(770);
+    public Object testField0771 = new Integer(771);
+    public Object testField0772 = new Integer(772);
+    public Object testField0773 = new Integer(773);
+    public Object testField0774 = new Integer(774);
+    public Object testField0775 = new Integer(775);
+    public Object testField0776 = new Integer(776);
+    public Object testField0777 = new Integer(777);
+    public Object testField0778 = new Integer(778);
+    public Object testField0779 = new Integer(779);
+    public Object testField0780 = new Integer(780);
+    public Object testField0781 = new Integer(781);
+    public Object testField0782 = new Integer(782);
+    public Object testField0783 = new Integer(783);
+    public Object testField0784 = new Integer(784);
+    public Object testField0785 = new Integer(785);
+    public Object testField0786 = new Integer(786);
+    public Object testField0787 = new Integer(787);
+    public Object testField0788 = new Integer(788);
+    public Object testField0789 = new Integer(789);
+    public Object testField0790 = new Integer(790);
+    public Object testField0791 = new Integer(791);
+    public Object testField0792 = new Integer(792);
+    public Object testField0793 = new Integer(793);
+    public Object testField0794 = new Integer(794);
+    public Object testField0795 = new Integer(795);
+    public Object testField0796 = new Integer(796);
+    public Object testField0797 = new Integer(797);
+    public Object testField0798 = new Integer(798);
+    public Object testField0799 = new Integer(799);
+    public Object testField0800 = new Integer(800);
+    public Object testField0801 = new Integer(801);
+    public Object testField0802 = new Integer(802);
+    public Object testField0803 = new Integer(803);
+    public Object testField0804 = new Integer(804);
+    public Object testField0805 = new Integer(805);
+    public Object testField0806 = new Integer(806);
+    public Object testField0807 = new Integer(807);
+    public Object testField0808 = new Integer(808);
+    public Object testField0809 = new Integer(809);
+    public Object testField0810 = new Integer(810);
+    public Object testField0811 = new Integer(811);
+    public Object testField0812 = new Integer(812);
+    public Object testField0813 = new Integer(813);
+    public Object testField0814 = new Integer(814);
+    public Object testField0815 = new Integer(815);
+    public Object testField0816 = new Integer(816);
+    public Object testField0817 = new Integer(817);
+    public Object testField0818 = new Integer(818);
+    public Object testField0819 = new Integer(819);
+    public Object testField0820 = new Integer(820);
+    public Object testField0821 = new Integer(821);
+    public Object testField0822 = new Integer(822);
+    public Object testField0823 = new Integer(823);
+    public Object testField0824 = new Integer(824);
+    public Object testField0825 = new Integer(825);
+    public Object testField0826 = new Integer(826);
+    public Object testField0827 = new Integer(827);
+    public Object testField0828 = new Integer(828);
+    public Object testField0829 = new Integer(829);
+    public Object testField0830 = new Integer(830);
+    public Object testField0831 = new Integer(831);
+    public Object testField0832 = new Integer(832);
+    public Object testField0833 = new Integer(833);
+    public Object testField0834 = new Integer(834);
+    public Object testField0835 = new Integer(835);
+    public Object testField0836 = new Integer(836);
+    public Object testField0837 = new Integer(837);
+    public Object testField0838 = new Integer(838);
+    public Object testField0839 = new Integer(839);
+    public Object testField0840 = new Integer(840);
+    public Object testField0841 = new Integer(841);
+    public Object testField0842 = new Integer(842);
+    public Object testField0843 = new Integer(843);
+    public Object testField0844 = new Integer(844);
+    public Object testField0845 = new Integer(845);
+    public Object testField0846 = new Integer(846);
+    public Object testField0847 = new Integer(847);
+    public Object testField0848 = new Integer(848);
+    public Object testField0849 = new Integer(849);
+    public Object testField0850 = new Integer(850);
+    public Object testField0851 = new Integer(851);
+    public Object testField0852 = new Integer(852);
+    public Object testField0853 = new Integer(853);
+    public Object testField0854 = new Integer(854);
+    public Object testField0855 = new Integer(855);
+    public Object testField0856 = new Integer(856);
+    public Object testField0857 = new Integer(857);
+    public Object testField0858 = new Integer(858);
+    public Object testField0859 = new Integer(859);
+    public Object testField0860 = new Integer(860);
+    public Object testField0861 = new Integer(861);
+    public Object testField0862 = new Integer(862);
+    public Object testField0863 = new Integer(863);
+    public Object testField0864 = new Integer(864);
+    public Object testField0865 = new Integer(865);
+    public Object testField0866 = new Integer(866);
+    public Object testField0867 = new Integer(867);
+    public Object testField0868 = new Integer(868);
+    public Object testField0869 = new Integer(869);
+    public Object testField0870 = new Integer(870);
+    public Object testField0871 = new Integer(871);
+    public Object testField0872 = new Integer(872);
+    public Object testField0873 = new Integer(873);
+    public Object testField0874 = new Integer(874);
+    public Object testField0875 = new Integer(875);
+    public Object testField0876 = new Integer(876);
+    public Object testField0877 = new Integer(877);
+    public Object testField0878 = new Integer(878);
+    public Object testField0879 = new Integer(879);
+    public Object testField0880 = new Integer(880);
+    public Object testField0881 = new Integer(881);
+    public Object testField0882 = new Integer(882);
+    public Object testField0883 = new Integer(883);
+    public Object testField0884 = new Integer(884);
+    public Object testField0885 = new Integer(885);
+    public Object testField0886 = new Integer(886);
+    public Object testField0887 = new Integer(887);
+    public Object testField0888 = new Integer(888);
+    public Object testField0889 = new Integer(889);
+    public Object testField0890 = new Integer(890);
+    public Object testField0891 = new Integer(891);
+    public Object testField0892 = new Integer(892);
+    public Object testField0893 = new Integer(893);
+    public Object testField0894 = new Integer(894);
+    public Object testField0895 = new Integer(895);
+    public Object testField0896 = new Integer(896);
+    public Object testField0897 = new Integer(897);
+    public Object testField0898 = new Integer(898);
+    public Object testField0899 = new Integer(899);
+    public Object testField0900 = new Integer(900);
+    public Object testField0901 = new Integer(901);
+    public Object testField0902 = new Integer(902);
+    public Object testField0903 = new Integer(903);
+    public Object testField0904 = new Integer(904);
+    public Object testField0905 = new Integer(905);
+    public Object testField0906 = new Integer(906);
+    public Object testField0907 = new Integer(907);
+    public Object testField0908 = new Integer(908);
+    public Object testField0909 = new Integer(909);
+    public Object testField0910 = new Integer(910);
+    public Object testField0911 = new Integer(911);
+    public Object testField0912 = new Integer(912);
+    public Object testField0913 = new Integer(913);
+    public Object testField0914 = new Integer(914);
+    public Object testField0915 = new Integer(915);
+    public Object testField0916 = new Integer(916);
+    public Object testField0917 = new Integer(917);
+    public Object testField0918 = new Integer(918);
+    public Object testField0919 = new Integer(919);
+    public Object testField0920 = new Integer(920);
+    public Object testField0921 = new Integer(921);
+    public Object testField0922 = new Integer(922);
+    public Object testField0923 = new Integer(923);
+    public Object testField0924 = new Integer(924);
+    public Object testField0925 = new Integer(925);
+    public Object testField0926 = new Integer(926);
+    public Object testField0927 = new Integer(927);
+    public Object testField0928 = new Integer(928);
+    public Object testField0929 = new Integer(929);
+    public Object testField0930 = new Integer(930);
+    public Object testField0931 = new Integer(931);
+    public Object testField0932 = new Integer(932);
+    public Object testField0933 = new Integer(933);
+    public Object testField0934 = new Integer(934);
+    public Object testField0935 = new Integer(935);
+    public Object testField0936 = new Integer(936);
+    public Object testField0937 = new Integer(937);
+    public Object testField0938 = new Integer(938);
+    public Object testField0939 = new Integer(939);
+    public Object testField0940 = new Integer(940);
+    public Object testField0941 = new Integer(941);
+    public Object testField0942 = new Integer(942);
+    public Object testField0943 = new Integer(943);
+    public Object testField0944 = new Integer(944);
+    public Object testField0945 = new Integer(945);
+    public Object testField0946 = new Integer(946);
+    public Object testField0947 = new Integer(947);
+    public Object testField0948 = new Integer(948);
+    public Object testField0949 = new Integer(949);
+    public Object testField0950 = new Integer(950);
+    public Object testField0951 = new Integer(951);
+    public Object testField0952 = new Integer(952);
+    public Object testField0953 = new Integer(953);
+    public Object testField0954 = new Integer(954);
+    public Object testField0955 = new Integer(955);
+    public Object testField0956 = new Integer(956);
+    public Object testField0957 = new Integer(957);
+    public Object testField0958 = new Integer(958);
+    public Object testField0959 = new Integer(959);
+    public Object testField0960 = new Integer(960);
+    public Object testField0961 = new Integer(961);
+    public Object testField0962 = new Integer(962);
+    public Object testField0963 = new Integer(963);
+    public Object testField0964 = new Integer(964);
+    public Object testField0965 = new Integer(965);
+    public Object testField0966 = new Integer(966);
+    public Object testField0967 = new Integer(967);
+    public Object testField0968 = new Integer(968);
+    public Object testField0969 = new Integer(969);
+    public Object testField0970 = new Integer(970);
+    public Object testField0971 = new Integer(971);
+    public Object testField0972 = new Integer(972);
+    public Object testField0973 = new Integer(973);
+    public Object testField0974 = new Integer(974);
+    public Object testField0975 = new Integer(975);
+    public Object testField0976 = new Integer(976);
+    public Object testField0977 = new Integer(977);
+    public Object testField0978 = new Integer(978);
+    public Object testField0979 = new Integer(979);
+    public Object testField0980 = new Integer(980);
+    public Object testField0981 = new Integer(981);
+    public Object testField0982 = new Integer(982);
+    public Object testField0983 = new Integer(983);
+    public Object testField0984 = new Integer(984);
+    public Object testField0985 = new Integer(985);
+    public Object testField0986 = new Integer(986);
+    public Object testField0987 = new Integer(987);
+    public Object testField0988 = new Integer(988);
+    public Object testField0989 = new Integer(989);
+    public Object testField0990 = new Integer(990);
+    public Object testField0991 = new Integer(991);
+    public Object testField0992 = new Integer(992);
+    public Object testField0993 = new Integer(993);
+    public Object testField0994 = new Integer(994);
+    public Object testField0995 = new Integer(995);
+    public Object testField0996 = new Integer(996);
+    public Object testField0997 = new Integer(997);
+    public Object testField0998 = new Integer(998);
+    public Object testField0999 = new Integer(999);
+}
diff --git a/test/160-read-barrier-stress/src/ManyFieldsBase1.java b/test/160-read-barrier-stress/src/ManyFieldsBase1.java
new file mode 100644
index 0000000..8680c6b
--- /dev/null
+++ b/test/160-read-barrier-stress/src/ManyFieldsBase1.java
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+class ManyFieldsBase1 extends ManyFieldsBase0 {
+    public Object testField1000 = new Integer(1000);
+    public Object testField1001 = new Integer(1001);
+    public Object testField1002 = new Integer(1002);
+    public Object testField1003 = new Integer(1003);
+    public Object testField1004 = new Integer(1004);
+    public Object testField1005 = new Integer(1005);
+    public Object testField1006 = new Integer(1006);
+    public Object testField1007 = new Integer(1007);
+    public Object testField1008 = new Integer(1008);
+    public Object testField1009 = new Integer(1009);
+    public Object testField1010 = new Integer(1010);
+    public Object testField1011 = new Integer(1011);
+    public Object testField1012 = new Integer(1012);
+    public Object testField1013 = new Integer(1013);
+    public Object testField1014 = new Integer(1014);
+    public Object testField1015 = new Integer(1015);
+    public Object testField1016 = new Integer(1016);
+    public Object testField1017 = new Integer(1017);
+    public Object testField1018 = new Integer(1018);
+    public Object testField1019 = new Integer(1019);
+    public Object testField1020 = new Integer(1020);
+    public Object testField1021 = new Integer(1021);
+    public Object testField1022 = new Integer(1022);
+    public Object testField1023 = new Integer(1023);
+    public Object testField1024 = new Integer(1024);
+    public Object testField1025 = new Integer(1025);
+    public Object testField1026 = new Integer(1026);
+    public Object testField1027 = new Integer(1027);
+    public Object testField1028 = new Integer(1028);
+    public Object testField1029 = new Integer(1029);
+    public Object testField1030 = new Integer(1030);
+    public Object testField1031 = new Integer(1031);
+    public Object testField1032 = new Integer(1032);
+    public Object testField1033 = new Integer(1033);
+    public Object testField1034 = new Integer(1034);
+    public Object testField1035 = new Integer(1035);
+    public Object testField1036 = new Integer(1036);
+    public Object testField1037 = new Integer(1037);
+    public Object testField1038 = new Integer(1038);
+    public Object testField1039 = new Integer(1039);
+    public Object testField1040 = new Integer(1040);
+    public Object testField1041 = new Integer(1041);
+    public Object testField1042 = new Integer(1042);
+    public Object testField1043 = new Integer(1043);
+    public Object testField1044 = new Integer(1044);
+    public Object testField1045 = new Integer(1045);
+    public Object testField1046 = new Integer(1046);
+    public Object testField1047 = new Integer(1047);
+    public Object testField1048 = new Integer(1048);
+    public Object testField1049 = new Integer(1049);
+    public Object testField1050 = new Integer(1050);
+    public Object testField1051 = new Integer(1051);
+    public Object testField1052 = new Integer(1052);
+    public Object testField1053 = new Integer(1053);
+    public Object testField1054 = new Integer(1054);
+    public Object testField1055 = new Integer(1055);
+    public Object testField1056 = new Integer(1056);
+    public Object testField1057 = new Integer(1057);
+    public Object testField1058 = new Integer(1058);
+    public Object testField1059 = new Integer(1059);
+    public Object testField1060 = new Integer(1060);
+    public Object testField1061 = new Integer(1061);
+    public Object testField1062 = new Integer(1062);
+    public Object testField1063 = new Integer(1063);
+    public Object testField1064 = new Integer(1064);
+    public Object testField1065 = new Integer(1065);
+    public Object testField1066 = new Integer(1066);
+    public Object testField1067 = new Integer(1067);
+    public Object testField1068 = new Integer(1068);
+    public Object testField1069 = new Integer(1069);
+    public Object testField1070 = new Integer(1070);
+    public Object testField1071 = new Integer(1071);
+    public Object testField1072 = new Integer(1072);
+    public Object testField1073 = new Integer(1073);
+    public Object testField1074 = new Integer(1074);
+    public Object testField1075 = new Integer(1075);
+    public Object testField1076 = new Integer(1076);
+    public Object testField1077 = new Integer(1077);
+    public Object testField1078 = new Integer(1078);
+    public Object testField1079 = new Integer(1079);
+    public Object testField1080 = new Integer(1080);
+    public Object testField1081 = new Integer(1081);
+    public Object testField1082 = new Integer(1082);
+    public Object testField1083 = new Integer(1083);
+    public Object testField1084 = new Integer(1084);
+    public Object testField1085 = new Integer(1085);
+    public Object testField1086 = new Integer(1086);
+    public Object testField1087 = new Integer(1087);
+    public Object testField1088 = new Integer(1088);
+    public Object testField1089 = new Integer(1089);
+    public Object testField1090 = new Integer(1090);
+    public Object testField1091 = new Integer(1091);
+    public Object testField1092 = new Integer(1092);
+    public Object testField1093 = new Integer(1093);
+    public Object testField1094 = new Integer(1094);
+    public Object testField1095 = new Integer(1095);
+    public Object testField1096 = new Integer(1096);
+    public Object testField1097 = new Integer(1097);
+    public Object testField1098 = new Integer(1098);
+    public Object testField1099 = new Integer(1099);
+    public Object testField1100 = new Integer(1100);
+    public Object testField1101 = new Integer(1101);
+    public Object testField1102 = new Integer(1102);
+    public Object testField1103 = new Integer(1103);
+    public Object testField1104 = new Integer(1104);
+    public Object testField1105 = new Integer(1105);
+    public Object testField1106 = new Integer(1106);
+    public Object testField1107 = new Integer(1107);
+    public Object testField1108 = new Integer(1108);
+    public Object testField1109 = new Integer(1109);
+    public Object testField1110 = new Integer(1110);
+    public Object testField1111 = new Integer(1111);
+    public Object testField1112 = new Integer(1112);
+    public Object testField1113 = new Integer(1113);
+    public Object testField1114 = new Integer(1114);
+    public Object testField1115 = new Integer(1115);
+    public Object testField1116 = new Integer(1116);
+    public Object testField1117 = new Integer(1117);
+    public Object testField1118 = new Integer(1118);
+    public Object testField1119 = new Integer(1119);
+    public Object testField1120 = new Integer(1120);
+    public Object testField1121 = new Integer(1121);
+    public Object testField1122 = new Integer(1122);
+    public Object testField1123 = new Integer(1123);
+    public Object testField1124 = new Integer(1124);
+    public Object testField1125 = new Integer(1125);
+    public Object testField1126 = new Integer(1126);
+    public Object testField1127 = new Integer(1127);
+    public Object testField1128 = new Integer(1128);
+    public Object testField1129 = new Integer(1129);
+    public Object testField1130 = new Integer(1130);
+    public Object testField1131 = new Integer(1131);
+    public Object testField1132 = new Integer(1132);
+    public Object testField1133 = new Integer(1133);
+    public Object testField1134 = new Integer(1134);
+    public Object testField1135 = new Integer(1135);
+    public Object testField1136 = new Integer(1136);
+    public Object testField1137 = new Integer(1137);
+    public Object testField1138 = new Integer(1138);
+    public Object testField1139 = new Integer(1139);
+    public Object testField1140 = new Integer(1140);
+    public Object testField1141 = new Integer(1141);
+    public Object testField1142 = new Integer(1142);
+    public Object testField1143 = new Integer(1143);
+    public Object testField1144 = new Integer(1144);
+    public Object testField1145 = new Integer(1145);
+    public Object testField1146 = new Integer(1146);
+    public Object testField1147 = new Integer(1147);
+    public Object testField1148 = new Integer(1148);
+    public Object testField1149 = new Integer(1149);
+    public Object testField1150 = new Integer(1150);
+    public Object testField1151 = new Integer(1151);
+    public Object testField1152 = new Integer(1152);
+    public Object testField1153 = new Integer(1153);
+    public Object testField1154 = new Integer(1154);
+    public Object testField1155 = new Integer(1155);
+    public Object testField1156 = new Integer(1156);
+    public Object testField1157 = new Integer(1157);
+    public Object testField1158 = new Integer(1158);
+    public Object testField1159 = new Integer(1159);
+    public Object testField1160 = new Integer(1160);
+    public Object testField1161 = new Integer(1161);
+    public Object testField1162 = new Integer(1162);
+    public Object testField1163 = new Integer(1163);
+    public Object testField1164 = new Integer(1164);
+    public Object testField1165 = new Integer(1165);
+    public Object testField1166 = new Integer(1166);
+    public Object testField1167 = new Integer(1167);
+    public Object testField1168 = new Integer(1168);
+    public Object testField1169 = new Integer(1169);
+    public Object testField1170 = new Integer(1170);
+    public Object testField1171 = new Integer(1171);
+    public Object testField1172 = new Integer(1172);
+    public Object testField1173 = new Integer(1173);
+    public Object testField1174 = new Integer(1174);
+    public Object testField1175 = new Integer(1175);
+    public Object testField1176 = new Integer(1176);
+    public Object testField1177 = new Integer(1177);
+    public Object testField1178 = new Integer(1178);
+    public Object testField1179 = new Integer(1179);
+    public Object testField1180 = new Integer(1180);
+    public Object testField1181 = new Integer(1181);
+    public Object testField1182 = new Integer(1182);
+    public Object testField1183 = new Integer(1183);
+    public Object testField1184 = new Integer(1184);
+    public Object testField1185 = new Integer(1185);
+    public Object testField1186 = new Integer(1186);
+    public Object testField1187 = new Integer(1187);
+    public Object testField1188 = new Integer(1188);
+    public Object testField1189 = new Integer(1189);
+    public Object testField1190 = new Integer(1190);
+    public Object testField1191 = new Integer(1191);
+    public Object testField1192 = new Integer(1192);
+    public Object testField1193 = new Integer(1193);
+    public Object testField1194 = new Integer(1194);
+    public Object testField1195 = new Integer(1195);
+    public Object testField1196 = new Integer(1196);
+    public Object testField1197 = new Integer(1197);
+    public Object testField1198 = new Integer(1198);
+    public Object testField1199 = new Integer(1199);
+    public Object testField1200 = new Integer(1200);
+    public Object testField1201 = new Integer(1201);
+    public Object testField1202 = new Integer(1202);
+    public Object testField1203 = new Integer(1203);
+    public Object testField1204 = new Integer(1204);
+    public Object testField1205 = new Integer(1205);
+    public Object testField1206 = new Integer(1206);
+    public Object testField1207 = new Integer(1207);
+    public Object testField1208 = new Integer(1208);
+    public Object testField1209 = new Integer(1209);
+    public Object testField1210 = new Integer(1210);
+    public Object testField1211 = new Integer(1211);
+    public Object testField1212 = new Integer(1212);
+    public Object testField1213 = new Integer(1213);
+    public Object testField1214 = new Integer(1214);
+    public Object testField1215 = new Integer(1215);
+    public Object testField1216 = new Integer(1216);
+    public Object testField1217 = new Integer(1217);
+    public Object testField1218 = new Integer(1218);
+    public Object testField1219 = new Integer(1219);
+    public Object testField1220 = new Integer(1220);
+    public Object testField1221 = new Integer(1221);
+    public Object testField1222 = new Integer(1222);
+    public Object testField1223 = new Integer(1223);
+    public Object testField1224 = new Integer(1224);
+    public Object testField1225 = new Integer(1225);
+    public Object testField1226 = new Integer(1226);
+    public Object testField1227 = new Integer(1227);
+    public Object testField1228 = new Integer(1228);
+    public Object testField1229 = new Integer(1229);
+    public Object testField1230 = new Integer(1230);
+    public Object testField1231 = new Integer(1231);
+    public Object testField1232 = new Integer(1232);
+    public Object testField1233 = new Integer(1233);
+    public Object testField1234 = new Integer(1234);
+    public Object testField1235 = new Integer(1235);
+    public Object testField1236 = new Integer(1236);
+    public Object testField1237 = new Integer(1237);
+    public Object testField1238 = new Integer(1238);
+    public Object testField1239 = new Integer(1239);
+    public Object testField1240 = new Integer(1240);
+    public Object testField1241 = new Integer(1241);
+    public Object testField1242 = new Integer(1242);
+    public Object testField1243 = new Integer(1243);
+    public Object testField1244 = new Integer(1244);
+    public Object testField1245 = new Integer(1245);
+    public Object testField1246 = new Integer(1246);
+    public Object testField1247 = new Integer(1247);
+    public Object testField1248 = new Integer(1248);
+    public Object testField1249 = new Integer(1249);
+    public Object testField1250 = new Integer(1250);
+    public Object testField1251 = new Integer(1251);
+    public Object testField1252 = new Integer(1252);
+    public Object testField1253 = new Integer(1253);
+    public Object testField1254 = new Integer(1254);
+    public Object testField1255 = new Integer(1255);
+    public Object testField1256 = new Integer(1256);
+    public Object testField1257 = new Integer(1257);
+    public Object testField1258 = new Integer(1258);
+    public Object testField1259 = new Integer(1259);
+    public Object testField1260 = new Integer(1260);
+    public Object testField1261 = new Integer(1261);
+    public Object testField1262 = new Integer(1262);
+    public Object testField1263 = new Integer(1263);
+    public Object testField1264 = new Integer(1264);
+    public Object testField1265 = new Integer(1265);
+    public Object testField1266 = new Integer(1266);
+    public Object testField1267 = new Integer(1267);
+    public Object testField1268 = new Integer(1268);
+    public Object testField1269 = new Integer(1269);
+    public Object testField1270 = new Integer(1270);
+    public Object testField1271 = new Integer(1271);
+    public Object testField1272 = new Integer(1272);
+    public Object testField1273 = new Integer(1273);
+    public Object testField1274 = new Integer(1274);
+    public Object testField1275 = new Integer(1275);
+    public Object testField1276 = new Integer(1276);
+    public Object testField1277 = new Integer(1277);
+    public Object testField1278 = new Integer(1278);
+    public Object testField1279 = new Integer(1279);
+    public Object testField1280 = new Integer(1280);
+    public Object testField1281 = new Integer(1281);
+    public Object testField1282 = new Integer(1282);
+    public Object testField1283 = new Integer(1283);
+    public Object testField1284 = new Integer(1284);
+    public Object testField1285 = new Integer(1285);
+    public Object testField1286 = new Integer(1286);
+    public Object testField1287 = new Integer(1287);
+    public Object testField1288 = new Integer(1288);
+    public Object testField1289 = new Integer(1289);
+    public Object testField1290 = new Integer(1290);
+    public Object testField1291 = new Integer(1291);
+    public Object testField1292 = new Integer(1292);
+    public Object testField1293 = new Integer(1293);
+    public Object testField1294 = new Integer(1294);
+    public Object testField1295 = new Integer(1295);
+    public Object testField1296 = new Integer(1296);
+    public Object testField1297 = new Integer(1297);
+    public Object testField1298 = new Integer(1298);
+    public Object testField1299 = new Integer(1299);
+    public Object testField1300 = new Integer(1300);
+    public Object testField1301 = new Integer(1301);
+    public Object testField1302 = new Integer(1302);
+    public Object testField1303 = new Integer(1303);
+    public Object testField1304 = new Integer(1304);
+    public Object testField1305 = new Integer(1305);
+    public Object testField1306 = new Integer(1306);
+    public Object testField1307 = new Integer(1307);
+    public Object testField1308 = new Integer(1308);
+    public Object testField1309 = new Integer(1309);
+    public Object testField1310 = new Integer(1310);
+    public Object testField1311 = new Integer(1311);
+    public Object testField1312 = new Integer(1312);
+    public Object testField1313 = new Integer(1313);
+    public Object testField1314 = new Integer(1314);
+    public Object testField1315 = new Integer(1315);
+    public Object testField1316 = new Integer(1316);
+    public Object testField1317 = new Integer(1317);
+    public Object testField1318 = new Integer(1318);
+    public Object testField1319 = new Integer(1319);
+    public Object testField1320 = new Integer(1320);
+    public Object testField1321 = new Integer(1321);
+    public Object testField1322 = new Integer(1322);
+    public Object testField1323 = new Integer(1323);
+    public Object testField1324 = new Integer(1324);
+    public Object testField1325 = new Integer(1325);
+    public Object testField1326 = new Integer(1326);
+    public Object testField1327 = new Integer(1327);
+    public Object testField1328 = new Integer(1328);
+    public Object testField1329 = new Integer(1329);
+    public Object testField1330 = new Integer(1330);
+    public Object testField1331 = new Integer(1331);
+    public Object testField1332 = new Integer(1332);
+    public Object testField1333 = new Integer(1333);
+    public Object testField1334 = new Integer(1334);
+    public Object testField1335 = new Integer(1335);
+    public Object testField1336 = new Integer(1336);
+    public Object testField1337 = new Integer(1337);
+    public Object testField1338 = new Integer(1338);
+    public Object testField1339 = new Integer(1339);
+    public Object testField1340 = new Integer(1340);
+    public Object testField1341 = new Integer(1341);
+    public Object testField1342 = new Integer(1342);
+    public Object testField1343 = new Integer(1343);
+    public Object testField1344 = new Integer(1344);
+    public Object testField1345 = new Integer(1345);
+    public Object testField1346 = new Integer(1346);
+    public Object testField1347 = new Integer(1347);
+    public Object testField1348 = new Integer(1348);
+    public Object testField1349 = new Integer(1349);
+    public Object testField1350 = new Integer(1350);
+    public Object testField1351 = new Integer(1351);
+    public Object testField1352 = new Integer(1352);
+    public Object testField1353 = new Integer(1353);
+    public Object testField1354 = new Integer(1354);
+    public Object testField1355 = new Integer(1355);
+    public Object testField1356 = new Integer(1356);
+    public Object testField1357 = new Integer(1357);
+    public Object testField1358 = new Integer(1358);
+    public Object testField1359 = new Integer(1359);
+    public Object testField1360 = new Integer(1360);
+    public Object testField1361 = new Integer(1361);
+    public Object testField1362 = new Integer(1362);
+    public Object testField1363 = new Integer(1363);
+    public Object testField1364 = new Integer(1364);
+    public Object testField1365 = new Integer(1365);
+    public Object testField1366 = new Integer(1366);
+    public Object testField1367 = new Integer(1367);
+    public Object testField1368 = new Integer(1368);
+    public Object testField1369 = new Integer(1369);
+    public Object testField1370 = new Integer(1370);
+    public Object testField1371 = new Integer(1371);
+    public Object testField1372 = new Integer(1372);
+    public Object testField1373 = new Integer(1373);
+    public Object testField1374 = new Integer(1374);
+    public Object testField1375 = new Integer(1375);
+    public Object testField1376 = new Integer(1376);
+    public Object testField1377 = new Integer(1377);
+    public Object testField1378 = new Integer(1378);
+    public Object testField1379 = new Integer(1379);
+    public Object testField1380 = new Integer(1380);
+    public Object testField1381 = new Integer(1381);
+    public Object testField1382 = new Integer(1382);
+    public Object testField1383 = new Integer(1383);
+    public Object testField1384 = new Integer(1384);
+    public Object testField1385 = new Integer(1385);
+    public Object testField1386 = new Integer(1386);
+    public Object testField1387 = new Integer(1387);
+    public Object testField1388 = new Integer(1388);
+    public Object testField1389 = new Integer(1389);
+    public Object testField1390 = new Integer(1390);
+    public Object testField1391 = new Integer(1391);
+    public Object testField1392 = new Integer(1392);
+    public Object testField1393 = new Integer(1393);
+    public Object testField1394 = new Integer(1394);
+    public Object testField1395 = new Integer(1395);
+    public Object testField1396 = new Integer(1396);
+    public Object testField1397 = new Integer(1397);
+    public Object testField1398 = new Integer(1398);
+    public Object testField1399 = new Integer(1399);
+    public Object testField1400 = new Integer(1400);
+    public Object testField1401 = new Integer(1401);
+    public Object testField1402 = new Integer(1402);
+    public Object testField1403 = new Integer(1403);
+    public Object testField1404 = new Integer(1404);
+    public Object testField1405 = new Integer(1405);
+    public Object testField1406 = new Integer(1406);
+    public Object testField1407 = new Integer(1407);
+    public Object testField1408 = new Integer(1408);
+    public Object testField1409 = new Integer(1409);
+    public Object testField1410 = new Integer(1410);
+    public Object testField1411 = new Integer(1411);
+    public Object testField1412 = new Integer(1412);
+    public Object testField1413 = new Integer(1413);
+    public Object testField1414 = new Integer(1414);
+    public Object testField1415 = new Integer(1415);
+    public Object testField1416 = new Integer(1416);
+    public Object testField1417 = new Integer(1417);
+    public Object testField1418 = new Integer(1418);
+    public Object testField1419 = new Integer(1419);
+    public Object testField1420 = new Integer(1420);
+    public Object testField1421 = new Integer(1421);
+    public Object testField1422 = new Integer(1422);
+    public Object testField1423 = new Integer(1423);
+    public Object testField1424 = new Integer(1424);
+    public Object testField1425 = new Integer(1425);
+    public Object testField1426 = new Integer(1426);
+    public Object testField1427 = new Integer(1427);
+    public Object testField1428 = new Integer(1428);
+    public Object testField1429 = new Integer(1429);
+    public Object testField1430 = new Integer(1430);
+    public Object testField1431 = new Integer(1431);
+    public Object testField1432 = new Integer(1432);
+    public Object testField1433 = new Integer(1433);
+    public Object testField1434 = new Integer(1434);
+    public Object testField1435 = new Integer(1435);
+    public Object testField1436 = new Integer(1436);
+    public Object testField1437 = new Integer(1437);
+    public Object testField1438 = new Integer(1438);
+    public Object testField1439 = new Integer(1439);
+    public Object testField1440 = new Integer(1440);
+    public Object testField1441 = new Integer(1441);
+    public Object testField1442 = new Integer(1442);
+    public Object testField1443 = new Integer(1443);
+    public Object testField1444 = new Integer(1444);
+    public Object testField1445 = new Integer(1445);
+    public Object testField1446 = new Integer(1446);
+    public Object testField1447 = new Integer(1447);
+    public Object testField1448 = new Integer(1448);
+    public Object testField1449 = new Integer(1449);
+    public Object testField1450 = new Integer(1450);
+    public Object testField1451 = new Integer(1451);
+    public Object testField1452 = new Integer(1452);
+    public Object testField1453 = new Integer(1453);
+    public Object testField1454 = new Integer(1454);
+    public Object testField1455 = new Integer(1455);
+    public Object testField1456 = new Integer(1456);
+    public Object testField1457 = new Integer(1457);
+    public Object testField1458 = new Integer(1458);
+    public Object testField1459 = new Integer(1459);
+    public Object testField1460 = new Integer(1460);
+    public Object testField1461 = new Integer(1461);
+    public Object testField1462 = new Integer(1462);
+    public Object testField1463 = new Integer(1463);
+    public Object testField1464 = new Integer(1464);
+    public Object testField1465 = new Integer(1465);
+    public Object testField1466 = new Integer(1466);
+    public Object testField1467 = new Integer(1467);
+    public Object testField1468 = new Integer(1468);
+    public Object testField1469 = new Integer(1469);
+    public Object testField1470 = new Integer(1470);
+    public Object testField1471 = new Integer(1471);
+    public Object testField1472 = new Integer(1472);
+    public Object testField1473 = new Integer(1473);
+    public Object testField1474 = new Integer(1474);
+    public Object testField1475 = new Integer(1475);
+    public Object testField1476 = new Integer(1476);
+    public Object testField1477 = new Integer(1477);
+    public Object testField1478 = new Integer(1478);
+    public Object testField1479 = new Integer(1479);
+    public Object testField1480 = new Integer(1480);
+    public Object testField1481 = new Integer(1481);
+    public Object testField1482 = new Integer(1482);
+    public Object testField1483 = new Integer(1483);
+    public Object testField1484 = new Integer(1484);
+    public Object testField1485 = new Integer(1485);
+    public Object testField1486 = new Integer(1486);
+    public Object testField1487 = new Integer(1487);
+    public Object testField1488 = new Integer(1488);
+    public Object testField1489 = new Integer(1489);
+    public Object testField1490 = new Integer(1490);
+    public Object testField1491 = new Integer(1491);
+    public Object testField1492 = new Integer(1492);
+    public Object testField1493 = new Integer(1493);
+    public Object testField1494 = new Integer(1494);
+    public Object testField1495 = new Integer(1495);
+    public Object testField1496 = new Integer(1496);
+    public Object testField1497 = new Integer(1497);
+    public Object testField1498 = new Integer(1498);
+    public Object testField1499 = new Integer(1499);
+    public Object testField1500 = new Integer(1500);
+    public Object testField1501 = new Integer(1501);
+    public Object testField1502 = new Integer(1502);
+    public Object testField1503 = new Integer(1503);
+    public Object testField1504 = new Integer(1504);
+    public Object testField1505 = new Integer(1505);
+    public Object testField1506 = new Integer(1506);
+    public Object testField1507 = new Integer(1507);
+    public Object testField1508 = new Integer(1508);
+    public Object testField1509 = new Integer(1509);
+    public Object testField1510 = new Integer(1510);
+    public Object testField1511 = new Integer(1511);
+    public Object testField1512 = new Integer(1512);
+    public Object testField1513 = new Integer(1513);
+    public Object testField1514 = new Integer(1514);
+    public Object testField1515 = new Integer(1515);
+    public Object testField1516 = new Integer(1516);
+    public Object testField1517 = new Integer(1517);
+    public Object testField1518 = new Integer(1518);
+    public Object testField1519 = new Integer(1519);
+    public Object testField1520 = new Integer(1520);
+    public Object testField1521 = new Integer(1521);
+    public Object testField1522 = new Integer(1522);
+    public Object testField1523 = new Integer(1523);
+    public Object testField1524 = new Integer(1524);
+    public Object testField1525 = new Integer(1525);
+    public Object testField1526 = new Integer(1526);
+    public Object testField1527 = new Integer(1527);
+    public Object testField1528 = new Integer(1528);
+    public Object testField1529 = new Integer(1529);
+    public Object testField1530 = new Integer(1530);
+    public Object testField1531 = new Integer(1531);
+    public Object testField1532 = new Integer(1532);
+    public Object testField1533 = new Integer(1533);
+    public Object testField1534 = new Integer(1534);
+    public Object testField1535 = new Integer(1535);
+    public Object testField1536 = new Integer(1536);
+    public Object testField1537 = new Integer(1537);
+    public Object testField1538 = new Integer(1538);
+    public Object testField1539 = new Integer(1539);
+    public Object testField1540 = new Integer(1540);
+    public Object testField1541 = new Integer(1541);
+    public Object testField1542 = new Integer(1542);
+    public Object testField1543 = new Integer(1543);
+    public Object testField1544 = new Integer(1544);
+    public Object testField1545 = new Integer(1545);
+    public Object testField1546 = new Integer(1546);
+    public Object testField1547 = new Integer(1547);
+    public Object testField1548 = new Integer(1548);
+    public Object testField1549 = new Integer(1549);
+    public Object testField1550 = new Integer(1550);
+    public Object testField1551 = new Integer(1551);
+    public Object testField1552 = new Integer(1552);
+    public Object testField1553 = new Integer(1553);
+    public Object testField1554 = new Integer(1554);
+    public Object testField1555 = new Integer(1555);
+    public Object testField1556 = new Integer(1556);
+    public Object testField1557 = new Integer(1557);
+    public Object testField1558 = new Integer(1558);
+    public Object testField1559 = new Integer(1559);
+    public Object testField1560 = new Integer(1560);
+    public Object testField1561 = new Integer(1561);
+    public Object testField1562 = new Integer(1562);
+    public Object testField1563 = new Integer(1563);
+    public Object testField1564 = new Integer(1564);
+    public Object testField1565 = new Integer(1565);
+    public Object testField1566 = new Integer(1566);
+    public Object testField1567 = new Integer(1567);
+    public Object testField1568 = new Integer(1568);
+    public Object testField1569 = new Integer(1569);
+    public Object testField1570 = new Integer(1570);
+    public Object testField1571 = new Integer(1571);
+    public Object testField1572 = new Integer(1572);
+    public Object testField1573 = new Integer(1573);
+    public Object testField1574 = new Integer(1574);
+    public Object testField1575 = new Integer(1575);
+    public Object testField1576 = new Integer(1576);
+    public Object testField1577 = new Integer(1577);
+    public Object testField1578 = new Integer(1578);
+    public Object testField1579 = new Integer(1579);
+    public Object testField1580 = new Integer(1580);
+    public Object testField1581 = new Integer(1581);
+    public Object testField1582 = new Integer(1582);
+    public Object testField1583 = new Integer(1583);
+    public Object testField1584 = new Integer(1584);
+    public Object testField1585 = new Integer(1585);
+    public Object testField1586 = new Integer(1586);
+    public Object testField1587 = new Integer(1587);
+    public Object testField1588 = new Integer(1588);
+    public Object testField1589 = new Integer(1589);
+    public Object testField1590 = new Integer(1590);
+    public Object testField1591 = new Integer(1591);
+    public Object testField1592 = new Integer(1592);
+    public Object testField1593 = new Integer(1593);
+    public Object testField1594 = new Integer(1594);
+    public Object testField1595 = new Integer(1595);
+    public Object testField1596 = new Integer(1596);
+    public Object testField1597 = new Integer(1597);
+    public Object testField1598 = new Integer(1598);
+    public Object testField1599 = new Integer(1599);
+    public Object testField1600 = new Integer(1600);
+    public Object testField1601 = new Integer(1601);
+    public Object testField1602 = new Integer(1602);
+    public Object testField1603 = new Integer(1603);
+    public Object testField1604 = new Integer(1604);
+    public Object testField1605 = new Integer(1605);
+    public Object testField1606 = new Integer(1606);
+    public Object testField1607 = new Integer(1607);
+    public Object testField1608 = new Integer(1608);
+    public Object testField1609 = new Integer(1609);
+    public Object testField1610 = new Integer(1610);
+    public Object testField1611 = new Integer(1611);
+    public Object testField1612 = new Integer(1612);
+    public Object testField1613 = new Integer(1613);
+    public Object testField1614 = new Integer(1614);
+    public Object testField1615 = new Integer(1615);
+    public Object testField1616 = new Integer(1616);
+    public Object testField1617 = new Integer(1617);
+    public Object testField1618 = new Integer(1618);
+    public Object testField1619 = new Integer(1619);
+    public Object testField1620 = new Integer(1620);
+    public Object testField1621 = new Integer(1621);
+    public Object testField1622 = new Integer(1622);
+    public Object testField1623 = new Integer(1623);
+    public Object testField1624 = new Integer(1624);
+    public Object testField1625 = new Integer(1625);
+    public Object testField1626 = new Integer(1626);
+    public Object testField1627 = new Integer(1627);
+    public Object testField1628 = new Integer(1628);
+    public Object testField1629 = new Integer(1629);
+    public Object testField1630 = new Integer(1630);
+    public Object testField1631 = new Integer(1631);
+    public Object testField1632 = new Integer(1632);
+    public Object testField1633 = new Integer(1633);
+    public Object testField1634 = new Integer(1634);
+    public Object testField1635 = new Integer(1635);
+    public Object testField1636 = new Integer(1636);
+    public Object testField1637 = new Integer(1637);
+    public Object testField1638 = new Integer(1638);
+    public Object testField1639 = new Integer(1639);
+    public Object testField1640 = new Integer(1640);
+    public Object testField1641 = new Integer(1641);
+    public Object testField1642 = new Integer(1642);
+    public Object testField1643 = new Integer(1643);
+    public Object testField1644 = new Integer(1644);
+    public Object testField1645 = new Integer(1645);
+    public Object testField1646 = new Integer(1646);
+    public Object testField1647 = new Integer(1647);
+    public Object testField1648 = new Integer(1648);
+    public Object testField1649 = new Integer(1649);
+    public Object testField1650 = new Integer(1650);
+    public Object testField1651 = new Integer(1651);
+    public Object testField1652 = new Integer(1652);
+    public Object testField1653 = new Integer(1653);
+    public Object testField1654 = new Integer(1654);
+    public Object testField1655 = new Integer(1655);
+    public Object testField1656 = new Integer(1656);
+    public Object testField1657 = new Integer(1657);
+    public Object testField1658 = new Integer(1658);
+    public Object testField1659 = new Integer(1659);
+    public Object testField1660 = new Integer(1660);
+    public Object testField1661 = new Integer(1661);
+    public Object testField1662 = new Integer(1662);
+    public Object testField1663 = new Integer(1663);
+    public Object testField1664 = new Integer(1664);
+    public Object testField1665 = new Integer(1665);
+    public Object testField1666 = new Integer(1666);
+    public Object testField1667 = new Integer(1667);
+    public Object testField1668 = new Integer(1668);
+    public Object testField1669 = new Integer(1669);
+    public Object testField1670 = new Integer(1670);
+    public Object testField1671 = new Integer(1671);
+    public Object testField1672 = new Integer(1672);
+    public Object testField1673 = new Integer(1673);
+    public Object testField1674 = new Integer(1674);
+    public Object testField1675 = new Integer(1675);
+    public Object testField1676 = new Integer(1676);
+    public Object testField1677 = new Integer(1677);
+    public Object testField1678 = new Integer(1678);
+    public Object testField1679 = new Integer(1679);
+    public Object testField1680 = new Integer(1680);
+    public Object testField1681 = new Integer(1681);
+    public Object testField1682 = new Integer(1682);
+    public Object testField1683 = new Integer(1683);
+    public Object testField1684 = new Integer(1684);
+    public Object testField1685 = new Integer(1685);
+    public Object testField1686 = new Integer(1686);
+    public Object testField1687 = new Integer(1687);
+    public Object testField1688 = new Integer(1688);
+    public Object testField1689 = new Integer(1689);
+    public Object testField1690 = new Integer(1690);
+    public Object testField1691 = new Integer(1691);
+    public Object testField1692 = new Integer(1692);
+    public Object testField1693 = new Integer(1693);
+    public Object testField1694 = new Integer(1694);
+    public Object testField1695 = new Integer(1695);
+    public Object testField1696 = new Integer(1696);
+    public Object testField1697 = new Integer(1697);
+    public Object testField1698 = new Integer(1698);
+    public Object testField1699 = new Integer(1699);
+    public Object testField1700 = new Integer(1700);
+    public Object testField1701 = new Integer(1701);
+    public Object testField1702 = new Integer(1702);
+    public Object testField1703 = new Integer(1703);
+    public Object testField1704 = new Integer(1704);
+    public Object testField1705 = new Integer(1705);
+    public Object testField1706 = new Integer(1706);
+    public Object testField1707 = new Integer(1707);
+    public Object testField1708 = new Integer(1708);
+    public Object testField1709 = new Integer(1709);
+    public Object testField1710 = new Integer(1710);
+    public Object testField1711 = new Integer(1711);
+    public Object testField1712 = new Integer(1712);
+    public Object testField1713 = new Integer(1713);
+    public Object testField1714 = new Integer(1714);
+    public Object testField1715 = new Integer(1715);
+    public Object testField1716 = new Integer(1716);
+    public Object testField1717 = new Integer(1717);
+    public Object testField1718 = new Integer(1718);
+    public Object testField1719 = new Integer(1719);
+    public Object testField1720 = new Integer(1720);
+    public Object testField1721 = new Integer(1721);
+    public Object testField1722 = new Integer(1722);
+    public Object testField1723 = new Integer(1723);
+    public Object testField1724 = new Integer(1724);
+    public Object testField1725 = new Integer(1725);
+    public Object testField1726 = new Integer(1726);
+    public Object testField1727 = new Integer(1727);
+    public Object testField1728 = new Integer(1728);
+    public Object testField1729 = new Integer(1729);
+    public Object testField1730 = new Integer(1730);
+    public Object testField1731 = new Integer(1731);
+    public Object testField1732 = new Integer(1732);
+    public Object testField1733 = new Integer(1733);
+    public Object testField1734 = new Integer(1734);
+    public Object testField1735 = new Integer(1735);
+    public Object testField1736 = new Integer(1736);
+    public Object testField1737 = new Integer(1737);
+    public Object testField1738 = new Integer(1738);
+    public Object testField1739 = new Integer(1739);
+    public Object testField1740 = new Integer(1740);
+    public Object testField1741 = new Integer(1741);
+    public Object testField1742 = new Integer(1742);
+    public Object testField1743 = new Integer(1743);
+    public Object testField1744 = new Integer(1744);
+    public Object testField1745 = new Integer(1745);
+    public Object testField1746 = new Integer(1746);
+    public Object testField1747 = new Integer(1747);
+    public Object testField1748 = new Integer(1748);
+    public Object testField1749 = new Integer(1749);
+    public Object testField1750 = new Integer(1750);
+    public Object testField1751 = new Integer(1751);
+    public Object testField1752 = new Integer(1752);
+    public Object testField1753 = new Integer(1753);
+    public Object testField1754 = new Integer(1754);
+    public Object testField1755 = new Integer(1755);
+    public Object testField1756 = new Integer(1756);
+    public Object testField1757 = new Integer(1757);
+    public Object testField1758 = new Integer(1758);
+    public Object testField1759 = new Integer(1759);
+    public Object testField1760 = new Integer(1760);
+    public Object testField1761 = new Integer(1761);
+    public Object testField1762 = new Integer(1762);
+    public Object testField1763 = new Integer(1763);
+    public Object testField1764 = new Integer(1764);
+    public Object testField1765 = new Integer(1765);
+    public Object testField1766 = new Integer(1766);
+    public Object testField1767 = new Integer(1767);
+    public Object testField1768 = new Integer(1768);
+    public Object testField1769 = new Integer(1769);
+    public Object testField1770 = new Integer(1770);
+    public Object testField1771 = new Integer(1771);
+    public Object testField1772 = new Integer(1772);
+    public Object testField1773 = new Integer(1773);
+    public Object testField1774 = new Integer(1774);
+    public Object testField1775 = new Integer(1775);
+    public Object testField1776 = new Integer(1776);
+    public Object testField1777 = new Integer(1777);
+    public Object testField1778 = new Integer(1778);
+    public Object testField1779 = new Integer(1779);
+    public Object testField1780 = new Integer(1780);
+    public Object testField1781 = new Integer(1781);
+    public Object testField1782 = new Integer(1782);
+    public Object testField1783 = new Integer(1783);
+    public Object testField1784 = new Integer(1784);
+    public Object testField1785 = new Integer(1785);
+    public Object testField1786 = new Integer(1786);
+    public Object testField1787 = new Integer(1787);
+    public Object testField1788 = new Integer(1788);
+    public Object testField1789 = new Integer(1789);
+    public Object testField1790 = new Integer(1790);
+    public Object testField1791 = new Integer(1791);
+    public Object testField1792 = new Integer(1792);
+    public Object testField1793 = new Integer(1793);
+    public Object testField1794 = new Integer(1794);
+    public Object testField1795 = new Integer(1795);
+    public Object testField1796 = new Integer(1796);
+    public Object testField1797 = new Integer(1797);
+    public Object testField1798 = new Integer(1798);
+    public Object testField1799 = new Integer(1799);
+    public Object testField1800 = new Integer(1800);
+    public Object testField1801 = new Integer(1801);
+    public Object testField1802 = new Integer(1802);
+    public Object testField1803 = new Integer(1803);
+    public Object testField1804 = new Integer(1804);
+    public Object testField1805 = new Integer(1805);
+    public Object testField1806 = new Integer(1806);
+    public Object testField1807 = new Integer(1807);
+    public Object testField1808 = new Integer(1808);
+    public Object testField1809 = new Integer(1809);
+    public Object testField1810 = new Integer(1810);
+    public Object testField1811 = new Integer(1811);
+    public Object testField1812 = new Integer(1812);
+    public Object testField1813 = new Integer(1813);
+    public Object testField1814 = new Integer(1814);
+    public Object testField1815 = new Integer(1815);
+    public Object testField1816 = new Integer(1816);
+    public Object testField1817 = new Integer(1817);
+    public Object testField1818 = new Integer(1818);
+    public Object testField1819 = new Integer(1819);
+    public Object testField1820 = new Integer(1820);
+    public Object testField1821 = new Integer(1821);
+    public Object testField1822 = new Integer(1822);
+    public Object testField1823 = new Integer(1823);
+    public Object testField1824 = new Integer(1824);
+    public Object testField1825 = new Integer(1825);
+    public Object testField1826 = new Integer(1826);
+    public Object testField1827 = new Integer(1827);
+    public Object testField1828 = new Integer(1828);
+    public Object testField1829 = new Integer(1829);
+    public Object testField1830 = new Integer(1830);
+    public Object testField1831 = new Integer(1831);
+    public Object testField1832 = new Integer(1832);
+    public Object testField1833 = new Integer(1833);
+    public Object testField1834 = new Integer(1834);
+    public Object testField1835 = new Integer(1835);
+    public Object testField1836 = new Integer(1836);
+    public Object testField1837 = new Integer(1837);
+    public Object testField1838 = new Integer(1838);
+    public Object testField1839 = new Integer(1839);
+    public Object testField1840 = new Integer(1840);
+    public Object testField1841 = new Integer(1841);
+    public Object testField1842 = new Integer(1842);
+    public Object testField1843 = new Integer(1843);
+    public Object testField1844 = new Integer(1844);
+    public Object testField1845 = new Integer(1845);
+    public Object testField1846 = new Integer(1846);
+    public Object testField1847 = new Integer(1847);
+    public Object testField1848 = new Integer(1848);
+    public Object testField1849 = new Integer(1849);
+    public Object testField1850 = new Integer(1850);
+    public Object testField1851 = new Integer(1851);
+    public Object testField1852 = new Integer(1852);
+    public Object testField1853 = new Integer(1853);
+    public Object testField1854 = new Integer(1854);
+    public Object testField1855 = new Integer(1855);
+    public Object testField1856 = new Integer(1856);
+    public Object testField1857 = new Integer(1857);
+    public Object testField1858 = new Integer(1858);
+    public Object testField1859 = new Integer(1859);
+    public Object testField1860 = new Integer(1860);
+    public Object testField1861 = new Integer(1861);
+    public Object testField1862 = new Integer(1862);
+    public Object testField1863 = new Integer(1863);
+    public Object testField1864 = new Integer(1864);
+    public Object testField1865 = new Integer(1865);
+    public Object testField1866 = new Integer(1866);
+    public Object testField1867 = new Integer(1867);
+    public Object testField1868 = new Integer(1868);
+    public Object testField1869 = new Integer(1869);
+    public Object testField1870 = new Integer(1870);
+    public Object testField1871 = new Integer(1871);
+    public Object testField1872 = new Integer(1872);
+    public Object testField1873 = new Integer(1873);
+    public Object testField1874 = new Integer(1874);
+    public Object testField1875 = new Integer(1875);
+    public Object testField1876 = new Integer(1876);
+    public Object testField1877 = new Integer(1877);
+    public Object testField1878 = new Integer(1878);
+    public Object testField1879 = new Integer(1879);
+    public Object testField1880 = new Integer(1880);
+    public Object testField1881 = new Integer(1881);
+    public Object testField1882 = new Integer(1882);
+    public Object testField1883 = new Integer(1883);
+    public Object testField1884 = new Integer(1884);
+    public Object testField1885 = new Integer(1885);
+    public Object testField1886 = new Integer(1886);
+    public Object testField1887 = new Integer(1887);
+    public Object testField1888 = new Integer(1888);
+    public Object testField1889 = new Integer(1889);
+    public Object testField1890 = new Integer(1890);
+    public Object testField1891 = new Integer(1891);
+    public Object testField1892 = new Integer(1892);
+    public Object testField1893 = new Integer(1893);
+    public Object testField1894 = new Integer(1894);
+    public Object testField1895 = new Integer(1895);
+    public Object testField1896 = new Integer(1896);
+    public Object testField1897 = new Integer(1897);
+    public Object testField1898 = new Integer(1898);
+    public Object testField1899 = new Integer(1899);
+    public Object testField1900 = new Integer(1900);
+    public Object testField1901 = new Integer(1901);
+    public Object testField1902 = new Integer(1902);
+    public Object testField1903 = new Integer(1903);
+    public Object testField1904 = new Integer(1904);
+    public Object testField1905 = new Integer(1905);
+    public Object testField1906 = new Integer(1906);
+    public Object testField1907 = new Integer(1907);
+    public Object testField1908 = new Integer(1908);
+    public Object testField1909 = new Integer(1909);
+    public Object testField1910 = new Integer(1910);
+    public Object testField1911 = new Integer(1911);
+    public Object testField1912 = new Integer(1912);
+    public Object testField1913 = new Integer(1913);
+    public Object testField1914 = new Integer(1914);
+    public Object testField1915 = new Integer(1915);
+    public Object testField1916 = new Integer(1916);
+    public Object testField1917 = new Integer(1917);
+    public Object testField1918 = new Integer(1918);
+    public Object testField1919 = new Integer(1919);
+    public Object testField1920 = new Integer(1920);
+    public Object testField1921 = new Integer(1921);
+    public Object testField1922 = new Integer(1922);
+    public Object testField1923 = new Integer(1923);
+    public Object testField1924 = new Integer(1924);
+    public Object testField1925 = new Integer(1925);
+    public Object testField1926 = new Integer(1926);
+    public Object testField1927 = new Integer(1927);
+    public Object testField1928 = new Integer(1928);
+    public Object testField1929 = new Integer(1929);
+    public Object testField1930 = new Integer(1930);
+    public Object testField1931 = new Integer(1931);
+    public Object testField1932 = new Integer(1932);
+    public Object testField1933 = new Integer(1933);
+    public Object testField1934 = new Integer(1934);
+    public Object testField1935 = new Integer(1935);
+    public Object testField1936 = new Integer(1936);
+    public Object testField1937 = new Integer(1937);
+    public Object testField1938 = new Integer(1938);
+    public Object testField1939 = new Integer(1939);
+    public Object testField1940 = new Integer(1940);
+    public Object testField1941 = new Integer(1941);
+    public Object testField1942 = new Integer(1942);
+    public Object testField1943 = new Integer(1943);
+    public Object testField1944 = new Integer(1944);
+    public Object testField1945 = new Integer(1945);
+    public Object testField1946 = new Integer(1946);
+    public Object testField1947 = new Integer(1947);
+    public Object testField1948 = new Integer(1948);
+    public Object testField1949 = new Integer(1949);
+    public Object testField1950 = new Integer(1950);
+    public Object testField1951 = new Integer(1951);
+    public Object testField1952 = new Integer(1952);
+    public Object testField1953 = new Integer(1953);
+    public Object testField1954 = new Integer(1954);
+    public Object testField1955 = new Integer(1955);
+    public Object testField1956 = new Integer(1956);
+    public Object testField1957 = new Integer(1957);
+    public Object testField1958 = new Integer(1958);
+    public Object testField1959 = new Integer(1959);
+    public Object testField1960 = new Integer(1960);
+    public Object testField1961 = new Integer(1961);
+    public Object testField1962 = new Integer(1962);
+    public Object testField1963 = new Integer(1963);
+    public Object testField1964 = new Integer(1964);
+    public Object testField1965 = new Integer(1965);
+    public Object testField1966 = new Integer(1966);
+    public Object testField1967 = new Integer(1967);
+    public Object testField1968 = new Integer(1968);
+    public Object testField1969 = new Integer(1969);
+    public Object testField1970 = new Integer(1970);
+    public Object testField1971 = new Integer(1971);
+    public Object testField1972 = new Integer(1972);
+    public Object testField1973 = new Integer(1973);
+    public Object testField1974 = new Integer(1974);
+    public Object testField1975 = new Integer(1975);
+    public Object testField1976 = new Integer(1976);
+    public Object testField1977 = new Integer(1977);
+    public Object testField1978 = new Integer(1978);
+    public Object testField1979 = new Integer(1979);
+    public Object testField1980 = new Integer(1980);
+    public Object testField1981 = new Integer(1981);
+    public Object testField1982 = new Integer(1982);
+    public Object testField1983 = new Integer(1983);
+    public Object testField1984 = new Integer(1984);
+    public Object testField1985 = new Integer(1985);
+    public Object testField1986 = new Integer(1986);
+    public Object testField1987 = new Integer(1987);
+    public Object testField1988 = new Integer(1988);
+    public Object testField1989 = new Integer(1989);
+    public Object testField1990 = new Integer(1990);
+    public Object testField1991 = new Integer(1991);
+    public Object testField1992 = new Integer(1992);
+    public Object testField1993 = new Integer(1993);
+    public Object testField1994 = new Integer(1994);
+    public Object testField1995 = new Integer(1995);
+    public Object testField1996 = new Integer(1996);
+    public Object testField1997 = new Integer(1997);
+    public Object testField1998 = new Integer(1998);
+    public Object testField1999 = new Integer(1999);
+}
diff --git a/test/160-read-barrier-stress/src/ManyFieldsBase2.java b/test/160-read-barrier-stress/src/ManyFieldsBase2.java
new file mode 100644
index 0000000..54bbe99
--- /dev/null
+++ b/test/160-read-barrier-stress/src/ManyFieldsBase2.java
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+class ManyFieldsBase2 extends ManyFieldsBase1 {
+    public Object testField2000 = new Integer(2000);
+    public Object testField2001 = new Integer(2001);
+    public Object testField2002 = new Integer(2002);
+    public Object testField2003 = new Integer(2003);
+    public Object testField2004 = new Integer(2004);
+    public Object testField2005 = new Integer(2005);
+    public Object testField2006 = new Integer(2006);
+    public Object testField2007 = new Integer(2007);
+    public Object testField2008 = new Integer(2008);
+    public Object testField2009 = new Integer(2009);
+    public Object testField2010 = new Integer(2010);
+    public Object testField2011 = new Integer(2011);
+    public Object testField2012 = new Integer(2012);
+    public Object testField2013 = new Integer(2013);
+    public Object testField2014 = new Integer(2014);
+    public Object testField2015 = new Integer(2015);
+    public Object testField2016 = new Integer(2016);
+    public Object testField2017 = new Integer(2017);
+    public Object testField2018 = new Integer(2018);
+    public Object testField2019 = new Integer(2019);
+    public Object testField2020 = new Integer(2020);
+    public Object testField2021 = new Integer(2021);
+    public Object testField2022 = new Integer(2022);
+    public Object testField2023 = new Integer(2023);
+    public Object testField2024 = new Integer(2024);
+    public Object testField2025 = new Integer(2025);
+    public Object testField2026 = new Integer(2026);
+    public Object testField2027 = new Integer(2027);
+    public Object testField2028 = new Integer(2028);
+    public Object testField2029 = new Integer(2029);
+    public Object testField2030 = new Integer(2030);
+    public Object testField2031 = new Integer(2031);
+    public Object testField2032 = new Integer(2032);
+    public Object testField2033 = new Integer(2033);
+    public Object testField2034 = new Integer(2034);
+    public Object testField2035 = new Integer(2035);
+    public Object testField2036 = new Integer(2036);
+    public Object testField2037 = new Integer(2037);
+    public Object testField2038 = new Integer(2038);
+    public Object testField2039 = new Integer(2039);
+    public Object testField2040 = new Integer(2040);
+    public Object testField2041 = new Integer(2041);
+    public Object testField2042 = new Integer(2042);
+    public Object testField2043 = new Integer(2043);
+    public Object testField2044 = new Integer(2044);
+    public Object testField2045 = new Integer(2045);
+    public Object testField2046 = new Integer(2046);
+    public Object testField2047 = new Integer(2047);
+    public Object testField2048 = new Integer(2048);
+    public Object testField2049 = new Integer(2049);
+    public Object testField2050 = new Integer(2050);
+    public Object testField2051 = new Integer(2051);
+    public Object testField2052 = new Integer(2052);
+    public Object testField2053 = new Integer(2053);
+    public Object testField2054 = new Integer(2054);
+    public Object testField2055 = new Integer(2055);
+    public Object testField2056 = new Integer(2056);
+    public Object testField2057 = new Integer(2057);
+    public Object testField2058 = new Integer(2058);
+    public Object testField2059 = new Integer(2059);
+    public Object testField2060 = new Integer(2060);
+    public Object testField2061 = new Integer(2061);
+    public Object testField2062 = new Integer(2062);
+    public Object testField2063 = new Integer(2063);
+    public Object testField2064 = new Integer(2064);
+    public Object testField2065 = new Integer(2065);
+    public Object testField2066 = new Integer(2066);
+    public Object testField2067 = new Integer(2067);
+    public Object testField2068 = new Integer(2068);
+    public Object testField2069 = new Integer(2069);
+    public Object testField2070 = new Integer(2070);
+    public Object testField2071 = new Integer(2071);
+    public Object testField2072 = new Integer(2072);
+    public Object testField2073 = new Integer(2073);
+    public Object testField2074 = new Integer(2074);
+    public Object testField2075 = new Integer(2075);
+    public Object testField2076 = new Integer(2076);
+    public Object testField2077 = new Integer(2077);
+    public Object testField2078 = new Integer(2078);
+    public Object testField2079 = new Integer(2079);
+    public Object testField2080 = new Integer(2080);
+    public Object testField2081 = new Integer(2081);
+    public Object testField2082 = new Integer(2082);
+    public Object testField2083 = new Integer(2083);
+    public Object testField2084 = new Integer(2084);
+    public Object testField2085 = new Integer(2085);
+    public Object testField2086 = new Integer(2086);
+    public Object testField2087 = new Integer(2087);
+    public Object testField2088 = new Integer(2088);
+    public Object testField2089 = new Integer(2089);
+    public Object testField2090 = new Integer(2090);
+    public Object testField2091 = new Integer(2091);
+    public Object testField2092 = new Integer(2092);
+    public Object testField2093 = new Integer(2093);
+    public Object testField2094 = new Integer(2094);
+    public Object testField2095 = new Integer(2095);
+    public Object testField2096 = new Integer(2096);
+    public Object testField2097 = new Integer(2097);
+    public Object testField2098 = new Integer(2098);
+    public Object testField2099 = new Integer(2099);
+    public Object testField2100 = new Integer(2100);
+    public Object testField2101 = new Integer(2101);
+    public Object testField2102 = new Integer(2102);
+    public Object testField2103 = new Integer(2103);
+    public Object testField2104 = new Integer(2104);
+    public Object testField2105 = new Integer(2105);
+    public Object testField2106 = new Integer(2106);
+    public Object testField2107 = new Integer(2107);
+    public Object testField2108 = new Integer(2108);
+    public Object testField2109 = new Integer(2109);
+    public Object testField2110 = new Integer(2110);
+    public Object testField2111 = new Integer(2111);
+    public Object testField2112 = new Integer(2112);
+    public Object testField2113 = new Integer(2113);
+    public Object testField2114 = new Integer(2114);
+    public Object testField2115 = new Integer(2115);
+    public Object testField2116 = new Integer(2116);
+    public Object testField2117 = new Integer(2117);
+    public Object testField2118 = new Integer(2118);
+    public Object testField2119 = new Integer(2119);
+    public Object testField2120 = new Integer(2120);
+    public Object testField2121 = new Integer(2121);
+    public Object testField2122 = new Integer(2122);
+    public Object testField2123 = new Integer(2123);
+    public Object testField2124 = new Integer(2124);
+    public Object testField2125 = new Integer(2125);
+    public Object testField2126 = new Integer(2126);
+    public Object testField2127 = new Integer(2127);
+    public Object testField2128 = new Integer(2128);
+    public Object testField2129 = new Integer(2129);
+    public Object testField2130 = new Integer(2130);
+    public Object testField2131 = new Integer(2131);
+    public Object testField2132 = new Integer(2132);
+    public Object testField2133 = new Integer(2133);
+    public Object testField2134 = new Integer(2134);
+    public Object testField2135 = new Integer(2135);
+    public Object testField2136 = new Integer(2136);
+    public Object testField2137 = new Integer(2137);
+    public Object testField2138 = new Integer(2138);
+    public Object testField2139 = new Integer(2139);
+    public Object testField2140 = new Integer(2140);
+    public Object testField2141 = new Integer(2141);
+    public Object testField2142 = new Integer(2142);
+    public Object testField2143 = new Integer(2143);
+    public Object testField2144 = new Integer(2144);
+    public Object testField2145 = new Integer(2145);
+    public Object testField2146 = new Integer(2146);
+    public Object testField2147 = new Integer(2147);
+    public Object testField2148 = new Integer(2148);
+    public Object testField2149 = new Integer(2149);
+    public Object testField2150 = new Integer(2150);
+    public Object testField2151 = new Integer(2151);
+    public Object testField2152 = new Integer(2152);
+    public Object testField2153 = new Integer(2153);
+    public Object testField2154 = new Integer(2154);
+    public Object testField2155 = new Integer(2155);
+    public Object testField2156 = new Integer(2156);
+    public Object testField2157 = new Integer(2157);
+    public Object testField2158 = new Integer(2158);
+    public Object testField2159 = new Integer(2159);
+    public Object testField2160 = new Integer(2160);
+    public Object testField2161 = new Integer(2161);
+    public Object testField2162 = new Integer(2162);
+    public Object testField2163 = new Integer(2163);
+    public Object testField2164 = new Integer(2164);
+    public Object testField2165 = new Integer(2165);
+    public Object testField2166 = new Integer(2166);
+    public Object testField2167 = new Integer(2167);
+    public Object testField2168 = new Integer(2168);
+    public Object testField2169 = new Integer(2169);
+    public Object testField2170 = new Integer(2170);
+    public Object testField2171 = new Integer(2171);
+    public Object testField2172 = new Integer(2172);
+    public Object testField2173 = new Integer(2173);
+    public Object testField2174 = new Integer(2174);
+    public Object testField2175 = new Integer(2175);
+    public Object testField2176 = new Integer(2176);
+    public Object testField2177 = new Integer(2177);
+    public Object testField2178 = new Integer(2178);
+    public Object testField2179 = new Integer(2179);
+    public Object testField2180 = new Integer(2180);
+    public Object testField2181 = new Integer(2181);
+    public Object testField2182 = new Integer(2182);
+    public Object testField2183 = new Integer(2183);
+    public Object testField2184 = new Integer(2184);
+    public Object testField2185 = new Integer(2185);
+    public Object testField2186 = new Integer(2186);
+    public Object testField2187 = new Integer(2187);
+    public Object testField2188 = new Integer(2188);
+    public Object testField2189 = new Integer(2189);
+    public Object testField2190 = new Integer(2190);
+    public Object testField2191 = new Integer(2191);
+    public Object testField2192 = new Integer(2192);
+    public Object testField2193 = new Integer(2193);
+    public Object testField2194 = new Integer(2194);
+    public Object testField2195 = new Integer(2195);
+    public Object testField2196 = new Integer(2196);
+    public Object testField2197 = new Integer(2197);
+    public Object testField2198 = new Integer(2198);
+    public Object testField2199 = new Integer(2199);
+    public Object testField2200 = new Integer(2200);
+    public Object testField2201 = new Integer(2201);
+    public Object testField2202 = new Integer(2202);
+    public Object testField2203 = new Integer(2203);
+    public Object testField2204 = new Integer(2204);
+    public Object testField2205 = new Integer(2205);
+    public Object testField2206 = new Integer(2206);
+    public Object testField2207 = new Integer(2207);
+    public Object testField2208 = new Integer(2208);
+    public Object testField2209 = new Integer(2209);
+    public Object testField2210 = new Integer(2210);
+    public Object testField2211 = new Integer(2211);
+    public Object testField2212 = new Integer(2212);
+    public Object testField2213 = new Integer(2213);
+    public Object testField2214 = new Integer(2214);
+    public Object testField2215 = new Integer(2215);
+    public Object testField2216 = new Integer(2216);
+    public Object testField2217 = new Integer(2217);
+    public Object testField2218 = new Integer(2218);
+    public Object testField2219 = new Integer(2219);
+    public Object testField2220 = new Integer(2220);
+    public Object testField2221 = new Integer(2221);
+    public Object testField2222 = new Integer(2222);
+    public Object testField2223 = new Integer(2223);
+    public Object testField2224 = new Integer(2224);
+    public Object testField2225 = new Integer(2225);
+    public Object testField2226 = new Integer(2226);
+    public Object testField2227 = new Integer(2227);
+    public Object testField2228 = new Integer(2228);
+    public Object testField2229 = new Integer(2229);
+    public Object testField2230 = new Integer(2230);
+    public Object testField2231 = new Integer(2231);
+    public Object testField2232 = new Integer(2232);
+    public Object testField2233 = new Integer(2233);
+    public Object testField2234 = new Integer(2234);
+    public Object testField2235 = new Integer(2235);
+    public Object testField2236 = new Integer(2236);
+    public Object testField2237 = new Integer(2237);
+    public Object testField2238 = new Integer(2238);
+    public Object testField2239 = new Integer(2239);
+    public Object testField2240 = new Integer(2240);
+    public Object testField2241 = new Integer(2241);
+    public Object testField2242 = new Integer(2242);
+    public Object testField2243 = new Integer(2243);
+    public Object testField2244 = new Integer(2244);
+    public Object testField2245 = new Integer(2245);
+    public Object testField2246 = new Integer(2246);
+    public Object testField2247 = new Integer(2247);
+    public Object testField2248 = new Integer(2248);
+    public Object testField2249 = new Integer(2249);
+    public Object testField2250 = new Integer(2250);
+    public Object testField2251 = new Integer(2251);
+    public Object testField2252 = new Integer(2252);
+    public Object testField2253 = new Integer(2253);
+    public Object testField2254 = new Integer(2254);
+    public Object testField2255 = new Integer(2255);
+    public Object testField2256 = new Integer(2256);
+    public Object testField2257 = new Integer(2257);
+    public Object testField2258 = new Integer(2258);
+    public Object testField2259 = new Integer(2259);
+    public Object testField2260 = new Integer(2260);
+    public Object testField2261 = new Integer(2261);
+    public Object testField2262 = new Integer(2262);
+    public Object testField2263 = new Integer(2263);
+    public Object testField2264 = new Integer(2264);
+    public Object testField2265 = new Integer(2265);
+    public Object testField2266 = new Integer(2266);
+    public Object testField2267 = new Integer(2267);
+    public Object testField2268 = new Integer(2268);
+    public Object testField2269 = new Integer(2269);
+    public Object testField2270 = new Integer(2270);
+    public Object testField2271 = new Integer(2271);
+    public Object testField2272 = new Integer(2272);
+    public Object testField2273 = new Integer(2273);
+    public Object testField2274 = new Integer(2274);
+    public Object testField2275 = new Integer(2275);
+    public Object testField2276 = new Integer(2276);
+    public Object testField2277 = new Integer(2277);
+    public Object testField2278 = new Integer(2278);
+    public Object testField2279 = new Integer(2279);
+    public Object testField2280 = new Integer(2280);
+    public Object testField2281 = new Integer(2281);
+    public Object testField2282 = new Integer(2282);
+    public Object testField2283 = new Integer(2283);
+    public Object testField2284 = new Integer(2284);
+    public Object testField2285 = new Integer(2285);
+    public Object testField2286 = new Integer(2286);
+    public Object testField2287 = new Integer(2287);
+    public Object testField2288 = new Integer(2288);
+    public Object testField2289 = new Integer(2289);
+    public Object testField2290 = new Integer(2290);
+    public Object testField2291 = new Integer(2291);
+    public Object testField2292 = new Integer(2292);
+    public Object testField2293 = new Integer(2293);
+    public Object testField2294 = new Integer(2294);
+    public Object testField2295 = new Integer(2295);
+    public Object testField2296 = new Integer(2296);
+    public Object testField2297 = new Integer(2297);
+    public Object testField2298 = new Integer(2298);
+    public Object testField2299 = new Integer(2299);
+    public Object testField2300 = new Integer(2300);
+    public Object testField2301 = new Integer(2301);
+    public Object testField2302 = new Integer(2302);
+    public Object testField2303 = new Integer(2303);
+    public Object testField2304 = new Integer(2304);
+    public Object testField2305 = new Integer(2305);
+    public Object testField2306 = new Integer(2306);
+    public Object testField2307 = new Integer(2307);
+    public Object testField2308 = new Integer(2308);
+    public Object testField2309 = new Integer(2309);
+    public Object testField2310 = new Integer(2310);
+    public Object testField2311 = new Integer(2311);
+    public Object testField2312 = new Integer(2312);
+    public Object testField2313 = new Integer(2313);
+    public Object testField2314 = new Integer(2314);
+    public Object testField2315 = new Integer(2315);
+    public Object testField2316 = new Integer(2316);
+    public Object testField2317 = new Integer(2317);
+    public Object testField2318 = new Integer(2318);
+    public Object testField2319 = new Integer(2319);
+    public Object testField2320 = new Integer(2320);
+    public Object testField2321 = new Integer(2321);
+    public Object testField2322 = new Integer(2322);
+    public Object testField2323 = new Integer(2323);
+    public Object testField2324 = new Integer(2324);
+    public Object testField2325 = new Integer(2325);
+    public Object testField2326 = new Integer(2326);
+    public Object testField2327 = new Integer(2327);
+    public Object testField2328 = new Integer(2328);
+    public Object testField2329 = new Integer(2329);
+    public Object testField2330 = new Integer(2330);
+    public Object testField2331 = new Integer(2331);
+    public Object testField2332 = new Integer(2332);
+    public Object testField2333 = new Integer(2333);
+    public Object testField2334 = new Integer(2334);
+    public Object testField2335 = new Integer(2335);
+    public Object testField2336 = new Integer(2336);
+    public Object testField2337 = new Integer(2337);
+    public Object testField2338 = new Integer(2338);
+    public Object testField2339 = new Integer(2339);
+    public Object testField2340 = new Integer(2340);
+    public Object testField2341 = new Integer(2341);
+    public Object testField2342 = new Integer(2342);
+    public Object testField2343 = new Integer(2343);
+    public Object testField2344 = new Integer(2344);
+    public Object testField2345 = new Integer(2345);
+    public Object testField2346 = new Integer(2346);
+    public Object testField2347 = new Integer(2347);
+    public Object testField2348 = new Integer(2348);
+    public Object testField2349 = new Integer(2349);
+    public Object testField2350 = new Integer(2350);
+    public Object testField2351 = new Integer(2351);
+    public Object testField2352 = new Integer(2352);
+    public Object testField2353 = new Integer(2353);
+    public Object testField2354 = new Integer(2354);
+    public Object testField2355 = new Integer(2355);
+    public Object testField2356 = new Integer(2356);
+    public Object testField2357 = new Integer(2357);
+    public Object testField2358 = new Integer(2358);
+    public Object testField2359 = new Integer(2359);
+    public Object testField2360 = new Integer(2360);
+    public Object testField2361 = new Integer(2361);
+    public Object testField2362 = new Integer(2362);
+    public Object testField2363 = new Integer(2363);
+    public Object testField2364 = new Integer(2364);
+    public Object testField2365 = new Integer(2365);
+    public Object testField2366 = new Integer(2366);
+    public Object testField2367 = new Integer(2367);
+    public Object testField2368 = new Integer(2368);
+    public Object testField2369 = new Integer(2369);
+    public Object testField2370 = new Integer(2370);
+    public Object testField2371 = new Integer(2371);
+    public Object testField2372 = new Integer(2372);
+    public Object testField2373 = new Integer(2373);
+    public Object testField2374 = new Integer(2374);
+    public Object testField2375 = new Integer(2375);
+    public Object testField2376 = new Integer(2376);
+    public Object testField2377 = new Integer(2377);
+    public Object testField2378 = new Integer(2378);
+    public Object testField2379 = new Integer(2379);
+    public Object testField2380 = new Integer(2380);
+    public Object testField2381 = new Integer(2381);
+    public Object testField2382 = new Integer(2382);
+    public Object testField2383 = new Integer(2383);
+    public Object testField2384 = new Integer(2384);
+    public Object testField2385 = new Integer(2385);
+    public Object testField2386 = new Integer(2386);
+    public Object testField2387 = new Integer(2387);
+    public Object testField2388 = new Integer(2388);
+    public Object testField2389 = new Integer(2389);
+    public Object testField2390 = new Integer(2390);
+    public Object testField2391 = new Integer(2391);
+    public Object testField2392 = new Integer(2392);
+    public Object testField2393 = new Integer(2393);
+    public Object testField2394 = new Integer(2394);
+    public Object testField2395 = new Integer(2395);
+    public Object testField2396 = new Integer(2396);
+    public Object testField2397 = new Integer(2397);
+    public Object testField2398 = new Integer(2398);
+    public Object testField2399 = new Integer(2399);
+    public Object testField2400 = new Integer(2400);
+    public Object testField2401 = new Integer(2401);
+    public Object testField2402 = new Integer(2402);
+    public Object testField2403 = new Integer(2403);
+    public Object testField2404 = new Integer(2404);
+    public Object testField2405 = new Integer(2405);
+    public Object testField2406 = new Integer(2406);
+    public Object testField2407 = new Integer(2407);
+    public Object testField2408 = new Integer(2408);
+    public Object testField2409 = new Integer(2409);
+    public Object testField2410 = new Integer(2410);
+    public Object testField2411 = new Integer(2411);
+    public Object testField2412 = new Integer(2412);
+    public Object testField2413 = new Integer(2413);
+    public Object testField2414 = new Integer(2414);
+    public Object testField2415 = new Integer(2415);
+    public Object testField2416 = new Integer(2416);
+    public Object testField2417 = new Integer(2417);
+    public Object testField2418 = new Integer(2418);
+    public Object testField2419 = new Integer(2419);
+    public Object testField2420 = new Integer(2420);
+    public Object testField2421 = new Integer(2421);
+    public Object testField2422 = new Integer(2422);
+    public Object testField2423 = new Integer(2423);
+    public Object testField2424 = new Integer(2424);
+    public Object testField2425 = new Integer(2425);
+    public Object testField2426 = new Integer(2426);
+    public Object testField2427 = new Integer(2427);
+    public Object testField2428 = new Integer(2428);
+    public Object testField2429 = new Integer(2429);
+    public Object testField2430 = new Integer(2430);
+    public Object testField2431 = new Integer(2431);
+    public Object testField2432 = new Integer(2432);
+    public Object testField2433 = new Integer(2433);
+    public Object testField2434 = new Integer(2434);
+    public Object testField2435 = new Integer(2435);
+    public Object testField2436 = new Integer(2436);
+    public Object testField2437 = new Integer(2437);
+    public Object testField2438 = new Integer(2438);
+    public Object testField2439 = new Integer(2439);
+    public Object testField2440 = new Integer(2440);
+    public Object testField2441 = new Integer(2441);
+    public Object testField2442 = new Integer(2442);
+    public Object testField2443 = new Integer(2443);
+    public Object testField2444 = new Integer(2444);
+    public Object testField2445 = new Integer(2445);
+    public Object testField2446 = new Integer(2446);
+    public Object testField2447 = new Integer(2447);
+    public Object testField2448 = new Integer(2448);
+    public Object testField2449 = new Integer(2449);
+    public Object testField2450 = new Integer(2450);
+    public Object testField2451 = new Integer(2451);
+    public Object testField2452 = new Integer(2452);
+    public Object testField2453 = new Integer(2453);
+    public Object testField2454 = new Integer(2454);
+    public Object testField2455 = new Integer(2455);
+    public Object testField2456 = new Integer(2456);
+    public Object testField2457 = new Integer(2457);
+    public Object testField2458 = new Integer(2458);
+    public Object testField2459 = new Integer(2459);
+    public Object testField2460 = new Integer(2460);
+    public Object testField2461 = new Integer(2461);
+    public Object testField2462 = new Integer(2462);
+    public Object testField2463 = new Integer(2463);
+    public Object testField2464 = new Integer(2464);
+    public Object testField2465 = new Integer(2465);
+    public Object testField2466 = new Integer(2466);
+    public Object testField2467 = new Integer(2467);
+    public Object testField2468 = new Integer(2468);
+    public Object testField2469 = new Integer(2469);
+    public Object testField2470 = new Integer(2470);
+    public Object testField2471 = new Integer(2471);
+    public Object testField2472 = new Integer(2472);
+    public Object testField2473 = new Integer(2473);
+    public Object testField2474 = new Integer(2474);
+    public Object testField2475 = new Integer(2475);
+    public Object testField2476 = new Integer(2476);
+    public Object testField2477 = new Integer(2477);
+    public Object testField2478 = new Integer(2478);
+    public Object testField2479 = new Integer(2479);
+    public Object testField2480 = new Integer(2480);
+    public Object testField2481 = new Integer(2481);
+    public Object testField2482 = new Integer(2482);
+    public Object testField2483 = new Integer(2483);
+    public Object testField2484 = new Integer(2484);
+    public Object testField2485 = new Integer(2485);
+    public Object testField2486 = new Integer(2486);
+    public Object testField2487 = new Integer(2487);
+    public Object testField2488 = new Integer(2488);
+    public Object testField2489 = new Integer(2489);
+    public Object testField2490 = new Integer(2490);
+    public Object testField2491 = new Integer(2491);
+    public Object testField2492 = new Integer(2492);
+    public Object testField2493 = new Integer(2493);
+    public Object testField2494 = new Integer(2494);
+    public Object testField2495 = new Integer(2495);
+    public Object testField2496 = new Integer(2496);
+    public Object testField2497 = new Integer(2497);
+    public Object testField2498 = new Integer(2498);
+    public Object testField2499 = new Integer(2499);
+    public Object testField2500 = new Integer(2500);
+    public Object testField2501 = new Integer(2501);
+    public Object testField2502 = new Integer(2502);
+    public Object testField2503 = new Integer(2503);
+    public Object testField2504 = new Integer(2504);
+    public Object testField2505 = new Integer(2505);
+    public Object testField2506 = new Integer(2506);
+    public Object testField2507 = new Integer(2507);
+    public Object testField2508 = new Integer(2508);
+    public Object testField2509 = new Integer(2509);
+    public Object testField2510 = new Integer(2510);
+    public Object testField2511 = new Integer(2511);
+    public Object testField2512 = new Integer(2512);
+    public Object testField2513 = new Integer(2513);
+    public Object testField2514 = new Integer(2514);
+    public Object testField2515 = new Integer(2515);
+    public Object testField2516 = new Integer(2516);
+    public Object testField2517 = new Integer(2517);
+    public Object testField2518 = new Integer(2518);
+    public Object testField2519 = new Integer(2519);
+    public Object testField2520 = new Integer(2520);
+    public Object testField2521 = new Integer(2521);
+    public Object testField2522 = new Integer(2522);
+    public Object testField2523 = new Integer(2523);
+    public Object testField2524 = new Integer(2524);
+    public Object testField2525 = new Integer(2525);
+    public Object testField2526 = new Integer(2526);
+    public Object testField2527 = new Integer(2527);
+    public Object testField2528 = new Integer(2528);
+    public Object testField2529 = new Integer(2529);
+    public Object testField2530 = new Integer(2530);
+    public Object testField2531 = new Integer(2531);
+    public Object testField2532 = new Integer(2532);
+    public Object testField2533 = new Integer(2533);
+    public Object testField2534 = new Integer(2534);
+    public Object testField2535 = new Integer(2535);
+    public Object testField2536 = new Integer(2536);
+    public Object testField2537 = new Integer(2537);
+    public Object testField2538 = new Integer(2538);
+    public Object testField2539 = new Integer(2539);
+    public Object testField2540 = new Integer(2540);
+    public Object testField2541 = new Integer(2541);
+    public Object testField2542 = new Integer(2542);
+    public Object testField2543 = new Integer(2543);
+    public Object testField2544 = new Integer(2544);
+    public Object testField2545 = new Integer(2545);
+    public Object testField2546 = new Integer(2546);
+    public Object testField2547 = new Integer(2547);
+    public Object testField2548 = new Integer(2548);
+    public Object testField2549 = new Integer(2549);
+    public Object testField2550 = new Integer(2550);
+    public Object testField2551 = new Integer(2551);
+    public Object testField2552 = new Integer(2552);
+    public Object testField2553 = new Integer(2553);
+    public Object testField2554 = new Integer(2554);
+    public Object testField2555 = new Integer(2555);
+    public Object testField2556 = new Integer(2556);
+    public Object testField2557 = new Integer(2557);
+    public Object testField2558 = new Integer(2558);
+    public Object testField2559 = new Integer(2559);
+    public Object testField2560 = new Integer(2560);
+    public Object testField2561 = new Integer(2561);
+    public Object testField2562 = new Integer(2562);
+    public Object testField2563 = new Integer(2563);
+    public Object testField2564 = new Integer(2564);
+    public Object testField2565 = new Integer(2565);
+    public Object testField2566 = new Integer(2566);
+    public Object testField2567 = new Integer(2567);
+    public Object testField2568 = new Integer(2568);
+    public Object testField2569 = new Integer(2569);
+    public Object testField2570 = new Integer(2570);
+    public Object testField2571 = new Integer(2571);
+    public Object testField2572 = new Integer(2572);
+    public Object testField2573 = new Integer(2573);
+    public Object testField2574 = new Integer(2574);
+    public Object testField2575 = new Integer(2575);
+    public Object testField2576 = new Integer(2576);
+    public Object testField2577 = new Integer(2577);
+    public Object testField2578 = new Integer(2578);
+    public Object testField2579 = new Integer(2579);
+    public Object testField2580 = new Integer(2580);
+    public Object testField2581 = new Integer(2581);
+    public Object testField2582 = new Integer(2582);
+    public Object testField2583 = new Integer(2583);
+    public Object testField2584 = new Integer(2584);
+    public Object testField2585 = new Integer(2585);
+    public Object testField2586 = new Integer(2586);
+    public Object testField2587 = new Integer(2587);
+    public Object testField2588 = new Integer(2588);
+    public Object testField2589 = new Integer(2589);
+    public Object testField2590 = new Integer(2590);
+    public Object testField2591 = new Integer(2591);
+    public Object testField2592 = new Integer(2592);
+    public Object testField2593 = new Integer(2593);
+    public Object testField2594 = new Integer(2594);
+    public Object testField2595 = new Integer(2595);
+    public Object testField2596 = new Integer(2596);
+    public Object testField2597 = new Integer(2597);
+    public Object testField2598 = new Integer(2598);
+    public Object testField2599 = new Integer(2599);
+    public Object testField2600 = new Integer(2600);
+    public Object testField2601 = new Integer(2601);
+    public Object testField2602 = new Integer(2602);
+    public Object testField2603 = new Integer(2603);
+    public Object testField2604 = new Integer(2604);
+    public Object testField2605 = new Integer(2605);
+    public Object testField2606 = new Integer(2606);
+    public Object testField2607 = new Integer(2607);
+    public Object testField2608 = new Integer(2608);
+    public Object testField2609 = new Integer(2609);
+    public Object testField2610 = new Integer(2610);
+    public Object testField2611 = new Integer(2611);
+    public Object testField2612 = new Integer(2612);
+    public Object testField2613 = new Integer(2613);
+    public Object testField2614 = new Integer(2614);
+    public Object testField2615 = new Integer(2615);
+    public Object testField2616 = new Integer(2616);
+    public Object testField2617 = new Integer(2617);
+    public Object testField2618 = new Integer(2618);
+    public Object testField2619 = new Integer(2619);
+    public Object testField2620 = new Integer(2620);
+    public Object testField2621 = new Integer(2621);
+    public Object testField2622 = new Integer(2622);
+    public Object testField2623 = new Integer(2623);
+    public Object testField2624 = new Integer(2624);
+    public Object testField2625 = new Integer(2625);
+    public Object testField2626 = new Integer(2626);
+    public Object testField2627 = new Integer(2627);
+    public Object testField2628 = new Integer(2628);
+    public Object testField2629 = new Integer(2629);
+    public Object testField2630 = new Integer(2630);
+    public Object testField2631 = new Integer(2631);
+    public Object testField2632 = new Integer(2632);
+    public Object testField2633 = new Integer(2633);
+    public Object testField2634 = new Integer(2634);
+    public Object testField2635 = new Integer(2635);
+    public Object testField2636 = new Integer(2636);
+    public Object testField2637 = new Integer(2637);
+    public Object testField2638 = new Integer(2638);
+    public Object testField2639 = new Integer(2639);
+    public Object testField2640 = new Integer(2640);
+    public Object testField2641 = new Integer(2641);
+    public Object testField2642 = new Integer(2642);
+    public Object testField2643 = new Integer(2643);
+    public Object testField2644 = new Integer(2644);
+    public Object testField2645 = new Integer(2645);
+    public Object testField2646 = new Integer(2646);
+    public Object testField2647 = new Integer(2647);
+    public Object testField2648 = new Integer(2648);
+    public Object testField2649 = new Integer(2649);
+    public Object testField2650 = new Integer(2650);
+    public Object testField2651 = new Integer(2651);
+    public Object testField2652 = new Integer(2652);
+    public Object testField2653 = new Integer(2653);
+    public Object testField2654 = new Integer(2654);
+    public Object testField2655 = new Integer(2655);
+    public Object testField2656 = new Integer(2656);
+    public Object testField2657 = new Integer(2657);
+    public Object testField2658 = new Integer(2658);
+    public Object testField2659 = new Integer(2659);
+    public Object testField2660 = new Integer(2660);
+    public Object testField2661 = new Integer(2661);
+    public Object testField2662 = new Integer(2662);
+    public Object testField2663 = new Integer(2663);
+    public Object testField2664 = new Integer(2664);
+    public Object testField2665 = new Integer(2665);
+    public Object testField2666 = new Integer(2666);
+    public Object testField2667 = new Integer(2667);
+    public Object testField2668 = new Integer(2668);
+    public Object testField2669 = new Integer(2669);
+    public Object testField2670 = new Integer(2670);
+    public Object testField2671 = new Integer(2671);
+    public Object testField2672 = new Integer(2672);
+    public Object testField2673 = new Integer(2673);
+    public Object testField2674 = new Integer(2674);
+    public Object testField2675 = new Integer(2675);
+    public Object testField2676 = new Integer(2676);
+    public Object testField2677 = new Integer(2677);
+    public Object testField2678 = new Integer(2678);
+    public Object testField2679 = new Integer(2679);
+    public Object testField2680 = new Integer(2680);
+    public Object testField2681 = new Integer(2681);
+    public Object testField2682 = new Integer(2682);
+    public Object testField2683 = new Integer(2683);
+    public Object testField2684 = new Integer(2684);
+    public Object testField2685 = new Integer(2685);
+    public Object testField2686 = new Integer(2686);
+    public Object testField2687 = new Integer(2687);
+    public Object testField2688 = new Integer(2688);
+    public Object testField2689 = new Integer(2689);
+    public Object testField2690 = new Integer(2690);
+    public Object testField2691 = new Integer(2691);
+    public Object testField2692 = new Integer(2692);
+    public Object testField2693 = new Integer(2693);
+    public Object testField2694 = new Integer(2694);
+    public Object testField2695 = new Integer(2695);
+    public Object testField2696 = new Integer(2696);
+    public Object testField2697 = new Integer(2697);
+    public Object testField2698 = new Integer(2698);
+    public Object testField2699 = new Integer(2699);
+    public Object testField2700 = new Integer(2700);
+    public Object testField2701 = new Integer(2701);
+    public Object testField2702 = new Integer(2702);
+    public Object testField2703 = new Integer(2703);
+    public Object testField2704 = new Integer(2704);
+    public Object testField2705 = new Integer(2705);
+    public Object testField2706 = new Integer(2706);
+    public Object testField2707 = new Integer(2707);
+    public Object testField2708 = new Integer(2708);
+    public Object testField2709 = new Integer(2709);
+    public Object testField2710 = new Integer(2710);
+    public Object testField2711 = new Integer(2711);
+    public Object testField2712 = new Integer(2712);
+    public Object testField2713 = new Integer(2713);
+    public Object testField2714 = new Integer(2714);
+    public Object testField2715 = new Integer(2715);
+    public Object testField2716 = new Integer(2716);
+    public Object testField2717 = new Integer(2717);
+    public Object testField2718 = new Integer(2718);
+    public Object testField2719 = new Integer(2719);
+    public Object testField2720 = new Integer(2720);
+    public Object testField2721 = new Integer(2721);
+    public Object testField2722 = new Integer(2722);
+    public Object testField2723 = new Integer(2723);
+    public Object testField2724 = new Integer(2724);
+    public Object testField2725 = new Integer(2725);
+    public Object testField2726 = new Integer(2726);
+    public Object testField2727 = new Integer(2727);
+    public Object testField2728 = new Integer(2728);
+    public Object testField2729 = new Integer(2729);
+    public Object testField2730 = new Integer(2730);
+    public Object testField2731 = new Integer(2731);
+    public Object testField2732 = new Integer(2732);
+    public Object testField2733 = new Integer(2733);
+    public Object testField2734 = new Integer(2734);
+    public Object testField2735 = new Integer(2735);
+    public Object testField2736 = new Integer(2736);
+    public Object testField2737 = new Integer(2737);
+    public Object testField2738 = new Integer(2738);
+    public Object testField2739 = new Integer(2739);
+    public Object testField2740 = new Integer(2740);
+    public Object testField2741 = new Integer(2741);
+    public Object testField2742 = new Integer(2742);
+    public Object testField2743 = new Integer(2743);
+    public Object testField2744 = new Integer(2744);
+    public Object testField2745 = new Integer(2745);
+    public Object testField2746 = new Integer(2746);
+    public Object testField2747 = new Integer(2747);
+    public Object testField2748 = new Integer(2748);
+    public Object testField2749 = new Integer(2749);
+    public Object testField2750 = new Integer(2750);
+    public Object testField2751 = new Integer(2751);
+    public Object testField2752 = new Integer(2752);
+    public Object testField2753 = new Integer(2753);
+    public Object testField2754 = new Integer(2754);
+    public Object testField2755 = new Integer(2755);
+    public Object testField2756 = new Integer(2756);
+    public Object testField2757 = new Integer(2757);
+    public Object testField2758 = new Integer(2758);
+    public Object testField2759 = new Integer(2759);
+    public Object testField2760 = new Integer(2760);
+    public Object testField2761 = new Integer(2761);
+    public Object testField2762 = new Integer(2762);
+    public Object testField2763 = new Integer(2763);
+    public Object testField2764 = new Integer(2764);
+    public Object testField2765 = new Integer(2765);
+    public Object testField2766 = new Integer(2766);
+    public Object testField2767 = new Integer(2767);
+    public Object testField2768 = new Integer(2768);
+    public Object testField2769 = new Integer(2769);
+    public Object testField2770 = new Integer(2770);
+    public Object testField2771 = new Integer(2771);
+    public Object testField2772 = new Integer(2772);
+    public Object testField2773 = new Integer(2773);
+    public Object testField2774 = new Integer(2774);
+    public Object testField2775 = new Integer(2775);
+    public Object testField2776 = new Integer(2776);
+    public Object testField2777 = new Integer(2777);
+    public Object testField2778 = new Integer(2778);
+    public Object testField2779 = new Integer(2779);
+    public Object testField2780 = new Integer(2780);
+    public Object testField2781 = new Integer(2781);
+    public Object testField2782 = new Integer(2782);
+    public Object testField2783 = new Integer(2783);
+    public Object testField2784 = new Integer(2784);
+    public Object testField2785 = new Integer(2785);
+    public Object testField2786 = new Integer(2786);
+    public Object testField2787 = new Integer(2787);
+    public Object testField2788 = new Integer(2788);
+    public Object testField2789 = new Integer(2789);
+    public Object testField2790 = new Integer(2790);
+    public Object testField2791 = new Integer(2791);
+    public Object testField2792 = new Integer(2792);
+    public Object testField2793 = new Integer(2793);
+    public Object testField2794 = new Integer(2794);
+    public Object testField2795 = new Integer(2795);
+    public Object testField2796 = new Integer(2796);
+    public Object testField2797 = new Integer(2797);
+    public Object testField2798 = new Integer(2798);
+    public Object testField2799 = new Integer(2799);
+    public Object testField2800 = new Integer(2800);
+    public Object testField2801 = new Integer(2801);
+    public Object testField2802 = new Integer(2802);
+    public Object testField2803 = new Integer(2803);
+    public Object testField2804 = new Integer(2804);
+    public Object testField2805 = new Integer(2805);
+    public Object testField2806 = new Integer(2806);
+    public Object testField2807 = new Integer(2807);
+    public Object testField2808 = new Integer(2808);
+    public Object testField2809 = new Integer(2809);
+    public Object testField2810 = new Integer(2810);
+    public Object testField2811 = new Integer(2811);
+    public Object testField2812 = new Integer(2812);
+    public Object testField2813 = new Integer(2813);
+    public Object testField2814 = new Integer(2814);
+    public Object testField2815 = new Integer(2815);
+    public Object testField2816 = new Integer(2816);
+    public Object testField2817 = new Integer(2817);
+    public Object testField2818 = new Integer(2818);
+    public Object testField2819 = new Integer(2819);
+    public Object testField2820 = new Integer(2820);
+    public Object testField2821 = new Integer(2821);
+    public Object testField2822 = new Integer(2822);
+    public Object testField2823 = new Integer(2823);
+    public Object testField2824 = new Integer(2824);
+    public Object testField2825 = new Integer(2825);
+    public Object testField2826 = new Integer(2826);
+    public Object testField2827 = new Integer(2827);
+    public Object testField2828 = new Integer(2828);
+    public Object testField2829 = new Integer(2829);
+    public Object testField2830 = new Integer(2830);
+    public Object testField2831 = new Integer(2831);
+    public Object testField2832 = new Integer(2832);
+    public Object testField2833 = new Integer(2833);
+    public Object testField2834 = new Integer(2834);
+    public Object testField2835 = new Integer(2835);
+    public Object testField2836 = new Integer(2836);
+    public Object testField2837 = new Integer(2837);
+    public Object testField2838 = new Integer(2838);
+    public Object testField2839 = new Integer(2839);
+    public Object testField2840 = new Integer(2840);
+    public Object testField2841 = new Integer(2841);
+    public Object testField2842 = new Integer(2842);
+    public Object testField2843 = new Integer(2843);
+    public Object testField2844 = new Integer(2844);
+    public Object testField2845 = new Integer(2845);
+    public Object testField2846 = new Integer(2846);
+    public Object testField2847 = new Integer(2847);
+    public Object testField2848 = new Integer(2848);
+    public Object testField2849 = new Integer(2849);
+    public Object testField2850 = new Integer(2850);
+    public Object testField2851 = new Integer(2851);
+    public Object testField2852 = new Integer(2852);
+    public Object testField2853 = new Integer(2853);
+    public Object testField2854 = new Integer(2854);
+    public Object testField2855 = new Integer(2855);
+    public Object testField2856 = new Integer(2856);
+    public Object testField2857 = new Integer(2857);
+    public Object testField2858 = new Integer(2858);
+    public Object testField2859 = new Integer(2859);
+    public Object testField2860 = new Integer(2860);
+    public Object testField2861 = new Integer(2861);
+    public Object testField2862 = new Integer(2862);
+    public Object testField2863 = new Integer(2863);
+    public Object testField2864 = new Integer(2864);
+    public Object testField2865 = new Integer(2865);
+    public Object testField2866 = new Integer(2866);
+    public Object testField2867 = new Integer(2867);
+    public Object testField2868 = new Integer(2868);
+    public Object testField2869 = new Integer(2869);
+    public Object testField2870 = new Integer(2870);
+    public Object testField2871 = new Integer(2871);
+    public Object testField2872 = new Integer(2872);
+    public Object testField2873 = new Integer(2873);
+    public Object testField2874 = new Integer(2874);
+    public Object testField2875 = new Integer(2875);
+    public Object testField2876 = new Integer(2876);
+    public Object testField2877 = new Integer(2877);
+    public Object testField2878 = new Integer(2878);
+    public Object testField2879 = new Integer(2879);
+    public Object testField2880 = new Integer(2880);
+    public Object testField2881 = new Integer(2881);
+    public Object testField2882 = new Integer(2882);
+    public Object testField2883 = new Integer(2883);
+    public Object testField2884 = new Integer(2884);
+    public Object testField2885 = new Integer(2885);
+    public Object testField2886 = new Integer(2886);
+    public Object testField2887 = new Integer(2887);
+    public Object testField2888 = new Integer(2888);
+    public Object testField2889 = new Integer(2889);
+    public Object testField2890 = new Integer(2890);
+    public Object testField2891 = new Integer(2891);
+    public Object testField2892 = new Integer(2892);
+    public Object testField2893 = new Integer(2893);
+    public Object testField2894 = new Integer(2894);
+    public Object testField2895 = new Integer(2895);
+    public Object testField2896 = new Integer(2896);
+    public Object testField2897 = new Integer(2897);
+    public Object testField2898 = new Integer(2898);
+    public Object testField2899 = new Integer(2899);
+    public Object testField2900 = new Integer(2900);
+    public Object testField2901 = new Integer(2901);
+    public Object testField2902 = new Integer(2902);
+    public Object testField2903 = new Integer(2903);
+    public Object testField2904 = new Integer(2904);
+    public Object testField2905 = new Integer(2905);
+    public Object testField2906 = new Integer(2906);
+    public Object testField2907 = new Integer(2907);
+    public Object testField2908 = new Integer(2908);
+    public Object testField2909 = new Integer(2909);
+    public Object testField2910 = new Integer(2910);
+    public Object testField2911 = new Integer(2911);
+    public Object testField2912 = new Integer(2912);
+    public Object testField2913 = new Integer(2913);
+    public Object testField2914 = new Integer(2914);
+    public Object testField2915 = new Integer(2915);
+    public Object testField2916 = new Integer(2916);
+    public Object testField2917 = new Integer(2917);
+    public Object testField2918 = new Integer(2918);
+    public Object testField2919 = new Integer(2919);
+    public Object testField2920 = new Integer(2920);
+    public Object testField2921 = new Integer(2921);
+    public Object testField2922 = new Integer(2922);
+    public Object testField2923 = new Integer(2923);
+    public Object testField2924 = new Integer(2924);
+    public Object testField2925 = new Integer(2925);
+    public Object testField2926 = new Integer(2926);
+    public Object testField2927 = new Integer(2927);
+    public Object testField2928 = new Integer(2928);
+    public Object testField2929 = new Integer(2929);
+    public Object testField2930 = new Integer(2930);
+    public Object testField2931 = new Integer(2931);
+    public Object testField2932 = new Integer(2932);
+    public Object testField2933 = new Integer(2933);
+    public Object testField2934 = new Integer(2934);
+    public Object testField2935 = new Integer(2935);
+    public Object testField2936 = new Integer(2936);
+    public Object testField2937 = new Integer(2937);
+    public Object testField2938 = new Integer(2938);
+    public Object testField2939 = new Integer(2939);
+    public Object testField2940 = new Integer(2940);
+    public Object testField2941 = new Integer(2941);
+    public Object testField2942 = new Integer(2942);
+    public Object testField2943 = new Integer(2943);
+    public Object testField2944 = new Integer(2944);
+    public Object testField2945 = new Integer(2945);
+    public Object testField2946 = new Integer(2946);
+    public Object testField2947 = new Integer(2947);
+    public Object testField2948 = new Integer(2948);
+    public Object testField2949 = new Integer(2949);
+    public Object testField2950 = new Integer(2950);
+    public Object testField2951 = new Integer(2951);
+    public Object testField2952 = new Integer(2952);
+    public Object testField2953 = new Integer(2953);
+    public Object testField2954 = new Integer(2954);
+    public Object testField2955 = new Integer(2955);
+    public Object testField2956 = new Integer(2956);
+    public Object testField2957 = new Integer(2957);
+    public Object testField2958 = new Integer(2958);
+    public Object testField2959 = new Integer(2959);
+    public Object testField2960 = new Integer(2960);
+    public Object testField2961 = new Integer(2961);
+    public Object testField2962 = new Integer(2962);
+    public Object testField2963 = new Integer(2963);
+    public Object testField2964 = new Integer(2964);
+    public Object testField2965 = new Integer(2965);
+    public Object testField2966 = new Integer(2966);
+    public Object testField2967 = new Integer(2967);
+    public Object testField2968 = new Integer(2968);
+    public Object testField2969 = new Integer(2969);
+    public Object testField2970 = new Integer(2970);
+    public Object testField2971 = new Integer(2971);
+    public Object testField2972 = new Integer(2972);
+    public Object testField2973 = new Integer(2973);
+    public Object testField2974 = new Integer(2974);
+    public Object testField2975 = new Integer(2975);
+    public Object testField2976 = new Integer(2976);
+    public Object testField2977 = new Integer(2977);
+    public Object testField2978 = new Integer(2978);
+    public Object testField2979 = new Integer(2979);
+    public Object testField2980 = new Integer(2980);
+    public Object testField2981 = new Integer(2981);
+    public Object testField2982 = new Integer(2982);
+    public Object testField2983 = new Integer(2983);
+    public Object testField2984 = new Integer(2984);
+    public Object testField2985 = new Integer(2985);
+    public Object testField2986 = new Integer(2986);
+    public Object testField2987 = new Integer(2987);
+    public Object testField2988 = new Integer(2988);
+    public Object testField2989 = new Integer(2989);
+    public Object testField2990 = new Integer(2990);
+    public Object testField2991 = new Integer(2991);
+    public Object testField2992 = new Integer(2992);
+    public Object testField2993 = new Integer(2993);
+    public Object testField2994 = new Integer(2994);
+    public Object testField2995 = new Integer(2995);
+    public Object testField2996 = new Integer(2996);
+    public Object testField2997 = new Integer(2997);
+    public Object testField2998 = new Integer(2998);
+    public Object testField2999 = new Integer(2999);
+}
diff --git a/test/160-read-barrier-stress/src/ManyFieldsBase3.java b/test/160-read-barrier-stress/src/ManyFieldsBase3.java
new file mode 100644
index 0000000..e7cfaac
--- /dev/null
+++ b/test/160-read-barrier-stress/src/ManyFieldsBase3.java
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+class ManyFieldsBase3 extends ManyFieldsBase2 {
+    public Object testField3000 = new Integer(3000);
+    public Object testField3001 = new Integer(3001);
+    public Object testField3002 = new Integer(3002);
+    public Object testField3003 = new Integer(3003);
+    public Object testField3004 = new Integer(3004);
+    public Object testField3005 = new Integer(3005);
+    public Object testField3006 = new Integer(3006);
+    public Object testField3007 = new Integer(3007);
+    public Object testField3008 = new Integer(3008);
+    public Object testField3009 = new Integer(3009);
+    public Object testField3010 = new Integer(3010);
+    public Object testField3011 = new Integer(3011);
+    public Object testField3012 = new Integer(3012);
+    public Object testField3013 = new Integer(3013);
+    public Object testField3014 = new Integer(3014);
+    public Object testField3015 = new Integer(3015);
+    public Object testField3016 = new Integer(3016);
+    public Object testField3017 = new Integer(3017);
+    public Object testField3018 = new Integer(3018);
+    public Object testField3019 = new Integer(3019);
+    public Object testField3020 = new Integer(3020);
+    public Object testField3021 = new Integer(3021);
+    public Object testField3022 = new Integer(3022);
+    public Object testField3023 = new Integer(3023);
+    public Object testField3024 = new Integer(3024);
+    public Object testField3025 = new Integer(3025);
+    public Object testField3026 = new Integer(3026);
+    public Object testField3027 = new Integer(3027);
+    public Object testField3028 = new Integer(3028);
+    public Object testField3029 = new Integer(3029);
+    public Object testField3030 = new Integer(3030);
+    public Object testField3031 = new Integer(3031);
+    public Object testField3032 = new Integer(3032);
+    public Object testField3033 = new Integer(3033);
+    public Object testField3034 = new Integer(3034);
+    public Object testField3035 = new Integer(3035);
+    public Object testField3036 = new Integer(3036);
+    public Object testField3037 = new Integer(3037);
+    public Object testField3038 = new Integer(3038);
+    public Object testField3039 = new Integer(3039);
+    public Object testField3040 = new Integer(3040);
+    public Object testField3041 = new Integer(3041);
+    public Object testField3042 = new Integer(3042);
+    public Object testField3043 = new Integer(3043);
+    public Object testField3044 = new Integer(3044);
+    public Object testField3045 = new Integer(3045);
+    public Object testField3046 = new Integer(3046);
+    public Object testField3047 = new Integer(3047);
+    public Object testField3048 = new Integer(3048);
+    public Object testField3049 = new Integer(3049);
+    public Object testField3050 = new Integer(3050);
+    public Object testField3051 = new Integer(3051);
+    public Object testField3052 = new Integer(3052);
+    public Object testField3053 = new Integer(3053);
+    public Object testField3054 = new Integer(3054);
+    public Object testField3055 = new Integer(3055);
+    public Object testField3056 = new Integer(3056);
+    public Object testField3057 = new Integer(3057);
+    public Object testField3058 = new Integer(3058);
+    public Object testField3059 = new Integer(3059);
+    public Object testField3060 = new Integer(3060);
+    public Object testField3061 = new Integer(3061);
+    public Object testField3062 = new Integer(3062);
+    public Object testField3063 = new Integer(3063);
+    public Object testField3064 = new Integer(3064);
+    public Object testField3065 = new Integer(3065);
+    public Object testField3066 = new Integer(3066);
+    public Object testField3067 = new Integer(3067);
+    public Object testField3068 = new Integer(3068);
+    public Object testField3069 = new Integer(3069);
+    public Object testField3070 = new Integer(3070);
+    public Object testField3071 = new Integer(3071);
+    public Object testField3072 = new Integer(3072);
+    public Object testField3073 = new Integer(3073);
+    public Object testField3074 = new Integer(3074);
+    public Object testField3075 = new Integer(3075);
+    public Object testField3076 = new Integer(3076);
+    public Object testField3077 = new Integer(3077);
+    public Object testField3078 = new Integer(3078);
+    public Object testField3079 = new Integer(3079);
+    public Object testField3080 = new Integer(3080);
+    public Object testField3081 = new Integer(3081);
+    public Object testField3082 = new Integer(3082);
+    public Object testField3083 = new Integer(3083);
+    public Object testField3084 = new Integer(3084);
+    public Object testField3085 = new Integer(3085);
+    public Object testField3086 = new Integer(3086);
+    public Object testField3087 = new Integer(3087);
+    public Object testField3088 = new Integer(3088);
+    public Object testField3089 = new Integer(3089);
+    public Object testField3090 = new Integer(3090);
+    public Object testField3091 = new Integer(3091);
+    public Object testField3092 = new Integer(3092);
+    public Object testField3093 = new Integer(3093);
+    public Object testField3094 = new Integer(3094);
+    public Object testField3095 = new Integer(3095);
+    public Object testField3096 = new Integer(3096);
+    public Object testField3097 = new Integer(3097);
+    public Object testField3098 = new Integer(3098);
+    public Object testField3099 = new Integer(3099);
+    public Object testField3100 = new Integer(3100);
+    public Object testField3101 = new Integer(3101);
+    public Object testField3102 = new Integer(3102);
+    public Object testField3103 = new Integer(3103);
+    public Object testField3104 = new Integer(3104);
+    public Object testField3105 = new Integer(3105);
+    public Object testField3106 = new Integer(3106);
+    public Object testField3107 = new Integer(3107);
+    public Object testField3108 = new Integer(3108);
+    public Object testField3109 = new Integer(3109);
+    public Object testField3110 = new Integer(3110);
+    public Object testField3111 = new Integer(3111);
+    public Object testField3112 = new Integer(3112);
+    public Object testField3113 = new Integer(3113);
+    public Object testField3114 = new Integer(3114);
+    public Object testField3115 = new Integer(3115);
+    public Object testField3116 = new Integer(3116);
+    public Object testField3117 = new Integer(3117);
+    public Object testField3118 = new Integer(3118);
+    public Object testField3119 = new Integer(3119);
+    public Object testField3120 = new Integer(3120);
+    public Object testField3121 = new Integer(3121);
+    public Object testField3122 = new Integer(3122);
+    public Object testField3123 = new Integer(3123);
+    public Object testField3124 = new Integer(3124);
+    public Object testField3125 = new Integer(3125);
+    public Object testField3126 = new Integer(3126);
+    public Object testField3127 = new Integer(3127);
+    public Object testField3128 = new Integer(3128);
+    public Object testField3129 = new Integer(3129);
+    public Object testField3130 = new Integer(3130);
+    public Object testField3131 = new Integer(3131);
+    public Object testField3132 = new Integer(3132);
+    public Object testField3133 = new Integer(3133);
+    public Object testField3134 = new Integer(3134);
+    public Object testField3135 = new Integer(3135);
+    public Object testField3136 = new Integer(3136);
+    public Object testField3137 = new Integer(3137);
+    public Object testField3138 = new Integer(3138);
+    public Object testField3139 = new Integer(3139);
+    public Object testField3140 = new Integer(3140);
+    public Object testField3141 = new Integer(3141);
+    public Object testField3142 = new Integer(3142);
+    public Object testField3143 = new Integer(3143);
+    public Object testField3144 = new Integer(3144);
+    public Object testField3145 = new Integer(3145);
+    public Object testField3146 = new Integer(3146);
+    public Object testField3147 = new Integer(3147);
+    public Object testField3148 = new Integer(3148);
+    public Object testField3149 = new Integer(3149);
+    public Object testField3150 = new Integer(3150);
+    public Object testField3151 = new Integer(3151);
+    public Object testField3152 = new Integer(3152);
+    public Object testField3153 = new Integer(3153);
+    public Object testField3154 = new Integer(3154);
+    public Object testField3155 = new Integer(3155);
+    public Object testField3156 = new Integer(3156);
+    public Object testField3157 = new Integer(3157);
+    public Object testField3158 = new Integer(3158);
+    public Object testField3159 = new Integer(3159);
+    public Object testField3160 = new Integer(3160);
+    public Object testField3161 = new Integer(3161);
+    public Object testField3162 = new Integer(3162);
+    public Object testField3163 = new Integer(3163);
+    public Object testField3164 = new Integer(3164);
+    public Object testField3165 = new Integer(3165);
+    public Object testField3166 = new Integer(3166);
+    public Object testField3167 = new Integer(3167);
+    public Object testField3168 = new Integer(3168);
+    public Object testField3169 = new Integer(3169);
+    public Object testField3170 = new Integer(3170);
+    public Object testField3171 = new Integer(3171);
+    public Object testField3172 = new Integer(3172);
+    public Object testField3173 = new Integer(3173);
+    public Object testField3174 = new Integer(3174);
+    public Object testField3175 = new Integer(3175);
+    public Object testField3176 = new Integer(3176);
+    public Object testField3177 = new Integer(3177);
+    public Object testField3178 = new Integer(3178);
+    public Object testField3179 = new Integer(3179);
+    public Object testField3180 = new Integer(3180);
+    public Object testField3181 = new Integer(3181);
+    public Object testField3182 = new Integer(3182);
+    public Object testField3183 = new Integer(3183);
+    public Object testField3184 = new Integer(3184);
+    public Object testField3185 = new Integer(3185);
+    public Object testField3186 = new Integer(3186);
+    public Object testField3187 = new Integer(3187);
+    public Object testField3188 = new Integer(3188);
+    public Object testField3189 = new Integer(3189);
+    public Object testField3190 = new Integer(3190);
+    public Object testField3191 = new Integer(3191);
+    public Object testField3192 = new Integer(3192);
+    public Object testField3193 = new Integer(3193);
+    public Object testField3194 = new Integer(3194);
+    public Object testField3195 = new Integer(3195);
+    public Object testField3196 = new Integer(3196);
+    public Object testField3197 = new Integer(3197);
+    public Object testField3198 = new Integer(3198);
+    public Object testField3199 = new Integer(3199);
+    public Object testField3200 = new Integer(3200);
+    public Object testField3201 = new Integer(3201);
+    public Object testField3202 = new Integer(3202);
+    public Object testField3203 = new Integer(3203);
+    public Object testField3204 = new Integer(3204);
+    public Object testField3205 = new Integer(3205);
+    public Object testField3206 = new Integer(3206);
+    public Object testField3207 = new Integer(3207);
+    public Object testField3208 = new Integer(3208);
+    public Object testField3209 = new Integer(3209);
+    public Object testField3210 = new Integer(3210);
+    public Object testField3211 = new Integer(3211);
+    public Object testField3212 = new Integer(3212);
+    public Object testField3213 = new Integer(3213);
+    public Object testField3214 = new Integer(3214);
+    public Object testField3215 = new Integer(3215);
+    public Object testField3216 = new Integer(3216);
+    public Object testField3217 = new Integer(3217);
+    public Object testField3218 = new Integer(3218);
+    public Object testField3219 = new Integer(3219);
+    public Object testField3220 = new Integer(3220);
+    public Object testField3221 = new Integer(3221);
+    public Object testField3222 = new Integer(3222);
+    public Object testField3223 = new Integer(3223);
+    public Object testField3224 = new Integer(3224);
+    public Object testField3225 = new Integer(3225);
+    public Object testField3226 = new Integer(3226);
+    public Object testField3227 = new Integer(3227);
+    public Object testField3228 = new Integer(3228);
+    public Object testField3229 = new Integer(3229);
+    public Object testField3230 = new Integer(3230);
+    public Object testField3231 = new Integer(3231);
+    public Object testField3232 = new Integer(3232);
+    public Object testField3233 = new Integer(3233);
+    public Object testField3234 = new Integer(3234);
+    public Object testField3235 = new Integer(3235);
+    public Object testField3236 = new Integer(3236);
+    public Object testField3237 = new Integer(3237);
+    public Object testField3238 = new Integer(3238);
+    public Object testField3239 = new Integer(3239);
+    public Object testField3240 = new Integer(3240);
+    public Object testField3241 = new Integer(3241);
+    public Object testField3242 = new Integer(3242);
+    public Object testField3243 = new Integer(3243);
+    public Object testField3244 = new Integer(3244);
+    public Object testField3245 = new Integer(3245);
+    public Object testField3246 = new Integer(3246);
+    public Object testField3247 = new Integer(3247);
+    public Object testField3248 = new Integer(3248);
+    public Object testField3249 = new Integer(3249);
+    public Object testField3250 = new Integer(3250);
+    public Object testField3251 = new Integer(3251);
+    public Object testField3252 = new Integer(3252);
+    public Object testField3253 = new Integer(3253);
+    public Object testField3254 = new Integer(3254);
+    public Object testField3255 = new Integer(3255);
+    public Object testField3256 = new Integer(3256);
+    public Object testField3257 = new Integer(3257);
+    public Object testField3258 = new Integer(3258);
+    public Object testField3259 = new Integer(3259);
+    public Object testField3260 = new Integer(3260);
+    public Object testField3261 = new Integer(3261);
+    public Object testField3262 = new Integer(3262);
+    public Object testField3263 = new Integer(3263);
+    public Object testField3264 = new Integer(3264);
+    public Object testField3265 = new Integer(3265);
+    public Object testField3266 = new Integer(3266);
+    public Object testField3267 = new Integer(3267);
+    public Object testField3268 = new Integer(3268);
+    public Object testField3269 = new Integer(3269);
+    public Object testField3270 = new Integer(3270);
+    public Object testField3271 = new Integer(3271);
+    public Object testField3272 = new Integer(3272);
+    public Object testField3273 = new Integer(3273);
+    public Object testField3274 = new Integer(3274);
+    public Object testField3275 = new Integer(3275);
+    public Object testField3276 = new Integer(3276);
+    public Object testField3277 = new Integer(3277);
+    public Object testField3278 = new Integer(3278);
+    public Object testField3279 = new Integer(3279);
+    public Object testField3280 = new Integer(3280);
+    public Object testField3281 = new Integer(3281);
+    public Object testField3282 = new Integer(3282);
+    public Object testField3283 = new Integer(3283);
+    public Object testField3284 = new Integer(3284);
+    public Object testField3285 = new Integer(3285);
+    public Object testField3286 = new Integer(3286);
+    public Object testField3287 = new Integer(3287);
+    public Object testField3288 = new Integer(3288);
+    public Object testField3289 = new Integer(3289);
+    public Object testField3290 = new Integer(3290);
+    public Object testField3291 = new Integer(3291);
+    public Object testField3292 = new Integer(3292);
+    public Object testField3293 = new Integer(3293);
+    public Object testField3294 = new Integer(3294);
+    public Object testField3295 = new Integer(3295);
+    public Object testField3296 = new Integer(3296);
+    public Object testField3297 = new Integer(3297);
+    public Object testField3298 = new Integer(3298);
+    public Object testField3299 = new Integer(3299);
+    public Object testField3300 = new Integer(3300);
+    public Object testField3301 = new Integer(3301);
+    public Object testField3302 = new Integer(3302);
+    public Object testField3303 = new Integer(3303);
+    public Object testField3304 = new Integer(3304);
+    public Object testField3305 = new Integer(3305);
+    public Object testField3306 = new Integer(3306);
+    public Object testField3307 = new Integer(3307);
+    public Object testField3308 = new Integer(3308);
+    public Object testField3309 = new Integer(3309);
+    public Object testField3310 = new Integer(3310);
+    public Object testField3311 = new Integer(3311);
+    public Object testField3312 = new Integer(3312);
+    public Object testField3313 = new Integer(3313);
+    public Object testField3314 = new Integer(3314);
+    public Object testField3315 = new Integer(3315);
+    public Object testField3316 = new Integer(3316);
+    public Object testField3317 = new Integer(3317);
+    public Object testField3318 = new Integer(3318);
+    public Object testField3319 = new Integer(3319);
+    public Object testField3320 = new Integer(3320);
+    public Object testField3321 = new Integer(3321);
+    public Object testField3322 = new Integer(3322);
+    public Object testField3323 = new Integer(3323);
+    public Object testField3324 = new Integer(3324);
+    public Object testField3325 = new Integer(3325);
+    public Object testField3326 = new Integer(3326);
+    public Object testField3327 = new Integer(3327);
+    public Object testField3328 = new Integer(3328);
+    public Object testField3329 = new Integer(3329);
+    public Object testField3330 = new Integer(3330);
+    public Object testField3331 = new Integer(3331);
+    public Object testField3332 = new Integer(3332);
+    public Object testField3333 = new Integer(3333);
+    public Object testField3334 = new Integer(3334);
+    public Object testField3335 = new Integer(3335);
+    public Object testField3336 = new Integer(3336);
+    public Object testField3337 = new Integer(3337);
+    public Object testField3338 = new Integer(3338);
+    public Object testField3339 = new Integer(3339);
+    public Object testField3340 = new Integer(3340);
+    public Object testField3341 = new Integer(3341);
+    public Object testField3342 = new Integer(3342);
+    public Object testField3343 = new Integer(3343);
+    public Object testField3344 = new Integer(3344);
+    public Object testField3345 = new Integer(3345);
+    public Object testField3346 = new Integer(3346);
+    public Object testField3347 = new Integer(3347);
+    public Object testField3348 = new Integer(3348);
+    public Object testField3349 = new Integer(3349);
+    public Object testField3350 = new Integer(3350);
+    public Object testField3351 = new Integer(3351);
+    public Object testField3352 = new Integer(3352);
+    public Object testField3353 = new Integer(3353);
+    public Object testField3354 = new Integer(3354);
+    public Object testField3355 = new Integer(3355);
+    public Object testField3356 = new Integer(3356);
+    public Object testField3357 = new Integer(3357);
+    public Object testField3358 = new Integer(3358);
+    public Object testField3359 = new Integer(3359);
+    public Object testField3360 = new Integer(3360);
+    public Object testField3361 = new Integer(3361);
+    public Object testField3362 = new Integer(3362);
+    public Object testField3363 = new Integer(3363);
+    public Object testField3364 = new Integer(3364);
+    public Object testField3365 = new Integer(3365);
+    public Object testField3366 = new Integer(3366);
+    public Object testField3367 = new Integer(3367);
+    public Object testField3368 = new Integer(3368);
+    public Object testField3369 = new Integer(3369);
+    public Object testField3370 = new Integer(3370);
+    public Object testField3371 = new Integer(3371);
+    public Object testField3372 = new Integer(3372);
+    public Object testField3373 = new Integer(3373);
+    public Object testField3374 = new Integer(3374);
+    public Object testField3375 = new Integer(3375);
+    public Object testField3376 = new Integer(3376);
+    public Object testField3377 = new Integer(3377);
+    public Object testField3378 = new Integer(3378);
+    public Object testField3379 = new Integer(3379);
+    public Object testField3380 = new Integer(3380);
+    public Object testField3381 = new Integer(3381);
+    public Object testField3382 = new Integer(3382);
+    public Object testField3383 = new Integer(3383);
+    public Object testField3384 = new Integer(3384);
+    public Object testField3385 = new Integer(3385);
+    public Object testField3386 = new Integer(3386);
+    public Object testField3387 = new Integer(3387);
+    public Object testField3388 = new Integer(3388);
+    public Object testField3389 = new Integer(3389);
+    public Object testField3390 = new Integer(3390);
+    public Object testField3391 = new Integer(3391);
+    public Object testField3392 = new Integer(3392);
+    public Object testField3393 = new Integer(3393);
+    public Object testField3394 = new Integer(3394);
+    public Object testField3395 = new Integer(3395);
+    public Object testField3396 = new Integer(3396);
+    public Object testField3397 = new Integer(3397);
+    public Object testField3398 = new Integer(3398);
+    public Object testField3399 = new Integer(3399);
+    public Object testField3400 = new Integer(3400);
+    public Object testField3401 = new Integer(3401);
+    public Object testField3402 = new Integer(3402);
+    public Object testField3403 = new Integer(3403);
+    public Object testField3404 = new Integer(3404);
+    public Object testField3405 = new Integer(3405);
+    public Object testField3406 = new Integer(3406);
+    public Object testField3407 = new Integer(3407);
+    public Object testField3408 = new Integer(3408);
+    public Object testField3409 = new Integer(3409);
+    public Object testField3410 = new Integer(3410);
+    public Object testField3411 = new Integer(3411);
+    public Object testField3412 = new Integer(3412);
+    public Object testField3413 = new Integer(3413);
+    public Object testField3414 = new Integer(3414);
+    public Object testField3415 = new Integer(3415);
+    public Object testField3416 = new Integer(3416);
+    public Object testField3417 = new Integer(3417);
+    public Object testField3418 = new Integer(3418);
+    public Object testField3419 = new Integer(3419);
+    public Object testField3420 = new Integer(3420);
+    public Object testField3421 = new Integer(3421);
+    public Object testField3422 = new Integer(3422);
+    public Object testField3423 = new Integer(3423);
+    public Object testField3424 = new Integer(3424);
+    public Object testField3425 = new Integer(3425);
+    public Object testField3426 = new Integer(3426);
+    public Object testField3427 = new Integer(3427);
+    public Object testField3428 = new Integer(3428);
+    public Object testField3429 = new Integer(3429);
+    public Object testField3430 = new Integer(3430);
+    public Object testField3431 = new Integer(3431);
+    public Object testField3432 = new Integer(3432);
+    public Object testField3433 = new Integer(3433);
+    public Object testField3434 = new Integer(3434);
+    public Object testField3435 = new Integer(3435);
+    public Object testField3436 = new Integer(3436);
+    public Object testField3437 = new Integer(3437);
+    public Object testField3438 = new Integer(3438);
+    public Object testField3439 = new Integer(3439);
+    public Object testField3440 = new Integer(3440);
+    public Object testField3441 = new Integer(3441);
+    public Object testField3442 = new Integer(3442);
+    public Object testField3443 = new Integer(3443);
+    public Object testField3444 = new Integer(3444);
+    public Object testField3445 = new Integer(3445);
+    public Object testField3446 = new Integer(3446);
+    public Object testField3447 = new Integer(3447);
+    public Object testField3448 = new Integer(3448);
+    public Object testField3449 = new Integer(3449);
+    public Object testField3450 = new Integer(3450);
+    public Object testField3451 = new Integer(3451);
+    public Object testField3452 = new Integer(3452);
+    public Object testField3453 = new Integer(3453);
+    public Object testField3454 = new Integer(3454);
+    public Object testField3455 = new Integer(3455);
+    public Object testField3456 = new Integer(3456);
+    public Object testField3457 = new Integer(3457);
+    public Object testField3458 = new Integer(3458);
+    public Object testField3459 = new Integer(3459);
+    public Object testField3460 = new Integer(3460);
+    public Object testField3461 = new Integer(3461);
+    public Object testField3462 = new Integer(3462);
+    public Object testField3463 = new Integer(3463);
+    public Object testField3464 = new Integer(3464);
+    public Object testField3465 = new Integer(3465);
+    public Object testField3466 = new Integer(3466);
+    public Object testField3467 = new Integer(3467);
+    public Object testField3468 = new Integer(3468);
+    public Object testField3469 = new Integer(3469);
+    public Object testField3470 = new Integer(3470);
+    public Object testField3471 = new Integer(3471);
+    public Object testField3472 = new Integer(3472);
+    public Object testField3473 = new Integer(3473);
+    public Object testField3474 = new Integer(3474);
+    public Object testField3475 = new Integer(3475);
+    public Object testField3476 = new Integer(3476);
+    public Object testField3477 = new Integer(3477);
+    public Object testField3478 = new Integer(3478);
+    public Object testField3479 = new Integer(3479);
+    public Object testField3480 = new Integer(3480);
+    public Object testField3481 = new Integer(3481);
+    public Object testField3482 = new Integer(3482);
+    public Object testField3483 = new Integer(3483);
+    public Object testField3484 = new Integer(3484);
+    public Object testField3485 = new Integer(3485);
+    public Object testField3486 = new Integer(3486);
+    public Object testField3487 = new Integer(3487);
+    public Object testField3488 = new Integer(3488);
+    public Object testField3489 = new Integer(3489);
+    public Object testField3490 = new Integer(3490);
+    public Object testField3491 = new Integer(3491);
+    public Object testField3492 = new Integer(3492);
+    public Object testField3493 = new Integer(3493);
+    public Object testField3494 = new Integer(3494);
+    public Object testField3495 = new Integer(3495);
+    public Object testField3496 = new Integer(3496);
+    public Object testField3497 = new Integer(3497);
+    public Object testField3498 = new Integer(3498);
+    public Object testField3499 = new Integer(3499);
+    public Object testField3500 = new Integer(3500);
+    public Object testField3501 = new Integer(3501);
+    public Object testField3502 = new Integer(3502);
+    public Object testField3503 = new Integer(3503);
+    public Object testField3504 = new Integer(3504);
+    public Object testField3505 = new Integer(3505);
+    public Object testField3506 = new Integer(3506);
+    public Object testField3507 = new Integer(3507);
+    public Object testField3508 = new Integer(3508);
+    public Object testField3509 = new Integer(3509);
+    public Object testField3510 = new Integer(3510);
+    public Object testField3511 = new Integer(3511);
+    public Object testField3512 = new Integer(3512);
+    public Object testField3513 = new Integer(3513);
+    public Object testField3514 = new Integer(3514);
+    public Object testField3515 = new Integer(3515);
+    public Object testField3516 = new Integer(3516);
+    public Object testField3517 = new Integer(3517);
+    public Object testField3518 = new Integer(3518);
+    public Object testField3519 = new Integer(3519);
+    public Object testField3520 = new Integer(3520);
+    public Object testField3521 = new Integer(3521);
+    public Object testField3522 = new Integer(3522);
+    public Object testField3523 = new Integer(3523);
+    public Object testField3524 = new Integer(3524);
+    public Object testField3525 = new Integer(3525);
+    public Object testField3526 = new Integer(3526);
+    public Object testField3527 = new Integer(3527);
+    public Object testField3528 = new Integer(3528);
+    public Object testField3529 = new Integer(3529);
+    public Object testField3530 = new Integer(3530);
+    public Object testField3531 = new Integer(3531);
+    public Object testField3532 = new Integer(3532);
+    public Object testField3533 = new Integer(3533);
+    public Object testField3534 = new Integer(3534);
+    public Object testField3535 = new Integer(3535);
+    public Object testField3536 = new Integer(3536);
+    public Object testField3537 = new Integer(3537);
+    public Object testField3538 = new Integer(3538);
+    public Object testField3539 = new Integer(3539);
+    public Object testField3540 = new Integer(3540);
+    public Object testField3541 = new Integer(3541);
+    public Object testField3542 = new Integer(3542);
+    public Object testField3543 = new Integer(3543);
+    public Object testField3544 = new Integer(3544);
+    public Object testField3545 = new Integer(3545);
+    public Object testField3546 = new Integer(3546);
+    public Object testField3547 = new Integer(3547);
+    public Object testField3548 = new Integer(3548);
+    public Object testField3549 = new Integer(3549);
+    public Object testField3550 = new Integer(3550);
+    public Object testField3551 = new Integer(3551);
+    public Object testField3552 = new Integer(3552);
+    public Object testField3553 = new Integer(3553);
+    public Object testField3554 = new Integer(3554);
+    public Object testField3555 = new Integer(3555);
+    public Object testField3556 = new Integer(3556);
+    public Object testField3557 = new Integer(3557);
+    public Object testField3558 = new Integer(3558);
+    public Object testField3559 = new Integer(3559);
+    public Object testField3560 = new Integer(3560);
+    public Object testField3561 = new Integer(3561);
+    public Object testField3562 = new Integer(3562);
+    public Object testField3563 = new Integer(3563);
+    public Object testField3564 = new Integer(3564);
+    public Object testField3565 = new Integer(3565);
+    public Object testField3566 = new Integer(3566);
+    public Object testField3567 = new Integer(3567);
+    public Object testField3568 = new Integer(3568);
+    public Object testField3569 = new Integer(3569);
+    public Object testField3570 = new Integer(3570);
+    public Object testField3571 = new Integer(3571);
+    public Object testField3572 = new Integer(3572);
+    public Object testField3573 = new Integer(3573);
+    public Object testField3574 = new Integer(3574);
+    public Object testField3575 = new Integer(3575);
+    public Object testField3576 = new Integer(3576);
+    public Object testField3577 = new Integer(3577);
+    public Object testField3578 = new Integer(3578);
+    public Object testField3579 = new Integer(3579);
+    public Object testField3580 = new Integer(3580);
+    public Object testField3581 = new Integer(3581);
+    public Object testField3582 = new Integer(3582);
+    public Object testField3583 = new Integer(3583);
+    public Object testField3584 = new Integer(3584);
+    public Object testField3585 = new Integer(3585);
+    public Object testField3586 = new Integer(3586);
+    public Object testField3587 = new Integer(3587);
+    public Object testField3588 = new Integer(3588);
+    public Object testField3589 = new Integer(3589);
+    public Object testField3590 = new Integer(3590);
+    public Object testField3591 = new Integer(3591);
+    public Object testField3592 = new Integer(3592);
+    public Object testField3593 = new Integer(3593);
+    public Object testField3594 = new Integer(3594);
+    public Object testField3595 = new Integer(3595);
+    public Object testField3596 = new Integer(3596);
+    public Object testField3597 = new Integer(3597);
+    public Object testField3598 = new Integer(3598);
+    public Object testField3599 = new Integer(3599);
+    public Object testField3600 = new Integer(3600);
+    public Object testField3601 = new Integer(3601);
+    public Object testField3602 = new Integer(3602);
+    public Object testField3603 = new Integer(3603);
+    public Object testField3604 = new Integer(3604);
+    public Object testField3605 = new Integer(3605);
+    public Object testField3606 = new Integer(3606);
+    public Object testField3607 = new Integer(3607);
+    public Object testField3608 = new Integer(3608);
+    public Object testField3609 = new Integer(3609);
+    public Object testField3610 = new Integer(3610);
+    public Object testField3611 = new Integer(3611);
+    public Object testField3612 = new Integer(3612);
+    public Object testField3613 = new Integer(3613);
+    public Object testField3614 = new Integer(3614);
+    public Object testField3615 = new Integer(3615);
+    public Object testField3616 = new Integer(3616);
+    public Object testField3617 = new Integer(3617);
+    public Object testField3618 = new Integer(3618);
+    public Object testField3619 = new Integer(3619);
+    public Object testField3620 = new Integer(3620);
+    public Object testField3621 = new Integer(3621);
+    public Object testField3622 = new Integer(3622);
+    public Object testField3623 = new Integer(3623);
+    public Object testField3624 = new Integer(3624);
+    public Object testField3625 = new Integer(3625);
+    public Object testField3626 = new Integer(3626);
+    public Object testField3627 = new Integer(3627);
+    public Object testField3628 = new Integer(3628);
+    public Object testField3629 = new Integer(3629);
+    public Object testField3630 = new Integer(3630);
+    public Object testField3631 = new Integer(3631);
+    public Object testField3632 = new Integer(3632);
+    public Object testField3633 = new Integer(3633);
+    public Object testField3634 = new Integer(3634);
+    public Object testField3635 = new Integer(3635);
+    public Object testField3636 = new Integer(3636);
+    public Object testField3637 = new Integer(3637);
+    public Object testField3638 = new Integer(3638);
+    public Object testField3639 = new Integer(3639);
+    public Object testField3640 = new Integer(3640);
+    public Object testField3641 = new Integer(3641);
+    public Object testField3642 = new Integer(3642);
+    public Object testField3643 = new Integer(3643);
+    public Object testField3644 = new Integer(3644);
+    public Object testField3645 = new Integer(3645);
+    public Object testField3646 = new Integer(3646);
+    public Object testField3647 = new Integer(3647);
+    public Object testField3648 = new Integer(3648);
+    public Object testField3649 = new Integer(3649);
+    public Object testField3650 = new Integer(3650);
+    public Object testField3651 = new Integer(3651);
+    public Object testField3652 = new Integer(3652);
+    public Object testField3653 = new Integer(3653);
+    public Object testField3654 = new Integer(3654);
+    public Object testField3655 = new Integer(3655);
+    public Object testField3656 = new Integer(3656);
+    public Object testField3657 = new Integer(3657);
+    public Object testField3658 = new Integer(3658);
+    public Object testField3659 = new Integer(3659);
+    public Object testField3660 = new Integer(3660);
+    public Object testField3661 = new Integer(3661);
+    public Object testField3662 = new Integer(3662);
+    public Object testField3663 = new Integer(3663);
+    public Object testField3664 = new Integer(3664);
+    public Object testField3665 = new Integer(3665);
+    public Object testField3666 = new Integer(3666);
+    public Object testField3667 = new Integer(3667);
+    public Object testField3668 = new Integer(3668);
+    public Object testField3669 = new Integer(3669);
+    public Object testField3670 = new Integer(3670);
+    public Object testField3671 = new Integer(3671);
+    public Object testField3672 = new Integer(3672);
+    public Object testField3673 = new Integer(3673);
+    public Object testField3674 = new Integer(3674);
+    public Object testField3675 = new Integer(3675);
+    public Object testField3676 = new Integer(3676);
+    public Object testField3677 = new Integer(3677);
+    public Object testField3678 = new Integer(3678);
+    public Object testField3679 = new Integer(3679);
+    public Object testField3680 = new Integer(3680);
+    public Object testField3681 = new Integer(3681);
+    public Object testField3682 = new Integer(3682);
+    public Object testField3683 = new Integer(3683);
+    public Object testField3684 = new Integer(3684);
+    public Object testField3685 = new Integer(3685);
+    public Object testField3686 = new Integer(3686);
+    public Object testField3687 = new Integer(3687);
+    public Object testField3688 = new Integer(3688);
+    public Object testField3689 = new Integer(3689);
+    public Object testField3690 = new Integer(3690);
+    public Object testField3691 = new Integer(3691);
+    public Object testField3692 = new Integer(3692);
+    public Object testField3693 = new Integer(3693);
+    public Object testField3694 = new Integer(3694);
+    public Object testField3695 = new Integer(3695);
+    public Object testField3696 = new Integer(3696);
+    public Object testField3697 = new Integer(3697);
+    public Object testField3698 = new Integer(3698);
+    public Object testField3699 = new Integer(3699);
+    public Object testField3700 = new Integer(3700);
+    public Object testField3701 = new Integer(3701);
+    public Object testField3702 = new Integer(3702);
+    public Object testField3703 = new Integer(3703);
+    public Object testField3704 = new Integer(3704);
+    public Object testField3705 = new Integer(3705);
+    public Object testField3706 = new Integer(3706);
+    public Object testField3707 = new Integer(3707);
+    public Object testField3708 = new Integer(3708);
+    public Object testField3709 = new Integer(3709);
+    public Object testField3710 = new Integer(3710);
+    public Object testField3711 = new Integer(3711);
+    public Object testField3712 = new Integer(3712);
+    public Object testField3713 = new Integer(3713);
+    public Object testField3714 = new Integer(3714);
+    public Object testField3715 = new Integer(3715);
+    public Object testField3716 = new Integer(3716);
+    public Object testField3717 = new Integer(3717);
+    public Object testField3718 = new Integer(3718);
+    public Object testField3719 = new Integer(3719);
+    public Object testField3720 = new Integer(3720);
+    public Object testField3721 = new Integer(3721);
+    public Object testField3722 = new Integer(3722);
+    public Object testField3723 = new Integer(3723);
+    public Object testField3724 = new Integer(3724);
+    public Object testField3725 = new Integer(3725);
+    public Object testField3726 = new Integer(3726);
+    public Object testField3727 = new Integer(3727);
+    public Object testField3728 = new Integer(3728);
+    public Object testField3729 = new Integer(3729);
+    public Object testField3730 = new Integer(3730);
+    public Object testField3731 = new Integer(3731);
+    public Object testField3732 = new Integer(3732);
+    public Object testField3733 = new Integer(3733);
+    public Object testField3734 = new Integer(3734);
+    public Object testField3735 = new Integer(3735);
+    public Object testField3736 = new Integer(3736);
+    public Object testField3737 = new Integer(3737);
+    public Object testField3738 = new Integer(3738);
+    public Object testField3739 = new Integer(3739);
+    public Object testField3740 = new Integer(3740);
+    public Object testField3741 = new Integer(3741);
+    public Object testField3742 = new Integer(3742);
+    public Object testField3743 = new Integer(3743);
+    public Object testField3744 = new Integer(3744);
+    public Object testField3745 = new Integer(3745);
+    public Object testField3746 = new Integer(3746);
+    public Object testField3747 = new Integer(3747);
+    public Object testField3748 = new Integer(3748);
+    public Object testField3749 = new Integer(3749);
+    public Object testField3750 = new Integer(3750);
+    public Object testField3751 = new Integer(3751);
+    public Object testField3752 = new Integer(3752);
+    public Object testField3753 = new Integer(3753);
+    public Object testField3754 = new Integer(3754);
+    public Object testField3755 = new Integer(3755);
+    public Object testField3756 = new Integer(3756);
+    public Object testField3757 = new Integer(3757);
+    public Object testField3758 = new Integer(3758);
+    public Object testField3759 = new Integer(3759);
+    public Object testField3760 = new Integer(3760);
+    public Object testField3761 = new Integer(3761);
+    public Object testField3762 = new Integer(3762);
+    public Object testField3763 = new Integer(3763);
+    public Object testField3764 = new Integer(3764);
+    public Object testField3765 = new Integer(3765);
+    public Object testField3766 = new Integer(3766);
+    public Object testField3767 = new Integer(3767);
+    public Object testField3768 = new Integer(3768);
+    public Object testField3769 = new Integer(3769);
+    public Object testField3770 = new Integer(3770);
+    public Object testField3771 = new Integer(3771);
+    public Object testField3772 = new Integer(3772);
+    public Object testField3773 = new Integer(3773);
+    public Object testField3774 = new Integer(3774);
+    public Object testField3775 = new Integer(3775);
+    public Object testField3776 = new Integer(3776);
+    public Object testField3777 = new Integer(3777);
+    public Object testField3778 = new Integer(3778);
+    public Object testField3779 = new Integer(3779);
+    public Object testField3780 = new Integer(3780);
+    public Object testField3781 = new Integer(3781);
+    public Object testField3782 = new Integer(3782);
+    public Object testField3783 = new Integer(3783);
+    public Object testField3784 = new Integer(3784);
+    public Object testField3785 = new Integer(3785);
+    public Object testField3786 = new Integer(3786);
+    public Object testField3787 = new Integer(3787);
+    public Object testField3788 = new Integer(3788);
+    public Object testField3789 = new Integer(3789);
+    public Object testField3790 = new Integer(3790);
+    public Object testField3791 = new Integer(3791);
+    public Object testField3792 = new Integer(3792);
+    public Object testField3793 = new Integer(3793);
+    public Object testField3794 = new Integer(3794);
+    public Object testField3795 = new Integer(3795);
+    public Object testField3796 = new Integer(3796);
+    public Object testField3797 = new Integer(3797);
+    public Object testField3798 = new Integer(3798);
+    public Object testField3799 = new Integer(3799);
+    public Object testField3800 = new Integer(3800);
+    public Object testField3801 = new Integer(3801);
+    public Object testField3802 = new Integer(3802);
+    public Object testField3803 = new Integer(3803);
+    public Object testField3804 = new Integer(3804);
+    public Object testField3805 = new Integer(3805);
+    public Object testField3806 = new Integer(3806);
+    public Object testField3807 = new Integer(3807);
+    public Object testField3808 = new Integer(3808);
+    public Object testField3809 = new Integer(3809);
+    public Object testField3810 = new Integer(3810);
+    public Object testField3811 = new Integer(3811);
+    public Object testField3812 = new Integer(3812);
+    public Object testField3813 = new Integer(3813);
+    public Object testField3814 = new Integer(3814);
+    public Object testField3815 = new Integer(3815);
+    public Object testField3816 = new Integer(3816);
+    public Object testField3817 = new Integer(3817);
+    public Object testField3818 = new Integer(3818);
+    public Object testField3819 = new Integer(3819);
+    public Object testField3820 = new Integer(3820);
+    public Object testField3821 = new Integer(3821);
+    public Object testField3822 = new Integer(3822);
+    public Object testField3823 = new Integer(3823);
+    public Object testField3824 = new Integer(3824);
+    public Object testField3825 = new Integer(3825);
+    public Object testField3826 = new Integer(3826);
+    public Object testField3827 = new Integer(3827);
+    public Object testField3828 = new Integer(3828);
+    public Object testField3829 = new Integer(3829);
+    public Object testField3830 = new Integer(3830);
+    public Object testField3831 = new Integer(3831);
+    public Object testField3832 = new Integer(3832);
+    public Object testField3833 = new Integer(3833);
+    public Object testField3834 = new Integer(3834);
+    public Object testField3835 = new Integer(3835);
+    public Object testField3836 = new Integer(3836);
+    public Object testField3837 = new Integer(3837);
+    public Object testField3838 = new Integer(3838);
+    public Object testField3839 = new Integer(3839);
+    public Object testField3840 = new Integer(3840);
+    public Object testField3841 = new Integer(3841);
+    public Object testField3842 = new Integer(3842);
+    public Object testField3843 = new Integer(3843);
+    public Object testField3844 = new Integer(3844);
+    public Object testField3845 = new Integer(3845);
+    public Object testField3846 = new Integer(3846);
+    public Object testField3847 = new Integer(3847);
+    public Object testField3848 = new Integer(3848);
+    public Object testField3849 = new Integer(3849);
+    public Object testField3850 = new Integer(3850);
+    public Object testField3851 = new Integer(3851);
+    public Object testField3852 = new Integer(3852);
+    public Object testField3853 = new Integer(3853);
+    public Object testField3854 = new Integer(3854);
+    public Object testField3855 = new Integer(3855);
+    public Object testField3856 = new Integer(3856);
+    public Object testField3857 = new Integer(3857);
+    public Object testField3858 = new Integer(3858);
+    public Object testField3859 = new Integer(3859);
+    public Object testField3860 = new Integer(3860);
+    public Object testField3861 = new Integer(3861);
+    public Object testField3862 = new Integer(3862);
+    public Object testField3863 = new Integer(3863);
+    public Object testField3864 = new Integer(3864);
+    public Object testField3865 = new Integer(3865);
+    public Object testField3866 = new Integer(3866);
+    public Object testField3867 = new Integer(3867);
+    public Object testField3868 = new Integer(3868);
+    public Object testField3869 = new Integer(3869);
+    public Object testField3870 = new Integer(3870);
+    public Object testField3871 = new Integer(3871);
+    public Object testField3872 = new Integer(3872);
+    public Object testField3873 = new Integer(3873);
+    public Object testField3874 = new Integer(3874);
+    public Object testField3875 = new Integer(3875);
+    public Object testField3876 = new Integer(3876);
+    public Object testField3877 = new Integer(3877);
+    public Object testField3878 = new Integer(3878);
+    public Object testField3879 = new Integer(3879);
+    public Object testField3880 = new Integer(3880);
+    public Object testField3881 = new Integer(3881);
+    public Object testField3882 = new Integer(3882);
+    public Object testField3883 = new Integer(3883);
+    public Object testField3884 = new Integer(3884);
+    public Object testField3885 = new Integer(3885);
+    public Object testField3886 = new Integer(3886);
+    public Object testField3887 = new Integer(3887);
+    public Object testField3888 = new Integer(3888);
+    public Object testField3889 = new Integer(3889);
+    public Object testField3890 = new Integer(3890);
+    public Object testField3891 = new Integer(3891);
+    public Object testField3892 = new Integer(3892);
+    public Object testField3893 = new Integer(3893);
+    public Object testField3894 = new Integer(3894);
+    public Object testField3895 = new Integer(3895);
+    public Object testField3896 = new Integer(3896);
+    public Object testField3897 = new Integer(3897);
+    public Object testField3898 = new Integer(3898);
+    public Object testField3899 = new Integer(3899);
+    public Object testField3900 = new Integer(3900);
+    public Object testField3901 = new Integer(3901);
+    public Object testField3902 = new Integer(3902);
+    public Object testField3903 = new Integer(3903);
+    public Object testField3904 = new Integer(3904);
+    public Object testField3905 = new Integer(3905);
+    public Object testField3906 = new Integer(3906);
+    public Object testField3907 = new Integer(3907);
+    public Object testField3908 = new Integer(3908);
+    public Object testField3909 = new Integer(3909);
+    public Object testField3910 = new Integer(3910);
+    public Object testField3911 = new Integer(3911);
+    public Object testField3912 = new Integer(3912);
+    public Object testField3913 = new Integer(3913);
+    public Object testField3914 = new Integer(3914);
+    public Object testField3915 = new Integer(3915);
+    public Object testField3916 = new Integer(3916);
+    public Object testField3917 = new Integer(3917);
+    public Object testField3918 = new Integer(3918);
+    public Object testField3919 = new Integer(3919);
+    public Object testField3920 = new Integer(3920);
+    public Object testField3921 = new Integer(3921);
+    public Object testField3922 = new Integer(3922);
+    public Object testField3923 = new Integer(3923);
+    public Object testField3924 = new Integer(3924);
+    public Object testField3925 = new Integer(3925);
+    public Object testField3926 = new Integer(3926);
+    public Object testField3927 = new Integer(3927);
+    public Object testField3928 = new Integer(3928);
+    public Object testField3929 = new Integer(3929);
+    public Object testField3930 = new Integer(3930);
+    public Object testField3931 = new Integer(3931);
+    public Object testField3932 = new Integer(3932);
+    public Object testField3933 = new Integer(3933);
+    public Object testField3934 = new Integer(3934);
+    public Object testField3935 = new Integer(3935);
+    public Object testField3936 = new Integer(3936);
+    public Object testField3937 = new Integer(3937);
+    public Object testField3938 = new Integer(3938);
+    public Object testField3939 = new Integer(3939);
+    public Object testField3940 = new Integer(3940);
+    public Object testField3941 = new Integer(3941);
+    public Object testField3942 = new Integer(3942);
+    public Object testField3943 = new Integer(3943);
+    public Object testField3944 = new Integer(3944);
+    public Object testField3945 = new Integer(3945);
+    public Object testField3946 = new Integer(3946);
+    public Object testField3947 = new Integer(3947);
+    public Object testField3948 = new Integer(3948);
+    public Object testField3949 = new Integer(3949);
+    public Object testField3950 = new Integer(3950);
+    public Object testField3951 = new Integer(3951);
+    public Object testField3952 = new Integer(3952);
+    public Object testField3953 = new Integer(3953);
+    public Object testField3954 = new Integer(3954);
+    public Object testField3955 = new Integer(3955);
+    public Object testField3956 = new Integer(3956);
+    public Object testField3957 = new Integer(3957);
+    public Object testField3958 = new Integer(3958);
+    public Object testField3959 = new Integer(3959);
+    public Object testField3960 = new Integer(3960);
+    public Object testField3961 = new Integer(3961);
+    public Object testField3962 = new Integer(3962);
+    public Object testField3963 = new Integer(3963);
+    public Object testField3964 = new Integer(3964);
+    public Object testField3965 = new Integer(3965);
+    public Object testField3966 = new Integer(3966);
+    public Object testField3967 = new Integer(3967);
+    public Object testField3968 = new Integer(3968);
+    public Object testField3969 = new Integer(3969);
+    public Object testField3970 = new Integer(3970);
+    public Object testField3971 = new Integer(3971);
+    public Object testField3972 = new Integer(3972);
+    public Object testField3973 = new Integer(3973);
+    public Object testField3974 = new Integer(3974);
+    public Object testField3975 = new Integer(3975);
+    public Object testField3976 = new Integer(3976);
+    public Object testField3977 = new Integer(3977);
+    public Object testField3978 = new Integer(3978);
+    public Object testField3979 = new Integer(3979);
+    public Object testField3980 = new Integer(3980);
+    public Object testField3981 = new Integer(3981);
+    public Object testField3982 = new Integer(3982);
+    public Object testField3983 = new Integer(3983);
+    public Object testField3984 = new Integer(3984);
+    public Object testField3985 = new Integer(3985);
+    public Object testField3986 = new Integer(3986);
+    public Object testField3987 = new Integer(3987);
+    public Object testField3988 = new Integer(3988);
+    public Object testField3989 = new Integer(3989);
+    public Object testField3990 = new Integer(3990);
+    public Object testField3991 = new Integer(3991);
+    public Object testField3992 = new Integer(3992);
+    public Object testField3993 = new Integer(3993);
+    public Object testField3994 = new Integer(3994);
+    public Object testField3995 = new Integer(3995);
+    public Object testField3996 = new Integer(3996);
+    public Object testField3997 = new Integer(3997);
+    public Object testField3998 = new Integer(3998);
+    public Object testField3999 = new Integer(3999);
+}
diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java
index 182c07d..2b30986 100644
--- a/test/623-checker-loop-regressions/src/Main.java
+++ b/test/623-checker-loop-regressions/src/Main.java
@@ -280,6 +280,36 @@
     }
   }
 
+  // If vectorized, string encoding should be dealt with.
+  private static void string2Bytes(char[] a, String b) {
+    int min = Math.min(a.length, b.length());
+    for (int i = 0; i < min; i++) {
+      a[i] = b.charAt(i);
+    }
+  }
+
+  // A strange function that does not inline.
+  private static void $noinline$foo(boolean x, int n) {
+    if (n < 0)
+      throw new Error("oh no");
+    if (n > 100) {
+      $noinline$foo(!x, n - 1);
+      $noinline$foo(!x, n - 2);
+      $noinline$foo(!x, n - 3);
+      $noinline$foo(!x, n - 4);
+    }
+  }
+
+  // A loop with environment uses of x (the terminating condition). As exposed by bug
+  // b/37247891, the loop can be unrolled, but should handle the (unlikely, but clearly
+  // not impossible) environment uses of the terminating condition in a correct manner.
+  private static void envUsesInCond() {
+    boolean x = false;
+    for (int i = 0; !(x = i >= 1); i++) {
+      $noinline$foo(true, i);
+    }
+  }
+
   public static void main(String[] args) {
     expectEquals(10, earlyExitFirst(-1));
     for (int i = 0; i <= 10; i++) {
@@ -354,6 +384,15 @@
       expectEquals(2, yy[i]);
     }
 
+    char[] aa = new char[23];
+    String bb = "hello world how are you";
+    string2Bytes(aa, bb);
+    for (int i = 0; i < aa.length; i++) {
+      expectEquals(aa[i], bb.charAt(i));
+    }
+
+    envUsesInCond();
+
     System.out.println("passed");
   }
 
diff --git a/test/640-checker-double-simd/src/Main.java b/test/640-checker-double-simd/src/Main.java
index 43f65f1..0d4f87a 100644
--- a/test/640-checker-double-simd/src/Main.java
+++ b/test/640-checker-double-simd/src/Main.java
@@ -32,8 +32,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.add(double) loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecAdd   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void add(double x) {
     for (int i = 0; i < 128; i++)
       a[i] += x;
@@ -45,8 +47,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.sub(double) loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecSub   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void sub(double x) {
     for (int i = 0; i < 128; i++)
       a[i] -= x;
@@ -58,8 +62,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.mul(double) loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecMul   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void mul(double x) {
     for (int i = 0; i < 128; i++)
       a[i] *= x;
@@ -71,8 +77,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.div(double) loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecDiv   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void div(double x) {
     for (int i = 0; i < 128; i++)
       a[i] /= x;
@@ -84,8 +92,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNeg   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void neg() {
     for (int i = 0; i < 128; i++)
       a[i] = -a[i];
@@ -97,8 +107,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.abs() loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecAbs   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void abs() {
     for (int i = 0; i < 128; i++)
       a[i] = Math.abs(a[i]);
diff --git a/test/640-checker-long-simd/src/Main.java b/test/640-checker-long-simd/src/Main.java
index 90a2e76..5641182 100644
--- a/test/640-checker-long-simd/src/Main.java
+++ b/test/640-checker-long-simd/src/Main.java
@@ -31,8 +31,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.add(long) loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecAdd   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void add(long x) {
     for (int i = 0; i < 128; i++)
       a[i] += x;
@@ -44,8 +46,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.sub(long) loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecSub   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void sub(long x) {
     for (int i = 0; i < 128; i++)
       a[i] -= x;
@@ -56,9 +60,9 @@
   /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
+  //  Not supported for longs.
   /// CHECK-START-ARM64: void Main.mul(long) loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-NOT: VecMul
   static void mul(long x) {
     for (int i = 0; i < 128; i++)
       a[i] *= x;
@@ -84,8 +88,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNeg   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void neg() {
     for (int i = 0; i < 128; i++)
       a[i] = -a[i];
@@ -97,8 +103,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.not() loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNot   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void not() {
     for (int i = 0; i < 128; i++)
       a[i] = ~a[i];
@@ -110,8 +118,10 @@
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   //
   /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
-  //
-  // TODO: fill in when supported
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecShl   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
   static void shl4() {
     for (int i = 0; i < 128; i++)
       a[i] <<= 4;
diff --git a/test/646-checker-arraycopy-large-cst-pos/expected.txt b/test/646-checker-arraycopy-large-cst-pos/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/646-checker-arraycopy-large-cst-pos/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/646-checker-arraycopy-large-cst-pos/info.txt b/test/646-checker-arraycopy-large-cst-pos/info.txt
new file mode 100644
index 0000000..9ac21db
--- /dev/null
+++ b/test/646-checker-arraycopy-large-cst-pos/info.txt
@@ -0,0 +1,4 @@
+Regression test for an issue with a depleted VIXL scratch register
+pool during the emission of a SystemArrayCopy intrinsic with a large
+constant destination position, on ARM64, with read barriers
+(b/37256530).
diff --git a/test/646-checker-arraycopy-large-cst-pos/src/Main.java b/test/646-checker-arraycopy-large-cst-pos/src/Main.java
new file mode 100644
index 0000000..3144fc1
--- /dev/null
+++ b/test/646-checker-arraycopy-large-cst-pos/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Main {
+
+  public static void main(String[] args) {
+    System.out.println("passed");
+  }
+
+  /// CHECK-START-ARM64: void Main.test() disassembly (after)
+  /// CHECK: InvokeStaticOrDirect method_name:java.lang.System.arraycopy intrinsic:SystemArrayCopy
+  /// CHECK-NOT:    blr
+  /// CHECK: ReturnVoid
+
+  static void test() {
+    Object[] src = new Object[1024];
+    Object[] dst = new Object[2048];
+    // The length of the copied data must not be too large (smaller
+    // than kSystemArrayCopyThreshold = 128) for the call to
+    // System.arraycopy to be intrinsified.
+    System.arraycopy(src, 0, dst, 1024, 64);
+  }
+
+}
diff --git a/test/646-checker-long-const-to-int/expected.txt b/test/646-checker-long-const-to-int/expected.txt
new file mode 100644
index 0000000..9abd696
--- /dev/null
+++ b/test/646-checker-long-const-to-int/expected.txt
@@ -0,0 +1 @@
+305419896
diff --git a/test/646-checker-long-const-to-int/info.txt b/test/646-checker-long-const-to-int/info.txt
new file mode 100644
index 0000000..3f560c3
--- /dev/null
+++ b/test/646-checker-long-const-to-int/info.txt
@@ -0,0 +1 @@
+Regression test for bogus checks that a constant input of long-to-int conversion fits into int.
diff --git a/test/646-checker-long-const-to-int/src/Main.java b/test/646-checker-long-const-to-int/src/Main.java
new file mode 100644
index 0000000..85738dc
--- /dev/null
+++ b/test/646-checker-long-const-to-int/src/Main.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Main {
+
+  public static void main(String[] args) {
+    System.out.println(test());
+  }
+
+  public static long testField = 0;
+  public static long longField0 = 0;
+  public static long longField1 = 0;
+  public static long longField2 = 0;
+  public static long longField3 = 0;
+  public static long longField4 = 0;
+  public static long longField5 = 0;
+  public static long longField6 = 0;
+  public static long longField7 = 0;
+
+  /// CHECK-START-ARM: int Main.test() register (after)
+  /// CHECK: TypeConversion locations:[#-8690466096623102344]->{{.*}}
+  public static int test() {
+    // To avoid constant folding TypeConversion(const), hide the constant in a field.
+    // We do not run constant folding after load-store-elimination.
+    testField = 0x8765432112345678L;
+    long value = testField;
+    // Now, the `value` is in a register because of the store but we need
+    // a constant location to trigger the bug, so load a bunch of other fields.
+    long l0 = longField0;
+    long l1 = longField1;
+    long l2 = longField2;
+    long l3 = longField3;
+    long l4 = longField4;
+    long l5 = longField5;
+    long l6 = longField6;
+    long l7 = longField7;
+    if (l0 != 0 || l1 != 0 || l2 != 0 || l3 != 0 || l4 != 0 || l5 != 0 || l6 != 0 || l7 != 0) {
+      throw new Error();
+    }
+    // Do the conversion from constant location.
+    return (int)value;
+  }
+}
diff --git a/test/902-hello-transformation/src/Main.java b/test/902-hello-transformation/src/Main.java
index ed8a500..af49cb4 100644
--- a/test/902-hello-transformation/src/Main.java
+++ b/test/902-hello-transformation/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,53 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
 public class Main {
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye");
-   *   }
-   * }
-   */
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
-    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
-    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
-    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
-    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
-    "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
-    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
-    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
-    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
-    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
-    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
-    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
-    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
-    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
-    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
-    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test902.run();
   }
-
-  public static void doTest(Transform t) {
-    t.sayHi();
-    doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-    t.sayHi();
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] class_file,
-                                                       byte[] dex_file);
 }
diff --git a/test/902-hello-transformation/src/Transform.java b/test/902-hello-transformation/src/Transform.java
deleted file mode 100644
index 8e8af35..0000000
--- a/test/902-hello-transformation/src/Transform.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi() {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Goodbye" < "LTransform;" < "hello".
-    System.out.println("hello");
-  }
-}
diff --git a/test/902-hello-transformation/src/art/Redefinition.java b/test/902-hello-transformation/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/902-hello-transformation/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/902-hello-transformation/src/art/Test902.java b/test/902-hello-transformation/src/art/Test902.java
new file mode 100644
index 0000000..e95558f
--- /dev/null
+++ b/test/902-hello-transformation/src/art/Test902.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+public class Test902 {
+
+  static class Transform {
+    public void sayHi() {
+      // Use lower 'h' to make sure the string will have a different string id
+      // than the transformation (the transformation code is the same except
+      // the actual printed String, which was making the test inacurately passing
+      // in JIT mode when loading the string from the dex cache, as the string ids
+      // of the two different strings were the same).
+      // We know the string ids will be different because lexicographically:
+      // "Goodbye" < "LTransform;" < "hello".
+      System.out.println("hello");
+    }
+  }
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAIAoABgAOCQAPABAIABEKABIAEwcAFQcAGAEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAAxUZXN0OTAyLmphdmEMAAcA" +
+    "CAcAGQwAGgAbAQAHR29vZGJ5ZQcAHAwAHQAeBwAfAQAVYXJ0L1Rlc3Q5MDIkVHJhbnNmb3JtAQAJ" +
+    "VHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9T" +
+    "eXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFt" +
+    "AQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAC2FydC9UZXN0OTAyACAABQAGAAAA" +
+    "AAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAJQABAAsACAABAAkA" +
+    "AAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAnAAgAKAACAAwAAAACAA0AFwAAAAoA" +
+    "AQAFABQAFgAI");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQCpghS3AAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" +
+    "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" +
+    "AABMAQAAVQEAAG4BAAB9AQAAoQEAAMEBAADYAQAA7AEAAAACAAAUAgAAIgIAAC0CAAAwAgAANAIA" +
+    "AEECAABHAgAATAIAAFUCAABcAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
+    "DAAAAAgAAAAAAAAADQAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
+    "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA5AIAALgCAAAAAAAABjxpbml0PgAHR29vZGJ5ZQAX" +
+    "TGFydC9UZXN0OTAyJFRyYW5zZm9ybTsADUxhcnQvVGVzdDkwMjsAIkxkYWx2aWsvYW5ub3RhdGlv" +
+    "bi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEv" +
+    "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" +
+    "TGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTAyLmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vz" +
+    "c0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAQAAAAYAAAAlAAcOACcA" +
+    "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQEA" +
+    "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg4ECA8XCwACAAAAyAIAAM4CAADY" +
+    "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" +
+    "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" +
+    "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" +
+    "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA==");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi();
+    Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    t.sayHi();
+  }
+}
diff --git a/test/904-object-allocation/expected.txt b/test/904-object-allocation/expected.txt
index 371d2b7..fdec470 100644
--- a/test/904-object-allocation/expected.txt
+++ b/test/904-object-allocation/expected.txt
@@ -1,8 +1,8 @@
-ObjectAllocated type java.lang.Object/java.lang.Object size 8
-ObjectAllocated type java.lang.Integer/java.lang.Integer size 16
-ObjectAllocated type java.lang.Short/java.lang.Short size 16
+[]
+[ObjectAllocated type java.lang.Object/java.lang.Object size 8, ObjectAllocated type java.lang.Integer/java.lang.Integer size 16, ObjectAllocated type java.lang.Short/java.lang.Short size 16]
 Tracking on same thread
-ObjectAllocated type java.lang.Double/java.lang.Double size 16
+[ObjectAllocated type java.lang.Double/java.lang.Double size 16]
 Tracking on same thread, not disabling tracking
-ObjectAllocated type java.lang.Double/java.lang.Double size 16
+[ObjectAllocated type java.lang.Double/java.lang.Double size 16]
 Tracking on different thread
+[]
diff --git a/test/904-object-allocation/src/art/Test904.java b/test/904-object-allocation/src/art/Test904.java
index 31e0c8c..70a4b98 100644
--- a/test/904-object-allocation/src/art/Test904.java
+++ b/test/904-object-allocation/src/art/Test904.java
@@ -17,6 +17,7 @@
 package art;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 public class Test904 {
   public static void run() throws Exception {
@@ -48,6 +49,8 @@
     // Enable actual logging callback.
     setupObjectAllocCallback(true);
 
+    System.out.println(Arrays.toString(getTrackingEventMessages()));
+
     enableAllocationTracking(null, true);
 
     l.add(new Object());
@@ -65,16 +68,19 @@
 
     l.add(new Byte((byte)0));
 
+    System.out.println(Arrays.toString(getTrackingEventMessages()));
     System.out.println("Tracking on same thread");
 
     testThread(l, true, true);
 
     l.add(new Byte((byte)0));
 
+    System.out.println(Arrays.toString(getTrackingEventMessages()));
     System.out.println("Tracking on same thread, not disabling tracking");
 
     testThread(l, true, false);
 
+    System.out.println(Arrays.toString(getTrackingEventMessages()));
     System.out.println("Tracking on different thread");
 
     testThread(l, false, true);
@@ -84,6 +90,9 @@
     // Disable actual logging callback and re-enable tracking, so we can keep the event enabled and
     // check that shutdown works correctly.
     setupObjectAllocCallback(false);
+
+    System.out.println(Arrays.toString(getTrackingEventMessages()));
+
     enableAllocationTracking(null, true);
   }
 
@@ -142,4 +151,5 @@
 
   private static native void setupObjectAllocCallback(boolean enable);
   private static native void enableAllocationTracking(Thread thread, boolean enable);
+  private static native String[] getTrackingEventMessages();
 }
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index 8de350b..20b5328 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -15,11 +15,13 @@
  */
 
 #include <iostream>
+#include <mutex>
 #include <pthread.h>
 #include <stdio.h>
 #include <vector>
 
 #include "android-base/logging.h"
+#include "android-base/stringprintf.h"
 #include "jni.h"
 #include "jvmti.h"
 #include "scoped_local_ref.h"
@@ -41,6 +43,9 @@
   return utf_chars.c_str();
 }
 
+static std::mutex gEventsMutex;
+static std::vector<std::string> gEvents;
+
 static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED,
                                     JNIEnv* jni_env,
                                     jthread thread ATTRIBUTE_UNUSED,
@@ -51,10 +56,11 @@
   ScopedLocalRef<jclass> object_klass2(jni_env, jni_env->GetObjectClass(object));
   std::string object_klass_descriptor2 = GetClassName(jni_env, object_klass2.get());
 
-  printf("ObjectAllocated type %s/%s size %zu\n",
-         object_klass_descriptor.c_str(),
-         object_klass_descriptor2.c_str(),
-         static_cast<size_t>(size));
+  std::lock_guard<std::mutex> guard(gEventsMutex);
+  gEvents.push_back(android::base::StringPrintf("ObjectAllocated type %s/%s size %zu",
+                                                object_klass_descriptor.c_str(),
+                                                object_klass_descriptor2.c_str(),
+                                                static_cast<size_t>(size)));
 }
 
 extern "C" JNIEXPORT void JNICALL Java_art_Test904_setupObjectAllocCallback(
@@ -76,5 +82,18 @@
   JvmtiErrorToException(env, jvmti_env, ret);
 }
 
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test904_getTrackingEventMessages(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+  std::lock_guard<std::mutex> guard(gEventsMutex);
+  jobjectArray ret = CreateObjectArray(env,
+                                       static_cast<jint>(gEvents.size()),
+                                       "java/lang/String",
+                                       [&](jint i) {
+    return env->NewStringUTF(gEvents[i].c_str());
+  });
+  gEvents.clear();
+  return ret;
+}
+
 }  // namespace Test904ObjectAllocation
 }  // namespace art
diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt
index 8e6b6e7..c14c6c4 100644
--- a/test/910-methods/expected.txt
+++ b/test/910-methods/expected.txt
@@ -32,19 +32,19 @@
 interface java.util.List
 1025
 Max locals: 0
-Argument size: 0
-Location start: 0
-Location end: 0
+Argument size: 2
+Location start: -1
+Location end: -1
 Is native: false
 Is obsolete: false
 Is synthetic: false
 [run, ()V, null]
-class $Proxy0
+class $Proxy20
 17
 Max locals: 0
-Argument size: 0
-Location start: 0
-Location end: 0
+Argument size: 1
+Location start: -1
+Location end: -1
 Is native: false
 Is obsolete: false
 Is synthetic: false
diff --git a/test/910-methods/src/art/Test910.java b/test/910-methods/src/art/Test910.java
index b3490e9..aa6d13a 100644
--- a/test/910-methods/src/art/Test910.java
+++ b/test/910-methods/src/art/Test910.java
@@ -39,17 +39,6 @@
     testMethod(findSyntheticMethod(), NestedSynthetic.class, false);
   }
 
-  private static Class<?> proxyClass = null;
-
-  private static Class<?> getProxyClass() throws Exception {
-    if (proxyClass != null) {
-      return proxyClass;
-    }
-
-    proxyClass = Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Runnable.class });
-    return proxyClass;
-  }
-
   private static void testMethod(String className, String methodName, Class<?>... types)
       throws Exception {
     Class<?> base = Class.forName(className);
@@ -145,4 +134,62 @@
   private static native boolean isMethodNative(Method m);
   private static native boolean isMethodObsolete(Method m);
   private static native boolean isMethodSynthetic(Method m);
+
+  // We need this machinery for a consistent proxy name. Names of proxy classes include a
+  // unique number (derived by counting). This means that a simple call to getProxyClass
+  // depends on the test environment.
+  //
+  // To work around this, we assume that at most twenty proxies have been created before
+  // the test is run, and canonicalize on "$Proxy20". We add infrastructure to create
+  // as many proxy classes but cycling through subsets of the test-provided interfaces
+  // I0...I4.
+  //
+  //
+  // (This is made under the judgment that we do not want to have proxy-specific behavior
+  //  for testMethod.)
+
+  private static Class<?> proxyClass = null;
+
+  private static Class<?> getProxyClass() throws Exception {
+    if (proxyClass != null) {
+      return proxyClass;
+    }
+
+    for (int i = 1; i <= 21; i++) {
+      proxyClass = createProxyClass(i);
+      String name = proxyClass.getName();
+      if (name.equals("$Proxy20")) {
+        return proxyClass;
+      }
+    }
+    return proxyClass;
+  }
+
+  private static Class<?> createProxyClass(int i) throws Exception {
+    int count = Integer.bitCount(i);
+    Class<?>[] input = new Class<?>[count + 1];
+    input[0] = Runnable.class;
+    int inputIndex = 1;
+    int bitIndex = 0;
+    while (i != 0) {
+        if ((i & 1) != 0) {
+            input[inputIndex++] = Class.forName("art.Test910$I" + bitIndex);
+        }
+        i >>>= 1;
+        bitIndex++;
+    }
+    return Proxy.getProxyClass(Test910.class.getClassLoader(), input);
+  }
+
+  // Need this for the proxy naming.
+  public static interface I0 {
+  }
+  public static interface I1 {
+  }
+  public static interface I2 {
+  }
+  public static interface I3 {
+  }
+  public static interface I4 {
+  }
 }
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index 2a183ee..702b247 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -1,9 +1,10 @@
 ---
 true true
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
-0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -14,11 +15,13 @@
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
@@ -31,20 +34,24 @@
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
 root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
 root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -55,11 +62,13 @@
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
@@ -72,43 +81,48 @@
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 ---
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 ---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 ---
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 ---
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
-0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
 root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
 root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 ---
 [1@0 (32, 'HelloWorld'), 2@0 (16, '')]
 2
@@ -148,16 +162,17 @@
 10008
 --- klass ---
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
-0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
 1@1000 --(field@2)--> 2@1000 [size=16, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 ---
 1@1000 --(field@2)--> 2@1000 [size=16, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
 root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
@@ -167,13 +182,15 @@
 root@root --(thread)--> 1@1000 [size=16, length=-1]
 1@1000 --(field@2)--> 2@1000 [size=16, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 ---
 1@1000 --(field@2)--> 2@1000 [size=16, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 ---
 --- heap_filter ---
 ---- tagged objects
@@ -182,10 +199,11 @@
 ---
 ---
 ---- untagged objects
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
-0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -196,11 +214,13 @@
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
@@ -213,20 +233,24 @@
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
 root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1]
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
 root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -237,11 +261,13 @@
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
@@ -254,16 +280,20 @@
 2@1000 --(class)--> 1000@0 [size=123, length=-1]
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 ---- tagged classes
-root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -273,6 +303,7 @@
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
@@ -284,9 +315,12 @@
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=136, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=136, length=-1]
+root@root --(thread)--> 3000@0 [size=136, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 1002@0 --(interface)--> 2001@0 [size=124, length=-1]
 1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
@@ -296,6 +330,7 @@
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
@@ -307,24 +342,26 @@
 3@1001 --(class)--> 1001@0 [size=123, length=-1]
 4@1000 --(class)--> 1000@0 [size=123, length=-1]
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
+5@1002 --(field@8)--> 500@0 [size=20, length=2]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
 ---- untagged classes
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
-0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
 1@1000 --(field@2)--> 2@1000 [size=16, length=-1]
 1@1000 --(field@3)--> 3@1001 [size=24, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 ---
 1@1000 --(field@2)--> 2@1000 [size=16, length=-1]
 1@1000 --(field@3)--> 3@1001 [size=24, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
 root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
@@ -335,14 +372,16 @@
 1@1000 --(field@2)--> 2@1000 [size=16, length=-1]
 1@1000 --(field@3)--> 3@1001 [size=24, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 ---
 1@1000 --(field@2)--> 2@1000 [size=16, length=-1]
 1@1000 --(field@3)--> 3@1001 [size=24, length=-1]
 3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
-3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
-5@1002 --(field@8)--> 6@1000 [size=16, length=-1]
-5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=36, length=-1]
+500@0 --(array-element@1)--> 2@1000 [size=16, length=-1]
+5@1002 --(field@10)--> 1@1000 [size=16, length=-1]
+5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
 ---
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 6a06b29..19e12ae 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -19,40 +19,35 @@
 #include <string.h>
 
 #include <iostream>
+#include <sstream>
 #include <vector>
 
 #include "android-base/macros.h"
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 
-#include "jit/jit.h"
 #include "jni.h"
-#include "native_stack_dump.h"
 #include "jvmti.h"
-#include "runtime.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-inl.h"
-#include "thread_list.h"
 
 // Test infrastructure
 #include "jni_helper.h"
 #include "jvmti_helper.h"
 #include "test_env.h"
+#include "ti_utf.h"
 
 namespace art {
 namespace Test913Heaps {
 
 using android::base::StringPrintf;
 
+#define FINAL final
+#define OVERRIDE override
+#define UNREACHABLE  __builtin_unreachable
+
 extern "C" JNIEXPORT void JNICALL Java_art_Test913_forceGarbageCollection(
-    JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
   jvmtiError ret = jvmti_env->ForceGarbageCollection();
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error forcing a garbage collection: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 class IterationConfig {
@@ -92,7 +87,8 @@
                         user_data);
 }
 
-static bool Run(jint heap_filter,
+static bool Run(JNIEnv* env,
+                jint heap_filter,
                 jclass klass_filter,
                 jobject initial_object,
                 IterationConfig* config) {
@@ -105,14 +101,7 @@
                                                initial_object,
                                                &callbacks,
                                                config);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Failure running FollowReferences: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-    return false;
-  }
-  return true;
+  return !JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test913_followReferences(
@@ -142,6 +131,27 @@
                 jint length,
                 void* user_data ATTRIBUTE_UNUSED) OVERRIDE {
       jlong tag = *tag_ptr;
+
+      // Ignore any jni-global roots with untagged classes. These can be from the environment,
+      // or the JIT.
+      if (reference_kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL && class_tag == 0) {
+        return 0;
+      }
+      // Ignore classes (1000-1002@0) for thread objects. These can be held by the JIT.
+      if (reference_kind == JVMTI_HEAP_REFERENCE_THREAD && class_tag == 0 &&
+              (1000 <= *tag_ptr &&  *tag_ptr <= 1002)) {
+        return 0;
+      }
+      // Ignore stack-locals of untagged threads. That is the environment.
+      if (reference_kind == JVMTI_HEAP_REFERENCE_STACK_LOCAL &&
+          reference_info->stack_local.thread_tag != 3000) {
+        return 0;
+      }
+      // Ignore array elements with an untagged source. These are from the environment.
+      if (reference_kind == JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT && *referrer_tag_ptr == 0) {
+        return 0;
+      }
+
       // Only check tagged objects.
       if (tag == 0) {
         return JVMTI_VISIT_OBJECTS;
@@ -201,10 +211,6 @@
                                   reference_info,
                                   adapted_size,
                                   length));
-
-      if (reference_kind == JVMTI_HEAP_REFERENCE_THREAD && *tag_ptr == 1000) {
-        DumpStacks();
-      }
     }
 
     std::vector<std::string> GetLines() const {
@@ -259,9 +265,15 @@
         if (info_.jni_local.method != nullptr) {
           jvmti_env->GetMethodName(info_.jni_local.method, &name, nullptr, nullptr);
         }
+        // Normalize the thread id, as this depends on the number of other threads
+        // and which thread is running the test. Should be:
+        //   jlong thread_id = info_.jni_local.thread_id;
+        // TODO: A pre-pass before the test should be able fetch this number, so it can
+        //       be compared explicitly.
+        jlong thread_id = 1;
         std::string ret = StringPrintf("jni-local[id=%" PRId64 ",tag=%" PRId64 ",depth=%d,"
                                        "method=%s]",
-                                       info_.jni_local.thread_id,
+                                       thread_id,
                                        info_.jni_local.thread_tag,
                                        info_.jni_local.depth,
                                        name == nullptr ? "<null>" : name);
@@ -284,13 +296,12 @@
                         jlong size,
                         jint length,
                         const jvmtiHeapReferenceInfo* reference_info)
-          REQUIRES_SHARED(Locks::mutator_lock_)
           : Elem(referrer, referree, size, length) {
         memcpy(&info_, reference_info, sizeof(jvmtiHeapReferenceInfo));
-        // Debug stack trace for failure condition. Remove when done.
-        if (info_.stack_local.depth == 3 && info_.stack_local.slot == 13) {
-          DumpNativeStack(std::cerr, GetTid());
-          Thread::Current()->DumpJavaStack(std::cerr, false, false);
+
+        // Debug code. Try to figure out where bad depth is coming from.
+        if (reference_info->stack_local.depth == 6) {
+          LOG(FATAL) << "Unexpected depth of 6";
         }
       }
 
@@ -300,9 +311,15 @@
         if (info_.stack_local.method != nullptr) {
           jvmti_env->GetMethodName(info_.stack_local.method, &name, nullptr, nullptr);
         }
+        // Normalize the thread id, as this depends on the number of other threads
+        // and which thread is running the test. Should be:
+        //   jlong thread_id = info_.stack_local.thread_id;
+        // TODO: A pre-pass before the test should be able fetch this number, so it can
+        //       be compared explicitly.
+        jlong thread_id = 1;
         std::string ret = StringPrintf("stack-local[id=%" PRId64 ",tag=%" PRId64 ",depth=%d,"
                                        "method=%s,vreg=%d,location=% " PRId64 "]",
-                                       info_.stack_local.thread_id,
+                                       thread_id,
                                        info_.stack_local.thread_tag,
                                        info_.stack_local.depth,
                                        name == nullptr ? "<null>" : name,
@@ -361,7 +378,13 @@
                                                         tmp));
         }
         case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT: {
-          std::string tmp = StringPrintf("array-element@%d", reference_info->array.index);
+          jint index = reference_info->array.index;
+          // Normalize if it's "0@0" -> "3000@1".
+          // TODO: A pre-pass could probably give us this index to check explicitly.
+          if (referrer == "0@0" && referree == "3000@0") {
+            index = 0;
+          }
+          std::string tmp = StringPrintf("array-element@%d", index);
           return std::unique_ptr<Elem>(new StringElement(referrer,
                                                          referree,
                                                          size,
@@ -459,16 +482,6 @@
       UNREACHABLE();
     }
 
-    static void DumpStacks() NO_THREAD_SAFETY_ANALYSIS {
-      auto dump_function = [](art::Thread* t, void* data ATTRIBUTE_UNUSED) {
-        std::string name;
-        t->GetThreadName(name);
-        LOG(ERROR) << name;
-        art::DumpNativeStack(LOG_STREAM(ERROR), t->GetTid());
-      };
-      art::Runtime::Current()->GetThreadList()->ForEach(dump_function, nullptr);
-    }
-
     jint counter_;
     const jint stop_after_;
     const jint follow_set_;
@@ -476,8 +489,6 @@
     std::vector<std::unique_ptr<Elem>> lines_;
   };
 
-  jit::ScopedJitSuspend sjs;  // Wait to avoid JIT influence (e.g., JNI globals).
-
   // If jniRef isn't null, add a local and a global ref.
   ScopedLocalRef<jobject> jni_local_ref(env, nullptr);
   jobject jni_global_ref = nullptr;
@@ -487,7 +498,9 @@
   }
 
   PrintIterationConfig config(stop_after, follow_set);
-  Run(heap_filter, klass_filter, initial_object, &config);
+  if (!Run(env, heap_filter, klass_filter, initial_object, &config)) {
+    return nullptr;
+  }
 
   std::vector<std::string> lines = config.GetLines();
   jobjectArray ret = CreateObjectArray(env,
@@ -528,10 +541,10 @@
                                             void* user_data) {
       FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
       if (*tag_ptr != 0) {
-        size_t utf_byte_count = CountUtf8Bytes(value, value_length);
+        size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
         std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
         memset(mod_utf.get(), 0, utf_byte_count + 1);
-        ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
+        ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
         p->data.push_back(android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')",
                                                       *tag_ptr,
                                                       class_tag,
diff --git a/test/913-heaps/src/art/Test913.java b/test/913-heaps/src/art/Test913.java
index c54ecb0..d3b29cf 100644
--- a/test/913-heaps/src/art/Test913.java
+++ b/test/913-heaps/src/art/Test913.java
@@ -21,12 +21,34 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
 
 public class Test913 {
   public static void run() throws Exception {
     Main.bindAgentJNIForClass(Test913.class);
 
     doTest();
+
+    // Use a countdown latch for synchronization, as join() will introduce more roots.
+    final CountDownLatch cdl1 = new CountDownLatch(1);
+
+    // Run the follow-references tests on a dedicated thread so we know the specific Thread type.
+    Thread t = new Thread() {
+      @Override
+      public void run() {
+        try {
+          Test913.runFollowReferences();
+        } catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+        cdl1.countDown();
+      }
+    };
+    t.start();
+    cdl1.await();
+  }
+
+  public static void runFollowReferences() throws Exception {
     new TestConfig().doFollowReferencesTest();
 
     Runtime.getRuntime().gc();
@@ -349,6 +371,14 @@
       cInst.baz2 = aInst;
       v.add(cInstStr, aInstStr);  // C -->(field) --> A.
 
+      A[] aArray = new A[2];
+      setTag(aArray, 500);
+      aArray[1] = a2Inst;
+      cInst.array = aArray;
+      String aArrayStr = "500@0";
+      v.add(cInstStr, aArrayStr);
+      v.add(aArrayStr, a2InstStr);
+
       return aInst;
     }
   }
@@ -386,6 +416,7 @@
   public static class C extends B implements I2 {
     public A baz;
     public A baz2;
+    public A[] array;
 
     public C() {}
     public C(A a, A b) {
@@ -481,7 +512,8 @@
           if (currentHead == null) {
             currentHead = referrer;
           } else {
-            if (!currentHead.equals(referrer)) {
+            // Ignore 0@0, as it can happen at any time (as it stands for all other objects).
+            if (!currentHead.equals(referrer) && !referrer.equals("0@0")) {
               completedReferrers.add(currentHead);
               currentHead = referrer;
               if (completedReferrers.contains(referrer)) {
diff --git a/test/914-hello-obsolescence/src/Main.java b/test/914-hello-obsolescence/src/Main.java
index 2ec7664..ab5c7f4 100644
--- a/test/914-hello-obsolescence/src/Main.java
+++ b/test/914-hello-obsolescence/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,60 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
-
 public class Main {
-  // class Transform {
-  //   public void sayHi(Runnable r) {
-  //     System.out.println("Hello - Transformed");
-  //     r.run();
-  //     System.out.println("Goodbye - Transformed");
-  //   }
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
-    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
-    "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
-    "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
-    "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
-    "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
-    "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
-    "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsAAAA7AAIA" +
-    "AgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
-    "AQAPAAAAAgAQ");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQAYeAMMXgYWxoeSHAS9EWKCCtVRSAGpqZVQAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
-    "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
-    "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
-    "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
-    "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
-    "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
-    "AwAAAA4ABAACAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" +
-    "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
-    "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
-    "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
-    "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
-    "LjEzAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICABMQCAQHc" +
-    "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
-    "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
-    "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test914.run();
   }
-
-  public static void doTest(Transform t) {
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
-    t.sayHi(() -> {
-      System.out.println("transforming calling function");
-      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-    });
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/914-hello-obsolescence/src/Transform.java b/test/914-hello-obsolescence/src/Transform.java
deleted file mode 100644
index 8cda6cd..0000000
--- a/test/914-hello-obsolescence/src/Transform.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi(Runnable r) {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Hello" < "LTransform;" < "hello".
-    System.out.println("hello");
-    r.run();
-    System.out.println("goodbye");
-  }
-}
diff --git a/test/914-hello-obsolescence/src/art/Redefinition.java b/test/914-hello-obsolescence/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/914-hello-obsolescence/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/914-hello-obsolescence/src/art/Test914.java b/test/914-hello-obsolescence/src/art/Test914.java
new file mode 100644
index 0000000..ef2710d
--- /dev/null
+++ b/test/914-hello-obsolescence/src/art/Test914.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+
+public class Test914 {
+
+  // The class we will be transforming.
+  static class Transform {
+    public void sayHi(Runnable r) {
+      System.out.println("hello");
+      r.run();
+      System.out.println("goodbye");
+    }
+  }
+
+  // static class Transform {
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     r.run();
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAKAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAbBwAeAQAGPGluaXQ+AQADKClW" +
+    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+    "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDkxNC5qYXZhDAAJAAoHAB8MACAAIQEAE0hlbGxvIC0gVHJh" +
+    "bnNmb3JtZWQHACIMACMAJAcAJQwAJgAKAQAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkBwAnAQAVYXJ0" +
+    "L1Rlc3Q5MTQkVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5n" +
+    "L09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsB" +
+    "ABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEA" +
+    "EmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgEAC2FydC9UZXN0OTE0ACAABwAIAAAAAAACAAAACQAK" +
+    "AAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAABQABAA0ADgABAAsAAAA7AAIAAgAA" +
+    "ABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAcACAAIAA4ACQAWAAoAAgAP" +
+    "AAAAAgAQAB0AAAAKAAEABwAaABwACA==");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBlmxNYAAAAAAAAAAAAAAAAAAAAAAAAAAA8BAAAcAAAAHhWNBIAAAAAAAAAAHgDAAAX" +
+    "AAAAcAAAAAoAAADMAAAAAwAAAPQAAAABAAAAGAEAAAUAAAAgAQAAAQAAAEgBAADUAgAAaAEAAGgB" +
+    "AABwAQAAhwEAAJwBAAC1AQAAxAEAAOgBAAAIAgAAHwIAADMCAABJAgAAXQIAAHECAAB/AgAAigIA" +
+    "AI0CAACRAgAAngIAAKQCAACpAgAAsgIAALcCAAC+AgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+    "CQAAAAoAAAALAAAADgAAAA4AAAAJAAAAAAAAAA8AAAAJAAAAyAIAAA8AAAAJAAAA0AIAAAgABAAS" +
+    "AAAAAAAAAAAAAAAAAAEAFQAAAAQAAgATAAAABQAAAAAAAAAGAAAAFAAAAAAAAAAAAAAABQAAAAAA" +
+    "AAAMAAAAaAMAADwDAAAAAAAABjxpbml0PgAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkABNIZWxsbyAt" +
+    "IFRyYW5zZm9ybWVkABdMYXJ0L1Rlc3Q5MTQkVHJhbnNmb3JtOwANTGFydC9UZXN0OTE0OwAiTGRh" +
+    "bHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVy" +
+    "Q2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEv" +
+    "bGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxU" +
+    "ZXN0OTE0LmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vzc0ZsYWdzAARuYW1lAANvdXQAB3By" +
+    "aW50bG4AA3J1bgAFc2F5SGkABXZhbHVlAAAAAAEAAAAGAAAAAQAAAAcAAAAFAAcOAAcBAAcOAQgP" +
+    "AQMPAQgPAAEAAQABAAAA2AIAAAQAAABwEAMAAAAOAAQAAgACAAAA3QIAABQAAABiAAAAGwECAAAA" +
+    "biACABAAchAEAAMAYgAAABsBAQAAAG4gAgAQAA4AAAABAQCAgATsBQEBhAYAAAICARYYAQIDAhAE" +
+    "CBEXDQACAAAATAMAAFIDAABcAwAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAXAAAA" +
+    "cAAAAAIAAAAKAAAAzAAAAAMAAAADAAAA9AAAAAQAAAABAAAAGAEAAAUAAAAFAAAAIAEAAAYAAAAB" +
+    "AAAASAEAAAIgAAAXAAAAaAEAAAEQAAACAAAAyAIAAAMgAAACAAAA2AIAAAEgAAACAAAA7AIAAAAg" +
+    "AAABAAAAPAMAAAQgAAACAAAATAMAAAMQAAABAAAAXAMAAAYgAAABAAAAaAMAAAAQAAABAAAAeAMA" +
+    "AA==");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    t.sayHi(() -> {
+      System.out.println("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    });
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+}
diff --git a/test/915-obsolete-2/src/Main.java b/test/915-obsolete-2/src/Main.java
index fc73ee8..be51234 100644
--- a/test/915-obsolete-2/src/Main.java
+++ b/test/915-obsolete-2/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,86 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
-
 public class Main {
-  // class Transform {
-  //   private void Start() {
-  //     System.out.println("Hello - private - Transformed");
-  //   }
-  //
-  //   private void Finish() {
-  //     System.out.println("Goodbye - private - Transformed");
-  //   }
-  //
-  //   public void sayHi(Runnable r) {
-  //     System.out.println("Pre Start private method call - Transformed");
-  //     Start();
-  //     System.out.println("Post Start private method call - Transformed");
-  //     r.run();
-  //     System.out.println("Pre Finish private method call - Transformed");
-  //     Finish();
-  //     System.out.println("Post Finish private method call - Transformed");
-  //   }
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAMgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
-    "JwcAKAcAKQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
-    "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAO" +
-    "VHJhbnNmb3JtLmphdmEMAA8AEAcAKgwAKwAsAQAdSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3Jt" +
-    "ZWQHAC0MAC4ALwEAH0dvb2RieWUgLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQBACtQcmUgU3RhcnQg" +
-    "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAATABABACxQb3N0IFN0YXJ0IHByaXZh" +
-    "dGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcAMAwAMQAQAQAsUHJlIEZpbmlzaCBwcml2YXRl" +
-    "IG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQMABQAEAEALVBvc3QgRmluaXNoIHByaXZhdGUgbWV0" +
-    "aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAEACVRyYW5zZm9ybQEAEGphdmEvbGFuZy9PYmplY3QBABBq" +
-    "YXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9Q" +
-    "cmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABJqYXZhL2xhbmcv" +
-    "UnVubmFibGUBAANydW4AIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAAB" +
-    "ABIAAAAGAAEAAAABAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAA" +
-    "AAMACAAEAAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAcACAAI" +
-    "AAEAFQAWAAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQq" +
-    "twALsgACEgy2AASxAAAAAQASAAAAIgAIAAAACwAIAAwADAANABQADgAaAA8AIgAQACYAEQAuABIA" +
-    "AQAXAAAAAgAY");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQCM0QYTJmX+NsZXkImojgSkJtXyuew3oaXcBAAAcAAAAHhWNBIAAAAAAAAAADwEAAAX" +
-    "AAAAcAAAAAcAAADMAAAAAwAAAOgAAAABAAAADAEAAAcAAAAUAQAAAQAAAEwBAABwAwAAbAEAAD4C" +
-    "AABGAgAATgIAAG8CAACOAgAAmwIAALICAADGAgAA3AIAAPACAAAEAwAAMwMAAGEDAACPAwAAvAMA" +
-    "AMMDAADTAwAA1gMAANoDAADuAwAA8wMAAPwDAAABBAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAA" +
-    "EAAAABAAAAAGAAAAAAAAABEAAAAGAAAAMAIAABEAAAAGAAAAOAIAAAUAAQATAAAAAAAAAAAAAAAA" +
-    "AAAAAQAAAAAAAAAOAAAAAAABABYAAAABAAIAFAAAAAIAAAAAAAAAAwAAABUAAAAAAAAAAAAAAAIA" +
-    "AAAAAAAADwAAAAAAAAAmBAAAAAAAAAEAAQABAAAACAQAAAQAAABwEAUAAAAOAAMAAQACAAAADQQA" +
-    "AAkAAABiAAAAGwECAAAAbiAEABAADgAAAAMAAQACAAAAEwQAAAkAAABiAAAAGwEDAAAAbiAEABAA" +
-    "DgAAAAQAAgACAAAAGQQAACoAAABiAAAAGwENAAAAbiAEABAAcBACAAIAYgAAABsBCwAAAG4gBAAQ" +
-    "AHIQBgADAGIAAAAbAQwAAABuIAQAEABwEAEAAgBiAAAAGwEKAAAAbiAEABAADgABAAAAAwAAAAEA" +
-    "AAAEAAY8aW5pdD4ABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVs" +
-    "bG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07ABVMamF2YS9pby9QcmludFN0" +
-    "cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xh" +
-    "bmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AC1Qb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhv" +
-    "ZCBjYWxsIC0gVHJhbnNmb3JtZWQALFBvc3QgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRy" +
-    "YW5zZm9ybWVkACxQcmUgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAr" +
-    "UHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAFU3RhcnQADlRyYW5z" +
-    "Zm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00LjEzAANvdXQAB3ByaW50bG4AA3J1bgAF" +
-    "c2F5SGkAAQAHDgAHAAcOhwADAAcOhwALAQAHDoc8hzyHPIcAAAADAQCAgATsAgEChAMBAqgDAwHM" +
-    "Aw0AAAAAAAAAAQAAAAAAAAABAAAAFwAAAHAAAAACAAAABwAAAMwAAAADAAAAAwAAAOgAAAAEAAAA" +
-    "AQAAAAwBAAAFAAAABwAAABQBAAAGAAAAAQAAAEwBAAABIAAABAAAAGwBAAABEAAAAgAAADACAAAC" +
-    "IAAAFwAAAD4CAAADIAAABAAAAAgEAAAAIAAAAQAAACYEAAAAEAAAAQAAADwEAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test915.run();
   }
-
-  public static void doTest(Transform t) {
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
-    t.sayHi(() -> {
-      System.out.println("transforming calling function");
-      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-    });
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/915-obsolete-2/src/Transform.java b/test/915-obsolete-2/src/Transform.java
deleted file mode 100644
index e914e29..0000000
--- a/test/915-obsolete-2/src/Transform.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  private void Start() {
-    System.out.println("hello - private");
-  }
-
-  private void Finish() {
-    System.out.println("goodbye - private");
-  }
-
-  public void sayHi(Runnable r) {
-    System.out.println("Pre Start private method call");
-    Start();
-    System.out.println("Post Start private method call");
-    r.run();
-    System.out.println("Pre Finish private method call");
-    Finish();
-    System.out.println("Post Finish private method call");
-  }
-}
diff --git a/test/915-obsolete-2/src/art/Redefinition.java b/test/915-obsolete-2/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/915-obsolete-2/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/915-obsolete-2/src/art/Test915.java b/test/915-obsolete-2/src/art/Test915.java
new file mode 100644
index 0000000..63c7f34
--- /dev/null
+++ b/test/915-obsolete-2/src/art/Test915.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+
+public class Test915 {
+
+  static class Transform {
+    private void Start() {
+      System.out.println("hello - private");
+    }
+
+    private void Finish() {
+      System.out.println("goodbye - private");
+    }
+
+    public void sayHi(Runnable r) {
+      System.out.println("Pre Start private method call");
+      Start();
+      System.out.println("Post Start private method call");
+      r.run();
+      System.out.println("Pre Finish private method call");
+      Finish();
+      System.out.println("Post Finish private method call");
+    }
+  }
+
+  // static class Transform {
+  //   private void Start() {
+  //     System.out.println("Hello - private - Transformed");
+  //   }
+  //
+  //   private void Finish() {
+  //     System.out.println("Goodbye - private - Transformed");
+  //   }
+  //
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Pre Start private method call - Transformed");
+  //     Start();
+  //     System.out.println("Post Start private method call - Transformed");
+  //     r.run();
+  //     System.out.println("Pre Finish private method call - Transformed");
+  //     Finish();
+  //     System.out.println("Post Finish private method call - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQANgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
+    "JwcAKQcALAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
+    "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAM" +
+    "VGVzdDkxNS5qYXZhDAAPABAHAC0MAC4ALwEAHUhlbGxvIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVk" +
+    "BwAwDAAxADIBAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAQArUHJlIFN0YXJ0IHBy" +
+    "aXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwAEwAQAQAsUG9zdCBTdGFydCBwcml2YXRl" +
+    "IG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHADMMADQAEAEALFByZSBGaW5pc2ggcHJpdmF0ZSBt" +
+    "ZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAUABABAC1Qb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhv" +
+    "ZCBjYWxsIC0gVHJhbnNmb3JtZWQHADUBABVhcnQvVGVzdDkxNSRUcmFuc2Zvcm0BAAlUcmFuc2Zv" +
+    "cm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEA" +
+    "A291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmlu" +
+    "dGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuAQAL" +
+    "YXJ0L1Rlc3Q5MTUAIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAABABIA" +
+    "AAAGAAEAAAAFAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAAAAcA" +
+    "CAAIAAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAoACAALAAEA" +
+    "FQAWAAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQqtwAL" +
+    "sgACEgy2AASxAAAAAQASAAAAIgAIAAAADQAIAA4ADAAPABQAEAAaABEAIgASACYAEwAuABQAAgAX" +
+    "AAAAAgAYACsAAAAKAAEADQAoACoACA==");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAQ+GYcAAAAAAAAAAAAAAAAAAAAAAAAAADUBQAAcAAAAHhWNBIAAAAAAAAAABAFAAAd" +
+    "AAAAcAAAAAoAAADkAAAAAwAAAAwBAAABAAAAMAEAAAcAAAA4AQAAAQAAAHABAABEBAAAkAEAAJAB" +
+    "AACYAQAAoAEAAMEBAADgAQAA+QEAAAgCAAAsAgAATAIAAGMCAAB3AgAAjQIAAKECAAC1AgAA5AIA" +
+    "ABIDAABAAwAAbQMAAHQDAACCAwAAjQMAAJADAACUAwAAoQMAAKcDAACsAwAAtQMAALoDAADBAwAA" +
+    "BAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAAFAAAABQAAAAJAAAAAAAAABUAAAAJ" +
+    "AAAA0AMAABUAAAAJAAAAyAMAAAgABAAYAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAARAAAAAAABABsA" +
+    "AAAEAAIAGQAAAAUAAAAAAAAABgAAABoAAAAAAAAAAAAAAAUAAAAAAAAAEgAAAAAFAADMBAAAAAAA" +
+    "AAY8aW5pdD4ABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVsbG8g" +
+    "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAF0xhcnQvVGVzdDkxNSRUcmFuc2Zvcm07AA1MYXJ0L1Rl" +
+    "c3Q5MTU7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" +
+    "YXRpb24vSW5uZXJDbGFzczsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmpl" +
+    "Y3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5n" +
+    "L1N5c3RlbTsALVBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAs" +
+    "UG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALFByZSBGaW5pc2gg" +
+    "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACtQcmUgU3RhcnQgcHJpdmF0ZSBtZXRo" +
+    "b2QgY2FsbCAtIFRyYW5zZm9ybWVkAAVTdGFydAAMVGVzdDkxNS5qYXZhAAlUcmFuc2Zvcm0AAVYA" +
+    "AlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxuAANydW4ABXNheUhpAAV2YWx1ZQAB" +
+    "AAAABwAAAAEAAAAGAAAABQAHDgAKAAcOAQgPAAcABw4BCA8ADQEABw4BCA8BAw8BCA8BAw8BCA8B" +
+    "Aw8BCA8AAQABAAEAAADYAwAABAAAAHAQBQAAAA4AAwABAAIAAADdAwAACQAAAGIAAAAbAQIAAABu" +
+    "IAQAEAAOAAAAAwABAAIAAADlAwAACQAAAGIAAAAbAQMAAABuIAQAEAAOAAAABAACAAIAAADtAwAA" +
+    "KgAAAGIAAAAbARAAAABuIAQAEABwEAIAAgBiAAAAGwEOAAAAbiAEABAAchAGAAMAYgAAABsBDwAA" +
+    "AG4gBAAQAHAQAQACAGIAAAAbAQ0AAABuIAQAEAAOAAAAAwEAgIAEiAgBAqAIAQLECAMB6AgAAAIC" +
+    "ARwYAQIDAhYECBcXEwACAAAA5AQAAOoEAAD0BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAA" +
+    "AAEAAAAdAAAAcAAAAAIAAAAKAAAA5AAAAAMAAAADAAAADAEAAAQAAAABAAAAMAEAAAUAAAAHAAAA" +
+    "OAEAAAYAAAABAAAAcAEAAAIgAAAdAAAAkAEAAAEQAAACAAAAyAMAAAMgAAAEAAAA2AMAAAEgAAAE" +
+    "AAAACAQAAAAgAAABAAAAzAQAAAQgAAACAAAA5AQAAAMQAAABAAAA9AQAAAYgAAABAAAAAAUAAAAQ" +
+    "AAABAAAAEAUAAA==");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    t.sayHi(() -> {
+      System.out.println("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    });
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+}
diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java
index 3453261..cb202e4 100644
--- a/test/916-obsolete-jit/src/Main.java
+++ b/test/916-obsolete-jit/src/Main.java
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+
+import art.Redefinition;
+
 import java.util.function.Consumer;
 import java.lang.reflect.Method;
 import java.util.Base64;
@@ -144,7 +147,7 @@
         // Actually do the redefinition. The stack looks good.
         retry = false;
         w.accept("transforming calling function");
-        doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+        Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
       }
     };
     // This just prints something out to show we are running the Runnable.
@@ -168,9 +171,4 @@
   private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
 
   private static native void ensureJitCompiled(Class c, String name);
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/916-obsolete-jit/src/art/Redefinition.java b/test/916-obsolete-jit/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/916-obsolete-jit/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java
index 588af49..289b89f 100644
--- a/test/917-fields-transformation/src/Main.java
+++ b/test/917-fields-transformation/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,67 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
 public class Main {
-
-  // base64 encoded class/dex file for
-  // class Transform {
-  //   public String take1;
-  //   public String take2;
-  //
-  //   public Transform(String a, String b) {
-  //     take1 = a;
-  //     take2 = b;
-  //   }
-  //
-  //   public String getResult() {
-  //     return take2;
-  //   }
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAFwoABQARCQAEABIJAAQAEwcAFAcAFQEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
-    "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
-    "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
-    "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkAFgwABgAHDAAIAAcBAAlU" +
-    "cmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQADKClWACAABAAFAAAAAgABAAYABwAAAAEACAAH" +
-    "AAAAAgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAU" +
-    "AAQAFQAJABYADgAXAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAAGgAB" +
-    "AA8AAAACABA=");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQAGUTBb4jIABRlaI9rejdk7RCfyqR2kmNSkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAM" +
-    "AAAAcAAAAAQAAACgAAAAAwAAALAAAAACAAAA1AAAAAMAAADkAAAAAQAAAPwAAACIAQAAHAEAAFwB" +
-    "AABkAQAAZwEAAHQBAACIAQAAnAEAAKwBAACvAQAAtAEAAMgBAADTAQAA2gEAAAIAAAADAAAABAAA" +
-    "AAYAAAABAAAAAgAAAAAAAAAGAAAAAwAAAAAAAAAHAAAAAwAAAFQBAAAAAAIACgAAAAAAAgALAAAA" +
-    "AAACAAAAAAAAAAAACQAAAAEAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAUAAAAAAAAA8AEAAAAAAAAD" +
-    "AAMAAQAAAOEBAAAIAAAAcBACAAAAWwEAAFsCAQAOAAIAAQAAAAAA6wEAAAMAAABUEAEAEQAAAAIA" +
-    "AAACAAIABjxpbml0PgABTAALTFRyYW5zZm9ybTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv" +
-    "bGFuZy9TdHJpbmc7AA5UcmFuc2Zvcm0uamF2YQABVgADVkxMABJlbWl0dGVyOiBqYWNrLTQuMTkA" +
-    "CWdldFJlc3VsdAAFdGFrZTEABXRha2UyABQCAAAHDjwtLQAaAAcOAAACAQEAAQEBAIGABJwCAQG8" +
-    "AgAADQAAAAAAAAABAAAAAAAAAAEAAAAMAAAAcAAAAAIAAAAEAAAAoAAAAAMAAAADAAAAsAAAAAQA" +
-    "AAACAAAA1AAAAAUAAAADAAAA5AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA" +
-    "AAIgAAAMAAAAXAEAAAMgAAACAAAA4QEAAAAgAAABAAAA8AEAAAAQAAABAAAABAIAAA==");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform("Hello", "Goodbye"),
-           new Transform("start", "end"));
+  public static void main(String[] args) throws Exception {
+    art.Test917.run();
   }
-
-  private static void printTransform(Transform t) {
-    System.out.println("Result is " + t.getResult());
-    System.out.println("take1 is " + t.take1);
-    System.out.println("take2 is " + t.take2);
-  }
-  public static void doTest(Transform t1, Transform t2) {
-    printTransform(t1);
-    printTransform(t2);
-    doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-    printTransform(t1);
-    printTransform(t2);
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] class_file,
-                                                       byte[] dex_file);
 }
diff --git a/test/917-fields-transformation/src/Transform.java b/test/917-fields-transformation/src/Transform.java
deleted file mode 100644
index 6fe6223..0000000
--- a/test/917-fields-transformation/src/Transform.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public String take1;
-  public String take2;
-
-  public Transform(String take1, String take2) {
-    this.take1 = take1;
-    this.take2 = take2;
-  }
-
-  public String getResult() {
-    return take1;
-  }
-}
diff --git a/test/917-fields-transformation/src/art/Redefinition.java b/test/917-fields-transformation/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/917-fields-transformation/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/917-fields-transformation/src/art/Test917.java b/test/917-fields-transformation/src/art/Test917.java
new file mode 100644
index 0000000..245e92e
--- /dev/null
+++ b/test/917-fields-transformation/src/art/Test917.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+public class Test917 {
+
+  static class Transform {
+    public String take1;
+    public String take2;
+
+    public Transform(String take1, String take2) {
+      this.take1 = take1;
+      this.take2 = take2;
+    }
+
+    public String getResult() {
+      return take1;
+    }
+  }
+
+
+  // base64 encoded class/dex file for
+  // static class Transform {
+  //   public String take1;
+  //   public String take2;
+  //
+  //   public Transform(String a, String b) {
+  //     take1 = a;
+  //     take2 = b;
+  //   }
+  //
+  //   public String getResult() {
+  //     return take2;
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAGwoABQARCQAEABIJAAQAEwcAFQcAGAEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
+    "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
+    "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
+    "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAMVGVzdDkxNy5qYXZhDAAJABkMAAYABwwACAAHBwAaAQAV" +
+    "YXJ0L1Rlc3Q5MTckVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9s" +
+    "YW5nL09iamVjdAEAAygpVgEAC2FydC9UZXN0OTE3ACAABAAFAAAAAgABAAYABwAAAAEACAAHAAAA" +
+    "AgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAJAAQA" +
+    "CgAJAAsADgAMAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAADwACAA8A" +
+    "AAACABAAFwAAAAoAAQAEABQAFgAI");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBdcPySAAAAAAAAAAAAAAAAAAAAAAAAAACQAwAAcAAAAHhWNBIAAAAAAAAAAMwCAAAS" +
+    "AAAAcAAAAAcAAAC4AAAAAwAAANQAAAACAAAA+AAAAAMAAAAIAQAAAQAAACABAABQAgAAQAEAAEAB" +
+    "AABIAQAASwEAAGQBAABzAQAAlwEAALcBAADLAQAA3wEAAO0BAAD4AQAA+wEAAAACAAANAgAAGAIA" +
+    "AB4CAAAlAgAALAIAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAoAAAABAAAABQAAAAAAAAAKAAAA" +
+    "BgAAAAAAAAALAAAABgAAADQCAAAAAAUADwAAAAAABQAQAAAAAAACAAAAAAAAAAAADQAAAAQAAQAA" +
+    "AAAAAAAAAAAAAAAEAAAAAAAAAAgAAAC8AgAAjAIAAAAAAAAGPGluaXQ+AAFMABdMYXJ0L1Rlc3Q5" +
+    "MTckVHJhbnNmb3JtOwANTGFydC9UZXN0OTE3OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2lu" +
+    "Z0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5nL09iamVj" +
+    "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAMVGVzdDkxNy5qYXZhAAlUcmFuc2Zvcm0AAVYAA1ZMTAAL" +
+    "YWNjZXNzRmxhZ3MACWdldFJlc3VsdAAEbmFtZQAFdGFrZTEABXRha2UyAAV2YWx1ZQAAAgAAAAUA" +
+    "BQAJAgAABw4BAw8BAg8BAg8ADwAHDgAAAAADAAMAAQAAADwCAAAIAAAAcBACAAAAWwEAAFsCAQAO" +
+    "AAIAAQAAAAAATAIAAAMAAABUEAEAEQAAAAACAQEAAQEBAIGABNQEAQH0BAAAAgIBERgBAgMCDAQI" +
+    "DhcJAAIAAACgAgAApgIAALACAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAAABIAAABw" +
+    "AAAAAgAAAAcAAAC4AAAAAwAAAAMAAADUAAAABAAAAAIAAAD4AAAABQAAAAMAAAAIAQAABgAAAAEA" +
+    "AAAgAQAAAiAAABIAAABAAQAAARAAAAEAAAA0AgAAAyAAAAIAAAA8AgAAASAAAAIAAABUAgAAACAA" +
+    "AAEAAACMAgAABCAAAAIAAACgAgAAAxAAAAEAAACwAgAABiAAAAEAAAC8AgAAABAAAAEAAADMAgAA");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform("Hello", "Goodbye"),
+           new Transform("start", "end"));
+  }
+
+  private static void printTransform(Transform t) {
+    System.out.println("Result is " + t.getResult());
+    System.out.println("take1 is " + t.take1);
+    System.out.println("take2 is " + t.take2);
+  }
+  public static void doTest(Transform t1, Transform t2) {
+    printTransform(t1);
+    printTransform(t2);
+    Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    printTransform(t1);
+    printTransform(t2);
+  }
+}
diff --git a/test/919-obsolete-fields/src/Main.java b/test/919-obsolete-fields/src/Main.java
index 34ee2a9..10eadb2 100644
--- a/test/919-obsolete-fields/src/Main.java
+++ b/test/919-obsolete-fields/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,135 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.function.Consumer;
-import java.util.Base64;
-
 public class Main {
-
-  // What follows is the base64 encoded representation of the following class:
-  //
-  // import java.util.function.Consumer;
-  //
-  // class Transform {
-  //   private Consumer<String> reporter;
-  //   public Transform(Consumer<String> reporter) {
-  //     this.reporter = reporter;
-  //   }
-  //
-  //   private void Start() {
-  //     reporter.accept("Hello - private - Transformed");
-  //   }
-  //
-  //   private void Finish() {
-  //     reporter.accept("Goodbye - private - Transformed");
-  //   }
-  //
-  //   public void sayHi(Runnable r) {
-  //     reporter.accept("pre Start private method call - Transformed");
-  //     Start();
-  //     reporter.accept("post Start private method call - Transformed");
-  //     r.run();
-  //     reporter.accept("pre Finish private method call - Transformed");
-  //     Finish();
-  //     reporter.accept("post Finish private method call - Transformed");
-  //   }
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQANAoADgAfCQANACAIACELACIAIwgAJAgAJQoADQAmCAAnCwAoACkIACoKAA0AKwgA" +
-    "LAcALQcALgEACHJlcG9ydGVyAQAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsBAAlTaWdu" +
-    "YXR1cmUBADFMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47" +
-    "AQAGPGluaXQ+AQAgKExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAARDb2RlAQAPTGlu" +
-    "ZU51bWJlclRhYmxlAQA0KExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI8TGphdmEvbGFuZy9T" +
-    "dHJpbmc7PjspVgEABVN0YXJ0AQADKClWAQAGRmluaXNoAQAFc2F5SGkBABcoTGphdmEvbGFuZy9S" +
-    "dW5uYWJsZTspVgEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwAEwAZDAAPABABAB1IZWxs" +
-    "byAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAcALwwAMAAxAQAfR29vZGJ5ZSAtIHByaXZhdGUgLSBU" +
-    "cmFuc2Zvcm1lZAEAK3ByZSBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQM" +
-    "ABgAGQEALHBvc3QgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkBwAyDAAz" +
-    "ABkBACxwcmUgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwAGgAZAQAt" +
-    "cG9zdCBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkAQAJVHJhbnNmb3Jt" +
-    "AQAQamF2YS9sYW5nL09iamVjdAEAG2phdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcgEABmFjY2Vw" +
-    "dAEAFShMamF2YS9sYW5nL09iamVjdDspVgEAEmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgAgAA0A" +
-    "DgAAAAEAAgAPABAAAQARAAAAAgASAAQAAQATABQAAgAVAAAAKgACAAIAAAAKKrcAASortQACsQAA" +
-    "AAEAFgAAAA4AAwAAABUABAAWAAkAFwARAAAAAgAXAAIAGAAZAAEAFQAAACgAAgABAAAADCq0AAIS" +
-    "A7kABAIAsQAAAAEAFgAAAAoAAgAAABoACwAbAAIAGgAZAAEAFQAAACgAAgABAAAADCq0AAISBbkA" +
-    "BAIAsQAAAAEAFgAAAAoAAgAAAB4ACwAfAAEAGwAcAAEAFQAAAG8AAgACAAAAOyq0AAISBrkABAIA" +
-    "KrcAByq0AAISCLkABAIAK7kACQEAKrQAAhIKuQAEAgAqtwALKrQAAhIMuQAEAgCxAAAAAQAWAAAA" +
-    "IgAIAAAAIgALACMADwAkABoAJQAgACYAKwAnAC8AKAA6ACkAAQAdAAAAAgAe");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQAw/b59wCwTlSVDmuhPEezuK3oe0rtT4ujMBQAAcAAAAHhWNBIAAAAAAAAAAAgFAAAd" +
-    "AAAAcAAAAAYAAADkAAAABAAAAPwAAAABAAAALAEAAAcAAAA0AQAAAQAAAGwBAABABAAAjAEAAJoC" +
-    "AACdAgAAoAIAAKgCAACsAgAAsgIAALoCAADbAgAA+gIAAAcDAAAmAwAAOgMAAFADAABkAwAAggMA" +
-    "AKEDAACoAwAAuAMAALsDAAC/AwAAxwMAANsDAAAKBAAAOAQAAGYEAACTBAAAnQQAAKIEAACpBAAA" +
-    "CAAAAAkAAAAKAAAACwAAAA4AAAARAAAAEQAAAAUAAAAAAAAAEgAAAAUAAACEAgAAEgAAAAUAAACM" +
-    "AgAAEgAAAAUAAACUAgAAAAAEABkAAAAAAAMAAgAAAAAAAAAFAAAAAAAAAA8AAAAAAAIAGwAAAAIA" +
-    "AAACAAAAAwAAABoAAAAEAAEAEwAAAAAAAAAAAAAAAgAAAAAAAAAQAAAAZAIAAO8EAAAAAAAAAQAA" +
-    "ANEEAAABAAAA3wQAAAIAAgABAAAAsAQAAAYAAABwEAQAAABbAQAADgADAAEAAgAAALgEAAAJAAAA" +
-    "VCAAABsBBgAAAHIgBgAQAA4AAAADAAEAAgAAAL4EAAAJAAAAVCAAABsBBwAAAHIgBgAQAA4AAAAE" +
-    "AAIAAgAAAMQEAAAqAAAAVCAAABsBGAAAAHIgBgAQAHAQAgACAFQgAAAbARYAAAByIAYAEAByEAUA" +
-    "AwBUIAAAGwEXAAAAciAGABAAcBABAAIAVCAAABsBFQAAAHIgBgAQAA4AAAAAAAEAAAABAAAAAAAA" +
-    "AAAAAACMAQAAAAAAAJQBAAABAAAAAgAAAAEAAAADAAAAAQAAAAQAASgAATwABjxpbml0PgACPjsA" +
-    "BD47KVYABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVsbG8gLSBw" +
-    "cml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07AB1MZGFsdmlrL2Fubm90YXRpb24vU2ln" +
-    "bmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEv" +
-    "bGFuZy9TdHJpbmc7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAB1MamF2YS91dGlsL2Z1" +
-    "bmN0aW9uL0NvbnN1bWVyOwAFU3RhcnQADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAGYWNjZXB0ABJl" +
-    "bWl0dGVyOiBqYWNrLTQuMTkALXBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFu" +
-    "c2Zvcm1lZAAscG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALHBy" +
-    "ZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACtwcmUgU3RhcnQgcHJp" +
-    "dmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkAAhyZXBvcnRlcgADcnVuAAVzYXlIaQAFdmFs" +
-    "dWUAFQEABw48LQAeAAcOhwAaAAcOhwAiAQAHDoc8hzyHPIcAAgEBHBwEFw0XARcMFwMCAQEcHAUX" +
-    "ABcNFwEXDBcEAAEDAQACAIGABJwDAQK4AwEC3AMDAYAEABAAAAAAAAAAAQAAAAAAAAABAAAAHQAA" +
-    "AHAAAAACAAAABgAAAOQAAAADAAAABAAAAPwAAAAEAAAAAQAAACwBAAAFAAAABwAAADQBAAAGAAAA" +
-    "AQAAAGwBAAADEAAAAgAAAIwBAAABIAAABAAAAJwBAAAGIAAAAQAAAGQCAAABEAAAAwAAAIQCAAAC" +
-    "IAAAHQAAAJoCAAADIAAABAAAALAEAAAEIAAAAgAAANEEAAAAIAAAAQAAAO8EAAAAEAAAAQAAAAgF" +
-    "AAA=");
-
-  // A class that we can use to keep track of the output of this test.
-  private static class TestWatcher implements Consumer<String> {
-    private StringBuilder sb;
-    public TestWatcher() {
-      sb = new StringBuilder();
-    }
-
-    @Override
-    public void accept(String s) {
-      sb.append(s);
-      sb.append('\n');
-    }
-
-    public String getOutput() {
-      return sb.toString();
-    }
+  public static void main(String[] args) throws Exception {
+    art.Test919.run();
   }
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    TestWatcher w = new TestWatcher();
-    doTest(new Transform(w), w);
-  }
-
-  private static boolean interpreting = true;
-  private static boolean retry = false;
-
-  public static void doTest(Transform t, TestWatcher w) {
-    Runnable do_redefinition = () -> {
-      w.accept("transforming calling function");
-      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-    };
-    // This just prints something out to show we are running the Runnable.
-    Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
-
-    // Try and redefine.
-    t.sayHi(say_nothing);
-    t.sayHi(do_redefinition);
-    t.sayHi(say_nothing);
-
-    // Print output of last run.
-    System.out.print(w.getOutput());
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/919-obsolete-fields/src/Transform.java b/test/919-obsolete-fields/src/Transform.java
deleted file mode 100644
index c8e3cbd..0000000
--- a/test/919-obsolete-fields/src/Transform.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-import java.util.function.Consumer;
-
-class Transform {
-  private Consumer<String> reporter;
-  public Transform(Consumer<String> reporter) {
-    this.reporter = reporter;
-  }
-
-  private void Start() {
-    reporter.accept("hello - private");
-  }
-
-  private void Finish() {
-    reporter.accept("goodbye - private");
-  }
-
-  public void sayHi(Runnable r) {
-    reporter.accept("Pre Start private method call");
-    Start();
-    reporter.accept("Post Start private method call");
-    r.run();
-    reporter.accept("Pre Finish private method call");
-    Finish();
-    reporter.accept("Post Finish private method call");
-  }
-}
diff --git a/test/919-obsolete-fields/src/art/Redefinition.java b/test/919-obsolete-fields/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/919-obsolete-fields/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/919-obsolete-fields/src/art/Test919.java b/test/919-obsolete-fields/src/art/Test919.java
new file mode 100644
index 0000000..11971ef
--- /dev/null
+++ b/test/919-obsolete-fields/src/art/Test919.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.function.Consumer;
+import java.util.Base64;
+
+public class Test919 {
+
+  static class Transform {
+    private Consumer<String> reporter;
+    public Transform(Consumer<String> reporter) {
+      this.reporter = reporter;
+    }
+
+    private void Start() {
+      reporter.accept("hello - private");
+    }
+
+    private void Finish() {
+      reporter.accept("goodbye - private");
+    }
+
+    public void sayHi(Runnable r) {
+      reporter.accept("Pre Start private method call");
+      Start();
+      reporter.accept("Post Start private method call");
+      r.run();
+      reporter.accept("Pre Finish private method call");
+      Finish();
+      reporter.accept("Post Finish private method call");
+    }
+  }
+
+
+  // What follows is the base64 encoded representation of the following class:
+  //
+  // import java.util.function.Consumer;
+  //
+  // static class Transform {
+  //   private Consumer<String> reporter;
+  //   public Transform(Consumer<String> reporter) {
+  //     this.reporter = reporter;
+  //   }
+  //
+  //   private void Start() {
+  //     reporter.accept("Hello - private - Transformed");
+  //   }
+  //
+  //   private void Finish() {
+  //     reporter.accept("Goodbye - private - Transformed");
+  //   }
+  //
+  //   public void sayHi(Runnable r) {
+  //     reporter.accept("pre Start private method call - Transformed");
+  //     Start();
+  //     reporter.accept("post Start private method call - Transformed");
+  //     r.run();
+  //     reporter.accept("pre Finish private method call - Transformed");
+  //     Finish();
+  //     reporter.accept("post Finish private method call - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAOAoADgAfCQANACAIACELACIAIwgAJAgAJQoADQAmCAAnCwAoACkIACoKAA0AKwgA" +
+    "LAcALgcAMQEACHJlcG9ydGVyAQAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsBAAlTaWdu" +
+    "YXR1cmUBADFMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47" +
+    "AQAGPGluaXQ+AQAgKExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAARDb2RlAQAPTGlu" +
+    "ZU51bWJlclRhYmxlAQA0KExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI8TGphdmEvbGFuZy9T" +
+    "dHJpbmc7PjspVgEABVN0YXJ0AQADKClWAQAGRmluaXNoAQAFc2F5SGkBABcoTGphdmEvbGFuZy9S" +
+    "dW5uYWJsZTspVgEAClNvdXJjZUZpbGUBAAxUZXN0OTE5LmphdmEMABMAGQwADwAQAQAdSGVsbG8g" +
+    "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQHADIMADMANAEAH0dvb2RieWUgLSBwcml2YXRlIC0gVHJh" +
+    "bnNmb3JtZWQBACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAY" +
+    "ABkBACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANQwANgAZ" +
+    "AQAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQMABoAGQEALXBv" +
+    "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANwEAFWFydC9UZXN0" +
+    "OTE5JFRyYW5zZm9ybQEACVRyYW5zZm9ybQEADElubmVyQ2xhc3NlcwEAEGphdmEvbGFuZy9PYmpl" +
+    "Y3QBABtqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXIBAAZhY2NlcHQBABUoTGphdmEvbGFuZy9P" +
+    "YmplY3Q7KVYBABJqYXZhL2xhbmcvUnVubmFibGUBAANydW4BAAthcnQvVGVzdDkxOQAgAA0ADgAA" +
+    "AAEAAgAPABAAAQARAAAAAgASAAQAAQATABQAAgAVAAAAKgACAAIAAAAKKrcAASortQACsQAAAAEA" +
+    "FgAAAA4AAwAAAAgABAAJAAkACgARAAAAAgAXAAIAGAAZAAEAFQAAACgAAgABAAAADCq0AAISA7kA" +
+    "BAIAsQAAAAEAFgAAAAoAAgAAAA0ACwAOAAIAGgAZAAEAFQAAACgAAgABAAAADCq0AAISBbkABAIA" +
+    "sQAAAAEAFgAAAAoAAgAAABEACwASAAEAGwAcAAEAFQAAAG8AAgACAAAAOyq0AAISBrkABAIAKrcA" +
+    "Byq0AAISCLkABAIAK7kACQEAKrQAAhIKuQAEAgAqtwALKrQAAhIMuQAEAgCxAAAAAQAWAAAAIgAI" +
+    "AAAAFQALABYADwAXABoAGAAgABkAKwAaAC8AGwA6ABwAAgAdAAAAAgAeADAAAAAKAAEADQAtAC8A" +
+    "CA==");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBeEZYBAAAAAAAAAAAAAAAAAAAAAAAAAACMBgAAcAAAAHhWNBIAAAAAAAAAAMgFAAAi" +
+    "AAAAcAAAAAkAAAD4AAAABAAAABwBAAABAAAATAEAAAcAAABUAQAAAQAAAIwBAADgBAAArAEAAKwB" +
+    "AACvAQAAsgEAALoBAAC+AQAAxAEAAMwBAADtAQAADAIAACUCAAA0AgAAWAIAAHgCAACXAgAAqwIA" +
+    "AMECAADVAgAA8wIAABIDAAAZAwAAJwMAADIDAAA1AwAAOQMAAEEDAABOAwAAVAMAAIMDAACxAwAA" +
+    "3wMAAAwEAAAWBAAAGwQAACIEAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAAEQAAABUAAAAV" +
+    "AAAACAAAAAAAAAAWAAAACAAAADQEAAAWAAAACAAAADwEAAAWAAAACAAAACwEAAAAAAcAHgAAAAAA" +
+    "AwACAAAAAAAAAAUAAAAAAAAAEgAAAAAAAgAgAAAABQAAAAIAAAAGAAAAHwAAAAcAAQAXAAAAAAAA" +
+    "AAAAAAAFAAAAAAAAABMAAACoBQAARAUAAAAAAAABKAABPAAGPGluaXQ+AAI+OwAEPjspVgAGRmlu" +
+    "aXNoAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAB1IZWxsbyAtIHByaXZhdGUgLSBU" +
+    "cmFuc2Zvcm1lZAAXTGFydC9UZXN0OTE5JFRyYW5zZm9ybTsADUxhcnQvVGVzdDkxOTsAIkxkYWx2" +
+    "aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNs" +
+    "YXNzOwAdTGRhbHZpay9hbm5vdGF0aW9uL1NpZ25hdHVyZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAU" +
+    "TGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwAcTGphdmEvdXRpbC9mdW5j" +
+    "dGlvbi9Db25zdW1lcgAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsABVN0YXJ0AAxUZXN0" +
+    "OTE5LmphdmEACVRyYW5zZm9ybQABVgACVkwABmFjY2VwdAALYWNjZXNzRmxhZ3MABG5hbWUALXBv" +
+    "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscG9zdCBTdGFydCBw" +
+    "cml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRo" +
+    "b2QgY2FsbCAtIFRyYW5zZm9ybWVkACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRy" +
+    "YW5zZm9ybWVkAAhyZXBvcnRlcgADcnVuAAVzYXlIaQAFdmFsdWUAAAAAAQAAAAcAAAABAAAABQAA" +
+    "AAEAAAAGAAAACAEABw4BAw8BAg8AEQAHDgEIDwANAAcOAQgPABUBAAcOAQgPAQMPAQgPAQMPAQgP" +
+    "AQMPAQgPAAACAAIAAQAAAEQEAAAGAAAAcBAEAAAAWwEAAA4AAwABAAIAAABQBAAACQAAAFQgAAAb" +
+    "AQYAAAByIAYAEAAOAAAAAwABAAIAAABYBAAACQAAAFQgAAAbAQcAAAByIAYAEAAOAAAABAACAAIA" +
+    "AABgBAAAKgAAAFQgAAAbAR0AAAByIAYAEABwEAIAAgBUIAAAGwEbAAAAciAGABAAchAFAAMAVCAA" +
+    "ABsBHAAAAHIgBgAQAHAQAQACAFQgAAAbARoAAAByIAYAEAAOAAABAwEAAgCBgAT8CAECmAkBArwJ" +
+    "AwHgCQICASEYAQIDAhgECBkXFAIEASEcBBcQFwEXDxcDAgQBIRwFFwAXEBcBFw8XBAAAAAIAAABc" +
+    "BQAAYgUAAAEAAABrBQAAAQAAAHkFAACMBQAAAQAAAAEAAAAAAAAAAAAAAJgFAAAAAAAAoAUAABAA" +
+    "AAAAAAAAAQAAAAAAAAABAAAAIgAAAHAAAAACAAAACQAAAPgAAAADAAAABAAAABwBAAAEAAAAAQAA" +
+    "AEwBAAAFAAAABwAAAFQBAAAGAAAAAQAAAIwBAAACIAAAIgAAAKwBAAABEAAAAwAAACwEAAADIAAA" +
+    "BAAAAEQEAAABIAAABAAAAHwEAAAAIAAAAQAAAEQFAAAEIAAABAAAAFwFAAADEAAAAwAAAIwFAAAG" +
+    "IAAAAQAAAKgFAAAAEAAAAQAAAMgFAAA=");
+
+  // A class that we can use to keep track of the output of this test.
+  private static class TestWatcher implements Consumer<String> {
+    private StringBuilder sb;
+    public TestWatcher() {
+      sb = new StringBuilder();
+    }
+
+    @Override
+    public void accept(String s) {
+      sb.append(s);
+      sb.append('\n');
+    }
+
+    public String getOutput() {
+      return sb.toString();
+    }
+  }
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    TestWatcher w = new TestWatcher();
+    doTest(new Transform(w), w);
+  }
+
+  public static void doTest(Transform t, TestWatcher w) {
+    Runnable do_redefinition = () -> {
+      w.accept("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    };
+    // This just prints something out to show we are running the Runnable.
+    Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
+
+    // Try and redefine.
+    t.sayHi(say_nothing);
+    t.sayHi(do_redefinition);
+    t.sayHi(say_nothing);
+
+    // Print output of last run.
+    System.out.print(w.getOutput());
+  }
+}
diff --git a/test/921-hello-failure/src/Main.java b/test/921-hello-failure/src/Main.java
index d9a4948..cfdcdc2 100644
--- a/test/921-hello-failure/src/Main.java
+++ b/test/921-hello-failure/src/Main.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-import java.util.ArrayList;
+import art.Redefinition;
+import java.util.Arrays;
+
 public class Main {
 
   public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-
     Verification.doTest(new Transform());
     NewName.doTest(new Transform());
     DifferentAccess.doTest(new Transform());
@@ -37,40 +37,40 @@
     Unmodifiable.doTest(new Transform[] { new Transform(), });
   }
 
-  // Transforms the class. This throws an exception if something goes wrong.
-  public static native void doCommonClassRedefinition(Class<?> target,
-                                                      byte[] classfile,
-                                                      byte[] dexfile) throws Exception;
+  // TODO Replace this shim with a better re-write of this test.
+  private static Redefinition.CommonClassDefinition mapCCD(CommonClassDefinition d) {
+    return new Redefinition.CommonClassDefinition(d.target, d.class_file_bytes, d.dex_file_bytes);
+  }
 
+  private static Redefinition.CommonClassDefinition[] toCCDA(CommonClassDefinition[] ds) {
+    return Arrays.stream(ds).map(Main::mapCCD).toArray(Redefinition.CommonClassDefinition[]::new);
+  }
+
+  public static void doCommonClassRedefinition(Class<?> target,
+                                               byte[] classfile,
+                                               byte[] dexfile) throws Exception {
+    Redefinition.doCommonClassRedefinition(target, classfile, dexfile);
+  }
   public static void doMultiClassRedefinition(CommonClassDefinition... defs) throws Exception {
-    ArrayList<Class<?>> classes = new ArrayList<>();
-    ArrayList<byte[]> class_files = new ArrayList<>();
-    ArrayList<byte[]> dex_files = new ArrayList<>();
-
-    for (CommonClassDefinition d : defs) {
-      classes.add(d.target);
-      class_files.add(d.class_file_bytes);
-      dex_files.add(d.dex_file_bytes);
-    }
-    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
-                                   class_files.toArray(new byte[0][]),
-                                   dex_files.toArray(new byte[0][]));
+    Redefinition.doMultiClassRedefinition(toCCDA(defs));
   }
-
   public static void addMultiTransformationResults(CommonClassDefinition... defs) throws Exception {
-    for (CommonClassDefinition d : defs) {
-      addCommonTransformationResult(d.target.getCanonicalName(),
-                                    d.class_file_bytes,
-                                    d.dex_file_bytes);
-    }
+    Redefinition.addMultiTransformationResults(toCCDA(defs));
   }
-
-  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
-                                                           byte[][] classfiles,
-                                                           byte[][] dexfiles) throws Exception;
-  public static native void doCommonClassRetransformation(Class<?>... target) throws Exception;
-  public static native void enableCommonRetransformation(boolean enable);
-  public static native void addCommonTransformationResult(String target_name,
-                                                          byte[] class_bytes,
-                                                          byte[] dex_bytes);
+  public static void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                    byte[][] classfiles,
+                                                    byte[][] dexfiles) throws Exception {
+    Redefinition.doCommonMultiClassRedefinition(targets, classfiles, dexfiles);
+  }
+  public static void doCommonClassRetransformation(Class<?>... target) throws Exception {
+    Redefinition.doCommonClassRetransformation(target);
+  }
+  public static void enableCommonRetransformation(boolean enable) {
+    Redefinition.enableCommonRetransformation(enable);
+  }
+  public static void addCommonTransformationResult(String target_name,
+                                                   byte[] class_bytes,
+                                                   byte[] dex_bytes) {
+    Redefinition.addCommonTransformationResult(target_name, class_bytes, dex_bytes);
+  }
 }
diff --git a/test/921-hello-failure/src/art/Redefinition.java b/test/921-hello-failure/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/921-hello-failure/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/926-multi-obsolescence/src/CommonClassDefinition.java b/test/926-multi-obsolescence/src/CommonClassDefinition.java
deleted file mode 100644
index 62602a0..0000000
--- a/test/926-multi-obsolescence/src/CommonClassDefinition.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-class CommonClassDefinition {
-  public final Class<?> target;
-  public final byte[] class_file_bytes;
-  public final byte[] dex_file_bytes;
-
-  CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
-    this.target = target;
-    this.class_file_bytes = class_file_bytes;
-    this.dex_file_bytes = dex_file_bytes;
-  }
-}
diff --git a/test/926-multi-obsolescence/src/Main.java b/test/926-multi-obsolescence/src/Main.java
index 2440908..8e21b8f 100644
--- a/test/926-multi-obsolescence/src/Main.java
+++ b/test/926-multi-obsolescence/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,115 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.ArrayList;
-import java.util.Base64;
-
 public class Main {
-  // class Transform {
-  //   public void sayHi(Runnable r) {
-  //     System.out.println("Hello - Transformed");
-  //     r.run();
-  //     System.out.println("Goodbye - Transformed");
-  //   }
-  // }
-  private static CommonClassDefinition VALID_DEFINITION_T1 = new CommonClassDefinition(
-      Transform.class,
-      Base64.getDecoder().decode(
-        "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
-        "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
-        "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
-        "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
-        "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
-        "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
-        "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
-        "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsAAAA7AAIA" +
-        "AgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
-        "AQAPAAAAAgAQ"),
-      Base64.getDecoder().decode(
-        "ZGV4CjAzNQAYeAMMXgYWxoeSHAS9EWKCCtVRSAGpqZVQAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
-        "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
-        "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
-        "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
-        "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
-        "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
-        "AwAAAA4ABAACAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" +
-        "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
-        "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
-        "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
-        "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
-        "LjEzAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICABMQCAQHc" +
-        "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
-        "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
-        "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA="));
-  // class Transform2 {
-  //   public void sayHi(Runnable r) {
-  //     System.out.println("Hello 2 - Transformed");
-  //     r.run();
-  //     System.out.println("Goodbye 2 - Transformed");
-  //   }
-  // }
-  private static CommonClassDefinition VALID_DEFINITION_T2 = new CommonClassDefinition(
-      Transform2.class,
-      Base64.getDecoder().decode(
-        "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
-        "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
-        "KVYBAApTb3VyY2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAJAAoHABwMAB0AHgEAFUhlbGxvIDIg" +
-        "LSBUcmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABdHb29kYnllIDIgLSBUcmFuc2Zvcm1lZAEA" +
-        "ClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEA" +
-        "FUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAV" +
-        "KExqYXZhL2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAA" +
-        "AAACAAAACQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsA" +
-        "AAA7AAIAAgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4A" +
-        "BQAWAAYAAQAPAAAAAgAQ"),
-      Base64.getDecoder().decode(
-        "ZGV4CjAzNQCee5Z6+AuFcjnPjjn7QYgZmKSmFQCO4nxUAwAAcAAAAHhWNBIAAAAAAAAAALQCAAAR" +
-        "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAQAgAARAEAAKIB" +
-        "AACqAQAAwwEAANoBAADoAQAA/wEAABMCAAApAgAAPQIAAFECAABiAgAAZQIAAGkCAAB9AgAAggIA" +
-        "AIsCAACQAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
-        "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
-        "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAKUCAAAAAAAAAQABAAEAAACXAgAABAAAAHAQ" +
-        "AwAAAA4ABAACAAIAAACcAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" +
-        "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AF0dvb2RieWUgMiAtIFRyYW5zZm9ybWVkABVIZWxs" +
-        "byAyIC0gVHJhbnNmb3JtZWQADExUcmFuc2Zvcm0yOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJM" +
-        "amF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJMamF2YS9sYW5nL1N0cmlu" +
-        "ZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAPVHJhbnNmb3JtMi5qYXZhAAFWAAJWTAASZW1pdHRlcjog" +
-        "amFjay00LjIwAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICA" +
-        "BMQCAQHcAgANAAAAAAAAAAEAAAAAAAAAAQAAABEAAABwAAAAAgAAAAcAAAC0AAAAAwAAAAMAAADQ" +
-        "AAAABAAAAAEAAAD0AAAABQAAAAUAAAD8AAAABgAAAAEAAAAkAQAAASAAAAIAAABEAQAAARAAAAIA" +
-        "AACUAQAAAiAAABEAAACiAQAAAyAAAAIAAACXAgAAACAAAAEAAAClAgAAABAAAAEAAAC0AgAA"));
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform(), new Transform2());
+  public static void main(String[] args) throws Exception {
+    art.Test926.run();
   }
-
-  public static void doTest(final Transform t1, final Transform2 t2) {
-    t1.sayHi(() -> { t2.sayHi(() -> { System.out.println("Not doing anything here"); }); });
-    t1.sayHi(() -> {
-      t2.sayHi(() -> {
-        System.out.println("transforming calling functions");
-        doMultiClassRedefinition(VALID_DEFINITION_T1, VALID_DEFINITION_T2);
-      });
-    });
-    t1.sayHi(() -> { t2.sayHi(() -> { System.out.println("Not doing anything here"); }); });
-  }
-
-  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
-    ArrayList<Class<?>> classes = new ArrayList<>();
-    ArrayList<byte[]> class_files = new ArrayList<>();
-    ArrayList<byte[]> dex_files = new ArrayList<>();
-
-    for (CommonClassDefinition d : defs) {
-      classes.add(d.target);
-      class_files.add(d.class_file_bytes);
-      dex_files.add(d.dex_file_bytes);
-    }
-    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
-                                   class_files.toArray(new byte[0][]),
-                                   dex_files.toArray(new byte[0][]));
-  }
-
-  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
-                                                           byte[][] classfiles,
-                                                           byte[][] dexfiles);
 }
diff --git a/test/926-multi-obsolescence/src/Transform.java b/test/926-multi-obsolescence/src/Transform.java
deleted file mode 100644
index 8cda6cd..0000000
--- a/test/926-multi-obsolescence/src/Transform.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi(Runnable r) {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Hello" < "LTransform;" < "hello".
-    System.out.println("hello");
-    r.run();
-    System.out.println("goodbye");
-  }
-}
diff --git a/test/926-multi-obsolescence/src/Transform2.java b/test/926-multi-obsolescence/src/Transform2.java
deleted file mode 100644
index 4877f84..0000000
--- a/test/926-multi-obsolescence/src/Transform2.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform2 {
-  public void sayHi(Runnable r) {
-    System.out.println("hello - 2");
-    r.run();
-    System.out.println("goodbye - 2");
-  }
-}
diff --git a/test/926-multi-obsolescence/src/art/Redefinition.java b/test/926-multi-obsolescence/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/926-multi-obsolescence/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/926-multi-obsolescence/src/art/Test926.java b/test/926-multi-obsolescence/src/art/Test926.java
new file mode 100644
index 0000000..843d05c
--- /dev/null
+++ b/test/926-multi-obsolescence/src/art/Test926.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import static art.Redefinition.CommonClassDefinition;
+import java.util.ArrayList;
+import java.util.Base64;
+
+public class Test926 {
+
+  static class Transform {
+    public void sayHi(Runnable r) {
+      System.out.println("hello");
+      r.run();
+      System.out.println("goodbye");
+    }
+  }
+
+  static class Transform2 {
+    public void sayHi(Runnable r) {
+      System.out.println("hello - 2");
+      r.run();
+      System.out.println("goodbye - 2");
+    }
+  }
+  // static class Transform {
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     r.run();
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  // }
+  private static CommonClassDefinition VALID_DEFINITION_T1 = new CommonClassDefinition(
+      Transform.class,
+      Base64.getDecoder().decode(
+        "yv66vgAAADQAKAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAbBwAeAQAGPGluaXQ+AQADKClW" +
+        "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+        "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDkyNi5qYXZhDAAJAAoHAB8MACAAIQEAE0hlbGxvIC0gVHJh" +
+        "bnNmb3JtZWQHACIMACMAJAcAJQwAJgAKAQAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkBwAnAQAVYXJ0" +
+        "L1Rlc3Q5MjYkVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5n" +
+        "L09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsB" +
+        "ABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEA" +
+        "EmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgEAC2FydC9UZXN0OTI2ACAABwAIAAAAAAACAAAACQAK" +
+        "AAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAADAABAA0ADgABAAsAAAA7AAIAAgAA" +
+        "ABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAA4ACAAPAA4AEAAWABEAAgAP" +
+        "AAAAAgAQAB0AAAAKAAEABwAaABwACA=="),
+      Base64.getDecoder().decode(
+        "ZGV4CjAzNQB8m+R/AAAAAAAAAAAAAAAAAAAAAAAAAAA8BAAAcAAAAHhWNBIAAAAAAAAAAHgDAAAX" +
+        "AAAAcAAAAAoAAADMAAAAAwAAAPQAAAABAAAAGAEAAAUAAAAgAQAAAQAAAEgBAADUAgAAaAEAAGgB" +
+        "AABwAQAAhwEAAJwBAAC1AQAAxAEAAOgBAAAIAgAAHwIAADMCAABJAgAAXQIAAHECAAB/AgAAigIA" +
+        "AI0CAACRAgAAngIAAKQCAACpAgAAsgIAALcCAAC+AgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+        "CQAAAAoAAAALAAAADgAAAA4AAAAJAAAAAAAAAA8AAAAJAAAAyAIAAA8AAAAJAAAA0AIAAAgABAAS" +
+        "AAAAAAAAAAAAAAAAAAEAFQAAAAQAAgATAAAABQAAAAAAAAAGAAAAFAAAAAAAAAAAAAAABQAAAAAA" +
+        "AAAMAAAAaAMAADwDAAAAAAAABjxpbml0PgAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkABNIZWxsbyAt" +
+        "IFRyYW5zZm9ybWVkABdMYXJ0L1Rlc3Q5MjYkVHJhbnNmb3JtOwANTGFydC9UZXN0OTI2OwAiTGRh" +
+        "bHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVy" +
+        "Q2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEv" +
+        "bGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxU" +
+        "ZXN0OTI2LmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vzc0ZsYWdzAARuYW1lAANvdXQAB3By" +
+        "aW50bG4AA3J1bgAFc2F5SGkABXZhbHVlAAAAAAEAAAAGAAAAAQAAAAcAAAAMAAcOAA4BAAcOAQgP" +
+        "AQMPAQgPAAEAAQABAAAA2AIAAAQAAABwEAMAAAAOAAQAAgACAAAA3QIAABQAAABiAAAAGwECAAAA" +
+        "biACABAAchAEAAMAYgAAABsBAQAAAG4gAgAQAA4AAAABAQCAgATsBQEBhAYAAAICARYYAQIDAhAE" +
+        "CBEXDQACAAAATAMAAFIDAABcAwAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAXAAAA" +
+        "cAAAAAIAAAAKAAAAzAAAAAMAAAADAAAA9AAAAAQAAAABAAAAGAEAAAUAAAAFAAAAIAEAAAYAAAAB" +
+        "AAAASAEAAAIgAAAXAAAAaAEAAAEQAAACAAAAyAIAAAMgAAACAAAA2AIAAAEgAAACAAAA7AIAAAAg" +
+        "AAABAAAAPAMAAAQgAAACAAAATAMAAAMQAAABAAAAXAMAAAYgAAABAAAAaAMAAAAQAAABAAAAeAMA" +
+        "AA=="));
+  // static class Transform2 {
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Hello 2 - Transformed");
+  //     r.run();
+  //     System.out.println("Goodbye 2 - Transformed");
+  //   }
+  // }
+  private static CommonClassDefinition VALID_DEFINITION_T2 = new CommonClassDefinition(
+      Transform2.class,
+      Base64.getDecoder().decode(
+        "yv66vgAAADQAKAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAbBwAeAQAGPGluaXQ+AQADKClW" +
+        "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+        "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDkyNi5qYXZhDAAJAAoHAB8MACAAIQEAFUhlbGxvIDIgLSBU" +
+        "cmFuc2Zvcm1lZAcAIgwAIwAkBwAlDAAmAAoBABdHb29kYnllIDIgLSBUcmFuc2Zvcm1lZAcAJwEA" +
+        "FmFydC9UZXN0OTI2JFRyYW5zZm9ybTIBAApUcmFuc2Zvcm0yAQAMSW5uZXJDbGFzc2VzAQAQamF2" +
+        "YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0" +
+        "cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmlu" +
+        "ZzspVgEAEmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgEAC2FydC9UZXN0OTI2ACAABwAIAAAAAAAC" +
+        "AAAACQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAABQABAA0ADgABAAsAAAA7" +
+        "AAIAAgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAcACAAIAA4ACQAW" +
+        "AAoAAgAPAAAAAgAQAB0AAAAKAAEABwAaABwACA=="),
+      Base64.getDecoder().decode(
+        "ZGV4CjAzNQBCnaUuAAAAAAAAAAAAAAAAAAAAAAAAAABABAAAcAAAAHhWNBIAAAAAAAAAAHwDAAAX" +
+        "AAAAcAAAAAoAAADMAAAAAwAAAPQAAAABAAAAGAEAAAUAAAAgAQAAAQAAAEgBAADYAgAAaAEAAGgB" +
+        "AABwAQAAiQEAAKABAAC6AQAAyQEAAO0BAAANAgAAJAIAADgCAABOAgAAYgIAAHYCAACEAgAAkAIA" +
+        "AJMCAACXAgAApAIAAKoCAACvAgAAuAIAAL0CAADEAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+        "CQAAAAoAAAALAAAADgAAAA4AAAAJAAAAAAAAAA8AAAAJAAAAzAIAAA8AAAAJAAAA1AIAAAgABAAS" +
+        "AAAAAAAAAAAAAAAAAAEAFQAAAAQAAgATAAAABQAAAAAAAAAGAAAAFAAAAAAAAAAAAAAABQAAAAAA" +
+        "AAAMAAAAbAMAAEADAAAAAAAABjxpbml0PgAXR29vZGJ5ZSAyIC0gVHJhbnNmb3JtZWQAFUhlbGxv" +
+        "IDIgLSBUcmFuc2Zvcm1lZAAYTGFydC9UZXN0OTI2JFRyYW5zZm9ybTI7AA1MYXJ0L1Rlc3Q5MjY7" +
+        "ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24v" +
+        "SW5uZXJDbGFzczsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABRM" +
+        "amF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3Rl" +
+        "bTsADFRlc3Q5MjYuamF2YQAKVHJhbnNmb3JtMgABVgACVkwAC2FjY2Vzc0ZsYWdzAARuYW1lAANv" +
+        "dXQAB3ByaW50bG4AA3J1bgAFc2F5SGkABXZhbHVlAAABAAAABgAAAAEAAAAHAAAABQAHDgAHAQAH" +
+        "DgEIDwEDDwEIDwABAAEAAQAAANwCAAAEAAAAcBADAAAADgAEAAIAAgAAAOECAAAUAAAAYgAAABsB" +
+        "AgAAAG4gAgAQAHIQBAADAGIAAAAbAQEAAABuIAIAEAAOAAAAAQEAgIAE8AUBAYgGAAACAgEWGAEC" +
+        "AwIQBAgRFw0AAgAAAFADAABWAwAAYAMAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAA" +
+        "FwAAAHAAAAACAAAACgAAAMwAAAADAAAAAwAAAPQAAAAEAAAAAQAAABgBAAAFAAAABQAAACABAAAG" +
+        "AAAAAQAAAEgBAAACIAAAFwAAAGgBAAABEAAAAgAAAMwCAAADIAAAAgAAANwCAAABIAAAAgAAAPAC" +
+        "AAAAIAAAAQAAAEADAAAEIAAAAgAAAFADAAADEAAAAQAAAGADAAAGIAAAAQAAAGwDAAAAEAAAAQAA" +
+        "AHwDAAA="));
+
+  public static void run() throws Exception {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform(), new Transform2());
+  }
+
+  public static void doTest(final Transform t1, final Transform2 t2) throws Exception {
+    t1.sayHi(() -> { t2.sayHi(() -> { System.out.println("Not doing anything here"); }); });
+    t1.sayHi(() -> {
+      t2.sayHi(() -> {
+        System.out.println("transforming calling functions");
+        Redefinition.doMultiClassRedefinition(VALID_DEFINITION_T1, VALID_DEFINITION_T2);
+      });
+    });
+    t1.sayHi(() -> { t2.sayHi(() -> { System.out.println("Not doing anything here"); }); });
+  }
+}
diff --git a/test/930-hello-retransform/src/Main.java b/test/930-hello-retransform/src/Main.java
index da59c74..38c1d36 100644
--- a/test/930-hello-retransform/src/Main.java
+++ b/test/930-hello-retransform/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,57 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
 public class Main {
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye");
-   *   }
-   * }
-   */
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
-    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
-    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
-    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
-    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
-    "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
-    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
-    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
-    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
-    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
-    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
-    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
-    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
-    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
-    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
-    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test930.run();
   }
-
-  public static void doTest(Transform t) {
-    t.sayHi();
-    addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES);
-    enableCommonRetransformation(true);
-    doCommonClassRetransformation(Transform.class);
-    t.sayHi();
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRetransformation(Class<?>... target);
-  private static native void enableCommonRetransformation(boolean enable);
-  private static native void addCommonTransformationResult(String target_name,
-                                                           byte[] class_bytes,
-                                                           byte[] dex_bytes);
 }
diff --git a/test/930-hello-retransform/src/Transform.java b/test/930-hello-retransform/src/Transform.java
deleted file mode 100644
index 8e8af35..0000000
--- a/test/930-hello-retransform/src/Transform.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi() {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Goodbye" < "LTransform;" < "hello".
-    System.out.println("hello");
-  }
-}
diff --git a/test/930-hello-retransform/src/art/Redefinition.java b/test/930-hello-retransform/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/930-hello-retransform/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/930-hello-retransform/src/art/Test930.java b/test/930-hello-retransform/src/art/Test930.java
new file mode 100644
index 0000000..6c0fc16
--- /dev/null
+++ b/test/930-hello-retransform/src/art/Test930.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+public class Test930 {
+
+  static class Transform {
+    public void sayHi() {
+      System.out.println("hello");
+    }
+  }
+
+
+  /**
+   * base64 encoded class/dex file for
+   * static class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAIAoABgAOCQAPABAIABEKABIAEwcAFQcAGAEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAAxUZXN0OTMwLmphdmEMAAcA" +
+    "CAcAGQwAGgAbAQAHR29vZGJ5ZQcAHAwAHQAeBwAfAQAVYXJ0L1Rlc3Q5MzAkVHJhbnNmb3JtAQAJ" +
+    "VHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9T" +
+    "eXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFt" +
+    "AQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAC2FydC9UZXN0OTMwACAABQAGAAAA" +
+    "AAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAABQABAAsACAABAAkA" +
+    "AAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAHAAgACAACAAwAAAACAA0AFwAAAAoA" +
+    "AQAFABQAFgAI");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBsgu9qAAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" +
+    "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" +
+    "AABMAQAAVQEAAG4BAAB9AQAAoQEAAMEBAADYAQAA7AEAAAACAAAUAgAAIgIAAC0CAAAwAgAANAIA" +
+    "AEECAABHAgAATAIAAFUCAABcAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
+    "DAAAAAgAAAAAAAAADQAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
+    "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA5AIAALgCAAAAAAAABjxpbml0PgAHR29vZGJ5ZQAX" +
+    "TGFydC9UZXN0OTMwJFRyYW5zZm9ybTsADUxhcnQvVGVzdDkzMDsAIkxkYWx2aWsvYW5ub3RhdGlv" +
+    "bi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEv" +
+    "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" +
+    "TGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTMwLmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vz" +
+    "c0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAQAAAAYAAAAFAAcOAAcA" +
+    "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQEA" +
+    "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg4ECA8XCwACAAAAyAIAAM4CAADY" +
+    "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" +
+    "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" +
+    "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" +
+    "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA==");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_RETRANSFORM);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi();
+    Redefinition.addCommonTransformationResult("art/Test930$Transform", CLASS_BYTES, DEX_BYTES);
+    Redefinition.enableCommonRetransformation(true);
+    Redefinition.doCommonClassRetransformation(Transform.class);
+    t.sayHi();
+  }
+}
diff --git a/test/932-transform-saves/src/Main.java b/test/932-transform-saves/src/Main.java
index 14e5da0..fba40f6 100644
--- a/test/932-transform-saves/src/Main.java
+++ b/test/932-transform-saves/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,103 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
 public class Main {
-  /**
-   * base64 encoded class/dex file for
-   * class Transform {
-   *   public void sayHi() {
-   *    System.out.println("hello");
-   *   }
-   * }
-   */
-  private static final byte[] CLASS_BYTES_A = Base64.getDecoder().decode(
-      "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
-      "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
-      "BwAIBwAWDAAXABgBAAVoZWxsbwcAGQwAGgAbAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVj" +
-      "dAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZh" +
-      "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAgAAUABgAA" +
-      "AAAAAgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAABEAAQALAAgAAQAJ" +
-      "AAAAJQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAAGgAIABsAAQAMAAAAAgAN");
-  private static final byte[] DEX_BYTES_A = Base64.getDecoder().decode(
-      "ZGV4CjAzNQC6XWInnnDd1H4NdQ3P3inH8eCVmQI6W7LMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
-      "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
-      "AABqAQAAdwEAAI4BAACiAQAAtgEAAMoBAADaAQAA3QEAAOEBAAD1AQAA/AEAAAECAAAKAgAAAQAA" +
-      "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-      "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAABwCAAAA" +
-      "AAAAAQABAAEAAAARAgAABAAAAHAQAwAAAA4AAwABAAIAAAAWAgAACQAAAGIAAAAbAQoAAABuIAIA" +
-      "EAAOAAAAAQAAAAMABjxpbml0PgALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
-      "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
-      "OwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjIABWhlbGxvAANvdXQA" +
-      "B3ByaW50bG4ABXNheUhpABEABw4AGgAHDocAAAABAQCAgASgAgEBuAIAAA0AAAAAAAAAAQAAAAAA" +
-      "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
-      "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
-      "AgAAABECAAAAIAAAAQAAABwCAAAAEAAAAQAAACwCAAA=");
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye");
-   *   }
-   * }
-   */
-  private static final byte[] CLASS_BYTES_B = Base64.getDecoder().decode(
-    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
-    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
-    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
-    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
-    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
-    "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
-    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
-  private static final byte[] DEX_BYTES_B = Base64.getDecoder().decode(
-    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
-    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
-    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
-    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
-    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
-    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
-    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
-    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
-    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
-    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test932.run();
   }
-
-  public static void doTest(Transform t) {
-    // TODO We currently need to do this transform call since we don't have any way to make the
-    // original-dex-file a single-class dex-file letting us restore it easily. We should use the
-    // manipulation library that is being made when we store the original dex file.
-    // TODO REMOVE this theoretically does nothing but it ensures the original-dex-file we have set
-    // is one we can return to unaltered.
-    doCommonClassRedefinition(Transform.class, CLASS_BYTES_A, DEX_BYTES_A);
-    t.sayHi();
-
-    // Now turn it into DEX_BYTES_B so it says 'Goodbye'
-    addCommonTransformationResult("Transform", CLASS_BYTES_B, DEX_BYTES_B);
-    enableCommonRetransformation(true);
-    doCommonClassRetransformation(Transform.class);
-    t.sayHi();
-
-    // Now turn it back to normal by removing the load-hook and transforming again.
-    enableCommonRetransformation(false);
-    doCommonClassRetransformation(Transform.class);
-    t.sayHi();
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] class_bytes,
-                                                       byte[] dex_bytes);
-  private static native void doCommonClassRetransformation(Class<?>... target);
-  private static native void enableCommonRetransformation(boolean enable);
-  private static native void addCommonTransformationResult(String target_name,
-                                                           byte[] class_bytes,
-                                                           byte[] dex_bytes);
 }
diff --git a/test/932-transform-saves/src/Transform.java b/test/932-transform-saves/src/Transform.java
deleted file mode 100644
index 83f7aa4..0000000
--- a/test/932-transform-saves/src/Transform.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi() {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Goodbye" < "LTransform;" < "hello".
-    System.out.println("foobar");
-  }
-}
diff --git a/test/932-transform-saves/src/art/Redefinition.java b/test/932-transform-saves/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/932-transform-saves/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/932-transform-saves/src/art/Test932.java b/test/932-transform-saves/src/art/Test932.java
new file mode 100644
index 0000000..3a62232
--- /dev/null
+++ b/test/932-transform-saves/src/art/Test932.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+public class Test932 {
+
+  // This class is never used so just have it print out a bogus value so we can detect if something
+  // goes very wrong.
+  static class Transform {
+    public void sayHi() {
+      System.out.println("foobar");
+    }
+  }
+
+  /**
+   * base64 encoded class/dex file for
+   * static class Transform {
+   *   public void sayHi() {
+   *    System.out.println("hello");
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES_A = Base64.getDecoder().decode(
+    "yv66vgAAADQAIAoABgAOCQAPABAIABEKABIAEwcAFQcAGAEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAAxUZXN0OTMyLmphdmEMAAcA" +
+    "CAcAGQwAGgAbAQAFaGVsbG8HABwMAB0AHgcAHwEAFWFydC9UZXN0OTMyJFRyYW5zZm9ybQEACVRy" +
+    "YW5zZm9ybQEADElubmVyQ2xhc3NlcwEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3lz" +
+    "dGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEA" +
+    "B3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAthcnQvVGVzdDkzMgAgAAUABgAAAAAA" +
+    "AgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAUAAQALAAgAAQAJAAAA" +
+    "JQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAABwAIAAgAAgAMAAAAAgANABcAAAAKAAEA" +
+    "BQAUABYACA==");
+  private static final byte[] DEX_BYTES_A = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAngjnzAAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" +
+    "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" +
+    "AABMAQAAZQEAAHQBAACYAQAAuAEAAM8BAADjAQAA9wEAAAsCAAAZAgAAJAIAACcCAAArAgAAOAIA" +
+    "AD8CAABFAgAASgIAAFMCAABaAgAAAQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAALAAAA" +
+    "CwAAAAgAAAAAAAAADAAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
+    "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAJAAAA5AIAALgCAAAAAAAABjxpbml0PgAXTGFydC9UZXN0" +
+    "OTMyJFRyYW5zZm9ybTsADUxhcnQvVGVzdDkzMjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3Np" +
+    "bmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8vUHJpbnRT" +
+    "dHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFu" +
+    "Zy9TeXN0ZW07AAxUZXN0OTMyLmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vzc0ZsYWdzAAVo" +
+    "ZWxsbwAEbmFtZQADb3V0AAdwcmludGxuAAVzYXlIaQAFdmFsdWUAAAAAAQAAAAYAAAAFAAcOAAcA" +
+    "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQ4A" +
+    "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg0ECA8XCgACAAAAyAIAAM4CAADY" +
+    "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" +
+    "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" +
+    "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" +
+    "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA==");
+
+  /**
+   * base64 encoded class/dex file for
+   * static class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES_B = Base64.getDecoder().decode(
+    "yv66vgAAADQAIAoABgAOCQAPABAIABEKABIAEwcAFQcAGAEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAAxUZXN0OTMyLmphdmEMAAcA" +
+    "CAcAGQwAGgAbAQAHR29vZGJ5ZQcAHAwAHQAeBwAfAQAVYXJ0L1Rlc3Q5MzIkVHJhbnNmb3JtAQAJ" +
+    "VHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9T" +
+    "eXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFt" +
+    "AQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAC2FydC9UZXN0OTMyACAABQAGAAAA" +
+    "AAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAABQABAAsACAABAAkA" +
+    "AAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAHAAgACAACAAwAAAACAA0AFwAAAAoA" +
+    "AQAFABQAFgAI");
+  private static final byte[] DEX_BYTES_B = Base64.getDecoder().decode(
+    "ZGV4CjAzNQByglN3AAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" +
+    "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" +
+    "AABMAQAAVQEAAG4BAAB9AQAAoQEAAMEBAADYAQAA7AEAAAACAAAUAgAAIgIAAC0CAAAwAgAANAIA" +
+    "AEECAABHAgAATAIAAFUCAABcAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
+    "DAAAAAgAAAAAAAAADQAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
+    "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA5AIAALgCAAAAAAAABjxpbml0PgAHR29vZGJ5ZQAX" +
+    "TGFydC9UZXN0OTMyJFRyYW5zZm9ybTsADUxhcnQvVGVzdDkzMjsAIkxkYWx2aWsvYW5ub3RhdGlv" +
+    "bi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEv" +
+    "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" +
+    "TGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTMyLmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vz" +
+    "c0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAQAAAAYAAAAFAAcOAAcA" +
+    "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQEA" +
+    "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg4ECA8XCwACAAAAyAIAAM4CAADY" +
+    "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" +
+    "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" +
+    "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" +
+    "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA==");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_RETRANSFORM);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    // TODO We currently need to do this transform call since we don't have any way to make the
+    // original-dex-file a single-class dex-file letting us restore it easily. We should use the
+    // manipulation library that is being made when we store the original dex file.
+    // TODO REMOVE this theoretically does nothing but it ensures the original-dex-file we have set
+    // is one we can return to unaltered.
+    Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES_A, DEX_BYTES_A);
+    t.sayHi();
+
+    // Now turn it into DEX_BYTES_B so it says 'Goodbye'
+    Redefinition.addCommonTransformationResult("art/Test932$Transform", CLASS_BYTES_B, DEX_BYTES_B);
+    Redefinition.enableCommonRetransformation(true);
+    Redefinition.doCommonClassRetransformation(Transform.class);
+    t.sayHi();
+
+    // Now turn it back to normal by removing the load-hook and transforming again.
+    Redefinition.enableCommonRetransformation(false);
+    Redefinition.doCommonClassRetransformation(Transform.class);
+    t.sayHi();
+  }
+}
diff --git a/test/934-load-transform/src/Main.java b/test/934-load-transform/src/Main.java
index 606ce78..69c839f 100644
--- a/test/934-load-transform/src/Main.java
+++ b/test/934-load-transform/src/Main.java
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+import static art.Redefinition.addCommonTransformationResult;
+import static art.Redefinition.enableCommonRetransformation;
+import static art.Redefinition.setPopRetransformations;
+
 import java.lang.reflect.*;
 import java.util.Base64;
 
@@ -86,11 +90,4 @@
       e.printStackTrace();
     }
   }
-
-  private static native void setPopRetransformations(boolean should_pop);
-  // Transforms the class
-  private static native void enableCommonRetransformation(boolean enable);
-  private static native void addCommonTransformationResult(String target_name,
-                                                           byte[] class_bytes,
-                                                           byte[] dex_bytes);
 }
diff --git a/test/934-load-transform/src/art/Redefinition.java b/test/934-load-transform/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/934-load-transform/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/935-non-retransformable/src/Main.java b/test/935-non-retransformable/src/Main.java
index df92561..f240224 100644
--- a/test/935-non-retransformable/src/Main.java
+++ b/test/935-non-retransformable/src/Main.java
@@ -17,6 +17,8 @@
 import java.lang.reflect.*;
 import java.util.Base64;
 
+import art.Redefinition;
+
 class Main {
   public static String TEST_NAME = "935-non-retransformable";
 
@@ -74,10 +76,9 @@
   }
 
   public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    setPopRetransformations(false);
-    addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES);
-    enableCommonRetransformation(true);
+    Redefinition.setPopRetransformations(false);
+    Redefinition.addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES);
+    Redefinition.enableCommonRetransformation(true);
     try {
       /* this is the "alternate" DEX/Jar file */
       ClassLoader new_loader = getClassLoaderFor(System.getenv("DEX_LOCATION"));
@@ -89,23 +90,14 @@
       run_test.invoke(null);
 
       // Remove the original transformation. It has been used by now.
-      popTransformationFor("Transform");
+      Redefinition.popTransformationFor("Transform");
       // Make sure we don't get called for transformation again.
-      addCommonTransformationResult("Transform", new byte[0], new byte[0]);
-      doCommonClassRetransformation(new_loader.loadClass("Transform"));
+      Redefinition.addCommonTransformationResult("Transform", new byte[0], new byte[0]);
+      Redefinition.doCommonClassRetransformation(new_loader.loadClass("Transform"));
       run_test.invoke(null);
     } catch (Exception e) {
       System.out.println(e.toString());
       e.printStackTrace();
     }
   }
-
-  // Transforms the class
-  private static native void doCommonClassRetransformation(Class<?>... classes);
-  private static native void enableCommonRetransformation(boolean enable);
-  private static native void addCommonTransformationResult(String target_name,
-                                                           byte[] class_bytes,
-                                                           byte[] dex_bytes);
-  private static native void setPopRetransformations(boolean should_pop);
-  private static native void popTransformationFor(String target_name);
 }
diff --git a/test/935-non-retransformable/src/art/Redefinition.java b/test/935-non-retransformable/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/935-non-retransformable/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/937-hello-retransform-package/src/Main.java b/test/937-hello-retransform-package/src/Main.java
index 866f75d..eef56c2 100644
--- a/test/937-hello-retransform-package/src/Main.java
+++ b/test/937-hello-retransform-package/src/Main.java
@@ -17,6 +17,8 @@
 import java.util.Base64;
 
 import testing.*;
+import art.Redefinition;
+
 public class Main {
 
   /**
@@ -53,22 +55,14 @@
     "YgEAAAMgAAACAAAAGwIAAAAgAAABAAAAJgIAAAAQAAABAAAANAIAAA==");
 
   public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
     doTest(new Transform());
   }
 
   public static void doTest(Transform t) {
     t.sayHi();
-    addCommonTransformationResult("testing/Transform", CLASS_BYTES, DEX_BYTES);
-    enableCommonRetransformation(true);
-    doCommonClassRetransformation(Transform.class);
+    Redefinition.addCommonTransformationResult("testing/Transform", CLASS_BYTES, DEX_BYTES);
+    Redefinition.enableCommonRetransformation(true);
+    Redefinition.doCommonClassRetransformation(Transform.class);
     t.sayHi();
   }
-
-  // Transforms the class
-  private static native void doCommonClassRetransformation(Class<?>... target);
-  private static native void enableCommonRetransformation(boolean enable);
-  private static native void addCommonTransformationResult(String target_name,
-                                                           byte[] class_bytes,
-                                                           byte[] dex_bytes);
 }
diff --git a/test/937-hello-retransform-package/src/art/Redefinition.java b/test/937-hello-retransform-package/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/937-hello-retransform-package/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/938-load-transform-bcp/src/Main.java b/test/938-load-transform-bcp/src/Main.java
index 21b841f..e560942 100644
--- a/test/938-load-transform-bcp/src/Main.java
+++ b/test/938-load-transform-bcp/src/Main.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import static art.Redefinition.*;
 import java.lang.reflect.*;
 import java.util.Base64;
 
@@ -114,11 +115,4 @@
       e.printStackTrace();
     }
   }
-
-  private static native void setPopRetransformations(boolean should_pop);
-  // Transforms the class
-  private static native void enableCommonRetransformation(boolean enable);
-  private static native void addCommonTransformationResult(String target_name,
-                                                           byte[] class_bytes,
-                                                           byte[] dex_bytes);
 }
diff --git a/test/938-load-transform-bcp/src/art/Redefinition.java b/test/938-load-transform-bcp/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/938-load-transform-bcp/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/939-hello-transformation-bcp/src/Main.java b/test/939-hello-transformation-bcp/src/Main.java
index 0e1f845..7bda667 100644
--- a/test/939-hello-transformation-bcp/src/Main.java
+++ b/test/939-hello-transformation-bcp/src/Main.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import static art.Redefinition.doCommonClassRedefinition;
 import java.util.Base64;
 import java.util.OptionalLong;
 public class Main {
@@ -110,7 +111,6 @@
     "AABHBgAABCAAAAIAAACVBgAAACAAAAEAAACtBgAAABAAAAEAAAD4BgAA");
 
   public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
     // OptionalLong is a class that is unlikely to be used by the time this test starts and is not
     // likely to be changed in any meaningful way in the future.
     OptionalLong ol = OptionalLong.of(0xDEADBEEF);
@@ -119,9 +119,4 @@
     doCommonClassRedefinition(OptionalLong.class, CLASS_BYTES, DEX_BYTES);
     System.out.println("ol.toString() -> '" + ol.toString() + "'");
   }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] class_file,
-                                                       byte[] dex_file);
 }
diff --git a/test/939-hello-transformation-bcp/src/art/Redefinition.java b/test/939-hello-transformation-bcp/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/939-hello-transformation-bcp/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/940-recursive-obsolete/src/Main.java b/test/940-recursive-obsolete/src/Main.java
index 724f82d..0b0211c 100644
--- a/test/940-recursive-obsolete/src/Main.java
+++ b/test/940-recursive-obsolete/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,77 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
-
 public class Main {
-
-  // class Transform {
-  //   public void sayHi(int recur, Runnable r) {
-  //     System.out.println("Hello" + recur + " - transformed");
-  //     if (recur == 1) {
-  //       r.run();
-  //       sayHi(recur - 1, r);
-  //     } else if (recur != 0) {
-  //       sayHi(recur - 1, r);
-  //     }
-  //     System.out.println("Goodbye" + recur + " - transformed");
-  //   }
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQANwoADwAZCQAaABsHABwKAAMAGQgAHQoAAwAeCgADAB8IACAKAAMAIQoAIgAjCwAk" +
-    "ACUKAA4AJggAJwcAKAcAKQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUB" +
-    "AAVzYXlIaQEAGChJTGphdmEvbGFuZy9SdW5uYWJsZTspVgEADVN0YWNrTWFwVGFibGUBAApTb3Vy" +
-    "Y2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMABAAEQcAKgwAKwAsAQAXamF2YS9sYW5nL1N0cmluZ0J1" +
-    "aWxkZXIBAAVIZWxsbwwALQAuDAAtAC8BAA4gLSB0cmFuc2Zvcm1lZAwAMAAxBwAyDAAzADQHADUM" +
-    "ADYAEQwAFAAVAQAHR29vZGJ5ZQEACVRyYW5zZm9ybQEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZh" +
-    "L2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQAGYXBwZW5kAQAtKExq" +
-    "YXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAcKEkpTGphdmEvbGFu" +
-    "Zy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nAQAUKClMamF2YS9sYW5nL1N0cmluZzsBABNqYXZh" +
-    "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAEmphdmEv" +
-    "bGFuZy9SdW5uYWJsZQEAA3J1bgAgAA4ADwAAAAAAAgAAABAAEQABABIAAAAdAAEAAQAAAAUqtwAB" +
-    "sQAAAAEAEwAAAAYAAQAAAAEAAQAUABUAAQASAAAAnQADAAMAAABfsgACuwADWbcABBIFtgAGG7YA" +
-    "BxIItgAGtgAJtgAKGwSgABQsuQALAQAqGwRkLLYADKcADxuZAAsqGwRkLLYADLIAArsAA1m3AAQS" +
-    "DbYABhu2AAcSCLYABrYACbYACrEAAAACABMAAAAiAAgAAAADAB4ABAAjAAUAKQAGADQABwA4AAgA" +
-    "QAAKAF4ACwAWAAAABAACNAsAAQAXAAAAAgAY");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQA3pkIgnymz2/eri+mp2dyZo3jolQmaRPKEBAAAcAAAAHhWNBIAAAAAAAAAAOQDAAAa" +
-    "AAAAcAAAAAkAAADYAAAABgAAAPwAAAABAAAARAEAAAkAAABMAQAAAQAAAJQBAADQAgAAtAEAAJwC" +
-    "AACsAgAAtAIAAL0CAADEAgAAxwIAAMoCAADOAgAA0gIAAN8CAAD2AgAACgMAACADAAA0AwAATwMA" +
-    "AGMDAABzAwAAdgMAAHsDAAB/AwAAhwMAAJsDAACgAwAAqQMAAK4DAAC1AwAABAAAAAgAAAAJAAAA" +
-    "CgAAAAsAAAAMAAAADQAAAA4AAAAQAAAABQAAAAUAAAAAAAAABgAAAAYAAACEAgAABwAAAAYAAACM" +
-    "AgAAEAAAAAgAAAAAAAAAEQAAAAgAAACUAgAAEgAAAAgAAACMAgAABwACABUAAAABAAMAAQAAAAEA" +
-    "BAAYAAAAAgAFABYAAAADAAMAAQAAAAQAAwAXAAAABgADAAEAAAAGAAEAEwAAAAYAAgATAAAABgAA" +
-    "ABkAAAABAAAAAAAAAAMAAAAAAAAADwAAAAAAAADWAwAAAAAAAAEAAQABAAAAvwMAAAQAAABwEAMA" +
-    "AAAOAAYAAwADAAAAxAMAAFQAAABiAAAAIgEGAHAQBQABABsCAwAAAG4gBwAhAAwBbiAGAEEADAEb" +
-    "AgAAAABuIAcAIQAMAW4QCAABAAwBbiACABAAEhAzBCsAchAEAAUA2AAE/24wAQADBWIAAAAiAQYA" +
-    "cBAFAAEAGwICAAAAbiAHACEADAFuIAYAQQAMARsCAAAAAG4gBwAhAAwBbhAIAAEADAFuIAIAEAAO" +
-    "ADgE3//YAAT/bjABAAMFKNgBAAAAAAAAAAEAAAAFAAAAAgAAAAAABAAOIC0gdHJhbnNmb3JtZWQA" +
-    "Bjxpbml0PgAHR29vZGJ5ZQAFSGVsbG8AAUkAAUwAAkxJAAJMTAALTFRyYW5zZm9ybTsAFUxqYXZh" +
-    "L2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxl" +
-    "OwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABJMamF2YS9s" +
-    "YW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAANWSUwAAlZMAAZhcHBlbmQAEmVtaXR0ZXI6" +
-    "IGphY2stNC4yNAADb3V0AAdwcmludGxuAANydW4ABXNheUhpAAh0b1N0cmluZwABAAcOAAMCAAAH" +
-    "DgEgDzw8XQEgDxktAAAAAQEAgIAEtAMBAcwDDQAAAAAAAAABAAAAAAAAAAEAAAAaAAAAcAAAAAIA" +
-    "AAAJAAAA2AAAAAMAAAAGAAAA/AAAAAQAAAABAAAARAEAAAUAAAAJAAAATAEAAAYAAAABAAAAlAEA" +
-    "AAEgAAACAAAAtAEAAAEQAAADAAAAhAIAAAIgAAAaAAAAnAIAAAMgAAACAAAAvwMAAAAgAAABAAAA" +
-    "1gMAAAAQAAABAAAA5AMAAA==");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test940.run();
   }
-
-  public static void doTest(Transform t) {
-    t.sayHi(2, () -> { System.out.println("Not doing anything here"); });
-    t.sayHi(2, () -> {
-      System.out.println("transforming calling function");
-      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-    });
-    t.sayHi(2, () -> { System.out.println("Not doing anything here"); });
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/940-recursive-obsolete/src/Transform.java b/test/940-recursive-obsolete/src/Transform.java
deleted file mode 100644
index 97522cd..0000000
--- a/test/940-recursive-obsolete/src/Transform.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi(int recur, Runnable r) {
-    System.out.println("hello" + recur);
-    if (recur == 1) {
-      r.run();
-      sayHi(recur - 1, r);
-    } else if (recur != 0) {
-      sayHi(recur - 1, r);
-    }
-    System.out.println("goodbye" + recur);
-  }
-}
diff --git a/test/940-recursive-obsolete/src/art/Redefinition.java b/test/940-recursive-obsolete/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/940-recursive-obsolete/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/940-recursive-obsolete/src/art/Test940.java b/test/940-recursive-obsolete/src/art/Test940.java
new file mode 100644
index 0000000..d67d772
--- /dev/null
+++ b/test/940-recursive-obsolete/src/art/Test940.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+
+public class Test940 {
+
+  static class Transform {
+    public void sayHi(int recur, Runnable r) {
+      System.out.println("hello" + recur);
+      if (recur == 1) {
+        r.run();
+        sayHi(recur - 1, r);
+      } else if (recur != 0) {
+        sayHi(recur - 1, r);
+      }
+      System.out.println("goodbye" + recur);
+    }
+  }
+
+
+  // static class Transform {
+  //   public void sayHi(int recur, Runnable r) {
+  //     System.out.println("Hello" + recur + " - transformed");
+  //     if (recur == 1) {
+  //       r.run();
+  //       sayHi(recur - 1, r);
+  //     } else if (recur != 0) {
+  //       sayHi(recur - 1, r);
+  //     }
+  //     System.out.println("Goodbye" + recur + " - transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAOwoADwAZCQAaABsHABwKAAMAGQgAHQoAAwAeCgADAB8IACAKAAMAIQoAIgAjCwAk" +
+    "ACUKAA4AJggAJwcAKQcALAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUB" +
+    "AAVzYXlIaQEAGChJTGphdmEvbGFuZy9SdW5uYWJsZTspVgEADVN0YWNrTWFwVGFibGUBAApTb3Vy" +
+    "Y2VGaWxlAQAMVGVzdDk0MC5qYXZhDAAQABEHAC0MAC4ALwEAF2phdmEvbGFuZy9TdHJpbmdCdWls" +
+    "ZGVyAQAFSGVsbG8MADAAMQwAMAAyAQAOIC0gdHJhbnNmb3JtZWQMADMANAcANQwANgA3BwA4DAA5" +
+    "ABEMABQAFQEAB0dvb2RieWUHADoBABVhcnQvVGVzdDk0MCRUcmFuc2Zvcm0BAAlUcmFuc2Zvcm0B" +
+    "AAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291" +
+    "dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzsp" +
+    "TGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEAHChJKUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsB" +
+    "AAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQATamF2YS9pby9QcmludFN0cmVhbQEA" +
+    "B3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABJqYXZhL2xhbmcvUnVubmFibGUBAANy" +
+    "dW4BAAthcnQvVGVzdDk0MAAgAA4ADwAAAAAAAgAAABAAEQABABIAAAAdAAEAAQAAAAUqtwABsQAA" +
+    "AAEAEwAAAAYAAQAAAAUAAQAUABUAAQASAAAAnQADAAMAAABfsgACuwADWbcABBIFtgAGG7YABxII" +
+    "tgAGtgAJtgAKGwSgABQsuQALAQAqGwRkLLYADKcADxuZAAsqGwRkLLYADLIAArsAA1m3AAQSDbYA" +
+    "Bhu2AAcSCLYABrYACbYACrEAAAACABMAAAAiAAgAAAAHAB4ACAAjAAkAKQAKADQACwA4AAwAQAAO" +
+    "AF4ADwAWAAAABAACNAsAAgAXAAAAAgAYACsAAAAKAAEADgAoACoACA==");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQDQv3jgAAAAAAAAAAAAAAAAAAAAAAAAAAB8BQAAcAAAAHhWNBIAAAAAAAAAALgEAAAg" +
+    "AAAAcAAAAAwAAADwAAAABgAAACABAAABAAAAaAEAAAkAAABwAQAAAQAAALgBAACkAwAA2AEAANgB" +
+    "AADoAQAA8AEAAPkBAAAAAgAAAwIAAAYCAAAKAgAADgIAACcCAAA2AgAAWgIAAHoCAACRAgAApQIA" +
+    "ALsCAADPAgAA6gIAAP4CAAAMAwAAFwMAABoDAAAfAwAAIwMAADADAAA4AwAAPgMAAEMDAABMAwAA" +
+    "UQMAAFgDAABiAwAABAAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAEAAAABEAAAAU" +
+    "AAAABQAAAAgAAAAAAAAABgAAAAkAAAB8AwAABwAAAAkAAAB0AwAAFAAAAAsAAAAAAAAAFQAAAAsA" +
+    "AABsAwAAFgAAAAsAAAB0AwAACgAFABoAAAABAAMAAQAAAAEABAAdAAAABQAFABsAAAAGAAMAAQAA" +
+    "AAcAAwAcAAAACQADAAEAAAAJAAEAGAAAAAkAAgAYAAAACQAAAB4AAAABAAAAAAAAAAYAAAAAAAAA" +
+    "EgAAAKgEAAB8BAAAAAAAAA4gLSB0cmFuc2Zvcm1lZAAGPGluaXQ+AAdHb29kYnllAAVIZWxsbwAB" +
+    "SQABTAACTEkAAkxMABdMYXJ0L1Rlc3Q5NDAkVHJhbnNmb3JtOwANTGFydC9UZXN0OTQwOwAiTGRh" +
+    "bHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVy" +
+    "Q2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEv" +
+    "bGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwAZTGphdmEvbGFuZy9TdHJpbmdCdWls" +
+    "ZGVyOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTQwLmphdmEACVRyYW5zZm9ybQABVgADVklM" +
+    "AAJWTAALYWNjZXNzRmxhZ3MABmFwcGVuZAAEbmFtZQADb3V0AAdwcmludGxuAANydW4ABXNheUhp" +
+    "AAh0b1N0cmluZwAFdmFsdWUAAAAAAgAAAAAABwABAAAACAAAAAEAAAAAAAAABQAHDgAHAgAABw4B" +
+    "IA8BAw8BAw8BBRIBIA8BAQoBAg8AAAAAAQABAAEAAACEAwAABAAAAHAQAwAAAA4ABgADAAMAAACJ" +
+    "AwAAVQAAAGIAAAAiAQkAcBAFAAEAGwIDAAAAbiAHACEADAFuIAYAQQAMARsCAAAAAG4gBwAhAAwB" +
+    "bhAIAAEADAFuIAIAEAASEDMEKwByEAQABQDYAAT/bjABAAMFYgAAACIBCQBwEAUAAQAbAgIAAABu" +
+    "IAcAIQAMAW4gBgBBAAwBGwIAAAAAbiAHACEADAFuEAgAAQAMAW4gAgAQAA4AOATf/9gABP9uMAEA" +
+    "AwUpANj/AAAAAAEBAICABKgHAQHABwAAAgMBHxgCAgQCFwQIGRcTAAIAAACMBAAAkgQAAJwEAAAA" +
+    "AAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAAACAAAABwAAAAAgAAAAwAAADwAAAAAwAAAAYA" +
+    "AAAgAQAABAAAAAEAAABoAQAABQAAAAkAAABwAQAABgAAAAEAAAC4AQAAAiAAACAAAADYAQAAARAA" +
+    "AAMAAABsAwAAAyAAAAIAAACEAwAAASAAAAIAAACoAwAAACAAAAEAAAB8BAAABCAAAAIAAACMBAAA" +
+    "AxAAAAEAAACcBAAABiAAAAEAAACoBAAAABAAAAEAAAC4BAAA");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(2, () -> { System.out.println("Not doing anything here"); });
+    t.sayHi(2, () -> {
+      System.out.println("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    });
+    t.sayHi(2, () -> { System.out.println("Not doing anything here"); });
+  }
+}
diff --git a/test/941-recurive-obsolete-jit/src/Main.java b/test/941-recurive-obsolete-jit/src/Main.java
index d88bb9b..1c391a4 100644
--- a/test/941-recurive-obsolete-jit/src/Main.java
+++ b/test/941-recurive-obsolete-jit/src/Main.java
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+import static art.Redefinition.doCommonClassRedefinition;
+
 import java.util.Base64;
 import java.util.function.Consumer;
 import java.lang.reflect.Method;
@@ -148,9 +150,4 @@
   private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
 
   private static native void ensureJitCompiled(Class c, String name);
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/941-recurive-obsolete-jit/src/art/Redefinition.java b/test/941-recurive-obsolete-jit/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/941-recurive-obsolete-jit/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/942-private-recursive/src/Main.java b/test/942-private-recursive/src/Main.java
index cac75c0..8a1f7c6 100644
--- a/test/942-private-recursive/src/Main.java
+++ b/test/942-private-recursive/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,82 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
-
 public class Main {
-
-  // class Transform {
-  //   public void sayHi(int recur, Runnable r) {
-  //     privateSayHi(recur, r);
-  //   }
-  //   private void privateSayHi(int recur, Runnable r) {
-  //     System.out.println("Hello" + recur + " - transformed");
-  //     if (recur == 1) {
-  //       r.run();
-  //       privateSayHi(recur - 1, r);
-  //     } else if (recur != 0) {
-  //       privateSayHi(recur - 1, r);
-  //     }
-  //     System.out.println("Goodbye" + recur + " - transformed");
-  //   }
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAOAoADwAaCgAOABsJABwAHQcAHgoABAAaCAAfCgAEACAKAAQAIQgAIgoABAAjCgAk" +
-    "ACULACYAJwgAKAcAKQcAKgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUB" +
-    "AAVzYXlIaQEAGChJTGphdmEvbGFuZy9SdW5uYWJsZTspVgEADHByaXZhdGVTYXlIaQEADVN0YWNr" +
-    "TWFwVGFibGUBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMABAAEQwAFgAVBwArDAAsAC0B" +
-    "ABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEABUhlbGxvDAAuAC8MAC4AMAEADiAtIHRyYW5zZm9y" +
-    "bWVkDAAxADIHADMMADQANQcANgwANwARAQAHR29vZGJ5ZQEACVRyYW5zZm9ybQEAEGphdmEvbGFu" +
-    "Zy9PYmplY3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07" +
-    "AQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7" +
-    "AQAcKEkpTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nAQAUKClMamF2YS9sYW5n" +
-    "L1N0cmluZzsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0" +
-    "cmluZzspVgEAEmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgAgAA4ADwAAAAAAAwAAABAAEQABABIA" +
-    "AAAdAAEAAQAAAAUqtwABsQAAAAEAEwAAAAYAAQAAAAEAAQAUABUAAQASAAAAIwADAAMAAAAHKhss" +
-    "twACsQAAAAEAEwAAAAoAAgAAAAMABgAEAAIAFgAVAAEAEgAAAJ0AAwADAAAAX7IAA7sABFm3AAUS" +
-    "BrYABxu2AAgSCbYAB7YACrYACxsEoAAULLkADAEAKhsEZCy3AAKnAA8bmQALKhsEZCy3AAKyAAO7" +
-    "AARZtwAFEg22AAcbtgAIEgm2AAe2AAq2AAuxAAAAAgATAAAAIgAIAAAABgAeAAcAIwAIACkACQA0" +
-    "AAoAOAALAEAADQBeAA4AFwAAAAQAAjQLAAEAGAAAAAIAGQ==");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQBQqwVIiZvIuS8j1HDurKbXZEV62Mnug5PEBAAAcAAAAHhWNBIAAAAAAAAAACQEAAAb" +
-    "AAAAcAAAAAkAAADcAAAABgAAAAABAAABAAAASAEAAAoAAABQAQAAAQAAAKABAAAEAwAAwAEAAMAC" +
-    "AADQAgAA2AIAAOECAADoAgAA6wIAAO4CAADyAgAA9gIAAAMDAAAaAwAALgMAAEQDAABYAwAAcwMA" +
-    "AIcDAACXAwAAmgMAAJ8DAACjAwAAqwMAAL8DAADEAwAAzQMAANsDAADgAwAA5wMAAAQAAAAIAAAA" +
-    "CQAAAAoAAAALAAAADAAAAA0AAAAOAAAAEAAAAAUAAAAFAAAAAAAAAAYAAAAGAAAAqAIAAAcAAAAG" +
-    "AAAAsAIAABAAAAAIAAAAAAAAABEAAAAIAAAAuAIAABIAAAAIAAAAsAIAAAcAAgAVAAAAAQADAAEA" +
-    "AAABAAQAFwAAAAEABAAZAAAAAgAFABYAAAADAAMAAQAAAAQAAwAYAAAABgADAAEAAAAGAAEAEwAA" +
-    "AAYAAgATAAAABgAAABoAAAABAAAAAAAAAAMAAAAAAAAADwAAAAAAAAAQBAAAAAAAAAEAAQABAAAA" +
-    "8QMAAAQAAABwEAQAAAAOAAYAAwADAAAA9gMAAFQAAABiAAAAIgEGAHAQBgABABsCAwAAAG4gCAAh" +
-    "AAwBbiAHAEEADAEbAgAAAABuIAgAIQAMAW4QCQABAAwBbiADABAAEhAzBCsAchAFAAUA2AAE/3Aw" +
-    "AQADBWIAAAAiAQYAcBAGAAEAGwICAAAAbiAIACEADAFuIAcAQQAMARsCAAAAAG4gCAAhAAwBbhAJ" +
-    "AAEADAFuIAMAEAAOADgE3//YAAT/cDABAAMFKNgDAAMAAwAAAAgEAAAEAAAAcDABABACDgABAAAA" +
-    "AAAAAAEAAAAFAAAAAgAAAAAABAAOIC0gdHJhbnNmb3JtZWQABjxpbml0PgAHR29vZGJ5ZQAFSGVs" +
-    "bG8AAUkAAUwAAkxJAAJMTAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGph" +
-    "dmEvbGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7" +
-    "ABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABJMamF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9y" +
-    "bS5qYXZhAAFWAANWSUwAAlZMAAZhcHBlbmQAEmVtaXR0ZXI6IGphY2stNC4yNAADb3V0AAdwcmlu" +
-    "dGxuAAxwcml2YXRlU2F5SGkAA3J1bgAFc2F5SGkACHRvU3RyaW5nAAEABw4ABgIAAAcOASAPPDxd" +
-    "ASAPGS0AAwIAAAcOPAAAAAIBAICABMADAQLYAwIBkAUAAA0AAAAAAAAAAQAAAAAAAAABAAAAGwAA" +
-    "AHAAAAACAAAACQAAANwAAAADAAAABgAAAAABAAAEAAAAAQAAAEgBAAAFAAAACgAAAFABAAAGAAAA" +
-    "AQAAAKABAAABIAAAAwAAAMABAAABEAAAAwAAAKgCAAACIAAAGwAAAMACAAADIAAAAwAAAPEDAAAA" +
-    "IAAAAQAAABAEAAAAEAAAAQAAACQEAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test942.run();
   }
-
-  public static void doTest(Transform t) {
-    t.sayHi(2, () -> { System.out.println("Not doing anything here"); });
-    t.sayHi(2, () -> {
-      System.out.println("transforming calling function");
-      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-    });
-    t.sayHi(2, () -> { System.out.println("Not doing anything here"); });
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/942-private-recursive/src/Transform.java b/test/942-private-recursive/src/Transform.java
deleted file mode 100644
index 7714326..0000000
--- a/test/942-private-recursive/src/Transform.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  private void privateSayHi(int recur, Runnable r) {
-    System.out.println("hello" + recur);
-    if (recur == 1) {
-      r.run();
-      privateSayHi(recur - 1, r);
-    } else if (recur != 0) {
-      privateSayHi(recur - 1, r);
-    }
-    System.out.println("goodbye" + recur);
-  }
-
-  public void sayHi(int recur, Runnable r) {
-    privateSayHi(recur, r);
-  }
-}
diff --git a/test/942-private-recursive/src/art/Redefinition.java b/test/942-private-recursive/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/942-private-recursive/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/942-private-recursive/src/art/Test942.java b/test/942-private-recursive/src/art/Test942.java
new file mode 100644
index 0000000..cccc2fd
--- /dev/null
+++ b/test/942-private-recursive/src/art/Test942.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+
+public class Test942 {
+
+  static class Transform {
+    private void privateSayHi(int recur, Runnable r) {
+      System.out.println("hello" + recur);
+      if (recur == 1) {
+        r.run();
+        privateSayHi(recur - 1, r);
+      } else if (recur != 0) {
+        privateSayHi(recur - 1, r);
+      }
+      System.out.println("goodbye" + recur);
+    }
+
+    public void sayHi(int recur, Runnable r) {
+      privateSayHi(recur, r);
+    }
+  }
+
+
+  // static class Transform {
+  //   public void sayHi(int recur, Runnable r) {
+  //     privateSayHi(recur, r);
+  //   }
+  //   private void privateSayHi(int recur, Runnable r) {
+  //     System.out.println("Hello" + recur + " - transformed");
+  //     if (recur == 1) {
+  //       r.run();
+  //       privateSayHi(recur - 1, r);
+  //     } else if (recur != 0) {
+  //       privateSayHi(recur - 1, r);
+  //     }
+  //     System.out.println("Goodbye" + recur + " - transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAPAoADwAaCgAOABsJABwAHQcAHgoABAAaCAAfCgAEACAKAAQAIQgAIgoABAAjCgAk" +
+    "ACULACYAJwgAKAcAKgcALQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUB" +
+    "AAVzYXlIaQEAGChJTGphdmEvbGFuZy9SdW5uYWJsZTspVgEADHByaXZhdGVTYXlIaQEADVN0YWNr" +
+    "TWFwVGFibGUBAApTb3VyY2VGaWxlAQAMVGVzdDk0Mi5qYXZhDAAQABEMABYAFQcALgwALwAwAQAX" +
+    "amF2YS9sYW5nL1N0cmluZ0J1aWxkZXIBAAVIZWxsbwwAMQAyDAAxADMBAA4gLSB0cmFuc2Zvcm1l" +
+    "ZAwANAA1BwA2DAA3ADgHADkMADoAEQEAB0dvb2RieWUHADsBABVhcnQvVGVzdDk0MiRUcmFuc2Zv" +
+    "cm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9s" +
+    "YW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEABmFwcGVuZAEALShMamF2" +
+    "YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEAHChJKUxqYXZhL2xhbmcv" +
+    "U3RyaW5nQnVpbGRlcjsBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQATamF2YS9p" +
+    "by9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABJqYXZhL2xh" +
+    "bmcvUnVubmFibGUBAANydW4BAAthcnQvVGVzdDk0MgAgAA4ADwAAAAAAAwAAABAAEQABABIAAAAd" +
+    "AAEAAQAAAAUqtwABsQAAAAEAEwAAAAYAAQAAAAUAAQAUABUAAQASAAAAIwADAAMAAAAHKhsstwAC" +
+    "sQAAAAEAEwAAAAoAAgAAAAcABgAIAAIAFgAVAAEAEgAAAJ0AAwADAAAAX7IAA7sABFm3AAUSBrYA" +
+    "Bxu2AAgSCbYAB7YACrYACxsEoAAULLkADAEAKhsEZCy3AAKnAA8bmQALKhsEZCy3AAKyAAO7AARZ" +
+    "twAFEg22AAcbtgAIEgm2AAe2AAq2AAuxAAAAAgATAAAAIgAIAAAACgAeAAsAIwAMACkADQA0AA4A" +
+    "OAAPAEAAEQBeABIAFwAAAAQAAjQLAAIAGAAAAAIAGQAsAAAACgABAA4AKQArAAg=");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQDiy6hGAAAAAAAAAAAAAAAAAAAAAAAAAAC4BQAAcAAAAHhWNBIAAAAAAAAAAPQEAAAh" +
+    "AAAAcAAAAAwAAAD0AAAABgAAACQBAAABAAAAbAEAAAoAAAB0AQAAAQAAAMQBAADUAwAA5AEAAOQB" +
+    "AAD0AQAA/AEAAAUCAAAMAgAADwIAABICAAAWAgAAGgIAADMCAABCAgAAZgIAAIYCAACdAgAAsQIA" +
+    "AMcCAADbAgAA9gIAAAoDAAAYAwAAIwMAACYDAAArAwAALwMAADwDAABEAwAASgMAAE8DAABYAwAA" +
+    "ZgMAAGsDAAByAwAAfAMAAAQAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAADwAAABAAAAAR" +
+    "AAAAFAAAAAUAAAAIAAAAAAAAAAYAAAAJAAAAlAMAAAcAAAAJAAAAjAMAABQAAAALAAAAAAAAABUA" +
+    "AAALAAAAhAMAABYAAAALAAAAjAMAAAoABQAaAAAAAQADAAEAAAABAAQAHAAAAAEABAAeAAAABQAF" +
+    "ABsAAAAGAAMAAQAAAAcAAwAdAAAACQADAAEAAAAJAAEAGAAAAAkAAgAYAAAACQAAAB8AAAABAAAA" +
+    "AAAAAAYAAAAAAAAAEgAAAOQEAAC0BAAAAAAAAA4gLSB0cmFuc2Zvcm1lZAAGPGluaXQ+AAdHb29k" +
+    "YnllAAVIZWxsbwABSQABTAACTEkAAkxMABdMYXJ0L1Rlc3Q5NDIkVHJhbnNmb3JtOwANTGFydC9U" +
+    "ZXN0OTQyOwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5v" +
+    "dGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2Jq" +
+    "ZWN0OwAUTGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwAZTGphdmEvbGFu" +
+    "Zy9TdHJpbmdCdWlsZGVyOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTQyLmphdmEACVRyYW5z" +
+    "Zm9ybQABVgADVklMAAJWTAALYWNjZXNzRmxhZ3MABmFwcGVuZAAEbmFtZQADb3V0AAdwcmludGxu" +
+    "AAxwcml2YXRlU2F5SGkAA3J1bgAFc2F5SGkACHRvU3RyaW5nAAV2YWx1ZQAAAgAAAAAABwABAAAA" +
+    "CAAAAAEAAAAAAAAABQAHDgAKAgAABw4BIA8BAw8BAw8BBRIBIA8BAQoBAg8ABwIAAAcOAQMPAAAB" +
+    "AAEAAQAAAJwDAAAEAAAAcBAEAAAADgAGAAMAAwAAAKEDAABVAAAAYgAAACIBCQBwEAYAAQAbAgMA" +
+    "AABuIAgAIQAMAW4gBwBBAAwBGwIAAAAAbiAIACEADAFuEAkAAQAMAW4gAwAQABIQMwQrAHIQBQAF" +
+    "ANgABP9wMAEAAwViAAAAIgEJAHAQBgABABsCAgAAAG4gCAAhAAwBbiAHAEEADAEbAgAAAABuIAgA" +
+    "IQAMAW4QCQABAAwBbiADABAADgA4BN//2AAE/3AwAQADBSkA2P8AAAMAAwADAAAAvQMAAAQAAABw" +
+    "MAEAEAIOAAAAAgEAgIAEyAcBAuAHAgGcCQAAAgMBIBgCAgQCFwQIGRcTAAIAAADIBAAAzgQAANgE" +
+    "AAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAAACEAAABwAAAAAgAAAAwAAAD0AAAAAwAA" +
+    "AAYAAAAkAQAABAAAAAEAAABsAQAABQAAAAoAAAB0AQAABgAAAAEAAADEAQAAAiAAACEAAADkAQAA" +
+    "ARAAAAMAAACEAwAAAyAAAAMAAACcAwAAASAAAAMAAADIAwAAACAAAAEAAAC0BAAABCAAAAIAAADI" +
+    "BAAAAxAAAAEAAADYBAAABiAAAAEAAADkBAAAABAAAAEAAAD0BAAA");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(2, () -> { System.out.println("Not doing anything here"); });
+    t.sayHi(2, () -> {
+      System.out.println("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    });
+    t.sayHi(2, () -> { System.out.println("Not doing anything here"); });
+  }
+}
diff --git a/test/943-private-recursive-jit/src/Main.java b/test/943-private-recursive-jit/src/Main.java
index f380c06..01760ad 100644
--- a/test/943-private-recursive-jit/src/Main.java
+++ b/test/943-private-recursive-jit/src/Main.java
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+import static art.Redefinition.doCommonClassRedefinition;
+
 import java.util.Base64;
 import java.util.function.Consumer;
 import java.lang.reflect.Method;
@@ -164,9 +166,4 @@
   private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
 
   private static native void ensureJitCompiled(Class c, String name);
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/943-private-recursive-jit/src/art/Redefinition.java b/test/943-private-recursive-jit/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/943-private-recursive-jit/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/944-transform-classloaders/classloader.cc b/test/944-transform-classloaders/classloader.cc
deleted file mode 100644
index 698e023..0000000
--- a/test/944-transform-classloaders/classloader.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include "android-base/macros.h"
-#include "jni.h"
-#include "jvmti.h"
-#include "mirror/class-inl.h"
-#include "scoped_local_ref.h"
-
-// Test infrastructure
-#include "test_env.h"
-
-namespace art {
-namespace Test944TransformClassloaders {
-
-extern "C" JNIEXPORT jlong JNICALL Java_Main_getDexFilePointer(JNIEnv* env, jclass, jclass klass) {
-  if (Runtime::Current() == nullptr) {
-    env->ThrowNew(env->FindClass("java/lang/Exception"),
-                  "We do not seem to be running in ART! Unable to get dex file.");
-    return 0;
-  }
-  ScopedObjectAccess soa(env);
-  // This sequence of casts must be the same as those done in
-  // runtime/native/dalvik_system_DexFile.cc in order to ensure that we get the same results.
-  return static_cast<jlong>(reinterpret_cast<uintptr_t>(
-      &soa.Decode<mirror::Class>(klass)->GetDexFile()));
-}
-
-}  // namespace Test944TransformClassloaders
-}  // namespace art
diff --git a/test/944-transform-classloaders/src/CommonClassDefinition.java b/test/944-transform-classloaders/src/CommonClassDefinition.java
deleted file mode 100644
index 62602a0..0000000
--- a/test/944-transform-classloaders/src/CommonClassDefinition.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-class CommonClassDefinition {
-  public final Class<?> target;
-  public final byte[] class_file_bytes;
-  public final byte[] dex_file_bytes;
-
-  CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
-    this.target = target;
-    this.class_file_bytes = class_file_bytes;
-    this.dex_file_bytes = dex_file_bytes;
-  }
-}
diff --git a/test/944-transform-classloaders/src/Main.java b/test/944-transform-classloaders/src/Main.java
index b558660..3d76d23 100644
--- a/test/944-transform-classloaders/src/Main.java
+++ b/test/944-transform-classloaders/src/Main.java
@@ -14,254 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.lang.reflect.*;
 public class Main {
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye");
-   *   }
-   * }
-   */
-  private static CommonClassDefinition TRANSFORM_DEFINITION = new CommonClassDefinition(
-      Transform.class,
-      Base64.getDecoder().decode(
-        "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
-        "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
-        "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
-        "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
-        "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
-        "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
-        "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0="),
-      Base64.getDecoder().decode(
-        "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
-        "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
-        "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
-        "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-        "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
-        "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-        "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
-        "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
-        "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
-        "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
-        "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
-        "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
-        "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="));
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform2 {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye2");
-   *   }
-   * }
-   */
-  private static CommonClassDefinition TRANSFORM2_DEFINITION = new CommonClassDefinition(
-      Transform2.class,
-      Base64.getDecoder().decode(
-        "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
-        "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA9UcmFuc2Zvcm0yLmphdmEM" +
-        "AAcACAcAFgwAFwAYAQAIR29vZGJ5ZTIHABkMABoAGwEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcv" +
-        "T2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEA" +
-        "E2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAA" +
-        "BQAGAAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAAQABAAsA" +
-        "CAABAAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAADAAgABAABAAwAAAACAA0="),
-      Base64.getDecoder().decode(
-        "ZGV4CjAzNQABX6vL8OT7aGLjbzFBEfCM9Aaz+zzGzVnQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
-        "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
-        "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" +
-        "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-        "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" +
-        "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-        "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTIADExUcmFuc2Zvcm0yOwAVTGphdmEvaW8vUHJp" +
-        "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" +
-        "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjQA" +
-        "A291dAAHcHJpbnRsbgAFc2F5SGkAAQAHDgADAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" +
-        "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
-        "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
-        "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA"));
-
   public static void main(String[] args) throws Exception {
-    doTest();
-    System.out.println("Passed");
+    art.Test944.run();
   }
-
-  private static void checkIsInstance(Class<?> klass, Object o) throws Exception {
-    if (!klass.isInstance(o)) {
-      throw new Exception(klass + " is not the class of " + o);
-    }
-  }
-
-  private static boolean arrayContains(long[] arr, long value) {
-    if (arr == null) {
-      return false;
-    }
-    for (int i = 0; i < arr.length; i++) {
-      if (arr[i] == value) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Checks that we can find the dex-file for the given class in its classloader.
-   *
-   * Throws if it fails.
-   */
-  private static void checkDexFileInClassLoader(Class<?> klass) throws Exception {
-    // If all the android BCP classes were availible when compiling this test and access checks
-    // weren't a thing this function would be written as follows:
-    //
-    // long dexFilePtr = getDexFilePointer(klass);
-    // dalvik.system.BaseDexClassLoader loader =
-    //     (dalvik.system.BaseDexClassLoader)klass.getClassLoader();
-    // dalvik.system.DexPathList pathListValue = loader.pathList;
-    // dalvik.system.DexPathList.Element[] elementArrayValue = pathListValue.dexElements;
-    // int array_length = elementArrayValue.length;
-    // for (int i = 0; i < array_length; i++) {
-    //   dalvik.system.DexPathList.Element curElement = elementArrayValue[i];
-    //   dalvik.system.DexFile curDexFile = curElement.dexFile;
-    //   if (curDexFile == null) {
-    //     continue;
-    //   }
-    //   long[] curCookie = (long[])curDexFile.mCookie;
-    //   long[] curInternalCookie = (long[])curDexFile.mInternalCookie;
-    //   if (arrayContains(curCookie, dexFilePtr) || arrayContains(curInternalCookie, dexFilePtr)) {
-    //     return;
-    //   }
-    // }
-    // throw new Exception(
-    //     "Unable to find dex file pointer " + dexFilePtr + " in class loader for " + klass);
-
-    // Get all the fields and classes we need by reflection.
-    Class<?> baseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
-    Field pathListField = baseDexClassLoaderClass.getDeclaredField("pathList");
-
-    Class<?> dexPathListClass = Class.forName("dalvik.system.DexPathList");
-    Field elementArrayField = dexPathListClass.getDeclaredField("dexElements");
-
-    Class<?> dexPathListElementClass = Class.forName("dalvik.system.DexPathList$Element");
-    Field dexFileField = dexPathListElementClass.getDeclaredField("dexFile");
-
-    Class<?> dexFileClass = Class.forName("dalvik.system.DexFile");
-    Field dexFileCookieField = dexFileClass.getDeclaredField("mCookie");
-    Field dexFileInternalCookieField = dexFileClass.getDeclaredField("mInternalCookie");
-
-    // Make all the fields accessible
-    AccessibleObject.setAccessible(new AccessibleObject[] { pathListField,
-                                                            elementArrayField,
-                                                            dexFileField,
-                                                            dexFileCookieField,
-                                                            dexFileInternalCookieField }, true);
-
-    long dexFilePtr = getDexFilePointer(klass);
-
-    ClassLoader loader = klass.getClassLoader();
-    checkIsInstance(baseDexClassLoaderClass, loader);
-    // DexPathList pathListValue = ((BaseDexClassLoader) loader).pathList;
-    Object pathListValue = pathListField.get(loader);
-
-    checkIsInstance(dexPathListClass, pathListValue);
-
-    // DexPathList.Element[] elementArrayValue = pathListValue.dexElements;
-    Object elementArrayValue = elementArrayField.get(pathListValue);
-    if (!elementArrayValue.getClass().isArray() ||
-        elementArrayValue.getClass().getComponentType() != dexPathListElementClass) {
-      throw new Exception("elementArrayValue is not an " + dexPathListElementClass + " array!");
-    }
-    // int array_length = elementArrayValue.length;
-    int array_length = Array.getLength(elementArrayValue);
-    for (int i = 0; i < array_length; i++) {
-      // DexPathList.Element curElement = elementArrayValue[i];
-      Object curElement = Array.get(elementArrayValue, i);
-      checkIsInstance(dexPathListElementClass, curElement);
-
-      // DexFile curDexFile = curElement.dexFile;
-      Object curDexFile = dexFileField.get(curElement);
-      if (curDexFile == null) {
-        continue;
-      }
-      checkIsInstance(dexFileClass, curDexFile);
-
-      // long[] curCookie = (long[])curDexFile.mCookie;
-      long[] curCookie = (long[])dexFileCookieField.get(curDexFile);
-      // long[] curInternalCookie = (long[])curDexFile.mInternalCookie;
-      long[] curInternalCookie = (long[])dexFileInternalCookieField.get(curDexFile);
-
-      if (arrayContains(curCookie, dexFilePtr) || arrayContains(curInternalCookie, dexFilePtr)) {
-        return;
-      }
-    }
-    throw new Exception(
-        "Unable to find dex file pointer " + dexFilePtr + " in class loader for " + klass);
-  }
-
-  private static void doTest() throws Exception {
-    art.Main.bindAgentJNIForClass(Main.class);
-
-    Transform t = new Transform();
-    Transform2 t2 = new Transform2();
-
-    long initial_t1_dex = getDexFilePointer(Transform.class);
-    long initial_t2_dex = getDexFilePointer(Transform2.class);
-    if (initial_t2_dex != initial_t1_dex) {
-      throw new Exception("The classes " + Transform.class + " and " + Transform2.class + " " +
-                          "have different initial dex files!");
-    }
-    checkDexFileInClassLoader(Transform.class);
-    checkDexFileInClassLoader(Transform2.class);
-
-    // Make sure they are loaded
-    t.sayHi();
-    t2.sayHi();
-    // Redefine both of the classes.
-    doMultiClassRedefinition(TRANSFORM_DEFINITION, TRANSFORM2_DEFINITION);
-    // Make sure we actually transformed them!
-    t.sayHi();
-    t2.sayHi();
-
-    long final_t1_dex = getDexFilePointer(Transform.class);
-    long final_t2_dex = getDexFilePointer(Transform2.class);
-    if (final_t2_dex == final_t1_dex) {
-      throw new Exception("The classes " + Transform.class + " and " + Transform2.class + " " +
-                          "have the same initial dex files!");
-    } else if (final_t1_dex == initial_t1_dex) {
-      throw new Exception("The class " + Transform.class + " did not get a new dex file!");
-    } else if (final_t2_dex == initial_t2_dex) {
-      throw new Exception("The class " + Transform2.class + " did not get a new dex file!");
-    }
-    // Check to make sure the new dex files are in the class loader.
-    checkDexFileInClassLoader(Transform.class);
-    checkDexFileInClassLoader(Transform2.class);
-  }
-
-  private static void doMultiClassRedefinition(CommonClassDefinition... defs) {
-    ArrayList<Class<?>> classes = new ArrayList<>();
-    ArrayList<byte[]> class_files = new ArrayList<>();
-    ArrayList<byte[]> dex_files = new ArrayList<>();
-
-    for (CommonClassDefinition d : defs) {
-      classes.add(d.target);
-      class_files.add(d.class_file_bytes);
-      dex_files.add(d.dex_file_bytes);
-    }
-    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
-                                   class_files.toArray(new byte[0][]),
-                                   dex_files.toArray(new byte[0][]));
-  }
-
-  // Gets the 'long' (really a native pointer) that is stored in the ClassLoader representing the
-  // DexFile a class is loaded from. This is converted from the DexFile* in the same way it is done
-  // in runtime/native/dalvik_system_DexFile.cc
-  private static native long getDexFilePointer(Class<?> target);
-  // Transforms the classes
-  private static native void doCommonMultiClassRedefinition(Class<?>[] targets,
-                                                            byte[][] classfiles,
-                                                            byte[][] dexfiles);
 }
diff --git a/test/944-transform-classloaders/src/Transform.java b/test/944-transform-classloaders/src/Transform.java
deleted file mode 100644
index 8e8af35..0000000
--- a/test/944-transform-classloaders/src/Transform.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi() {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Goodbye" < "LTransform;" < "hello".
-    System.out.println("hello");
-  }
-}
diff --git a/test/944-transform-classloaders/src/art/Redefinition.java b/test/944-transform-classloaders/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/944-transform-classloaders/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/944-transform-classloaders/src/art/Test944.java b/test/944-transform-classloaders/src/art/Test944.java
new file mode 100644
index 0000000..fe1c024
--- /dev/null
+++ b/test/944-transform-classloaders/src/art/Test944.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import static art.Redefinition.CommonClassDefinition;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.lang.reflect.*;
+public class Test944 {
+
+  static class Transform {
+    public void sayHi() {
+      System.out.println("hello");
+    }
+  }
+
+  static class Transform2 {
+    public void sayHi() {
+      System.out.println("hello2");
+    }
+  }
+
+  /**
+   * base64 encoded class/dex file for
+   * static class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static CommonClassDefinition TRANSFORM_DEFINITION = new CommonClassDefinition(
+      Transform.class,
+      Base64.getDecoder().decode(
+        "yv66vgAAADQAIAoABgAOCQAPABAIABEKABIAEwcAFQcAGAEABjxpbml0PgEAAygpVgEABENvZGUB" +
+        "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAAxUZXN0OTQ0LmphdmEMAAcA" +
+        "CAcAGQwAGgAbAQAHR29vZGJ5ZQcAHAwAHQAeBwAfAQAVYXJ0L1Rlc3Q5NDQkVHJhbnNmb3JtAQAJ" +
+        "VHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9T" +
+        "eXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFt" +
+        "AQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAC2FydC9UZXN0OTQ0ACAABQAGAAAA" +
+        "AAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAACgABAAsACAABAAkA" +
+        "AAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAMAAgADQACAAwAAAACAA0AFwAAAAoA" +
+        "AQAFABQAFgAI"),
+      Base64.getDecoder().decode(
+        "ZGV4CjAzNQCFgsuWAAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" +
+        "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" +
+        "AABMAQAAVQEAAG4BAAB9AQAAoQEAAMEBAADYAQAA7AEAAAACAAAUAgAAIgIAAC0CAAAwAgAANAIA" +
+        "AEECAABHAgAATAIAAFUCAABcAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
+        "DAAAAAgAAAAAAAAADQAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
+        "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA5AIAALgCAAAAAAAABjxpbml0PgAHR29vZGJ5ZQAX" +
+        "TGFydC9UZXN0OTQ0JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk0NDsAIkxkYWx2aWsvYW5ub3RhdGlv" +
+        "bi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEv" +
+        "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" +
+        "TGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTQ0LmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vz" +
+        "c0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAQAAAAYAAAAKAAcOAAwA" +
+        "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQEA" +
+        "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg4ECA8XCwACAAAAyAIAAM4CAADY" +
+        "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" +
+        "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" +
+        "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" +
+        "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA=="));
+
+  /**
+   * base64 encoded class/dex file for
+   * static class Transform2 {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye2");
+   *   }
+   * }
+   */
+  private static CommonClassDefinition TRANSFORM2_DEFINITION = new CommonClassDefinition(
+      Transform2.class,
+      Base64.getDecoder().decode(
+        "yv66vgAAADQAIAoABgAOCQAPABAIABEKABIAEwcAFQcAGAEABjxpbml0PgEAAygpVgEABENvZGUB" +
+        "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAAxUZXN0OTQ0LmphdmEMAAcA" +
+        "CAcAGQwAGgAbAQAIR29vZGJ5ZTIHABwMAB0AHgcAHwEAFmFydC9UZXN0OTQ0JFRyYW5zZm9ybTIB" +
+        "AApUcmFuc2Zvcm0yAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFu" +
+        "Zy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3Ry" +
+        "ZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAC2FydC9UZXN0OTQ0ACAABQAG" +
+        "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAABQABAAsACAAB" +
+        "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAHAAgACAACAAwAAAACAA0AFwAA" +
+        "AAoAAQAFABQAFgAI"),
+      Base64.getDecoder().decode(
+        "ZGV4CjAzNQAUg8BCAAAAAAAAAAAAAAAAAAAAAAAAAAC8AwAAcAAAAHhWNBIAAAAAAAAAAPgCAAAU" +
+        "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB4AgAARAEAAEQB" +
+        "AABMAQAAVgEAAHABAAB/AQAAowEAAMMBAADaAQAA7gEAAAICAAAWAgAAJAIAADACAAAzAgAANwIA" +
+        "AEQCAABKAgAATwIAAFgCAABfAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
+        "DAAAAAgAAAAAAAAADQAAAAgAAABoAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
+        "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA6AIAALwCAAAAAAAABjxpbml0PgAIR29vZGJ5ZTIA" +
+        "GExhcnQvVGVzdDk0NCRUcmFuc2Zvcm0yOwANTGFydC9UZXN0OTQ0OwAiTGRhbHZpay9hbm5vdGF0" +
+        "aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2" +
+        "YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7" +
+        "ABJMamF2YS9sYW5nL1N5c3RlbTsADFRlc3Q5NDQuamF2YQAKVHJhbnNmb3JtMgABVgACVkwAC2Fj" +
+        "Y2Vzc0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAAEAAAAGAAAABQAH" +
+        "DgAHAAcOAQgPAAAAAAEAAQABAAAAcAIAAAQAAABwEAMAAAAOAAMAAQACAAAAdQIAAAkAAABiAAAA" +
+        "GwEBAAAAbiACABAADgAAAAAAAQEAgIAEgAUBAZgFAAACAgETGAECAwIOBAgPFwsAAgAAAMwCAADS" +
+        "AgAA3AIAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAFAAAAHAAAAACAAAACQAAAMAA" +
+        "AAADAAAAAgAAAOQAAAAEAAAAAQAAAPwAAAAFAAAABAAAAAQBAAAGAAAAAQAAACQBAAACIAAAFAAA" +
+        "AEQBAAABEAAAAQAAAGgCAAADIAAAAgAAAHACAAABIAAAAgAAAIACAAAAIAAAAQAAALwCAAAEIAAA" +
+        "AgAAAMwCAAADEAAAAQAAANwCAAAGIAAAAQAAAOgCAAAAEAAAAQAAAPgCAAA="));
+
+  public static void run() throws Exception {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest();
+    System.out.println("Passed");
+  }
+
+  private static void checkIsInstance(Class<?> klass, Object o) throws Exception {
+    if (!klass.isInstance(o)) {
+      throw new Exception(klass + " is not the class of " + o);
+    }
+  }
+
+  private static boolean arrayContains(long[] arr, long value) {
+    if (arr == null) {
+      return false;
+    }
+    for (int i = 0; i < arr.length; i++) {
+      if (arr[i] == value) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Checks that we can find the dex-file for the given class in its classloader.
+   *
+   * Throws if it fails.
+   */
+  private static void checkDexFileInClassLoader(Class<?> klass) throws Exception {
+    // If all the android BCP classes were availible when compiling this test and access checks
+    // weren't a thing this function would be written as follows:
+    //
+    // long dexFilePtr = getDexFilePointer(klass);
+    // dalvik.system.BaseDexClassLoader loader =
+    //     (dalvik.system.BaseDexClassLoader)klass.getClassLoader();
+    // dalvik.system.DexPathList pathListValue = loader.pathList;
+    // dalvik.system.DexPathList.Element[] elementArrayValue = pathListValue.dexElements;
+    // int array_length = elementArrayValue.length;
+    // for (int i = 0; i < array_length; i++) {
+    //   dalvik.system.DexPathList.Element curElement = elementArrayValue[i];
+    //   dalvik.system.DexFile curDexFile = curElement.dexFile;
+    //   if (curDexFile == null) {
+    //     continue;
+    //   }
+    //   long[] curCookie = (long[])curDexFile.mCookie;
+    //   long[] curInternalCookie = (long[])curDexFile.mInternalCookie;
+    //   if (arrayContains(curCookie, dexFilePtr) || arrayContains(curInternalCookie, dexFilePtr)) {
+    //     return;
+    //   }
+    // }
+    // throw new Exception(
+    //     "Unable to find dex file pointer " + dexFilePtr + " in class loader for " + klass);
+
+    // Get all the fields and classes we need by reflection.
+    Class<?> baseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader");
+    Field pathListField = baseDexClassLoaderClass.getDeclaredField("pathList");
+
+    Class<?> dexPathListClass = Class.forName("dalvik.system.DexPathList");
+    Field elementArrayField = dexPathListClass.getDeclaredField("dexElements");
+
+    Class<?> dexPathListElementClass = Class.forName("dalvik.system.DexPathList$Element");
+    Field dexFileField = dexPathListElementClass.getDeclaredField("dexFile");
+
+    Class<?> dexFileClass = Class.forName("dalvik.system.DexFile");
+    Field dexFileCookieField = dexFileClass.getDeclaredField("mCookie");
+    Field dexFileInternalCookieField = dexFileClass.getDeclaredField("mInternalCookie");
+
+    // Make all the fields accessible
+    AccessibleObject.setAccessible(new AccessibleObject[] { pathListField,
+                                                            elementArrayField,
+                                                            dexFileField,
+                                                            dexFileCookieField,
+                                                            dexFileInternalCookieField }, true);
+
+    long dexFilePtr = getDexFilePointer(klass);
+
+    ClassLoader loader = klass.getClassLoader();
+    checkIsInstance(baseDexClassLoaderClass, loader);
+    // DexPathList pathListValue = ((BaseDexClassLoader) loader).pathList;
+    Object pathListValue = pathListField.get(loader);
+
+    checkIsInstance(dexPathListClass, pathListValue);
+
+    // DexPathList.Element[] elementArrayValue = pathListValue.dexElements;
+    Object elementArrayValue = elementArrayField.get(pathListValue);
+    if (!elementArrayValue.getClass().isArray() ||
+        elementArrayValue.getClass().getComponentType() != dexPathListElementClass) {
+      throw new Exception("elementArrayValue is not an " + dexPathListElementClass + " array!");
+    }
+    // int array_length = elementArrayValue.length;
+    int array_length = Array.getLength(elementArrayValue);
+    for (int i = 0; i < array_length; i++) {
+      // DexPathList.Element curElement = elementArrayValue[i];
+      Object curElement = Array.get(elementArrayValue, i);
+      checkIsInstance(dexPathListElementClass, curElement);
+
+      // DexFile curDexFile = curElement.dexFile;
+      Object curDexFile = dexFileField.get(curElement);
+      if (curDexFile == null) {
+        continue;
+      }
+      checkIsInstance(dexFileClass, curDexFile);
+
+      // long[] curCookie = (long[])curDexFile.mCookie;
+      long[] curCookie = (long[])dexFileCookieField.get(curDexFile);
+      // long[] curInternalCookie = (long[])curDexFile.mInternalCookie;
+      long[] curInternalCookie = (long[])dexFileInternalCookieField.get(curDexFile);
+
+      if (arrayContains(curCookie, dexFilePtr) || arrayContains(curInternalCookie, dexFilePtr)) {
+        return;
+      }
+    }
+    throw new Exception(
+        "Unable to find dex file pointer " + dexFilePtr + " in class loader for " + klass);
+  }
+
+  private static void doTest() throws Exception {
+    Transform t = new Transform();
+    Transform2 t2 = new Transform2();
+
+    long initial_t1_dex = getDexFilePointer(Transform.class);
+    long initial_t2_dex = getDexFilePointer(Transform2.class);
+    if (initial_t2_dex != initial_t1_dex) {
+      throw new Exception("The classes " + Transform.class + " and " + Transform2.class + " " +
+                          "have different initial dex files!");
+    }
+    checkDexFileInClassLoader(Transform.class);
+    checkDexFileInClassLoader(Transform2.class);
+
+    // Make sure they are loaded
+    t.sayHi();
+    t2.sayHi();
+    // Redefine both of the classes.
+    Redefinition.doMultiClassRedefinition(TRANSFORM_DEFINITION, TRANSFORM2_DEFINITION);
+    // Make sure we actually transformed them!
+    t.sayHi();
+    t2.sayHi();
+
+    long final_t1_dex = getDexFilePointer(Transform.class);
+    long final_t2_dex = getDexFilePointer(Transform2.class);
+    if (final_t2_dex == final_t1_dex) {
+      throw new Exception("The classes " + Transform.class + " and " + Transform2.class + " " +
+                          "have the same initial dex files!");
+    } else if (final_t1_dex == initial_t1_dex) {
+      throw new Exception("The class " + Transform.class + " did not get a new dex file!");
+    } else if (final_t2_dex == initial_t2_dex) {
+      throw new Exception("The class " + Transform2.class + " did not get a new dex file!");
+    }
+    // Check to make sure the new dex files are in the class loader.
+    checkDexFileInClassLoader(Transform.class);
+    checkDexFileInClassLoader(Transform2.class);
+  }
+
+  // Gets the 'long' (really a native pointer) that is stored in the ClassLoader representing the
+  // DexFile a class is loaded from. This is plucked out of the internal DexCache object associated
+  // with the class.
+  private static long getDexFilePointer(Class<?> target) throws Exception {
+    // If all the android BCP classes were available when compiling this test and access checks
+    // weren't a thing this function would be written as follows:
+    //
+    // java.lang.DexCache dexCacheObject = target.dexCache;
+    // if (dexCacheObject == null) {
+    //   return 0;
+    // }
+    // return dexCacheObject.dexFile;
+    Field dexCacheField = Class.class.getDeclaredField("dexCache");
+
+    Class<?> dexCacheClass = Class.forName("java.lang.DexCache");
+    Field dexFileField = dexCacheClass.getDeclaredField("dexFile");
+
+    AccessibleObject.setAccessible(new AccessibleObject[] { dexCacheField, dexFileField }, true);
+
+    Object dexCacheObject = dexCacheField.get(target);
+    if (dexCacheObject == null) {
+      return 0;
+    }
+    checkIsInstance(dexCacheClass, dexCacheObject);
+    return dexFileField.getLong(dexCacheObject);
+  }
+}
diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc
index ee653a4..e3090f5 100644
--- a/test/945-obsolete-native/obsolete_native.cc
+++ b/test/945-obsolete-native/obsolete_native.cc
@@ -19,31 +19,20 @@
 #include <stdio.h>
 
 #include "android-base/stringprintf.h"
-
-#include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "jni_binder.h"
 #include "test_env.h"
+#include "scoped_local_ref.h"
 
 namespace art {
 namespace Test945ObsoleteNative {
 
-extern "C" JNIEXPORT void JNICALL Java_Main_bindTest945ObsoleteNative(
-    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
-  BindFunctions(jvmti_env, env, "Transform");
-}
-
-extern "C" JNIEXPORT void JNICALL Java_Transform_doExecute(JNIEnv* env,
-                                                           jclass klass ATTRIBUTE_UNUSED,
-                                                           jobject runnable) {
+extern "C" JNIEXPORT void JNICALL Java_art_Test945_00024Transform_doExecute(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject runnable) {
   jclass runnable_klass = env->FindClass("java/lang/Runnable");
-  DCHECK(runnable_klass != nullptr);
   jmethodID run_method = env->GetMethodID(runnable_klass, "run", "()V");
   env->CallVoidMethod(runnable, run_method);
 }
diff --git a/test/945-obsolete-native/src/Main.java b/test/945-obsolete-native/src/Main.java
index a7901cd..c94bc22 100644
--- a/test/945-obsolete-native/src/Main.java
+++ b/test/945-obsolete-native/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,65 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
-
 public class Main {
-  // class Transform {
-  //   public void sayHi(Runnable r) {
-  //     System.out.println("Hello - Transformed");
-  //     doExecute(r);
-  //     System.out.println("Goodbye - Transformed");
-  //   }
-  //
-  //   private static native void doExecute(Runnable r);
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAIgoACAASCQATABQIABUKABYAFwoABwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
-    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
-    "KVYBAAlkb0V4ZWN1dGUBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAe" +
-    "AQATSGVsbG8gLSBUcmFuc2Zvcm1lZAcAHwwAIAAhDAAPAA4BABVHb29kYnllIC0gVHJhbnNmb3Jt" +
-    "ZWQBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291" +
-    "dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxu" +
-    "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABwAIAAAAAAADAAAACQAKAAEACwAAAB0AAQABAAAA" +
-    "BSq3AAGxAAAAAQAMAAAABgABAAAAEQABAA0ADgABAAsAAAA5AAIAAgAAABWyAAISA7YABCu4AAWy" +
-    "AAISBrYABLEAAAABAAwAAAASAAQAAAATAAgAFAAMABUAFAAWAQoADwAOAAAAAQAQAAAAAgAR");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQB1fZcJR/opPuXacK8mIla5shH0LSg72qJYAwAAcAAAAHhWNBIAAAAAAAAAALgCAAAR" +
-    "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAUAgAARAEAAKIB" +
-    "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAABuAgAAggIA" +
-    "AIcCAACQAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
-    "lAEAAAsAAAAGAAAAnAEAAAUAAQAOAAAAAAAAAAAAAAAAAAEADAAAAAAAAQAQAAAAAQACAA8AAAAC" +
-    "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAKUCAAAAAAAAAQABAAEAAACXAgAABAAAAHAQ" +
-    "BAAAAA4ABAACAAIAAACcAgAAFAAAAGIAAAAbAQIAAABuIAMAEABxEAEAAwBiAAAAGwEBAAAAbiAD" +
-    "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
-    "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
-    "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
-    "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAJZG9FeGVjdXRlABJlbWl0" +
-    "dGVyOiBqYWNrLTQuMjUAA291dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAQAHDoc8hwAAAAIBAICA" +
-    "BMQCAYoCAAIB3AIADQAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMAAAAD" +
-    "AAAA0AAAAAQAAAABAAAA9AAAAAUAAAAFAAAA/AAAAAYAAAABAAAAJAEAAAEgAAACAAAARAEAAAEQ" +
-    "AAACAAAAlAEAAAIgAAARAAAAogEAAAMgAAACAAAAlwIAAAAgAAABAAAApQIAAAAQAAABAAAAuAIA" +
-    "AA==");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    bindTest945ObsoleteNative();
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test945.run();
   }
-
-  public static void doTest(Transform t) {
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
-    t.sayHi(() -> {
-      System.out.println("transforming calling function");
-      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-    });
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
-
-  private static native void bindTest945ObsoleteNative();
 }
diff --git a/test/945-obsolete-native/src/Transform.java b/test/945-obsolete-native/src/Transform.java
deleted file mode 100644
index 2b7cc1b..0000000
--- a/test/945-obsolete-native/src/Transform.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi(Runnable r) {
-    System.out.println("hello");
-    doExecute(r);
-    System.out.println("goodbye");
-  }
-
-  private static native void doExecute(Runnable r);
-}
diff --git a/test/945-obsolete-native/src/art/Redefinition.java b/test/945-obsolete-native/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/945-obsolete-native/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/945-obsolete-native/src/art/Test945.java b/test/945-obsolete-native/src/art/Test945.java
new file mode 100644
index 0000000..6cf31f6
--- /dev/null
+++ b/test/945-obsolete-native/src/art/Test945.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+
+public class Test945 {
+
+  static class Transform {
+    static {
+      art.Main.bindAgentJNIForClass(Transform.class);
+    }
+
+    public void sayHi(Runnable r) {
+      System.out.println("hello");
+      doExecute(r);
+      System.out.println("goodbye");
+    }
+
+    private static native void doExecute(Runnable r);
+  }
+
+  // static class Transform {
+  //   static { }
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     doExecute(r);
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  //
+  //   private static native void doExecute(Runnable r);
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAJwoACAATCQAUABUIABYKABcAGAoABwAZCAAaBwAcBwAfAQAGPGluaXQ+AQADKClW" +
+    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+    "KVYBAAlkb0V4ZWN1dGUBAAg8Y2xpbml0PgEAClNvdXJjZUZpbGUBAAxUZXN0OTQ1LmphdmEMAAkA" +
+    "CgcAIAwAIQAiAQATSGVsbG8gLSBUcmFuc2Zvcm1lZAcAIwwAJAAlDAAPAA4BABVHb29kYnllIC0g" +
+    "VHJhbnNmb3JtZWQHACYBABVhcnQvVGVzdDk0NSRUcmFuc2Zvcm0BAAlUcmFuc2Zvcm0BAAxJbm5l" +
+    "ckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" +
+    "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" +
+    "YXZhL2xhbmcvU3RyaW5nOylWAQALYXJ0L1Rlc3Q5NDUAIAAHAAgAAAAAAAQAAAAJAAoAAQALAAAA" +
+    "HQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAAFAAEADQAOAAEACwAAADkAAgACAAAAFbIAAhID" +
+    "tgAEK7gABbIAAhIGtgAEsQAAAAEADAAAABIABAAAAAgACAAJAAwACgAUAAsBCgAPAA4AAAAIABAA" +
+    "CgABAAsAAAAZAAAAAAAAAAGxAAAAAQAMAAAABgABAAAABgACABEAAAACABIAHgAAAAoAAQAHABsA" +
+    "HQAI");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAFqcJFAAAAAAAAAAAAAAAAAAAAAAAAAAB8BAAAcAAAAHhWNBIAAAAAAAAAALgDAAAY" +
+    "AAAAcAAAAAoAAADQAAAAAwAAAPgAAAABAAAAHAEAAAYAAAAkAQAAAQAAAFQBAAAIAwAAdAEAAHQB" +
+    "AAB+AQAAhgEAAJ0BAACyAQAAywEAANoBAAD+AQAAHgIAADUCAABJAgAAXwIAAHMCAACHAgAAlQIA" +
+    "AKACAACjAgAApwIAALQCAAC/AgAAxQIAAMoCAADTAgAA2gIAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+    "CQAAAAoAAAALAAAADAAAAA8AAAAPAAAACQAAAAAAAAAQAAAACQAAAOQCAAAQAAAACQAAAOwCAAAI" +
+    "AAQAFAAAAAAAAAAAAAAAAAAAAAEAAAAAAAEAEgAAAAAAAQAWAAAABAACABUAAAAFAAAAAQAAAAAA" +
+    "AAAAAAAABQAAAAAAAAANAAAAqAMAAHQDAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AFUdvb2RieWUg" +
+    "LSBUcmFuc2Zvcm1lZAATSGVsbG8gLSBUcmFuc2Zvcm1lZAAXTGFydC9UZXN0OTQ1JFRyYW5zZm9y" +
+    "bTsADUxhcnQvVGVzdDk0NTsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxk" +
+    "YWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2" +
+    "YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJMamF2YS9sYW5nL1N0cmluZzsA" +
+    "EkxqYXZhL2xhbmcvU3lzdGVtOwAMVGVzdDk0NS5qYXZhAAlUcmFuc2Zvcm0AAVYAAlZMAAthY2Nl" +
+    "c3NGbGFncwAJZG9FeGVjdXRlAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAAAB" +
+    "AAAABgAAAAEAAAAHAAAABQAHDgAFAAcOAAgBAAcOAQgPAQMPAQgPAAAAAAAAAAAAAAAA9AIAAAEA" +
+    "AAAOAAAAAQABAAEAAAD5AgAABAAAAHAQBQAAAA4ABAACAAIAAAD+AgAAFAAAAGIAAAAbAQMAAABu" +
+    "IAQAEABxEAIAAwBiAAAAGwECAAAAbiAEABAADgAAAAMBAIiABJAGAYCABKQGAYoCAAMBvAYCAgEX" +
+    "GAECAwIRBAgTFw4AAgAAAIwDAACSAwAAnAMAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAAB" +
+    "AAAAGAAAAHAAAAACAAAACgAAANAAAAADAAAAAwAAAPgAAAAEAAAAAQAAABwBAAAFAAAABgAAACQB" +
+    "AAAGAAAAAQAAAFQBAAACIAAAGAAAAHQBAAABEAAAAgAAAOQCAAADIAAAAwAAAPQCAAABIAAAAwAA" +
+    "ABADAAAAIAAAAQAAAHQDAAAEIAAAAgAAAIwDAAADEAAAAQAAAJwDAAAGIAAAAQAAAKgDAAAAEAAA" +
+    "AQAAALgDAAA=");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    t.sayHi(() -> {
+      System.out.println("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+    });
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+}
diff --git a/test/946-obsolete-throw/expected.txt b/test/946-obsolete-throw/expected.txt
index 71d5182..edf796e 100644
--- a/test/946-obsolete-throw/expected.txt
+++ b/test/946-obsolete-throw/expected.txt
@@ -5,10 +5,11 @@
 transforming calling function
 Received error : java.lang.Error: Throwing exception into an obsolete method!
 java.lang.Error: Throwing exception into an obsolete method!
-	at Main$DoRedefinitionClass.run(Main.java:65)
-	at Transform.sayHi(Transform.java:27)
-	at Main.doTest(Main.java:72)
-	at Main.main(Main.java:57)
+	at art.Test946$DoRedefinitionClass.run(Test946.java:81)
+	at art.Test946$Transform.sayHi(Test946.java:26)
+	at art.Test946.doTest(Test946.java:88)
+	at art.Test946.run(Test946.java:73)
+	at Main.main(Main.java:19)
 Hello - Transformed
 Not doing anything here
 Goodbye - Transformed
diff --git a/test/946-obsolete-throw/src/Main.java b/test/946-obsolete-throw/src/Main.java
index 077ad72..0b1f78d 100644
--- a/test/946-obsolete-throw/src/Main.java
+++ b/test/946-obsolete-throw/src/Main.java
@@ -14,71 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
-
 public class Main {
-  // class Transform {
-  //   public void sayHi(Runnable r) {
-  //     System.out.println("Hello - Transformed");
-  //     r.run();
-  //     System.out.println("Goodbye - Transformed");
-  //   }
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
-    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
-    "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
-    "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
-    "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
-    "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
-    "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
-    "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsAAAA7AAIA" +
-    "AgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
-    "AQAPAAAAAgAQ");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQAYeAMMXgYWxoeSHAS9EWKCCtVRSAGpqZVQAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
-    "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
-    "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
-    "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
-    "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
-    "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
-    "AwAAAA4ABAACAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" +
-    "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
-    "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
-    "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
-    "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
-    "LjEzAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICABMQCAQHc" +
-    "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
-    "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
-    "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test946.run();
   }
-
-  static class DoRedefinitionClass implements Runnable {
-    @Override
-    public void run() {
-      System.out.println("transforming calling function");
-      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-      throw new Error("Throwing exception into an obsolete method!");
-    }
-  }
-
-  public static void doTest(Transform t) {
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
-    try {
-      t.sayHi(new DoRedefinitionClass());
-    } catch (Throwable e) {
-      System.out.println("Received error : " + e);
-      e.printStackTrace(System.out);
-    }
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/946-obsolete-throw/src/Transform.java b/test/946-obsolete-throw/src/Transform.java
deleted file mode 100644
index 4f43086..0000000
--- a/test/946-obsolete-throw/src/Transform.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-class Transform {
-  public void sayHi(Runnable r) {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Hello" < "LTransform;" < "hello".
-    System.out.println("hello");
-    r.run();
-    System.out.println("goodbye");
-  }
-}
diff --git a/test/946-obsolete-throw/src/art/Redefinition.java b/test/946-obsolete-throw/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/946-obsolete-throw/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/946-obsolete-throw/src/art/Test946.java b/test/946-obsolete-throw/src/art/Test946.java
new file mode 100644
index 0000000..9f0e57c
--- /dev/null
+++ b/test/946-obsolete-throw/src/art/Test946.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.Base64;
+
+public class Test946 {
+
+  static class Transform {
+    public void sayHi(Runnable r) {
+      System.out.println("hello");
+      r.run();
+      System.out.println("goodbye");
+    }
+  }
+
+  // static class Transform {
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     r.run();
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAKAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAbBwAeAQAGPGluaXQ+AQADKClW" +
+    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+    "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk0Ni5qYXZhDAAJAAoHAB8MACAAIQEAE0hlbGxvIC0gVHJh" +
+    "bnNmb3JtZWQHACIMACMAJAcAJQwAJgAKAQAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkBwAnAQAVYXJ0" +
+    "L1Rlc3Q5NDYkVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5n" +
+    "L09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsB" +
+    "ABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEA" +
+    "EmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgEAC2FydC9UZXN0OTQ2ACAABwAIAAAAAAACAAAACQAK" +
+    "AAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAABQABAA0ADgABAAsAAAA7AAIAAgAA" +
+    "ABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAcACAAIAA4ACQAWAAoAAgAP" +
+    "AAAAAgAQAB0AAAAKAAEABwAaABwACA==");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQB0mzt6AAAAAAAAAAAAAAAAAAAAAAAAAAA8BAAAcAAAAHhWNBIAAAAAAAAAAHgDAAAX" +
+    "AAAAcAAAAAoAAADMAAAAAwAAAPQAAAABAAAAGAEAAAUAAAAgAQAAAQAAAEgBAADUAgAAaAEAAGgB" +
+    "AABwAQAAhwEAAJwBAAC1AQAAxAEAAOgBAAAIAgAAHwIAADMCAABJAgAAXQIAAHECAAB/AgAAigIA" +
+    "AI0CAACRAgAAngIAAKQCAACpAgAAsgIAALcCAAC+AgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+    "CQAAAAoAAAALAAAADgAAAA4AAAAJAAAAAAAAAA8AAAAJAAAAyAIAAA8AAAAJAAAA0AIAAAgABAAS" +
+    "AAAAAAAAAAAAAAAAAAEAFQAAAAQAAgATAAAABQAAAAAAAAAGAAAAFAAAAAAAAAAAAAAABQAAAAAA" +
+    "AAAMAAAAaAMAADwDAAAAAAAABjxpbml0PgAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkABNIZWxsbyAt" +
+    "IFRyYW5zZm9ybWVkABdMYXJ0L1Rlc3Q5NDYkVHJhbnNmb3JtOwANTGFydC9UZXN0OTQ2OwAiTGRh" +
+    "bHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVy" +
+    "Q2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEv" +
+    "bGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxU" +
+    "ZXN0OTQ2LmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vzc0ZsYWdzAARuYW1lAANvdXQAB3By" +
+    "aW50bG4AA3J1bgAFc2F5SGkABXZhbHVlAAAAAAEAAAAGAAAAAQAAAAcAAAAFAAcOAAcBAAcOAQgP" +
+    "AQMPAQgPAAEAAQABAAAA2AIAAAQAAABwEAMAAAAOAAQAAgACAAAA3QIAABQAAABiAAAAGwECAAAA" +
+    "biACABAAchAEAAMAYgAAABsBAQAAAG4gAgAQAA4AAAABAQCAgATsBQEBhAYAAAICARYYAQIDAhAE" +
+    "CBEXDQACAAAATAMAAFIDAABcAwAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAXAAAA" +
+    "cAAAAAIAAAAKAAAAzAAAAAMAAAADAAAA9AAAAAQAAAABAAAAGAEAAAUAAAAFAAAAIAEAAAYAAAAB" +
+    "AAAASAEAAAIgAAAXAAAAaAEAAAEQAAACAAAAyAIAAAMgAAACAAAA2AIAAAEgAAACAAAA7AIAAAAg" +
+    "AAABAAAAPAMAAAQgAAACAAAATAMAAAMQAAABAAAAXAMAAAYgAAABAAAAaAMAAAAQAAABAAAAeAMA" +
+    "AA==");
+
+  public static void run() {
+    doTest(new Transform());
+  }
+
+  static class DoRedefinitionClass implements Runnable {
+    @Override
+    public void run() {
+      System.out.println("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+      throw new Error("Throwing exception into an obsolete method!");
+    }
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    try {
+      t.sayHi(new DoRedefinitionClass());
+    } catch (Throwable e) {
+      System.out.println("Received error : " + e);
+      e.printStackTrace(System.out);
+    }
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+}
diff --git a/test/947-reflect-method/src/Main.java b/test/947-reflect-method/src/Main.java
index da746ac..bc3f4b2 100644
--- a/test/947-reflect-method/src/Main.java
+++ b/test/947-reflect-method/src/Main.java
@@ -14,60 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
-import java.lang.reflect.Method;
-
 public class Main {
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye");
-   *   }
-   * }
-   */
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
-    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
-    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
-    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
-    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
-    "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
-    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
-    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
-    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
-    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
-    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
-    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
-    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
-    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
-    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
-    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test947.run();
   }
-
-  public static void doTest(Transform t) {
-    try {
-      Method say_hi_method = t.getClass().getDeclaredMethod("sayHi");
-      say_hi_method.invoke(t);
-      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-      say_hi_method.invoke(t);
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] class_file,
-                                                       byte[] dex_file);
 }
diff --git a/test/947-reflect-method/src/Transform.java b/test/947-reflect-method/src/Transform.java
deleted file mode 100644
index b8fe34a..0000000
--- a/test/947-reflect-method/src/Transform.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-class Transform {
-  public void sayHi() {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Goodbye" < "LTransform;" < "hello".
-    System.out.println("hello");
-  }
-}
diff --git a/test/947-reflect-method/src/art/Redefinition.java b/test/947-reflect-method/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/947-reflect-method/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/947-reflect-method/src/art/Test947.java b/test/947-reflect-method/src/art/Test947.java
new file mode 100644
index 0000000..8cb515e
--- /dev/null
+++ b/test/947-reflect-method/src/art/Test947.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.Base64;
+import java.lang.reflect.Method;
+
+public class Test947 {
+
+  static class Transform {
+    public void sayHi() {
+      System.out.println("hello");
+    }
+  }
+
+
+  /**
+   * base64 encoded class/dex file for
+   * static class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAIAoABgAOCQAPABAIABEKABIAEwcAFQcAGAEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAAxUZXN0OTQ3LmphdmEMAAcA" +
+    "CAcAGQwAGgAbAQAHR29vZGJ5ZQcAHAwAHQAeBwAfAQAVYXJ0L1Rlc3Q5NDckVHJhbnNmb3JtAQAJ" +
+    "VHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9T" +
+    "eXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFt" +
+    "AQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAC2FydC9UZXN0OTQ3ACAABQAGAAAA" +
+    "AAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAABQABAAsACAABAAkA" +
+    "AAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAHAAgACAACAAwAAAACAA0AFwAAAAoA" +
+    "AQAFABQAFgAI");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQCEgoKcAAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" +
+    "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" +
+    "AABMAQAAVQEAAG4BAAB9AQAAoQEAAMEBAADYAQAA7AEAAAACAAAUAgAAIgIAAC0CAAAwAgAANAIA" +
+    "AEECAABHAgAATAIAAFUCAABcAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
+    "DAAAAAgAAAAAAAAADQAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
+    "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA5AIAALgCAAAAAAAABjxpbml0PgAHR29vZGJ5ZQAX" +
+    "TGFydC9UZXN0OTQ3JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk0NzsAIkxkYWx2aWsvYW5ub3RhdGlv" +
+    "bi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEv" +
+    "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" +
+    "TGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTQ3LmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vz" +
+    "c0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAQAAAAYAAAAFAAcOAAcA" +
+    "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQEA" +
+    "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg4ECA8XCwACAAAAyAIAAM4CAADY" +
+    "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" +
+    "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" +
+    "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" +
+    "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA==");
+
+  public static void run() {
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    try {
+      Method say_hi_method = t.getClass().getDeclaredMethod("sayHi");
+      say_hi_method.invoke(t);
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+      say_hi_method.invoke(t);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+}
diff --git a/test/948-change-annotations/src/Main.java b/test/948-change-annotations/src/Main.java
index a290396..5d3406d 100644
--- a/test/948-change-annotations/src/Main.java
+++ b/test/948-change-annotations/src/Main.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import art.Redefinition;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.Comparator;
@@ -85,7 +86,9 @@
   }
 
   // Transforms the class
-  public static native void doCommonClassRedefinition(Class<?> target,
-                                                      byte[] class_file,
-                                                      byte[] dex_file);
+  public static void doCommonClassRedefinition(Class<?> target,
+                                               byte[] class_file,
+                                               byte[] dex_file) {
+    Redefinition.doCommonClassRedefinition(target, class_file, dex_file);
+  }
 }
diff --git a/test/948-change-annotations/src/art/Redefinition.java b/test/948-change-annotations/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/948-change-annotations/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/949-in-memory-transform/src/Main.java b/test/949-in-memory-transform/src/Main.java
index 1a6b224..b49a93f 100644
--- a/test/949-in-memory-transform/src/Main.java
+++ b/test/949-in-memory-transform/src/Main.java
@@ -14,112 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
-import java.lang.reflect.*;
-import java.nio.ByteBuffer;
-
 public class Main {
-  /**
-   * base64 encoded class/dex file for
-   * public class Transform {
-   *   public void sayHi() {
-   *    System.out.println("hello");
-   *   }
-   * }
-   */
-  private static final byte[] INITIAL_CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
-    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
-    "BwAIBwAWDAAXABgBAAVoZWxsbwcAGQwAGgAbAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVj" +
-    "dAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZh" +
-    "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAA" +
-    "AAAAAgABAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAABEAAQALAAgAAQAJ" +
-    "AAAAJQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAAGgAIABsAAQAMAAAAAgAN");
-  private static final byte[] INITIAL_DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQAJX3mZphwHJCT1qdTz/GS+jXOR+O/9e3fMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
-    "AABqAQAAdwEAAI4BAACiAQAAtgEAAMoBAADaAQAA3QEAAOEBAAD1AQAA/AEAAAECAAAKAgAAAQAA" +
-    "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAGAAAAAAAAABwCAAAA" +
-    "AAAAAQABAAEAAAARAgAABAAAAHAQAwAAAA4AAwABAAIAAAAWAgAACQAAAGIAAAAbAQoAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
-    "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
-    "OwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUABWhlbGxvAANvdXQA" +
-    "B3ByaW50bG4ABXNheUhpABEABw4AGgAHDocAAAABAQCBgASgAgEBuAIAAA0AAAAAAAAAAQAAAAAA" +
-    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
-    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
-    "AgAAABECAAAAIAAAAQAAABwCAAAAEAAAAQAAACwCAAA=");
-
-
-  /**
-   * base64 encoded class/dex file for
-   * public class Transform {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye");
-   *   }
-   * }
-   */
-  private static final byte[] TRANSFORMED_CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
-    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
-    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
-    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
-    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAG" +
-    "AAAAAAACAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
-    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAaAAgAGwABAAwAAAACAA0=");
-  private static final byte[] TRANSFORMED_DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQAPXh6T3l1FObhHsKf1U2vi+0GmAvElxBLMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
-    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
-    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
-    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
-    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
-    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUAA291" +
-    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgAaAAcOhwAAAAEBAIGABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
-    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
-    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
-    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
-
   public static void main(String[] args) throws Exception {
-    art.Main.bindAgentJNIForClass(Main.class);
-    ClassLoader loader;
-    try {
-      // Art uses this classloader to do in-memory dex files. There is no support for defineClass
-      loader = (ClassLoader)Class.forName("dalvik.system.InMemoryDexClassLoader")
-                                 .getConstructor(ByteBuffer.class, ClassLoader.class)
-                                 .newInstance(ByteBuffer.wrap(INITIAL_DEX_BYTES),
-                                              ClassLoader.getSystemClassLoader());
-    } catch (ClassNotFoundException e) {
-      // Seem to be on RI. Just make a new ClassLoader that calls defineClass.
-      loader = new ClassLoader() {
-        public Class<?> findClass(String name) throws ClassNotFoundException {
-          if (name.equals("Transform")) {
-            return defineClass(name, INITIAL_CLASS_BYTES, 0, INITIAL_CLASS_BYTES.length);
-          } else {
-            throw new ClassNotFoundException("Couldn't find class: " + name);
-          }
-        }
-      };
-    }
-    doTest(loader);
+    art.Test949.run();
   }
-
-  public static void doTest(ClassLoader loader) throws Exception {
-    // Get the class
-    Class<?> transform_class = loader.loadClass("Transform");
-    Method say_hi_method = transform_class.getMethod("sayHi");
-    Object t = transform_class.newInstance();
-
-    // Run the actual test.
-    say_hi_method.invoke(t);
-    doCommonClassRedefinition(transform_class, TRANSFORMED_CLASS_BYTES, TRANSFORMED_DEX_BYTES);
-    say_hi_method.invoke(t);
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] class_file,
-                                                       byte[] dex_file);
 }
diff --git a/test/949-in-memory-transform/src/art/Redefinition.java b/test/949-in-memory-transform/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/949-in-memory-transform/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/949-in-memory-transform/src/art/Test949.java b/test/949-in-memory-transform/src/art/Test949.java
new file mode 100644
index 0000000..cd733b9
--- /dev/null
+++ b/test/949-in-memory-transform/src/art/Test949.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import static art.Redefinition.doCommonClassRedefinition;
+
+import java.util.Base64;
+import java.lang.reflect.*;
+import java.nio.ByteBuffer;
+
+public class Test949 {
+  /**
+   * base64 encoded class/dex file for
+   * public class Transform {
+   *   public void sayHi() {
+   *    System.out.println("hello");
+   *   }
+   * }
+   */
+  private static final byte[] INITIAL_CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+    "BwAIBwAWDAAXABgBAAVoZWxsbwcAGQwAGgAbAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVj" +
+    "dAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZh" +
+    "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAA" +
+    "AAAAAgABAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAABEAAQALAAgAAQAJ" +
+    "AAAAJQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAAGgAIABsAAQAMAAAAAgAN");
+  private static final byte[] INITIAL_DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAJX3mZphwHJCT1qdTz/GS+jXOR+O/9e3fMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+    "AABqAQAAdwEAAI4BAACiAQAAtgEAAMoBAADaAQAA3QEAAOEBAAD1AQAA/AEAAAECAAAKAgAAAQAA" +
+    "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAGAAAAAAAAABwCAAAA" +
+    "AAAAAQABAAEAAAARAgAABAAAAHAQAwAAAA4AAwABAAIAAAAWAgAACQAAAGIAAAAbAQoAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+    "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+    "OwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUABWhlbGxvAANvdXQA" +
+    "B3ByaW50bG4ABXNheUhpABEABw4AGgAHDocAAAABAQCBgASgAgEBuAIAAA0AAAAAAAAAAQAAAAAA" +
+    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+    "AgAAABECAAAAIAAAAQAAABwCAAAAEAAAAQAAACwCAAA=");
+
+
+  /**
+   * base64 encoded class/dex file for
+   * public class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] TRANSFORMED_CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
+    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
+    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAG" +
+    "AAAAAAACAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
+    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAaAAgAGwABAAwAAAACAA0=");
+  private static final byte[] TRANSFORMED_DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAPXh6T3l1FObhHsKf1U2vi+0GmAvElxBLMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUAA291" +
+    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgAaAAcOhwAAAAEBAIGABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+  public static void run() throws Exception {
+    ClassLoader loader;
+    try {
+      // Art uses this classloader to do in-memory dex files. There is no support for defineClass
+      loader = (ClassLoader)Class.forName("dalvik.system.InMemoryDexClassLoader")
+                                 .getConstructor(ByteBuffer.class, ClassLoader.class)
+                                 .newInstance(ByteBuffer.wrap(INITIAL_DEX_BYTES),
+                                              ClassLoader.getSystemClassLoader());
+    } catch (ClassNotFoundException e) {
+      // Seem to be on RI. Just make a new ClassLoader that calls defineClass.
+      loader = new ClassLoader() {
+        public Class<?> findClass(String name) throws ClassNotFoundException {
+          if (name.equals("Transform")) {
+            return defineClass(name, INITIAL_CLASS_BYTES, 0, INITIAL_CLASS_BYTES.length);
+          } else {
+            throw new ClassNotFoundException("Couldn't find class: " + name);
+          }
+        }
+      };
+    }
+    doTest(loader);
+  }
+
+  public static void doTest(ClassLoader loader) throws Exception {
+    // Get the class
+    Class<?> transform_class = loader.loadClass("Transform");
+    Method say_hi_method = transform_class.getMethod("sayHi");
+    Object t = transform_class.newInstance();
+
+    // Run the actual test.
+    say_hi_method.invoke(t);
+    doCommonClassRedefinition(transform_class, TRANSFORMED_CLASS_BYTES, TRANSFORMED_DEX_BYTES);
+    say_hi_method.invoke(t);
+  }
+}
diff --git a/test/950-redefine-intrinsic/src/Main.java b/test/950-redefine-intrinsic/src/Main.java
index 2578d6e..369a8f4 100644
--- a/test/950-redefine-intrinsic/src/Main.java
+++ b/test/950-redefine-intrinsic/src/Main.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import static art.Redefinition.doCommonClassRedefinition;
 import java.util.Base64;
 import java.util.Random;
 import java.util.function.*;
@@ -464,9 +465,4 @@
     }
     System.out.println("Finished!");
   }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] class_file,
-                                                       byte[] dex_file);
 }
diff --git a/test/950-redefine-intrinsic/src/art/Redefinition.java b/test/950-redefine-intrinsic/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/950-redefine-intrinsic/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/951-threaded-obsolete/src/Main.java b/test/951-threaded-obsolete/src/Main.java
index a82090e..d245aa9 100644
--- a/test/951-threaded-obsolete/src/Main.java
+++ b/test/951-threaded-obsolete/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,84 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
-import java.util.concurrent.Semaphore;
-
 public class Main {
-  // class Transform {
-  //   public void sayHi(Runnable r) {
-  //     System.out.println("Hello - Transformed");
-  //     r.run();
-  //     System.out.println("Goodbye - Transformed");
-  //   }
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
-    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
-    "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
-    "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
-    "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
-    "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
-    "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
-    "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQABAA0ADgABAAsAAAA7AAIA" +
-    "AgAAABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
-    "AQAPAAAAAgAQ");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQAYeAMMXgYWxoeSHAS9EWKCCtVRSAGpqZVQAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
-    "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
-    "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
-    "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
-    "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
-    "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
-    "AwAAAA4ABAACAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAwBiAAAAGwEBAAAAbiAC" +
-    "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
-    "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
-    "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
-    "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
-    "LjEzAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAEBAICABMQCAQHc" +
-    "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
-    "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
-    "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    // Semaphores to let each thread know where the other is. We could use barriers but semaphores
-    // mean we don't need to have the worker thread be waiting around.
-    final Semaphore sem_redefine_start = new Semaphore(0);
-    final Semaphore sem_redefine_end = new Semaphore(0);
-    // Create a thread to do the actual redefinition. We will just communicate through an
-    // atomic-integer.
-    new Thread(() -> {
-      try {
-        // Wait for the other thread to ask for redefinition.
-        sem_redefine_start.acquire();
-        // Do the redefinition.
-        doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-        // Allow the other thread to wake up if it is waiting.
-        sem_redefine_end.release();
-      } catch (InterruptedException e) {
-        throw new Error("unable to do redefinition", e);
-      }
-    }).start();
-
-    Transform t = new Transform();
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
-    t.sayHi(() -> {
-      try {
-        System.out.println("transforming calling function");
-        // Wake up the waiting thread.
-        sem_redefine_start.release();
-        // Wait for the other thread to finish with redefinition.
-        sem_redefine_end.acquire();
-      } catch (InterruptedException e) {
-        throw new Error("unable to do redefinition", e);
-      }
-    });
-    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  public static void main(String[] args) throws Exception {
+    art.Test951.run();
   }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
 }
diff --git a/test/951-threaded-obsolete/src/Transform.java b/test/951-threaded-obsolete/src/Transform.java
deleted file mode 100644
index 8cda6cd..0000000
--- a/test/951-threaded-obsolete/src/Transform.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi(Runnable r) {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Hello" < "LTransform;" < "hello".
-    System.out.println("hello");
-    r.run();
-    System.out.println("goodbye");
-  }
-}
diff --git a/test/951-threaded-obsolete/src/art/Redefinition.java b/test/951-threaded-obsolete/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/951-threaded-obsolete/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/951-threaded-obsolete/src/art/Test951.java b/test/951-threaded-obsolete/src/art/Test951.java
new file mode 100644
index 0000000..3628f4f
--- /dev/null
+++ b/test/951-threaded-obsolete/src/art/Test951.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+import java.util.concurrent.Semaphore;
+
+public class Test951 {
+
+  static class Transform {
+    public void sayHi(Runnable r) {
+      System.out.println("hello");
+      r.run();
+      System.out.println("goodbye");
+    }
+  }
+
+  // static class Transform {
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     r.run();
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAKAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAbBwAeAQAGPGluaXQ+AQADKClW" +
+    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+    "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk1MS5qYXZhDAAJAAoHAB8MACAAIQEAE0hlbGxvIC0gVHJh" +
+    "bnNmb3JtZWQHACIMACMAJAcAJQwAJgAKAQAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkBwAnAQAVYXJ0" +
+    "L1Rlc3Q5NTEkVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5n" +
+    "L09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsB" +
+    "ABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEA" +
+    "EmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgEAC2FydC9UZXN0OTUxACAABwAIAAAAAAACAAAACQAK" +
+    "AAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAABQABAA0ADgABAAsAAAA7AAIAAgAA" +
+    "ABeyAAISA7YABCu5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAcACAAIAA4ACQAWAAoAAgAP" +
+    "AAAAAgAQAB0AAAAKAAEABwAaABwACA==");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBom/JeAAAAAAAAAAAAAAAAAAAAAAAAAAA8BAAAcAAAAHhWNBIAAAAAAAAAAHgDAAAX" +
+    "AAAAcAAAAAoAAADMAAAAAwAAAPQAAAABAAAAGAEAAAUAAAAgAQAAAQAAAEgBAADUAgAAaAEAAGgB" +
+    "AABwAQAAhwEAAJwBAAC1AQAAxAEAAOgBAAAIAgAAHwIAADMCAABJAgAAXQIAAHECAAB/AgAAigIA" +
+    "AI0CAACRAgAAngIAAKQCAACpAgAAsgIAALcCAAC+AgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+    "CQAAAAoAAAALAAAADgAAAA4AAAAJAAAAAAAAAA8AAAAJAAAAyAIAAA8AAAAJAAAA0AIAAAgABAAS" +
+    "AAAAAAAAAAAAAAAAAAEAFQAAAAQAAgATAAAABQAAAAAAAAAGAAAAFAAAAAAAAAAAAAAABQAAAAAA" +
+    "AAAMAAAAaAMAADwDAAAAAAAABjxpbml0PgAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkABNIZWxsbyAt" +
+    "IFRyYW5zZm9ybWVkABdMYXJ0L1Rlc3Q5NTEkVHJhbnNmb3JtOwANTGFydC9UZXN0OTUxOwAiTGRh" +
+    "bHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVy" +
+    "Q2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEv" +
+    "bGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxU" +
+    "ZXN0OTUxLmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vzc0ZsYWdzAARuYW1lAANvdXQAB3By" +
+    "aW50bG4AA3J1bgAFc2F5SGkABXZhbHVlAAAAAAEAAAAGAAAAAQAAAAcAAAAFAAcOAAcBAAcOAQgP" +
+    "AQMPAQgPAAEAAQABAAAA2AIAAAQAAABwEAMAAAAOAAQAAgACAAAA3QIAABQAAABiAAAAGwECAAAA" +
+    "biACABAAchAEAAMAYgAAABsBAQAAAG4gAgAQAA4AAAABAQCAgATsBQEBhAYAAAICARYYAQIDAhAE" +
+    "CBEXDQACAAAATAMAAFIDAABcAwAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAXAAAA" +
+    "cAAAAAIAAAAKAAAAzAAAAAMAAAADAAAA9AAAAAQAAAABAAAAGAEAAAUAAAAFAAAAIAEAAAYAAAAB" +
+    "AAAASAEAAAIgAAAXAAAAaAEAAAEQAAACAAAAyAIAAAMgAAACAAAA2AIAAAEgAAACAAAA7AIAAAAg" +
+    "AAABAAAAPAMAAAQgAAACAAAATAMAAAMQAAABAAAAXAMAAAYgAAABAAAAaAMAAAAQAAABAAAAeAMA" +
+    "AA==");
+
+  public static void run() {
+    // Semaphores to let each thread know where the other is. We could use barriers but semaphores
+    // mean we don't need to have the worker thread be waiting around.
+    final Semaphore sem_redefine_start = new Semaphore(0);
+    final Semaphore sem_redefine_end = new Semaphore(0);
+    // Create a thread to do the actual redefinition. We will just communicate through an
+    // atomic-integer.
+    new Thread(() -> {
+      try {
+        // Wait for the other thread to ask for redefinition.
+        sem_redefine_start.acquire();
+        // Do the redefinition.
+        Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+        // Allow the other thread to wake up if it is waiting.
+        sem_redefine_end.release();
+      } catch (InterruptedException e) {
+        throw new Error("unable to do redefinition", e);
+      }
+    }).start();
+
+    Transform t = new Transform();
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    t.sayHi(() -> {
+      try {
+        System.out.println("transforming calling function");
+        // Wake up the waiting thread.
+        sem_redefine_start.release();
+        // Wait for the other thread to finish with redefinition.
+        sem_redefine_end.acquire();
+      } catch (InterruptedException e) {
+        throw new Error("unable to do redefinition", e);
+      }
+    });
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+}
diff --git a/test/959-invoke-polymorphic-accessors/src/Main.java b/test/959-invoke-polymorphic-accessors/src/Main.java
index b7ecf8e..59db807 100644
--- a/test/959-invoke-polymorphic-accessors/src/Main.java
+++ b/test/959-invoke-polymorphic-accessors/src/Main.java
@@ -794,6 +794,7 @@
             ValueHolder valueHolder = new ValueHolder();
             MethodHandles.Lookup lookup = MethodHandles.lookup();
             MethodHandle h0 = lookup.findSetter(ValueHolder.class, "m_f", float.class);
+            MethodHandle s0 = lookup.findSetter(ValueHolder.class, "m_s", short.class);
             h0.invoke(valueHolder, 0.22f);
             h0.invoke(valueHolder, new Float(1.11f));
             Number floatNumber = getFloatAsNumber();
@@ -807,6 +808,11 @@
               unreachable();
             } catch (NullPointerException e) {}
 
+            // Test that type conversion checks work on small field types.
+            short temp = (short)s0.invoke(valueHolder, new Byte((byte)45));
+            assertTrue(temp == 0);
+            assertTrue(valueHolder.m_s == 45);
+
             h0.invoke(valueHolder, (byte)1);
             h0.invoke(valueHolder, (short)2);
             h0.invoke(valueHolder, 3);
@@ -848,6 +854,7 @@
 
         private static void testStaticSetter() throws Throwable {
             MethodHandles.Lookup lookup = MethodHandles.lookup();
+            MethodHandle s0 = lookup.findStaticSetter(ValueHolder.class, "s_s", short.class);
             MethodHandle h0 = lookup.findStaticSetter(ValueHolder.class, "s_f", float.class);
             h0.invoke(0.22f);
             h0.invoke(new Float(1.11f));
@@ -860,6 +867,11 @@
               unreachable();
             } catch (NullPointerException e) {}
 
+            // Test that type conversion checks work on small field types.
+            short temp = (short)s0.invoke(new Byte((byte)45));
+            assertTrue(temp == 0);
+            assertTrue(ValueHolder.s_s == 45);
+
             h0.invoke((byte)1);
             h0.invoke((short)2);
             h0.invoke(3);
diff --git a/test/980-redefine-object/check b/test/980-redefine-object/check
index 987066f..07b21b3 100755
--- a/test/980-redefine-object/check
+++ b/test/980-redefine-object/check
@@ -17,4 +17,4 @@
 # The number of paused background threads (and therefore InterruptedExceptions)
 # can change so we will just delete their lines from the log.
 
-sed "/Object allocated of type 'Ljava\/lang\/InterruptedException;'/d" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
+sed "/Object allocated of type 'java\.lang\.InterruptedException'/d" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
diff --git a/test/980-redefine-object/expected.txt b/test/980-redefine-object/expected.txt
index 6e9bce0..4c294bc 100644
--- a/test/980-redefine-object/expected.txt
+++ b/test/980-redefine-object/expected.txt
@@ -2,51 +2,31 @@
 	Allocating an j.l.Object before redefining Object class
 	Allocating a Transform before redefining Object class
 	Redefining the Object class to add a hook into the <init> method
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
 	Allocating an j.l.Object after redefining Object class
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Object'
 	Allocating a Transform after redefining Object class
-Object allocated of type 'LTransform;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'Transform'
 	Allocating an int[] after redefining Object class
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
 	Allocating an array list
-Object allocated of type 'Ljava/util/ArrayList;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.util.ArrayList'
 	Adding a bunch of stuff to the array list
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'LTransform;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'Transform'
 	Allocating a linked list
-Object allocated of type 'Ljava/util/LinkedList;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.util.LinkedList'
 	Adding a bunch of stuff to the linked list
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/lang/Object;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'LTransform;'
-Object allocated of type 'Ljava/util/LinkedList$Node;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.lang.Object'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'java.util.LinkedList$Node'
+Object allocated of type 'Transform'
+Object allocated of type 'java.util.LinkedList$Node'
 	Throwing from down 4 stack frames
-Object allocated of type 'Ljava/lang/Exception;'
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
+Object allocated of type 'java.lang.Exception'
 	Exception caught.
-Object allocated of type 'Ljava/lang/StringBuilder;'
-Object allocated of type 'Ljava/nio/HeapCharBuffer;'
 	Finishing test!
diff --git a/test/980-redefine-object/redefine_object.cc b/test/980-redefine-object/redefine_object.cc
deleted file mode 100644
index 1faf1a1..0000000
--- a/test/980-redefine-object/redefine_object.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include <inttypes.h>
-#include <iostream>
-
-#include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "jni.h"
-#include "jvmti.h"
-#include "scoped_utf_chars.h"
-
-// Test infrastructure
-#include "jni_binder.h"
-#include "jvmti_helper.h"
-#include "test_env.h"
-
-namespace art {
-namespace Test980RedefineObjects {
-
-extern "C" JNIEXPORT void JNICALL Java_Main_bindFunctionsForClass(
-    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass target) {
-  BindFunctionsOnClass(jvmti_env, env, target);
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_test_TestWatcher_NotifyConstructed(
-    JNIEnv* env, jclass TestWatcherClass ATTRIBUTE_UNUSED, jobject constructed) {
-  char* sig = nullptr;
-  char* generic_sig = nullptr;
-  if (JvmtiErrorToException(env,
-                            jvmti_env,
-                            jvmti_env->GetClassSignature(env->GetObjectClass(constructed),
-                                                         &sig,
-                                                         &generic_sig))) {
-    // Exception.
-    return;
-  }
-  std::cout << "Object allocated of type '" << sig << "'" << std::endl;
-  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
-  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(generic_sig));
-}
-
-}  // namespace Test980RedefineObjects
-}  // namespace art
diff --git a/test/980-redefine-object/src-ex/TestWatcher.java b/test/980-redefine-object/src-ex/TestWatcher.java
index d15e688..c38e07b 100644
--- a/test/980-redefine-object/src-ex/TestWatcher.java
+++ b/test/980-redefine-object/src-ex/TestWatcher.java
@@ -16,10 +16,60 @@
 
 package art.test;
 
+import java.util.concurrent.locks.ReentrantLock;
+
 public class TestWatcher {
-  // NB This function is native since it is called in the Object.<init> method and so cannot cause
-  // any java allocations at all. The normal System.out.print* functions will cause allocations to
-  // occur so we cannot use them. This means the easiest way to report the object as being created
-  // is to go into native code and do it there.
-  public static native void NotifyConstructed(Object o);
+  // Lock to synchronize access to the static state of this class.
+  private static final ReentrantLock lock = new ReentrantLock();
+  private static volatile boolean criticalFailure = false;
+  private static boolean reportingEnabled = true;
+  private static boolean doingReport = false;
+
+  private static void MonitorEnter() {
+    lock.lock();
+  }
+
+  private static void MonitorExit() {
+    // Need to do this manually since we need to notify critical failure but would deadlock if
+    // waited for the unlock.
+    if (!lock.isHeldByCurrentThread()) {
+      NotifyCriticalFailure();
+      throw new IllegalMonitorStateException("Locking error!");
+    } else {
+      lock.unlock();
+    }
+  }
+
+  // Stops reporting. Must be paired with an EnableReporting call.
+  public static void DisableReporting() {
+    MonitorEnter();
+    reportingEnabled = false;
+  }
+
+  // Stops reporting. Must be paired with a DisableReporting call.
+  public static void EnableReporting() {
+    reportingEnabled = true;
+    MonitorExit();
+  }
+
+  public static void NotifyCriticalFailure() {
+    criticalFailure = true;
+  }
+
+  public static void NotifyConstructed(Object o) {
+    if (criticalFailure) {
+      // Something went very wrong. We are probably trying to report it so don't get in the way.
+      return;
+    }
+    MonitorEnter();
+    // We could enter an infinite loop if println allocates (which it does) so we disable
+    // reporting while we are doing a report. Since we are synchronized we won't miss any
+    // allocations.
+    if (reportingEnabled && !doingReport) {
+      doingReport = true;
+      System.out.println("Object allocated of type '" + o.getClass().getName() + "'");
+      doingReport = false;
+    }
+    MonitorExit();
+  }
 }
diff --git a/test/980-redefine-object/src/Main.java b/test/980-redefine-object/src/Main.java
index a50215e..63c0cab 100644
--- a/test/980-redefine-object/src/Main.java
+++ b/test/980-redefine-object/src/Main.java
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+import static art.Redefinition.doCommonClassRedefinition;
+
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Base64;
 import java.util.LinkedList;
@@ -287,6 +290,31 @@
   private static final String LISTENER_LOCATION =
       System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar";
 
+  private static Method doEnableReporting;
+  private static Method doDisableReporting;
+
+  private static void DisableReporting() {
+    if (doDisableReporting == null) {
+      return;
+    }
+    try {
+      doDisableReporting.invoke(null);
+    } catch (Exception e) {
+      throw new Error("Unable to disable reporting!");
+    }
+  }
+
+  private static void EnableReporting() {
+    if (doEnableReporting == null) {
+      return;
+    }
+    try {
+      doEnableReporting.invoke(null);
+    } catch (Exception e) {
+      throw new Error("Unable to enable reporting!");
+    }
+  }
+
   public static void main(String[] args) {
     art.Main.bindAgentJNIForClass(Main.class);
     doTest();
@@ -298,8 +326,8 @@
       addToBootClassLoader(LISTENER_LOCATION);
       // Load TestWatcher from the bootclassloader and make sure it is initialized.
       Class<?> testwatcher_class = Class.forName("art.test.TestWatcher", true, null);
-      // Bind the native functions of testwatcher_class.
-      bindFunctionsForClass(testwatcher_class);
+      doEnableReporting = testwatcher_class.getDeclaredMethod("EnableReporting");
+      doDisableReporting = testwatcher_class.getDeclaredMethod("DisableReporting");
     } catch (Exception e) {
       throw new Error("Exception while making testwatcher", e);
     }
@@ -308,9 +336,9 @@
   // NB This function will cause 2 objects of type "Ljava/nio/HeapCharBuffer;" and
   // "Ljava/nio/HeapCharBuffer;" to be allocated each time it is called.
   private static void safePrintln(Object o) {
-    System.out.flush();
-    System.out.print("\t" + o + "\n");
-    System.out.flush();
+    DisableReporting();
+    System.out.println("\t" + o);
+    EnableReporting();
   }
 
   private static void throwFrom(int depth) throws Exception {
@@ -381,11 +409,4 @@
   }
 
   private static native void addToBootClassLoader(String s);
-
-  private static native void bindFunctionsForClass(Class<?> target);
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] class_file,
-                                                       byte[] dex_file);
 }
diff --git a/test/980-redefine-object/src/art/Redefinition.java b/test/980-redefine-object/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/980-redefine-object/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/981-dedup-original-dex/src/Main.java b/test/981-dedup-original-dex/src/Main.java
index 288f7ce..f90c15c 100644
--- a/test/981-dedup-original-dex/src/Main.java
+++ b/test/981-dedup-original-dex/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,190 +14,8 @@
  * limitations under the License.
  */
 
-import java.lang.reflect.Field;
-import java.util.Base64;
-import java.nio.ByteBuffer;
-
-import dalvik.system.ClassExt;
-import dalvik.system.InMemoryDexClassLoader;
-
 public class Main {
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye");
-   *   }
-   * }
-   */
-  private static final byte[] DEX_BYTES_1 = Base64.getDecoder().decode(
-    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
-    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
-    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
-    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
-    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
-    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
-    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
-    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
-    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
-    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform2 {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye2");
-   *   }
-   * }
-   */
-  private static final byte[] DEX_BYTES_2 = Base64.getDecoder().decode(
-    "ZGV4CjAzNQAjXDED2iflQ3NXbPtBRVjQVMqoDU9nDz/QAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
-    "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" +
-    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" +
-    "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTIADExUcmFuc2Zvcm0yOwAVTGphdmEvaW8vUHJp" +
-    "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" +
-    "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" +
-    "A291dAAHcHJpbnRsbgAFc2F5SGkAAQAHDgADAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" +
-    "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
-    "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
-    "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA");
-
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform3 {
-   *   public void sayHi() {
-   *    System.out.println("hello3");
-   *   }
-   * }
-   */
-  private static final byte[] DEX_BYTES_3_INITIAL = Base64.getDecoder().decode(
-    "ZGV4CjAzNQC2W2fBsAeLNAwWYlG8FVigzfsV7nBWITzQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
-    "AABqAQAAeAEAAI8BAACjAQAAtwEAAMsBAADcAQAA3wEAAOMBAAD3AQAA/wEAAAQCAAANAgAAAQAA" +
-    "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAAB8CAAAA" +
-    "AAAAAQABAAEAAAAUAgAABAAAAHAQAwAAAA4AAwABAAIAAAAZAgAACQAAAGIAAAAbAQoAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgAMTFRyYW5zZm9ybTM7ABVMamF2YS9pby9QcmludFN0cmVhbTsA" +
-    "EkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3Rl" +
-    "bTsAD1RyYW5zZm9ybTMuamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4zMAAGaGVsbG8zAANv" +
-    "dXQAB3ByaW50bG4ABXNheUhpAAIABw4ABAAHDocAAAABAQCAgASgAgEBuAIAAAANAAAAAAAAAAEA" +
-    "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
-    "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
-    "AyAAAAIAAAAUAgAAACAAAAEAAAAfAgAAABAAAAEAAAAwAgAA");
-
-  /**
-   * base64 encoded class/dex file for
-   * class Transform3 {
-   *   public void sayHi() {
-   *    System.out.println("Goodbye3");
-   *   }
-   * }
-   */
-  private static final byte[] DEX_BYTES_3_FINAL = Base64.getDecoder().decode(
-    "ZGV4CjAzNQBAXE5GthgMydaFBuinf+ZBfXcBYIw2UlXQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
-    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
-    "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" +
-    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
-    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" +
-    "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" +
-    "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTMADExUcmFuc2Zvcm0zOwAVTGphdmEvaW8vUHJp" +
-    "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" +
-    "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0zLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" +
-    "A291dAAHcHJpbnRsbgAFc2F5SGkAAgAHDgAEAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" +
-    "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
-    "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
-    "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    try {
-      doTest();
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
+  public static void main(String[] args) throws Exception {
+    art.Test981.run();
   }
-
-  private static void assertSame(Object a, Object b) throws Exception {
-    if (a != b) {
-      throw new AssertionError("'" + (a != null ? a.toString() : "null") + "' is not the same as " +
-                               "'" + (b != null ? b.toString() : "null") + "'");
-    }
-  }
-
-  private static Object getObjectField(Object o, String name) throws Exception {
-    return getObjectField(o, o.getClass(), name);
-  }
-
-  private static Object getObjectField(Object o, Class<?> type, String name) throws Exception {
-    Field f = type.getDeclaredField(name);
-    f.setAccessible(true);
-    return f.get(o);
-  }
-
-  private static Object getOriginalDexFile(Class<?> k) throws Exception {
-    ClassExt ext_data_object = (ClassExt) getObjectField(k, "extData");
-    if (ext_data_object == null) {
-      return null;
-    }
-
-    return getObjectField(ext_data_object, "originalDexFile");
-  }
-
-  public static void doTest() throws Exception {
-    // Make sure both of these are loaded prior to transformations being added so they have the same
-    // original dex files.
-    Transform t1 = new Transform();
-    Transform2 t2 = new Transform2();
-
-    assertSame(null, getOriginalDexFile(t1.getClass()));
-    assertSame(null, getOriginalDexFile(t2.getClass()));
-    assertSame(null, getOriginalDexFile(Main.class));
-
-    addCommonTransformationResult("Transform", new byte[0], DEX_BYTES_1);
-    addCommonTransformationResult("Transform2", new byte[0], DEX_BYTES_2);
-    enableCommonRetransformation(true);
-    doCommonClassRetransformation(Transform.class, Transform2.class);
-
-    assertSame(getOriginalDexFile(t1.getClass()), getOriginalDexFile(t2.getClass()));
-    assertSame(null, getOriginalDexFile(Main.class));
-    // Make sure that the original dex file is a DexCache object.
-    assertSame(getOriginalDexFile(t1.getClass()).getClass(), Class.forName("java.lang.DexCache"));
-
-    // Check that we end up with a byte[] if we do a direct RedefineClasses
-    enableCommonRetransformation(false);
-    doCommonClassRedefinition(Transform.class, new byte[0], DEX_BYTES_1);
-    assertSame((new byte[0]).getClass(), getOriginalDexFile(t1.getClass()).getClass());
-
-    // Check we don't have anything if we don't have any originalDexFile if the onload
-    // transformation doesn't do anything.
-    enableCommonRetransformation(true);
-    Class<?> transform3Class = new InMemoryDexClassLoader(
-        ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Main.class.getClassLoader()).loadClass("Transform3");
-    assertSame(null, getOriginalDexFile(transform3Class));
-
-    // Check that we end up with a java.lang.Long pointer if we do an 'on-load' redefinition.
-    addCommonTransformationResult("Transform3", new byte[0], DEX_BYTES_3_FINAL);
-    enableCommonRetransformation(true);
-    Class<?> transform3ClassTransformed = new InMemoryDexClassLoader(
-        ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Main.class.getClassLoader()).loadClass("Transform3");
-    assertSame(Long.class, getOriginalDexFile(transform3ClassTransformed).getClass());
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRetransformation(Class<?>... target);
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] class_file,
-                                                       byte[] dex_file);
-  private static native void enableCommonRetransformation(boolean enable);
-  private static native void addCommonTransformationResult(String target_name,
-                                                           byte[] class_bytes,
-                                                           byte[] dex_bytes);
 }
diff --git a/test/981-dedup-original-dex/src/Transform.java b/test/981-dedup-original-dex/src/Transform.java
deleted file mode 100644
index 3c97907..0000000
--- a/test/981-dedup-original-dex/src/Transform.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi() {
-    System.out.println("hello");
-  }
-}
diff --git a/test/981-dedup-original-dex/src/Transform2.java b/test/981-dedup-original-dex/src/Transform2.java
deleted file mode 100644
index eb22842..0000000
--- a/test/981-dedup-original-dex/src/Transform2.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform2 {
-  public void sayHi() {
-    System.out.println("hello2");
-  }
-}
diff --git a/test/981-dedup-original-dex/src/art/Redefinition.java b/test/981-dedup-original-dex/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/981-dedup-original-dex/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/981-dedup-original-dex/src/art/Test981.java b/test/981-dedup-original-dex/src/art/Test981.java
new file mode 100644
index 0000000..3a97268
--- /dev/null
+++ b/test/981-dedup-original-dex/src/art/Test981.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.util.Base64;
+import java.nio.ByteBuffer;
+
+import dalvik.system.ClassExt;
+import dalvik.system.InMemoryDexClassLoader;
+
+public class Test981 {
+
+  static class Transform {
+    public void sayHi() {
+      System.out.println("hello");
+    }
+  }
+
+  static class Transform2 {
+    public void sayHi() {
+      System.out.println("hello2");
+    }
+  }
+
+  /**
+   * base64 encoded class/dex file for
+   * static class Transform {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye");
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES_1 = Base64.getDecoder().decode(
+    "ZGV4CjAzNQB+giqQAAAAAAAAAAAAAAAAAAAAAAAAAAC4AwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAU" +
+    "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB0AgAARAEAAEQB" +
+    "AABMAQAAVQEAAG4BAAB9AQAAoQEAAMEBAADYAQAA7AEAAAACAAAUAgAAIgIAAC0CAAAwAgAANAIA" +
+    "AEECAABHAgAATAIAAFUCAABcAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
+    "DAAAAAgAAAAAAAAADQAAAAgAAABkAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
+    "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA5AIAALgCAAAAAAAABjxpbml0PgAHR29vZGJ5ZQAX" +
+    "TGFydC9UZXN0OTgxJFRyYW5zZm9ybTsADUxhcnQvVGVzdDk4MTsAIkxkYWx2aWsvYW5ub3RhdGlv" +
+    "bi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEv" +
+    "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" +
+    "TGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0OTgxLmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vz" +
+    "c0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAQAAAAYAAAAFAAcOAAcA" +
+    "Bw4BCA8AAAAAAQABAAEAAABsAgAABAAAAHAQAwAAAA4AAwABAAIAAABxAgAACQAAAGIAAAAbAQEA" +
+    "AABuIAIAEAAOAAAAAAABAQCAgAT8BAEBlAUAAAICARMYAQIDAg4ECA8XCwACAAAAyAIAAM4CAADY" +
+    "AgAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAUAAAAcAAAAAIAAAAJAAAAwAAAAAMA" +
+    "AAACAAAA5AAAAAQAAAABAAAA/AAAAAUAAAAEAAAABAEAAAYAAAABAAAAJAEAAAIgAAAUAAAARAEA" +
+    "AAEQAAABAAAAZAIAAAMgAAACAAAAbAIAAAEgAAACAAAAfAIAAAAgAAABAAAAuAIAAAQgAAACAAAA" +
+    "yAIAAAMQAAABAAAA2AIAAAYgAAABAAAA5AIAAAAQAAABAAAA9AIAAA==");
+
+  /**
+   * base64 encoded class/dex file for
+   * static class Transform2 {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye2");
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES_2 = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAhg+RVAAAAAAAAAAAAAAAAAAAAAAAAAAC8AwAAcAAAAHhWNBIAAAAAAAAAAPgCAAAU" +
+    "AAAAcAAAAAkAAADAAAAAAgAAAOQAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAB4AgAARAEAAEQB" +
+    "AABMAQAAVgEAAHABAAB/AQAAowEAAMMBAADaAQAA7gEAAAICAAAWAgAAJAIAADACAAAzAgAANwIA" +
+    "AEQCAABKAgAATwIAAFgCAABfAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAA" +
+    "DAAAAAgAAAAAAAAADQAAAAgAAABoAgAABwAEABAAAAAAAAAAAAAAAAAAAAASAAAABAABABEAAAAF" +
+    "AAAAAAAAAAAAAAAAAAAABQAAAAAAAAAKAAAA6AIAALwCAAAAAAAABjxpbml0PgAIR29vZGJ5ZTIA" +
+    "GExhcnQvVGVzdDk4MSRUcmFuc2Zvcm0yOwANTGFydC9UZXN0OTgxOwAiTGRhbHZpay9hbm5vdGF0" +
+    "aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2" +
+    "YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7" +
+    "ABJMamF2YS9sYW5nL1N5c3RlbTsADFRlc3Q5ODEuamF2YQAKVHJhbnNmb3JtMgABVgACVkwAC2Fj" +
+    "Y2Vzc0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQAAAAEAAAAGAAAACgAH" +
+    "DgAMAAcOAQgPAAAAAAEAAQABAAAAcAIAAAQAAABwEAMAAAAOAAMAAQACAAAAdQIAAAkAAABiAAAA" +
+    "GwEBAAAAbiACABAADgAAAAAAAQEAgIAEgAUBAZgFAAACAgETGAECAwIOBAgPFwsAAgAAAMwCAADS" +
+    "AgAA3AIAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAFAAAAHAAAAACAAAACQAAAMAA" +
+    "AAADAAAAAgAAAOQAAAAEAAAAAQAAAPwAAAAFAAAABAAAAAQBAAAGAAAAAQAAACQBAAACIAAAFAAA" +
+    "AEQBAAABEAAAAQAAAGgCAAADIAAAAgAAAHACAAABIAAAAgAAAIACAAAAIAAAAQAAALwCAAAEIAAA" +
+    "AgAAAMwCAAADEAAAAQAAANwCAAAGIAAAAQAAAOgCAAAAEAAAAQAAAPgCAAA=");
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform3 {
+   *   public void sayHi() {
+   *    System.out.println("hello3");
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES_3_INITIAL = Base64.getDecoder().decode(
+    "ZGV4CjAzNQC2W2fBsAeLNAwWYlG8FVigzfsV7nBWITzQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
+    "AABqAQAAeAEAAI8BAACjAQAAtwEAAMsBAADcAQAA3wEAAOMBAAD3AQAA/wEAAAQCAAANAgAAAQAA" +
+    "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAAB8CAAAA" +
+    "AAAAAQABAAEAAAAUAgAABAAAAHAQAwAAAA4AAwABAAIAAAAZAgAACQAAAGIAAAAbAQoAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgAMTFRyYW5zZm9ybTM7ABVMamF2YS9pby9QcmludFN0cmVhbTsA" +
+    "EkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3Rl" +
+    "bTsAD1RyYW5zZm9ybTMuamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4zMAAGaGVsbG8zAANv" +
+    "dXQAB3ByaW50bG4ABXNheUhpAAIABw4ABAAHDocAAAABAQCAgASgAgEBuAIAAAANAAAAAAAAAAEA" +
+    "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
+    "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
+    "AyAAAAIAAAAUAgAAACAAAAEAAAAfAgAAABAAAAEAAAAwAgAA");
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform3 {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye3");
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES_3_FINAL = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBAXE5GthgMydaFBuinf+ZBfXcBYIw2UlXQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
+    "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" +
+    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" +
+    "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTMADExUcmFuc2Zvcm0zOwAVTGphdmEvaW8vUHJp" +
+    "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" +
+    "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0zLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" +
+    "A291dAAHcHJpbnRsbgAFc2F5SGkAAgAHDgAEAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" +
+    "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
+    "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
+    "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA");
+
+  public static void run() throws Exception {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_RETRANSFORM);
+    doTest();
+  }
+
+  private static void assertSame(Object a, Object b) throws Exception {
+    if (a != b) {
+      throw new AssertionError("'" + (a != null ? a.toString() : "null") + "' is not the same as " +
+                               "'" + (b != null ? b.toString() : "null") + "'");
+    }
+  }
+
+  private static Object getObjectField(Object o, String name) throws Exception {
+    return getObjectField(o, o.getClass(), name);
+  }
+
+  private static Object getObjectField(Object o, Class<?> type, String name) throws Exception {
+    Field f = type.getDeclaredField(name);
+    f.setAccessible(true);
+    return f.get(o);
+  }
+
+  private static Object getOriginalDexFile(Class<?> k) throws Exception {
+    ClassExt ext_data_object = (ClassExt) getObjectField(k, "extData");
+    if (ext_data_object == null) {
+      return null;
+    }
+
+    return getObjectField(ext_data_object, "originalDexFile");
+  }
+
+  public static void doTest() throws Exception {
+    // Make sure both of these are loaded prior to transformations being added so they have the same
+    // original dex files.
+    Transform t1 = new Transform();
+    Transform2 t2 = new Transform2();
+
+    assertSame(null, getOriginalDexFile(t1.getClass()));
+    assertSame(null, getOriginalDexFile(t2.getClass()));
+    assertSame(null, getOriginalDexFile(Test981.class));
+
+    Redefinition.addCommonTransformationResult("art/Test981$Transform", new byte[0], DEX_BYTES_1);
+    Redefinition.addCommonTransformationResult("art/Test981$Transform2", new byte[0], DEX_BYTES_2);
+    Redefinition.enableCommonRetransformation(true);
+    Redefinition.doCommonClassRetransformation(Transform.class, Transform2.class);
+
+    assertSame(getOriginalDexFile(t1.getClass()), getOriginalDexFile(t2.getClass()));
+    assertSame(null, getOriginalDexFile(Test981.class));
+    // Make sure that the original dex file is a DexCache object.
+    assertSame(getOriginalDexFile(t1.getClass()).getClass(), Class.forName("java.lang.DexCache"));
+
+    // Check that we end up with a byte[] if we do a direct RedefineClasses
+    Redefinition.enableCommonRetransformation(false);
+    Redefinition.doCommonClassRedefinition(Transform.class, new byte[0], DEX_BYTES_1);
+    assertSame((new byte[0]).getClass(), getOriginalDexFile(t1.getClass()).getClass());
+
+    // Check we don't have anything if we don't have any originalDexFile if the onload
+    // transformation doesn't do anything.
+    Redefinition.enableCommonRetransformation(true);
+    Class<?> transform3Class = new InMemoryDexClassLoader(
+        ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Test981.class.getClassLoader()).loadClass("Transform3");
+    assertSame(null, getOriginalDexFile(transform3Class));
+
+    // Check that we end up with a java.lang.Long pointer if we do an 'on-load' redefinition.
+    Redefinition.addCommonTransformationResult("Transform3", new byte[0], DEX_BYTES_3_FINAL);
+    Redefinition.enableCommonRetransformation(true);
+    Class<?> transform3ClassTransformed = new InMemoryDexClassLoader(
+        ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Test981.class.getClassLoader()).loadClass("Transform3");
+    assertSame(Long.class, getOriginalDexFile(transform3ClassTransformed).getClass());
+  }
+}
diff --git a/test/982-ok-no-retransform/src/Main.java b/test/982-ok-no-retransform/src/Main.java
index 33e50d7..bd73e81 100644
--- a/test/982-ok-no-retransform/src/Main.java
+++ b/test/982-ok-no-retransform/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,22 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
 public class Main {
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest(new Transform());
+  public static void main(String[] args) throws Exception {
+    art.Test982.run();
   }
-
-  public static void doTest(Transform t) {
-    t.sayHi();
-    enableCommonRetransformation(true);
-    doCommonClassRetransformation(Transform.class);
-    t.sayHi();
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRetransformation(Class<?>... target);
-  private static native void enableCommonRetransformation(boolean enable);
 }
diff --git a/test/982-ok-no-retransform/src/Transform.java b/test/982-ok-no-retransform/src/Transform.java
deleted file mode 100644
index 8e8af35..0000000
--- a/test/982-ok-no-retransform/src/Transform.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi() {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Goodbye" < "LTransform;" < "hello".
-    System.out.println("hello");
-  }
-}
diff --git a/test/982-ok-no-retransform/src/art/Redefinition.java b/test/982-ok-no-retransform/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/982-ok-no-retransform/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/982-ok-no-retransform/src/art/Test982.java b/test/982-ok-no-retransform/src/art/Test982.java
new file mode 100644
index 0000000..080d47f
--- /dev/null
+++ b/test/982-ok-no-retransform/src/art/Test982.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+public class Test982 {
+
+  static class Transform {
+    public void sayHi() {
+      System.out.println("hello");
+    }
+  }
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_RETRANSFORM);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi();
+    Redefinition.enableCommonRetransformation(true);
+    Redefinition.doCommonClassRetransformation(Transform.class);
+    t.sayHi();
+  }
+}
diff --git a/test/983-source-transform-verify/expected.txt b/test/983-source-transform-verify/expected.txt
index 0a94212..abcdf3a 100644
--- a/test/983-source-transform-verify/expected.txt
+++ b/test/983-source-transform-verify/expected.txt
@@ -1,2 +1,2 @@
-Dex file hook for Transform
+Dex file hook for art/Test983$Transform
 Dex file hook for java/lang/Object
diff --git a/test/983-source-transform-verify/src/Main.java b/test/983-source-transform-verify/src/Main.java
index ad081a2..e1d20f6 100644
--- a/test/983-source-transform-verify/src/Main.java
+++ b/test/983-source-transform-verify/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,23 +14,8 @@
  * limitations under the License.
  */
 
-import java.util.Base64;
 public class Main {
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest();
+  public static void main(String[] args) throws Exception {
+    art.Test983.run();
   }
-
-  public static void doTest() {
-    Transform abc = new Transform();
-    enableCommonRetransformation(true);
-    doCommonClassRetransformation(Transform.class);
-    doCommonClassRetransformation(Object.class);
-    enableCommonRetransformation(false);
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRetransformation(Class<?>... target);
-  private static native void enableCommonRetransformation(boolean enable);
 }
diff --git a/test/983-source-transform-verify/src/Transform.java b/test/983-source-transform-verify/src/Transform.java
deleted file mode 100644
index 8e8af35..0000000
--- a/test/983-source-transform-verify/src/Transform.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  public void sayHi() {
-    // Use lower 'h' to make sure the string will have a different string id
-    // than the transformation (the transformation code is the same except
-    // the actual printed String, which was making the test inacurately passing
-    // in JIT mode when loading the string from the dex cache, as the string ids
-    // of the two different strings were the same).
-    // We know the string ids will be different because lexicographically:
-    // "Goodbye" < "LTransform;" < "hello".
-    System.out.println("hello");
-  }
-}
diff --git a/test/983-source-transform-verify/src/art/Redefinition.java b/test/983-source-transform-verify/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/983-source-transform-verify/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/983-source-transform-verify/src/art/Test983.java b/test/983-source-transform-verify/src/art/Test983.java
new file mode 100644
index 0000000..b81e7f4
--- /dev/null
+++ b/test/983-source-transform-verify/src/art/Test983.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.Base64;
+public class Test983 {
+  static class Transform {
+    public void sayHi() {
+      System.out.println("hello");
+    }
+  }
+
+  public static void run() {
+    doTest();
+  }
+
+  public static void doTest() {
+    Transform abc = new Transform();
+    Redefinition.enableCommonRetransformation(true);
+    Redefinition.doCommonClassRetransformation(Transform.class);
+    Redefinition.doCommonClassRetransformation(Object.class);
+    Redefinition.enableCommonRetransformation(false);
+  }
+}
diff --git a/test/984-obsolete-invoke/obsolete_invoke.cc b/test/984-obsolete-invoke/obsolete_invoke.cc
index 27e36ba..ab2499a 100644
--- a/test/984-obsolete-invoke/obsolete_invoke.cc
+++ b/test/984-obsolete-invoke/obsolete_invoke.cc
@@ -17,20 +17,20 @@
 #include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "mirror/class-inl.h"
-#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "test_env.h"
 
 #include "jvmti_helper.h"
+#include "scoped_local_ref.h"
 
 namespace art {
 namespace Test984ObsoleteInvoke {
 
 static constexpr size_t kNumFrames = 30;
 
-extern "C" JNIEXPORT jobject JNICALL Java_Main_getFirstObsoleteMethod984(JNIEnv* env, jclass) {
+extern "C" JNIEXPORT jobject JNICALL Java_art_Test984_getFirstObsoleteMethod984(JNIEnv* env,
+                                                                                jclass) {
   jthread cur;
   jint frame_count;
   jvmtiFrameInfo frames[kNumFrames];
diff --git a/test/984-obsolete-invoke/src/Main.java b/test/984-obsolete-invoke/src/Main.java
index 418d64d..04a368dc 100644
--- a/test/984-obsolete-invoke/src/Main.java
+++ b/test/984-obsolete-invoke/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,96 +14,8 @@
  * limitations under the License.
  */
 
-import java.lang.reflect.Method;
-import java.util.Base64;
-
 public class Main {
-  // class Transform {
-  //   public static void sayHi(Runnable r) {
-  //     System.out.println("Hello - Transformed");
-  //     r.run();
-  //     System.out.println("Goodbye - Transformed");
-  //   }
-  // }
-  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
-    "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
-    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
-    "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
-    "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
-    "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
-    "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
-    "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
-    "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQAJAA0ADgABAAsAAAA7AAIA" +
-    "AQAAABeyAAISA7YABCq5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
-    "AQAPAAAAAgAQ");
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQCMekj2NPwzrEp/v+2yzzSg8xZvBtU1bC1QAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
-    "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
-    "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
-    "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
-    "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
-    "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
-    "AwAAAA4AAwABAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAgBiAAAAGwEBAAAAbiAC" +
-    "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
-    "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
-    "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
-    "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
-    "LjMxAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAIAAICABMQCAQnc" +
-    "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
-    "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
-    "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
-
-  public static void main(String[] args) {
-    art.Main.bindAgentJNIForClass(Main.class);
-    doTest();
+  public static void main(String[] args) throws Exception {
+    art.Test984.run();
   }
-
-  // The Method that holds an obsolete method pointer. We will fill it in by getting a jmethodID
-  // from a stack with an obsolete method in it. There should be no other ways to obtain an obsolete
-  // jmethodID in ART without unsafe casts.
-  public static Method obsolete_method = null;
-
-  public static void doTest() {
-    // Capture the obsolete method.
-    //
-    // NB The obsolete method must be direct so that we will not look in the receiver type to get
-    // the actual method.
-    Transform.sayHi(() -> {
-      System.out.println("transforming calling function");
-      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
-      System.out.println("Retrieving obsolete method from current stack");
-      // This should get the obsolete sayHi method (as the only obsolete method on the current
-      // threads stack).
-      Main.obsolete_method = getFirstObsoleteMethod984();
-    });
-
-    // Prove we did actually redefine something.
-    System.out.println("Invoking redefined version of method.");
-    Transform.sayHi(() -> { System.out.println("Not doing anything here"); });
-
-    System.out.println("invoking obsolete method");
-    try {
-      obsolete_method.invoke(null, (Runnable)() -> {
-        throw new Error("Unexpected code running from invoke of obsolete method!");
-      });
-      throw new Error("Running obsolete method did not throw exception");
-    } catch (Throwable e) {
-      if (e instanceof InternalError || e.getCause() instanceof InternalError) {
-        System.out.println("Caught expected error from attempting to invoke an obsolete method.");
-      } else {
-        System.out.println("Unexpected error type for calling obsolete method! Expected either "
-            + "an InternalError or something that is caused by an InternalError.");
-        throw new Error("Unexpected error caught: ", e);
-      }
-    }
-  }
-
-  // Transforms the class
-  private static native void doCommonClassRedefinition(Class<?> target,
-                                                       byte[] classfile,
-                                                       byte[] dexfile);
-
-  // Gets the first obsolete method on the current threads stack (NB only looks through the first 30
-  // stack frames).
-  private static native Method getFirstObsoleteMethod984();
 }
diff --git a/test/984-obsolete-invoke/src/Transform.java b/test/984-obsolete-invoke/src/Transform.java
deleted file mode 100644
index 536de84..0000000
--- a/test/984-obsolete-invoke/src/Transform.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-class Transform {
-  // This method must be 'static' so that when we try to invoke it through a j.l.r.Method we will
-  // simply use the jmethodID directly and not do any lookup in any receiver object.
-  public static void sayHi(Runnable r) {
-    System.out.println("hello");
-    r.run();
-    System.out.println("goodbye");
-  }
-}
diff --git a/test/984-obsolete-invoke/src/art/Redefinition.java b/test/984-obsolete-invoke/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/984-obsolete-invoke/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/984-obsolete-invoke/src/art/Test984.java b/test/984-obsolete-invoke/src/art/Test984.java
new file mode 100644
index 0000000..3fe66f6
--- /dev/null
+++ b/test/984-obsolete-invoke/src/art/Test984.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Test984 {
+
+  static class Transform {
+    // This method must be 'static' so that when we try to invoke it through a j.l.r.Method we will
+    // simply use the jmethodID directly and not do any lookup in any receiver object.
+    public static void sayHi(Runnable r) {
+      System.out.println("hello");
+      r.run();
+      System.out.println("goodbye");
+    }
+  }
+  // static class Transform {
+  //   public static void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     r.run();
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAKAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAbBwAeAQAGPGluaXQ+AQADKClW" +
+    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+    "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk4NC5qYXZhDAAJAAoHAB8MACAAIQEAE0hlbGxvIC0gVHJh" +
+    "bnNmb3JtZWQHACIMACMAJAcAJQwAJgAKAQAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkBwAnAQAVYXJ0" +
+    "L1Rlc3Q5ODQkVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5n" +
+    "L09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsB" +
+    "ABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEA" +
+    "EmphdmEvbGFuZy9SdW5uYWJsZQEAA3J1bgEAC2FydC9UZXN0OTg0ACAABwAIAAAAAAACAAAACQAK" +
+    "AAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAABQAJAA0ADgABAAsAAAA7AAIAAQAA" +
+    "ABeyAAISA7YABCq5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAcACAAIAA4ACQAWAAoAAgAP" +
+    "AAAAAgAQAB0AAAAKAAEABwAaABwACA==");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQB/mxSMAAAAAAAAAAAAAAAAAAAAAAAAAAA8BAAAcAAAAHhWNBIAAAAAAAAAAHgDAAAX" +
+    "AAAAcAAAAAoAAADMAAAAAwAAAPQAAAABAAAAGAEAAAUAAAAgAQAAAQAAAEgBAADUAgAAaAEAAGgB" +
+    "AABwAQAAhwEAAJwBAAC1AQAAxAEAAOgBAAAIAgAAHwIAADMCAABJAgAAXQIAAHECAAB/AgAAigIA" +
+    "AI0CAACRAgAAngIAAKQCAACpAgAAsgIAALcCAAC+AgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+    "CQAAAAoAAAALAAAADgAAAA4AAAAJAAAAAAAAAA8AAAAJAAAAyAIAAA8AAAAJAAAA0AIAAAgABAAS" +
+    "AAAAAAAAAAAAAAAAAAEAFQAAAAQAAgATAAAABQAAAAAAAAAGAAAAFAAAAAAAAAAAAAAABQAAAAAA" +
+    "AAAMAAAAaAMAADwDAAAAAAAABjxpbml0PgAVR29vZGJ5ZSAtIFRyYW5zZm9ybWVkABNIZWxsbyAt" +
+    "IFRyYW5zZm9ybWVkABdMYXJ0L1Rlc3Q5ODQkVHJhbnNmb3JtOwANTGFydC9UZXN0OTg0OwAiTGRh" +
+    "bHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVy" +
+    "Q2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAUTGphdmEv" +
+    "bGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxU" +
+    "ZXN0OTg0LmphdmEACVRyYW5zZm9ybQABVgACVkwAC2FjY2Vzc0ZsYWdzAARuYW1lAANvdXQAB3By" +
+    "aW50bG4AA3J1bgAFc2F5SGkABXZhbHVlAAAAAAEAAAAGAAAAAQAAAAcAAAAFAAcOAAcBAAcOAQgP" +
+    "AQMPAQgPAAEAAQABAAAA2AIAAAQAAABwEAMAAAAOAAMAAQACAAAA3QIAABQAAABiAAAAGwECAAAA" +
+    "biACABAAchAEAAIAYgAAABsBAQAAAG4gAgAQAA4AAAACAACAgATsBQEJhAYAAAICARYYAQIDAhAE" +
+    "CBEXDQACAAAATAMAAFIDAABcAwAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAXAAAA" +
+    "cAAAAAIAAAAKAAAAzAAAAAMAAAADAAAA9AAAAAQAAAABAAAAGAEAAAUAAAAFAAAAIAEAAAYAAAAB" +
+    "AAAASAEAAAIgAAAXAAAAaAEAAAEQAAACAAAAyAIAAAMgAAACAAAA2AIAAAEgAAACAAAA7AIAAAAg" +
+    "AAABAAAAPAMAAAQgAAACAAAATAMAAAMQAAABAAAAXAMAAAYgAAABAAAAaAMAAAAQAAABAAAAeAMA" +
+    "AA==");
+
+  public static void run() {
+    art.Main.bindAgentJNIForClass(Test984.class);
+    doTest();
+  }
+
+  // The Method that holds an obsolete method pointer. We will fill it in by getting a jmethodID
+  // from a stack with an obsolete method in it. There should be no other ways to obtain an obsolete
+  // jmethodID in ART without unsafe casts.
+  public static Method obsolete_method = null;
+
+  public static void doTest() {
+    // Capture the obsolete method.
+    //
+    // NB The obsolete method must be direct so that we will not look in the receiver type to get
+    // the actual method.
+    Transform.sayHi(() -> {
+      System.out.println("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+      System.out.println("Retrieving obsolete method from current stack");
+      // This should get the obsolete sayHi method (as the only obsolete method on the current
+      // threads stack).
+      Test984.obsolete_method = getFirstObsoleteMethod984();
+    });
+
+    // Prove we did actually redefine something.
+    System.out.println("Invoking redefined version of method.");
+    Transform.sayHi(() -> { System.out.println("Not doing anything here"); });
+
+    System.out.println("invoking obsolete method");
+    try {
+      obsolete_method.invoke(null, (Runnable)() -> {
+        throw new Error("Unexpected code running from invoke of obsolete method!");
+      });
+      throw new Error("Running obsolete method did not throw exception");
+    } catch (Throwable e) {
+      if (e instanceof InternalError || e.getCause() instanceof InternalError) {
+        System.out.println("Caught expected error from attempting to invoke an obsolete method.");
+      } else {
+        System.out.println("Unexpected error type for calling obsolete method! Expected either "
+            + "an InternalError or something that is caused by an InternalError.");
+        throw new Error("Unexpected error caught: ", e);
+      }
+    }
+  }
+
+  // Gets the first obsolete method on the current threads stack (NB only looks through the first 30
+  // stack frames).
+  private static native Method getFirstObsoleteMethod984();
+}
diff --git a/test/985-re-obsolete/expected.txt b/test/985-re-obsolete/expected.txt
new file mode 100644
index 0000000..5159a00
--- /dev/null
+++ b/test/985-re-obsolete/expected.txt
@@ -0,0 +1,35 @@
+Pre Start private method call
+hello - private
+Post Start private method call
+Not doing anything here
+Pre Finish private method call
+goodbye - private
+Post Finish private method call
+Pre Start private method call
+hello - private
+Post Start private method call
+transforming calling function
+Pre Finish private method call
+Goodbye - private - Transformed
+Post Finish private method call
+Pre Start private method call - Transformed
+Hello - private - Transformed
+Post Start private method call - Transformed
+Not doing anything here
+Pre Finish private method call - Transformed
+Goodbye - private - Transformed
+Post Finish private method call - Transformed
+Pre Start private method call - Transformed
+Hello - private - Transformed
+Post Start private method call - Transformed
+transforming calling function
+Pre Finish private method call - Transformed
+second - Goodbye - private - Transformed
+Post Finish private method call - Transformed
+second - Pre Start private method call - Transformed
+second - Hello - private - Transformed
+second - Post Start private method call - Transformed
+Not doing anything here
+second - Pre Finish private method call - Transformed
+second - Goodbye - private - Transformed
+second - Post Finish private method call - Transformed
diff --git a/test/985-re-obsolete/info.txt b/test/985-re-obsolete/info.txt
new file mode 100644
index 0000000..c8eafdc
--- /dev/null
+++ b/test/985-re-obsolete/info.txt
@@ -0,0 +1,4 @@
+Tests basic obsolete method support
+
+Regression test for b/37475600 which was caused by incorrectly checking for
+differences in the obsolete methods map.
diff --git a/test/985-re-obsolete/run b/test/985-re-obsolete/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/985-re-obsolete/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+./default-run "$@" --jvmti
diff --git a/test/944-transform-classloaders/src/Transform2.java b/test/985-re-obsolete/src/Main.java
similarity index 77%
rename from test/944-transform-classloaders/src/Transform2.java
rename to test/985-re-obsolete/src/Main.java
index eb22842..d78d591 100644
--- a/test/944-transform-classloaders/src/Transform2.java
+++ b/test/985-re-obsolete/src/Main.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-class Transform2 {
-  public void sayHi() {
-    System.out.println("hello2");
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test985.run();
   }
 }
diff --git a/test/985-re-obsolete/src/art/Main.java b/test/985-re-obsolete/src/art/Main.java
new file mode 100644
index 0000000..8b01920
--- /dev/null
+++ b/test/985-re-obsolete/src/art/Main.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+// Binder class so the agent's C code has something that can be bound and exposed to tests.
+// In a package to separate cleanly and work around CTS reference issues (though this class
+// should be replaced in the CTS version).
+public class Main {
+  // Load the given class with the given classloader, and bind all native methods to corresponding
+  // C methods in the agent. Will abort if any of the steps fail.
+  public static native void bindAgentJNI(String className, ClassLoader classLoader);
+  // Same as above, giving the class directly.
+  public static native void bindAgentJNIForClass(Class<?> klass);
+}
diff --git a/test/985-re-obsolete/src/art/Redefinition.java b/test/985-re-obsolete/src/art/Redefinition.java
new file mode 100644
index 0000000..0350ab4
--- /dev/null
+++ b/test/985-re-obsolete/src/art/Redefinition.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  // Bind native functions.
+  static {
+    Main.bindAgentJNIForClass(Redefinition.class);
+  }
+
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/985-re-obsolete/src/art/Test985.java b/test/985-re-obsolete/src/art/Test985.java
new file mode 100644
index 0000000..405abd5
--- /dev/null
+++ b/test/985-re-obsolete/src/art/Test985.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.util.Base64;
+
+public class Test985 {
+
+  static class Transform {
+    private void Start() {
+      System.out.println("hello - private");
+    }
+
+    private void Finish() {
+      System.out.println("goodbye - private");
+    }
+
+    public void sayHi(Runnable r) {
+      System.out.println("Pre Start private method call");
+      Start();
+      System.out.println("Post Start private method call");
+      r.run();
+      System.out.println("Pre Finish private method call");
+      Finish();
+      System.out.println("Post Finish private method call");
+    }
+  }
+
+  // static class Transform {
+  //   private void Start() {
+  //     System.out.println("Hello - private - Transformed");
+  //   }
+  //
+  //   private void Finish() {
+  //     System.out.println("Goodbye - private - Transformed");
+  //   }
+  //
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("Pre Start private method call - Transformed");
+  //     Start();
+  //     System.out.println("Post Start private method call - Transformed");
+  //     r.run();
+  //     System.out.println("Pre Finish private method call - Transformed");
+  //     Finish();
+  //     System.out.println("Post Finish private method call - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES_1 = Base64.getDecoder().decode(
+    "yv66vgAAADQANgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
+    "JwcAKQcALAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
+    "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAM" +
+    "VGVzdDk4NS5qYXZhDAAPABAHAC0MAC4ALwEAHUhlbGxvIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVk" +
+    "BwAwDAAxADIBAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAQArUHJlIFN0YXJ0IHBy" +
+    "aXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwAEwAQAQAsUG9zdCBTdGFydCBwcml2YXRl" +
+    "IG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHADMMADQAEAEALFByZSBGaW5pc2ggcHJpdmF0ZSBt" +
+    "ZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAUABABAC1Qb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhv" +
+    "ZCBjYWxsIC0gVHJhbnNmb3JtZWQHADUBABVhcnQvVGVzdDk4NSRUcmFuc2Zvcm0BAAlUcmFuc2Zv" +
+    "cm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEA" +
+    "A291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmlu" +
+    "dGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuAQAL" +
+    "YXJ0L1Rlc3Q5ODUAIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAABABIA" +
+    "AAAGAAEAAAAEAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAAAAYA" +
+    "CAAHAAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAkACAAKAAEA" +
+    "FQAWAAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQqtwAL" +
+    "sgACEgy2AASxAAAAAQASAAAAIgAIAAAADAAIAA0ADAAOABQADwAaABAAIgARACYAEgAuABMAAgAX" +
+    "AAAAAgAYACsAAAAKAAEADQAoACoACA==");
+  private static final byte[] DEX_BYTES_1 = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAh+CJbAAAAAAAAAAAAAAAAAAAAAAAAAADUBQAAcAAAAHhWNBIAAAAAAAAAABAFAAAd" +
+    "AAAAcAAAAAoAAADkAAAAAwAAAAwBAAABAAAAMAEAAAcAAAA4AQAAAQAAAHABAABEBAAAkAEAAJAB" +
+    "AACYAQAAoAEAAMEBAADgAQAA+QEAAAgCAAAsAgAATAIAAGMCAAB3AgAAjQIAAKECAAC1AgAA5AIA" +
+    "ABIDAABAAwAAbQMAAHQDAACCAwAAjQMAAJADAACUAwAAoQMAAKcDAACsAwAAtQMAALoDAADBAwAA" +
+    "BAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAAFAAAABQAAAAJAAAAAAAAABUAAAAJ" +
+    "AAAA0AMAABUAAAAJAAAAyAMAAAgABAAYAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAARAAAAAAABABsA" +
+    "AAAEAAIAGQAAAAUAAAAAAAAABgAAABoAAAAAAAAAAAAAAAUAAAAAAAAAEgAAAAAFAADMBAAAAAAA" +
+    "AAY8aW5pdD4ABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVsbG8g" +
+    "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAF0xhcnQvVGVzdDk4NSRUcmFuc2Zvcm07AA1MYXJ0L1Rl" +
+    "c3Q5ODU7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" +
+    "YXRpb24vSW5uZXJDbGFzczsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmpl" +
+    "Y3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5n" +
+    "L1N5c3RlbTsALVBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAs" +
+    "UG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALFByZSBGaW5pc2gg" +
+    "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACtQcmUgU3RhcnQgcHJpdmF0ZSBtZXRo" +
+    "b2QgY2FsbCAtIFRyYW5zZm9ybWVkAAVTdGFydAAMVGVzdDk4NS5qYXZhAAlUcmFuc2Zvcm0AAVYA" +
+    "AlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxuAANydW4ABXNheUhpAAV2YWx1ZQAB" +
+    "AAAABwAAAAEAAAAGAAAABAAHDgAJAAcOAQgPAAYABw4BCA8ADAEABw4BCA8BAw8BCA8BAw8BCA8B" +
+    "Aw8BCA8AAQABAAEAAADYAwAABAAAAHAQBQAAAA4AAwABAAIAAADdAwAACQAAAGIAAAAbAQIAAABu" +
+    "IAQAEAAOAAAAAwABAAIAAADlAwAACQAAAGIAAAAbAQMAAABuIAQAEAAOAAAABAACAAIAAADtAwAA" +
+    "KgAAAGIAAAAbARAAAABuIAQAEABwEAIAAgBiAAAAGwEOAAAAbiAEABAAchAGAAMAYgAAABsBDwAA" +
+    "AG4gBAAQAHAQAQACAGIAAAAbAQ0AAABuIAQAEAAOAAAAAwEAgIAEiAgBAqAIAQLECAMB6AgAAAIC" +
+    "ARwYAQIDAhYECBcXEwACAAAA5AQAAOoEAAD0BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAA" +
+    "AAEAAAAdAAAAcAAAAAIAAAAKAAAA5AAAAAMAAAADAAAADAEAAAQAAAABAAAAMAEAAAUAAAAHAAAA" +
+    "OAEAAAYAAAABAAAAcAEAAAIgAAAdAAAAkAEAAAEQAAACAAAAyAMAAAMgAAAEAAAA2AMAAAEgAAAE" +
+    "AAAACAQAAAAgAAABAAAAzAQAAAQgAAACAAAA5AQAAAMQAAABAAAA9AQAAAYgAAABAAAAAAUAAAAQ" +
+    "AAABAAAAEAUAAA==");
+
+  // static class Transform {
+  //   private void Start() {
+  //     System.out.println("second - Hello - private - Transformed");
+  //   }
+  //
+  //   private void Finish() {
+  //     System.out.println("second - Goodbye - private - Transformed");
+  //   }
+  //
+  //   public void sayHi(Runnable r) {
+  //     System.out.println("second - Pre Start private method call - Transformed");
+  //     Start();
+  //     System.out.println("second - Post Start private method call - Transformed");
+  //     r.run();
+  //     System.out.println("second - Pre Finish private method call - Transformed");
+  //     Finish();
+  //     System.out.println("second - Post Finish private method call - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES_2 = Base64.getDecoder().decode(
+    "yv66vgAAADQANgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
+    "JwcAKQcALAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
+    "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAM" +
+    "VGVzdDk4NS5qYXZhDAAPABAHAC0MAC4ALwEAJnNlY29uZCAtIEhlbGxvIC0gcHJpdmF0ZSAtIFRy" +
+    "YW5zZm9ybWVkBwAwDAAxADIBAChzZWNvbmQgLSBHb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9y" +
+    "bWVkAQA0c2Vjb25kIC0gUHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1l" +
+    "ZAwAEwAQAQA1c2Vjb25kIC0gUG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNm" +
+    "b3JtZWQHADMMADQAEAEANXNlY29uZCAtIFByZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAt" +
+    "IFRyYW5zZm9ybWVkDAAUABABADZzZWNvbmQgLSBQb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhvZCBj" +
+    "YWxsIC0gVHJhbnNmb3JtZWQHADUBABVhcnQvVGVzdDk4NSRUcmFuc2Zvcm0BAAlUcmFuc2Zvcm0B" +
+    "AAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291" +
+    "dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxu" +
+    "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuAQALYXJ0" +
+    "L1Rlc3Q5ODUAIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAABABIAAAAG" +
+    "AAEAAAAEAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAAAAYACAAH" +
+    "AAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAkACAAKAAEAFQAW" +
+    "AAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQqtwALsgAC" +
+    "Egy2AASxAAAAAQASAAAAIgAIAAAADAAIAA0ADAAOABQADwAaABAAIgARACYAEgAuABMAAgAXAAAA" +
+    "AgAYACsAAAAKAAEADQAoACoACA==");
+  private static final byte[] DEX_BYTES_2 = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBw/x+UAAAAAAAAAAAAAAAAAAAAAAAAAAAMBgAAcAAAAHhWNBIAAAAAAAAAAEgFAAAd" +
+    "AAAAcAAAAAoAAADkAAAAAwAAAAwBAAABAAAAMAEAAAcAAAA4AQAAAQAAAHABAAB8BAAAkAEAAJAB" +
+    "AACYAQAAoAEAALkBAADIAQAA7AEAAAwCAAAjAgAANwIAAE0CAABhAgAAdQIAAHwCAACKAgAAlQIA" +
+    "AJgCAACcAgAAqQIAAK8CAAC0AgAAvQIAAMICAADJAgAA8wIAABsDAABTAwAAigMAAMEDAAD3AwAA" +
+    "AgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAADgAAAA4AAAAJAAAAAAAAAA8AAAAJ" +
+    "AAAACAQAAA8AAAAJAAAAAAQAAAgABAASAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAALAAAAAAABABUA" +
+    "AAAEAAIAEwAAAAUAAAAAAAAABgAAABQAAAAAAAAAAAAAAAUAAAAAAAAADAAAADgFAAAEBQAAAAAA" +
+    "AAY8aW5pdD4ABkZpbmlzaAAXTGFydC9UZXN0OTg1JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk4NTsA" +
+    "IkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9J" +
+    "bm5lckNsYXNzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAFExq" +
+    "YXZhL2xhbmcvUnVubmFibGU7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+    "OwAFU3RhcnQADFRlc3Q5ODUuamF2YQAJVHJhbnNmb3JtAAFWAAJWTAALYWNjZXNzRmxhZ3MABG5h" +
+    "bWUAA291dAAHcHJpbnRsbgADcnVuAAVzYXlIaQAoc2Vjb25kIC0gR29vZGJ5ZSAtIHByaXZhdGUg" +
+    "LSBUcmFuc2Zvcm1lZAAmc2Vjb25kIC0gSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQANnNl" +
+    "Y29uZCAtIFBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAA1c2Vj" +
+    "b25kIC0gUG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQANXNlY29u" +
+    "ZCAtIFByZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkADRzZWNvbmQg" +
+    "LSBQcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkAAV2YWx1ZQAAAAEA" +
+    "AAAHAAAAAQAAAAYAAAAEAAcOAAkABw4BCA8ABgAHDgEIDwAMAQAHDgEIDwEDDwEIDwEDDwEIDwED" +
+    "DwEIDwABAAEAAQAAABAEAAAEAAAAcBAFAAAADgADAAEAAgAAABUEAAAJAAAAYgAAABsBFgAAAG4g" +
+    "BAAQAA4AAAADAAEAAgAAAB0EAAAJAAAAYgAAABsBFwAAAG4gBAAQAA4AAAAEAAIAAgAAACUEAAAq" +
+    "AAAAYgAAABsBGwAAAG4gBAAQAHAQAgACAGIAAAAbARkAAABuIAQAEAByEAYAAwBiAAAAGwEaAAAA" +
+    "biAEABAAcBABAAIAYgAAABsBGAAAAG4gBAAQAA4AAAADAQCAgATACAEC2AgBAvwIAwGgCQAAAgIB" +
+    "HBgBAgMCEAQIERcNAAIAAAAcBQAAIgUAACwFAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAA" +
+    "AQAAAB0AAABwAAAAAgAAAAoAAADkAAAAAwAAAAMAAAAMAQAABAAAAAEAAAAwAQAABQAAAAcAAAA4" +
+    "AQAABgAAAAEAAABwAQAAAiAAAB0AAACQAQAAARAAAAIAAAAABAAAAyAAAAQAAAAQBAAAASAAAAQA" +
+    "AABABAAAACAAAAEAAAAEBQAABCAAAAIAAAAcBQAAAxAAAAEAAAAsBQAABiAAAAEAAAA4BQAAABAA" +
+    "AAEAAABIBQAA");
+
+  public static void run() {
+    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    t.sayHi(() -> {
+      System.out.println("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES_1, DEX_BYTES_1);
+    });
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+    t.sayHi(() -> {
+      System.out.println("transforming calling function");
+      Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES_2, DEX_BYTES_2);
+    });
+    t.sayHi(() -> { System.out.println("Not doing anything here"); });
+  }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 9a8e174..c5d96da 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -250,6 +250,7 @@
         "ti-agent/jni_binder.cc",
         "ti-agent/jvmti_helper.cc",
         "ti-agent/test_env.cc",
+        "ti-agent/common_helper.cc",
         // This is the list of non-special OnLoad things and excludes BCI and anything that depends
         // on ART internals.
         "903-hello-tagging/tagging.cc",
@@ -260,6 +261,7 @@
         "908-gc-start-finish/gc_callbacks.cc",
         "910-methods/methods.cc",
         "911-get-stack-trace/stack_trace.cc",
+        "913-heaps/heaps.cc",
         "918-fields/fields.cc",
         "920-objects/objects.cc",
         "922-properties/properties.cc",
@@ -271,6 +273,8 @@
         "929-search/search.cc",
         "931-agent-thread/agent_thread.cc",
         "933-misc-events/misc_events.cc",
+        "945-obsolete-native/obsolete_native.cc",
+        "984-obsolete-invoke/obsolete_invoke.cc",
     ],
     shared_libs: [
         "libbase",
@@ -286,20 +290,14 @@
         // This is to get the IsInterpreted native method.
         "common/stack_inspect.cc",
         "common/runtime_state.cc",
+        "ti-agent/common_load.cc",
         // This includes the remaining test functions. We should try to refactor things to
         // make this list smaller.
-        "ti-agent/common_helper.cc",
-        "ti-agent/common_load.cc",
         "901-hello-ti-agent/basics.cc",
         "909-attach-agent/attach.cc",
         "912-classes/classes.cc",
-        "913-heaps/heaps.cc",
         "936-search-onload/search_onload.cc",
-        "944-transform-classloaders/classloader.cc",
-        "945-obsolete-native/obsolete_native.cc",
-        "980-redefine-object/redefine_object.cc",
         "983-source-transform-verify/source_transform.cc",
-        "984-obsolete-invoke/obsolete_invoke.cc",
     ],
 }
 
@@ -318,6 +316,33 @@
     shared_libs: ["libartd"],
 }
 
+art_cc_defaults {
+    name: "libtistress-defaults",
+    defaults: ["libartagent-defaults"],
+    srcs: [
+        "ti-stress/stress.cc",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    header_libs: ["libopenjdkjvmti_headers"],
+}
+
+art_cc_test_library {
+    name: "libtistress",
+    defaults: [ "libtistress-defaults"],
+    shared_libs: ["libart"],
+}
+
+art_cc_test_library {
+    name: "libtistressd",
+    defaults: [
+        "libtistress-defaults",
+        "art_debug_defaults",
+    ],
+    shared_libs: ["libartd"],
+}
+
 art_cc_test_library {
     name: "libctstiagent",
     defaults: ["libtiagent-base-defaults"],
diff --git a/test/Android.run-test-jvmti-java-library.mk b/test/Android.run-test-jvmti-java-library.mk
index b6da92b..dcb238c 100644
--- a/test/Android.run-test-jvmti-java-library.mk
+++ b/test/Android.run-test-jvmti-java-library.mk
@@ -18,13 +18,17 @@
 
 include $(CLEAR_VARS)
 
-# Main shim classes. We use one that exposes the tagging common functionality.
-LOCAL_MAIN_SHIM := 903-hello-tagging/src/art/Main.java
-LOCAL_SRC_FILES := $(LOCAL_MAIN_SHIM)
+# shim classes. We use one that exposes the common functionality.
+LOCAL_SHIM_CLASSES := \
+  902-hello-transformation/src/art/Redefinition.java \
+  903-hello-tagging/src/art/Main.java \
+
+LOCAL_SRC_FILES := $(LOCAL_SHIM_CLASSES)
 
 # Actual test classes.
 LOCAL_SRC_FILES += \
   901-hello-ti-agent/src/art/Test901.java \
+  902-hello-transformation/src/art/Test902.java \
   903-hello-tagging/src/art/Test903.java \
   904-object-allocation/src/art/Test904.java \
   905-object-free/src/art/Test905.java \
@@ -42,19 +46,36 @@
     911-get-stack-trace/src/art/SameThread.java \
     911-get-stack-trace/src/art/ThreadListTraces.java \
   913-heaps/src/art/Test913.java \
+  914-hello-obsolescence/src/art/Test914.java \
+  915-obsolete-2/src/art/Test915.java \
+  917-fields-transformation/src/art/Test917.java \
   918-fields/src/art/Test918.java \
+  919-obsolete-fields/src/art/Test919.java \
   920-objects/src/art/Test920.java \
   922-properties/src/art/Test922.java \
   923-monitors/src/art/Test923.java \
   924-threads/src/art/Test924.java \
   925-threadgroups/src/art/Test925.java \
+  926-multi-obsolescence/src/art/Test926.java \
   927-timers/src/art/Test927.java \
   928-jni-table/src/art/Test928.java \
+  930-hello-retransform/src/art/Test930.java \
   931-agent-thread/src/art/Test931.java \
+  932-transform-saves/src/art/Test932.java \
   933-misc-events/src/art/Test933.java \
+  940-recursive-obsolete/src/art/Test940.java \
+  942-private-recursive/src/art/Test942.java \
+  944-transform-classloaders/src/art/Test944.java \
+  945-obsolete-native/src/art/Test945.java \
+  947-reflect-method/src/art/Test947.java \
+  951-threaded-obsolete/src/art/Test951.java \
+  981-dedup-original-dex/src/art/Test981.java \
+  982-ok-no-retransform/src/art/Test982.java \
+  984-obsolete-invoke/src/art/Test984.java \
 
 JVMTI_RUN_TEST_GENERATED_NUMBERS := \
   901 \
+  902 \
   903 \
   904 \
   905 \
@@ -64,16 +85,32 @@
   910 \
   911 \
   913 \
+  914 \
+  915 \
+  917 \
   918 \
+  919 \
   920 \
   922 \
   923 \
   924 \
   925 \
+  926 \
   927 \
   928 \
+  930 \
   931 \
+  932 \
   933 \
+  940 \
+  942 \
+  944 \
+  945 \
+  947 \
+  951 \
+  981 \
+  982 \
+  984 \
 
 # Try to enforce that the directories correspond to the Java files we pull in.
 JVMTI_RUN_TEST_DIR_CHECK := $(sort $(foreach DIR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS), \
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ece5762..4415b2c 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -114,6 +114,14 @@
 TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtiagentd)
 endif
 
+# Also need libtistress.
+TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtistress)
+TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtistressd)
+ifdef TARGET_2ND_ARCH
+TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtistress)
+TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtistressd)
+endif
+
 # Also need libarttest.
 TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libarttest)
 TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libarttestd)
@@ -145,6 +153,8 @@
   $(HOST_OUT_EXECUTABLES)/hprof-conv \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagent) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagentd) \
+  $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtistress) \
+  $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtistressd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libartagent) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libartagentd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libarttest) \
@@ -160,6 +170,8 @@
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libtiagent) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libtiagentd) \
+  $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libtistress) \
+  $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libtistressd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libartagent) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libartagentd) \
   $(OUT_DIR)/$(ART_TEST_LIST_host_$(2ND_ART_HOST_ARCH)_libarttest) \
diff --git a/test/etc/default-build b/test/etc/default-build
index d74b24d..744c38b 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -91,7 +91,7 @@
 JAVAC_EXPERIMENTAL_ARGS["lambdas"]="-source 1.8 -target 1.8"
 JAVAC_EXPERIMENTAL_ARGS["method-handles"]="-source 1.8 -target 1.8"
 # We need to leave javac at default 1.7 so that dx will continue to work
-JAVAC_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-source 1.7 -target 1.7"
+JAVAC_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-source 1.8 -target 1.8"
 JAVAC_EXPERIMENTAL_ARGS["agents"]="-source 1.8 -target 1.8"
 
 while true; do
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index f1b6132..56cfd24 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -62,6 +62,7 @@
 TEST_VDEX="n"
 TEST_IS_NDEBUG="n"
 APP_IMAGE="y"
+JVMTI_STRESS="n"
 VDEX_FILTER=""
 PROFILE="n"
 RANDOM_PROFILE="n"
@@ -145,6 +146,11 @@
     elif [ "x$1" = "x--prebuild" ]; then
         PREBUILD="y"
         shift
+    elif [ "x$1" = "x--jvmti-stress" ]; then
+        # APP_IMAGE doesn't really work with jvmti-torture
+        APP_IMAGE="n"
+        JVMTI_STRESS="y"
+        shift
     elif [ "x$1" = "x--no-app-image" ]; then
         APP_IMAGE="n"
         shift
@@ -365,6 +371,28 @@
   fi
 fi
 
+if [[ "$JVMTI_STRESS" = "y" ]]; then
+  if [[ "$USE_JVM" = "n" ]]; then
+    plugin=libopenjdkjvmtid.so
+    agent=libtistressd.so
+    if  [[ "$TEST_IS_NDEBUG" = "y" ]]; then
+      agent=libtistress.so
+      plugin=libopenjdkjvmti.so
+    fi
+
+    file_1=$(mktemp --tmpdir=${DEX_LOCATION})
+    file_2=$(mktemp --tmpdir=${DEX_LOCATION})
+    # TODO Remove need for DEXTER_BINARY!
+    FLAGS="${FLAGS} -agentpath:${agent}=${DEXTER_BINARY},${file_1},${file_2}"
+    if [ "$IS_JVMTI_TEST" = "n" ]; then
+      FLAGS="${FLAGS} -Xplugin:${plugin}"
+      FLAGS="${FLAGS} -Xcompiler-option --debuggable"
+      # Always make the compilation be debuggable.
+      COMPILE_FLAGS="${COMPILE_FLAGS} --debuggable"
+    fi
+  fi
+fi
+
 if [ "$USE_JVM" = "y" ]; then
   export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
   # Xmx is necessary since we don't pass down the ART flags to JVM.
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 7891d4c..e7343a0 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -366,5 +366,248 @@
                   "634-vdex-duplicate"],
         "description": ["Profile driven dexlayout does not work with vdex or dex verifier."],
         "variant": "speed-profile"
+    },
+    {
+        "tests": [
+            "004-checker-UnsafeTest18",
+            "127-checker-secondarydex",
+            "441-checker-inliner",
+            "442-checker-constant-folding",
+            "444-checker-nce",
+            "445-checker-licm",
+            "446-checker-inliner2",
+            "447-checker-inliner3",
+            "449-checker-bce",
+            "450-checker-types",
+            "455-checker-gvn",
+            "458-checker-instruct-simplification",
+            "462-checker-inlining-dex-files",
+            "463-checker-boolean-simplifier",
+            "464-checker-inline-sharpen-calls",
+            "465-checker-clinit-gvn",
+            "468-checker-bool-simplif-regression",
+            "473-checker-inliner-constants",
+            "474-checker-boolean-input",
+            "476-checker-ctor-memory-barrier",
+            "477-checker-bound-type",
+            "478-checker-clinit-check-pruning",
+            "478-checker-inline-noreturn",
+            "478-checker-inliner-nested-loop",
+            "480-checker-dead-blocks",
+            "482-checker-loop-back-edge-use",
+            "484-checker-register-hints",
+            "485-checker-dce-loop-update",
+            "485-checker-dce-switch",
+            "486-checker-must-do-null-check",
+            "487-checker-inline-calls",
+            "488-checker-inline-recursive-calls",
+            "490-checker-inline",
+            "492-checker-inline-invoke-interface",
+            "493-checker-inline-invoke-interface",
+            "494-checker-instanceof-tests",
+            "495-checker-checkcast-tests",
+            "496-checker-inlining-class-loader",
+            "508-checker-disassembly",
+            "510-checker-try-catch",
+            "517-checker-builder-fallthrough",
+            "521-checker-array-set-null",
+            "522-checker-regression-monitor-exit",
+            "523-checker-can-throw-regression",
+            "525-checker-arrays-fields1",
+            "525-checker-arrays-fields2",
+            "526-checker-caller-callee-regs",
+            "527-checker-array-access-split",
+            "529-checker-unresolved",
+            "530-checker-loops1",
+            "530-checker-loops2",
+            "530-checker-loops3",
+            "530-checker-loops4",
+            "530-checker-loops5",
+            "530-checker-lse",
+            "530-checker-lse2",
+            "530-checker-regression-reftyp-final",
+            "532-checker-nonnull-arrayset",
+            "534-checker-bce-deoptimization",
+            "536-checker-intrinsic-optimization",
+            "536-checker-needs-access-check",
+            "537-checker-arraycopy",
+            "537-checker-debuggable",
+            "537-checker-inline-and-unverified",
+            "537-checker-jump-over-jump",
+            "538-checker-embed-constants",
+            "540-checker-rtp-bug",
+            "543-checker-dce-trycatch",
+            "548-checker-inlining-and-dce",
+            "549-checker-types-merge",
+            "550-checker-multiply-accumulate",
+            "550-checker-regression-wide-store",
+            "551-checker-clinit",
+            "551-checker-shifter-operand",
+            "552-checker-primitive-typeprop",
+            "552-checker-sharpening",
+            "554-checker-rtp-checkcast",
+            "557-checker-instruct-simplifier-ror",
+            "557-checker-ref-equivalent",
+            "559-checker-irreducible-loop",
+            "559-checker-rtp-ifnotnull",
+            "562-checker-no-intermediate",
+            "563-checker-fakestring",
+            "563-checker-invoke-super",
+            "564-checker-bitcount",
+            "564-checker-inline-loop",
+            "564-checker-irreducible-loop",
+            "564-checker-negbitwise",
+            "565-checker-condition-liveness",
+            "565-checker-doublenegbitwise",
+            "565-checker-irreducible-loop",
+            "565-checker-rotate",
+            "566-checker-codegen-select",
+            "566-checker-signum",
+            "567-checker-compare",
+            "568-checker-onebit",
+            "569-checker-pattern-replacement",
+            "570-checker-osr",
+            "570-checker-select",
+            "572-checker-array-get-regression",
+            "573-checker-checkcast-regression",
+            "575-checker-isnan",
+            "575-checker-string-init-alias",
+            "577-checker-fp2int",
+            "580-checker-round",
+            "580-checker-string-fact-intrinsics",
+            "582-checker-bce-length",
+            "583-checker-zero",
+            "584-checker-div-bool",
+            "586-checker-null-array-get",
+            "588-checker-irreducib-lifetime-hole",
+            "590-checker-arr-set-null-regression",
+            "591-checker-regression-dead-loop",
+            "592-checker-regression-bool-input",
+            "593-checker-boolean-2-integral-conv",
+            "593-checker-long-2-float-regression",
+            "593-checker-shift-and-simplifier",
+            "594-checker-array-alias",
+            "594-checker-irreducible-linorder",
+            "596-checker-dead-phi",
+            "598-checker-irreducible-dominance",
+            "599-checker-irreducible-loop",
+            "603-checker-instanceof",
+            "608-checker-unresolved-lse",
+            "609-checker-inline-interface",
+            "609-checker-x86-bounds-check",
+            "611-checker-simplify-if",
+            "614-checker-dump-constant-location",
+            "615-checker-arm64-store-zero",
+            "618-checker-induction",
+            "619-checker-current-method",
+            "620-checker-bce-intrinsics",
+            "622-checker-bce-regressions",
+            "623-checker-loop-regressions",
+            "624-checker-stringops",
+            "625-checker-licm-regressions",
+            "626-checker-arm64-scratch-register",
+            "627-checker-unroll",
+            "631-checker-fp-abs",
+            "631-checker-get-class",
+            "632-checker-char-at-bounds",
+            "633-checker-rtp-getclass",
+            "635-checker-arm64-volatile-load-cc",
+            "637-checker-throw-inline",
+            "638-checker-inline-caches",
+            "639-checker-code-sinking",
+            "640-checker-boolean-simd",
+            "640-checker-byte-simd",
+            "640-checker-char-simd",
+            "640-checker-double-simd",
+            "640-checker-float-simd",
+            "640-checker-integer-valueof",
+            "640-checker-int-simd",
+            "640-checker-long-simd",
+            "640-checker-short-simd",
+            "641-checker-arraycopy",
+            "643-checker-bogus-ic",
+            "644-checker-deopt",
+            "645-checker-abs-simd",
+            "706-checker-scheduler"],
+        "description": ["Checker tests are not compatible with jvmti."],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "961-default-iface-resolution-gen",
+            "964-default-iface-init-gen"
+        ],
+        "description": ["Tests that just take too long with jvmti-stress"],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "950-redefine-intrinsic",
+            "951-threaded-obsolete",
+            "952-invoke-custom",
+            "953-invoke-polymorphic-compiler",
+            "954-invoke-polymorphic-verifier",
+            "955-methodhandles-smali",
+            "956-methodhandles",
+            "957-methodhandle-transforms",
+            "958-methodhandle-stackframe",
+            "959-invoke-polymorphic-accessors"
+        ],
+        "description": [
+            "Tests that use dex version 38 which is not yet supported by",
+            "dexter/slicer."
+        ],
+        "bug": "b/37272822",
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "137-cfi",
+            "595-profile-saving",
+            "900-hello-plugin",
+            "909-attach-agent",
+            "981-dedup-original-dex"
+        ],
+        "description": ["Tests that require exact knowledge of the number of plugins and agents."],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "097-duplicate-method",
+            "138-duplicate-classes-check2",
+            "804-class-extends-itself",
+            "921-hello-failure"
+        ],
+        "description": [
+            "Tests that use illegal dex files or otherwise break dexter assumptions"
+        ],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "018-stack-overflow",
+            "068-classloader",
+            "086-null-super",
+            "087-gc-after-link",
+            "626-const-class-linking",
+            "629-vdex-speed",
+            "944-transform-classloaders"
+        ],
+        "description": [
+            "Tests that use custom class loaders or other features not supported ",
+            "by our JVMTI implementation"
+        ],
+        "variant": "jvmti-stress"
+    },
+    {
+        "tests": [
+            "031-class-attributes",
+            "911-get-stack-trace"
+        ],
+        "description": [
+            "Tests that use annotations and debug data that is not kept around by dexter."
+        ],
+        "bug": "b/37239009",
+        "variant": "jvmti-stress"
     }
 ]
diff --git a/test/run-test b/test/run-test
index e46099d..f60f766 100755
--- a/test/run-test
+++ b/test/run-test
@@ -137,6 +137,7 @@
 basic_verify="false"
 gc_verify="false"
 gc_stress="false"
+jvmti_stress="false"
 strace="false"
 always_clean="no"
 never_clean="no"
@@ -233,6 +234,9 @@
         basic_verify="true"
         gc_stress="true"
         shift
+    elif [ "x$1" = "x--jvmti-stress" ]; then
+        jvmti_stress="true"
+        shift
     elif [ "x$1" = "x--suspend-timeout" ]; then
         shift
         suspend_timeout="$1"
@@ -443,6 +447,9 @@
 if [ "$gc_stress" = "true" ]; then
   run_args="${run_args} --gc-stress --runtime-option -Xgc:gcstress --runtime-option -Xms2m --runtime-option -Xmx16m"
 fi
+if [ "$jvmti_stress" = "true" ]; then
+    run_args="${run_args} --no-app-image --jvmti-stress"
+fi
 if [ "$trace" = "true" ]; then
     run_args="${run_args} --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file-size:2000000"
     if [ "$trace_stream" = "true" ]; then
@@ -651,6 +658,7 @@
         echo "    --stream              Run method tracing in streaming mode (requires --trace)"
         echo "    --gcstress            Run with gc stress testing"
         echo "    --gcverify            Run with gc verification"
+        echo "    --jvmti-stress        Run with jvmti stress testing"
         echo "    --always-clean        Delete the test files even if the test fails."
         echo "    --never-clean         Keep the test files even if the test succeeds."
         echo "    --android-root [path] The path on target for the android root. (/system by default)."
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index f5e2a61..7d9297f 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -202,6 +202,9 @@
 # Note: ART_2ND_PHONY_TEST_TARGET_SUFFIX is 2ND_ART_PHONY_TEST_TARGET_SUFFIX in .mk files
 # Note: ART_2ND_PHONY_TEST_HOST_SUFFIX is 2ND_ART_PHONY_HOST_TARGET_SUFFIX in .mk files
 # Python does not let us have variable names starting with a digit, so it has differ.
+
+ART_TEST_RUN_TEST_JVMTI_STRESS = _getEnvBoolean('ART_TEST_RUN_TEST_JVMTI_STRESS', ART_TEST_FULL)
+
 if TARGET_2ND_ARCH:
   if "64" in TARGET_ARCH:
     ART_PHONY_TEST_TARGET_SUFFIX = "64"
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 8e29b8e..6e47c5e 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -26,7 +26,7 @@
         'make' : 'test-art-host-gtest',
         'run-test' : [],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false'
+            'ART_USE_READ_BARRIER' : 'true'
         }
     },
 
@@ -45,19 +45,19 @@
     'art-interpreter' : {
         'run-test' : ['--interpreter'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false'
+            'ART_USE_READ_BARRIER' : 'true'
         }
     },
     'art-interpreter-access-checks' : {
         'run-test' : ['--interp-ac'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false'
+            'ART_USE_READ_BARRIER' : 'true'
         }
     },
     'art-jit' : {
         'run-test' : ['--jit'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false'
+            'ART_USE_READ_BARRIER' : 'true'
         }
     },
     'art-gcstress-gcverify': {
@@ -167,51 +167,51 @@
     'art-tracing' : {
         'run-test' : ['--trace'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false'
+            'ART_USE_READ_BARRIER' : 'true'
         }
     },
     'art-interpreter-tracing' : {
         'run-test' : ['--interpreter',
                       '--trace'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false',
+            'ART_USE_READ_BARRIER' : 'true',
         }
     },
     'art-forcecopy' : {
         'run-test' : ['--forcecopy'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false',
+            'ART_USE_READ_BARRIER' : 'true',
         }
     },
     'art-no-prebuild' : {
         'run-test' : ['--no-prebuild'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false',
+            'ART_USE_READ_BARRIER' : 'true',
         }
     },
     'art-no-image' : {
         'run-test' : ['--no-image'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false',
+            'ART_USE_READ_BARRIER' : 'true',
         }
     },
     'art-interpreter-no-image' : {
         'run-test' : ['--interpreter',
                       '--no-image'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false',
+            'ART_USE_READ_BARRIER' : 'true',
         }
     },
     'art-relocate-no-patchoat' : {
         'run-test' : ['--relocate-npatchoat'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false',
+            'ART_USE_READ_BARRIER' : 'true',
         }
     },
     'art-no-dex2oat' : {
         'run-test' : ['--no-dex2oat'],
         'env' : {
-            'ART_USE_READ_BARRIER' : 'false',
+            'ART_USE_READ_BARRIER' : 'true',
         }
     },
     'art-heap-poisoning' : {
@@ -222,6 +222,18 @@
             'ART_HEAP_POISONING' : 'true'
         }
     },
+    'art-preopt' : {
+        # This test configuration is intended to be representative of the case
+        # of preopted apps, which are precompiled compiled pic against an
+        # unrelocated image, then used with a relocated image.
+        'run-test' : ['--pictest',
+                      '--prebuild',
+                      '--relocate',
+                      '--jit'],
+        'env' : {
+            'ART_USE_READ_BARRIER' : 'true'
+        }
+    },
 
     # ART gtest configurations
     # (calls make 'target' which builds and then runs the gtests).
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 5d3687e..8072631 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -73,6 +73,7 @@
 DEBUGGABLE_TYPES = set()
 ADDRESS_SIZES = set()
 OPTIMIZING_COMPILER_TYPES = set()
+JVMTI_TYPES = set()
 ADDRESS_SIZES_TARGET = {'host': set(), 'target': set()}
 # timeout for individual tests.
 # TODO: make it adjustable per tests and for buildbots
@@ -146,6 +147,7 @@
   VARIANT_TYPE_DICT['relocate'] = {'relocate-npatchoat', 'relocate', 'no-relocate'}
   VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'}
   VARIANT_TYPE_DICT['address_sizes'] = {'64', '32'}
+  VARIANT_TYPE_DICT['jvmti'] = {'no-jvmti', 'jvmti-stress'}
   VARIANT_TYPE_DICT['compiler'] = {'interp-ac', 'interpreter', 'jit', 'optimizing',
                               'regalloc_gc', 'speed-profile'}
 
@@ -195,6 +197,10 @@
   if env.ART_TEST_SPEED_PROFILE:
     COMPILER_TYPES.add('speed-profile')
 
+  # By default only run without jvmti
+  if not JVMTI_TYPES:
+    JVMTI_TYPES.add('no-jvmti')
+
   # By default we run all 'compiler' variants.
   if not COMPILER_TYPES:
     COMPILER_TYPES.add('optimizing')
@@ -310,6 +316,7 @@
   total_test_count *= len(PICTEST_TYPES)
   total_test_count *= len(DEBUGGABLE_TYPES)
   total_test_count *= len(COMPILER_TYPES)
+  total_test_count *= len(JVMTI_TYPES)
   target_address_combinations = 0
   for target in TARGET_TYPES:
     for address_size in ADDRESS_SIZES_TARGET[target]:
@@ -336,10 +343,10 @@
   config = itertools.product(tests, TARGET_TYPES, RUN_TYPES, PREBUILD_TYPES,
                              COMPILER_TYPES, RELOCATE_TYPES, TRACE_TYPES,
                              GC_TYPES, JNI_TYPES, IMAGE_TYPES, PICTEST_TYPES,
-                             DEBUGGABLE_TYPES)
+                             DEBUGGABLE_TYPES, JVMTI_TYPES)
 
   for test, target, run, prebuild, compiler, relocate, trace, gc, \
-      jni, image, pictest, debuggable in config:
+      jni, image, pictest, debuggable, jvmti in config:
     for address_size in ADDRESS_SIZES_TARGET[target]:
       if stop_testrunner:
         # When ART_TEST_KEEP_GOING is set to false, then as soon as a test
@@ -361,11 +368,12 @@
       test_name += image + '-'
       test_name += pictest + '-'
       test_name += debuggable + '-'
+      test_name += jvmti + '-'
       test_name += test
       test_name += address_size
 
       variant_set = {target, run, prebuild, compiler, relocate, trace, gc, jni,
-                     image, pictest, debuggable, address_size}
+                     image, pictest, debuggable, jvmti, address_size}
 
       options_test = options_all
 
@@ -428,6 +436,9 @@
       if debuggable == 'debuggable':
         options_test += ' --debuggable'
 
+      if jvmti == 'jvmti-stress':
+        options_test += ' --jvmti-stress'
+
       if address_size == '64':
         options_test += ' --64'
 
@@ -762,6 +773,7 @@
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['image']) + ')-'
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['pictest']) + ')-'
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['debuggable']) + ')-'
+  regex += '(' + '|'.join(VARIANT_TYPE_DICT['jvmti']) + ')-'
   regex += '(' + '|'.join(RUN_TEST_SET) + ')'
   regex += '(' + '|'.join(VARIANT_TYPE_DICT['address_sizes']) + ')$'
   match = re.match(regex, test_name)
@@ -777,8 +789,9 @@
     IMAGE_TYPES.add(match.group(9))
     PICTEST_TYPES.add(match.group(10))
     DEBUGGABLE_TYPES.add(match.group(11))
-    ADDRESS_SIZES.add(match.group(13))
-    return {match.group(12)}
+    JVMTI_TYPES.add(match.group(12))
+    ADDRESS_SIZES.add(match.group(14))
+    return {match.group(13)}
   raise ValueError(test_name + " is not a valid test")
 
 
@@ -918,6 +931,10 @@
     GC_TYPES.add('cms')
   if options['multipicimage']:
     IMAGE_TYPES.add('multipicimage')
+  if options['jvmti_stress']:
+    JVMTI_TYPES.add('jvmti-stress')
+  if options['no_jvmti']:
+    JVMTI_TYPES.add('no-jvmti')
   if options['verbose']:
     verbose = True
   if options['n_thread']:
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index ab5dbcc..bfd4d25 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -17,18 +17,15 @@
 #include "common_helper.h"
 
 #include <dlfcn.h>
+#include <map>
 #include <stdio.h>
 #include <sstream>
 #include <deque>
+#include <vector>
 
 #include "android-base/stringprintf.h"
-#include "art_method.h"
 #include "jni.h"
-#include "jni_internal.h"
 #include "jvmti.h"
-#include "scoped_thread_state_change-inl.h"
-#include "stack.h"
-#include "utils.h"
 
 #include "jni_binder.h"
 #include "jvmti_helper.h"
@@ -37,6 +34,10 @@
 
 namespace art {
 
+static void SetupCommonRetransform();
+static void SetupCommonRedefine();
+static void SetupCommonTransform();
+
 template <bool is_redefine>
 static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
                                          JNIEnv* env,
@@ -108,18 +109,15 @@
 
 // Magic JNI export that classes can use for redefining classes.
 // To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
-extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRedefinition(JNIEnv* env,
-                                                                      jclass,
-                                                                      jclass target,
-                                                                      jbyteArray class_file_bytes,
-                                                                      jbyteArray dex_file_bytes) {
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRedefinition(
+    JNIEnv* env, jclass, jclass target, jbyteArray class_file_bytes, jbyteArray dex_file_bytes) {
   DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
 }
 
 // Magic JNI export that classes can use for redefining classes.
 // To use classes should declare this as a native function with signature
 // ([Ljava/lang/Class;[[B[[B)V
-extern "C" JNIEXPORT void JNICALL Java_Main_doCommonMultiClassRedefinition(
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonMultiClassRedefinition(
     JNIEnv* env,
     jclass,
     jobjectArray targets,
@@ -155,11 +153,7 @@
     printf("Unable to get jvmti env!\n");
     return 1;
   }
-  jvmtiCapabilities caps;
-  jvmti_env->GetPotentialCapabilities(&caps);
-  caps.can_retransform_classes = 0;
-  caps.can_retransform_any_class = 0;
-  jvmti_env->AddCapabilities(&caps);
+  SetupCommonRedefine();
   return 0;
 }
 
@@ -183,11 +177,8 @@
 std::map<std::string, std::deque<CommonTransformationResult>> gTransformations;
 bool gPopTransformations = true;
 
-extern "C" JNIEXPORT void JNICALL Java_Main_addCommonTransformationResult(JNIEnv* env,
-                                                                          jclass,
-                                                                          jstring class_name,
-                                                                          jbyteArray class_array,
-                                                                          jbyteArray dex_array) {
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_addCommonTransformationResult(
+    JNIEnv* env, jclass, jstring class_name, jbyteArray class_array, jbyteArray dex_array) {
   const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
   std::string name_str(name_chrs);
   env->ReleaseStringUTFChars(class_name, name_chrs);
@@ -244,15 +235,15 @@
   }
 }
 
-extern "C" JNIEXPORT void Java_Main_setPopRetransformations(JNIEnv*,
-                                                            jclass,
-                                                            jboolean enable) {
+extern "C" JNIEXPORT void Java_art_Redefinition_setPopRetransformations(JNIEnv*,
+                                                                        jclass,
+                                                                        jboolean enable) {
   gPopTransformations = enable;
 }
 
-extern "C" JNIEXPORT void Java_Main_popTransformationFor(JNIEnv* env,
-                                                         jclass,
-                                                         jstring class_name) {
+extern "C" JNIEXPORT void Java_art_Redefinition_popTransformationFor(JNIEnv* env,
+                                                                         jclass,
+                                                                         jstring class_name) {
   const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
   std::string name_str(name_chrs);
   env->ReleaseStringUTFChars(class_name, name_chrs);
@@ -267,9 +258,9 @@
   }
 }
 
-extern "C" JNIEXPORT void Java_Main_enableCommonRetransformation(JNIEnv* env,
-                                                                 jclass,
-                                                                 jboolean enable) {
+extern "C" JNIEXPORT void Java_art_Redefinition_enableCommonRetransformation(JNIEnv* env,
+                                                                                 jclass,
+                                                                                 jboolean enable) {
   jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
                                                        JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
                                                        nullptr);
@@ -298,9 +289,8 @@
   }
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRetransformation(JNIEnv* env,
-                                                                          jclass,
-                                                                          jobjectArray targets) {
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRetransformation(
+    JNIEnv* env, jclass, jobjectArray targets) {
   jvmtiCapabilities caps;
   jvmtiError caps_err = jvmti_env->GetCapabilities(&caps);
   if (caps_err != JVMTI_ERROR_NONE) {
@@ -338,14 +328,7 @@
     printf("Unable to get jvmti env!\n");
     return 1;
   }
-  SetAllCapabilities(jvmti_env);
-  jvmtiEventCallbacks cb;
-  memset(&cb, 0, sizeof(cb));
-  cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
-  if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
-    printf("Unable to set class file load hook cb!\n");
-    return 1;
-  }
+  SetupCommonRetransform();
   return 0;
 }
 
@@ -353,8 +336,6 @@
 
 namespace common_transform {
 
-using art::common_retransform::CommonClassFileLoadHookRetransformable;
-
 // Get all capabilities except those related to retransformation.
 jint OnLoad(JavaVM* vm,
             char* options ATTRIBUTE_UNUSED,
@@ -363,6 +344,35 @@
     printf("Unable to get jvmti env!\n");
     return 1;
   }
+  SetupCommonTransform();
+  return 0;
+}
+
+}  // namespace common_transform
+
+#define CONFIGURATION_COMMON_REDEFINE 0
+#define CONFIGURATION_COMMON_RETRANSFORM 1
+#define CONFIGURATION_COMMON_TRANSFORM 2
+
+static void SetupCommonRedefine() {
+  jvmtiCapabilities caps;
+  jvmti_env->GetPotentialCapabilities(&caps);
+  caps.can_retransform_classes = 0;
+  caps.can_retransform_any_class = 0;
+  jvmti_env->AddCapabilities(&caps);
+}
+
+static void SetupCommonRetransform() {
+  SetAllCapabilities(jvmti_env);
+  jvmtiEventCallbacks cb;
+  memset(&cb, 0, sizeof(cb));
+  cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
+  jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
+  CHECK_EQ(res, JVMTI_ERROR_NONE);
+  common_retransform::gTransformations.clear();
+}
+
+static void SetupCommonTransform() {
   // Don't set the retransform caps
   jvmtiCapabilities caps;
   jvmti_env->GetPotentialCapabilities(&caps);
@@ -373,14 +383,31 @@
   // Use the same callback as the retransform test.
   jvmtiEventCallbacks cb;
   memset(&cb, 0, sizeof(cb));
-  cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
-  if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
-    printf("Unable to set class file load hook cb!\n");
-    return 1;
-  }
-  return 0;
+  cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
+  jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
+  CHECK_EQ(res, JVMTI_ERROR_NONE);
+  common_retransform::gTransformations.clear();
 }
 
-}  // namespace common_transform
-
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_nativeSetTestConfiguration(JNIEnv*,
+                                                                                   jclass,
+                                                                                   jint type) {
+  switch (type) {
+    case CONFIGURATION_COMMON_REDEFINE: {
+      SetupCommonRedefine();
+      return;
+    }
+    case CONFIGURATION_COMMON_RETRANSFORM: {
+      SetupCommonRetransform();
+      return;
+    }
+    case CONFIGURATION_COMMON_TRANSFORM: {
+      SetupCommonTransform();
+      return;
+    }
+    default: {
+      LOG(FATAL) << "Unknown test configuration: " << type;
+    }
+  }
+}
 }  // namespace art
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 9e7b75d..3455409 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -60,31 +60,17 @@
 // MinimalOnLoad.
 static AgentLib agents[] = {
   { "901-hello-ti-agent", Test901HelloTi::OnLoad, nullptr },
-  { "902-hello-transformation", common_redefine::OnLoad, nullptr },
   { "909-attach-agent", nullptr, Test909AttachAgent::OnAttach },
-  { "914-hello-obsolescence", common_redefine::OnLoad, nullptr },
-  { "915-obsolete-2", common_redefine::OnLoad, nullptr },
   { "916-obsolete-jit", common_redefine::OnLoad, nullptr },
-  { "917-fields-transformation", common_redefine::OnLoad, nullptr },
-  { "919-obsolete-fields", common_redefine::OnLoad, nullptr },
   { "921-hello-failure", common_retransform::OnLoad, nullptr },
-  { "926-multi-obsolescence", common_redefine::OnLoad, nullptr },
-  { "930-hello-retransform", common_retransform::OnLoad, nullptr },
-  { "932-transform-saves", common_retransform::OnLoad, nullptr },
   { "934-load-transform", common_retransform::OnLoad, nullptr },
   { "935-non-retransformable", common_transform::OnLoad, nullptr },
   { "936-search-onload", Test936SearchOnload::OnLoad, nullptr },
   { "937-hello-retransform-package", common_retransform::OnLoad, nullptr },
   { "938-load-transform-bcp", common_retransform::OnLoad, nullptr },
   { "939-hello-transformation-bcp", common_redefine::OnLoad, nullptr },
-  { "940-recursive-obsolete", common_redefine::OnLoad, nullptr },
   { "941-recursive-obsolete-jit", common_redefine::OnLoad, nullptr },
-  { "942-private-recursive", common_redefine::OnLoad, nullptr },
   { "943-private-recursive-jit", common_redefine::OnLoad, nullptr },
-  { "944-transform-classloaders", common_redefine::OnLoad, nullptr },
-  { "945-obsolete-native", common_redefine::OnLoad, nullptr },
-  { "981-dedup-original-dex", common_retransform::OnLoad, nullptr },
-  { "982-ok-no-retransform", common_retransform::OnLoad, nullptr },
   { "983-source-transform-verify", Test983SourceTransformVerify::OnLoad, nullptr },
 };
 
diff --git a/test/ti-agent/scoped_local_ref.h b/test/ti-agent/scoped_local_ref.h
index daa1583..ba9725f 100644
--- a/test/ti-agent/scoped_local_ref.h
+++ b/test/ti-agent/scoped_local_ref.h
@@ -44,7 +44,7 @@
     }
   }
 
-  T release() __attribute__((warn_unused_result)) {
+  T release() WARN_UNUSED {
     T localRef = mLocalRef;
     mLocalRef = nullptr;
     return localRef;
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
new file mode 100644
index 0000000..fa49a35
--- /dev/null
+++ b/test/ti-stress/stress.cc
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <stdio.h>
+#include <sstream>
+
+#include "jvmti.h"
+#include "exec_utils.h"
+#include "utils.h"
+
+namespace art {
+
+// Should we do a 'full_rewrite' with this test?
+static constexpr bool kDoFullRewrite = true;
+
+struct StressData {
+  std::string dexter_cmd;
+  std::string out_temp_dex;
+  std::string in_temp_dex;
+  bool vm_class_loader_initialized;
+};
+
+static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
+  std::ofstream file(fname, std::ios::binary | std::ios::out | std::ios::trunc);
+  file.write(reinterpret_cast<const char*>(data), data_len);
+  file.flush();
+}
+
+static bool ReadIntoBuffer(const std::string& fname, /*out*/std::vector<unsigned char>* data) {
+  std::ifstream file(fname, std::ios::binary | std::ios::in);
+  file.seekg(0, std::ios::end);
+  size_t len = file.tellg();
+  data->resize(len);
+  file.seekg(0);
+  file.read(reinterpret_cast<char*>(data->data()), len);
+  return len != 0;
+}
+
+// TODO rewrite later.
+static bool DoExtractClassFromData(StressData* data,
+                                   const std::string& class_name,
+                                   jint in_len,
+                                   const unsigned char* in_data,
+                                   /*out*/std::vector<unsigned char>* dex) {
+  // Write the dex file into a temporary file.
+  WriteToFile(data->in_temp_dex, in_len, in_data);
+  // Clear out file so even if something suppresses the exit value we will still detect dexter
+  // failure.
+  WriteToFile(data->out_temp_dex, 0, nullptr);
+  // Have dexter do the extraction.
+  std::vector<std::string> args;
+  args.push_back(data->dexter_cmd);
+  if (kDoFullRewrite) {
+    args.push_back("-x");
+    args.push_back("full_rewrite");
+  }
+  args.push_back("-e");
+  args.push_back(class_name);
+  args.push_back("-o");
+  args.push_back(data->out_temp_dex);
+  args.push_back(data->in_temp_dex);
+  std::string error;
+  if (ExecAndReturnCode(args, &error) != 0) {
+    LOG(ERROR) << "unable to execute dexter: " << error;
+    return false;
+  }
+  return ReadIntoBuffer(data->out_temp_dex, dex);
+}
+
+// The hook we are using.
+void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
+                                         JNIEnv* jni_env ATTRIBUTE_UNUSED,
+                                         jclass class_being_redefined ATTRIBUTE_UNUSED,
+                                         jobject loader ATTRIBUTE_UNUSED,
+                                         const char* name,
+                                         jobject protection_domain ATTRIBUTE_UNUSED,
+                                         jint class_data_len,
+                                         const unsigned char* class_data,
+                                         jint* new_class_data_len,
+                                         unsigned char** new_class_data) {
+  std::vector<unsigned char> out;
+  std::string name_str(name);
+  // Make the jvmti semi-descriptor into the java style descriptor (though with $ for inner
+  // classes).
+  std::replace(name_str.begin(), name_str.end(), '/', '.');
+  StressData* data = nullptr;
+  CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
+           JVMTI_ERROR_NONE);
+  if (!data->vm_class_loader_initialized) {
+    LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
+                 << "initialized. Transforming this class could cause spurious test failures.";
+    return;
+  } else if (DoExtractClassFromData(data, name_str, class_data_len, class_data, /*out*/ &out)) {
+    LOG(INFO) << "Extracted class: " << name;
+    unsigned char* new_data;
+    CHECK_EQ(JVMTI_ERROR_NONE, jvmti->Allocate(out.size(), &new_data));
+    memcpy(new_data, out.data(), out.size());
+    *new_class_data_len = static_cast<jint>(out.size());
+    *new_class_data = new_data;
+  } else {
+    std::cerr << "Unable to extract class " << name_str << std::endl;
+    *new_class_data_len = 0;
+    *new_class_data = nullptr;
+  }
+}
+
+// Options are ${DEXTER_BINARY},${TEMP_FILE_1},${TEMP_FILE_2}
+static void ReadOptions(StressData* data, char* options) {
+  std::string ops(options);
+  data->dexter_cmd = ops.substr(0, ops.find(','));
+  ops = ops.substr(ops.find(',') + 1);
+  data->in_temp_dex = ops.substr(0, ops.find(','));
+  ops = ops.substr(ops.find(',') + 1);
+  data->out_temp_dex = ops;
+}
+
+// We need to make sure that VMClassLoader is initialized before we start redefining anything since
+// it can give (non-fatal) error messages if it's initialized after we've redefined BCP classes.
+// These error messages are expected and no problem but they will mess up our testing
+// infrastructure.
+static void JNICALL EnsureVMClassloaderInitializedCB(jvmtiEnv *jvmti_env,
+                                                     JNIEnv* jni_env,
+                                                     jthread thread ATTRIBUTE_UNUSED) {
+  // Load the VMClassLoader class. We will get a ClassNotFound exception because we don't have
+  // visibility but the class will be loaded behind the scenes.
+  LOG(INFO) << "manual load & initialization of class java/lang/VMClassLoader!";
+  jclass klass = jni_env->FindClass("java/lang/VMClassLoader");
+  if (klass == nullptr) {
+    LOG(ERROR) << "Unable to find VMClassLoader class!";
+  } else {
+    // GetMethodID is spec'd to cause the class to be initialized.
+    jni_env->GetMethodID(klass, "hashCode", "()I");
+    jni_env->DeleteLocalRef(klass);
+    StressData* data = nullptr;
+    CHECK_EQ(jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
+             JVMTI_ERROR_NONE);
+    data->vm_class_loader_initialized = true;
+  }
+}
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
+                                               char* options,
+                                               void* reserved ATTRIBUTE_UNUSED) {
+  jvmtiEnv* jvmti = nullptr;
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0)) {
+    LOG(ERROR) << "Unable to get jvmti env.";
+    return 1;
+  }
+  StressData* data = nullptr;
+  if (JVMTI_ERROR_NONE != jvmti->Allocate(sizeof(StressData),
+                                          reinterpret_cast<unsigned char**>(&data))) {
+    LOG(ERROR) << "Unable to allocate data for stress test.";
+    return 1;
+  }
+  memset(data, 0, sizeof(StressData));
+  // Read the options into the static variables that hold them.
+  ReadOptions(data, options);
+  // Save the data
+  if (JVMTI_ERROR_NONE != jvmti->SetEnvironmentLocalStorage(data)) {
+    LOG(ERROR) << "Unable to save stress test data.";
+    return 1;
+  }
+
+  // Just get all capabilities.
+  jvmtiCapabilities caps;
+  jvmti->GetPotentialCapabilities(&caps);
+  jvmti->AddCapabilities(&caps);
+
+  // Set callbacks.
+  jvmtiEventCallbacks cb;
+  memset(&cb, 0, sizeof(cb));
+  cb.ClassFileLoadHook = ClassFileLoadHookSecretNoOp;
+  cb.VMInit = EnsureVMClassloaderInitializedCB;
+  if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to set class file load hook cb!";
+    return 1;
+  }
+  if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+                                      JVMTI_EVENT_VM_INIT,
+                                      nullptr) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
+    return 1;
+  }
+  if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+                                      JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+                                      nullptr) != JVMTI_ERROR_NONE) {
+    LOG(ERROR) << "Unable to enable CLASS_FILE_LOAD_HOOK event!";
+    return 1;
+  }
+  return 0;
+}
+
+}  // namespace art