System calls dex2oat when opening a dex file with an output location

This fix addresses the old dalvik test 071. The test has 2 source jars,
and one is loaded later. To support this, the other jar has to be run
though dex2oat. Eventually, we want the system to invoke dex2oat on any
jar files that need it.

Change-Id: Ie8428004f397cdb56ee437d0b38c0670f14e77fa
diff --git a/src/dalvik_system_DexFile.cc b/src/dalvik_system_DexFile.cc
index bb486e2..6e0a923 100644
--- a/src/dalvik_system_DexFile.cc
+++ b/src/dalvik_system_DexFile.cc
@@ -19,9 +19,12 @@
 #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"
 
@@ -79,7 +82,7 @@
     void operator=(const NullableScopedUtfChars&);
 };
 
-static jint DexFile_openDexFile(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName) {
+static jint DexFile_openDexFile(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) {
   ScopedUtfChars sourceName(env, javaSourceName);
   if (sourceName.c_str() == NULL) {
     return 0;
@@ -89,14 +92,82 @@
     return 0;
   }
   if (outputName.c_str() != NULL) {
-    // TODO: extract dex and run dex2oat like oatopt
-    UNIMPLEMENTED(FATAL) << sourceName.c_str() << " " << outputName.c_str();
+    // 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 = DexFile::Open(sourceName.c_str(), "");
+
+  const DexFile* dex_file;
+  if (outputName.c_str() == NULL) {
+    dex_file = DexFile::Open(sourceName.c_str(), "");
+  } else {
+    dex_file = DexFile::Open(outputName.c_str(), "");
+  }
   if (dex_file == NULL) {
     jniThrowExceptionFmt(env, "java/io/IOException", "unable to open DEX file: %s",
                          sourceName.c_str());
-    return NULL;
+    return 0;
   }
   return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file));
 }
diff --git a/src/space.h b/src/space.h
index d312365..7ebc654 100644
--- a/src/space.h
+++ b/src/space.h
@@ -83,6 +83,11 @@
     return *image_header_;
   }
 
+  const std::string& GetImageFilename() const {
+    CHECK(IsImageSpace());
+    return name_;
+  }
+
   size_t AllocationSize(const Object* obj);
 
  private:
diff --git a/test/071-dexfile/src/Main.java b/test/071-dexfile/src/Main.java
index 2cbe380..0845e87 100644
--- a/test/071-dexfile/src/Main.java
+++ b/test/071-dexfile/src/Main.java
@@ -22,8 +22,8 @@
  * DexFile tests (Dalvik-specific).
  */
 public class Main {
-    private static final String CLASS_PATH = "test-ex.jar";
-    private static final String ODEX_DIR = "/sdcard";
+    private static final String CLASS_PATH = "/data/art-test/071-dexfile-ex.jar";
+    private static final String ODEX_DIR = "/data/art-test";
     //private static final String ODEX_DIR = ".";
     private static final String ODEX_ALT = "/tmp";
     private static final String LIB_DIR = "/nowhere/nothing/";
diff --git a/test/etc/default-build b/test/etc/default-build
index 0ac7fbf..9647794 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -46,10 +46,4 @@
     zip ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME-ex.jar classes.dex
     mv classes.dex classes-ex.dex
     mv classes-1.dex 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-ex.jar \
-        --oat=${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME-ex.oat \
-        --host-prefix=${ANDROID_PRODUCT_OUT}
 fi
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index 24bc8c6..bcc0ceb 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -85,15 +85,11 @@
   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
-  adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME-ex.oat /data/art-test
-  adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME-ex.art /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
-  adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME-ex.oat /data/art-test >/dev/null 2>&1
-  adb push ${ANDROID_PRODUCT_OUT}/data/art-test/$TEST_NAME-ex.art /data/art-test >/dev/null 2>&1
 fi
 
 if [ "$DEBUG" = "y" ]; then