Merge "ART: Use _exit in dex2oat"
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 4297979..41df56b 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2940,6 +2940,199 @@
   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)
@@ -2951,7 +3144,6 @@
 UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
 
 UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
 
 UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 0536f322..0e0d66b 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1816,6 +1816,10 @@
       output_location += "/" + dex_file_location + ".new";
     }
     new_file.reset(OS::CreateEmptyFile(output_location.c_str()));
+    if (new_file == nullptr) {
+      LOG(ERROR) << "Could not create dex writer output file: " << output_location;
+      return;
+    }
     ftruncate(new_file->Fd(), header_->FileSize());
     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));
@@ -1825,7 +1829,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/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..3f7461c 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,11 @@
   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);
+}
+
 }  // namespace art
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/heap.cc b/runtime/gc/heap.cc
index 357541f..a853b98 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.
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/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/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/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/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..7a82fde 100644
--- a/test/980-redefine-object/src/Main.java
+++ b/test/980-redefine-object/src/Main.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Base64;
 import java.util.LinkedList;
@@ -287,6 +288,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 +324,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 +334,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 {
@@ -382,8 +408,6 @@
 
   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,
diff --git a/test/Android.bp b/test/Android.bp
index 8059a2f..538fac4 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -297,7 +297,6 @@
         "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",
     ],