Merge "Fix for interpreter crash on new instance of class"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index a679ac2..e89a381 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -76,18 +76,6 @@
 endif
 
 #
-# Used to enable JIT
-#
-ART_JIT := false
-ifneq ($(wildcard art/JIT_ART),)
-$(info Enabling ART_JIT because of existence of art/JIT_ART)
-ART_JIT := true
-endif
-ifeq ($(WITH_ART_JIT), true)
-ART_JIT := true
-endif
-
-#
 # Used to change the default GC. Valid values are CMS, SS, GSS. The default is CMS.
 #
 ART_DEFAULT_GC_TYPE ?= CMS
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index a592162..aab24eb 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1087,8 +1087,11 @@
   uint32_t method_index = resolved_method->GetDexMethodIndex();
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+  Handle<mirror::ClassLoader> class_loader(handles_->NewHandle(
+      resolved_method->GetDeclaringClass()->GetClassLoader()));
+
   DexCompilationUnit dex_compilation_unit(
-      caller_compilation_unit_.GetClassLoader(),
+      class_loader.ToJObject(),
       class_linker,
       callee_dex_file,
       code_item,
diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h
index e2d4c24..68cacd5 100644
--- a/runtime/base/arena_containers.h
+++ b/runtime/base/arena_containers.h
@@ -20,6 +20,7 @@
 #include <deque>
 #include <queue>
 #include <set>
+#include <stack>
 #include <utility>
 
 #include "arena_allocator.h"
@@ -54,6 +55,12 @@
 using ArenaVector = dchecked_vector<T, ArenaAllocatorAdapter<T>>;
 
 template <typename T, typename Comparator = std::less<T>>
+using ArenaPriorityQueue = std::priority_queue<T, ArenaVector<T>, Comparator>;
+
+template <typename T>
+using ArenaStdStack = std::stack<T, ArenaDeque<T>>;
+
+template <typename T, typename Comparator = std::less<T>>
 using ArenaSet = std::set<T, Comparator, ArenaAllocatorAdapter<T>>;
 
 template <typename K, typename V, typename Comparator = std::less<K>>
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f13fea0..9736573 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2200,10 +2200,14 @@
         }
       }
     }
-    if (index < kNumYieldIterations) {
-      sched_yield();
-    } else {
-      usleep(kSleepDurationUS);
+    {
+      // Handle wrapper deals with klass moving.
+      ScopedThreadSuspension sts(self, kSuspended);
+      if (index < kNumYieldIterations) {
+        sched_yield();
+      } else {
+        usleep(kSleepDurationUS);
+      }
     }
     ++index;
   }
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 90446b0..0fdc869 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -47,6 +47,10 @@
 // If kFilterModUnionCards then we attempt to filter cards that don't need to be dirty in the mod
 // union table. Disabled since it does not seem to help the pause much.
 static constexpr bool kFilterModUnionCards = kIsDebugBuild;
+// If kDisallowReadBarrierDuringScan is true then the GC aborts if there are any that occur during
+// ConcurrentCopying::Scan. May be used to diagnose possibly unnecessary read barriers.
+// Only enabled for kIsDebugBuild to avoid performance hit.
+static constexpr bool kDisallowReadBarrierDuringScan = kIsDebugBuild;
 
 ConcurrentCopying::ConcurrentCopying(Heap* heap,
                                      const std::string& name_prefix,
@@ -520,7 +524,7 @@
   explicit ImmuneSpaceScanObjVisitor(ConcurrentCopying* cc)
       : collector_(cc) {}
 
-  ALWAYS_INLINE void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) {
+  void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) {
     if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
       if (obj->GetReadBarrierPointer() == ReadBarrier::GrayPtr()) {
         collector_->ScanImmuneObject(obj);
@@ -534,10 +538,6 @@
     }
   }
 
-  static void Callback(mirror::Object* obj, void* arg) SHARED_REQUIRES(Locks::mutator_lock_) {
-    reinterpret_cast<ImmuneSpaceScanObjVisitor*>(arg)->operator()(obj);
-  }
-
  private:
   ConcurrentCopying* const collector_;
 };
@@ -562,15 +562,10 @@
     for (auto& space : immune_spaces_.GetSpaces()) {
       DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
       accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
-      accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
       ImmuneSpaceScanObjVisitor visitor(this);
-      if (table != nullptr) {
-        table->VisitObjects(ImmuneSpaceScanObjVisitor::Callback, &visitor);
-      } else {
-        live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
-                                      reinterpret_cast<uintptr_t>(space->Limit()),
-                                      visitor);
-      }
+      live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+                                    reinterpret_cast<uintptr_t>(space->Limit()),
+                                    visitor);
     }
   }
   if (kUseBakerReadBarrier) {
@@ -1723,7 +1718,7 @@
 
 // Scan ref fields of an object.
 inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
-  if (kIsDebugBuild) {
+  if (kDisallowReadBarrierDuringScan) {
     // Avoid all read barriers during visit references to help performance.
     Thread::Current()->ModifyDebugDisallowReadBarrier(1);
   }
@@ -1733,7 +1728,7 @@
   // Disable the read barrier for a performance reason.
   to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
       visitor, visitor);
-  if (kIsDebugBuild) {
+  if (kDisallowReadBarrierDuringScan) {
     Thread::Current()->ModifyDebugDisallowReadBarrier(-1);
   }
 }
@@ -1849,7 +1844,10 @@
   ScopedGcGraysImmuneObjects scoped_gc_gray_immune_objects(this);
   CHECK_ALIGNED(byte_size, kObjectAlignment);
   memset(dummy_obj, 0, byte_size);
-  mirror::Class* int_array_class = mirror::IntArray::GetArrayClass();
+  // Avoid going through read barrier for since kDisallowReadBarrierDuringScan may be enabled.
+  // Explicitly mark to make sure to get an object in the to-space.
+  mirror::Class* int_array_class = down_cast<mirror::Class*>(
+      Mark(mirror::IntArray::GetArrayClass<kWithoutReadBarrier>()));
   CHECK(int_array_class != nullptr);
   AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class);
   size_t component_size = int_array_class->GetComponentSize<kWithoutReadBarrier>();
@@ -1859,9 +1857,9 @@
     // An int array is too big. Use java.lang.Object.
     mirror::Class* java_lang_Object = WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object);
     AssertToSpaceInvariant(nullptr, MemberOffset(0), java_lang_Object);
-    CHECK_EQ(byte_size, java_lang_Object->GetObjectSize());
+    CHECK_EQ(byte_size, (java_lang_Object->GetObjectSize<kVerifyNone, kWithoutReadBarrier>()));
     dummy_obj->SetClass(java_lang_Object);
-    CHECK_EQ(byte_size, dummy_obj->SizeOf());
+    CHECK_EQ(byte_size, (dummy_obj->SizeOf<kVerifyNone, kWithoutReadBarrier>()));
   } else {
     // Use an int array.
     dummy_obj->SetClass(int_array_class);
@@ -1884,14 +1882,16 @@
   CHECK_ALIGNED(alloc_size, space::RegionSpace::kAlignment);
   Thread* self = Thread::Current();
   size_t min_object_size = RoundUp(sizeof(mirror::Object), space::RegionSpace::kAlignment);
-  MutexLock mu(self, skipped_blocks_lock_);
-  auto it = skipped_blocks_map_.lower_bound(alloc_size);
-  if (it == skipped_blocks_map_.end()) {
-    // Not found.
-    return nullptr;
-  }
+  size_t byte_size;
+  uint8_t* addr;
   {
-    size_t byte_size = it->first;
+    MutexLock mu(self, skipped_blocks_lock_);
+    auto it = skipped_blocks_map_.lower_bound(alloc_size);
+    if (it == skipped_blocks_map_.end()) {
+      // Not found.
+      return nullptr;
+    }
+    byte_size = it->first;
     CHECK_GE(byte_size, alloc_size);
     if (byte_size > alloc_size && byte_size - alloc_size < min_object_size) {
       // If remainder would be too small for a dummy object, retry with a larger request size.
@@ -1904,27 +1904,33 @@
       CHECK_GE(it->first - alloc_size, min_object_size)
           << "byte_size=" << byte_size << " it->first=" << it->first << " alloc_size=" << alloc_size;
     }
+    // Found a block.
+    CHECK(it != skipped_blocks_map_.end());
+    byte_size = it->first;
+    addr = it->second;
+    CHECK_GE(byte_size, alloc_size);
+    CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr)));
+    CHECK_ALIGNED(byte_size, space::RegionSpace::kAlignment);
+    if (kVerboseMode) {
+      LOG(INFO) << "Reusing skipped bytes : " << reinterpret_cast<void*>(addr) << ", " << byte_size;
+    }
+    skipped_blocks_map_.erase(it);
   }
-  // Found a block.
-  CHECK(it != skipped_blocks_map_.end());
-  size_t byte_size = it->first;
-  uint8_t* addr = it->second;
-  CHECK_GE(byte_size, alloc_size);
-  CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr)));
-  CHECK_ALIGNED(byte_size, space::RegionSpace::kAlignment);
-  if (kVerboseMode) {
-    LOG(INFO) << "Reusing skipped bytes : " << reinterpret_cast<void*>(addr) << ", " << byte_size;
-  }
-  skipped_blocks_map_.erase(it);
   memset(addr, 0, byte_size);
   if (byte_size > alloc_size) {
     // Return the remainder to the map.
     CHECK_ALIGNED(byte_size - alloc_size, space::RegionSpace::kAlignment);
     CHECK_GE(byte_size - alloc_size, min_object_size);
+    // FillWithDummyObject may mark an object, avoid holding skipped_blocks_lock_ to prevent lock
+    // violation and possible deadlock. The deadlock case is a recursive case:
+    // FillWithDummyObject -> IntArray::GetArrayClass -> Mark -> Copy -> AllocateInSkippedBlock.
     FillWithDummyObject(reinterpret_cast<mirror::Object*>(addr + alloc_size),
                         byte_size - alloc_size);
     CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr + alloc_size)));
-    skipped_blocks_map_.insert(std::make_pair(byte_size - alloc_size, addr + alloc_size));
+    {
+      MutexLock mu(self, skipped_blocks_lock_);
+      skipped_blocks_map_.insert(std::make_pair(byte_size - alloc_size, addr + alloc_size));
+    }
   }
   return reinterpret_cast<mirror::Object*>(addr);
 }
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 32b05fa..72112fa 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -127,7 +127,7 @@
   void PushOntoMarkStack(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_);
   mirror::Object* Copy(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
-      REQUIRES(!skipped_blocks_lock_, !mark_stack_lock_);
+      REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
   void Scan(mirror::Object* to_ref) SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!mark_stack_lock_);
   void Process(mirror::Object* obj, MemberOffset offset)
@@ -185,9 +185,11 @@
   void SweepLargeObjects(bool swap_bitmaps)
       SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
   void FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size)
+      REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
   mirror::Object* AllocateInSkippedBlock(size_t alloc_size)
-      SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!skipped_blocks_lock_);
+      REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_)
+      SHARED_REQUIRES(Locks::mutator_lock_);
   void CheckEmptyMarkStack() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
   void IssueEmptyCheckpoint() SHARED_REQUIRES(Locks::mutator_lock_);
   bool IsOnAllocStack(mirror::Object* ref) SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 9a21ec2..042c340 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -162,9 +162,10 @@
     array_class_ = GcRoot<Class>(array_class);
   }
 
+  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   static Class* GetArrayClass() SHARED_REQUIRES(Locks::mutator_lock_) {
     DCHECK(!array_class_.IsNull());
-    return array_class_.Read();
+    return array_class_.Read<kReadBarrierOption>();
   }
 
   static void ResetArrayClass() {
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 8700a90..fd58907 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -16,18 +16,7 @@
 
 #include "oat_file_assistant.h"
 
-#include <fcntl.h>
-#ifdef __linux__
-#include <sys/sendfile.h>
-#else
-#include <sys/socket.h>
-#endif
-#include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
-
-#include <set>
-
 #include "base/logging.h"
 #include "base/stringprintf.h"
 #include "compiler_filter.h"
@@ -71,7 +60,7 @@
                                    const char* oat_location,
                                    const InstructionSet isa,
                                    bool load_executable)
-    : isa_(isa), load_executable_(load_executable) {
+    : isa_(isa), load_executable_(load_executable), odex_(this), oat_(this) {
   CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
   dex_location_.assign(dex_location);
 
@@ -81,15 +70,23 @@
     load_executable_ = false;
   }
 
+  // Get the odex filename.
   std::string error_msg;
-  if (!DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name_, &error_msg)) {
+  std::string odex_file_name;
+  if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) {
+    odex_.Reset(odex_file_name);
+  } else {
     LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
   }
 
+  // Get the oat filename.
   if (oat_location != nullptr) {
-    oat_file_name_ = std::string(oat_location);
+    oat_.Reset(oat_location);
   } else {
-    if (!DexLocationToOatFilename(dex_location_, isa_, &oat_file_name_, &error_msg)) {
+    std::string oat_file_name;
+    if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
+      oat_.Reset(oat_file_name);
+    } else {
       LOG(WARNING) << "Failed to determine oat file name for dex location "
         << dex_location_ << ": " << error_msg;
     }
@@ -124,11 +121,12 @@
   CHECK(error_msg != nullptr);
   CHECK(!flock_.HasFile()) << "OatFileAssistant::Lock already acquired";
 
-  if (OatFileName() == nullptr) {
+  const std::string* oat_file_name = oat_.Filename();
+  if (oat_file_name == nullptr) {
     *error_msg = "Failed to determine lock file";
     return false;
   }
-  std::string lock_file_name = *OatFileName() + ".flock";
+  std::string lock_file_name = *oat_file_name + ".flock";
 
   if (!flock_.Init(lock_file_name.c_str(), error_msg)) {
     unlink(lock_file_name.c_str());
@@ -137,64 +135,33 @@
   return true;
 }
 
-static bool GivenOatFileCompilerFilterIsOkay(const OatFile& oat_file,
-                                             CompilerFilter::Filter target,
-                                             bool profile_changed) {
-  CompilerFilter::Filter current = oat_file.GetCompilerFilter();
-
-  if (profile_changed && CompilerFilter::DependsOnProfile(current)) {
-    VLOG(oat) << "Compiler filter not okay because Profile changed";
-    return false;
-  }
-  return CompilerFilter::IsAsGoodAs(current, target);
-}
-
-bool OatFileAssistant::OatFileCompilerFilterIsOkay(CompilerFilter::Filter target,
-                                                   bool profile_changed) {
-  const OatFile* oat_file = GetOatFile();
-  if (oat_file != nullptr) {
-    return GivenOatFileCompilerFilterIsOkay(*oat_file, target, profile_changed);
-  }
-  return false;
-}
-
-bool OatFileAssistant::OdexFileCompilerFilterIsOkay(CompilerFilter::Filter target,
-                                                    bool profile_changed) {
-  const OatFile* odex_file = GetOdexFile();
-  if (odex_file != nullptr) {
-    return GivenOatFileCompilerFilterIsOkay(*odex_file, target, profile_changed);
-  }
-  return false;
-}
-
 OatFileAssistant::DexOptNeeded
-OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
-                                  bool profile_changed) {
+OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) {
   bool compilation_desired = CompilerFilter::IsBytecodeCompilationEnabled(target);
 
   // See if the oat file is in good shape as is.
-  bool oat_okay = OatFileCompilerFilterIsOkay(target, profile_changed);
+  bool oat_okay = oat_.CompilerFilterIsOkay(target, profile_changed);
   if (oat_okay) {
     if (compilation_desired) {
-      if (OatFileIsUpToDate()) {
+      if (oat_.IsUpToDate()) {
         return kNoDexOptNeeded;
       }
     } else {
-      if (!OatFileIsOutOfDate()) {
+      if (!oat_.IsOutOfDate()) {
         return kNoDexOptNeeded;
       }
     }
   }
 
   // See if the odex file is in good shape as is.
-  bool odex_okay = OdexFileCompilerFilterIsOkay(target, profile_changed);
+  bool odex_okay = odex_.CompilerFilterIsOkay(target, profile_changed);
   if (odex_okay) {
     if (compilation_desired) {
-      if (OdexFileIsUpToDate()) {
+      if (odex_.IsUpToDate()) {
         return kNoDexOptNeeded;
       }
     } else {
-      if (!OdexFileIsOutOfDate()) {
+      if (!odex_.IsOutOfDate()) {
         return kNoDexOptNeeded;
       }
     }
@@ -202,11 +169,11 @@
 
   // See if we can get an up-to-date file by running patchoat.
   if (compilation_desired) {
-    if (odex_okay && OdexFileNeedsRelocation() && OdexFileHasPatchInfo()) {
+    if (odex_okay && odex_.NeedsRelocation() && odex_.HasPatchInfo()) {
       return kPatchOatNeeded;
     }
 
-    if (oat_okay && OatFileNeedsRelocation() && OatFileHasPatchInfo()) {
+    if (oat_okay && oat_.NeedsRelocation() && oat_.HasPatchInfo()) {
       return kSelfPatchOatNeeded;
     }
   }
@@ -251,8 +218,8 @@
   switch (GetDexOptNeeded(target, profile_changed)) {
     case kNoDexOptNeeded: return kUpdateSucceeded;
     case kDex2OatNeeded: return GenerateOatFile(error_msg);
-    case kPatchOatNeeded: return RelocateOatFile(OdexFileName(), error_msg);
-    case kSelfPatchOatNeeded: return RelocateOatFile(OatFileName(), error_msg);
+    case kPatchOatNeeded: return RelocateOatFile(odex_.Filename(), error_msg);
+    case kSelfPatchOatNeeded: return RelocateOatFile(oat_.Filename(), error_msg);
   }
   UNREACHABLE();
 }
@@ -263,46 +230,40 @@
   // 2. Not out-of-date files that are already opened non-executable.
   // 3. Not out-of-date files that we must reopen non-executable.
 
-  if (OatFileIsUpToDate()) {
-    oat_file_released_ = true;
-    return std::move(cached_oat_file_);
+  if (oat_.IsUpToDate()) {
+    return oat_.ReleaseFile();
   }
 
-  if (OdexFileIsUpToDate()) {
-    oat_file_released_ = true;
-    return std::move(cached_odex_file_);
+  if (odex_.IsUpToDate()) {
+    return odex_.ReleaseFile();
   }
 
   VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
     << " attempting to fall back to interpreting oat file instead.";
 
-  if (!OatFileIsOutOfDate() && !OatFileIsExecutable()) {
-    oat_file_released_ = true;
-    return std::move(cached_oat_file_);
+  if (!oat_.IsOutOfDate() && !oat_.IsExecutable()) {
+    return oat_.ReleaseFile();
   }
 
-  if (!OdexFileIsOutOfDate() && !OdexFileIsExecutable()) {
-    oat_file_released_ = true;
-    return std::move(cached_odex_file_);
+  if (!odex_.IsOutOfDate() && !odex_.IsExecutable()) {
+    return odex_.ReleaseFile();
   }
 
-  if (!OatFileIsOutOfDate()) {
+  if (!oat_.IsOutOfDate()) {
     load_executable_ = false;
-    ClearOatFileCache();
-    if (!OatFileIsOutOfDate()) {
-      CHECK(!OatFileIsExecutable());
-      oat_file_released_ = true;
-      return std::move(cached_oat_file_);
+    oat_.Reset();
+    if (!oat_.IsOutOfDate()) {
+      CHECK(!oat_.IsExecutable());
+      return oat_.ReleaseFile();
     }
   }
 
-  if (!OdexFileIsOutOfDate()) {
+  if (!odex_.IsOutOfDate()) {
     load_executable_ = false;
-    ClearOdexFileCache();
-    if (!OdexFileIsOutOfDate()) {
-      CHECK(!OdexFileIsExecutable());
-      oat_file_released_ = true;
-      return std::move(cached_odex_file_);
+    odex_.Reset();
+    if (!odex_.IsOutOfDate()) {
+      CHECK(!odex_.IsExecutable());
+      return odex_.ReleaseFile();
     }
   }
 
@@ -358,43 +319,31 @@
 }
 
 const std::string* OatFileAssistant::OdexFileName() {
-  return odex_file_name_.empty() ? nullptr : &odex_file_name_;
+  return odex_.Filename();
 }
 
 bool OatFileAssistant::OdexFileExists() {
-  return GetOdexFile() != nullptr;
+  return odex_.Exists();
 }
 
 OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() {
-  if (!odex_file_status_attempted_) {
-    odex_file_status_attempted_ = true;
-    const OatFile* odex_file = GetOdexFile();
-    if (odex_file == nullptr) {
-      cached_odex_file_status_ = kOatOutOfDate;
-    } else {
-      cached_odex_file_status_ = GivenOatFileStatus(*odex_file);
-    }
-  }
-  return cached_odex_file_status_;
+  return odex_.Status();
 }
 
 bool OatFileAssistant::OdexFileIsOutOfDate() {
-  return OdexFileStatus() == kOatOutOfDate;
+  return odex_.IsOutOfDate();
 }
 
 bool OatFileAssistant::OdexFileNeedsRelocation() {
-  return OdexFileStatus() == kOatNeedsRelocation;
+  return odex_.NeedsRelocation();
 }
 
 bool OatFileAssistant::OdexFileIsUpToDate() {
-  return OdexFileStatus() == kOatUpToDate;
+  return odex_.IsUpToDate();
 }
 
 CompilerFilter::Filter OatFileAssistant::OdexFileCompilerFilter() {
-  const OatFile* odex_file = GetOdexFile();
-  CHECK(odex_file != nullptr);
-
-  return odex_file->GetCompilerFilter();
+  return odex_.CompilerFilter();
 }
 
 static std::string ArtFileName(const OatFile* oat_file) {
@@ -409,43 +358,31 @@
 }
 
 const std::string* OatFileAssistant::OatFileName() {
-  return oat_file_name_.empty() ? nullptr : &oat_file_name_;
+  return oat_.Filename();
 }
 
 bool OatFileAssistant::OatFileExists() {
-  return GetOatFile() != nullptr;
+  return oat_.Exists();
 }
 
 OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() {
-  if (!oat_file_status_attempted_) {
-    oat_file_status_attempted_ = true;
-    const OatFile* oat_file = GetOatFile();
-    if (oat_file == nullptr) {
-      cached_oat_file_status_ = kOatOutOfDate;
-    } else {
-      cached_oat_file_status_ = GivenOatFileStatus(*oat_file);
-    }
-  }
-  return cached_oat_file_status_;
+  return oat_.Status();
 }
 
 bool OatFileAssistant::OatFileIsOutOfDate() {
-  return OatFileStatus() == kOatOutOfDate;
+  return oat_.IsOutOfDate();
 }
 
 bool OatFileAssistant::OatFileNeedsRelocation() {
-  return OatFileStatus() == kOatNeedsRelocation;
+  return oat_.NeedsRelocation();
 }
 
 bool OatFileAssistant::OatFileIsUpToDate() {
-  return OatFileStatus() == kOatUpToDate;
+  return oat_.IsUpToDate();
 }
 
 CompilerFilter::Filter OatFileAssistant::OatFileCompilerFilter() {
-  const OatFile* oat_file = GetOatFile();
-  CHECK(oat_file != nullptr);
-
-  return oat_file->GetCompilerFilter();
+  return oat_.CompilerFilter();
 }
 
 OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
@@ -572,12 +509,12 @@
   }
   const std::string& input_file_name = *input_file;
 
-  if (OatFileName() == nullptr) {
+  if (oat_.Filename() == nullptr) {
     *error_msg = "Patching of oat file for dex location " + dex_location_
       + " not attempted because the oat file name could not be determined.";
     return kUpdateNotAttempted;
   }
-  const std::string& oat_file_name = *OatFileName();
+  const std::string& oat_file_name = *oat_.Filename();
 
   const ImageInfo* image_info = GetImageInfo();
   Runtime* runtime = Runtime::Current();
@@ -609,7 +546,7 @@
   }
 
   // Mark that the oat file has changed and we should try to reload.
-  ClearOatFileCache();
+  oat_.Reset();
   return kUpdateSucceeded;
 }
 
@@ -624,12 +561,12 @@
     return kUpdateNotAttempted;
   }
 
-  if (OatFileName() == nullptr) {
+  if (oat_.Filename() == nullptr) {
     *error_msg = "Generation of oat file for dex location " + dex_location_
       + " not attempted because the oat file name could not be determined.";
     return kUpdateNotAttempted;
   }
-  const std::string& oat_file_name = *OatFileName();
+  const std::string& oat_file_name = *oat_.Filename();
 
   // dex2oat ignores missing dex files and doesn't report an error.
   // Check explicitly here so we can detect the error properly.
@@ -674,7 +611,7 @@
   }
 
   // Mark that the oat file has changed and we should try to reload.
-  ClearOatFileCache();
+  oat_.Reset();
   return kUpdateSucceeded;
 }
 
@@ -821,7 +758,7 @@
       has_original_dex_files_ = false;
 
       // Get the checksum from the odex if we can.
-      const OatFile* odex_file = GetOdexFile();
+      const OatFile* odex_file = odex_.GetFile();
       if (odex_file != nullptr) {
         const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(
             dex_location_.c_str(), nullptr, false);
@@ -835,86 +772,6 @@
   return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr;
 }
 
-const OatFile* OatFileAssistant::GetOdexFile() {
-  CHECK(!oat_file_released_) << "OdexFile called after oat file released.";
-  if (!odex_file_load_attempted_) {
-    odex_file_load_attempted_ = true;
-    if (OdexFileName() != nullptr) {
-      const std::string& odex_file_name = *OdexFileName();
-      std::string error_msg;
-      cached_odex_file_.reset(OatFile::Open(odex_file_name.c_str(),
-                                            odex_file_name.c_str(),
-                                            nullptr,
-                                            nullptr,
-                                            load_executable_,
-                                            /*low_4gb*/false,
-                                            dex_location_.c_str(),
-                                            &error_msg));
-      if (cached_odex_file_.get() == nullptr) {
-        VLOG(oat) << "OatFileAssistant test for existing pre-compiled oat file "
-          << odex_file_name << ": " << error_msg;
-      }
-    }
-  }
-  return cached_odex_file_.get();
-}
-
-bool OatFileAssistant::OdexFileIsExecutable() {
-  const OatFile* odex_file = GetOdexFile();
-  return (odex_file != nullptr && odex_file->IsExecutable());
-}
-
-bool OatFileAssistant::OdexFileHasPatchInfo() {
-  const OatFile* odex_file = GetOdexFile();
-  return (odex_file != nullptr && odex_file->HasPatchInfo());
-}
-
-void OatFileAssistant::ClearOdexFileCache() {
-  odex_file_load_attempted_ = false;
-  cached_odex_file_.reset();
-  odex_file_status_attempted_ = false;
-}
-
-const OatFile* OatFileAssistant::GetOatFile() {
-  CHECK(!oat_file_released_) << "OatFile called after oat file released.";
-  if (!oat_file_load_attempted_) {
-    oat_file_load_attempted_ = true;
-    if (OatFileName() != nullptr) {
-      const std::string& oat_file_name = *OatFileName();
-      std::string error_msg;
-      cached_oat_file_.reset(OatFile::Open(oat_file_name.c_str(),
-                                           oat_file_name.c_str(),
-                                           nullptr,
-                                           nullptr,
-                                           load_executable_,
-                                           /*low_4gb*/false,
-                                           dex_location_.c_str(),
-                                           &error_msg));
-      if (cached_oat_file_.get() == nullptr) {
-        VLOG(oat) << "OatFileAssistant test for existing oat file "
-          << oat_file_name << ": " << error_msg;
-      }
-    }
-  }
-  return cached_oat_file_.get();
-}
-
-bool OatFileAssistant::OatFileIsExecutable() {
-  const OatFile* oat_file = GetOatFile();
-  return (oat_file != nullptr && oat_file->IsExecutable());
-}
-
-bool OatFileAssistant::OatFileHasPatchInfo() {
-  const OatFile* oat_file = GetOatFile();
-  return (oat_file != nullptr && oat_file->HasPatchInfo());
-}
-
-void OatFileAssistant::ClearOatFileCache() {
-  oat_file_load_attempted_ = false;
-  cached_oat_file_.reset();
-  oat_file_status_attempted_ = false;
-}
-
 const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() {
   if (!image_info_load_attempted_) {
     image_info_load_attempted_ = true;
@@ -990,5 +847,113 @@
   return ret;
 }
 
+OatFileAssistant::OatFileInfo::OatFileInfo(OatFileAssistant* oat_file_assistant)
+  : oat_file_assistant_(oat_file_assistant)
+{}
+
+const std::string* OatFileAssistant::OatFileInfo::Filename() {
+  return filename_provided_ ? &filename_ : nullptr;
+}
+
+bool OatFileAssistant::OatFileInfo::Exists() {
+  return GetFile() != nullptr;
+}
+
+OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() {
+  if (!status_attempted_) {
+    status_attempted_ = true;
+    const OatFile* file = GetFile();
+    if (file == nullptr) {
+      status_ = kOatOutOfDate;
+    } else {
+      status_ = oat_file_assistant_->GivenOatFileStatus(*file);
+    }
+  }
+  return status_;
+}
+
+bool OatFileAssistant::OatFileInfo::IsOutOfDate() {
+  return Status() == kOatOutOfDate;
+}
+
+bool OatFileAssistant::OatFileInfo::NeedsRelocation() {
+  return Status() == kOatNeedsRelocation;
+}
+
+bool OatFileAssistant::OatFileInfo::IsUpToDate() {
+  return Status() == kOatUpToDate;
+}
+
+CompilerFilter::Filter OatFileAssistant::OatFileInfo::CompilerFilter() {
+  const OatFile* file = GetFile();
+  CHECK(file != nullptr);
+  return file->GetCompilerFilter();
+}
+
+const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
+  CHECK(!file_released_) << "GetFile called after oat file released.";
+  if (!load_attempted_) {
+    load_attempted_ = true;
+    if (filename_provided_) {
+      std::string error_msg;
+      file_.reset(OatFile::Open(filename_.c_str(),
+                                filename_.c_str(),
+                                nullptr,
+                                nullptr,
+                                oat_file_assistant_->load_executable_,
+                                /*low_4gb*/false,
+                                oat_file_assistant_->dex_location_.c_str(),
+                                &error_msg));
+      if (file_.get() == nullptr) {
+        VLOG(oat) << "OatFileAssistant test for existing oat file "
+          << filename_ << ": " << error_msg;
+      }
+    }
+  }
+  return file_.get();
+}
+
+bool OatFileAssistant::OatFileInfo::CompilerFilterIsOkay(
+    CompilerFilter::Filter target, bool profile_changed) {
+  const OatFile* file = GetFile();
+  if (file == nullptr) {
+    return false;
+  }
+
+  CompilerFilter::Filter current = file->GetCompilerFilter();
+  if (profile_changed && CompilerFilter::DependsOnProfile(current)) {
+    VLOG(oat) << "Compiler filter not okay because Profile changed";
+    return false;
+  }
+  return CompilerFilter::IsAsGoodAs(current, target);
+}
+
+bool OatFileAssistant::OatFileInfo::IsExecutable() {
+  const OatFile* file = GetFile();
+  return (file != nullptr && file->IsExecutable());
+}
+
+bool OatFileAssistant::OatFileInfo::HasPatchInfo() {
+  const OatFile* file = GetFile();
+  return (file != nullptr && file->HasPatchInfo());
+}
+
+void OatFileAssistant::OatFileInfo::Reset() {
+  load_attempted_ = false;
+  file_.reset();
+  status_attempted_ = false;
+}
+
+void OatFileAssistant::OatFileInfo::Reset(const std::string& filename) {
+  filename_provided_ = true;
+  filename_ = filename;
+  Reset();
+}
+
+std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFile() {
+  file_released_ = true;
+  return std::move(file_);
+}
+
 }  // namespace art
 
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 04bd20c..effeabb 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -306,6 +306,73 @@
     std::string location;
   };
 
+  class OatFileInfo {
+   public:
+    // Initially the info is for no file in particular. It will treat the
+    // file as out of date until Reset is called with a real filename to use
+    // the cache for.
+    explicit OatFileInfo(OatFileAssistant* oat_file_assistant);
+
+    const std::string* Filename();
+    bool Exists();
+    OatStatus Status();
+    bool IsOutOfDate();
+    bool NeedsRelocation();
+    bool IsUpToDate();
+    // Must only be called if the associated file exists, i.e, if
+    // |Exists() == true|.
+    CompilerFilter::Filter CompilerFilter();
+
+    // Returns the loaded file.
+    // Loads the file if needed. Returns null if the file failed to load.
+    // The caller shouldn't clean up or free the returned pointer.
+    const OatFile* GetFile();
+
+    // Returns true if the compiler filter used to generate the file is at
+    // least as good as the given target filter. profile_changed should be
+    // true to indicate the profile has recently changed for this dex
+    // location.
+    bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
+
+    // Returns true if the file is opened executable.
+    bool IsExecutable();
+
+    // Returns true if the file has patch info required to run patchoat.
+    bool HasPatchInfo();
+
+    // Clear any cached information about the file that depends on the
+    // contents of the file. This does not reset the provided filename.
+    void Reset();
+
+    // Clear any cached information and switch to getting info about the oat
+    // file with the given filename.
+    void Reset(const std::string& filename);
+
+    // Release the loaded oat file.
+    // Returns null if the oat file hasn't been loaded.
+    //
+    // After this call, no other methods of the OatFileInfo should be
+    // called, because access to the loaded oat file has been taken away from
+    // the OatFileInfo object.
+    std::unique_ptr<OatFile> ReleaseFile();
+
+   private:
+    OatFileAssistant* oat_file_assistant_;
+
+    bool filename_provided_ = false;
+    std::string filename_;
+
+    bool load_attempted_ = false;
+    std::unique_ptr<OatFile> file_;
+
+    bool status_attempted_ = false;
+    OatStatus status_;
+
+    // For debugging only.
+    // If this flag is set, the file has been released to the user and the
+    // OatFileInfo object is in a bad state and should no longer be used.
+    bool file_released_ = false;
+  };
 
   // Returns the current image location.
   // Returns an empty string if the image location could not be retrieved.
@@ -322,46 +389,6 @@
   // found for the dex_location_ dex file.
   const uint32_t* GetRequiredDexChecksum();
 
-  // Returns the loaded odex file.
-  // Loads the file if needed. Returns null if the file failed to load.
-  // The caller shouldn't clean up or free the returned pointer.
-  const OatFile* GetOdexFile();
-
-  // Returns true if the compiler filter used to generate the odex file is at
-  // least as good as the given target filter. profile_changed should be true
-  // to indicate the profile has recently changed for this dex location.
-  bool OdexFileCompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
-
-  // Returns true if the odex file is opened executable.
-  bool OdexFileIsExecutable();
-
-  // Returns true if the odex file has patch info required to run patchoat.
-  bool OdexFileHasPatchInfo();
-
-  // Clear any cached information about the odex file that depends on the
-  // contents of the file.
-  void ClearOdexFileCache();
-
-  // Returns the loaded oat file.
-  // Loads the file if needed. Returns null if the file failed to load.
-  // The caller shouldn't clean up or free the returned pointer.
-  const OatFile* GetOatFile();
-
-  // Returns true if the compiler filter used to generate the oat file is at
-  // least as good as the given target filter. profile_changed should be true
-  // to indicate the profile has recently changed for this dex location.
-  bool OatFileCompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed);
-
-  // Returns true if the oat file is opened executable.
-  bool OatFileIsExecutable();
-
-  // Returns true if the oat file has patch info required to run patchoat.
-  bool OatFileHasPatchInfo();
-
-  // Clear any cached information about the oat file that depends on the
-  // contents of the file.
-  void ClearOatFileCache();
-
   // Returns the loaded image info.
   // Loads the image info if needed. Returns null if the image info failed
   // to load.
@@ -391,33 +418,8 @@
   bool required_dex_checksum_found_;
   bool has_original_dex_files_;
 
-  // The sentinel value "" is used if the odex file name could not be
-  // determined.
-  std::string odex_file_name_;
-
-  // Cached value of the loaded odex file.
-  // Use the GetOdexFile method rather than accessing this directly, unless you
-  // know the odex file isn't out of date.
-  bool odex_file_load_attempted_ = false;
-  std::unique_ptr<OatFile> cached_odex_file_;
-
-  // Cached results for OdexFileStatus
-  bool odex_file_status_attempted_ = false;
-  OatStatus cached_odex_file_status_;
-
-  // The sentinel value "" is used if the oat file name could not be
-  // determined.
-  std::string oat_file_name_;
-
-  // Cached value of the loaded oat file.
-  // Use the GetOatFile method rather than accessing this directly, unless you
-  // know the oat file isn't out of date.
-  bool oat_file_load_attempted_ = false;
-  std::unique_ptr<OatFile> cached_oat_file_;
-
-  // Cached results for OatFileStatus
-  bool oat_file_status_attempted_ = false;
-  OatStatus cached_oat_file_status_;
+  OatFileInfo odex_;
+  OatFileInfo oat_;
 
   // Cached value of the image info.
   // Use the GetImageInfo method rather than accessing these directly.
@@ -428,12 +430,6 @@
   ImageInfo cached_image_info_;
   uint32_t combined_image_checksum_ = 0;
 
-  // For debugging only.
-  // If this flag is set, the oat or odex file has been released to the user
-  // of the OatFileAssistant object and the OatFileAssistant object is in a
-  // bad state and should no longer be used.
-  bool oat_file_released_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(OatFileAssistant);
 };
 
diff --git a/test/613-inlining-dex-cache/expected.txt b/test/613-inlining-dex-cache/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/613-inlining-dex-cache/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/613-inlining-dex-cache/info.txt b/test/613-inlining-dex-cache/info.txt
new file mode 100644
index 0000000..e80f642
--- /dev/null
+++ b/test/613-inlining-dex-cache/info.txt
@@ -0,0 +1,2 @@
+Regression test for the JIT compiler which used to
+wrongly update the dex cache of a class loader.
diff --git a/test/613-inlining-dex-cache/run b/test/613-inlining-dex-cache/run
new file mode 100644
index 0000000..9c1e7aa
--- /dev/null
+++ b/test/613-inlining-dex-cache/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+flags="$@"
+# We need the dex files pre-verified to avoid running the verifier
+# at runtime which will update the dex cache.
+exec ${RUN} ${flags/verify-at-runtime/interpret-only}
diff --git a/test/613-inlining-dex-cache/src-ex/B.java b/test/613-inlining-dex-cache/src-ex/B.java
new file mode 100644
index 0000000..4da9a1d
--- /dev/null
+++ b/test/613-inlining-dex-cache/src-ex/B.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+public class B {
+}
diff --git a/test/613-inlining-dex-cache/src-ex/LoadedByAppClassLoader.java b/test/613-inlining-dex-cache/src-ex/LoadedByAppClassLoader.java
new file mode 100644
index 0000000..f4e0f10
--- /dev/null
+++ b/test/613-inlining-dex-cache/src-ex/LoadedByAppClassLoader.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+public class LoadedByAppClassLoader {
+  public static void letMeInlineYou() {
+    // We used to pass the wrong class loader when trying to inline 'Main.foo'.
+    Main.foo(null);
+  }
+}
diff --git a/test/613-inlining-dex-cache/src/B.java b/test/613-inlining-dex-cache/src/B.java
new file mode 100644
index 0000000..6e7e55d
--- /dev/null
+++ b/test/613-inlining-dex-cache/src/B.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+public class B {
+  public void foo() {
+  }
+}
diff --git a/test/613-inlining-dex-cache/src/Main.java b/test/613-inlining-dex-cache/src/Main.java
new file mode 100644
index 0000000..31ab1d2
--- /dev/null
+++ b/test/613-inlining-dex-cache/src/Main.java
@@ -0,0 +1,85 @@
+/*
+ * 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.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+import dalvik.system.PathClassLoader;
+
+// ClassLoader not delegating for non java. packages.
+class DelegateLastPathClassLoader extends PathClassLoader {
+
+  public DelegateLastPathClassLoader(String dexPath, ClassLoader parent) {
+    super(dexPath, parent);
+  }
+
+  @Override
+  protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+    if (!name.startsWith("java.")) {
+      try {
+        return findClass(name);
+      } catch (ClassNotFoundException ignore) {
+        // Ignore and fall through to parent class loader.
+      }
+    }
+    return super.loadClass(name, resolve);
+  }
+}
+
+public class Main {
+
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+    final String DEX_FILE = System.getenv("DEX_LOCATION") + "/613-inlining-dex-cache-ex.jar";
+    ClassLoader loader = new DelegateLastPathClassLoader(DEX_FILE, Main.class.getClassLoader());
+    Class cls = loader.loadClass("LoadedByAppClassLoader");
+    Method m = cls.getDeclaredMethod("letMeInlineYou");
+    // Invoke the method enough times to get JITted.
+    for (int i = 0; i < 10000; ++i) {
+      m.invoke(null);
+    }
+    ensureJitCompiled(cls, "letMeInlineYou");
+    ClassLoader bLoader = areYouB();
+    if (bLoader != Main.class.getClassLoader()) {
+      throw new Error("Wrong class loader");
+    }
+  }
+
+  public static void foo(Main o) {
+    // LoadedByAppClassLoader.letMeInlineYou will try to inline this
+    // method but used to pass the wrong class loader. As a result,
+    // the lookup of B.foo was updating the dex cache with the other
+    // class loader's B class.
+    if (o != null) {
+      o.myField.foo();
+    }
+  }
+
+  public B myField;
+
+  public static ClassLoader areYouB() {
+    return OtherClass.getB().getClassLoader();
+  }
+
+  public static native void ensureJitCompiled(Class cls, String method_name);
+}
+
+class OtherClass {
+  public static Class getB() {
+    // This used to return the B class of another class loader.
+    return B.class;
+  }
+}