ART: Allow --no-inline-from to specify multiple dex files.

This will allow tests to specify arbitrary dex files from
which we should not inline, in addition to core-oj, so that
we can test the related functionality. Additionally, should
the core-oj.jar core grow beyond a single classes.dex and
require a multi-dex .jar, this change makes sure that we
prevent inlining also from the extra classes<N>.dex files.

Change-Id: I74da4839bf9bb405dd62ad80563bf646a7a65dd9
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 818d50a..c483f33 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2582,7 +2582,8 @@
                                        const DexFile* inlined_into) const {
   // We're not allowed to inline across dex files if we're the no-inline-from dex file.
   if (inlined_from != inlined_into &&
-      compiler_options_->GetNoInlineFromDexFile() == inlined_from) {
+      compiler_options_->GetNoInlineFromDexFile() != nullptr &&
+      ContainsElement(*compiler_options_->GetNoInlineFromDexFile(), inlined_from)) {
     return false;
   }
 
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 2644528..4f6e922 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -62,7 +62,7 @@
                                  size_t num_dex_methods_threshold,
                                  size_t inline_depth_limit,
                                  size_t inline_max_code_units,
-                                 const DexFile* no_inline_from,
+                                 const std::vector<const DexFile*>* no_inline_from,
                                  bool include_patch_information,
                                  double top_k_profile_threshold,
                                  bool debuggable,
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index d47fc2a..9d51b75 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -72,7 +72,7 @@
                   size_t num_dex_methods_threshold,
                   size_t inline_depth_limit,
                   size_t inline_max_code_units,
-                  const DexFile* no_inline_from,
+                  const std::vector<const DexFile*>* no_inline_from,
                   bool include_patch_information,
                   double top_k_profile_threshold,
                   bool debuggable,
@@ -220,7 +220,7 @@
     return abort_on_hard_verifier_failure_;
   }
 
-  const DexFile* GetNoInlineFromDexFile() const {
+  const std::vector<const DexFile*>* GetNoInlineFromDexFile() const {
     return no_inline_from_;
   }
 
@@ -257,8 +257,10 @@
   size_t inline_depth_limit_;
   size_t inline_max_code_units_;
 
-  // A dex file from which we should not inline code.
-  const DexFile* no_inline_from_;
+  // Dex files from which we should not inline code.
+  // This is usually a very short list (i.e. a single dex file), so we
+  // prefer vector<> over a lookup-oriented container, such as set<>.
+  const std::vector<const DexFile*>* no_inline_from_;
 
   bool include_patch_information_;
   // When using a profile file only the top K% of the profiled samples will be compiled.
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 86f51e1..917d458 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -548,6 +548,7 @@
       driver_(nullptr),
       opened_dex_files_maps_(),
       opened_dex_files_(),
+      no_inline_from_dex_files_(),
       dump_stats_(false),
       dump_passes_(false),
       dump_timing_(false),
@@ -1452,14 +1453,18 @@
     TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
     compiler_phases_timings_.reset(new CumulativeLogger("compilation times"));
 
-    // Find the dex file we should not inline from.
+    // Find the dex files we should not inline from.
+
+    std::vector<std::string> no_inline_filters;
+    Split(no_inline_from_string_, ',', &no_inline_filters);
 
     // For now, on the host always have core-oj removed.
-    if (!kIsTargetBuild && no_inline_from_string_.empty()) {
-      no_inline_from_string_ = "core-oj";
+    const std::string core_oj = "core-oj";
+    if (!kIsTargetBuild && !ContainsElement(no_inline_filters, core_oj)) {
+      no_inline_filters.push_back(core_oj);
     }
 
-    if (!no_inline_from_string_.empty()) {
+    if (!no_inline_filters.empty()) {
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
       std::vector<const std::vector<const DexFile*>*> dex_file_vectors = {
@@ -1468,34 +1473,30 @@
           &dex_files_
       };
       for (const std::vector<const DexFile*>* dex_file_vector : dex_file_vectors) {
-        if (dex_file_vector == nullptr) {
-          continue;
-        }
-
-        bool found = false;
-
         for (const DexFile* dex_file : *dex_file_vector) {
-          // Try the complete location first.
-          found = no_inline_from_string_ == dex_file->GetLocation();
-          // The try just the name.
-          if (!found) {
-            size_t last_slash = dex_file->GetLocation().rfind('/');
-            if (last_slash != std::string::npos) {
-              found = StartsWith(dex_file->GetLocation().substr(last_slash + 1),
-                                 no_inline_from_string_.c_str());
+          for (const std::string& filter : no_inline_filters) {
+            // Use dex_file->GetLocation() rather than dex_file->GetBaseLocation(). This
+            // allows tests to specify <test-dexfile>:classes2.dex if needed but if the
+            // base location passes the StartsWith() test, so do all extra locations.
+            std::string dex_location = dex_file->GetLocation();
+            if (filter.find('/') == std::string::npos) {
+              // The filter does not contain the path. Remove the path from dex_location as well.
+              size_t last_slash = dex_file->GetLocation().rfind('/');
+              if (last_slash != std::string::npos) {
+                dex_location = dex_location.substr(last_slash + 1);
+              }
+            }
+
+            if (StartsWith(dex_location, filter.c_str())) {
+              VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation();
+              no_inline_from_dex_files_.push_back(dex_file);
+              break;
             }
           }
-
-          if (found) {
-            VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation();
-            compiler_options_->no_inline_from_ = dex_file;
-            break;
-          }
         }
-
-        if (found) {
-          break;
-        }
+      }
+      if (!no_inline_from_dex_files_.empty()) {
+        compiler_options_->no_inline_from_ = &no_inline_from_dex_files_;
       }
     }
 
@@ -2384,6 +2385,8 @@
   std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
   std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
 
+  std::vector<const DexFile*> no_inline_from_dex_files_;
+
   std::vector<std::string> verbose_methods_;
   bool dump_stats_;
   bool dump_passes_;