Merge "Add VisitNewArray() in lsa."
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 0b1bce6..bd530ac 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1935,14 +1935,12 @@
   // time. So instead we assume these classes still need to be verified at
   // runtime.
   for (const DexFile* dex_file : dex_files) {
-    // Fetch the list of unverified classes and turn it into a set for faster
-    // lookups.
-    const std::vector<dex::TypeIndex>& unverified_classes =
+    // Fetch the list of unverified classes.
+    const std::set<dex::TypeIndex>& unverified_classes =
         verifier_deps->GetUnverifiedClasses(*dex_file);
-    std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
     for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
       const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
-      if (set.find(class_def.class_idx_) == set.end()) {
+      if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) {
         if (compiler_only_verifies) {
           // Just update the compiled_classes_ map. The compiler doesn't need to resolve
           // the type.
diff --git a/compiler/image_test.h b/compiler/image_test.h
index 57d0987..daa4b11 100644
--- a/compiler/image_test.h
+++ b/compiler/image_test.h
@@ -214,7 +214,8 @@
                                                       /*compile_app_image*/false,
                                                       storage_mode,
                                                       oat_filename_vector,
-                                                      dex_file_to_oat_index_map));
+                                                      dex_file_to_oat_index_map,
+                                                      /*dirty_image_objects*/nullptr));
   {
     {
       jobject class_loader = nullptr;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index fc7cd01..9e4971c 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -579,7 +579,12 @@
         }
       }
 
-      if (klass->GetStatus() == Class::kStatusInitialized) {
+      // Move known dirty objects into their own sections. This includes:
+      //   - classes with dirty static fields.
+      if (dirty_image_objects_ != nullptr &&
+          dirty_image_objects_->find(klass->PrettyDescriptor()) != dirty_image_objects_->end()) {
+        bin = kBinKnownDirty;
+      } else if (klass->GetStatus() == Class::kStatusInitialized) {
         bin = kBinClassInitialized;
 
         // If the class's static fields are all final, put it into a separate bin
@@ -770,18 +775,18 @@
       *result_ = true;
     }
 
-    // Record the object visited in case of circular reference.
-    visited_->emplace(ref);
     if (ref->IsClass()) {
       *result_ = *result_ ||
           image_writer_->PruneAppImageClassInternal(ref->AsClass(), early_exit_, visited_);
     } else {
+      // Record the object visited in case of circular reference.
+      visited_->emplace(ref);
       *result_ = *result_ ||
           image_writer_->PruneAppImageClassInternal(klass, early_exit_, visited_);
       ref->VisitReferences(*this, *this);
+      // Clean up before exit for next call of this function.
+      visited_->erase(ref);
     }
-    // Clean up before exit for next call of this function.
-    visited_->erase(ref);
   }
 
   ALWAYS_INLINE void operator() (ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
@@ -2774,7 +2779,8 @@
     bool compile_app_image,
     ImageHeader::StorageMode image_storage_mode,
     const std::vector<const char*>& oat_filenames,
-    const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map)
+    const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+    const std::unordered_set<std::string>* dirty_image_objects)
     : compiler_driver_(compiler_driver),
       global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
       image_objects_offset_begin_(0),
@@ -2786,7 +2792,8 @@
       clean_methods_(0u),
       image_storage_mode_(image_storage_mode),
       oat_filenames_(oat_filenames),
-      dex_file_oat_index_map_(dex_file_oat_index_map) {
+      dex_file_oat_index_map_(dex_file_oat_index_map),
+      dirty_image_objects_(dirty_image_objects) {
   CHECK_NE(image_begin, 0U);
   std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
   CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 34bbbad..866e204 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -75,7 +75,8 @@
               bool compile_app_image,
               ImageHeader::StorageMode image_storage_mode,
               const std::vector<const char*>& oat_filenames,
-              const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map);
+              const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+              const std::unordered_set<std::string>* dirty_image_objects);
 
   bool PrepareImageAddressSpace();
 
@@ -159,6 +160,7 @@
   // Classify different kinds of bins that objects end up getting packed into during image writing.
   // Ordered from dirtiest to cleanest (until ArtMethods).
   enum Bin {
+    kBinKnownDirty,               // Known dirty objects from --dirty-image-objects list
     kBinMiscDirty,                // Dex caches, object locks, etc...
     kBinClassVerified,            // Class verified, but initializers haven't been run
     // Unknown mix of clean/dirty:
@@ -599,6 +601,9 @@
   // Map of dex files to the indexes of oat files that they were compiled into.
   const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_;
 
+  // Set of objects known to be dirty in the image. Can be nullptr if there are none.
+  const std::unordered_set<std::string>* dirty_image_objects_;
+
   class ComputeLazyFieldsForClassesVisitor;
   class FixupClassVisitor;
   class FixupRootVisitor;
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 274f065..0ef7dcd 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -40,6 +40,8 @@
   instruction->RemoveAsUserOfAllInputs();
   instruction->RemoveEnvironmentUsers();
   instruction->GetBlock()->RemoveInstructionOrPhi(instruction, /*ensure_safety=*/ false);
+  RemoveEnvironmentUses(instruction);
+  ResetEnvironmentInputRecords(instruction);
 }
 
 // Detect a goto block and sets succ to the single successor.
@@ -267,6 +269,21 @@
   return instruction;
 }
 
+// Check that instructions from the induction sets are fully removed: have no uses
+// and no other instructions use them.
+static bool CheckInductionSetFullyRemoved(ArenaSet<HInstruction*>* iset) {
+  for (HInstruction* instr : *iset) {
+    if (instr->GetBlock() != nullptr ||
+        !instr->GetUses().empty() ||
+        !instr->GetEnvUses().empty() ||
+        HasEnvironmentUsedByOthers(instr)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 //
 // Class methods.
 //
@@ -448,6 +465,9 @@
         for (HInstruction* i : *iset_) {
           RemoveFromCycle(i);
         }
+
+        // Check that there are no records of the deleted instructions.
+        DCHECK(CheckInductionSetFullyRemoved(iset_));
         simplified_ = true;
       }
     }
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ca48e08..3a1864b 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -90,7 +90,8 @@
   }
 }
 
-static void RemoveEnvironmentUses(HInstruction* instruction) {
+// Remove the environment use records of the instruction for users.
+void RemoveEnvironmentUses(HInstruction* instruction) {
   for (HEnvironment* environment = instruction->GetEnvironment();
        environment != nullptr;
        environment = environment->GetParent()) {
@@ -102,6 +103,35 @@
   }
 }
 
+// Return whether the instruction has an environment and it's used by others.
+bool HasEnvironmentUsedByOthers(HInstruction* instruction) {
+  for (HEnvironment* environment = instruction->GetEnvironment();
+       environment != nullptr;
+       environment = environment->GetParent()) {
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      HInstruction* user = environment->GetInstructionAt(i);
+      if (user != nullptr) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+// Reset environment records of the instruction itself.
+void ResetEnvironmentInputRecords(HInstruction* instruction) {
+  for (HEnvironment* environment = instruction->GetEnvironment();
+       environment != nullptr;
+       environment = environment->GetParent()) {
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      DCHECK(environment->GetHolder() == instruction);
+      if (environment->GetInstructionAt(i) != nullptr) {
+        environment->SetRawEnvAt(i, nullptr);
+      }
+    }
+  }
+}
+
 static void RemoveAsUser(HInstruction* instruction) {
   instruction->RemoveAsUserOfAllInputs();
   RemoveEnvironmentUses(instruction);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index fa29378..e443142 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -7059,6 +7059,10 @@
   return instruction;
 }
 
+void RemoveEnvironmentUses(HInstruction* instruction);
+bool HasEnvironmentUsedByOthers(HInstruction* instruction);
+void ResetEnvironmentInputRecords(HInstruction* instruction);
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index aa42fd6..7c6b69f 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -208,8 +208,8 @@
 
 void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   if (invoke->IsStaticWithExplicitClinitCheck()) {
-    HLoadClass* last_input = invoke->GetInputs().back()->AsLoadClass();
-    DCHECK(last_input != nullptr)
+    HInstruction* last_input = invoke->GetInputs().back();
+    DCHECK(last_input->IsLoadClass())
         << "Last input is not HLoadClass. It is " << last_input->DebugName();
 
     // Detach the explicit class initialization check from the invoke.
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index e9f3f80..6538925 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -229,8 +229,7 @@
         hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
     MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
     for (const DexFile* dex_file : dex_files_) {
-      const std::vector<dex::TypeIndex>& unverified_classes = deps.GetUnverifiedClasses(*dex_file);
-      std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
+      const std::set<dex::TypeIndex>& unverified_classes = deps.GetUnverifiedClasses(*dex_file);
       for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
         const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
         const char* descriptor = dex_file->GetClassDescriptor(class_def);
@@ -238,7 +237,7 @@
         if (cls == nullptr) {
           CHECK(soa.Self()->IsExceptionPending());
           soa.Self()->ClearException();
-        } else if (set.find(class_def.class_idx_) == set.end()) {
+        } else if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) {
           ASSERT_EQ(cls->GetStatus(), mirror::Class::kStatusVerified);
         } else {
           ASSERT_LT(cls->GetStatus(), mirror::Class::kStatusVerified);
@@ -1145,6 +1144,39 @@
   ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;"));
 }
 
+TEST_F(VerifierDepsTest, UnverifiedOrder) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject loader = LoadDex("VerifierDeps");
+  std::vector<const DexFile*> dex_files = GetDexFiles(loader);
+  ASSERT_GT(dex_files.size(), 0u);
+  const DexFile* dex_file = dex_files[0];
+  VerifierDeps deps1(dex_files);
+  Thread* const self = Thread::Current();
+  ASSERT_TRUE(self->GetVerifierDeps() == nullptr);
+  self->SetVerifierDeps(&deps1);
+  deps1.MaybeRecordVerificationStatus(*dex_file,
+                                      dex::TypeIndex(0),
+                                      verifier::FailureKind::kHardFailure);
+  deps1.MaybeRecordVerificationStatus(*dex_file,
+                                      dex::TypeIndex(1),
+                                      verifier::FailureKind::kHardFailure);
+  VerifierDeps deps2(dex_files);
+  self->SetVerifierDeps(nullptr);
+  self->SetVerifierDeps(&deps2);
+  deps2.MaybeRecordVerificationStatus(*dex_file,
+                                      dex::TypeIndex(1),
+                                      verifier::FailureKind::kHardFailure);
+  deps2.MaybeRecordVerificationStatus(*dex_file,
+                                      dex::TypeIndex(0),
+                                      verifier::FailureKind::kHardFailure);
+  self->SetVerifierDeps(nullptr);
+  std::vector<uint8_t> buffer1;
+  deps1.Encode(dex_files, &buffer1);
+  std::vector<uint8_t> buffer2;
+  deps2.Encode(dex_files, &buffer2);
+  EXPECT_EQ(buffer1, buffer2);
+}
+
 TEST_F(VerifierDepsTest, VerifyDeps) {
   VerifyDexFile();
 
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 0826fa1..ff193e9 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -355,6 +355,9 @@
   UsageError("");
   UsageError("  --debuggable: Produce code debuggable with Java debugger.");
   UsageError("");
+  UsageError("  --avoid-storing-invocation: Avoid storing the invocation args in the key value");
+  UsageError("      store. Used to test determinism with different args.");
+  UsageError("");
   UsageError("  --runtime-arg <argument>: used to specify various arguments for the runtime,");
   UsageError("      such as initial heap size, maximum heap size, and verbose output.");
   UsageError("      Use a separate --runtime-arg switch for each argument.");
@@ -408,17 +411,17 @@
   UsageError("");
   UsageError("  --class-loader-context=<string spec>: a string specifying the intended");
   UsageError("      runtime loading context for the compiled dex files.");
-  UsageError("      ");
+  UsageError("");
   UsageError("      It describes how the class loader chain should be built in order to ensure");
   UsageError("      classes are resolved during dex2aot as they would be resolved at runtime.");
   UsageError("      This spec will be encoded in the oat file. If at runtime the dex file is");
   UsageError("      loaded in a different context, the oat file will be rejected.");
-  UsageError("      ");
+  UsageError("");
   UsageError("      The chain is interpreted in the natural 'parent order', meaning that class");
   UsageError("      loader 'i+1' will be the parent of class loader 'i'.");
   UsageError("      The compilation sources will be appended to the classpath of the first class");
   UsageError("      loader.");
-  UsageError("      ");
+  UsageError("");
   UsageError("      E.g. if the context is 'PCL[lib1.dex];DLC[lib2.dex]' and ");
   UsageError("      --dex-file=src.dex then dex2oat will setup a PathClassLoader with classpath ");
   UsageError("      'lib1.dex:src.dex' and set its parent to a DelegateLastClassLoader with ");
@@ -428,9 +431,12 @@
   UsageError("      with --dex-file are found in the classpath. The source dex files will be");
   UsageError("      removed from any class loader's classpath possibly resulting in empty");
   UsageError("      class loaders.");
-  UsageError("      ");
+  UsageError("");
   UsageError("      Example: --class-loader-context=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]");
   UsageError("");
+  UsageError("  --dirty-image-objects=<directory-path>: list of known dirty objects in the image.");
+  UsageError("      The image writer will group them together.");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -593,6 +599,7 @@
       compiled_methods_zip_filename_(nullptr),
       compiled_methods_filename_(nullptr),
       passes_to_run_filename_(nullptr),
+      dirty_image_objects_filename_(nullptr),
       multi_image_(false),
       is_host_(false),
       elf_writers_(),
@@ -607,6 +614,7 @@
       dump_passes_(false),
       dump_timing_(false),
       dump_slow_timing_(kIsDebugBuild),
+      avoid_storing_invocation_(false),
       swap_fd_(kInvalidFd),
       app_image_fd_(kInvalidFd),
       profile_file_fd_(kInvalidFd),
@@ -1129,14 +1137,16 @@
 
   void InsertCompileOptions(int argc, char** argv) {
     std::ostringstream oss;
-    for (int i = 0; i < argc; ++i) {
-      if (i > 0) {
-        oss << ' ';
+    if (!avoid_storing_invocation_) {
+      for (int i = 0; i < argc; ++i) {
+        if (i > 0) {
+          oss << ' ';
+        }
+        oss << argv[i];
       }
-      oss << argv[i];
+      key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
+      oss.str("");  // Reset.
     }
-    key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
-    oss.str("");  // Reset.
     oss << kRuntimeISA;
     key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str());
     key_value_store_->Put(
@@ -1267,6 +1277,8 @@
         dump_passes_ = true;
       } else if (option == "--dump-stats") {
         dump_stats_ = true;
+      } else if (option == "--avoid-storing-invocation") {
+        avoid_storing_invocation_ = true;
       } else if (option.starts_with("--swap-file=")) {
         swap_file_name_ = option.substr(strlen("--swap-file=")).data();
       } else if (option.starts_with("--swap-fd=")) {
@@ -1304,9 +1316,11 @@
       } else if (option.starts_with("--class-loader-context=")) {
         class_loader_context_ = ClassLoaderContext::Create(
             option.substr(strlen("--class-loader-context=")).data());
-        if (class_loader_context_== nullptr) {
+        if (class_loader_context_ == nullptr) {
           Usage("Option --class-loader-context has an incorrect format: %s", option.data());
         }
+      } else if (option.starts_with("--dirty-image-objects=")) {
+        dirty_image_objects_filename_ = option.substr(strlen("--dirty-image-objects=")).data();
       } else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
         Usage("Unknown argument %s", option.data());
       }
@@ -1508,7 +1522,8 @@
   dex2oat::ReturnCode Setup() {
     TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
 
-    if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
+    if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods() ||
+        !PrepareDirtyObjects()) {
       return dex2oat::ReturnCode::kOther;
     }
 
@@ -1569,20 +1584,12 @@
       }
 
       // Open dex files for class path.
+
       if (class_loader_context_ == nullptr) {
-        // TODO(calin): Temporary workaround while we transition to use
-        // --class-loader-context instead of --runtime-arg -cp
-        if (runtime_->GetClassPathString().empty()) {
-          class_loader_context_ = std::unique_ptr<ClassLoaderContext>(
-              new ClassLoaderContext());
-        } else {
-          std::string spec = runtime_->GetClassPathString() == OatFile::kSpecialSharedLibrary
-              ? OatFile::kSpecialSharedLibrary
-              : "PCL[" + runtime_->GetClassPathString() + "]";
-          class_loader_context_ = ClassLoaderContext::Create(spec);
-        }
+        // If no context was specified use the default one (which is an empty PathClassLoader).
+        class_loader_context_ = std::unique_ptr<ClassLoaderContext>(ClassLoaderContext::Default());
       }
-      CHECK(class_loader_context_ != nullptr);
+
       DCHECK_EQ(oat_writers_.size(), 1u);
 
       // Note: Ideally we would reject context where the source dex files are also
@@ -2002,7 +2009,8 @@
                                           IsAppImage(),
                                           image_storage_mode_,
                                           oat_filenames_,
-                                          dex_file_oat_index_map_));
+                                          dex_file_oat_index_map_,
+                                          dirty_image_objects_.get()));
 
       // We need to prepare method offsets in the image address space for direct method patching.
       TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
@@ -2428,6 +2436,22 @@
     return true;
   }
 
+  bool PrepareDirtyObjects() {
+    if (dirty_image_objects_filename_ != nullptr) {
+      dirty_image_objects_.reset(ReadCommentedInputFromFile<std::unordered_set<std::string>>(
+          dirty_image_objects_filename_,
+          nullptr));
+      if (dirty_image_objects_ == nullptr) {
+        LOG(ERROR) << "Failed to create list of dirty objects from '"
+            << dirty_image_objects_filename_ << "'";
+        return false;
+      }
+    } else {
+      dirty_image_objects_.reset(nullptr);
+    }
+    return true;
+  }
+
   void PruneNonExistentDexFiles() {
     DCHECK_EQ(dex_filenames_.size(), dex_locations_.size());
     size_t kept = 0u;
@@ -2845,9 +2869,11 @@
   const char* compiled_methods_zip_filename_;
   const char* compiled_methods_filename_;
   const char* passes_to_run_filename_;
+  const char* dirty_image_objects_filename_;
   std::unique_ptr<std::unordered_set<std::string>> image_classes_;
   std::unique_ptr<std::unordered_set<std::string>> compiled_classes_;
   std::unique_ptr<std::unordered_set<std::string>> compiled_methods_;
+  std::unique_ptr<std::unordered_set<std::string>> dirty_image_objects_;
   std::unique_ptr<std::vector<std::string>> passes_to_run_;
   bool multi_image_;
   bool is_host_;
@@ -2873,6 +2899,7 @@
   bool dump_passes_;
   bool dump_timing_;
   bool dump_slow_timing_;
+  bool avoid_storing_invocation_;
   std::string swap_file_name_;
   int swap_fd_;
   size_t min_dex_files_for_swap_ = kDefaultMinDexFilesForSwap;
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 95fb16d..46c5f58 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -340,6 +340,15 @@
     // EXPECT_GE(profile_sizes.oat_size / kRatio, compiled_methods_sizes.oat_size);
     EXPECT_GE(profile_sizes.vdex_size / kRatio, compiled_methods_sizes.vdex_size);
   }
+  // Test dirty image objects.
+  {
+    ScratchFile classes;
+    GenerateClasses(classes.GetFile(), /*frequency*/ 1u);
+    image_classes_sizes = CompileImageAndGetSizes(
+        {"--dirty-image-objects=" + classes.GetFilename()});
+    classes.Close();
+    std::cout << "Dirty image object sizes " << image_classes_sizes << std::endl;
+  }
 }
 
 }  // namespace art
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 32877a8..12bceb3 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -134,7 +134,7 @@
     }
   }
 
-  // Check the input compiler filter against the generated oat file's filter. Mayb be overridden
+  // Check the input compiler filter against the generated oat file's filter. May be overridden
   // in subclasses when equality is not expected.
   virtual void CheckFilter(CompilerFilter::Filter expected, CompilerFilter::Filter actual) {
     EXPECT_EQ(expected, actual);
@@ -153,14 +153,7 @@
 
     std::vector<std::string> argv;
     argv.push_back(runtime->GetCompilerExecutable());
-    argv.push_back("--runtime-arg");
-    argv.push_back("-classpath");
-    argv.push_back("--runtime-arg");
-    std::string class_path = runtime->GetClassPathString();
-    if (class_path == "") {
-      class_path = OatFile::kSpecialSharedLibrary;
-    }
-    argv.push_back(class_path);
+
     if (runtime->IsJavaDebuggable()) {
       argv.push_back("--debuggable");
     }
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 7ef24c7..9ffc414 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -333,9 +333,11 @@
                         std::vector<uint8_t>* remote_contents,
                         std::vector<uint8_t>* zygote_contents,
                         const backtrace_map_t& boot_map,
-                        const ImageHeader& image_header) :
-    RegionCommon<mirror::Object>(os, remote_contents, zygote_contents, boot_map, image_header),
-    os_(*os) { }
+                        const ImageHeader& image_header,
+                        bool dump_dirty_objects)
+      : RegionCommon<mirror::Object>(os, remote_contents, zygote_contents, boot_map, image_header),
+        os_(*os),
+        dump_dirty_objects_(dump_dirty_objects) { }
 
   void CheckEntrySanity(const uint8_t* current) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -396,7 +398,10 @@
     class_data_[klass].AddDirtyObject(entry, entry_remote);
   }
 
-  void DiffEntryContents(mirror::Object* entry, uint8_t* remote_bytes, const uint8_t* base_ptr)
+  void DiffEntryContents(mirror::Object* entry,
+                         uint8_t* remote_bytes,
+                         const uint8_t* base_ptr,
+                         bool log_dirty_objects)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     const char* tabs = "    ";
     // Attempt to find fields for all dirty bytes.
@@ -453,6 +458,9 @@
       }
     }
     if (!dirty_static_fields.empty()) {
+      if (dump_dirty_objects_ && log_dirty_objects) {
+        dirty_objects_.insert(entry);
+      }
       os_ << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n";
       for (ArtField* field : dirty_static_fields) {
         os_ << tabs << ArtField::PrettyField(field)
@@ -463,6 +471,14 @@
     os_ << "\n";
   }
 
+  void DumpDirtyObjects() REQUIRES_SHARED(Locks::mutator_lock_) {
+    for (mirror::Object* obj : dirty_objects_) {
+      if (obj->IsClass()) {
+        os_ << "Private dirty object: " << obj->AsClass()->PrettyDescriptor() << "\n";
+      }
+    }
+  }
+
   void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
     // vector of pairs (size_t count, Class*)
     auto dirty_object_class_values =
@@ -592,6 +608,8 @@
   };
 
   std::ostream& os_;
+  bool dump_dirty_objects_;
+  std::unordered_set<mirror::Object*> dirty_objects_;
   std::map<mirror::Class*, ClassData> class_data_;
 
   DISALLOW_COPY_AND_ASSIGN(RegionSpecializedBase);
@@ -720,9 +738,15 @@
              std::vector<uint8_t>* remote_contents,
              std::vector<uint8_t>* zygote_contents,
              const backtrace_map_t& boot_map,
-             const ImageHeader& image_header) :
-    RegionSpecializedBase<T>(os, remote_contents, zygote_contents, boot_map, image_header),
-    os_(*os) {
+             const ImageHeader& image_header,
+             bool dump_dirty_objects)
+      : RegionSpecializedBase<T>(os,
+                                 remote_contents,
+                                 zygote_contents,
+                                 boot_map,
+                                 image_header,
+                                 dump_dirty_objects),
+        os_(*os) {
     CHECK(remote_contents != nullptr);
     CHECK(zygote_contents != nullptr);
   }
@@ -773,7 +797,8 @@
     DiffDirtyEntries(ProcessType::kRemote,
                      begin_image_ptr,
                      RegionCommon<T>::remote_contents_,
-                     base_ptr);
+                     base_ptr,
+                     /*log_dirty_objects*/true);
     // Print shared dirty after since it's less important.
     if (RegionCommon<T>::GetZygoteDirtyEntryCount() != 0) {
       // We only reach this point if both pids were specified.  Furthermore,
@@ -784,8 +809,10 @@
       DiffDirtyEntries(ProcessType::kZygote,
                        begin_image_ptr,
                        RegionCommon<T>::zygote_contents_,
-                       begin_image_ptr);
+                       begin_image_ptr,
+                       /*log_dirty_objects*/false);
     }
+    RegionSpecializedBase<T>::DumpDirtyObjects();
     RegionSpecializedBase<T>::DumpDirtyEntries();
     RegionSpecializedBase<T>::DumpFalseDirtyEntries();
     RegionSpecializedBase<T>::DumpCleanEntries();
@@ -797,7 +824,8 @@
   void DiffDirtyEntries(ProcessType process_type,
                         const uint8_t* begin_image_ptr,
                         std::vector<uint8_t>* contents,
-                        const uint8_t* base_ptr)
+                        const uint8_t* base_ptr,
+                        bool log_dirty_objects)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     os_ << RegionCommon<T>::dirty_entries_.size() << "\n";
     const std::set<T*>& entries =
@@ -808,7 +836,10 @@
       uint8_t* entry_bytes = reinterpret_cast<uint8_t*>(entry);
       ptrdiff_t offset = entry_bytes - begin_image_ptr;
       uint8_t* remote_bytes = &(*contents)[offset];
-      RegionSpecializedBase<T>::DiffEntryContents(entry, remote_bytes, &base_ptr[offset]);
+      RegionSpecializedBase<T>::DiffEntryContents(entry,
+                                                  remote_bytes,
+                                                  &base_ptr[offset],
+                                                  log_dirty_objects);
     }
   }
 
@@ -872,12 +903,14 @@
                          const ImageHeader& image_header,
                          const std::string& image_location,
                          pid_t image_diff_pid,
-                         pid_t zygote_diff_pid)
+                         pid_t zygote_diff_pid,
+                         bool dump_dirty_objects)
       : os_(os),
         image_header_(image_header),
         image_location_(image_location),
         image_diff_pid_(image_diff_pid),
         zygote_diff_pid_(zygote_diff_pid),
+        dump_dirty_objects_(dump_dirty_objects),
         zygote_pid_only_(false) {}
 
   bool Init() {
@@ -1207,7 +1240,8 @@
                                                   &remote_contents_,
                                                   &zygote_contents_,
                                                   boot_map_,
-                                                  image_header_);
+                                                  image_header_,
+                                                  dump_dirty_objects_);
 
     RemoteProcesses remotes;
     if (zygote_pid_only_) {
@@ -1364,6 +1398,7 @@
   const std::string image_location_;
   pid_t image_diff_pid_;  // Dump image diff against boot.art if pid is non-negative
   pid_t zygote_diff_pid_;  // Dump image diff against zygote boot.art if pid is non-negative
+  bool dump_dirty_objects_;  // Adds dumping of objects that are dirty.
   bool zygote_pid_only_;  // The user only specified a pid for the zygote.
 
   // BacktraceMap used for finding the memory mapping of the image file.
@@ -1391,7 +1426,8 @@
 static int DumpImage(Runtime* runtime,
                      std::ostream* os,
                      pid_t image_diff_pid,
-                     pid_t zygote_diff_pid) {
+                     pid_t zygote_diff_pid,
+                     bool dump_dirty_objects) {
   ScopedObjectAccess soa(Thread::Current());
   gc::Heap* heap = runtime->GetHeap();
   std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
@@ -1407,7 +1443,8 @@
                                   image_header,
                                   image_space->GetImageLocation(),
                                   image_diff_pid,
-                                  zygote_diff_pid);
+                                  zygote_diff_pid,
+                                  dump_dirty_objects);
     if (!img_diag_dumper.Init()) {
       return EXIT_FAILURE;
     }
@@ -1445,6 +1482,8 @@
         *error_msg = "Zygote diff pid out of range";
         return kParseError;
       }
+    } else if (option == "--dump-dirty-objects") {
+      dump_dirty_objects_ = true;
     } else {
       return kParseUnknownArgument;
     }
@@ -1497,6 +1536,7 @@
         "  --zygote-diff-pid=<pid>: provide the PID of the zygote whose boot.art you want to diff "
         "against.\n"
         "      Example: --zygote-diff-pid=$(pid zygote)\n"
+        "  --dump-dirty-objects: additionally output dirty objects of interest.\n"
         "\n";
 
     return usage;
@@ -1505,6 +1545,7 @@
  public:
   pid_t image_diff_pid_ = -1;
   pid_t zygote_diff_pid_ = -1;
+  bool dump_dirty_objects_ = false;
 };
 
 struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> {
@@ -1514,7 +1555,8 @@
     return DumpImage(runtime,
                      args_->os_,
                      args_->image_diff_pid_,
-                     args_->zygote_diff_pid_) == EXIT_SUCCESS;
+                     args_->zygote_diff_pid_,
+                     args_->dump_dirty_objects_) == EXIT_SUCCESS;
   }
 };
 
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
index 871604b..b1bc3f8 100644
--- a/runtime/aot_class_linker.cc
+++ b/runtime/aot_class_linker.cc
@@ -31,18 +31,29 @@
 bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
                                   bool can_init_statics, bool can_init_parents) {
   Runtime* const runtime = Runtime::Current();
+  bool strict_mode_ = runtime->IsActiveStrictTransactionMode();
 
   DCHECK(klass != nullptr);
   if (klass->IsInitialized() || klass->IsInitializing()) {
     return ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
   }
 
-  if (runtime->IsActiveStrictTransactionMode()) {
+  // Don't initialize klass if it's superclass is not initialized, because superclass might abort
+  // the transaction and rolled back after klass's change is commited.
+  if (strict_mode_ && !klass->IsInterface() && klass->HasSuperClass()) {
+    if (klass->GetSuperClass()->GetStatus() == mirror::Class::kStatusInitializing) {
+      runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve "
+          + klass->PrettyTypeOf() + " because it's superclass is not initialized.");
+      return false;
+    }
+  }
+
+  if (strict_mode_) {
     runtime->EnterTransactionMode(true, klass.Get()->AsClass());
   }
   bool success = ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
 
-  if (runtime->IsActiveStrictTransactionMode()) {
+  if (strict_mode_) {
     if (success) {
       // Exit Transaction if success.
       runtime->ExitTransactionMode();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 775651b..3ac87c5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4900,7 +4900,9 @@
       if (!super_initialized) {
         // The super class was verified ahead of entering initializing, we should only be here if
         // the super class became erroneous due to initialization.
-        CHECK(handle_scope_super->IsErroneous() && self->IsExceptionPending())
+        // For the case of aot compiler, the super class might also be initializing but we don't
+        // want to process circular dependencies in pre-compile.
+        CHECK(self->IsExceptionPending())
             << "Super class initialization failed for "
             << handle_scope_super->PrettyDescriptor()
             << " that has unexpected status " << handle_scope_super->GetStatus()
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 92d0f8d..e7051b3 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -68,6 +68,10 @@
   }
 }
 
+std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Default() {
+  return Create("");
+}
+
 std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string& spec) {
   std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext());
   if (result->Parse(spec)) {
@@ -263,7 +267,16 @@
   return removed_locations;
 }
 
+std::string ClassLoaderContext::EncodeContextForDex2oat(const std::string& base_dir) const {
+  return EncodeContext(base_dir, /*for_dex2oat*/ true);
+}
+
 std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir) const {
+  return EncodeContext(base_dir, /*for_dex2oat*/ false);
+}
+
+std::string ClassLoaderContext::EncodeContext(const std::string& base_dir,
+                                              bool for_dex2oat) const {
   CheckDexFilesOpened("EncodeContextForOatFile");
   if (special_shared_library_) {
     return OatFile::kSpecialSharedLibrary;
@@ -286,8 +299,17 @@
     }
     out << GetClassLoaderTypeName(info.type);
     out << kClassLoaderOpeningMark;
+    std::set<std::string> seen_locations;
     for (size_t k = 0; k < info.opened_dex_files.size(); k++) {
       const std::unique_ptr<const DexFile>& dex_file = info.opened_dex_files[k];
+      if (for_dex2oat) {
+        // dex2oat only needs the base location. It cannot accept multidex locations.
+        // So ensure we only add each file once.
+        bool new_insert = seen_locations.insert(dex_file->GetBaseLocation()).second;
+        if (!new_insert) {
+          continue;
+        }
+      }
       const std::string& location = dex_file->GetLocation();
       if (k > 0) {
         out << kClasspathSeparator;
@@ -298,8 +320,11 @@
       } else {
         out << dex_file->GetLocation().c_str();
       }
-      out << kDexFileChecksumSeparator;
-      out << dex_file->GetLocationChecksum();
+      // dex2oat does not need the checksums.
+      if (!for_dex2oat) {
+        out << kDexFileChecksumSeparator;
+        out << dex_file->GetLocationChecksum();
+      }
     }
     out << kClassLoaderClosingMark;
   }
@@ -593,7 +618,7 @@
   }
 }
 
-bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) {
+bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const {
   ClassLoaderContext expected_context;
   if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
     LOG(WARNING) << "Invalid class loader context: " << context_spec;
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 37dd02b..9afa880 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -34,9 +34,6 @@
 // Utility class which holds the class loader context used during compilation/verification.
 class ClassLoaderContext {
  public:
-  // Creates an empty context (with no class loaders).
-  ClassLoaderContext();
-
   ~ClassLoaderContext();
 
   // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
@@ -82,9 +79,16 @@
   // (so that it can be read and verified at runtime against the actual class
   // loader hierarchy).
   // Should only be called if OpenDexFiles() returned true.
-  // E.g. if the context is PCL[a.dex:b.dex] this will return "a.dex*a_checksum*b.dex*a_checksum".
+  // E.g. if the context is PCL[a.dex:b.dex] this will return
+  // "PCL[a.dex*a_checksum*b.dex*a_checksum]".
   std::string EncodeContextForOatFile(const std::string& base_dir) const;
 
+  // Encodes the context as a string suitable to be passed to dex2oat.
+  // This is the same as EncodeContextForOatFile but without adding the checksums
+  // and only adding each dex files once (no multidex).
+  // Should only be called if OpenDexFiles() returned true.
+  std::string EncodeContextForDex2oat(const std::string& base_dir) const;
+
   // Flattens the opened dex files into the given vector.
   // Should only be called if OpenDexFiles() returned true.
   std::vector<const DexFile*> FlattenOpenedDexFiles() const;
@@ -94,7 +98,7 @@
   //    - the number and type of the class loaders from the chain matches
   //    - the class loader from the same position have the same classpath
   //      (the order and checksum of the dex files matches)
-  bool VerifyClassLoaderContextMatch(const std::string& context_spec);
+  bool VerifyClassLoaderContextMatch(const std::string& context_spec) const;
 
   // Creates the class loader context from the given string.
   // The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
@@ -119,6 +123,10 @@
   static std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader,
                                                                          jobjectArray dex_elements);
 
+  // Returns the default class loader context to be used when none is specified.
+  // This will return a context with a single and empty PathClassLoader.
+  static std::unique_ptr<ClassLoaderContext> Default();
+
  private:
   enum ClassLoaderType {
     kInvalidClassLoader = 0,
@@ -144,6 +152,9 @@
     explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {}
   };
 
+  // Creates an empty context (with no class loaders).
+  ClassLoaderContext();
+
   // Constructs an empty context.
   // `owns_the_dex_files` specifies whether or not the context will own the opened dex files
   // present in the class loader chain. If `owns_the_dex_files` is true then OpenDexFiles cannot
@@ -173,7 +184,15 @@
   bool AddInfoToContextFromClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                        Handle<mirror::ClassLoader> class_loader,
                                        Handle<mirror::ObjectArray<mirror::Object>> dex_elements)
-  REQUIRES_SHARED(Locks::mutator_lock_);
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Encodes the context as a string suitable to be passed to dex2oat or to be added to the
+  // oat file as the class path key.
+  // If for_dex2oat is true, the encoding adds each file once (i.e. it does not add multidex
+  // location). Otherwise, for oat files, the encoding adds all the dex files (including multidex)
+  // together with their checksums.
+  // Should only be called if OpenDexFiles() returned true.
+  std::string EncodeContext(const std::string& base_dir, bool for_dex2oat) const;
 
   // Extracts the class loader type from the given spec.
   // Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 2b85188..d4688c1 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -455,6 +455,20 @@
   ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
 }
 
+TEST_F(ClassLoaderContextTest, EncodeForDex2oat) {
+  std::string dex1_name = GetTestDexFileName("Main");
+  std::string dex2_name = GetTestDexFileName("MultiDex");
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]");
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
+
+  std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main");
+  std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MultiDex");
+  std::string encoding = context->EncodeContextForDex2oat("");
+  std::string expected_encoding = "PCL[" + dex1_name + ":" + dex2_name + "]";
+  ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat(""));
+}
+
 // TODO(calin) add a test which creates the context for a class loader together with dex_elements.
 TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) {
   // The chain is
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 6642869..c775cf4 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -464,14 +464,25 @@
   return FindInterfaceMethod(name, signature, pointer_size);
 }
 
+static inline bool IsValidInheritanceCheck(ObjPtr<mirror::Class> klass,
+                                           ObjPtr<mirror::Class> declaring_class)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (klass->IsArrayClass()) {
+    return declaring_class->IsObjectClass();
+  } else if (klass->IsInterface()) {
+    return declaring_class->IsObjectClass() || declaring_class == klass;
+  } else {
+    return klass->IsSubClass(declaring_class);
+  }
+}
+
 static inline bool IsInheritedMethod(ObjPtr<mirror::Class> klass,
                                      ObjPtr<mirror::Class> declaring_class,
                                      ArtMethod& method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK_EQ(declaring_class, method.GetDeclaringClass());
   DCHECK_NE(klass, declaring_class);
-  DCHECK(klass->IsArrayClass() ? declaring_class->IsObjectClass()
-                               : klass->IsSubClass(declaring_class));
+  DCHECK(IsValidInheritanceCheck(klass, declaring_class));
   uint32_t access_flags = method.GetAccessFlags();
   if ((access_flags & (kAccPublic | kAccProtected)) != 0) {
     return true;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index dae41c1..3794f51 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -37,6 +37,7 @@
 #include "scoped_thread_state_change-inl.h"
 #include "utils.h"
 #include "vdex_file.h"
+#include "class_loader_context.h"
 
 namespace art {
 
@@ -225,13 +226,25 @@
 }
 
 OatFileAssistant::ResultOfAttemptToUpdate
-OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) {
+OatFileAssistant::MakeUpToDate(bool profile_changed,
+                               const std::string& class_loader_context,
+                               std::string* error_msg) {
   CompilerFilter::Filter target;
   if (!GetRuntimeCompilerFilterOption(&target, error_msg)) {
     return kUpdateNotAttempted;
   }
 
   OatFileInfo& info = GetBestInfo();
+  // TODO(calin, jeffhao): the context should really be passed to GetDexOptNeeded: b/62269291.
+  // This is actually not trivial in the current logic as it will interact with the collision
+  // check:
+  //   - currently, if the context does not match but we have no collisions we still accept the
+  //     oat file.
+  //   - if GetDexOptNeeded would return kDex2OatFromScratch for a context mismatch and we make
+  //     the oat code up to date the collision check becomes useless.
+  //   - however, MakeUpToDate will not always succeed (e.g. for primary apks, or for dex files
+  //     loaded in other processes). So it boils down to how far do we want to complicate
+  //     the logic in order to enable the use of oat files. Maybe its time to try simplify it.
   switch (info.GetDexOptNeeded(target, profile_changed, /*downgrade*/ false)) {
     case kNoDexOptNeeded:
       return kUpdateSucceeded;
@@ -243,7 +256,7 @@
     case kDex2OatForBootImage:
     case kDex2OatForRelocation:
     case kDex2OatForFilter:
-      return GenerateOatFileNoChecks(info, target, error_msg);
+      return GenerateOatFileNoChecks(info, target, class_loader_context, error_msg);
   }
   UNREACHABLE();
 }
@@ -628,7 +641,10 @@
 }
 
 OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks(
-      OatFileAssistant::OatFileInfo& info, CompilerFilter::Filter filter, std::string* error_msg) {
+      OatFileAssistant::OatFileInfo& info,
+      CompilerFilter::Filter filter,
+      const std::string& class_loader_context,
+      std::string* error_msg) {
   CHECK(error_msg != nullptr);
 
   Runtime* runtime = Runtime::Current();
@@ -704,6 +720,7 @@
   args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
   args.push_back("--oat-location=" + oat_file_name);
   args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+  args.push_back("--class-loader-context=" + class_loader_context);
 
   if (!Dex2Oat(args, error_msg)) {
     // Manually delete the oat and vdex files. This ensures there is no garbage
@@ -743,14 +760,6 @@
 
   std::vector<std::string> argv;
   argv.push_back(runtime->GetCompilerExecutable());
-  argv.push_back("--runtime-arg");
-  argv.push_back("-classpath");
-  argv.push_back("--runtime-arg");
-  std::string class_path = runtime->GetClassPathString();
-  if (class_path == "") {
-    class_path = OatFile::kSpecialSharedLibrary;
-  }
-  argv.push_back(class_path);
   if (runtime->IsJavaDebuggable()) {
     argv.push_back("--debuggable");
   }
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 320aa4f..5eec943 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -185,12 +185,17 @@
   // profile_changed should be true to indicate the profile has recently
   // changed for this dex location.
   //
+  // If the dex files need to be made up to date, class_loader_context will be
+  // passed to dex2oat.
+  //
   // Returns the result of attempting to update the code.
   //
   // If the result is not kUpdateSucceeded, the value of error_msg will be set
   // to a string describing why there was a failure or the update was not
   // attempted. error_msg must not be null.
-  ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed, std::string* error_msg);
+  ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed,
+                                       const std::string& class_loader_context,
+                                       std::string* error_msg);
 
   // Returns an oat file that can be used for loading dex files.
   // Returns null if no suitable oat file was found.
@@ -389,7 +394,8 @@
   };
 
   // Generate the oat file for the given info from the dex file using the
-  // current runtime compiler options and the specified filter.
+  // current runtime compiler options, the specified filter and class loader
+  // context.
   // This does not check the current status before attempting to generate the
   // oat file.
   //
@@ -398,6 +404,7 @@
   // attempted. error_msg must not be null.
   ResultOfAttemptToUpdate GenerateOatFileNoChecks(OatFileInfo& info,
                                                   CompilerFilter::Filter target,
+                                                  const std::string& class_loader_context,
                                                   std::string* error_msg);
 
   // Return info for the best oat file.
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index c59dafc..e048177 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -27,8 +27,10 @@
 
 #include "art_field-inl.h"
 #include "class_linker-inl.h"
+#include "class_loader_context.h"
 #include "common_runtime_test.h"
 #include "dexopt_test.h"
+#include "oat_file.h"
 #include "oat_file_manager.h"
 #include "os.h"
 #include "scoped_thread_state_change-inl.h"
@@ -37,6 +39,8 @@
 
 namespace art {
 
+static const std::string kSpecialSharedLibrary = "&";
+
 class OatFileAssistantTest : public DexoptTest {};
 
 class OatFileAssistantNoDex2OatTest : public DexoptTest {
@@ -116,7 +120,8 @@
 
   // Trying to make the oat file up to date should not fail or crash.
   std::string error_msg;
-  EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, oat_file_assistant.MakeUpToDate(false, &error_msg));
+  EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
+          oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
 
   // Trying to get the best oat file should fail, but not crash.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -769,7 +774,8 @@
   std::string error_msg;
   Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
   EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+      oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+          error_msg;
 
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -949,7 +955,7 @@
   // We should get kUpdateSucceeded from MakeUpToDate since there's nothing
   // that can be done in this situation.
   ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg));
+      oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
 
   // Verify it didn't create an oat in the default location (dalvik-cache).
   OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
@@ -1028,7 +1034,7 @@
   std::string error_msg;
   Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
   EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg));
+      oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
   EXPECT_TRUE(error_msg.empty());
 }
 
@@ -1175,7 +1181,8 @@
   std::string error_msg;
   Runtime::Current()->AddCompilerOption("--compiler-filter=quicken");
   EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+      oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+          error_msg;
   EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
   EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
@@ -1183,7 +1190,8 @@
 
   Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
   EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+      oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg))
+          << error_msg;
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
       oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
@@ -1191,7 +1199,7 @@
 
   Runtime::Current()->AddCompilerOption("--compiler-filter=bogus");
   EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted,
-      oat_file_assistant.MakeUpToDate(false, &error_msg));
+      oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
 }
 
 TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) {
@@ -1251,7 +1259,8 @@
       OatFileAssistant::kDefaultCompilerFilterForDexLoading;
   std::string error_msg;
   EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
-      oat_file_assistant.MakeUpToDate(false, &error_msg)) << error_msg;
+      oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+          error_msg;
   EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
             oat_file_assistant.GetDexOptNeeded(default_filter));
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -1259,6 +1268,50 @@
   EXPECT_EQ(default_filter, oat_file->GetCompilerFilter());
 }
 
+TEST_F(OatFileAssistantTest, MakeUpToDateWithSpecialSharedLibrary) {
+  std::string dex_location = GetScratchDir() + "/TestDex.jar";
+  Copy(GetDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+  const CompilerFilter::Filter default_filter =
+      OatFileAssistant::kDefaultCompilerFilterForDexLoading;
+  std::string error_msg;
+  int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg);
+  EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+  EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+      oat_file_assistant.GetDexOptNeeded(default_filter));
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  EXPECT_NE(nullptr, oat_file.get());
+  EXPECT_EQ(kSpecialSharedLibrary,
+            oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
+}
+
+TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) {
+  std::string dex_location = GetScratchDir() + "/TestDex.jar";
+  std::string context_location = GetScratchDir() + "/ContextDex.jar";
+  Copy(GetDexSrc1(), dex_location);
+  Copy(GetDexSrc2(), context_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+  const CompilerFilter::Filter default_filter =
+      OatFileAssistant::kDefaultCompilerFilterForDexLoading;
+  std::string error_msg;
+  std::string context_str = "PCL[" + context_location + "]";
+  int status = oat_file_assistant.MakeUpToDate(false, context_str, &error_msg);
+  EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+  EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+  oat_file_assistant.GetDexOptNeeded(default_filter));
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  EXPECT_NE(nullptr, oat_file.get());
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create(context_str);
+  context->OpenDexFiles(kRuntimeISA, "");
+  EXPECT_EQ(context->EncodeContextForOatFile(""),
+      oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
+}
+
 // TODO: More Tests:
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index e950fca..5baf59c 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -329,12 +329,14 @@
 
 // Check for class-def collisions in dex files.
 //
-// This first walks the class loader chain, getting all the dex files from the class loader. If
-// the class loader is null or one of the class loaders in the chain is unsupported, we collect
-// dex files from all open non-boot oat files to be safe.
+// This first walks the class loader chain present in the given context, getting all the dex files
+// from the class loader.
 //
-// This first checks whether the shared libraries are in the expected order and the oat files
-// have the expected checksums. If so, we exit early. Otherwise, we do the collision check.
+// If the context is null (which means the initial class loader was null or unsupported)
+// this returns false. b/37777332.
+//
+// This first checks whether all class loaders in the context have the same type and
+// classpath. If so, we exit early. Otherwise, we do the collision check.
 //
 // The collision check works by maintaining a heap with one class from each dex file, sorted by the
 // class descriptor. Then a dex-file/class pair is continually removed from the heap and compared
@@ -342,23 +344,11 @@
 // the two elements agree on whether their dex file was from an already-loaded oat-file or the
 // new oat file. Any disagreement indicates a collision.
 bool OatFileManager::HasCollisions(const OatFile* oat_file,
-                                   jobject class_loader,
-                                   jobjectArray dex_elements,
+                                   const ClassLoaderContext* context,
                                    std::string* error_msg /*out*/) const {
   DCHECK(oat_file != nullptr);
   DCHECK(error_msg != nullptr);
 
-  // If the class_loader is null there's not much we can do. This happens if a dex files is loaded
-  // directly with DexFile APIs instead of using class loaders.
-  if (class_loader == nullptr) {
-    LOG(WARNING) << "Opening an oat file without a class loader. "
-        << "Are you using the deprecated DexFile APIs?";
-    return false;
-  }
-
-  std::unique_ptr<ClassLoaderContext> context =
-      ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
-
   // The context might be null if there are unrecognized class loaders in the chain or they
   // don't meet sensible sanity conditions. In this case we assume that the app knows what it's
   // doing and accept the oat file.
@@ -406,6 +396,17 @@
   Locks::mutator_lock_->AssertNotHeld(self);
   Runtime* const runtime = Runtime::Current();
 
+  std::unique_ptr<ClassLoaderContext> context;
+  // If the class_loader is null there's not much we can do. This happens if a dex files is loaded
+  // directly with DexFile APIs instead of using class loaders.
+  if (class_loader == nullptr) {
+    LOG(WARNING) << "Opening an oat file without a class loader. "
+                 << "Are you using the deprecated DexFile APIs?";
+    context = nullptr;
+  } else {
+    context = ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
+  }
+
   OatFileAssistant oat_file_assistant(dex_location,
                                       kRuntimeISA,
                                       !runtime->IsAotCompiler());
@@ -425,7 +426,12 @@
     // Update the oat file on disk if we can, based on the --compiler-filter
     // option derived from the current runtime options.
     // This may fail, but that's okay. Best effort is all that matters here.
-    switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)) {
+
+    const std::string& dex2oat_context = context == nullptr
+        ? OatFile::kSpecialSharedLibrary
+        : context->EncodeContextForDex2oat(/*base_dir*/ "");
+    switch (oat_file_assistant.MakeUpToDate(
+        /*profile_changed*/false, dex2oat_context, /*out*/ &error_msg)) {
       case OatFileAssistant::kUpdateFailed:
         LOG(WARNING) << error_msg;
         break;
@@ -451,7 +457,7 @@
   if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
     // Take the file only if it has no collisions, or we must take it because of preopting.
     bool accept_oat_file =
-        !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
+        !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg);
     if (!accept_oat_file) {
       // Failed the collision check. Print warning.
       if (Runtime::Current()->IsDexFileFallbackEnabled()) {
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 05a5f5b..4523494 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -35,6 +35,7 @@
 }  // namespace space
 }  // namespace gc
 
+class ClassLoaderContext;
 class DexFile;
 class OatFile;
 
@@ -105,15 +106,17 @@
   void DumpForSigQuit(std::ostream& os);
 
  private:
-  // Check that the shared libraries in the given oat file match those in the given class loader and
-  // dex elements. If the class loader is null or we do not support one of the class loaders in the
-  // chain, compare against all non-boot oat files instead. If the shared libraries are not ok,
-  // check for duplicate class definitions of the given oat file against the oat files (either from
-  // the class loader and dex elements if possible or all non-boot oat files otherwise).
+  // Check that the class loader context of the given oat file matches the given context.
+  // This will perform a check that all class loaders in the chain have the same type and
+  // classpath.
+  // If the context is null (which means the initial class loader was null or unsupported)
+  // this returns false.
+  // If the context does not validate the method will check for duplicate class definitions of
+  // the given oat file against the oat files (either from the class loaders if possible or all
+  // non-boot oat files otherwise).
   // Return true if there are any class definition collisions in the oat_file.
   bool HasCollisions(const OatFile* oat_file,
-                     jobject class_loader,
-                     jobjectArray dex_elements,
+                     const ClassLoaderContext* context,
                      /*out*/ std::string* error_msg) const
       REQUIRES(!Locks::oat_file_manager_lock_);
 
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 0351fd3..63058cf 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -72,8 +72,8 @@
 
    private:
     static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
-    // Last update: Change method lookup.
-    static constexpr uint8_t kVdexVersion[] = { '0', '0', '9', '\0' };
+    // Last update: Use set for unverified_classes_.
+    static constexpr uint8_t kVdexVersion[] = { '0', '1', '0', '\0' };
 
     uint8_t magic_[4];
     uint8_t version_[4];
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 0c460db..312d8df 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3861,10 +3861,20 @@
   // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
   VerifierDeps::MaybeRecordMethodResolution(*dex_file_, dex_method_idx, res_method);
 
+  bool must_fail = false;
+  // This is traditional and helps with screwy bytecode. It will tell you that, yes, a method
+  // exists, but that it's called incorrectly. This significantly helps debugging, as locally it's
+  // hard to see the differences.
+  // If we don't have res_method here we must fail. Just use this bool to make sure of that with a
+  // DCHECK.
   if (res_method == nullptr) {
+    must_fail = true;
     // Try to find the method also with the other type for better error reporting below
     // but do not store such bogus lookup result in the DexCache or VerifierDeps.
     if (klass->IsInterface()) {
+      // NB This is normally not really allowed but we want to get any static or private object
+      // methods for error message purposes. This will never be returned.
+      // TODO We might want to change the verifier to not require this.
       res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
     } else {
       // If there was an interface method with the same signature,
@@ -3922,6 +3932,20 @@
     }
   }
 
+  // Check specifically for non-public object methods being provided for interface dispatch. This
+  // can occur if we failed to find a method with FindInterfaceMethod but later find one with
+  // FindClassMethod for error message use.
+  if (method_type == METHOD_INTERFACE &&
+      res_method->GetDeclaringClass()->IsObjectClass() &&
+      !res_method->IsPublic()) {
+    Fail(VERIFY_ERROR_NO_METHOD) << "invoke-interface " << klass->PrettyDescriptor() << "."
+                                 << dex_file_->GetMethodName(method_id) << " "
+                                 << dex_file_->GetMethodSignature(method_id) << " resolved to "
+                                 << "non-public object method " << res_method->PrettyMethod() << " "
+                                 << "but non-public Object methods are excluded from interface "
+                                 << "method resolution.";
+    return nullptr;
+  }
   // Check if access is allowed.
   if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
     Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call "
@@ -3950,6 +3974,13 @@
                                        "type of " << res_method->PrettyMethod();
     return nullptr;
   }
+  // Make sure we weren't expecting to fail.
+  DCHECK(!must_fail) << "invoke type (" << method_type << ")"
+                     << klass->PrettyDescriptor() << "."
+                     << dex_file_->GetMethodName(method_id) << " "
+                     << dex_file_->GetMethodSignature(method_id) << " unexpectedly resolved to "
+                     << res_method->PrettyMethod() << " without error. Initially this method was "
+                     << "not found so we were expecting to fail for some reason.";
   return res_method;
 }
 
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 470b0b3..0481f24 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -59,9 +59,7 @@
     MergeSets(my_deps->classes_, other_deps.classes_);
     MergeSets(my_deps->fields_, other_deps.fields_);
     MergeSets(my_deps->methods_, other_deps.methods_);
-    for (dex::TypeIndex entry : other_deps.unverified_classes_) {
-      my_deps->unverified_classes_.push_back(entry);
-    }
+    MergeSets(my_deps->unverified_classes_, other_deps.unverified_classes_);
   }
 }
 
@@ -507,7 +505,7 @@
   VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
   if (thread_deps != nullptr) {
     DexFileDeps* dex_deps = thread_deps->GetDexFileDeps(dex_file);
-    dex_deps->unverified_classes_.push_back(type_idx);
+    dex_deps->unverified_classes_.insert(type_idx);
   }
 }
 
@@ -586,6 +584,16 @@
   return dex::StringIndex(in);
 }
 
+// TODO: Clean this up, if we use a template arg here it confuses the compiler.
+static inline void EncodeTuple(std::vector<uint8_t>* out, const dex::TypeIndex& t) {
+  EncodeUnsignedLeb128(out, Encode(t));
+}
+
+// TODO: Clean this up, if we use a template arg here it confuses the compiler.
+static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, dex::TypeIndex* t) {
+  *t = Decode<dex::TypeIndex>(DecodeUint32WithOverflowCheck(in, end));
+}
+
 template<typename T1, typename T2>
 static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2>& t) {
   EncodeUnsignedLeb128(out, Encode(std::get<0>(t)));
@@ -692,7 +700,7 @@
     EncodeSet(buffer, deps.classes_);
     EncodeSet(buffer, deps.fields_);
     EncodeSet(buffer, deps.methods_);
-    EncodeUint16Vector(buffer, deps.unverified_classes_);
+    EncodeSet(buffer, deps.unverified_classes_);
   }
 }
 
@@ -715,7 +723,7 @@
     DecodeSet(&data_start, data_end, &deps->classes_);
     DecodeSet(&data_start, data_end, &deps->fields_);
     DecodeSet(&data_start, data_end, &deps->methods_);
-    DecodeUint16Vector(&data_start, data_end, &deps->unverified_classes_);
+    DecodeSet(&data_start, data_end, &deps->unverified_classes_);
   }
   CHECK_LE(data_start, data_end);
 }
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 2d452f6..4069a11 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -117,7 +117,7 @@
   bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader, Thread* self) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  const std::vector<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const {
+  const std::set<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const {
     return GetDexFileDeps(dex_file)->unverified_classes_;
   }
 
@@ -197,7 +197,7 @@
     std::set<MethodResolution> methods_;
 
     // List of classes that were not fully verified in that dex file.
-    std::vector<dex::TypeIndex> unverified_classes_;
+    std::set<dex::TypeIndex> unverified_classes_;
 
     bool Equals(const DexFileDeps& rhs) const;
   };
diff --git a/test/075-verification-error/expected.txt b/test/075-verification-error/expected.txt
index 6e4f584..7ccc32c 100644
--- a/test/075-verification-error/expected.txt
+++ b/test/075-verification-error/expected.txt
@@ -10,3 +10,4 @@
 Got expected IllegalAccessError (meth-class)
 Got expected IllegalAccessError (field-class)
 Got expected IllegalAccessError (meth-meth)
+Got expected IncompatibleClassChangeError (interface)
diff --git a/test/075-verification-error/src/BadIfaceImpl.java b/test/075-verification-error/src/BadIfaceImpl.java
new file mode 100644
index 0000000..fa2a970
--- /dev/null
+++ b/test/075-verification-error/src/BadIfaceImpl.java
@@ -0,0 +1,17 @@
+/*
+ * 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 BadIfaceImpl implements BadInterface { }
diff --git a/test/075-verification-error/src/BadInterface.java b/test/075-verification-error/src/BadInterface.java
new file mode 100644
index 0000000..439aba4
--- /dev/null
+++ b/test/075-verification-error/src/BadInterface.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 interface BadInterface {
+  public default Object internalClone() {
+    throw new Error("Should not be called");
+  }
+}
diff --git a/test/075-verification-error/src/Main.java b/test/075-verification-error/src/Main.java
index 3f2881e..13aeaee 100644
--- a/test/075-verification-error/src/Main.java
+++ b/test/075-verification-error/src/Main.java
@@ -28,6 +28,20 @@
         testClassNewInstance();
         testMissingStuff();
         testBadAccess();
+        testBadInterfaceMethod();
+     }
+    /**
+     * Try to create and invoke a non-existant interface method.
+     */
+    static void testBadInterfaceMethod() {
+        BadInterface badiface = new BadIfaceImpl();
+        try {
+            badiface.internalClone();
+        } catch (IncompatibleClassChangeError icce) {
+            // TODO b/64274113 This should really be an NSME
+            System.out.println("Got expected IncompatibleClassChangeError (interface)");
+            if (VERBOSE) System.out.println("--- " + icce);
+        }
     }
 
     /**
diff --git a/test/075-verification-error/src2/BadInterface.java b/test/075-verification-error/src2/BadInterface.java
new file mode 100644
index 0000000..5d939cb
--- /dev/null
+++ b/test/075-verification-error/src2/BadInterface.java
@@ -0,0 +1,17 @@
+/*
+ * 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 interface BadInterface { }
+
diff --git a/test/162-method-resolution/expected.txt b/test/162-method-resolution/expected.txt
index 1bf39c9..9b48a4c 100644
--- a/test/162-method-resolution/expected.txt
+++ b/test/162-method-resolution/expected.txt
@@ -41,3 +41,6 @@
 Calling Test9User2.test():
 Caught java.lang.reflect.InvocationTargetException
   caused by java.lang.IncompatibleClassChangeError
+Calling Test10User.test():
+Caught java.lang.reflect.InvocationTargetException
+  caused by java.lang.IncompatibleClassChangeError
diff --git a/test/162-method-resolution/jasmin/Test10Base.j b/test/162-method-resolution/jasmin/Test10Base.j
new file mode 100644
index 0000000..628f38d
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test10Base.j
@@ -0,0 +1,25 @@
+; 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                   public Test10Base
+.super                   java/lang/Object
+.implements              Test10Interface
+
+.method                  public <init>()V
+   .limit stack          1
+   .limit locals         1
+   aload_0
+   invokespecial         java/lang/Object/<init>()V
+   return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test10User.j b/test/162-method-resolution/jasmin/Test10User.j
new file mode 100644
index 0000000..6beadab
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test10User.j
@@ -0,0 +1,36 @@
+; 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 public Test10User
+.super java/lang/Object
+
+.method public static test()V
+    .limit stack 3
+    .limit locals 3
+    new Test10Base
+    dup
+    invokespecial Test10Base.<init>()V
+    invokestatic  Test10User.doInvoke(LTest10Interface;)V
+    return
+.end method
+
+.method public static doInvoke(LTest10Interface;)V
+    .limit stack 3
+    .limit locals 3
+    aload_0
+    invokeinterface Test10Interface.clone()Ljava.lang.Object; 1
+    pop
+    return
+.end method
+
diff --git a/test/162-method-resolution/multidex.jpp b/test/162-method-resolution/multidex.jpp
index 22e3aee..5722f7f 100644
--- a/test/162-method-resolution/multidex.jpp
+++ b/test/162-method-resolution/multidex.jpp
@@ -112,6 +112,16 @@
   @@com.android.jack.annotations.ForceInMainDex
   class Test9User2
 
+Test10Base:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Test10Base
+Test10Interface:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Test10Interface
+Test10User:
+  @@com.android.jack.annotations.ForceInMainDex
+  class Test10User
+
 Main:
   @@com.android.jack.annotations.ForceInMainDex
   class Main
diff --git a/test/162-method-resolution/src/Main.java b/test/162-method-resolution/src/Main.java
index fa95aa7..864c878 100644
--- a/test/162-method-resolution/src/Main.java
+++ b/test/162-method-resolution/src/Main.java
@@ -36,6 +36,7 @@
             test7();
             test8();
             test9();
+            test10();
 
             // TODO: How to test that interface method resolution returns the unique
             // maximally-specific non-abstract superinterface method if there is one?
@@ -376,6 +377,31 @@
         invokeUserTest("Test9User2");
     }
 
+    /*
+     * Test10
+     * -----
+     * Tested function:
+     *     public class Test10Base implements Test10Interface { }
+     *     public interface Test10Interface { }
+     * Tested invokes:
+     *     invoke-interface Test10Interface.clone()Ljava/lang/Object; from Test10Caller in first dex
+     *         TODO b/64274113 This should throw a NSME (JLS 13.4.12) but actually throws an ICCE.
+     *         expected: Throws NoSuchMethodError (JLS 13.4.12)
+     *         actual: Throws IncompatibleClassChangeError
+     *
+     * This test is simulating compiling Test10Interface with "public Object clone()" method, along
+     * with every other class. Then we delete "clone" from Test10Interface only, which under JLS
+     * 13.4.12 is expected to be binary incompatible and throw a NoSuchMethodError.
+     *
+     * Files:
+     *   jasmin/Test10Base.j          - implements Test10Interface
+     *   jasmin/Test10Interface.java  - defines empty interface
+     *   jasmin/Test10User.j          - invokeinterface Test10Interface.clone()Ljava/lang/Object;
+     */
+    private static void test10() throws Exception {
+        invokeUserTest("Test10User");
+    }
+
     private static void invokeUserTest(String userName) throws Exception {
         System.out.println("Calling " + userName + ".test():");
         try {
diff --git a/test/162-method-resolution/src/Test10Interface.java b/test/162-method-resolution/src/Test10Interface.java
new file mode 100644
index 0000000..3c75ea5
--- /dev/null
+++ b/test/162-method-resolution/src/Test10Interface.java
@@ -0,0 +1,18 @@
+/*
+ * 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 interface Test10Interface { }
+
diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java
index aca997e..fc7bcb2 100644
--- a/test/623-checker-loop-regressions/src/Main.java
+++ b/test/623-checker-loop-regressions/src/Main.java
@@ -426,6 +426,21 @@
     }
   }
 
+  // Environment of an instruction, removed during SimplifyInduction, should be adjusted.
+  //
+  /// CHECK-START: void Main.inductionMax(int[]) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: Phi loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: void Main.inductionMax(int[]) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static void inductionMax(int[] a) {
+   int s = 0;
+    for (int i = 0; i < 10; i++) {
+      s = Math.max(s, 5);
+    }
+  }
+
   public static void main(String[] args) {
     expectEquals(10, earlyExitFirst(-1));
     for (int i = 0; i <= 10; i++) {
@@ -539,6 +554,8 @@
       expectEquals((byte)(i + 1), b1[i]);
     }
 
+    inductionMax(yy);
+
     System.out.println("passed");
   }
 
diff --git a/test/660-clinit/profile b/test/660-clinit/profile
new file mode 100644
index 0000000..0239f22
--- /dev/null
+++ b/test/660-clinit/profile
@@ -0,0 +1,10 @@
+LMain;
+LClInit;
+LDay;
+LA;
+LB;
+LC;
+LG;
+LGs;
+LObjectRef;
+
diff --git a/test/660-clinit/run b/test/660-clinit/run
new file mode 100644
index 0000000..d24ef42
--- /dev/null
+++ b/test/660-clinit/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} $@ --profile
diff --git a/test/660-clinit/src/Main.java b/test/660-clinit/src/Main.java
index cf2ffe7..f9b068e 100644
--- a/test/660-clinit/src/Main.java
+++ b/test/660-clinit/src/Main.java
@@ -30,6 +30,13 @@
     expectNotPreInit(A.class); // should pass
     expectNotPreInit(B.class); // should fail
     expectNotPreInit(C.class); // should fail
+    expectNotPreInit(G.class); // should fail
+    expectNotPreInit(Gs.class); // should fail
+    expectNotPreInit(Gss.class); // should fail
+
+    expectNotPreInit(Add.class);
+    expectNotPreInit(Mul.class);
+    expectNotPreInit(ObjectRef.class);
 
     A x = new A();
     System.out.println("A.a: " + A.a);
@@ -146,3 +153,38 @@
     c = A.a; // read other's static field, fail
   }
 }
+
+class G {
+  static G g;
+  static int i;
+  static {
+    g = new Gss(); // fail because recursive dependency
+    i = A.a;  // read other's static field, fail
+  }
+}
+
+// Gs will be successfully initialized as G's status is initializing at that point, which will
+// later aborted but Gs' transaction is already committed.
+// Instantiation of Gs will fail because we try to invoke G's <init>
+// but G's status will be StatusVerified. INVOKE_DIRECT will not initialize class.
+class Gs extends G {}  // fail because super class can't be initialized
+class Gss extends Gs {}
+
+// pruned because holding reference to non-image class
+class ObjectRef {
+  static Class<?> klazz[] = new Class<?>[]{Add.class, Mul.class};
+}
+
+// non-image
+class Add {
+  static int exec(int a, int b) {
+    return a + b;
+  }
+}
+
+// non-image
+class Mul {
+  static int exec(int a, int b) {
+    return a * b;
+  }
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 5a67fbc..bd9591a 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -711,5 +711,10 @@
         "variant": "no-image | no-dex2oat | no-prebuild",
         "description": ["Tests <clinit> for app images, which --no-image, --no-prebuild and",
                         "--no-dex2oat do not create"]
+    },
+    {
+        "tests": "961-default-iface-resolution-gen",
+        "env_vars": {"SANITIZE_HOST": "address"},
+        "description": ["Test hits dex2oat watchdog timeout (60sec) on art-asan"]
     }
 ]
diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc
index 7a9d1e0..1f8ceff 100644
--- a/test/ti-agent/trace_helper.cc
+++ b/test/ti-agent/trace_helper.cc
@@ -388,10 +388,12 @@
   data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
   data->in_callback = false;
 
-  void* old_data = nullptr;
-  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+  TraceData* old_data = nullptr;
+  if (JvmtiErrorToException(env, jvmti_env,
+                            jvmti_env->GetEnvironmentLocalStorage(
+                                reinterpret_cast<void**>(&old_data)))) {
     return;
-  } else if (old_data != nullptr) {
+  } else if (old_data != nullptr && old_data->test_klass != nullptr) {
     ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
     env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
     return;
@@ -455,6 +457,21 @@
 
 extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
+  TraceData* data = nullptr;
+  if (JvmtiErrorToException(
+      env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+    return;
+  }
+  // If data is null then we haven't ever enabled tracing so we don't need to do anything.
+  if (data == nullptr || data->test_klass == nullptr) {
+    return;
+  }
+  env->DeleteGlobalRef(data->test_klass);
+  if (env->ExceptionCheck()) {
+    return;
+  }
+  // Clear test_klass so we know this isn't being used
+  data->test_klass = nullptr;
   if (JvmtiErrorToException(env, jvmti_env,
                             jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
                                                                 JVMTI_EVENT_FIELD_ACCESS,
diff --git a/tools/art b/tools/art
index 18c5c84..dea7ebd 100644
--- a/tools/art
+++ b/tools/art
@@ -119,6 +119,71 @@
   env "$@"
 }
 
+# Parse a colon-separated list into an array (e.g. "foo.dex:bar.dex" -> (foo.dex bar.dex))
+PARSE_CLASSPATH_RESULT=()  # Return value will be here due to shell limitations.
+parse_classpath() {
+  local cp="$1"
+  local oldifs=$IFS
+
+  local cp_array
+  cp_array=()
+
+  IFS=":"
+  for part in $cp; do
+    cp_array+=("$part")
+  done
+  IFS=$oldifs
+
+  PARSE_CLASSPATH_RESULT=("${cp_array[@]}")
+}
+
+# Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+# e.g. (-cp foo/classes.dex:bar/classes.dex) -> (foo/classes.dex bar/classes.dex)
+find_cp_in_args() {
+  local found="false"
+  local index=0
+  local what
+
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+      -cp|-classpath)
+        parse_classpath "$2"
+        # Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+        # Subsequent parses will overwrite the preceding.
+        shift
+        ;;
+    esac
+    shift
+  done
+}
+
+# Delete the 'oat' directories relative to the classpath's dex files.
+# e.g. (foo/classes.dex bar/classes.dex) would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory() {
+  local classpath
+  classpath=("$@")
+
+  local dirpath
+
+  for path in "${classpath[@]}"; do
+    dirpath="$(dirname "$path")"
+    [[ -d "$dirpath" ]] && verbose_run rm -rf "$dirpath/oat"
+  done
+}
+
+# Parse -cp <CP>, -classpath <CP>, and $CLASSPATH to find the dex files.
+# Each dex file's directory will have an 'oat' file directory, delete it.
+# Input: Command line arguments to the art script.
+# e.g. -cp foo/classes.dex:bar/classes.dex would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory_for_classpath() {
+  # First try: Use $CLASSPATH environment variable.
+  parse_classpath "$CLASSPATH"
+  # Second try: Look for latest -cp or -classpath arg which will take precedence.
+  find_cp_in_args "$@"
+
+  cleanup_oat_directory "${PARSE_CLASSPATH_RESULT[@]}"
+}
+
 # Attempt to find $ANDROID_ROOT/framework/<isa>/core.art' without knowing what <isa> is.
 function check_if_boot_image_file_exists() {
   local image_location_dir="$1"
@@ -151,9 +216,15 @@
   echo "$image_location"
 }
 
+# Runs dalvikvm, returns its exit code.
+# (Oat directories are cleaned up in between runs)
 function run_art() {
   local image_location="$(detect_boot_image_location)"
+  local ret
 
+  # First cleanup any left-over 'oat' files from the last time dalvikvm was run.
+  cleanup_oat_directory_for_classpath "$@"
+  # Run dalvikvm.
   verbose_run ANDROID_DATA=$ANDROID_DATA               \
               ANDROID_ROOT=$ANDROID_ROOT               \
               LD_LIBRARY_PATH=$LD_LIBRARY_PATH         \
@@ -164,6 +235,13 @@
               -Xnorelocate                             \
               -Ximage:"$image_location"                \
               "$@"
+  ret=$?
+
+  # Avoid polluting disk with 'oat' files after dalvikvm has finished.
+  cleanup_oat_directory_for_classpath "$@"
+
+  # Forward exit code of dalvikvm.
+  return $ret
 }
 
 while [[ "$1" = "-"* ]]; do
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Executor.java b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
index 074672d..0367a83 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Executor.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
@@ -114,8 +114,6 @@
 
     commandBuilder.append("--oat-file=output.oat ");
     commandBuilder.append("--android-root=").append(device.getAndroidHostOut()).append(" ");
-    commandBuilder.append("--runtime-arg -classpath ");
-    commandBuilder.append("--runtime-arg ").append(programName).append(" ");
     commandBuilder.append("--dex-file=").append(programName).append(" ");
     commandBuilder.append("--compiler-filter=quicken --runtime-arg -Xnorelocate ");
 
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index b4c6f2b..66bc252 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -9,6 +9,7 @@
   result: EXEC_FAILED,
   modes: [device],
   names: ["libcore.icu.TransliteratorTest#testAll",
+          "libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
           "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout",
           "libcore.java.util.TimeZoneTest#testSetDefaultDeadlock",
           "org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"]