Merge "ART: Add OWNERS file"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c87abe5..b6ffcc5 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -30,6 +30,10 @@
   ErroneousA \
   ErroneousB \
   ErroneousInit \
+  ForClassLoaderA \
+  ForClassLoaderB \
+  ForClassLoaderC \
+  ForClassLoaderD \
   ExceptionHandle \
   GetMethodSignature \
   ImageLayoutA \
@@ -99,7 +103,7 @@
 ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
 
 ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
 ART_GTEST_class_table_test_DEX_DEPS := XandY
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
 ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
diff --git a/compiler/Android.bp b/compiler/Android.bp
index a1269dc..62226da 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -429,13 +429,20 @@
 
     shared_libs: [
         "libartd-compiler",
-        "libartd-simulator",
         "libvixld-arm",
         "libvixld-arm64",
 
         "libbacktrace",
         "libnativeloader",
     ],
+
+    target: {
+        host: {
+            shared_libs: [
+                "libartd-simulator",
+            ],
+        },
+    },
 }
 
 art_cc_test {
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index a04349e..db95bd6 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -103,16 +103,16 @@
 }
 
 inline ArtMethod* CompilerDriver::ResolveMethod(
-    ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
-    Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
-    uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) {
+    ScopedObjectAccess& soa,
+    Handle<mirror::DexCache> dex_cache,
+    Handle<mirror::ClassLoader> class_loader,
+    const DexCompilationUnit* mUnit,
+    uint32_t method_idx,
+    InvokeType invoke_type) {
   DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
   ArtMethod* resolved_method =
-      check_incompatible_class_change
-          ? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
-              *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type)
-          : mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
-              *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type);
+      mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
+          *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type);
   if (UNLIKELY(resolved_method == nullptr)) {
     DCHECK(soa.Self()->IsExceptionPending());
     // Clean up any exception left by type resolution.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index f834f30..bb64755 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -87,6 +87,10 @@
 // Print additional info during profile guided compilation.
 static constexpr bool kDebugProfileGuidedCompilation = false;
 
+// Max encoded fields allowed for initializing app image. Hardcode the number for now
+// because 5000 should be large enough.
+static constexpr uint32_t kMaxEncodedFields = 5000;
+
 static double Percentage(size_t x, size_t y) {
   return 100.0 * (static_cast<double>(x)) / (static_cast<double>(x + y));
 }
@@ -2273,11 +2277,17 @@
         }
         // Otherwise it's in app image but superclasses can't be initialized, no need to proceed.
         old_status = klass->GetStatus();
+
+        bool too_many_encoded_fields = false;
+        if (!is_boot_image && klass->NumStaticFields() > kMaxEncodedFields) {
+          too_many_encoded_fields = true;
+        }
         // If the class was not initialized, we can proceed to see if we can initialize static
-        // fields.
+        // fields. Limit the max number of encoded fields.
         if (!klass->IsInitialized() &&
             (is_app_image || is_boot_image) &&
             is_superclass_initialized &&
+            !too_many_encoded_fields &&
             manager_->GetCompiler()->IsImageClass(descriptor)) {
           bool can_init_static_fields = false;
           if (is_boot_image) {
@@ -2415,30 +2425,6 @@
     }
   }
 
-  bool NoPotentialInternStrings(Handle<mirror::Class> klass,
-                                Handle<mirror::ClassLoader>* class_loader)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    StackHandleScope<1> hs(Thread::Current());
-    Handle<mirror::DexCache> h_dex_cache = hs.NewHandle(klass->GetDexCache());
-    const DexFile* dex_file = h_dex_cache->GetDexFile();
-    const DexFile::ClassDef* class_def = klass->GetClassDef();
-    annotations::RuntimeEncodedStaticFieldValueIterator value_it(*dex_file,
-                                                                 &h_dex_cache,
-                                                                 class_loader,
-                                                                 manager_->GetClassLinker(),
-                                                                 *class_def);
-
-    const auto jString = annotations::RuntimeEncodedStaticFieldValueIterator::kString;
-    for ( ; value_it.HasNext(); value_it.Next()) {
-      if (value_it.GetValueType() == jString) {
-        // We don't want cache the static encoded strings which is a potential intern.
-        return false;
-      }
-    }
-
-    return true;
-  }
-
   bool ResolveTypesOfMethods(Thread* self, ArtMethod* m)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     auto rtn_type = m->GetReturnType(true);  // return value is discarded because resolve will be done internally.
@@ -2568,7 +2554,7 @@
       }
     }
 
-    return NoPotentialInternStrings(klass, class_loader);
+    return true;
   }
 
   const ParallelCompilationManager* const manager_;
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index cd4f400..e9e7378 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -254,9 +254,12 @@
 
   // Resolve a method. Returns null on failure, including incompatible class change.
   ArtMethod* ResolveMethod(
-      ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
-      Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
-      uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change = true)
+      ScopedObjectAccess& soa,
+      Handle<mirror::DexCache> dex_cache,
+      Handle<mirror::ClassLoader> class_loader,
+      const DexCompilationUnit* mUnit,
+      uint32_t method_idx,
+      InvokeType invoke_type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   void ProcessedInstanceField(bool resolved);
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 531bc98..ed011d6 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -58,7 +58,8 @@
   bool show_section_headers_ = false;
   bool show_section_statistics_ = false;
   bool verbose_ = false;
-  bool verify_output_ = false;
+  // TODO: Set verify_output_ back to false by default. Was set to true for debugging b/62840842.
+  bool verify_output_ = true;
   bool visualize_pattern_ = false;
   OutputFormat output_format_ = kOutputPlain;
   const char* output_dex_directory_ = nullptr;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 7aab9de..a19085f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -55,6 +55,7 @@
 #include "gc_root-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/heap_bitmap-inl.h"
+#include "gc/accounting/space_bitmap-inl.h"
 #include "gc/heap.h"
 #include "gc/scoped_gc_critical_section.h"
 #include "gc/space/image_space.h"
@@ -88,6 +89,7 @@
 #include "mirror/method_handles_lookup.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
+#include "mirror/object-refvisitor-inl.h"
 #include "mirror/proxy.h"
 #include "mirror/reference-inl.h"
 #include "mirror/stack_trace_element.h"
@@ -1193,6 +1195,63 @@
   gc::accounting::HeapBitmap* const live_bitmap_;
 };
 
+class FixupInternVisitor {
+ public:
+  ALWAYS_INLINE ObjPtr<mirror::Object> TryInsertIntern(mirror::Object* obj) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (obj != nullptr && obj->IsString()) {
+      const auto intern = Runtime::Current()->GetInternTable()->InternStrong(obj->AsString());
+      return intern;
+    }
+    return obj;
+  }
+
+  ALWAYS_INLINE void VisitRootIfNonNull(
+      mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    root->Assign(TryInsertIntern(root->AsMirrorPtr()));
+  }
+
+  // Visit Class Fields
+  ALWAYS_INLINE void operator()(ObjPtr<mirror::Object> obj,
+                                MemberOffset offset,
+                                bool is_static ATTRIBUTE_UNUSED) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    // There could be overlap between ranges, we must avoid visiting the same reference twice.
+    // Avoid the class field since we already fixed it up in FixupClassVisitor.
+    if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) {
+      // Updating images, don't do a read barrier.
+      // Only string fields are fixed, don't do a verify.
+      mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
+          offset);
+      obj->SetFieldObject<false, false>(offset, TryInsertIntern(ref));
+    }
+  }
+
+  void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+                  ObjPtr<mirror::Reference> ref) const
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+    this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+  }
+
+  void operator()(mirror::Object* obj) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (obj->IsDexCache()) {
+      obj->VisitReferences<true, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+    } else {
+      // Don't visit native roots for non-dex-cache
+      obj->VisitReferences<false, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+    }
+  }
+};
+
 // Copies data from one array to another array at the same position
 // if pred returns false. If there is a page of continuous data in
 // the src array for which pred consistently returns true then
@@ -1285,6 +1344,7 @@
         return false;
       }
     }
+
     // Only add the classes to the class loader after the points where we can return false.
     for (size_t i = 0; i < num_dex_caches; i++) {
       ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
@@ -1448,6 +1508,21 @@
       }
     }
   }
+  {
+    // Fixup all the literal strings happens at app images which are supposed to be interned.
+    ScopedTrace timing("Fixup String Intern in image and dex_cache");
+    const auto& image_header = space->GetImageHeader();
+    const auto bitmap = space->GetMarkBitmap();  // bitmap of objects
+    const uint8_t* target_base = space->GetMemMap()->Begin();
+    const ImageSection& objects_section =
+        image_header.GetImageSection(ImageHeader::kSectionObjects);
+
+    uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
+    uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+
+    FixupInternVisitor fixup_intern_visitor;
+    bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor);
+  }
   if (*out_forward_dex_cache_array) {
     ScopedTrace timing("Fixup ArtMethod dex cache arrays");
     FixupArtMethodArrayVisitor visitor(header);
@@ -2410,74 +2485,121 @@
   return ClassPathEntry(nullptr, nullptr);
 }
 
+// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
+// (they both have the same behaviour with respect to class lockup order)
+static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                   Handle<mirror::ClassLoader> class_loader)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::Class* class_loader_class = class_loader->GetClass();
+  return
+      (class_loader_class ==
+          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
+      (class_loader_class ==
+          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
+}
+
+static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                      Handle<mirror::ClassLoader> class_loader)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::Class* class_loader_class = class_loader->GetClass();
+  return class_loader_class ==
+      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+}
+
 bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                                 Thread* self,
                                                 const char* descriptor,
                                                 size_t hash,
                                                 Handle<mirror::ClassLoader> class_loader,
                                                 ObjPtr<mirror::Class>* result) {
-  // Termination case: boot class-loader.
+  // Termination case: boot class loader.
   if (IsBootClassLoader(soa, class_loader.Get())) {
-    // The boot class loader, search the boot class path.
-    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
-    if (pair.second != nullptr) {
-      ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
-      if (klass != nullptr) {
-        *result = EnsureResolved(self, descriptor, klass);
-      } else {
-        *result = DefineClass(self,
-                              descriptor,
-                              hash,
-                              ScopedNullHandle<mirror::ClassLoader>(),
-                              *pair.first,
-                              *pair.second);
-      }
-      if (*result == nullptr) {
-        CHECK(self->IsExceptionPending()) << descriptor;
-        self->ClearException();
-      }
+    *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
+    return true;
+  }
+
+  if (IsPathOrDexClassLoader(soa, class_loader)) {
+    // For regular path or dex class loader the search order is:
+    //    - parent
+    //    - class loader dex files
+
+    // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+    StackHandleScope<1> hs(self);
+    Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+    if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result)) {
+      return false;  // One of the parents is not supported.
+    }
+    if (*result != nullptr) {
+      return true;  // Found the class up the chain.
+    }
+
+    // Search the current class loader classpath.
+    *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+    return true;
+  }
+
+  if (IsDelegateLastClassLoader(soa, class_loader)) {
+    // For delegate last, the search order is:
+    //    - boot class path
+    //    - class loader dex files
+    //    - parent
+    *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
+    if (*result != nullptr) {
+      return true;  // The class is part of the boot class path.
+    }
+
+    *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+    if (*result != nullptr) {
+      return true;  // Found the class in the current class loader
+    }
+
+    // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+    StackHandleScope<1> hs(self);
+    Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+    return FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result);
+  }
+
+  // Unsupported class loader.
+  *result = nullptr;
+  return false;
+}
+
+// Finds the class in the boot class loader.
+// If the class is found the method returns the resolved class. Otherwise it returns null.
+ObjPtr<mirror::Class> ClassLinker::FindClassInBootClassLoaderClassPath(Thread* self,
+                                                                       const char* descriptor,
+                                                                       size_t hash) {
+  ObjPtr<mirror::Class> result = nullptr;
+  ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
+  if (pair.second != nullptr) {
+    ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
+    if (klass != nullptr) {
+      result = EnsureResolved(self, descriptor, klass);
     } else {
-      *result = nullptr;
+      result = DefineClass(self,
+                           descriptor,
+                           hash,
+                           ScopedNullHandle<mirror::ClassLoader>(),
+                           *pair.first,
+                           *pair.second);
     }
-    return true;
-  }
-
-  // Unsupported class-loader?
-  if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
-      class_loader->GetClass()) {
-    // PathClassLoader is the most common case, so it's the one we check first. For secondary dex
-    // files, we also check DexClassLoader here.
-    if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader) !=
-        class_loader->GetClass()) {
-      *result = nullptr;
-      return false;
+    if (result == nullptr) {
+      CHECK(self->IsExceptionPending()) << descriptor;
+      self->ClearException();
     }
   }
+  return result;
+}
 
-  // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
-  StackHandleScope<4> hs(self);
-  Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
-  bool recursive_result = FindClassInBaseDexClassLoader(soa,
-                                                        self,
-                                                        descriptor,
-                                                        hash,
-                                                        h_parent,
-                                                        result);
+ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath(
+    ScopedObjectAccessAlreadyRunnable& soa,
+    const char* descriptor,
+    size_t hash,
+    Handle<mirror::ClassLoader> class_loader) {
+  CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
+      << "Unexpected class loader for descriptor " << descriptor;
 
-  if (!recursive_result) {
-    // Something wrong up the chain.
-    return false;
-  }
-
-  if (*result != nullptr) {
-    // Found the class up the chain.
-    return true;
-  }
-
-  // Handle this step.
-  // Handle as if this is the child PathClassLoader.
-  // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
-  // We need to get the DexPathList and loop through it.
+  Thread* self = soa.Self();
   ArtField* const cookie_field =
       jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
   ArtField* const dex_file_field =
@@ -2489,10 +2611,11 @@
     // DexPathList has an array dexElements of Elements[] which each contain a dex file.
     ObjPtr<mirror::Object> dex_elements_obj =
         jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
-        GetObject(dex_path_list);
+            GetObject(dex_path_list);
     // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
     // at the mCookie which is a DexFile vector.
     if (dex_elements_obj != nullptr) {
+      StackHandleScope<1> hs(self);
       Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
           hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
       for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
@@ -2518,19 +2641,18 @@
                 OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
             if (dex_class_def != nullptr) {
               ObjPtr<mirror::Class> klass = DefineClass(self,
-                                                 descriptor,
-                                                 hash,
-                                                 class_loader,
-                                                 *cp_dex_file,
-                                                 *dex_class_def);
+                                                        descriptor,
+                                                        hash,
+                                                        class_loader,
+                                                        *cp_dex_file,
+                                                        *dex_class_def);
               if (klass == nullptr) {
                 CHECK(self->IsExceptionPending()) << descriptor;
                 self->ClearException();
                 // TODO: Is it really right to break here, and not check the other dex files?
-                return true;
+                return nullptr;
               }
-              *result = klass;
-              return true;
+              return klass;
             }
           }
         }
@@ -2538,9 +2660,7 @@
     }
     self->AssertNoPendingException();
   }
-
-  // Result is still null from the parent call, no need to set it again...
-  return true;
+  return nullptr;
 }
 
 mirror::Class* ClassLinker::FindClass(Thread* self,
@@ -8640,8 +8760,15 @@
   return descriptor;
 }
 
-jobject ClassLinker::CreatePathClassLoader(Thread* self,
-                                           const std::vector<const DexFile*>& dex_files) {
+jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
+                                               const std::vector<const DexFile*>& dex_files,
+                                               jclass loader_class,
+                                               jobject parent_loader) {
+  CHECK(self->GetJniEnv()->IsSameObject(loader_class,
+                                        WellKnownClasses::dalvik_system_PathClassLoader) ||
+        self->GetJniEnv()->IsSameObject(loader_class,
+                                        WellKnownClasses::dalvik_system_DelegateLastClassLoader));
+
   // SOAAlreadyRunnable is protected, and we need something to add a global reference.
   // We could move the jobject to the callers, but all call-sites do this...
   ScopedObjectAccessUnchecked soa(self);
@@ -8677,8 +8804,8 @@
   for (const DexFile* dex_file : dex_files) {
     StackHandleScope<4> hs2(self);
 
-    // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the
-    // oat file but we can leave it null.
+    // CreateWellKnownClassLoader is only used by gtests and compiler.
+    // Index 0 of h_long_array is supposed to be the oat file but we can leave it null.
     Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
         self,
         kDexFileIndexStart + 1));
@@ -8713,36 +8840,44 @@
   // Set elements.
   dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get());
 
-  // Create PathClassLoader.
-  Handle<mirror::Class> h_path_class_class = hs.NewHandle(
-      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
-  Handle<mirror::Object> h_path_class_loader = hs.NewHandle(
-      h_path_class_class->AllocObject(self));
-  DCHECK(h_path_class_loader != nullptr);
+  // Create the class loader..
+  Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class));
+  Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self));
+  DCHECK(h_class_loader != nullptr);
   // Set DexPathList.
   ArtField* path_list_field =
       jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
   DCHECK(path_list_field != nullptr);
-  path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get());
+  path_list_field->SetObject<false>(h_class_loader.Get(), h_dex_path_list.Get());
 
   // Make a pretend boot-classpath.
   // TODO: Should we scan the image?
   ArtField* const parent_field =
       mirror::Class::FindField(self,
-                               h_path_class_loader->GetClass(),
+                               h_class_loader->GetClass(),
                                "parent",
                                "Ljava/lang/ClassLoader;");
   DCHECK(parent_field != nullptr);
-  ObjPtr<mirror::Object> boot_cl =
-      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
-  parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl);
+
+  ObjPtr<mirror::Object> parent = (parent_loader != nullptr)
+      ? soa.Decode<mirror::ClassLoader>(parent_loader)
+      : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
+  parent_field->SetObject<false>(h_class_loader.Get(), parent);
 
   // Make it a global ref and return.
   ScopedLocalRef<jobject> local_ref(
-      soa.Env(), soa.Env()->AddLocalReference<jobject>(h_path_class_loader.Get()));
+      soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get()));
   return soa.Env()->NewGlobalRef(local_ref.get());
 }
 
+jobject ClassLinker::CreatePathClassLoader(Thread* self,
+                                           const std::vector<const DexFile*>& dex_files) {
+  return CreateWellKnownClassLoader(self,
+                                    dex_files,
+                                    WellKnownClasses::dalvik_system_PathClassLoader,
+                                    nullptr);
+}
+
 void ClassLinker::DropFindArrayClassCache() {
   std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
   find_array_class_cache_next_victim_ = 0;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 1e8125e..de1fefd 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -553,8 +553,24 @@
       REQUIRES(!Locks::classlinker_classes_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
+  // Creates a GlobalRef PathClassLoader or DelegateLastClassLoader (specified by loader_class)
+  // that can be used to load classes from the given dex files. The parent of the class loader
+  // will be set to `parent_loader`. If `parent_loader` is null the parent will be
+  // the boot class loader.
+  // If class_loader points to a different class than PathClassLoader or DelegateLastClassLoader
+  // this method will abort.
   // Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
+  jobject CreateWellKnownClassLoader(Thread* self,
+                                     const std::vector<const DexFile*>& dex_files,
+                                     jclass loader_class,
+                                     jobject parent_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_);
+
+  // Calls CreateWellKnownClassLoader(self,
+  //                                  dex_files,
+  //                                  WellKnownClasses::dalvik_system_PathClassLoader,
+  //                                  nullptr)
   jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
@@ -819,6 +835,27 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
 
+  // Finds the class in the classpath of the given class loader. It only searches the class loader
+  // dex files and does not recurse into its parent.
+  // The method checks that the provided class loader is either a PathClassLoader or a
+  // DexClassLoader.
+  // If the class is found the method returns the resolved class. Otherwise it returns null.
+  ObjPtr<mirror::Class> FindClassInBaseDexClassLoaderClassPath(
+          ScopedObjectAccessAlreadyRunnable& soa,
+          const char* descriptor,
+          size_t hash,
+          Handle<mirror::ClassLoader> class_loader)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_);
+
+  // Finds the class in the boot class loader.
+  // If the class is found the method returns the resolved class. Otherwise it returns null.
+  ObjPtr<mirror::Class> FindClassInBootClassLoaderClassPath(Thread* self,
+                                                            const char* descriptor,
+                                                            size_t hash)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_);
+
   // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
   // by the given 'class_loader'. Uses the provided hash for the descriptor.
   mirror::Class* LookupClass(Thread* self,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 684a261..03cc6c5 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1533,4 +1533,110 @@
   ASSERT_TRUE(method1_type.Get() != method2_type.Get());
 }
 
+// Verify that ClassLinker's CreateWellknownClassLoader works as expected
+// by creating a chain of class loaders with various dex files.
+TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) {
+  // LoadDexIn*ClassLoader methods already assert that the parent loader is the expected one.
+  // No need to check again.
+  jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr);
+  jobject class_loader_b = LoadDexInDelegateLastClassLoader("Nested", class_loader_a);
+  jobject class_loader_c = LoadDexInPathClassLoader("MultiDex", class_loader_b);
+  LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c);
+}
+
+class ClassLinkerClassLoaderTest : public ClassLinkerTest {
+ protected:
+  // Verifies that the class identified by the given descriptor is loaded with
+  // the expected_class_loader_obj when search from class_loader_to_search_obj.
+  // When expected_class_loader_obj is null the check will be done against BootClassLoader.
+  void VerifyClassResolution(const std::string& descriptor,
+                             jobject class_loader_to_search_obj,
+                             jobject expected_class_loader_obj,
+                             bool should_find = true) {
+    Thread* self = Thread::Current();
+    ScopedObjectAccess soa(self);
+    StackHandleScope<3> hs(self);
+    Handle<mirror::ClassLoader> class_loader_to_search(
+        hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_to_search_obj)));
+
+    Handle<mirror::Class> klass = hs.NewHandle(
+        class_linker_->FindClass(soa.Self(), descriptor.c_str(), class_loader_to_search));
+
+    if (!should_find) {
+      if (self->IsExceptionPending()) {
+        self->ClearException();
+      }
+      ASSERT_TRUE(klass == nullptr);
+    } else if (expected_class_loader_obj == nullptr) {
+      ASSERT_TRUE(ClassLinker::IsBootClassLoader(soa, klass->GetClassLoader()));
+    } else {
+      ASSERT_TRUE(klass != nullptr) << descriptor;
+      Handle<mirror::ClassLoader> expected_class_loader(
+          hs.NewHandle(soa.Decode<mirror::ClassLoader>(expected_class_loader_obj)));
+      ASSERT_EQ(klass->GetClassLoader(), expected_class_loader.Get());
+    }
+  }
+};
+
+TEST_F(ClassLinkerClassLoaderTest, CreatePathClassLoader) {
+  jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+  VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+  VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+  VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateDelegateLastClassLoader) {
+  jobject class_loader_a = LoadDexInDelegateLastClassLoader("ForClassLoaderA", nullptr);
+  VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+  VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+  VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateClassLoaderChain) {
+  // The chain is
+  //    ClassLoaderA (PathClassLoader, defines: A, AB, AC, AD)
+  //       ^
+  //       |
+  //    ClassLoaderB (DelegateLastClassLoader, defines: B, AB, BC, BD)
+  //       ^
+  //       |
+  //    ClassLoaderC (PathClassLoader, defines: C, AC, BC, CD)
+  //       ^
+  //       |
+  //    ClassLoaderD (DelegateLastClassLoader, defines: D, AD, BD, CD)
+
+  jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+  jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
+  jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b);
+  jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+  // Verify exclusive classes (present in only one class loader).
+  VerifyClassResolution("LDefinedInD;", class_loader_d, class_loader_d);
+  VerifyClassResolution("LDefinedInC;", class_loader_d, class_loader_c);
+  VerifyClassResolution("LDefinedInB;", class_loader_d, class_loader_b);
+  VerifyClassResolution("LDefinedInA;", class_loader_d, class_loader_a);
+
+  // Verify classes that are defined in multiple classloader.
+
+  // Classes defined in B should be found in B even if they are defined in A or C because
+  // B is a DelegateLastClassLoader.
+  VerifyClassResolution("LDefinedInAB;", class_loader_d, class_loader_b);
+  VerifyClassResolution("LDefinedInABC;", class_loader_d, class_loader_b);
+  VerifyClassResolution("LDefinedInBC;", class_loader_d, class_loader_b);
+
+  // Classes defined in D should be found in D even if they are defined in parent class loaders
+  // as well because D is a DelegateLastClassLoader.
+  VerifyClassResolution("LDefinedInAD;", class_loader_d, class_loader_d);
+  VerifyClassResolution("LDefinedInBD;", class_loader_d, class_loader_d);
+  VerifyClassResolution("LDefinedInCD;", class_loader_d, class_loader_d);
+
+
+  // Classes not defined in the DelegateLastClassLoaders (i.e. D or B) should be found
+  // in the top parent.
+  VerifyClassResolution("LDefinedInAC;", class_loader_d, class_loader_a);
+
+  // Sanity check that we don't find an undefined class.
+  VerifyClassResolution("LNotDefined;", class_loader_d, nullptr, /*should_find*/ false);
+}
+
 }  // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 6441a44..659c7e4 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -680,19 +680,66 @@
 }
 
 jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) {
-  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
+  jobject class_loader = LoadDexInPathClassLoader(dex_name, nullptr);
+  Thread::Current()->SetClassLoaderOverride(class_loader);
+  return class_loader;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::string& dex_name,
+                                                             jclass loader_class,
+                                                             jobject parent_loader) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name.c_str());
   std::vector<const DexFile*> class_path;
   CHECK_NE(0U, dex_files.size());
   for (auto& dex_file : dex_files) {
     class_path.push_back(dex_file.get());
     loaded_dex_files_.push_back(std::move(dex_file));
   }
-
   Thread* self = Thread::Current();
-  jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self,
-                                                                                     class_path);
-  self->SetClassLoaderOverride(class_loader);
-  return class_loader;
+  ScopedObjectAccess soa(self);
+
+  jobject result = Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
+      self,
+      class_path,
+      loader_class,
+      parent_loader);
+
+  {
+    // Verify we build the correct chain.
+
+    ObjPtr<mirror::ClassLoader> actual_class_loader = soa.Decode<mirror::ClassLoader>(result);
+    // Verify that the result has the correct class.
+    CHECK_EQ(soa.Decode<mirror::Class>(loader_class), actual_class_loader->GetClass());
+    // Verify that the parent is not null. The boot class loader will be set up as a
+    // proper object.
+    ObjPtr<mirror::ClassLoader> actual_parent(actual_class_loader->GetParent());
+    CHECK(actual_parent != nullptr);
+
+    if (parent_loader != nullptr) {
+      // We were given a parent. Verify that it's what we expect.
+      ObjPtr<mirror::ClassLoader> expected_parent = soa.Decode<mirror::ClassLoader>(parent_loader);
+      CHECK_EQ(expected_parent, actual_parent);
+    } else {
+      // No parent given. The parent must be the BootClassLoader.
+      CHECK(Runtime::Current()->GetClassLinker()->IsBootClassLoader(soa, actual_parent));
+    }
+  }
+
+  return result;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name,
+                                                        jobject parent_loader) {
+  return LoadDexInWellKnownClassLoader(dex_name,
+                                       WellKnownClasses::dalvik_system_PathClassLoader,
+                                       parent_loader);
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name,
+                                                                jobject parent_loader) {
+  return LoadDexInWellKnownClassLoader(dex_name,
+                                       WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+                                       parent_loader);
 }
 
 std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 3b3e6c5..5893573 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -134,10 +134,20 @@
 
   std::unique_ptr<const DexFile> OpenTestDexFile(const char* name);
 
+  // Loads the test dex file identified by the given dex_name into a PathClassLoader.
+  // Returns the created class loader.
   jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
+  // Loads the test dex file identified by the given first_dex_name and second_dex_name
+  // into a PathClassLoader. Returns the created class loader.
   jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  jobject LoadDexInPathClassLoader(const std::string& dex_name, jobject parent_loader);
+  jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
+  jobject LoadDexInWellKnownClassLoader(const std::string& dex_name,
+                                        jclass loader_class,
+                                        jobject parent_loader);
+
   std::string android_data_;
   std::string dalvik_cache_;
 
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 37734e8..6547299 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -376,6 +376,7 @@
     mirror::Class* referring_class = referrer->GetDeclaringClass();
     if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
                                                             resolved_field,
+                                                            referrer->GetDexCache(),
                                                             field_idx))) {
       DCHECK(self->IsExceptionPending());  // Throw exception and unwind.
       return nullptr;  // Failure.
@@ -461,9 +462,11 @@
   } else if (access_check) {
     mirror::Class* methods_class = resolved_method->GetDeclaringClass();
     bool can_access_resolved_method =
-        referrer->GetDeclaringClass()->CheckResolvedMethodAccess<type>(methods_class,
-                                                                       resolved_method,
-                                                                       method_idx);
+        referrer->GetDeclaringClass()->CheckResolvedMethodAccess(methods_class,
+                                                                 resolved_method,
+                                                                 referrer->GetDexCache(),
+                                                                 method_idx,
+                                                                 type);
     if (UNLIKELY(!can_access_resolved_method)) {
       DCHECK(self->IsExceptionPending());  // Throw exception and unwind.
       return nullptr;  // Failure.
@@ -662,7 +665,7 @@
       return nullptr;
     }
   }
-  mirror::Class* referring_class = referrer->GetDeclaringClass();
+  ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
   if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
                !referring_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) ||
                (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) {
@@ -677,18 +680,17 @@
 }
 
 // Fast path method resolution that can't throw exceptions.
+template <InvokeType type, bool access_check>
 inline ArtMethod* FindMethodFast(uint32_t method_idx,
                                  ObjPtr<mirror::Object> this_object,
-                                 ArtMethod* referrer,
-                                 bool access_check,
-                                 InvokeType type) {
+                                 ArtMethod* referrer) {
   ScopedAssertNoThreadSuspension ants(__FUNCTION__);
   if (UNLIKELY(this_object == nullptr && type != kStatic)) {
     return nullptr;
   }
-  mirror::Class* referring_class = referrer->GetDeclaringClass();
-  ArtMethod* resolved_method =
-      referrer->GetDexCache()->GetResolvedMethod(method_idx, kRuntimePointerSize);
+  ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+  ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
+  ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_idx, kRuntimePointerSize);
   if (UNLIKELY(resolved_method == nullptr)) {
     return nullptr;
   }
@@ -698,7 +700,7 @@
     if (UNLIKELY(icce)) {
       return nullptr;
     }
-    mirror::Class* methods_class = resolved_method->GetDeclaringClass();
+    ObjPtr<mirror::Class> methods_class = resolved_method->GetDeclaringClass();
     if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
                  !referring_class->CanAccessMember(methods_class,
                                                    resolved_method->GetAccessFlags()))) {
@@ -713,7 +715,6 @@
     return resolved_method;
   } else if (type == kSuper) {
     // TODO This lookup is rather slow.
-    ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
     dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
     ObjPtr<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType(
         method_type_idx, dex_cache, referrer->GetClassLoader());
@@ -727,7 +728,7 @@
       if (!method_reference_class->IsAssignableFrom(referring_class)) {
         return nullptr;
       }
-      mirror::Class* super_class = referring_class->GetSuperClass();
+      ObjPtr<mirror::Class> super_class = referring_class->GetSuperClass();
       if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) {
         // The super class does not have the method.
         return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index eed08aa..fe85887 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -137,11 +137,10 @@
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // Fast path method resolution that can't throw exceptions.
+template <InvokeType type, bool access_check>
 inline ArtMethod* FindMethodFast(uint32_t method_idx,
                                  ObjPtr<mirror::Object> this_object,
-                                 ArtMethod* referrer,
-                                 bool access_check,
-                                 InvokeType type)
+                                 ArtMethod* referrer)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx,
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2c99aeb..36885d8 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2363,7 +2363,7 @@
 // It is valid to use this, as at the usage points here (returns from C functions) we are assuming
 // to hold the mutator lock (see REQUIRES_SHARED(Locks::mutator_lock_) annotations).
 
-template<InvokeType type, bool access_check>
+template <InvokeType type, bool access_check>
 static TwoWordReturn artInvokeCommon(uint32_t method_idx,
                                      ObjPtr<mirror::Object> this_object,
                                      Thread* self,
@@ -2371,7 +2371,7 @@
   ScopedQuickEntrypointChecks sqec(self);
   DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
   ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
-  ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type);
+  ArtMethod* method = FindMethodFast<type, access_check>(method_idx, this_object, caller_method);
   if (UNLIKELY(method == nullptr)) {
     const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
     uint32_t shorty_len;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index b57e2b2..0687b75 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -951,13 +951,20 @@
   // Test whether to use the interpreter or compiler entrypoint, and save that result to pass to
   // PerformCall. A deoptimization could occur at any time, and we shouldn't change which
   // entrypoint to use once we start building the shadow frame.
-  bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
-      called_method, called_method->GetEntryPointFromQuickCompiledCode());
+
+  // For unstarted runtimes, always use the interpreter entrypoint. This fixes the case where we are
+  // doing cross compilation. Note that GetEntryPointFromQuickCompiledCode doesn't use the image
+  // pointer size here and this may case an overflow if it is called from the compiler. b/62402160
+  const bool use_interpreter_entrypoint = !Runtime::Current()->IsStarted() ||
+      ClassLinker::ShouldUseInterpreterEntrypoint(
+          called_method,
+          called_method->GetEntryPointFromQuickCompiledCode());
   if (LIKELY(code_item != nullptr)) {
     // When transitioning to compiled code, space only needs to be reserved for the input registers.
     // The rest of the frame gets discarded. This also prevents accessing the called method's code
     // item, saving memory by keeping code items of compiled code untouched.
-    if (Runtime::Current()->IsStarted() && !use_interpreter_entrypoint) {
+    if (!use_interpreter_entrypoint) {
+      DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint";
       num_regs = number_of_inputs;
     } else {
       num_regs = code_item->registers_size_;
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 12baf38..419a4db 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -99,7 +99,7 @@
 inline uint32_t Class::GetCopiedMethodsStartOffset() {
   // Object::GetFieldShort returns an int16_t value, but
   // Class::copied_methods_offset_ is an uint16_t value; cast the
-  // latter to int16_t before returning it as an uint32_t value, so
+  // latter to uint16_t before returning it as an uint32_t value, so
   // that uint16_t values between 2^15 and 2^16-1 are correctly
   // handled.
   return static_cast<uint16_t>(
@@ -113,7 +113,7 @@
 inline uint32_t Class::GetVirtualMethodsStartOffset() {
   // Object::GetFieldShort returns an int16_t value, but
   // Class::virtual_method_offset_ is an uint16_t value; cast the
-  // latter to int16_t before returning it as an uint32_t value, so
+  // latter to uint16_t before returning it as an uint32_t value, so
   // that uint16_t values between 2^15 and 2^16-1 are correctly
   // handled.
   return static_cast<uint16_t>(
@@ -410,25 +410,24 @@
   return IsArrayAssignableFromArray(src);
 }
 
-template <bool throw_on_failure, bool use_referrers_cache>
+template <bool throw_on_failure>
 inline bool Class::ResolvedFieldAccessTest(ObjPtr<Class> access_to,
                                            ArtField* field,
-                                           uint32_t field_idx,
-                                           ObjPtr<DexCache> dex_cache) {
-  DCHECK_EQ(use_referrers_cache, dex_cache == nullptr);
+                                           ObjPtr<DexCache> dex_cache,
+                                           uint32_t field_idx) {
+  DCHECK(dex_cache != nullptr);
   if (UNLIKELY(!this->CanAccess(access_to))) {
     // The referrer class can't access the field's declaring class but may still be able
     // to access the field if the FieldId specifies an accessible subclass of the declaring
     // class rather than the declaring class itself.
-    ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
-    dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
+    dex::TypeIndex class_idx = dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
     // The referenced class has already been resolved with the field, but may not be in the dex
     // cache. Use LookupResolveType here to search the class table if it is not in the dex cache.
     // should be no thread suspension due to the class being resolved.
     ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType(
-        *referrer_dex_cache->GetDexFile(),
+        *dex_cache->GetDexFile(),
         class_idx,
-        referrer_dex_cache,
+        dex_cache,
         access_to->GetClassLoader());
     DCHECK(dex_access_to != nullptr);
     if (UNLIKELY(!this->CanAccess(dex_access_to))) {
@@ -447,25 +446,25 @@
   return false;
 }
 
-template <bool throw_on_failure, bool use_referrers_cache, InvokeType throw_invoke_type>
+template <bool throw_on_failure>
 inline bool Class::ResolvedMethodAccessTest(ObjPtr<Class> access_to,
                                             ArtMethod* method,
+                                            ObjPtr<DexCache> dex_cache,
                                             uint32_t method_idx,
-                                            ObjPtr<DexCache> dex_cache) {
-  static_assert(throw_on_failure || throw_invoke_type == kStatic, "Non-default throw invoke type");
-  DCHECK_EQ(use_referrers_cache, dex_cache == nullptr);
+                                            InvokeType throw_invoke_type) {
+  DCHECK(throw_on_failure || throw_invoke_type == kStatic);
+  DCHECK(dex_cache != nullptr);
   if (UNLIKELY(!this->CanAccess(access_to))) {
     // The referrer class can't access the method's declaring class but may still be able
     // to access the method if the MethodId specifies an accessible subclass of the declaring
     // class rather than the declaring class itself.
-    ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
-    dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
+    dex::TypeIndex class_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
     // The referenced class has already been resolved with the method, but may not be in the dex
     // cache.
     ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType(
-        *referrer_dex_cache->GetDexFile(),
+        *dex_cache->GetDexFile(),
         class_idx,
-        referrer_dex_cache,
+        dex_cache,
         access_to->GetClassLoader());
     DCHECK(dex_access_to != nullptr);
     if (UNLIKELY(!this->CanAccess(dex_access_to))) {
@@ -491,30 +490,30 @@
                                           ArtField* field,
                                           ObjPtr<DexCache> dex_cache,
                                           uint32_t field_idx) {
-  return ResolvedFieldAccessTest<false, false>(access_to, field, field_idx, dex_cache);
+  return ResolvedFieldAccessTest<false>(access_to, field, dex_cache, field_idx);
 }
 
 inline bool Class::CheckResolvedFieldAccess(ObjPtr<Class> access_to,
                                             ArtField* field,
+                                            ObjPtr<DexCache> dex_cache,
                                             uint32_t field_idx) {
-  return ResolvedFieldAccessTest<true, true>(access_to, field, field_idx, nullptr);
+  return ResolvedFieldAccessTest<true>(access_to, field, dex_cache, field_idx);
 }
 
 inline bool Class::CanAccessResolvedMethod(ObjPtr<Class> access_to,
                                            ArtMethod* method,
                                            ObjPtr<DexCache> dex_cache,
                                            uint32_t method_idx) {
-  return ResolvedMethodAccessTest<false, false, kStatic>(access_to, method, method_idx, dex_cache);
+  return ResolvedMethodAccessTest<false>(access_to, method, dex_cache, method_idx, kStatic);
 }
 
-template <InvokeType throw_invoke_type>
 inline bool Class::CheckResolvedMethodAccess(ObjPtr<Class> access_to,
                                              ArtMethod* method,
-                                             uint32_t method_idx) {
-  return ResolvedMethodAccessTest<true, true, throw_invoke_type>(access_to,
-                                                                 method,
-                                                                 method_idx,
-                                                                 nullptr);
+                                             ObjPtr<DexCache> dex_cache,
+                                             uint32_t method_idx,
+                                             InvokeType throw_invoke_type) {
+  return ResolvedMethodAccessTest<true>(
+      access_to, method, dex_cache, method_idx, throw_invoke_type);
 }
 
 inline bool Class::IsSubClass(ObjPtr<Class> klass) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 61d6e05..00498bc 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -643,7 +643,10 @@
                               ObjPtr<DexCache> dex_cache,
                               uint32_t field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  bool CheckResolvedFieldAccess(ObjPtr<Class> access_to, ArtField* field, uint32_t field_idx)
+  bool CheckResolvedFieldAccess(ObjPtr<Class> access_to,
+                                ArtField* field,
+                                ObjPtr<DexCache> dex_cache,
+                                uint32_t field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Can this class access a resolved method?
@@ -654,10 +657,11 @@
                                ObjPtr<DexCache> dex_cache,
                                uint32_t method_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  template <InvokeType throw_invoke_type>
   bool CheckResolvedMethodAccess(ObjPtr<Class> access_to,
                                  ArtMethod* resolved_method,
-                                 uint32_t method_idx)
+                                 ObjPtr<DexCache> dex_cache,
+                                 uint32_t method_idx,
+                                 InvokeType throw_invoke_type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsSubClass(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1352,18 +1356,19 @@
                                                                     uint32_t end_offset)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template <bool throw_on_failure, bool use_referrers_cache>
+  template <bool throw_on_failure>
   bool ResolvedFieldAccessTest(ObjPtr<Class> access_to,
                                ArtField* field,
-                               uint32_t field_idx,
-                               ObjPtr<DexCache> dex_cache)
+                               ObjPtr<DexCache> dex_cache,
+                               uint32_t field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  template <bool throw_on_failure, bool use_referrers_cache, InvokeType throw_invoke_type>
+  template <bool throw_on_failure>
   bool ResolvedMethodAccessTest(ObjPtr<Class> access_to,
                                 ArtMethod* resolved_method,
+                                ObjPtr<DexCache> dex_cache,
                                 uint32_t method_idx,
-                                ObjPtr<DexCache> dex_cache)
+                                InvokeType throw_invoke_type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool Implements(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index a110ed7..5b1ba8d 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -106,12 +106,12 @@
 
   EXPECT_NE(klass1->NumStaticFields(), 0u);
   for (ArtField& field : klass2->GetSFields()) {
-    EXPECT_FALSE((
-        klass1->ResolvedFieldAccessTest</*throw_on_failure*/ false,
-            /*use_referrers_cache*/ false>(klass2.Get(),
-                                           &field,
-                                           field.GetDexFieldIndex(),
-                                           klass1->GetDexCache())));
+    EXPECT_FALSE(
+        klass1->ResolvedFieldAccessTest</*throw_on_failure*/ false>(
+            klass2.Get(),
+            &field,
+            klass1->GetDexCache(),
+            field.GetDexFieldIndex()));
   }
 }
 
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 21239fe..505e844 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -913,12 +913,12 @@
   }
 
   static jvmtiError GetBytecodes(jvmtiEnv* env,
-                                 jmethodID method ATTRIBUTE_UNUSED,
-                                 jint* bytecode_count_ptr ATTRIBUTE_UNUSED,
-                                 unsigned char** bytecodes_ptr ATTRIBUTE_UNUSED) {
+                                 jmethodID method,
+                                 jint* bytecode_count_ptr,
+                                 unsigned char** bytecodes_ptr) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_bytecodes);
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::GetBytecodes(env, method, bytecode_count_ptr, bytecodes_ptr);
   }
 
   static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 2d5d527..c63e502 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -216,7 +216,7 @@
     .can_tag_objects                                 = 1,
     .can_generate_field_modification_events          = 1,
     .can_generate_field_access_events                = 1,
-    .can_get_bytecodes                               = 0,
+    .can_get_bytecodes                               = 1,
     .can_get_synthetic_attribute                     = 1,
     .can_get_owned_monitor_info                      = 0,
     .can_get_current_contended_monitor               = 0,
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index beb639e..9b5b964 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -91,6 +91,40 @@
   runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
 }
 
+jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
+                                    jmethodID method,
+                                    jint* size_ptr,
+                                    unsigned char** bytecode_ptr) {
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+  if (art_method->IsNative()) {
+    return ERR(NATIVE_METHOD);
+  }
+
+  if (size_ptr == nullptr || bytecode_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
+  if (code_item == nullptr) {
+    *size_ptr = 0;
+    *bytecode_ptr = nullptr;
+    return OK;
+  }
+  // 2 bytes per instruction for dex code.
+  *size_ptr = code_item->insns_size_in_code_units_ * 2;
+  jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
+  if (err != OK) {
+    return err;
+  }
+  memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
+  return OK;
+}
+
 jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
                                         jmethodID method,
                                         jint* size_ptr) {
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index cc161c8..d95a81b 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -44,6 +44,11 @@
   static void Register(EventHandler* event_handler);
   static void Unregister();
 
+  static jvmtiError GetBytecodes(jvmtiEnv* env,
+                                 jmethodID method,
+                                 jint* count_ptr,
+                                 unsigned char** bytecodes);
+
   static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
 
   static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 4669e16..36ecd33 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -573,7 +573,14 @@
       // Use a large local volatile array to ensure a large frame size. Do not use anything close
       // to a full page for ASAN. It would be nice to ensure the frame size is at most a page, but
       // there is no pragma support for this.
-      volatile char space[kPageSize - 256];
+      // Note: for ASAN we need to shrink the array a bit, as there's other overhead.
+      constexpr size_t kAsanMultiplier =
+#ifdef ADDRESS_SANITIZER
+          2u;
+#else
+          1u;
+#endif
+      volatile char space[kPageSize - (kAsanMultiplier * 256)];
       char sink ATTRIBUTE_UNUSED = space[zero];
       if (reinterpret_cast<uintptr_t>(space) >= target + kPageSize) {
         Touch(target);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 24f194b..8d505e2 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -37,6 +37,7 @@
 jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
 jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
 jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
+jclass WellKnownClasses::dalvik_system_DelegateLastClassLoader;
 jclass WellKnownClasses::dalvik_system_DexClassLoader;
 jclass WellKnownClasses::dalvik_system_DexFile;
 jclass WellKnownClasses::dalvik_system_DexPathList;
@@ -270,6 +271,7 @@
       CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
   dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
   dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
+  dalvik_system_DelegateLastClassLoader = CacheClass(env, "dalvik/system/DelegateLastClassLoader");
   dalvik_system_DexClassLoader = CacheClass(env, "dalvik/system/DexClassLoader");
   dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
   dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c184731..c5a16c1 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -47,6 +47,7 @@
   static jclass dalvik_annotation_optimization_CriticalNative;
   static jclass dalvik_annotation_optimization_FastNative;
   static jclass dalvik_system_BaseDexClassLoader;
+  static jclass dalvik_system_DelegateLastClassLoader;
   static jclass dalvik_system_DexClassLoader;
   static jclass dalvik_system_DexFile;
   static jclass dalvik_system_DexPathList;
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 9072c8b..3cfe006 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -65,7 +65,8 @@
         String line;
         int count = 0;
         while ((line = reader.readLine()) != null) {
-            if (line.contains("@141-class-unload-ex.jar")) {
+            if (line.contains("141-class-unload-ex.odex") ||
+                line.contains("141-class-unload-ex.vdex")) {
                 System.out.println(line);
                 ++count;
             }
diff --git a/test/1901-get-bytecodes/bytecodes.cc b/test/1901-get-bytecodes/bytecodes.cc
new file mode 100644
index 0000000..edcb734
--- /dev/null
+++ b/test/1901-get-bytecodes/bytecodes.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 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 <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1901Bytecodes {
+
+extern "C" JNIEXPORT jbyteArray JNICALL Java_art_Test1901_getBytecodes(JNIEnv* env,
+                                                                       jclass,
+                                                                       jobject jmethod) {
+  jmethodID method = env->FromReflectedMethod(jmethod);
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  unsigned char* bytecodes = nullptr;
+  jint bytecodes_size = 0;
+  if (JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->GetBytecodes(method, &bytecodes_size, &bytecodes))) {
+    return nullptr;
+  }
+  jbyteArray out = env->NewByteArray(bytecodes_size);
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  } else if (bytecodes_size == 0) {
+    return out;
+  }
+  jbyte* bytes = env->GetByteArrayElements(out, /* is_copy */ nullptr);
+  memcpy(bytes, bytecodes, bytecodes_size);
+  env->ReleaseByteArrayElements(out, bytes, 0);
+  return out;
+}
+
+}  // namespace Test1901Bytecodes
+}  // namespace art
diff --git a/test/1901-get-bytecodes/expected.txt b/test/1901-get-bytecodes/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/1901-get-bytecodes/expected.txt
diff --git a/test/1901-get-bytecodes/info.txt b/test/1901-get-bytecodes/info.txt
new file mode 100644
index 0000000..c8c9189
--- /dev/null
+++ b/test/1901-get-bytecodes/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetBytecodes function works as expected.
diff --git a/test/1901-get-bytecodes/run b/test/1901-get-bytecodes/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1901-get-bytecodes/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+./default-run "$@" --jvmti
diff --git a/test/1901-get-bytecodes/src/Main.java b/test/1901-get-bytecodes/src/Main.java
new file mode 100644
index 0000000..d37fcdb
--- /dev/null
+++ b/test/1901-get-bytecodes/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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) throws Exception {
+    art.Test1901.run();
+  }
+}
diff --git a/test/1901-get-bytecodes/src/art/Test1901.java b/test/1901-get-bytecodes/src/art/Test1901.java
new file mode 100644
index 0000000..6940491
--- /dev/null
+++ b/test/1901-get-bytecodes/src/art/Test1901.java
@@ -0,0 +1,147 @@
+/*
+ * 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.Constructor;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Base64;
+
+public class Test1901 {
+  // Class & Dex file containing the following class.
+  // Using this representation to prevent any changes to the compiler or the file formats from
+  // changing the output of this test.
+  //
+  // package art;
+  // public class Target {
+  //   public void doNothing() {
+  //     return;
+  //   }
+  //
+  //   public static void staticNothing() {
+  //     return;
+  //   }
+  //
+  //   public void sayHi() {
+  //     System.out.println("hello");
+  //   }
+  // }
+  public static byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAlkb05vdGhpbmcBAA1zdGF0aWNOb3RoaW5nAQAFc2F5SGkBAApT" +
+    "b3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMAAcACAcAGAwAGQAaAQAFaGVsbG8HABsMABwAHQEACmFy" +
+    "dC9UYXJnZXQBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" +
+    "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" +
+    "YXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAAEAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGx" +
+    "AAAAAQAKAAAABgABAAAAAgABAAsACAABAAkAAAAZAAAAAQAAAAGxAAAAAQAKAAAABgABAAAABAAJ" +
+    "AAwACAABAAkAAAAZAAAAAAAAAAGxAAAAAQAKAAAABgABAAAACAABAA0ACAABAAkAAAAlAAIAAQAA" +
+    "AAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAMAAgADQABAA4AAAACAA8=");
+  public static byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAbYkxNjiZ8a+fNWF4smR2+uXbrq88/FNoYAwAAcAAAAHhWNBIAAAAAAAAAAHgCAAAP" +
+    "AAAAcAAAAAYAAACsAAAAAgAAAMQAAAABAAAA3AAAAAYAAADkAAAAAQAAABQBAADkAQAANAEAAJoB" +
+    "AACiAQAAsAEAAMcBAADbAQAA7wEAAAMCAAAQAgAAEwIAABcCAAAiAgAAKQIAAC4CAAA3AgAAPgIA" +
+    "AAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAcAAAAFAAAAAAAAAAgAAAAFAAAAlAEAAAQAAQALAAAA" +
+    "AAAAAAAAAAAAAAAACQAAAAAAAAANAAAAAAAAAA4AAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAAC" +
+    "AAAAAAAAAAYAAAAAAAAAYgIAAAAAAAABAAEAAQAAAE0CAAAEAAAAcBAFAAAADgAAAAAAAAAAAFIC" +
+    "AAABAAAADgAAAAEAAQAAAAAAVwIAAAEAAAAOAAAAAwABAAIAAABcAgAACAAAAGIAAAAaAQoAbiAE" +
+    "ABAADgABAAAAAwAGPGluaXQ+AAxMYXJ0L1RhcmdldDsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+    "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+    "OwALVGFyZ2V0LmphdmEAAVYAAlZMAAlkb05vdGhpbmcABWhlbGxvAANvdXQAB3ByaW50bG4ABXNh" +
+    "eUhpAA1zdGF0aWNOb3RoaW5nAAIABw4ACAAHDgAEAAcOAAwABw54AAAAAgIAgYAEtAIDCcwCAQHg" +
+    "AgEB9AINAAAAAAAAAAEAAAAAAAAAAQAAAA8AAABwAAAAAgAAAAYAAACsAAAAAwAAAAIAAADEAAAA" +
+    "BAAAAAEAAADcAAAABQAAAAYAAADkAAAABgAAAAEAAAAUAQAAASAAAAQAAAA0AQAAARAAAAEAAACU" +
+    "AQAAAiAAAA8AAACaAQAAAyAAAAQAAABNAgAAACAAAAEAAABiAgAAABAAAAEAAAB4AgAA");
+
+  public static byte[][] DO_NOTHING_BYTECODES = new byte[][] {
+    // Dex Bytecodes for doNothing
+    // 0e00           |0000: return-void
+    new byte[] { 14, 0 },
+    // Java bytecodes
+    // 0: return
+    new byte[] { -79 },
+  };
+
+  public static byte[][] STATIC_NOTHING_BYTECODES = new byte[][] {
+    // Dex Bytecodes for staticNothing
+    // 0e00           |0000: return-void
+    new byte[] { 14, 0 },
+    // Java bytecodes
+    // 0: return
+    new byte[] { -79 },
+  };
+
+  public static byte[][] SAY_HI_NOTHING_BYTECODES = new byte[][] {
+    // Dex Bytecodes for sayHi
+    // 6200 0000      |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
+    // 1a01 0a00      |0002: const-string v1, "hello" // string@000a
+    // 6e20 0400 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0004
+    // 0e00           |0007: return-void
+    new byte[] { 98, 0, 0, 0, 26, 1, 10, 0, 110, 32, 4, 0, 16, 0, 14, 0 },
+    // Java bytecodes
+    // 0: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
+    // 3: ldc           #3  // String hello
+    // 5: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
+    // 8: return
+    new byte[] { -78, 0, 2, 18, 3, -74, 0, 4, -79 },
+  };
+
+  public static ClassLoader getClassLoader() throws Exception {
+    try {
+      Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+      Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+      // We are on art since we got the InMemoryDexClassLoader.
+      return (ClassLoader)ctor.newInstance(
+          ByteBuffer.wrap(DEX_BYTES), Test1901.class.getClassLoader());
+    } catch (ClassNotFoundException e) {
+      // Running on RI.
+      return new ClassLoader(Test1901.class.getClassLoader()) {
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+          if (name.equals("art.Target")) {
+            return defineClass(name, CLASS_BYTES, 0, CLASS_BYTES.length);
+          } else {
+            return super.findClass(name);
+          }
+        }
+      };
+    }
+  }
+
+  public static void CheckMethodBytes(Method m, byte[][] possible_bytecodes) {
+    byte[] real_codes = getBytecodes(m);
+    for (byte[] pos : possible_bytecodes) {
+      if (Arrays.equals(pos, real_codes)) {
+        return;
+      }
+    }
+    System.out.println("Unexpected bytecodes for " + m);
+    System.out.println("Received: " + Arrays.toString(real_codes));
+    System.out.println("Expected one of:");
+    for (byte[] pos : possible_bytecodes) {
+      System.out.println("\t" + Arrays.toString(pos));
+    }
+  }
+
+  public static void run() throws Exception {
+    Class<?> target = getClassLoader().loadClass("art.Target");
+    CheckMethodBytes(target.getDeclaredMethod("doNothing"), DO_NOTHING_BYTECODES);
+    CheckMethodBytes(target.getDeclaredMethod("staticNothing"), STATIC_NOTHING_BYTECODES);
+    CheckMethodBytes(target.getDeclaredMethod("sayHi"), SAY_HI_NOTHING_BYTECODES);
+  }
+
+  public static native byte[] getBytecodes(Method m);
+}
diff --git a/test/596-app-images/src/Main.java b/test/596-app-images/src/Main.java
index 8ee3c88..88d95f4 100644
--- a/test/596-app-images/src/Main.java
+++ b/test/596-app-images/src/Main.java
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
 class Main {
   static class Inner {
     final public static int abc = 10;
@@ -46,13 +50,76 @@
     if (!checkInitialized(StaticFieldsInit.class))
       System.out.println("StaticFieldsInit class is not initialized!");
 
-    if (checkInitialized(StaticInternString.class))
-      System.out.println("StaticInternString class is initialized!");
+    if (!checkInitialized(StaticInternString.class))
+      System.out.println("StaticInternString class is not initialized!");
+
+    StringBuffer sb = new StringBuffer();
+    sb.append("java.");
+    sb.append("abc.");
+    sb.append("Action");
+
+    String tmp = sb.toString();
+    String intern = tmp.intern();
+
+    assertNotEqual(tmp, intern, "Dynamically constructed String, not interned.");
+    assertEqual(intern, StaticInternString.intent, "Static encoded literal String not interned.");
+    assertEqual(BootInternedString.boot, BootInternedString.boot.intern(),
+        "Static encoded literal String not moved back to runtime intern table.");
+
+    try {
+      Field f = StaticInternString.class.getDeclaredField("intent");
+      assertEqual(intern, f.get(null), "String Literals are not interned properly.");
+
+    } catch (Exception e) {
+      System.out.println("Exception");
+    }
+
+    assertEqual(StaticInternString.getIntent(), StaticInternString2.getIntent(),
+        "String Literals are not intenred properly, App image static strings duplicated.");
+
+    // reload the class StaticInternString, check whether static strings interned properly
+    final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar";
+    final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+
+    try {
+      Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+      if (pathClassLoader == null) {
+        throw new AssertionError("Counldn't find path class loader class");
+      }
+      Constructor<?> ctor =
+          pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
+      ClassLoader loader = (ClassLoader) ctor.newInstance(
+          DEX_FILE, LIBRARY_SEARCH_PATH, null);
+
+      Class<?> staticInternString = loader.loadClass("StaticInternString");
+
+      if (!checkAppImageContains(staticInternString)) {
+        System.out.println("Not loaded again.");
+      }
+      Method getIntent = staticInternString.getDeclaredMethod("getIntent");
+
+      assertEqual(StaticInternString.getIntent(), getIntent.invoke(staticInternString),
+          "Dynamically loaded app image's literal strings not interned properly.");
+    } catch (Exception e) {
+      e.printStackTrace(System.out);
+    }
+
   }
 
   public static native boolean checkAppImageLoaded();
   public static native boolean checkAppImageContains(Class<?> klass);
   public static native boolean checkInitialized(Class<?> klass);
+
+  public static void assertEqual(Object a, Object b, String msg) {
+    if (a != b)
+      System.out.println(msg);
+  }
+
+  public static void assertNotEqual(Object a, Object b, String msg) {
+    if (a == b)
+      System.out.println(msg);
+  }
+
 }
 
 class StaticFields{
@@ -68,6 +135,21 @@
 }
 
 class StaticInternString {
-  final public static String intern = "java.abc.Action";
+  final public static String intent = "java.abc.Action";
+  static public String getIntent() {
+    return intent;
+  }
+}
+
+class BootInternedString {
+  final public static String boot = "double";
+}
+
+class StaticInternString2 {
+  final public static String intent = "java.abc.Action";
+
+  static String getIntent() {
+    return intent;
+  }
 }
 
diff --git a/test/Android.bp b/test/Android.bp
index f893531..7d7afa5 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -286,6 +286,7 @@
         "992-source-data/source_file.cc",
         "993-breakpoints/breakpoints.cc",
         "996-breakpoint-obsolete/obsolete_breakpoints.cc",
+        "1901-get-bytecodes/bytecodes.cc",
     ],
     shared_libs: [
         "libbase",
@@ -445,6 +446,7 @@
         "art_debug_defaults",
         "art_defaults",
     ],
+    header_libs: ["libnativebridge-dummy-headers"],
     srcs: ["115-native-bridge/nativebridge.cc"],
     target: {
         android: {
diff --git a/test/ForClassLoaderA/Classes.java b/test/ForClassLoaderA/Classes.java
new file mode 100644
index 0000000..a65ef64
--- /dev/null
+++ b/test/ForClassLoaderA/Classes.java
@@ -0,0 +1,31 @@
+/*
+ * 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 DefinedInA {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInAD {
+}
+
diff --git a/test/ForClassLoaderB/Classes.java b/test/ForClassLoaderB/Classes.java
new file mode 100644
index 0000000..8c85ed5
--- /dev/null
+++ b/test/ForClassLoaderB/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * 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 DefinedInB {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInBD {
+}
diff --git a/test/ForClassLoaderC/Classes.java b/test/ForClassLoaderC/Classes.java
new file mode 100644
index 0000000..7b9e83f
--- /dev/null
+++ b/test/ForClassLoaderC/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * 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 DefinedInC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInCD {
+}
diff --git a/test/ForClassLoaderD/Classes.java b/test/ForClassLoaderD/Classes.java
new file mode 100644
index 0000000..b34177f
--- /dev/null
+++ b/test/ForClassLoaderD/Classes.java
@@ -0,0 +1,27 @@
+/*
+ * 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 DefinedInD {
+}
+
+class DefinedInAD {
+}
+
+class DefinedInBD {
+}
+
+class DefinedInCD {
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index d3d92c6..7b801e2 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -645,6 +645,11 @@
         "env_vars": {"SANITIZE_HOST": "address"}
     },
     {
+        "tests": "141-class-unload",
+        "description": "Idk why this fails",
+        "env_vars": {"SANITIZE_HOST": "address"}
+    },
+    {
         "tests": ["988-method-trace"],
         "variant": "redefine-stress | jvmti-stress",
         "description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."