dex2oat invoked by system if it can't find an oat file for a dex file.

This allows the old dalvik tests to be run without ever explicitly
running dex2oat on anything. Just upload the jar files and the system
will take care of generating the files it needs.

Change-Id: Iad553bf6f57e28da4edb8eb0df47e62e08a0be44
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 8638b30..589213a 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -550,6 +550,48 @@
   }
 }
 
+const OatFile* ClassLinker::GenerateOatFile(const std::string& filename) {
+  std::string oat_filename(GetArtCacheFilenameOrDie(OatFile::DexFilenameToOatFilename(filename)));
+
+  // fork and exec dex2oat
+  pid_t pid = fork();
+  if (pid == 0) {
+    std::string boot_image_option("--boot-image=");
+    boot_image_option += Heap::GetSpaces()[0]->GetImageFilename();
+
+    std::string dex_file_option("--dex-file=");
+    dex_file_option += filename;
+
+    std::string oat_file_option("--oat=");
+    oat_file_option += oat_filename;
+
+    execl("/system/bin/dex2oatd",
+          "/system/bin/dex2oatd",
+          "-Xms64m",
+          "-Xmx64m",
+          boot_image_option.c_str(),
+          dex_file_option.c_str(),
+          oat_file_option.c_str(),
+          NULL);
+
+    PLOG(FATAL) << "execl(dex2oatd) failed";
+    return NULL;
+  } else {
+    // wait for dex2oat to finish
+    int status;
+    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+    if (got_pid != pid) {
+      PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
+      return NULL;
+    }
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+      LOG(ERROR) << "dex2oatd failed with dex-file=" << filename;
+      return NULL;
+    }
+  }
+  return OatFile::Open(oat_filename, "", NULL);
+}
+
 OatFile* ClassLinker::OpenOat(const Space* space) {
   MutexLock mu(dex_lock_);
   const Runtime* runtime = Runtime::Current();
@@ -584,7 +626,18 @@
 const OatFile* ClassLinker::FindOatFile(const DexFile& dex_file) {
   MutexLock mu(dex_lock_);
   // TODO: check if dex_file matches an OatDexFile location and checksum
-  return FindOatFile(OatFile::DexFileToOatFilename(dex_file));
+  const OatFile* oat_file = FindOatFile(OatFile::DexFilenameToOatFilename(dex_file.GetLocation()));
+  if (oat_file != NULL) {
+    return oat_file;
+  }
+  // generate oat file if it wasn't found
+  oat_file = GenerateOatFile(dex_file.GetLocation());
+  if (oat_file == NULL) {
+    LOG(ERROR) << "Failed to generate oat file from dex file " << dex_file.GetLocation();
+    return NULL;
+  }
+  oat_files_.push_back(oat_file);
+  return oat_file;
 }
 
 const OatFile* ClassLinker::FindOpenedOatFile(const std::string& location) {
@@ -612,14 +665,14 @@
     }
 
     // not found in /foo/bar/baz.oat? try /data/art-cache/foo@bar@baz.oat
-    std::string cache_location = GetArtCacheOatFilenameOrDie(location);
+    std::string cache_location = GetArtCacheFilenameOrDie(location);
     oat_file = FindOpenedOatFile(cache_location);
     if (oat_file != NULL) {
       return oat_file;
     }
     oat_file = OatFile::Open(cache_location, "", NULL);
     if (oat_file  == NULL) {
-      LOG(ERROR) << "Failed to open oat file from " << location << " or " << cache_location << ".";
+      LOG(INFO) << "Failed to open oat file from " << location << " or " << cache_location << ".";
       return NULL;
     }
   }
diff --git a/src/class_linker.h b/src/class_linker.h
index 12d7b37..dd78174 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -209,6 +209,9 @@
   DexCache* FindDexCache(const DexFile& dex_file) const;
   bool IsDexFileRegistered(const DexFile& dex_file) const;
 
+  // Generate an oat file from a dex file
+  const OatFile* GenerateOatFile(const std::string& filename);
+
   // Find, possibily opening, an OatFile corresponding to a DexFile
   const OatFile* FindOatFile(const DexFile& dex_file);
   const OatFile* FindOatFile(const std::string& location);
diff --git a/src/dalvik_system_DexFile.cc b/src/dalvik_system_DexFile.cc
index 6e0a923..40a1626 100644
--- a/src/dalvik_system_DexFile.cc
+++ b/src/dalvik_system_DexFile.cc
@@ -19,11 +19,9 @@
 #include "class_loader.h"
 #include "class_linker.h"
 #include "dex_file.h"
-#include "file.h"
 #include "logging.h"
 #include "os.h"
 #include "runtime.h"
-#include "space.h"
 #include "zip_archive.h"
 #include "toStringArray.h"
 #include "ScopedUtfChars.h"
@@ -91,79 +89,43 @@
   if (env->ExceptionCheck()) {
     return 0;
   }
-  if (outputName.c_str() != NULL) {
-    // Check that output name ends in .dex.
-    if (!StringPiece(outputName.c_str()).ends_with(".dex")) {
-      LOG(ERROR) << "Output filename '" << outputName.c_str() << "' does not end with .dex";
-      return 0;
-    }
-
-    // Extract the dex file.
-    UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(sourceName.c_str()));
-    if (zip_archive.get() == NULL) {
-      LOG(ERROR) << "Failed to open " << sourceName.c_str() << " when looking for classes.dex";
-      return 0;
-    }
-
-    UniquePtr<ZipEntry> zip_entry(zip_archive->Find(DexFile::kClassesDex));
-    if (zip_entry.get() == NULL) {
-      LOG(ERROR) << "Failed to find classes.dex within " << sourceName.c_str();
-      return 0;
-    }
-
-    UniquePtr<File> file(OS::OpenFile(outputName.c_str(), true));
-    bool success = zip_entry->Extract(*file);
-    if (!success) {
-      LOG(ERROR) << "Failed to extract classes.dex from " << sourceName.c_str();
-      return 0;
-    }
-
-    // Fork and exec dex2oat.
-    pid_t pid = fork();
-    if (pid == 0) {
-      std::string boot_image_option("--boot-image=");
-      boot_image_option += Heap::GetSpaces()[0]->GetImageFilename();
-
-      std::string dex_file_option("--dex-file=");
-      dex_file_option += outputName.c_str();
-
-      std::string oat_file_option("--oat=");
-      oat_file_option += outputName.c_str();
-      oat_file_option.erase(oat_file_option.size() - 3);
-      oat_file_option += "oat";
-
-      execl("/system/bin/dex2oatd",
-            "/system/bin/dex2oatd",
-            "-Xms64m",
-            "-Xmx64m",
-            boot_image_option.c_str(),
-            dex_file_option.c_str(),
-            oat_file_option.c_str(),
-            NULL);
-
-      PLOG(FATAL) << "execl(dex2oatd) failed";
-      return 0;
-    } else {
-      // Wait for dex2oat to finish.
-      int status;
-      pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-      if (got_pid != pid) {
-        PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
-        return 0;
-      }
-      if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-        LOG(ERROR) << "dex2oatd failed with dex-file=" << outputName.c_str();
-        return 0;
-      }
-    }
-  }
-
   const DexFile* dex_file;
   if (outputName.c_str() == NULL) {
     dex_file = DexFile::Open(sourceName.c_str(), "");
   } else {
+    // Sanity check the arguments.
+    if (!IsValidZipFilename(sourceName.c_str()) || !IsValidDexFilename(outputName.c_str())) {
+      LOG(ERROR) << "Bad filenames extracting dex '" << outputName.c_str()
+                 << "' from zip '" << sourceName.c_str() << "'";
+      return NULL;
+    }
+
+    // Extract classes.dex from the input zip file to the output dex file
+    UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(sourceName.c_str()));
+    if (zip_archive.get() == NULL) {
+      LOG(ERROR) << "Failed to open " << sourceName.c_str() << " when looking for classes.dex";
+      return NULL;
+    }
+    UniquePtr<ZipEntry> zip_entry(zip_archive->Find(DexFile::kClassesDex));
+    if (zip_entry.get() == NULL) {
+      LOG(ERROR) << "Failed to find classes.dex within " << sourceName.c_str();
+      return NULL;
+    }
+    UniquePtr<File> file(OS::OpenFile(outputName.c_str(), true));
+    bool success = zip_entry->Extract(*file);
+    if (!success) {
+      LOG(ERROR) << "Failed to extract classes.dex from " << sourceName.c_str();
+      return NULL;
+    }
     dex_file = DexFile::Open(outputName.c_str(), "");
+
+    // Generate the oat file for the newly extracted dex file
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    if (class_linker->GenerateOatFile(outputName.c_str()) == NULL) {
+      return 0;
+    }
   }
+
   if (dex_file == NULL) {
     jniThrowExceptionFmt(env, "java/io/IOException", "unable to open DEX file: %s",
                          sourceName.c_str());
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 83866a9..7a41845 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -57,13 +57,12 @@
 
 const DexFile* DexFile::Open(const std::string& filename,
                              const std::string& strip_location_prefix) {
-  if (filename.size() < 4) {
-    LOG(WARNING) << "Attempting to open dex file with unknown extension '" << filename << "'";
-  }
-  std::string suffix(filename.substr(filename.size() - 4));
-  if (suffix == ".zip" || suffix == ".jar" || suffix == ".apk") {
+  if (IsValidZipFilename(filename)) {
     return DexFile::OpenZip(filename, strip_location_prefix);
   } else {
+    if (!IsValidDexFilename(filename)) {
+      LOG(WARNING) << "Attempting to open dex file with unknown extension '" << filename << "'";
+    }
     return DexFile::OpenFile(filename, filename, strip_location_prefix);
   }
 }
@@ -200,14 +199,9 @@
                  << " when looking for classes.dex";
       return NULL;
   }
-  std::string cache_file(absolute_path+1); // skip leading slash
-  std::replace(cache_file.begin(), cache_file.end(), '/', '@');
-  cache_file.push_back('@');
-  cache_file.append(kClassesDex);
-  // Example cache_file = parent@dir@foo.jar@classes.dex
-
-  std::string art_cache = GetArtCacheOrDie();
-  std::string cache_path_tmp = art_cache + "/" + cache_file;
+  std::string cache_path_tmp(GetArtCacheFilenameOrDie(absolute_path));
+  cache_path_tmp.push_back('@');
+  cache_path_tmp.append(kClassesDex);
   // Example cache_path_tmp = /data/art-cache/parent@dir@foo.jar@classes.dex
 
   UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(filename));
diff --git a/src/dex_file.h b/src/dex_file.h
index 070901b..3077ae6 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -330,6 +330,7 @@
   // Opens .dex file, guessing the container format based on file extension
   static const DexFile* Open(const std::string& filename,
                              const std::string& strip_location_prefix);
+
   // Closes a .dex file.
   virtual ~DexFile();
 
diff --git a/src/oat_file.cc b/src/oat_file.cc
index 6e8c60f..3dcde8e 100644
--- a/src/oat_file.cc
+++ b/src/oat_file.cc
@@ -10,15 +10,12 @@
 
 namespace art {
 
-std::string OatFile::DexFileToOatFilename(const DexFile& dex_file) {
-  std::string location(dex_file.GetLocation());
-  CHECK(StringPiece(location).ends_with(".dex")
-        || StringPiece(location).ends_with(".zip")
-        || StringPiece(location).ends_with(".jar")
-        || StringPiece(location).ends_with(".apk"));
-  location.erase(location.size()-3);
-  location += "oat";
-  return location;
+std::string OatFile::DexFilenameToOatFilename(const std::string& location) {
+  LOG(INFO) << "dexfilenametooatfilename " << location;
+  CHECK(IsValidDexFilename(location) || IsValidZipFilename(location));
+  std::string oat_location = location.substr(0, location.size()-3);
+  oat_location += "oat";
+  return oat_location;
 }
 
 OatFile* OatFile::Open(const std::string& filename,
diff --git a/src/oat_file.h b/src/oat_file.h
index e83b294..1ee8c91 100644
--- a/src/oat_file.h
+++ b/src/oat_file.h
@@ -17,7 +17,7 @@
  public:
 
   // Returns an OatFile name based on a DexFile location
-  static std::string DexFileToOatFilename(const DexFile& dex_file);
+  static std::string DexFilenameToOatFilename(const std::string& location);
 
   // Open an oat file. Returns NULL on failure.  Requested base can
   // optionally be used to request where the file should be loaded.
diff --git a/src/oatopt.cc b/src/oatopt.cc
index b2a33cb..133af59 100644
--- a/src/oatopt.cc
+++ b/src/oatopt.cc
@@ -47,7 +47,7 @@
   dex_file_option += zip_name;
 
   std::string oat_file_option("--oat=");
-  oat_file_option += GetArtCacheOatFilenameOrDie(OatFile::DexFileToOatFilename(*dex_file.get()));
+  oat_file_option += GetArtCacheFilenameOrDie(OatFile::DexFilenameToOatFilename(dex_file.get()->GetLocation()));
 
   execl("/system/bin/dex2oatd",
         "/system/bin/dex2oatd",
diff --git a/src/utils.cc b/src/utils.cc
index ceaf7cd..bfd7e79 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -514,7 +514,7 @@
   return art_cache;
 }
 
-std::string GetArtCacheOatFilenameOrDie(const std::string& location) {
+std::string GetArtCacheFilenameOrDie(const std::string& location) {
   std::string art_cache = GetArtCacheOrDie();
   CHECK_EQ(location[0], '/');
   std::string cache_file(location, 1); // skip leading slash
@@ -522,6 +522,22 @@
   return art_cache + "/" + cache_file;
 }
 
+bool IsValidZipFilename(const std::string& filename) {
+  if (filename.size() < 4) {
+    return false;
+  }
+  std::string suffix(filename.substr(filename.size() - 4));
+  return (suffix == ".zip" || suffix == ".jar" || suffix == ".apk");
+}
+
+bool IsValidDexFilename(const std::string& filename) {
+  if (filename.size() < 4) {
+    return false;
+  }
+  std::string suffix(filename.substr(filename.size() - 4));
+  return (suffix == ".dex");
+}
+
 }  // namespace art
 
 // Neither bionic nor glibc exposes gettid(2).
diff --git a/src/utils.h b/src/utils.h
index dd597e9..df1e1c1 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -221,8 +221,12 @@
 // Returns the art-cache location, or dies trying.
 std::string GetArtCacheOrDie();
 
-// Returns the art-cache location for an OatFile, or dies trying.
-std::string GetArtCacheOatFilenameOrDie(const std::string& location);
+// Returns the art-cache location for a DexFile or OatFile, or dies trying.
+std::string GetArtCacheFilenameOrDie(const std::string& location);
+
+// Check whether the given filename has a valid zip or dex extension
+bool IsValidZipFilename(const std::string& filename);
+bool IsValidDexFilename(const std::string& filename);
 
 }  // namespace art
 
diff --git a/test/etc/default-build b/test/etc/default-build
index 9647794..e71db2a 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -28,12 +28,6 @@
     --dump-width=1000 classes
 zip ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME.jar classes.dex
 
-dex2oatd -Xms16m -Xmx16m \
-    --boot-image=${ANDROID_PRODUCT_OUT}/data/art-test/core.art \
-    --dex-file=${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME.jar \
-    --oat=${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME.oat \
-    --host-prefix=${ANDROID_PRODUCT_OUT}
-
 if [ -r src-ex ]; then
     mkdir classes-ex
     ${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index ec7bf46..ae1474a 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -91,13 +91,9 @@
 
 if [ "$QUIET" = "n" ]; then
   adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME.jar /system/framework
-  adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME.oat /data/art-test
-  adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME.art /data/art-test
   adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME-ex.jar /data/art-test
 else
   adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME.jar /data/art-test >/dev/null 2>&1
-  adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME.oat /data/art-test >/dev/null 2>&1
-  adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME.art /data/art-test >/dev/null 2>&1
   adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME-ex.jar /data/art-test >/dev/null 2>&1
 fi