Execute an application even when dex2oat crashes.

Bug: 17000769

(cherry picked from commit 4fcdc94d22a4608e355aa8df36240181149d10e8)

Change-Id: Iccb1fec94fe64ce4c3097510952f275482b86aa9
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 903eda1..003e868 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -890,12 +890,18 @@
     }
   }
 
-  // Create the oat file.
-  open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(),
-                                                  oat_location, error_msgs));
+  if (Runtime::Current()->IsDex2OatEnabled()) {
+    // Create the oat file.
+    open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(),
+                                                    oat_location, error_msgs));
+  }
 
   // Failed, bail.
   if (open_oat_file.get() == nullptr) {
+    std::string error_msg;
+    // dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress.
+    DexFile::Open(dex_location, dex_location, &error_msg, dex_files);
+    error_msgs->push_back(error_msg);
     return false;
   }
 
@@ -2019,15 +2025,21 @@
   return mirror::Class::ComputeClassSize(false, 0, num_32, num_64, num_ref);
 }
 
-OatFile::OatClass ClassLinker::GetOatClass(const DexFile& dex_file, uint16_t class_def_idx) {
+bool ClassLinker::FindOatClass(const DexFile& dex_file,
+                               uint16_t class_def_idx,
+                               OatFile::OatClass* oat_class) {
+  DCHECK(oat_class != nullptr);
   DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
   const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file);
-  CHECK(oat_file != NULL) << dex_file.GetLocation();
+  if (oat_file == nullptr) {
+    return false;
+  }
   uint dex_location_checksum = dex_file.GetLocationChecksum();
   const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
                                                                     &dex_location_checksum);
   CHECK(oat_dex_file != NULL) << dex_file.GetLocation();
-  return oat_dex_file->GetOatClass(class_def_idx);
+  *oat_class = oat_dex_file->GetOatClass(class_def_idx);
+  return true;
 }
 
 static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx,
@@ -2064,7 +2076,8 @@
   return 0;
 }
 
-const OatFile::OatMethod ClassLinker::GetOatMethodFor(mirror::ArtMethod* method) {
+bool ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method) {
+  DCHECK(oat_method != nullptr);
   // Although we overwrite the trampoline of non-static methods, we may get here via the resolution
   // method for direct methods (or virtual methods made direct).
   mirror::Class* declaring_class = method->GetDeclaringClass();
@@ -2091,10 +2104,15 @@
             GetOatMethodIndexFromMethodIndex(*declaring_class->GetDexCache()->GetDexFile(),
                                              method->GetDeclaringClass()->GetDexClassDefIndex(),
                                              method->GetDexMethodIndex()));
-  const OatFile::OatClass oat_class = GetOatClass(*declaring_class->GetDexCache()->GetDexFile(),
-                                                  declaring_class->GetDexClassDefIndex());
+  OatFile::OatClass oat_class;
+  if (!FindOatClass(*declaring_class->GetDexCache()->GetDexFile(),
+                    declaring_class->GetDexClassDefIndex(),
+                    &oat_class)) {
+    return false;
+  }
 
-  return oat_class.GetOatMethod(oat_method_index);
+  *oat_method = oat_class.GetOatMethod(oat_method_index);
+  return true;
 }
 
 // Special case to get oat code without overwriting a trampoline.
@@ -2103,7 +2121,12 @@
   if (method->IsProxyMethod()) {
     return GetQuickProxyInvokeHandler();
   }
-  const void* result = GetOatMethodFor(method).GetQuickCode();
+  OatFile::OatMethod oat_method;
+  const void* result = nullptr;
+  if (FindOatMethodFor(method, &oat_method)) {
+    result = oat_method.GetQuickCode();
+  }
+
   if (result == nullptr) {
     if (method->IsNative()) {
       // No code and native? Use generic trampoline.
@@ -2126,10 +2149,16 @@
   if (method->IsProxyMethod()) {
     return GetPortableProxyInvokeHandler();
   }
-  const OatFile::OatMethod oat_method = GetOatMethodFor(method);
-  const void* result = oat_method.GetPortableCode();
+  OatFile::OatMethod oat_method;
+  const void* result = nullptr;
+  const void* quick_code = nullptr;
+  if (FindOatMethodFor(method, &oat_method)) {
+    result = oat_method.GetPortableCode();
+    quick_code = oat_method.GetQuickCode();
+  }
+
   if (result == nullptr) {
-    if (oat_method.GetQuickCode() == nullptr) {
+    if (quick_code == nullptr) {
       // No code? You must mean to go into the interpreter.
       result = GetPortableToInterpreterBridge();
     } else {
@@ -2144,14 +2173,20 @@
 
 const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
                                             uint32_t method_idx) {
-  const OatFile::OatClass oat_class = GetOatClass(dex_file, class_def_idx);
+  OatFile::OatClass oat_class;
+  if (!FindOatClass(dex_file, class_def_idx, &oat_class)) {
+    return nullptr;
+  }
   uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
   return oat_class.GetOatMethod(oat_method_idx).GetQuickCode();
 }
 
 const void* ClassLinker::GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
                                                uint32_t method_idx) {
-  const OatFile::OatClass oat_class = GetOatClass(dex_file, class_def_idx);
+  OatFile::OatClass oat_class;
+  if (!FindOatClass(dex_file, class_def_idx, &oat_class)) {
+    return nullptr;
+  }
   uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
   return oat_class.GetOatMethod(oat_method_idx).GetPortableCode();
 }
@@ -2194,7 +2229,6 @@
   const byte* class_data = dex_file.GetClassData(*dex_class_def);
   // There should always be class data if there were direct methods.
   CHECK(class_data != nullptr) << PrettyDescriptor(klass);
-  const OatFile::OatClass oat_class = GetOatClass(dex_file, klass->GetDexClassDefIndex());
   ClassDataItemIterator it(dex_file, class_data);
   // Skip fields
   while (it.HasNextStaticField()) {
@@ -2203,6 +2237,8 @@
   while (it.HasNextInstanceField()) {
     it.Next();
   }
+  OatFile::OatClass oat_class;
+  bool has_oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class);
   // Link the code of methods skipped by LinkCode.
   for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) {
     mirror::ArtMethod* method = klass->GetDirectMethod(method_index);
@@ -2210,8 +2246,13 @@
       // Only update static methods.
       continue;
     }
-    const void* portable_code = oat_class.GetOatMethod(method_index).GetPortableCode();
-    const void* quick_code = oat_class.GetOatMethod(method_index).GetQuickCode();
+    const void* portable_code = nullptr;
+    const void* quick_code = nullptr;
+    if (has_oat_class) {
+      OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
+      portable_code = oat_method.GetPortableCode();
+      quick_code = oat_method.GetQuickCode();
+    }
     const bool enter_interpreter = NeedsInterpreter(method, quick_code, portable_code);
     bool have_portable_code = false;
     if (enter_interpreter) {
@@ -2243,16 +2284,21 @@
 void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
                            const DexFile& dex_file, uint32_t dex_method_index,
                            uint32_t method_index) {
+  if (Runtime::Current()->IsCompiler()) {
+    // The following code only applies to a non-compiler runtime.
+    return;
+  }
   // Method shouldn't have already been linked.
   DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
   DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr);
-  // Every kind of method should at least get an invoke stub from the oat_method.
-  // non-abstract methods also get their code pointers.
-  const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
-  oat_method.LinkMethod(method.Get());
+  if (oat_class != nullptr) {
+    // Every kind of method should at least get an invoke stub from the oat_method.
+    // non-abstract methods also get their code pointers.
+    const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
+    oat_method.LinkMethod(method.Get());
+  }
 
   // Install entry point from interpreter.
-  Runtime* runtime = Runtime::Current();
   bool enter_interpreter = NeedsInterpreter(method.Get(),
                                             method->GetEntryPointFromQuickCompiledCode(),
                                             method->GetEntryPointFromPortableCompiledCode());
@@ -2306,6 +2352,7 @@
   }
 
   // Allow instrumentation its chance to hijack code.
+  Runtime* runtime = Runtime::Current();
   runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
                                                    method->GetEntryPointFromQuickCompiledCode(),
                                                    method->GetEntryPointFromPortableCompiledCode(),
@@ -2342,8 +2389,10 @@
     return;  // no fields or methods - for example a marker interface
   }
 
-  if (Runtime::Current()->IsStarted() && !Runtime::Current()->UseCompileTimeClassPath()) {
-    const OatFile::OatClass oat_class = GetOatClass(dex_file, klass->GetDexClassDefIndex());
+  OatFile::OatClass oat_class;
+  if (Runtime::Current()->IsStarted()
+      && !Runtime::Current()->UseCompileTimeClassPath()
+      && FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) {
     LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class);
   } else {
     LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr);
@@ -2426,9 +2475,7 @@
       return;
     }
     klass->SetDirectMethod(i, method.Get());
-    if (oat_class != nullptr) {
-      LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
-    }
+    LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
     method->SetMethodIndex(class_def_method_index);
     class_def_method_index++;
   }
@@ -2441,9 +2488,7 @@
     }
     klass->SetVirtualMethod(i, method.Get());
     DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
-    if (oat_class != nullptr) {
-      LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
-    }
+    LinkCode(method, oat_class, dex_file, it.GetMemberIndex(), class_def_method_index);
     class_def_method_index++;
   }
   DCHECK(!it.HasNext());
@@ -3224,7 +3269,7 @@
   const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file);
   // Make this work with gtests, which do not set up the image properly.
   // TODO: we should clean up gtests to set up the image path properly.
-  if (Runtime::Current()->IsCompiler() && (oat_file == NULL)) {
+  if (Runtime::Current()->IsCompiler() || (oat_file == NULL)) {
     return false;
   }
 
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 8c09042..6fc0f0e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -392,7 +392,7 @@
   }
 
  private:
-  const OatFile::OatMethod GetOatMethodFor(mirror::ArtMethod* method)
+  bool FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   OatFile& GetImageOatFile(gc::space::ImageSpace* space)
@@ -461,8 +461,9 @@
 
   void FixupStaticTrampolines(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Finds the associated oat class for a dex_file and descriptor
-  OatFile::OatClass GetOatClass(const DexFile& dex_file, uint16_t class_def_idx)
+  // Finds the associated oat class for a dex_file and descriptor. Returns whether the class
+  // was found, and sets the data in oat_class.
+  bool FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, OatFile::OatClass* oat_class)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
@@ -742,6 +743,7 @@
   friend class ImageWriter;  // for GetClassRoots
   friend class ImageDumper;  // for FindOpenedOatFileFromOatLocation
   friend class ElfPatcher;  // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
+  friend class NoDex2OatTest;  // for FindOpenedOatFileForDexFile
   FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
   FRIEND_TEST(mirror::DexCacheTest, Open);
   FRIEND_TEST(ExceptionTest, FindExceptionHandler);
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index c3304e6..f199c99 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -114,7 +114,9 @@
   bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs,
                                              dex_files.get());
 
-  if (success) {
+  if (success || !dex_files->empty()) {
+    // In the case of non-success, we have not found or could not generate the oat file.
+    // But we may still have found a dex file that we can use.
     return static_cast<jlong>(reinterpret_cast<uintptr_t>(dex_files.release()));
   } else {
     // The vector should be empty after a failed loading attempt.
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 9710a2a..810eccb 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -133,6 +133,8 @@
               const uint32_t code_offset,
               const uint32_t gc_map_offset);
 
+    OatMethod() {}
+
    private:
     template<class T>
     T GetOatPointer(uint32_t offset) const {
@@ -166,6 +168,8 @@
     // methods are not included.
     const OatMethod GetOatMethod(uint32_t method_index) const;
 
+    OatClass() {}
+
    private:
     OatClass(const OatFile* oat_file,
              mirror::Class::Status status,
@@ -174,13 +178,13 @@
              const uint32_t* bitmap_pointer,
              const OatMethodOffsets* methods_pointer);
 
-    const OatFile* const oat_file_;
+    const OatFile* oat_file_;
 
-    const mirror::Class::Status status_;
+    mirror::Class::Status status_;
 
-    const OatClassType type_;
+    OatClassType type_;
 
-    const uint32_t* const bitmap_;
+    const uint32_t* bitmap_;
 
     const OatMethodOffsets* methods_pointer_;
 
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 244f3c4..b06eb5f 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -226,6 +226,7 @@
   compiler_callbacks_ = nullptr;
   is_zygote_ = false;
   must_relocate_ = kDefaultMustRelocate;
+  dex2oat_enabled_ = true;
   if (kPoisonHeapReferences) {
     // kPoisonHeapReferences currently works only with the interpreter only.
     // TODO: make it work with the compiler.
@@ -426,6 +427,10 @@
       must_relocate_ = true;
     } else if (option == "-Xnorelocate") {
       must_relocate_ = false;
+    } else if (option == "-Xnodex2oat") {
+      dex2oat_enabled_ = false;
+    } else if (option == "-Xdex2oat") {
+      dex2oat_enabled_ = true;
     } else if (option == "-Xint") {
       interpreter_only_ = true;
     } else if (StartsWith(option, "-Xgc:")) {
@@ -716,7 +721,7 @@
   UsageMessage(stream, "The following standard options are supported:\n");
   UsageMessage(stream, "  -classpath classpath (-cp classpath)\n");
   UsageMessage(stream, "  -Dproperty=value\n");
-  UsageMessage(stream, "  -verbose:tag  ('gc', 'jni', or 'class')\n");
+  UsageMessage(stream, "  -verbose:tag ('gc', 'jni', or 'class')\n");
   UsageMessage(stream, "  -showversion\n");
   UsageMessage(stream, "  -help\n");
   UsageMessage(stream, "  -agentlib:jdwp=options\n");
@@ -726,9 +731,9 @@
   UsageMessage(stream, "  -Xrunjdwp:<options>\n");
   UsageMessage(stream, "  -Xbootclasspath:bootclasspath\n");
   UsageMessage(stream, "  -Xcheck:tag  (e.g. 'jni')\n");
-  UsageMessage(stream, "  -XmsN  (min heap, must be multiple of 1K, >= 1MB)\n");
-  UsageMessage(stream, "  -XmxN  (max heap, must be multiple of 1K, >= 2MB)\n");
-  UsageMessage(stream, "  -XssN  (stack size)\n");
+  UsageMessage(stream, "  -XmsN (min heap, must be multiple of 1K, >= 1MB)\n");
+  UsageMessage(stream, "  -XmxN (max heap, must be multiple of 1K, >= 2MB)\n");
+  UsageMessage(stream, "  -XssN (stack size)\n");
   UsageMessage(stream, "  -Xint\n");
   UsageMessage(stream, "\n");
 
@@ -782,6 +787,7 @@
   UsageMessage(stream, "  -Ximage-compiler-option dex2oat-option\n");
   UsageMessage(stream, "  -Xpatchoat:filename\n");
   UsageMessage(stream, "  -X[no]relocate\n");
+  UsageMessage(stream, "  -X[no]dex2oat (Whether to invoke dex2oat on the application)\n");
   UsageMessage(stream, "\n");
 
   UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 1cb9b3a..a4bee5e 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -49,6 +49,7 @@
   CompilerCallbacks* compiler_callbacks_;
   bool is_zygote_;
   bool must_relocate_;
+  bool dex2oat_enabled_;
   std::string patchoat_executable_;
   bool interpreter_only_;
   bool is_explicit_gc_disabled_;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index eeddac1..8a85e0d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -101,6 +101,7 @@
       must_relocate_(false),
       is_concurrent_gc_enabled_(true),
       is_explicit_gc_disabled_(false),
+      dex2oat_enabled_(true),
       default_stack_size_(0),
       heap_(nullptr),
       max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
@@ -559,6 +560,7 @@
   must_relocate_ = options->must_relocate_;
   is_zygote_ = options->is_zygote_;
   is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
+  dex2oat_enabled_ = options->dex2oat_enabled_;
 
   vfprintf_ = options->hook_vfprintf_;
   exit_ = options->hook_exit_;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 9c8a24c..34ccdcb 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -106,6 +106,10 @@
     return must_relocate_;
   }
 
+  bool IsDex2OatEnabled() const {
+    return dex2oat_enabled_;
+  }
+
   CompilerCallbacks* GetCompilerCallbacks() {
     return compiler_callbacks_;
   }
@@ -504,6 +508,7 @@
   bool must_relocate_;
   bool is_concurrent_gc_enabled_;
   bool is_explicit_gc_disabled_;
+  bool dex2oat_enabled_;
 
   std::string compiler_executable_;
   std::string patchoat_executable_;
diff --git a/test/116-nodex2oat/expected.txt b/test/116-nodex2oat/expected.txt
new file mode 100644
index 0000000..05b1c2f
--- /dev/null
+++ b/test/116-nodex2oat/expected.txt
@@ -0,0 +1,6 @@
+Run -Xnodex2oat
+Has oat is false, is dex2oat enabled is false.
+Run -Xdex2oat
+Has oat is true, is dex2oat enabled is true.
+Run default
+Has oat is true, is dex2oat enabled is true.
diff --git a/test/116-nodex2oat/info.txt b/test/116-nodex2oat/info.txt
new file mode 100644
index 0000000..f063d9f
--- /dev/null
+++ b/test/116-nodex2oat/info.txt
@@ -0,0 +1 @@
+Test that disables dex2oat'ing the application.
diff --git a/test/116-nodex2oat/nodex2oat.cc b/test/116-nodex2oat/nodex2oat.cc
new file mode 100644
index 0000000..4326db0
--- /dev/null
+++ b/test/116-nodex2oat/nodex2oat.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "class_linker.h"
+#include "dex_file-inl.h"
+#include "mirror/class-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+class NoDex2OatTest {
+ public:
+  static bool hasOat(jclass cls) {
+    ScopedObjectAccess soa(Thread::Current());
+    mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+    const DexFile& dex_file = klass->GetDexFile();
+    const OatFile* oat_file =
+        Runtime::Current()->GetClassLinker()->FindOpenedOatFileForDexFile(dex_file);
+    return oat_file != nullptr;
+  }
+};
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasOat(JNIEnv*, jclass cls) {
+  return NoDex2OatTest::hasOat(cls);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isDex2OatEnabled(JNIEnv*, jclass cls) {
+  return Runtime::Current()->IsDex2OatEnabled();
+}
+
+}  // namespace art
diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run
new file mode 100755
index 0000000..5ffeecd
--- /dev/null
+++ b/test/116-nodex2oat/run
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Remove prebuild from the flags, this test is for testing not having oat files.
+flags="${@/--prebuild/}"
+
+# Make sure we can run without an oat file,
+echo "Run -Xnodex2oat"
+${RUN} ${flags} --runtime-option -Xnodex2oat
+
+# Make sure we can run with the oat file.
+echo "Run -Xdex2oat"
+${RUN} ${flags} --runtime-option -Xdex2oat
+
+# Make sure we can run with the default settings.
+echo "Run default"
+${RUN} ${flags}
diff --git a/test/116-nodex2oat/src/Main.java b/test/116-nodex2oat/src/Main.java
new file mode 100644
index 0000000..37ac9d5
--- /dev/null
+++ b/test/116-nodex2oat/src/Main.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println(
+        "Has oat is " + hasOat() + ", is dex2oat enabled is " + isDex2OatEnabled() + ".");
+
+    if (hasOat() && !isDex2OatEnabled()) {
+      throw new Error("Application with dex2oat disabled runs with an oat file");
+    } else if (!hasOat() && isDex2OatEnabled()) {
+      throw new Error("Application with dex2oat enabled runs without an oat file");
+    }
+  }
+
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  private native static boolean hasOat();
+
+  private native static boolean isDex2OatEnabled();
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index f3563a4..3871b28 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -23,7 +23,8 @@
   004-SignalTest/signaltest.cc \
   004-ReferenceMap/stack_walk_refmap_jni.cc \
   004-StackWalk/stack_walk_jni.cc \
-  004-UnsafeTest/unsafe_test.cc
+  004-UnsafeTest/unsafe_test.cc \
+  116-nodex2oat/nodex2oat.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ifdef TARGET_2ND_ARCH