Merge "Tests should include exception stack traces on failure." into dalvik-dev
diff --git a/Android.mk b/Android.mk
index eda1e33..df2b843 100644
--- a/Android.mk
+++ b/Android.mk
@@ -155,7 +155,6 @@
 $(eval $(call build-art-cache-oat,system/app/ContactsProvider.apk))
 $(eval $(call build-art-cache-oat,system/app/DefaultContainerService.apk))
 $(eval $(call build-art-cache-oat,system/app/DeskClockGoogle.apk))
-$(eval $(call build-art-cache-oat,system/app/Development.apk))
 $(eval $(call build-art-cache-oat,system/app/DownloadProvider.apk))
 $(eval $(call build-art-cache-oat,system/app/DownloadProviderUi.apk))
 $(eval $(call build-art-cache-oat,system/app/DrmProvider.apk))
@@ -193,7 +192,6 @@
 $(eval $(call build-art-cache-oat,system/app/Music2.apk))
 $(eval $(call build-art-cache-oat,system/app/MusicFX.apk))
 $(eval $(call build-art-cache-oat,system/app/MyVerizon.apk))
-$(eval $(call build-art-cache-oat,system/app/NetSpeed.apk))
 $(eval $(call build-art-cache-oat,system/app/NetworkLocation.apk))
 $(eval $(call build-art-cache-oat,system/app/Nfc.apk))
 $(eval $(call build-art-cache-oat,system/app/OneTimeInitializer.apk))
@@ -208,10 +206,7 @@
 $(eval $(call build-art-cache-oat,system/app/SetupWizard.apk))
 $(eval $(call build-art-cache-oat,system/app/SharedStorageBackup.apk))
 $(eval $(call build-art-cache-oat,system/app/SoundRecorder.apk))
-$(eval $(call build-art-cache-oat,system/app/SpeechRecorder.apk))
 $(eval $(call build-art-cache-oat,system/app/SPG.apk))
-$(eval $(call build-art-cache-oat,system/app/StingrayProgramMenu.apk))
-$(eval $(call build-art-cache-oat,system/app/StingrayProgramMenuSystem.apk))
 $(eval $(call build-art-cache-oat,system/app/Stk.apk))
 $(eval $(call build-art-cache-oat,system/app/Street.apk))
 $(eval $(call build-art-cache-oat,system/app/SyncMLSvc.apk))
@@ -250,6 +245,10 @@
 $(eval $(call build-art-cache-oat,system/framework/send_bug.jar))
 $(eval $(call build-art-cache-oat,system/framework/svc.jar))
 
+ifeq ($(TARGET_BUILD_VARIANT),eng)
+  $(eval $(call build-art-cache-oat,system/app/Development.apk))
+endif
+
 else
 
 ifeq ($(TARGET_PRODUCT),$(filter $(TARGET_PRODUCT),soju sojus))
@@ -270,7 +269,6 @@
 $(eval $(call build-art-cache-oat,system/app/ContactsProvider.apk))
 $(eval $(call build-art-cache-oat,system/app/DefaultContainerService.apk))
 $(eval $(call build-art-cache-oat,system/app/DeskClockGoogle.apk))
-$(eval $(call build-art-cache-oat,system/app/Development.apk))
 $(eval $(call build-art-cache-oat,system/app/DownloadProvider.apk))
 $(eval $(call build-art-cache-oat,system/app/DownloadProviderUi.apk))
 $(eval $(call build-art-cache-oat,system/app/DrmProvider.apk))
@@ -306,7 +304,6 @@
 $(eval $(call build-art-cache-oat,system/app/Mms.apk))
 $(eval $(call build-art-cache-oat,system/app/Music2.apk))
 $(eval $(call build-art-cache-oat,system/app/MusicFX.apk))
-$(eval $(call build-art-cache-oat,system/app/NetSpeed.apk))
 $(eval $(call build-art-cache-oat,system/app/NetworkLocation.apk))
 $(eval $(call build-art-cache-oat,system/app/Nfc.apk))
 $(eval $(call build-art-cache-oat,system/app/OneTimeInitializer.apk))
@@ -319,9 +316,6 @@
 $(eval $(call build-art-cache-oat,system/app/SetupWizard.apk))
 $(eval $(call build-art-cache-oat,system/app/SharedStorageBackup.apk))
 $(eval $(call build-art-cache-oat,system/app/SoundRecorder.apk))
-$(eval $(call build-art-cache-oat,system/app/SpeechRecorder.apk))
-$(eval $(call build-art-cache-oat,system/app/StingrayProgramMenu.apk))
-$(eval $(call build-art-cache-oat,system/app/StingrayProgramMenuSystem.apk))
 $(eval $(call build-art-cache-oat,system/app/Street.apk))
 $(eval $(call build-art-cache-oat,system/app/SystemUI.apk))
 $(eval $(call build-art-cache-oat,system/app/TagGoogle.apk))
@@ -355,6 +349,10 @@
 $(eval $(call build-art-cache-oat,system/framework/send_bug.jar))
 $(eval $(call build-art-cache-oat,system/framework/svc.jar))
 
+ifeq ($(TARGET_BUILD_VARIANT),eng)
+  $(eval $(call build-art-cache-oat,system/app/Development.apk))
+endif
+
 else
 
 ifeq ($(TARGET_PRODUCT),$(filter $(TARGET_PRODUCT),stingray))
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 51e5f3c..f343ed4 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1924,6 +1924,7 @@
   const DexFile& dex_file = FindDexFile(klass->GetDexCache());
   if (VerifyClassUsingOatFile(dex_file, klass) ||
       verifier::DexVerifier::VerifyClass(klass, error_msg)) {
+    DCHECK(!Thread::Current()->IsExceptionPending());
     // Make sure all classes referenced by catch blocks are resolved
     ResolveClassExceptionHandlerTypes(dex_file, klass);
     klass->SetStatus(Class::kStatusVerified);
@@ -1934,7 +1935,7 @@
         << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
         << " because: " << error_msg;
     Thread* self = Thread::Current();
-    CHECK(!self->IsExceptionPending()) << self->GetException()->Dump();
+    CHECK(!self->IsExceptionPending());
     self->ThrowNewException("Ljava/lang/VerifyError;", error_msg.c_str());
     CHECK_EQ(klass->GetStatus(), Class::kStatusVerifying) << PrettyDescriptor(klass);
     klass->SetStatus(Class::kStatusError);
@@ -1961,17 +1962,34 @@
   UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(class_def_index));
   CHECK(oat_class.get() != NULL) << descriptor;
   Class::Status status = oat_class->GetStatus();
-  if (status == Class::kStatusError) {
-    Thread* self = Thread::Current();
-    self->ThrowNewExceptionF("Ljava/lang/VerifyError;", "Compile-time verification of %s failed",
-        PrettyDescriptor(klass).c_str());
-    klass->SetStatus(Class::kStatusError);
-    return true;
-  }
   if (status == Class::kStatusVerified || status == Class::kStatusInitialized) {
     return true;
   }
+  if (status == Class::kStatusError) {
+    // Compile time verification failed. Compile time verification can fail because we have
+    // incomplete type information. Consider the following:
+    // class ... {
+    //   Foo x;
+    //   .... () {
+    //     if (...) {
+    //       v1 gets assigned a type of resolved class Foo
+    //     } else {
+    //       v1 gets assigned a type of unresolved class Bar
+    //     }
+    //     iput x = v1
+    // } }
+    // when we merge v1 following the if-the-else it results in Conflict
+    // (see verifier::RegType::Merge) as we can't know the type of Bar and we could possibly be
+    // allowing an unsafe assignment to the field x in the iput (javac may have compiled this as
+    // it knew Bar was a sub-class of Foo, but for us this may have been moved into a separate apk
+    // at compile time).
+    return false;
+  }
   if (status == Class::kStatusNotReady) {
+    // Status is uninitialized if we couldn't determine the status at compile time, for example,
+    // not loading the class.
+    // TODO: when the verifier doesn't rely on Class-es failing to resolve/load the type hierarchy
+    // isn't a problem and this case shouldn't occur
     return false;
   }
   LOG(FATAL) << "Unexpected class status: " << status;
diff --git a/src/dalvik_system_VMRuntime.cc b/src/dalvik_system_VMRuntime.cc
index 5dcbb41..76da622 100644
--- a/src/dalvik_system_VMRuntime.cc
+++ b/src/dalvik_system_VMRuntime.cc
@@ -98,12 +98,21 @@
   return toStringArray(env, Runtime::Current()->GetProperties());
 }
 
+// This is for backward compatibility with dalvik which returned the
+// meaningless "." when no boot classpath or classpath was
+// specified. Unfortunately, some tests were using java.class.path to
+// lookup relative file locations, so they are counting on this to be
+// ".", presumably some applications or libraries could have as well.
+const char* DefaultToDot(const std::string& class_path) {
+  return class_path.empty() ? "." : class_path.c_str();
+}
+
 jstring VMRuntime_bootClassPath(JNIEnv* env, jobject) {
-  return env->NewStringUTF(Runtime::Current()->GetBootClassPath().c_str());
+  return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetBootClassPath()));
 }
 
 jstring VMRuntime_classPath(JNIEnv* env, jobject) {
-  return env->NewStringUTF(Runtime::Current()->GetClassPath().c_str());
+  return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetClassPath()));
 }
 
 jstring VMRuntime_vmVersion(JNIEnv* env, jobject) {
diff --git a/src/dex_instruction.h b/src/dex_instruction.h
index 26e5653..975ec86 100644
--- a/src/dex_instruction.h
+++ b/src/dex_instruction.h
@@ -195,7 +195,7 @@
 
   int GetVerifyTypeArgumentC() const {
     return (kInstructionVerifyFlags[Opcode()] & (kVerifyRegC | kVerifyRegCField |
-             kVerifyRegCNewArray | kVerifyRegBType | kVerifyRegBWide));
+             kVerifyRegCNewArray | kVerifyRegCType | kVerifyRegCWide));
   }
 
   int GetVerifyExtraFlags() const {
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index c03b724..584bddd 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -210,7 +210,7 @@
         } else if (!IsUnresolvedTypes() && GetClass()->IsInterface()) {
           return true;  // We allow assignment to any interface, see comment in ClassJoin
         } else if (IsJavaLangObjectArray()) {
-          return src.IsObjectArray();  // All reference arrays may be assigned to Object[]
+          return src.IsObjectArrayTypes();  // All reference arrays may be assigned to Object[]
         } else if (!IsUnresolvedTypes() && !src.IsUnresolvedTypes() &&
                    GetClass()->IsAssignableFrom(src.GetClass())) {
           // We're assignable from the Class point-of-view
@@ -553,7 +553,7 @@
 }
 
 const RegType& RegTypeCache::GetComponentType(const RegType& array, const ClassLoader* loader) {
-  CHECK(array.IsArrayClass());
+  CHECK(array.IsArrayTypes());
   if (array.IsUnresolvedTypes()) {
     std::string descriptor(array.GetDescriptor()->ToModifiedUtf8());
     std::string component(descriptor.substr(1, descriptor.size() - 1));
@@ -629,24 +629,6 @@
   return this_type;
 }
 
-Class* RegisterLine::GetClassFromRegister(uint32_t vsrc) const {
-  /* get the element type of the array held in vsrc */
-  const RegType& type = GetRegisterType(vsrc);
-  /* if "always zero", we allow it to fail at runtime */
-  if (type.IsZero()) {
-    return NULL;
-  } else if (!type.IsReferenceTypes()) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "tried to get class from non-ref register v" << vsrc
-                                         << " (type=" << type << ")";
-    return NULL;
-  } else if (type.IsUninitializedReference()) {
-    verifier_->Fail(VERIFY_ERROR_GENERIC) << "register " << vsrc << " holds uninitialized reference";
-    return NULL;
-  } else {
-    return type.GetClass();
-  }
-}
-
 bool RegisterLine::VerifyRegisterType(uint32_t vsrc, const RegType& check_type) {
   // Verify the src register type against the check type refining the type of the register
   const RegType& src_type = GetRegisterType(vsrc);
@@ -2119,7 +2101,7 @@
     case Instruction::ARRAY_LENGTH: {
       const RegType& res_type = work_line_->GetRegisterType(dec_insn.vB_);
       if (res_type.IsReferenceTypes()) {
-        if (!res_type.IsArrayClass() && !res_type.IsZero()) {  // ie not an array or null
+        if (!res_type.IsArrayTypes() && !res_type.IsZero()) {  // ie not an array or null
           Fail(VERIFY_ERROR_GENERIC) << "array-length on non-array " << res_type;
         } else {
           work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Integer());
@@ -2147,7 +2129,7 @@
     case Instruction::NEW_ARRAY: {
       const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vC_);
       // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
-      if (!res_type.IsArrayClass()) {
+      if (!res_type.IsArrayTypes()) {
         Fail(VERIFY_ERROR_GENERIC) << "new-array on non-array class " << res_type;
       } else {
         /* make sure "size" register is valid type */
@@ -2161,7 +2143,7 @@
     case Instruction::FILLED_NEW_ARRAY_RANGE: {
       const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB_);
       // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
-      if (!res_type.IsArrayClass()) {
+      if (!res_type.IsArrayTypes()) {
         Fail(VERIFY_ERROR_GENERIC) << "filled-new-array on non-array class";
       } else {
         bool is_range = (dec_insn.opcode_ == Instruction::FILLED_NEW_ARRAY_RANGE);
@@ -2211,31 +2193,27 @@
 
     case Instruction::FILL_ARRAY_DATA: {
       /* Similar to the verification done for APUT */
-      Class* res_class = work_line_->GetClassFromRegister(dec_insn.vA_);
-      if (failure_ == VERIFY_ERROR_NONE) {
-        /* res_class can be null if the reg type is Zero */
-        if (res_class != NULL) {
-          Class* component_type = res_class->GetComponentType();
-          if (!res_class->IsArrayClass() || !component_type->IsPrimitive()  ||
-              component_type->IsPrimitiveVoid()) {
-            Fail(VERIFY_ERROR_GENERIC) << "invalid fill-array-data on "
-                                       << PrettyDescriptor(res_class);
+      const RegType& array_type = work_line_->GetRegisterType(dec_insn.vA_);
+      /* array_type can be null if the reg type is Zero */
+      if (!array_type.IsZero()) {
+        const RegType& component_type = reg_types_.GetComponentType(array_type,
+                                                    method_->GetDeclaringClass()->GetClassLoader());
+        DCHECK(!component_type.IsUnknown());
+        if (!array_type.IsArrayTypes() || component_type.IsNonZeroReferenceTypes()) {
+          Fail(VERIFY_ERROR_GENERIC) << "invalid fill-array-data on " << array_type;
+        } else {
+          // Now verify if the element width in the table matches the element width declared in
+          // the array
+          const uint16_t* array_data = insns + (insns[1] | (((int32_t) insns[2]) << 16));
+          if (array_data[0] != Instruction::kArrayDataSignature) {
+            Fail(VERIFY_ERROR_GENERIC) << "invalid magic for array-data";
           } else {
-            const RegType& value_type = reg_types_.FromClass(component_type);
-            DCHECK(!value_type.IsUnknown());
-            // Now verify if the element width in the table matches the element width declared in
-            // the array
-            const uint16_t* array_data = insns + (insns[1] | (((int32_t) insns[2]) << 16));
-            if (array_data[0] != Instruction::kArrayDataSignature) {
-              Fail(VERIFY_ERROR_GENERIC) << "invalid magic for array-data";
-            } else {
-              size_t elem_width = Primitive::ComponentSize(component_type->GetPrimitiveType());
-              // Since we don't compress the data in Dex, expect to see equal width of data stored
-              // in the table and expected from the array class.
-              if (array_data[1] != elem_width) {
-                Fail(VERIFY_ERROR_GENERIC) << "array-data size mismatch (" << array_data[1]
-                                           << " vs " << elem_width << ")";
-              }
+            size_t elem_width = Primitive::ComponentSize(component_type.GetPrimitiveType());
+            // Since we don't compress the data in Dex, expect to see equal width of data stored
+            // in the table and expected from the array class.
+            if (array_data[1] != elem_width) {
+              Fail(VERIFY_ERROR_GENERIC) << "array-data size mismatch (" << array_data[1]
+                                         << " vs " << elem_width << ")";
             }
           }
         }
@@ -3060,17 +3038,17 @@
             common_super = &reg_types_.JavaLangThrowable();
           } else {
             const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
-            /* TODO: on error do we want to keep going?  If we don't fail this we run the risk of
-             * having a non-Throwable introduced at runtime. However, that won't pass an instanceof
-             * test, so is essentially harmless.
-             */
-            if(!reg_types_.JavaLangThrowable().IsAssignableFrom(exception)) {
+            if (common_super == NULL) {
+              // Unconditionally assign for the first handler. We don't assert this is a Throwable
+              // as that is caught at runtime
+              common_super = &exception;
+            } else if(!reg_types_.JavaLangThrowable().IsAssignableFrom(exception)) {
+              // We don't know enough about the type and the common path merge will result in
+              // Conflict. Fail here knowing the correct thing can be done at runtime.
               Fail(VERIFY_ERROR_GENERIC) << "unexpected non-exception class " << exception;
               return reg_types_.Unknown();
-            } else if (common_super == NULL) {
-              common_super = &exception;
             } else if (common_super->Equals(exception)) {
-              // nothing to do
+              // odd case, but nothing to do
             } else {
               common_super = &common_super->Merge(exception, &reg_types_);
               CHECK(reg_types_.JavaLangThrowable().IsAssignableFrom(*common_super));
@@ -3084,6 +3062,7 @@
   if (common_super == NULL) {
     /* no catch blocks, or no catches with classes we can find */
     Fail(VERIFY_ERROR_GENERIC) << "unable to find exception handler";
+    return reg_types_.Unknown();
   }
   return *common_super;
 }
@@ -3284,45 +3263,39 @@
   if (!index_type.IsArrayIndexTypes()) {
     Fail(VERIFY_ERROR_GENERIC) << "Invalid reg type for array index (" << index_type << ")";
   } else {
-    Class* array_class = work_line_->GetClassFromRegister(dec_insn.vB_);
-    if (failure_ == VERIFY_ERROR_NONE) {
-      if (array_class == NULL) {
-        // Null array class; this code path will fail at runtime. Infer a merge-able type from the
-        // instruction type. TODO: have a proper notion of bottom here.
-        if (!is_primitive || insn_type.IsCategory1Types()) {
-          // Reference or category 1
-          work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Zero());
-        } else {
-          // Category 2
-          work_line_->SetRegisterType(dec_insn.vA_, reg_types_.ConstLo());
-        }
+    const RegType& array_type = work_line_->GetRegisterType(dec_insn.vB_);
+    if (array_type.IsZero()) {
+      // Null array class; this code path will fail at runtime. Infer a merge-able type from the
+      // instruction type. TODO: have a proper notion of bottom here.
+      if (!is_primitive || insn_type.IsCategory1Types()) {
+        // Reference or category 1
+        work_line_->SetRegisterType(dec_insn.vA_, reg_types_.Zero());
       } else {
-        /* verify the class */
-        Class* component_class = array_class->GetComponentType();
-        const RegType& component_type = reg_types_.FromClass(component_class);
-        if (!array_class->IsArrayClass()) {
-          Fail(VERIFY_ERROR_GENERIC) << "not array type "
-              << PrettyDescriptor(array_class) << " with aget";
-        } else if (component_class->IsPrimitive() && !is_primitive) {
-          Fail(VERIFY_ERROR_GENERIC) << "primitive array type "
-                                     << PrettyDescriptor(array_class)
-                                     << " source for aget-object";
-        } else if (!component_class->IsPrimitive() && is_primitive) {
-          Fail(VERIFY_ERROR_GENERIC) << "reference array type "
-                                     << PrettyDescriptor(array_class)
-                                     << " source for category 1 aget";
-        } else if (is_primitive && !insn_type.Equals(component_type) &&
-                   !((insn_type.IsInteger() && component_type.IsFloat()) ||
-                     (insn_type.IsLong() && component_type.IsDouble()))) {
-          Fail(VERIFY_ERROR_GENERIC) << "array type "
-              << PrettyDescriptor(array_class)
+        // Category 2
+        work_line_->SetRegisterType(dec_insn.vA_, reg_types_.ConstLo());
+      }
+    } else if (!array_type.IsArrayTypes()) {
+      Fail(VERIFY_ERROR_GENERIC) << "not array type " << array_type << " with aget";
+    } else {
+      /* verify the class */
+      const RegType& component_type = reg_types_.GetComponentType(array_type,
+                                                    method_->GetDeclaringClass()->GetClassLoader());
+      if (!component_type.IsReferenceTypes() && !is_primitive) {
+        Fail(VERIFY_ERROR_GENERIC) << "primitive array type " << array_type
+            << " source for aget-object";
+      } else if (component_type.IsNonZeroReferenceTypes() && is_primitive) {
+        Fail(VERIFY_ERROR_GENERIC) << "reference array type " << array_type
+            << " source for category 1 aget";
+      } else if (is_primitive && !insn_type.Equals(component_type) &&
+                 !((insn_type.IsInteger() && component_type.IsFloat()) ||
+                   (insn_type.IsLong() && component_type.IsDouble()))) {
+          Fail(VERIFY_ERROR_GENERIC) << "array type " << array_type
               << " incompatible with aget of type " << insn_type;
-        } else {
+      } else {
           // Use knowledge of the field type which is stronger than the type inferred from the
           // instruction, which can't differentiate object types and ints from floats, longs from
           // doubles.
           work_line_->SetRegisterType(dec_insn.vA_, component_type);
-        }
       }
     }
   }
@@ -3334,38 +3307,32 @@
   if (!index_type.IsArrayIndexTypes()) {
     Fail(VERIFY_ERROR_GENERIC) << "Invalid reg type for array index (" << index_type << ")";
   } else {
-    Class* array_class = work_line_->GetClassFromRegister(dec_insn.vB_);
-    if (failure_ == VERIFY_ERROR_NONE) {
-      if (array_class == NULL) {
-        // Null array class; this code path will fail at runtime. Infer a merge-able type from the
-        // instruction type.
+    const RegType& array_type = work_line_->GetRegisterType(dec_insn.vB_);
+    if (array_type.IsZero()) {
+      // Null array type; this code path will fail at runtime. Infer a merge-able type from the
+      // instruction type.
+    } else if (!array_type.IsArrayTypes()) {
+      Fail(VERIFY_ERROR_GENERIC) << "not array type " << array_type << " with aput";
+    } else {
+      /* verify the class */
+      const RegType& component_type = reg_types_.GetComponentType(array_type,
+                                                    method_->GetDeclaringClass()->GetClassLoader());
+      if (!component_type.IsReferenceTypes() && !is_primitive) {
+        Fail(VERIFY_ERROR_GENERIC) << "primitive array type " << array_type
+            << " source for aput-object";
+      } else if (component_type.IsNonZeroReferenceTypes() && is_primitive) {
+        Fail(VERIFY_ERROR_GENERIC) << "reference array type " << array_type
+            << " source for category 1 aput";
+      } else if (is_primitive && !insn_type.Equals(component_type) &&
+                 !((insn_type.IsInteger() && component_type.IsFloat()) ||
+                   (insn_type.IsLong() && component_type.IsDouble()))) {
+        Fail(VERIFY_ERROR_GENERIC) << "array type " << array_type
+            << " incompatible with aput of type " << insn_type;
       } else {
-        /* verify the class */
-        Class* component_class = array_class->GetComponentType();
-        const RegType& component_type = reg_types_.FromClass(component_class);
-        if (!array_class->IsArrayClass()) {
-          Fail(VERIFY_ERROR_GENERIC) << "not array type "
-              << PrettyDescriptor(array_class) << " with aput";
-        } else if (component_class->IsPrimitive() && !is_primitive) {
-          Fail(VERIFY_ERROR_GENERIC) << "primitive array type "
-                                     << PrettyDescriptor(array_class)
-                                     << " source for aput-object";
-        } else if (!component_class->IsPrimitive() && is_primitive) {
-          Fail(VERIFY_ERROR_GENERIC) << "reference array type "
-                                     << PrettyDescriptor(array_class)
-                                     << " source for category 1 aput";
-        } else if (is_primitive && !insn_type.Equals(component_type) &&
-                   !((insn_type.IsInteger() && component_type.IsFloat()) ||
-                     (insn_type.IsLong() && component_type.IsDouble()))) {
-          Fail(VERIFY_ERROR_GENERIC) << "array type "
-              << PrettyDescriptor(array_class)
-              << " incompatible with aput of type " << insn_type;
-        } else {
-          // The instruction agrees with the type of array, confirm the value to be stored does too
-          // Note: we use the instruction type (rather than the component type) for aput-object as
-          // incompatible classes will be caught at runtime as an array store exception
-          work_line_->VerifyRegisterType(dec_insn.vA_, is_primitive ? component_type : insn_type);
-        }
+        // The instruction agrees with the type of array, confirm the value to be stored does too
+        // Note: we use the instruction type (rather than the component type) for aput-object as
+        // incompatible classes will be caught at runtime as an array store exception
+        work_line_->VerifyRegisterType(dec_insn.vA_, is_primitive ? component_type : insn_type);
       }
     }
   }
@@ -3603,7 +3570,7 @@
 
 void DexVerifier::VerifyFilledNewArrayRegs(const Instruction::DecodedInstruction& dec_insn,
                                            const RegType& res_type, bool is_range) {
-  DCHECK(res_type.IsArrayClass()) << res_type;  // Checked before calling.
+  DCHECK(res_type.IsArrayTypes()) << res_type;  // Checked before calling.
   /*
    * Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of the
    * list and fail. It's legal, if silly, for arg_count to be zero.
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index 032e777..b5325ff 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -212,34 +212,8 @@
   bool IsJavaLangObject() const {
     return IsReference() && GetClass()->IsObjectClass();
   }
-  bool IsObjectArray() const {
-    if (IsReference()) {
-      Class* type = GetClass();
-      return type->IsArrayClass() && !type->GetComponentType()->IsPrimitive();
-    } else if (IsUnresolvedTypes()) {
-      // Primitive arrays will always resolve
-      DCHECK(GetDescriptor()->CharAt(1) == 'L' || GetDescriptor()->CharAt(1) == '[');
-      return GetDescriptor()->CharAt(0) == '[';
-    }
-    return false;
-  }
-  bool IsJavaLangObjectArray() const {
-    if (IsReference()) {
-      Class* type = GetClass();
-      return type->IsArrayClass() && type->GetComponentType()->IsObjectClass();
-    }
-    return false;
-  }
-  bool IsInstantiableTypes() const {
-    return IsUnresolvedTypes() || (IsNonZeroReferenceTypes() && GetClass()->IsInstantiable());
-  }
-  String* GetDescriptor() const {
-    DCHECK(IsUnresolvedTypes());
-    DCHECK(klass_or_descriptor_ != NULL);
-    DCHECK(klass_or_descriptor_->GetClass()->IsStringClass());
-    return down_cast<String*>(klass_or_descriptor_);
-  }
-  bool IsArrayClass() const {
+
+  bool IsArrayTypes() const {
     if (IsUnresolvedTypes()) {
       return GetDescriptor()->CharAt(0) == '[';
     } else if (!IsConstant()) {
@@ -249,6 +223,61 @@
     }
   }
 
+  bool IsObjectArrayTypes() const {
+    if (IsUnresolvedTypes()) {
+      // Primitive arrays will always resolve
+      DCHECK(GetDescriptor()->CharAt(1) == 'L' || GetDescriptor()->CharAt(1) == '[');
+      return GetDescriptor()->CharAt(0) == '[';
+    } else if (!IsConstant()) {
+      Class* type = GetClass();
+      return type->IsArrayClass() && !type->GetComponentType()->IsPrimitive();
+    } else {
+      return false;
+    }
+  }
+
+  Primitive::Type GetPrimitiveType() const {
+    if (IsNonZeroReferenceTypes()) {
+      return Primitive::kPrimNot;
+    } else if (IsBooleanTypes()) {
+      return Primitive::kPrimBoolean;
+    } else if (IsByteTypes()) {
+      return Primitive::kPrimByte;
+    } else if (IsShortTypes()) {
+      return Primitive::kPrimShort;
+    } else if (IsCharTypes()) {
+      return Primitive::kPrimChar;
+    } else if (IsFloat()) {
+      return Primitive::kPrimFloat;
+    } else if (IsIntegralTypes()) {
+      return Primitive::kPrimInt;
+    } else if (IsDouble()) {
+      return Primitive::kPrimDouble;
+    } else {
+      DCHECK(IsLongTypes());
+      return Primitive::kPrimLong;
+    }
+  }
+
+  bool IsJavaLangObjectArray() const {
+    if (IsReference()) {
+      Class* type = GetClass();
+      return type->IsArrayClass() && type->GetComponentType()->IsObjectClass();
+    }
+    return false;
+  }
+
+  bool IsInstantiableTypes() const {
+    return IsUnresolvedTypes() || (IsNonZeroReferenceTypes() && GetClass()->IsInstantiable());
+  }
+
+  String* GetDescriptor() const {
+    DCHECK(IsUnresolvedTypes());
+    DCHECK(klass_or_descriptor_ != NULL);
+    DCHECK(klass_or_descriptor_->GetClass()->IsStringClass());
+    return down_cast<String*>(klass_or_descriptor_);
+  }
+
   uint16_t GetId() const {
     return cache_id_;
   }
@@ -668,13 +697,6 @@
   const RegType& GetInvocationThis(const Instruction::DecodedInstruction& dec_insn);
 
   /*
-   * Get the value from a register, and cast it to a Class. Sets "*failure" if something fails.
-   * This fails if the register holds an uninitialized class.
-   * If the register holds kRegTypeZero, this returns a NULL pointer.
-   */
-  Class* GetClassFromRegister(uint32_t vsrc) const;
-
-  /*
    * Verify types for a simple two-register instruction (e.g. "neg-int").
    * "dst_type" is stored into vA, and "src_type" is verified against vB.
    */
@@ -1139,9 +1161,8 @@
 
   /*
    * For the "move-exception" instruction at "work_insn_idx_", which must be at an exception handler
-   * address, determine the first common superclass of all exceptions that can land here.
-   * Returns NULL if no matching exception handler can be found, or if the exception is not a
-   * subclass of Throwable.
+   * address, determine the Join of all exceptions that can land here. Fails if no matching
+   * exception handler can be found or if the Join of exception types fails.
    */
   const RegType& GetCaughtExceptionType();
 
diff --git a/src/runtime.cc b/src/runtime.cc
index 726b30c..306bb87 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -109,6 +109,10 @@
     }
     gAborting = true;
     os << "Runtime aborting...\n";
+    if (Runtime::Current() == NULL) {
+      os << "(Runtime does not yet exist!)\n";
+      return;
+    }
     Thread* self = Thread::Current();
     if (self == NULL) {
       os << "(Aborting thread was not attached to runtime!)\n";
@@ -259,14 +263,10 @@
   const char* boot_class_path = getenv("BOOTCLASSPATH");
   if (boot_class_path != NULL) {
     parsed->boot_class_path_ = boot_class_path;
-  } else {
-    parsed->boot_class_path_ = ".";
   }
   const char* class_path = getenv("CLASSPATH");
   if (class_path != NULL) {
     parsed->class_path_ = class_path;
-  } else {
-    parsed->class_path_ = ".";
   }
 #ifdef NDEBUG
   // -Xcheck:jni is off by default for regular builds...