Support for a set of verifier failures.

Change-Id: Idd0f8944bde7ee27271c3f68a2141b5a5265c382
diff --git a/src/class_linker.h b/src/class_linker.h
index a8fa01d..1925a9d 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -194,19 +194,6 @@
                       const ClassLoader* class_loader,
                       bool is_static);
 
-  Field* ResolveFieldJLS(uint32_t field_idx, const Method* referrer) {
-    Field* resolved_field =
-        referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx);
-    if (UNLIKELY(resolved_field == NULL)) {
-      Class* declaring_class = referrer->GetDeclaringClass();
-      DexCache* dex_cache = declaring_class->GetDexCache();
-      const ClassLoader* class_loader = declaring_class->GetClassLoader();
-      const DexFile& dex_file = FindDexFile(dex_cache);
-      resolved_field = ResolveFieldJLS(dex_file, field_idx, dex_cache, class_loader);
-    }
-    return resolved_field;
-  }
-
   // Resolve a field with a given ID from the DexFile, storing the
   // result in DexCache. The ClassLinker and ClassLoader are used as
   // in ResolveType. No is_static argument is provided so that Java
diff --git a/src/object_utils.h b/src/object_utils.h
index 59ef515..ceef186 100644
--- a/src/object_utils.h
+++ b/src/object_utils.h
@@ -183,7 +183,13 @@
   }
 
   std::string GetLocation() {
-    return GetDexCache()->GetLocation()->ToModifiedUtf8();
+    DexCache* dex_cache = GetDexCache();
+    if (dex_cache != NULL && !klass_->IsProxyClass()) {
+      return dex_cache->GetLocation()->ToModifiedUtf8();
+    } else {
+      // Arrays and proxies are generated and have no corresponding dex file location.
+      return "generated class";
+    }
   }
 
   const DexFile& GetDexFile() {
@@ -196,6 +202,16 @@
     return *result;
   }
 
+  DexCache* GetDexCache() {
+    DexCache* result = dex_cache_;
+    if (result == NULL) {
+      DCHECK(klass_ != NULL);
+      result = klass_->GetDexCache();
+      dex_cache_ = result;
+    }
+    return result;
+  }
+
  private:
   const DexFile::TypeList* GetInterfaceTypeList() {
     const DexFile::TypeList* result = interface_type_list_;
@@ -209,16 +225,6 @@
     return result;
   }
 
-  DexCache* GetDexCache() {
-    DexCache* result = dex_cache_;
-    if (result == NULL) {
-      DCHECK(klass_ != NULL);
-      result = klass_->GetDexCache();
-      dex_cache_ = result;
-    }
-    return result;
-  }
-
   ClassLinker* GetClassLinker() {
     ClassLinker* result = class_linker_;
     if (result == NULL) {
@@ -400,6 +406,7 @@
     SetMethod(new_m);
     shorty_ = NULL;
   }
+
   const char* GetName() {
     const DexFile& dex_file = GetDexFile();
     uint32_t dex_method_idx = method_->GetDexMethodIndex();
@@ -420,12 +427,14 @@
       }
     }
   }
+
   String* GetNameAsString() {
     const DexFile& dex_file = GetDexFile();
     uint32_t dex_method_idx = method_->GetDexMethodIndex();
     const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
     return GetClassLinker()->ResolveString(dex_file, method_id.name_idx_, GetDexCache());
   }
+
   const char* GetShorty() {
     const char* result = shorty_;
     if (result == NULL) {
@@ -436,12 +445,14 @@
     }
     return result;
   }
+
   uint32_t GetShortyLength() {
     if (shorty_ == NULL) {
       GetShorty();
     }
     return shorty_len_;
   }
+
   const std::string GetSignature() {
     const DexFile& dex_file = GetDexFile();
     uint32_t dex_method_idx = method_->GetDexMethodIndex();
@@ -451,14 +462,17 @@
       return "<no signature>";
     }
   }
+
   const DexFile::ProtoId& GetPrototype() {
     const DexFile& dex_file = GetDexFile();
     return dex_file.GetMethodPrototype(dex_file.GetMethodId(method_->GetDexMethodIndex()));
   }
+
   const DexFile::TypeList* GetParameterTypeList() {
     const DexFile::ProtoId& proto = GetPrototype();
     return GetDexFile().GetProtoParameters(proto);
   }
+
   ObjectArray<Class>* GetParameterTypes() {
     const DexFile::TypeList* params = GetParameterTypeList();
     Class* array_class = GetClassLinker()->FindSystemClass("[Ljava/lang/Class;");
@@ -474,6 +488,7 @@
     }
     return result;
   }
+
   Class* GetReturnType() {
     const DexFile& dex_file = GetDexFile();
     const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex());
@@ -481,6 +496,7 @@
     uint16_t return_type_idx = proto_id.return_type_idx_;
     return GetClassFromTypeIdx(return_type_idx);
   }
+
   const char* GetReturnTypeDescriptor() {
     const DexFile& dex_file = GetDexFile();
     const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex());
@@ -488,10 +504,12 @@
     uint16_t return_type_idx = proto_id.return_type_idx_;
     return dex_file.GetTypeDescriptor(dex_file.GetTypeId(return_type_idx));
   }
+
   int32_t GetLineNumFromNativePC(uintptr_t raw_pc) {
     const DexFile& dex_file = GetDexFile();
     return dex_file.GetLineNumFromPC(method_, method_->ToDexPC(raw_pc));
   }
+
   const char* GetDeclaringClassDescriptor() {
     Class* klass = method_->GetDeclaringClass();
     DCHECK(!klass->IsProxyClass());
@@ -499,6 +517,7 @@
     const DexFile& dex_file = GetDexFile();
     return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx));
   }
+
   const char* GetDeclaringClassSourceFile() {
     const char* descriptor = GetDeclaringClassDescriptor();
     const DexFile& dex_file = GetDexFile();
@@ -506,17 +525,33 @@
     CHECK(dex_class_def != NULL);
     return dex_file.GetSourceFile(*dex_class_def);
   }
+
+  uint32_t GetClassDefIndex() {
+    const char* descriptor = GetDeclaringClassDescriptor();
+    const DexFile& dex_file = GetDexFile();
+    uint32_t index;
+    CHECK(dex_file.FindClassDefIndex(descriptor, index));
+    return index;
+  }
+
+  ClassLoader* GetClassLoader() {
+    return method_->GetDeclaringClass()->GetClassLoader();
+  }
+
   bool IsStatic() {
     return method_->IsStatic();
   }
+
   bool IsClassInitializer() {
     return IsStatic() && StringPiece(GetName()) == "<clinit>";
   }
+
   size_t NumArgs() {
     // "1 +" because the first in Args is the receiver.
     // "- 1" because we don't count the return type.
     return (IsStatic() ? 0 : 1) + GetShortyLength() - 1;
   }
+
   // Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods
   bool IsParamALongOrDouble(size_t param) {
     CHECK_LT(param, NumArgs());
@@ -528,6 +563,7 @@
     char ch = GetShorty()[param];
     return (ch == 'J' || ch == 'D');
   }
+
   // Is the specified parameter a reference, where parameter 0 is 'this' for instance methods
   bool IsParamAReference(size_t param) {
     CHECK_LT(param, NumArgs());
@@ -538,6 +574,7 @@
     }
     return GetShorty()[param] == 'L';  // An array also has a shorty character of 'L' (not '[')
   }
+
   bool HasSameNameAndSignature(MethodHelper* other) {
     if (GetDexCache() == other->GetDexCache()) {
       const DexFile& dex_file = GetDexFile();
@@ -550,12 +587,15 @@
     StringPiece other_name(other->GetName());
     return name == other_name && GetSignature() == other->GetSignature();
   }
+
   const DexFile::CodeItem* GetCodeItem() {
     return GetDexFile().GetCodeItem(method_->GetCodeItemOffset());
   }
+
   bool IsResolvedTypeIdx(uint16_t type_idx) const {
     return method_->GetDexCacheResolvedTypes()->Get(type_idx) != NULL;
   }
+
   Class* GetClassFromTypeIdx(uint16_t type_idx) {
     Class* type = method_->GetDexCacheResolvedTypes()->Get(type_idx);
     if (type == NULL) {
@@ -564,13 +604,16 @@
     }
     return type;
   }
+
   const char* GetTypeDescriptorFromTypeIdx(uint16_t type_idx) {
     const DexFile& dex_file = GetDexFile();
     return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx));
   }
+
   Class* GetDexCacheResolvedType(uint16_t type_idx) {
     return GetDexCache()->GetResolvedType(type_idx);
   }
+
   const DexFile& GetDexFile() {
     const DexFile* result = dex_file_;
     if (result == NULL) {
@@ -580,6 +623,16 @@
     }
     return *result;
   }
+
+  DexCache* GetDexCache() {
+    DexCache* result = dex_cache_;
+    if (result == NULL) {
+      Class* klass = method_->GetDeclaringClass();
+      result = klass->GetDexCache();
+      dex_cache_ = result;
+    }
+    return result;
+  }
  private:
   // Set the method_ field, for proxy methods looking up the interface method via the resolved
   // methods table.
@@ -596,15 +649,7 @@
     }
     method_ = method;
   }
-  DexCache* GetDexCache() {
-    DexCache* result = dex_cache_;
-    if (result == NULL) {
-      Class* klass = method_->GetDeclaringClass();
-      result = klass->GetDexCache();
-      dex_cache_ = result;
-    }
-    return result;
-  }
+
   ClassLinker* GetClassLinker() {
     ClassLinker* result = class_linker_;
     if (result == NULL) {
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 50f0130..1152c79 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -359,9 +359,6 @@
   case verifier::VERIFY_ERROR_BAD_CLASS_HARD:
     // Generic VerifyError; use default exception, no message.
     break;
-  case verifier::VERIFY_ERROR_NONE:
-    CHECK(false);
-    break;
   }
 
   self->ThrowNewException(exception_class, msg.c_str());
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 1979940..ad4d6ed 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -184,174 +184,145 @@
     error += PrettyDescriptor(super);
     return false;
   }
-  for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
-    Method* method = klass->GetDirectMethod(i);
-    if (!VerifyMethod(method)) {
-      error = "Verifier rejected class ";
-      error += PrettyDescriptor(klass);
-      error += " due to bad method ";
-      error += PrettyMethod(method, true);
-      return false;
-    }
+  ClassHelper kh(klass);
+  const DexFile& dex_file = kh.GetDexFile();
+  uint32_t class_def_idx;
+  if (!dex_file.FindClassDefIndex(kh.GetDescriptor(), class_def_idx)) {
+    error = "Verifier rejected class ";
+    error += PrettyDescriptor(klass);
+    error += " that isn't present in dex file ";
+    error += dex_file.GetLocation();
+    return false;
   }
-  for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
-    Method* method = klass->GetVirtualMethod(i);
-    if (!VerifyMethod(method)) {
-      error = "Verifier rejected class ";
-      error += PrettyDescriptor(klass);
-      error += " due to bad method ";
-      error += PrettyMethod(method, true);
-      return false;
-    }
-  }
-  return true;
-}
-
-bool MethodVerifier::VerifyMethod(Method* method) {
-  MethodVerifier verifier(method);
-  bool success = verifier.VerifyAll();
-  CHECK_EQ(success, verifier.failure_ == VERIFY_ERROR_NONE);
-
-  // We expect either success and no verification error, or failure and a generic failure to
-  // reject the class.
-  if (success) {
-    if (verifier.failure_ != VERIFY_ERROR_NONE) {
-      LOG(FATAL) << "Unhandled failure in verification of " << PrettyMethod(method) << std::endl
-                 << verifier.fail_messages_;
-    }
-  } else {
-    LOG(INFO) << "Verification error in " << PrettyMethod(method) << " "
-               << verifier.fail_messages_.str();
-    if (gDebugVerify) {
-      std::cout << std::endl << verifier.info_messages_.str();
-      verifier.Dump(std::cout);
-    }
-    DCHECK((verifier.failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) ||
-           (verifier.failure_ == VERIFY_ERROR_BAD_CLASS_HARD)) << verifier.failure_;
-  }
-  return success;
-}
-
-void MethodVerifier::VerifyMethodAndDump(Method* method) {
-  MethodVerifier verifier(method);
-  verifier.VerifyAll();
-
-  LOG(INFO) << "Dump of method " << PrettyMethod(method) << " "
-            << verifier.fail_messages_.str() << std::endl
-            << verifier.info_messages_.str() << Dumpable<MethodVerifier>(verifier);
+  return VerifyClass(&dex_file, kh.GetDexCache(), klass->GetClassLoader(), class_def_idx, error);
 }
 
 bool MethodVerifier::VerifyClass(const DexFile* dex_file, DexCache* dex_cache,
     const ClassLoader* class_loader, uint32_t class_def_idx, std::string& error) {
   const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
   const byte* class_data = dex_file->GetClassData(class_def);
+  if (class_data == NULL) {
+    // empty class, probably a marker interface
+    return true;
+  }
   ClassDataItemIterator it(*dex_file, class_data);
   while (it.HasNextStaticField() || it.HasNextInstanceField()) {
     it.Next();
   }
+  size_t error_count = 0;
+  ClassLinker* linker = Runtime::Current()->GetClassLinker();
   while (it.HasNextDirectMethod()) {
     uint32_t method_idx = it.GetMemberIndex();
+    Method* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, true);
+    if (method == NULL) {
+      DCHECK(Thread::Current()->IsExceptionPending());
+      // We couldn't resolve the method, but continue regardless.
+      Thread::Current()->ClearException();
+    }
     if (!VerifyMethod(method_idx, dex_file, dex_cache, class_loader, class_def_idx,
-        it.GetMethodCodeItem())) {
-      error = "Verifier rejected class";
+        it.GetMethodCodeItem(), method, it.GetMemberAccessFlags())) {
+      if (error_count > 0) {
+        error += "\n";
+      }
+      error = "Verifier rejected class ";
       error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def));
       error += " due to bad method ";
       error += PrettyMethod(method_idx, *dex_file);
-      return false;
+      ++error_count;
     }
     it.Next();
   }
   while (it.HasNextVirtualMethod()) {
     uint32_t method_idx = it.GetMemberIndex();
+    Method* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, false);
+    if (method == NULL) {
+      DCHECK(Thread::Current()->IsExceptionPending());
+      // We couldn't resolve the method, but continue regardless.
+      Thread::Current()->ClearException();
+    }
     if (!VerifyMethod(method_idx, dex_file, dex_cache, class_loader, class_def_idx,
-        it.GetMethodCodeItem())) {
-      error = "Verifier rejected class";
+        it.GetMethodCodeItem(), method, it.GetMemberAccessFlags())) {
+      if (error_count > 0) {
+        error += "\n";
+      }
+      error = "Verifier rejected class ";
       error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def));
       error += " due to bad method ";
       error += PrettyMethod(method_idx, *dex_file);
-      return false;
+      ++error_count;
     }
     it.Next();
   }
-  return true;
+  return error_count == 0;
 }
 
 bool MethodVerifier::VerifyMethod(uint32_t method_idx, const DexFile* dex_file, DexCache* dex_cache,
-    const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item) {
-  MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item);
-
-  // Without a method*, we can only verify the structure.
-  bool success = verifier.VerifyStructure();
-  CHECK_EQ(success, verifier.failure_ == VERIFY_ERROR_NONE);
-
-  // We expect either success and no verification error, or failure and a generic failure to
-  // reject the class.
+    const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+    Method* method, uint32_t method_access_flags) {
+  MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item, method_idx,
+                          method, method_access_flags);
+  bool success = verifier.Verify();
   if (success) {
-    if (verifier.failure_ != VERIFY_ERROR_NONE) {
-      LOG(FATAL) << "Unhandled failure in verification of " << PrettyMethod(method_idx, *dex_file)
-                 << std::endl << verifier.fail_messages_;
+    // Verification completed, however failures may be pending that didn't cause the verification
+    // to hard fail.
+    if (verifier.failures_.size() != 0) {
+      verifier.DumpFailures(LOG(INFO) << "Soft verification failures in "
+                            << PrettyMethod(method_idx, *dex_file) << std::endl);
+      success = false;
     }
   } else {
-    LOG(INFO) << "Verification error in " << PrettyMethod(method_idx, *dex_file) << " "
-               << verifier.fail_messages_.str();
+    // Bad method data.
+    CHECK_NE(verifier.failures_.size(), 0U);
+    CHECK(verifier.have_pending_hard_failure_);
+    verifier.DumpFailures(LOG(INFO) << "Verification error in "
+                          << PrettyMethod(method_idx, *dex_file) << std::endl);
     if (gDebugVerify) {
       std::cout << std::endl << verifier.info_messages_.str();
       verifier.Dump(std::cout);
     }
-    DCHECK((verifier.failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) ||
-           (verifier.failure_ == VERIFY_ERROR_BAD_CLASS_HARD)) << verifier.failure_;
   }
   return success;
 }
 
-MethodVerifier::MethodVerifier(Method* method)
-    : work_insn_idx_(-1),
-      method_(method),
-      failure_(VERIFY_ERROR_NONE),
-      new_instance_count_(0),
-      monitor_enter_count_(0) {
+void MethodVerifier::VerifyMethodAndDump(Method* method) {
   CHECK(method != NULL);
-  dex_cache_ = method->GetDeclaringClass()->GetDexCache();
-  class_loader_ = method->GetDeclaringClass()->GetClassLoader();
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  dex_file_ = &class_linker->FindDexFile(dex_cache_);
-  code_item_ = dex_file_->GetCodeItem(method->GetCodeItemOffset());
-  const DexFile::ClassDef* class_def = ClassHelper(method_->GetDeclaringClass()).GetClassDef();
-  class_def_idx_ = dex_file_->GetIndexForClassDef(*class_def);
+  MethodHelper mh(method);
+  MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
+                          mh.GetClassDefIndex(), mh.GetCodeItem(), method->GetDexMethodIndex(),
+                          method, method->GetAccessFlags());
+  verifier.Verify();
+  verifier.DumpFailures(LOG(INFO) << "Dump of method " << PrettyMethod(method) << std::endl)
+      << verifier.info_messages_.str() << Dumpable<MethodVerifier>(verifier);
 }
 
 MethodVerifier::MethodVerifier(const DexFile* dex_file, DexCache* dex_cache,
-    const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item)
+    const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+    uint32_t method_idx, Method* method, uint32_t method_access_flags)
     : work_insn_idx_(-1),
-      method_(NULL),
+      method_idx_(method_idx),
+      foo_method_(method),
+      method_access_flags_(method_access_flags),
       dex_file_(dex_file),
       dex_cache_(dex_cache),
       class_loader_(class_loader),
       class_def_idx_(class_def_idx),
       code_item_(code_item),
-      failure_(VERIFY_ERROR_NONE),
+      have_pending_hard_failure_(false),
+      have_pending_rewrite_failure_(false),
       new_instance_count_(0),
       monitor_enter_count_(0) {
 }
 
-bool MethodVerifier::VerifyAll() {
-  CHECK(method_ != NULL);
+bool MethodVerifier::Verify() {
   // If there aren't any instructions, make sure that's expected, then exit successfully.
   if (code_item_ == NULL) {
-    if (!method_->IsNative() && !method_->IsAbstract()) {
+    if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
       return false;
     } else {
       return true;
     }
   }
-  return VerifyStructure() && VerifyCodeFlow();
-}
-
-bool MethodVerifier::VerifyStructure() {
-  if (code_item_ == NULL) {
-    return true;
-  }
   // Sanity-check the register counts. ins + locals = registers, so make sure that ins <= registers.
   if (code_item_->ins_size_ > code_item_->registers_size_) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" << code_item_->ins_size_
@@ -366,39 +337,73 @@
   result = result && ScanTryCatchBlocks();
   // Perform static instruction verification.
   result = result && VerifyInstructions();
-  return result;
+  // Perform code-flow analysis and return.
+  return result && VerifyCodeFlow();
 }
 
 std::ostream& MethodVerifier::Fail(VerifyError error) {
-  CHECK_EQ(failure_, VERIFY_ERROR_NONE);
-  if (Runtime::Current()->IsCompiler()) {
-    switch (error) {
-      // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx
-      // errors into soft verification errors so that we re-verify at runtime. We may fail to find
-      // or to agree on access because of not yet available class loaders, or class loaders that
-      // will differ at runtime.
-      case VERIFY_ERROR_NO_CLASS:
-      case VERIFY_ERROR_NO_FIELD:
-      case VERIFY_ERROR_NO_METHOD:
-      case VERIFY_ERROR_ACCESS_CLASS:
-      case VERIFY_ERROR_ACCESS_FIELD:
-      case VERIFY_ERROR_ACCESS_METHOD:
+  switch (error) {
+    case VERIFY_ERROR_NO_CLASS:
+    case VERIFY_ERROR_NO_FIELD:
+    case VERIFY_ERROR_NO_METHOD:
+    case VERIFY_ERROR_ACCESS_CLASS:
+    case VERIFY_ERROR_ACCESS_FIELD:
+    case VERIFY_ERROR_ACCESS_METHOD:
+      if (Runtime::Current()->IsCompiler()) {
+        // If we're optimistically running verification at compile time, turn NO_xxx and ACCESS_xxx
+        // errors into soft verification errors so that we re-verify at runtime. We may fail to find
+        // or to agree on access because of not yet available class loaders, or class loaders that
+        // will differ at runtime.
         error = VERIFY_ERROR_BAD_CLASS_SOFT;
-        break;
+      } else {
+        have_pending_rewrite_failure_ = true;
+      }
+      break;
+      // Errors that are bad at both compile and runtime, but don't cause rejection of the class.
+    case VERIFY_ERROR_CLASS_CHANGE:
+    case VERIFY_ERROR_INSTANTIATION:
+      have_pending_rewrite_failure_ = true;
+      break;
+      // Indication that verification should be retried at runtime.
+    case VERIFY_ERROR_BAD_CLASS_SOFT:
+      if (!Runtime::Current()->IsCompiler()) {
+        // It is runtime so hard fail.
+        have_pending_hard_failure_ = true;
+      }
+      break;
       // Hard verification failures at compile time will still fail at runtime, so the class is
       // marked as rejected to prevent it from being compiled.
-      case VERIFY_ERROR_BAD_CLASS_HARD: {
+    case VERIFY_ERROR_BAD_CLASS_HARD: {
+      if (Runtime::Current()->IsCompiler()) {
         Compiler::ClassReference ref(dex_file_, class_def_idx_);
         AddRejectedClass(ref);
-        break;
       }
-      default:
-        break;
+      have_pending_hard_failure_ = true;
+      break;
     }
   }
-  failure_ = error;
-  return fail_messages_ << "VFY: " << PrettyMethod(method_)
-                        << '[' << reinterpret_cast<void*>(work_insn_idx_) << "] : ";
+  failures_.push_back(error);
+  std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(method_idx_, *dex_file_).c_str(),
+                                    work_insn_idx_));
+  std::ostringstream* failure_message = new std::ostringstream(location);
+  failure_messages_.push_back(failure_message);
+  return *failure_message;
+}
+
+void MethodVerifier::PrependToLastFailMessage(std::string prepend) {
+  size_t failure_num = failure_messages_.size();
+  DCHECK_NE(failure_num, 0U);
+  std::ostringstream* last_fail_message = failure_messages_[failure_num - 1];
+  prepend += last_fail_message->str();
+  failure_messages_[failure_num - 1] = new std::ostringstream(prepend);
+  delete last_fail_message;
+}
+
+void MethodVerifier::AppendToLastFailMessage(std::string append) {
+  size_t failure_num = failure_messages_.size();
+  DCHECK_NE(failure_num, 0U);
+  std::ostringstream* last_fail_message = failure_messages_[failure_num - 1];
+  (*last_fail_message) << append;
 }
 
 bool MethodVerifier::ComputeWidthsAndCountOps() {
@@ -503,8 +508,7 @@
   uint32_t insns_size = code_item_->insns_size_in_code_units_;
   for (uint32_t dex_pc = 0; dex_pc < insns_size;) {
     if (!VerifyInstruction(inst, dex_pc)) {
-      DCHECK_NE(failure_, VERIFY_ERROR_NONE);
-      fail_messages_ << "Rejecting opcode " << inst->Name() << " at " << dex_pc;
+      DCHECK_NE(failures_.size(), 0U);
       return false;
     }
     /* Flag instructions that are garbage collection points */
@@ -928,30 +932,34 @@
 
   /* Initialize register types of method arguments. */
   if (!SetTypesFromSignature()) {
-    DCHECK_NE(failure_, VERIFY_ERROR_NONE);
-    fail_messages_ << "Bad signature in " << PrettyMethod(method_);
+    DCHECK_NE(failures_.size(), 0U);
+    std::string prepend("Bad signature in ");
+    prepend += PrettyMethod(method_idx_, *dex_file_);
+    PrependToLastFailMessage(prepend);
     return false;
   }
   /* Perform code flow verification. */
   if (!CodeFlowVerifyMethod()) {
-    DCHECK_NE(failure_, VERIFY_ERROR_NONE);
+    DCHECK_NE(failures_.size(), 0U);
     return false;
   }
 
   /* Generate a register map and add it to the method. */
   UniquePtr<const std::vector<uint8_t> > map(GenerateGcMap());
   if (map.get() == NULL) {
-    DCHECK_NE(failure_, VERIFY_ERROR_NONE);
+    DCHECK_NE(failures_.size(), 0U);
     return false;  // Not a real failure, but a failure to encode
   }
 #ifndef NDEBUG
   VerifyGcMap(*map);
 #endif
   const std::vector<uint8_t>* gc_map = CreateLengthPrefixedGcMap(*(map.get()));
-  Compiler::MethodReference ref(dex_file_, method_->GetDexMethodIndex());
+  Compiler::MethodReference ref(dex_file_, method_idx_);
   verifier::MethodVerifier::SetGcMap(ref, *gc_map);
 
-  method_->SetGcMap(&gc_map->at(0));
+  if (foo_method_ != NULL) {
+    foo_method_->SetGcMap(&gc_map->at(0));
+  }
 
 #if defined(ART_USE_LLVM_COMPILER)
   /* Generate Inferred Register Category for LLVM-based Code Generator */
@@ -962,6 +970,18 @@
   return true;
 }
 
+std::ostream& MethodVerifier::DumpFailures(std::ostream& os) {
+  DCHECK_EQ(failures_.size(), failure_messages_.size());
+  for (size_t i = 0; i < failures_.size(); ++i) {
+    os << failure_messages_[i]->str() << std::endl;
+  }
+  return os;
+}
+
+extern "C" void MethodVerifierGdbDump(MethodVerifier* v) {
+  v->Dump(std::cerr);
+}
+
 void MethodVerifier::Dump(std::ostream& os) {
   if (code_item_ == NULL) {
     os << "Native method" << std::endl;
@@ -1005,22 +1025,22 @@
   DCHECK_GE(arg_start, 0);      /* should have been verified earlier */
   //Include the "this" pointer.
   size_t cur_arg = 0;
-  if (!method_->IsStatic()) {
+  if (!IsStatic()) {
     // If this is a constructor for a class other than java.lang.Object, mark the first ("this")
     // argument as uninitialized. This restricts field access until the superclass constructor is
     // called.
-    Class* declaring_class = method_->GetDeclaringClass();
-    if (method_->IsConstructor() && !declaring_class->IsObjectClass()) {
+    const RegType& declaring_class = GetDeclaringClass();
+    if (IsConstructor() && !declaring_class.IsJavaLangObject()) {
       reg_line->SetRegisterType(arg_start + cur_arg,
                                 reg_types_.UninitializedThisArgument(declaring_class));
     } else {
-      reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.FromClass(declaring_class));
+      reg_line->SetRegisterType(arg_start + cur_arg, declaring_class);
     }
     cur_arg++;
   }
 
   const DexFile::ProtoId& proto_id =
-      dex_file_->GetMethodPrototype(dex_file_->GetMethodId(method_->GetDexMethodIndex()));
+      dex_file_->GetMethodPrototype(dex_file_->GetMethodId(method_idx_));
   DexFileParameterIterator iterator(*dex_file_, proto_id);
 
   for (; iterator.HasNext(); iterator.Next()) {
@@ -1041,8 +1061,7 @@
         // it's effectively considered initialized the instant we reach here (in the sense that we
         // can return without doing anything or call virtual methods).
         {
-          const RegType& reg_type =
-              reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
+          const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor);
           reg_line->SetRegisterType(arg_start + cur_arg, reg_type);
         }
         break;
@@ -1163,7 +1182,7 @@
         if (work_line_->CompareLine(register_line) != 0) {
           Dump(std::cout);
           std::cout << info_messages_.str();
-          LOG(FATAL) << "work_line diverged in " << PrettyMethod(method_)
+          LOG(FATAL) << "work_line diverged in " << PrettyMethod(method_idx_, *dex_file_)
                      << "@" << reinterpret_cast<void*>(work_insn_idx_) << std::endl
                      << " work_line=" << *work_line_ << std::endl
                      << "  expected=" << *register_line;
@@ -1172,7 +1191,9 @@
 #endif
     }
     if (!CodeFlowVerifyInstruction(&start_guess)) {
-      fail_messages_ << std::endl << PrettyMethod(method_) << " failed to verify";
+      std::string prepend(PrettyMethod(method_idx_, *dex_file_));
+      prepend += " failed to verify: ";
+      PrependToLastFailMessage(prepend);
       return false;
     }
     /* Clear "changed" and mark as visited. */
@@ -1180,7 +1201,7 @@
     insn_flags_[insn_idx].ClearChanged();
   }
 
-  if (DEAD_CODE_SCAN && ((method_->GetAccessFlags() & kAccWritable) == 0)) {
+  if (DEAD_CODE_SCAN && ((method_access_flags_ & kAccWritable) == 0)) {
     /*
      * Scan for dead code. There's nothing "evil" about dead code
      * (besides the wasted space), but it indicates a flaw somewhere
@@ -1335,14 +1356,14 @@
       break;
     }
     case Instruction::RETURN_VOID:
-      if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
-        if (!GetMethodReturnType().IsUnknown()) {
+      if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
+        if (!GetMethodReturnType().IsConflict()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void not expected";
         }
       }
       break;
     case Instruction::RETURN:
-      if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
+      if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
         /* check the method signature */
         const RegType& return_type = GetMethodReturnType();
         if (!return_type.IsCategory1Types()) {
@@ -1356,30 +1377,31 @@
                            return_type.IsShort() || return_type.IsChar()) &&
                            src_type.IsInteger()));
           /* check the register contents */
-          work_line_->VerifyRegisterType(dec_insn.vA, use_src ? src_type : return_type);
-          if (failure_ != VERIFY_ERROR_NONE) {
-            fail_messages_ << " return-1nr on invalid register v" << dec_insn.vA;
+          bool success =
+              work_line_->VerifyRegisterType(dec_insn.vA, use_src ? src_type : return_type);
+          if (!success) {
+            AppendToLastFailMessage(StringPrintf(" return-1nr on invalid register v%d", dec_insn.vA));
           }
         }
       }
       break;
     case Instruction::RETURN_WIDE:
-      if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
+      if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
         /* check the method signature */
         const RegType& return_type = GetMethodReturnType();
         if (!return_type.IsCategory2Types()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-wide not expected";
         } else {
           /* check the register contents */
-          work_line_->VerifyRegisterType(dec_insn.vA, return_type);
-          if (failure_ != VERIFY_ERROR_NONE) {
-            fail_messages_ << " return-wide on invalid register pair v" << dec_insn.vA;
+          bool success = work_line_->VerifyRegisterType(dec_insn.vA, return_type);
+          if (!success) {
+            AppendToLastFailMessage(StringPrintf(" return-wide on invalid register v%d", dec_insn.vA));
           }
         }
       }
       break;
     case Instruction::RETURN_OBJECT:
-      if (!method_->IsConstructor() || work_line_->CheckConstructorReturn()) {
+      if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
         const RegType& return_type = GetMethodReturnType();
         if (!return_type.IsReferenceTypes()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected";
@@ -1426,9 +1448,9 @@
       // Get type from instruction if unresolved then we need an access check
       // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
       const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB);
-      // Register holds class, ie its type is class, but on error we keep it Unknown
+      // Register holds class, ie its type is class, on error it will hold Conflict.
       work_line_->SetRegisterType(dec_insn.vA,
-                                  res_type.IsUnknown() ? res_type : reg_types_.JavaLangClass());
+                                  res_type.IsConflict() ? res_type : reg_types_.JavaLangClass());
       break;
     }
     case Instruction::MONITOR_ENTER:
@@ -1471,9 +1493,9 @@
       bool is_checkcast = dec_insn.opcode == Instruction::CHECK_CAST;
       const RegType& res_type =
           ResolveClassAndCheckAccess(is_checkcast ? dec_insn.vB : dec_insn.vC);
-      if (res_type.IsUnknown()) {
-        CHECK_NE(failure_, VERIFY_ERROR_NONE);
-        break;  // couldn't resolve class
+      if (res_type.IsConflict()) {
+        DCHECK_NE(failures_.size(), 0U);
+        break;  // bad class
       }
       // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
       const RegType& orig_type =
@@ -1504,9 +1526,9 @@
     }
     case Instruction::NEW_INSTANCE: {
       const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB);
-      if (res_type.IsUnknown()) {
-        CHECK_NE(failure_, VERIFY_ERROR_NONE);
-        break;  // couldn't resolve class
+      if (res_type.IsConflict()) {
+        DCHECK_NE(failures_.size(), 0U);
+        break;  // bad class
       }
       // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
       // can't create an instance of an interface or abstract class */
@@ -1590,9 +1612,8 @@
         if (!array_type.IsArrayTypes()) {
           Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type;
         } else {
-          const RegType& component_type = reg_types_.GetComponentType(array_type,
-                                                    method_->GetDeclaringClass()->GetClassLoader());
-          DCHECK(!component_type.IsUnknown());
+          const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
+          DCHECK(!component_type.IsConflict());
           if (component_type.IsNonZeroReferenceTypes()) {
             Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type "
                                               << component_type;
@@ -1806,28 +1827,25 @@
       bool is_super =  (dec_insn.opcode == Instruction::INVOKE_SUPER ||
                         dec_insn.opcode == Instruction::INVOKE_SUPER_RANGE);
       Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_VIRTUAL, is_range, is_super);
-      if (failure_ == VERIFY_ERROR_NONE) {
-        const char* descriptor;
-        if (called_method == NULL) {
-          uint32_t method_idx = dec_insn.vB;
-          const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
-          uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
-          descriptor =  dex_file_->StringByTypeIdx(return_type_idx);
-        } else {
-          descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
-        }
-        const RegType& return_type =
-            reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
-        work_line_->SetResultRegisterType(return_type);
-        just_set_result = true;
+      const char* descriptor;
+      if (called_method == NULL) {
+        uint32_t method_idx = dec_insn.vB;
+        const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+        uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+        descriptor =  dex_file_->StringByTypeIdx(return_type_idx);
+      } else {
+        descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
       }
+      const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor);
+      work_line_->SetResultRegisterType(return_type);
+      just_set_result = true;
       break;
     }
     case Instruction::INVOKE_DIRECT:
     case Instruction::INVOKE_DIRECT_RANGE: {
       bool is_range = (dec_insn.opcode == Instruction::INVOKE_DIRECT_RANGE);
       Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_DIRECT, is_range, false);
-      if (failure_ == VERIFY_ERROR_NONE) {
+      if (called_method != NULL) {
         /*
          * Some additional checks when calling a constructor. We know from the invocation arg check
          * that the "this" argument is an instance of called_method->klass. Now we further restrict
@@ -1835,18 +1853,9 @@
          * allowing the latter only if the "this" argument is the same as the "this" argument to
          * this method (which implies that we're in a constructor ourselves).
          */
-        bool is_constructor;
-        if (called_method != NULL) {
-          is_constructor = called_method->IsConstructor();
-        } else {
-          uint32_t method_idx = dec_insn.vB;
-          const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
-          const char* name = dex_file_->GetMethodName(method_id);
-          is_constructor = strcmp(name, "<init>") == 0;
-        }
-        if (is_constructor) {
+        if (called_method->IsConstructor()) {
           const RegType& this_type = work_line_->GetInvocationThis(dec_insn);
-          if (failure_ != VERIFY_ERROR_NONE)
+          if (this_type.IsConflict())  // failure.
             break;
 
           /* no null refs allowed (?) */
@@ -1855,18 +1864,29 @@
             break;
           }
           if (called_method != NULL) {
-            Class* this_class = this_type.GetClass();
-            DCHECK(this_class != NULL);
             /* must be in same class or in superclass */
-            if (called_method->GetDeclaringClass() == this_class->GetSuperClass()) {
-              if (this_class != method_->GetDeclaringClass()) {
+            const RegType& this_super_klass = this_type.GetSuperClass(&reg_types_);
+            if (this_super_klass.IsConflict()) {
+              // Unknown super class, fail so we re-check at runtime.
+              Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "super class unknown for '" << this_type << "'";
+              break;
+            } else {
+              if (!this_super_klass.IsZero() &&
+                  called_method->GetDeclaringClass() == this_super_klass.GetClass()) {
+                if (this_type.GetClass() != GetDeclaringClass().GetClass()) {
+                  Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+                        << "invoke-direct <init> on super only allowed for 'this' in <init>"
+                        << " (this class '" << this_type << "', called class '"
+                        << PrettyDescriptor(called_method->GetDeclaringClass()) << "')";
+                  break;
+                }
+              } else if (this_type.GetClass() != called_method->GetDeclaringClass()) {
                 Fail(VERIFY_ERROR_BAD_CLASS_HARD)
-                    << "invoke-direct <init> on super only allowed for 'this' in <init>";
+                      << "invoke-direct <init> must be on current class or super"
+                      << " (current class '" << this_type << "', called class '"
+                      << PrettyDescriptor(called_method->GetDeclaringClass()) << "')";
                 break;
               }
-            }  else if (called_method->GetDeclaringClass() != this_class) {
-              Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke-direct <init> must be on current class or super";
-              break;
             }
           }
 
@@ -1882,8 +1902,6 @@
            * registers that have the same object instance in them, not just the "this" register.
            */
           work_line_->MarkRefsAsInitialized(this_type);
-          if (failure_ != VERIFY_ERROR_NONE)
-            break;
         }
         const char* descriptor;
         if (called_method == NULL) {
@@ -1894,8 +1912,7 @@
         } else {
           descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
         }
-        const RegType& return_type =
-            reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
+        const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor);
         work_line_->SetResultRegisterType(return_type);
         just_set_result = true;
       }
@@ -1905,77 +1922,69 @@
     case Instruction::INVOKE_STATIC_RANGE: {
         bool is_range = (dec_insn.opcode == Instruction::INVOKE_STATIC_RANGE);
         Method* called_method = VerifyInvocationArgs(dec_insn, METHOD_STATIC, is_range, false);
-        if (failure_ == VERIFY_ERROR_NONE) {
-          const char* descriptor;
-          if (called_method == NULL) {
-            uint32_t method_idx = dec_insn.vB;
-            const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
-            uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
-            descriptor =  dex_file_->StringByTypeIdx(return_type_idx);
-          } else {
-            descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
-          }
-          const RegType& return_type =
-              reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
-          work_line_->SetResultRegisterType(return_type);
-          just_set_result = true;
+        const char* descriptor;
+        if (called_method == NULL) {
+          uint32_t method_idx = dec_insn.vB;
+          const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+          uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+          descriptor =  dex_file_->StringByTypeIdx(return_type_idx);
+        } else {
+          descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
         }
+        const RegType& return_type =  reg_types_.FromDescriptor(class_loader_, descriptor);
+        work_line_->SetResultRegisterType(return_type);
+        just_set_result = true;
       }
       break;
     case Instruction::INVOKE_INTERFACE:
     case Instruction::INVOKE_INTERFACE_RANGE: {
       bool is_range =  (dec_insn.opcode == Instruction::INVOKE_INTERFACE_RANGE);
       Method* abs_method = VerifyInvocationArgs(dec_insn, METHOD_INTERFACE, is_range, false);
-      if (failure_ == VERIFY_ERROR_NONE) {
-        if (abs_method != NULL) {
-          Class* called_interface = abs_method->GetDeclaringClass();
-          if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) {
-            Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '"
-                                            << PrettyMethod(abs_method) << "'";
-            break;
-          }
+      if (abs_method != NULL) {
+        Class* called_interface = abs_method->GetDeclaringClass();
+        if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) {
+          Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected interface class in invoke-interface '"
+              << PrettyMethod(abs_method) << "'";
+          break;
         }
-        /* Get the type of the "this" arg, which should either be a sub-interface of called
-         * interface or Object (see comments in RegType::JoinClass).
-         */
-        const RegType& this_type = work_line_->GetInvocationThis(dec_insn);
-        if (failure_ == VERIFY_ERROR_NONE) {
-          if (this_type.IsZero()) {
-            /* null pointer always passes (and always fails at runtime) */
-          } else {
-            if (this_type.IsUninitializedTypes()) {
-              Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface call on uninitialized object "
-                  << this_type;
-              break;
-            }
-            // In the past we have tried to assert that "called_interface" is assignable
-            // from "this_type.GetClass()", however, as we do an imprecise Join
-            // (RegType::JoinClass) we don't have full information on what interfaces are
-            // implemented by "this_type". For example, two classes may implement the same
-            // interfaces and have a common parent that doesn't implement the interface. The
-            // join will set "this_type" to the parent class and a test that this implements
-            // the interface will incorrectly fail.
-          }
-        }
-        /*
-         * We don't have an object instance, so we can't find the concrete method. However, all of
-         * the type information is in the abstract method, so we're good.
-         */
-        const char* descriptor;
-        if (abs_method == NULL) {
-          uint32_t method_idx = dec_insn.vB;
-          const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
-          uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
-          descriptor =  dex_file_->StringByTypeIdx(return_type_idx);
-        } else {
-          descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor();
-        }
-        const RegType& return_type =
-            reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
-        work_line_->SetResultRegisterType(return_type);
-        work_line_->SetResultRegisterType(return_type);
-        just_set_result = true;
       }
+      /* Get the type of the "this" arg, which should either be a sub-interface of called
+       * interface or Object (see comments in RegType::JoinClass).
+       */
+      const RegType& this_type = work_line_->GetInvocationThis(dec_insn);
+      if (this_type.IsZero()) {
+        /* null pointer always passes (and always fails at runtime) */
+      } else {
+        if (this_type.IsUninitializedTypes()) {
+          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface call on uninitialized object "
+              << this_type;
+          break;
+        }
+        // In the past we have tried to assert that "called_interface" is assignable
+        // from "this_type.GetClass()", however, as we do an imprecise Join
+        // (RegType::JoinClass) we don't have full information on what interfaces are
+        // implemented by "this_type". For example, two classes may implement the same
+        // interfaces and have a common parent that doesn't implement the interface. The
+        // join will set "this_type" to the parent class and a test that this implements
+        // the interface will incorrectly fail.
+      }
+      /*
+       * We don't have an object instance, so we can't find the concrete method. However, all of
+       * the type information is in the abstract method, so we're good.
+       */
+      const char* descriptor;
+      if (abs_method == NULL) {
+        uint32_t method_idx = dec_insn.vB;
+        const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+        uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
+        descriptor =  dex_file_->StringByTypeIdx(return_type_idx);
+      } else {
+        descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor();
+      }
+      const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor);
+      work_line_->SetResultRegisterType(return_type);
+      work_line_->SetResultRegisterType(return_type);
+      just_set_result = true;
       break;
     }
     case Instruction::NEG_INT:
@@ -2212,21 +2221,21 @@
      */
   }  // end - switch (dec_insn.opcode)
 
-  if (failure_ != VERIFY_ERROR_NONE) {
-    if (failure_ == VERIFY_ERROR_BAD_CLASS_HARD || failure_ == VERIFY_ERROR_BAD_CLASS_SOFT) {
-      /* immediate failure, reject class */
-      fail_messages_ << std::endl << "Rejecting opcode " << inst->DumpString(dex_file_);
-      return false;
-    } else {
-      /* replace opcode and continue on */
-      fail_messages_ << std::endl << "Replacing opcode " << inst->DumpString(dex_file_);
-      ReplaceFailingInstruction();
-      /* IMPORTANT: method->insns may have been changed */
-      insns = code_item_->insns_ + work_insn_idx_;
-      /* continue on as if we just handled a throw-verification-error */
-      failure_ = VERIFY_ERROR_NONE;
-      opcode_flags = Instruction::kThrow;
-    }
+  if (have_pending_hard_failure_) {
+    CHECK_EQ(failures_[failures_.size() - 1], VERIFY_ERROR_BAD_CLASS_HARD);
+    /* immediate failure, reject class */
+    info_messages_ << "Rejecting opcode " << inst->DumpString(dex_file_);
+    return false;
+  } else if (have_pending_rewrite_failure_) {
+    /* replace opcode and continue on */
+    std::string append("Replacing opcode ");
+    append += inst->DumpString(dex_file_);
+    AppendToLastFailMessage(append);
+    ReplaceFailingInstruction();
+    /* IMPORTANT: method->insns may have been changed */
+    insns = code_item_->insns_ + work_insn_idx_;
+    /* continue on as if we just handled a throw-verification-error */
+    opcode_flags = Instruction::kThrow;
   }
   /*
    * If we didn't just set the result register, clear it out. This ensures that you can only use
@@ -2401,25 +2410,25 @@
 
 const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
   const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
-  Class* referrer = method_->GetDeclaringClass();
-  Class* klass = method_->GetDexCacheResolvedTypes()->Get(class_idx);
+  const RegType& referrer = GetDeclaringClass();
+  Class* klass = dex_cache_->GetResolvedType(class_idx);
   const RegType& result =
       klass != NULL ? reg_types_.FromClass(klass)
-                    : reg_types_.FromDescriptor(referrer->GetClassLoader(), descriptor);
-  if (klass == NULL && !result.IsUnresolvedTypes()) {
-    method_->GetDexCacheResolvedTypes()->Set(class_idx, result.GetClass());
-  }
-  if (result.IsUnknown()) {
-    Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing unknown class in " << PrettyDescriptor(referrer);
+                    : reg_types_.FromDescriptor(class_loader_, descriptor);
+  if (result.IsConflict()) {
+    Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor
+        << "' in " << referrer;
     return result;
   }
-  // Check if access is allowed. Unresolved types use AllocObjectFromCodeWithAccessCheck to
+  if (klass == NULL && !result.IsUnresolvedTypes()) {
+    dex_cache_->SetResolvedType(class_idx, result.GetClass());
+  }
+  // Check if access is allowed. Unresolved types use xxxWithAccessCheck to
   // check at runtime if access is allowed and so pass here.
-  if (!result.IsUnresolvedTypes() && !referrer->CanAccess(result.GetClass())) {
+  if (!result.IsUnresolvedTypes() && !referrer.IsUnresolvedTypes() && !referrer.CanAccess(result)) {
     Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '"
-                                    << PrettyDescriptor(referrer) << "' -> '"
-                                    << result << "'";
-    return reg_types_.Unknown();
+                                    << referrer << "' -> '" << result << "'";
+    return reg_types_.Conflict();
   } else {
     return result;
   }
@@ -2446,7 +2455,7 @@
               // 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_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception;
-              return reg_types_.Unknown();
+              return reg_types_.Conflict();
             } else if (common_super->Equals(exception)) {
               // odd case, but nothing to do
             } else {
@@ -2462,25 +2471,26 @@
   if (common_super == NULL) {
     /* no catch blocks, or no catches with classes we can find */
     Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unable to find exception handler";
-    return reg_types_.Unknown();
+    return reg_types_.Conflict();
   }
   return *common_super;
 }
 
-Method* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t method_idx, MethodType method_type) {
-  const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
+Method* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx, MethodType method_type) {
+  const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx);
   const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
-  if (failure_ != VERIFY_ERROR_NONE) {
-    fail_messages_ << " in attempt to access method " << dex_file_->GetMethodName(method_id);
+  if (klass_type.IsConflict()) {
+    std::string append(" in attempt to access method ");
+    append += dex_file_->GetMethodName(method_id);
+    AppendToLastFailMessage(append);
     return NULL;
   }
   if (klass_type.IsUnresolvedTypes()) {
     return NULL;  // Can't resolve Class so no more to do here
   }
   Class* klass = klass_type.GetClass();
-  Class* referrer = method_->GetDeclaringClass();
-  DexCache* dex_cache = referrer->GetDexCache();
-  Method* res_method = dex_cache->GetResolvedMethod(method_idx);
+  const RegType& referrer = GetDeclaringClass();
+  Method* res_method = dex_cache_->GetResolvedMethod(dex_method_idx);
   if (res_method == NULL) {
     const char* name = dex_file_->GetMethodName(method_id);
     std::string signature(dex_file_->CreateMethodSignature(method_id.proto_idx_, NULL));
@@ -2493,7 +2503,7 @@
       res_method = klass->FindVirtualMethod(name, signature);
     }
     if (res_method != NULL) {
-      dex_cache->SetResolvedMethod(method_idx, res_method);
+      dex_cache_->SetResolvedMethod(dex_method_idx, res_method);
     } else {
       // If a virtual or interface method wasn't found with the expected type, look in
       // the direct methods. This can happen when the wrong invoke type is used or when
@@ -2523,9 +2533,9 @@
     return NULL;
   }
   // Check if access is allowed.
-  if (!referrer->CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
+  if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
     Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call " << PrettyMethod(res_method)
-                                  << " from " << PrettyDescriptor(referrer) << ")";
+                                  << " from " << referrer << ")";
     return NULL;
   }
   // Check that invoke-virtual and invoke-super are not used on private methods of the same class.
@@ -2570,15 +2580,18 @@
   // has a vtable entry for the target method.
   if (is_super) {
     DCHECK(method_type == METHOD_VIRTUAL);
-    Class* super = method_->GetDeclaringClass()->GetSuperClass();
-    if (super == NULL || res_method->GetMethodIndex() >= super->GetVTable()->GetLength()) {
-      if (super == NULL) {  // Only Object has no super class
-        Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(method_)
+    const RegType& super = GetDeclaringClass().GetSuperClass(&reg_types_);
+    Class* super_klass = super.GetClass();
+    if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) {
+      if (super.IsConflict()) {  // Only Object has no super class
+        Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
+                                     << PrettyMethod(method_idx_, *dex_file_)
                                      << " to super " << PrettyMethod(res_method);
       } else {
         MethodHelper mh(res_method);
-        Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(method_)
-                                     << " to super " << PrettyDescriptor(super)
+        Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
+                                     << PrettyMethod(method_idx_, *dex_file_)
+                                     << " to super " << super
                                      << "." << mh.GetName()
                                      << mh.GetSignature();
       }
@@ -2598,15 +2611,14 @@
   }
 
   /*
-   * Check the "this" argument, which must be an instance of the class
-   * that declared the method. For an interface class, we don't do the
-   * full interface merge, so we can't do a rigorous check here (which
-   * is okay since we have to do it at runtime).
+   * Check the "this" argument, which must be an instance of the class that declared the method.
+   * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a
+   * rigorous check here (which is okay since we have to do it at runtime).
    */
   size_t actual_args = 0;
   if (!res_method->IsStatic()) {
     const RegType& actual_arg_type = work_line_->GetInvocationThis(dec_insn);
-    if (failure_ != VERIFY_ERROR_NONE) {
+    if (actual_arg_type.IsConflict()) {  // GetInvocationThis failed.
       return NULL;
     }
     if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) {
@@ -2644,8 +2656,7 @@
           << " missing signature component";
       return NULL;
     }
-    const RegType& reg_type =
-        reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
+    const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor);
     uint32_t get_reg = is_range ? dec_insn.vC + actual_args : dec_insn.arg[actual_args];
     if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
       return NULL;
@@ -2661,16 +2672,11 @@
   }
 }
 
-const RegType& MethodVerifier::GetMethodReturnType() {
-  return reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(),
-                                   MethodHelper(method_).GetReturnTypeDescriptor());
-}
-
 void MethodVerifier::VerifyNewArray(const DecodedInstruction& dec_insn, bool is_filled,
                                  bool is_range) {
   const RegType& res_type = ResolveClassAndCheckAccess(is_filled ? dec_insn.vB : dec_insn.vC);
-  if (res_type.IsUnknown()) {
-    CHECK_NE(failure_, VERIFY_ERROR_NONE);
+  if (res_type.IsConflict()) {  // bad class
+    DCHECK_NE(failures_.size(), 0U);
   } else {
     // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
     if (!res_type.IsArrayTypes()) {
@@ -2683,13 +2689,12 @@
     } else {
       // 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.
-      const RegType& expected_type = reg_types_.GetComponentType(res_type,
-                                                    method_->GetDeclaringClass()->GetClassLoader());
+      const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_);
       uint32_t arg_count = dec_insn.vA;
       for (size_t ui = 0; ui < arg_count; ui++) {
         uint32_t get_reg = is_range ? dec_insn.vC + ui : dec_insn.arg[ui];
         if (!work_line_->VerifyRegisterType(get_reg, expected_type)) {
-          work_line_->SetResultRegisterType(reg_types_.Unknown());
+          work_line_->SetResultRegisterType(reg_types_.Conflict());
           return;
         }
       }
@@ -2720,8 +2725,7 @@
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
     } else {
       /* verify the class */
-      const RegType& component_type = reg_types_.GetComponentType(array_type,
-                                                    method_->GetDeclaringClass()->GetClassLoader());
+      const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
       if (!component_type.IsReferenceTypes() && !is_primitive) {
         Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
             << " source for aget-object";
@@ -2757,8 +2761,7 @@
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput";
     } else {
       /* verify the class */
-      const RegType& component_type = reg_types_.GetComponentType(array_type,
-                                                    method_->GetDeclaringClass()->GetClassLoader());
+      const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
       if (!component_type.IsReferenceTypes() && !is_primitive) {
         Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
             << " source for aput-object";
@@ -2784,16 +2787,17 @@
   const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
   // Check access to class
   const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
-  if (failure_ != VERIFY_ERROR_NONE) {
-    fail_messages_ << " in attempt to access static field " << field_idx << " ("
-              << dex_file_->GetFieldName(field_id) << ") in "
-              << dex_file_->GetFieldDeclaringClassDescriptor(field_id);
+  if (klass_type.IsConflict()) { // bad class
+    AppendToLastFailMessage(StringPrintf(" in attempt to access static field %d (%s) in %s",
+                                         field_idx, dex_file_->GetFieldName(field_id),
+                                         dex_file_->GetFieldDeclaringClassDescriptor(field_id)));
     return NULL;
   }
   if (klass_type.IsUnresolvedTypes()) {
-    return NULL;  // Can't resolve Class so no more to do here
+    return NULL;  // Can't resolve Class so no more to do here, will do checking at runtime.
   }
-  Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(field_idx, method_);
+  Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx,
+                                                                       dex_cache_, class_loader_);
   if (field == NULL) {
     LOG(INFO) << "unable to resolve static field " << field_idx << " ("
               << dex_file_->GetFieldName(field_id) << ") in "
@@ -2801,10 +2805,10 @@
     DCHECK(Thread::Current()->IsExceptionPending());
     Thread::Current()->ClearException();
     return NULL;
-  } else if (!method_->GetDeclaringClass()->CanAccessMember(field->GetDeclaringClass(),
-                                                            field->GetAccessFlags())) {
+  } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
+                                                  field->GetAccessFlags())) {
     Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot access static field " << PrettyField(field)
-                                   << " from " << PrettyClass(method_->GetDeclaringClass());
+                                   << " from " << GetDeclaringClass();
     return NULL;
   } else if (!field->IsStatic()) {
     Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field) << " to be static";
@@ -2818,16 +2822,17 @@
   const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
   // Check access to class
   const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
-  if (failure_ != VERIFY_ERROR_NONE) {
-    fail_messages_ << " in attempt to access instance field " << field_idx << " ("
-              << dex_file_->GetFieldName(field_id) << ") in "
-              << dex_file_->GetFieldDeclaringClassDescriptor(field_id);
+  if (klass_type.IsConflict()) {
+    AppendToLastFailMessage(StringPrintf(" in attempt to access instance field %d (%s) in %s",
+                                         field_idx, dex_file_->GetFieldName(field_id),
+                                         dex_file_->GetFieldDeclaringClassDescriptor(field_id)));
     return NULL;
   }
   if (klass_type.IsUnresolvedTypes()) {
     return NULL;  // Can't resolve Class so no more to do here
   }
-  Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(field_idx, method_);
+  Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx,
+                                                                       dex_cache_, class_loader_);
   if (field == NULL) {
     LOG(INFO) << "unable to resolve instance field " << field_idx << " ("
               << dex_file_->GetFieldName(field_id) << ") in "
@@ -2835,10 +2840,10 @@
     DCHECK(Thread::Current()->IsExceptionPending());
     Thread::Current()->ClearException();
     return NULL;
-  } else if (!method_->GetDeclaringClass()->CanAccessMember(field->GetDeclaringClass(),
-                                                            field->GetAccessFlags())) {
+  } else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
+                                                  field->GetAccessFlags())) {
     Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot access instance field " << PrettyField(field)
-                                    << " from " << PrettyClass(method_->GetDeclaringClass());
+                                    << " from " << GetDeclaringClass();
     return NULL;
   } else if (field->IsStatic()) {
     Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field)
@@ -2847,24 +2852,27 @@
   } else if (obj_type.IsZero()) {
     // Cannot infer and check type, however, access will cause null pointer exception
     return field;
-  } else if (obj_type.IsUninitializedTypes() &&
-      (!method_->IsConstructor() || method_->GetDeclaringClass() != obj_type.GetClass() ||
-          field->GetDeclaringClass() != method_->GetDeclaringClass())) {
-    // Field accesses through uninitialized references are only allowable for constructors where
-    // the field is declared in this class
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
-                                      << " of a not fully initialized object within the context of "
-                                      << PrettyMethod(method_);
-    return NULL;
-  } else if (!field->GetDeclaringClass()->IsAssignableFrom(obj_type.GetClass())) {
-    // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
-    // of C1. For resolution to occur the declared class of the field must be compatible with
-    // obj_type, we've discovered this wasn't so, so report the field didn't exist.
-    Fail(VERIFY_ERROR_NO_FIELD) << "cannot access instance field " << PrettyField(field)
-                                << " from object of type " << PrettyClass(obj_type.GetClass());
-    return NULL;
   } else {
-    return field;
+    const RegType& field_klass = reg_types_.FromClass(field->GetDeclaringClass());
+    if (obj_type.IsUninitializedTypes() &&
+        (!IsConstructor() || GetDeclaringClass().Equals(obj_type) ||
+            !field_klass.Equals(GetDeclaringClass()))) {
+      // Field accesses through uninitialized references are only allowable for constructors where
+      // the field is declared in this class
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
+                                      << " of a not fully initialized object within the context of "
+                                      << PrettyMethod(method_idx_, *dex_file_);
+      return NULL;
+    } else if (!field_klass.IsAssignableFrom(obj_type)) {
+      // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
+      // of C1. For resolution to occur the declared class of the field must be compatible with
+      // obj_type, we've discovered this wasn't so, so report the field didn't exist.
+      Fail(VERIFY_ERROR_NO_FIELD) << "cannot access instance field " << PrettyField(field)
+                                  << " from object of type " << obj_type;
+      return NULL;
+    } else {
+      return field;
+    }
   }
 }
 
@@ -2878,46 +2886,44 @@
     const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB);
     field = GetInstanceField(object_type, field_idx);
   }
-  if (failure_ != VERIFY_ERROR_NONE) {
-    work_line_->SetRegisterType(dec_insn.vA, reg_types_.Unknown());
+  const char* descriptor;
+  const ClassLoader* loader;
+  if (field != NULL) {
+    descriptor = FieldHelper(field).GetTypeDescriptor();
+    loader = field->GetDeclaringClass()->GetClassLoader();
   } else {
-    const char* descriptor;
-    const ClassLoader* loader;
-    if (field != NULL) {
-      descriptor = FieldHelper(field).GetTypeDescriptor();
-      loader = field->GetDeclaringClass()->GetClassLoader();
-    } else {
-      const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
-      descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
-      loader = method_->GetDeclaringClass()->GetClassLoader();
-    }
-    const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
-    if (is_primitive) {
-      if (field_type.Equals(insn_type) ||
-          (field_type.IsFloat() && insn_type.IsIntegralTypes()) ||
-          (field_type.IsDouble() && insn_type.IsLongTypes())) {
-        // expected that read is of the correct primitive type or that int reads are reading
-        // floats or long reads are reading doubles
-      } else {
-        // This is a global failure rather than a class change failure as the instructions and
-        // the descriptors for the type should have been consistent within the same file at
-        // compile time
-        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
-                                          << " to be of type '" << insn_type
-                                          << "' but found type '" << field_type << "' in get";
-        return;
-      }
-    } else {
-      if (!insn_type.IsAssignableFrom(field_type)) {
-        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                          << " to be compatible with type '" << insn_type
-                                          << "' but found type '" << field_type
-                                          << "' in get-object";
-        return;
-      }
-    }
-    work_line_->SetRegisterType(dec_insn.vA, field_type);
+    const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
+    descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
+    loader = class_loader_;
   }
+  const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
+  if (is_primitive) {
+    if (field_type.Equals(insn_type) ||
+        (field_type.IsFloat() && insn_type.IsIntegralTypes()) ||
+        (field_type.IsDouble() && insn_type.IsLongTypes())) {
+      // expected that read is of the correct primitive type or that int reads are reading
+      // floats or long reads are reading doubles
+    } else {
+      // This is a global failure rather than a class change failure as the instructions and
+      // the descriptors for the type should have been consistent within the same file at
+      // compile time
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+                                        << " to be of type '" << insn_type
+                                        << "' but found type '" << field_type << "' in get";
+      work_line_->SetRegisterType(dec_insn.vA, reg_types_.Conflict());
+      return;
+    }
+  } else {
+    if (!insn_type.IsAssignableFrom(field_type)) {
+      Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+                                        << " to be compatible with type '" << insn_type
+                                        << "' but found type '" << field_type
+                                        << "' in get-object";
+      work_line_->SetRegisterType(dec_insn.vA, reg_types_.Conflict());
+      return;
+    }
+  }
+  work_line_->SetRegisterType(dec_insn.vA, field_type);
 }
 
 void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn,
@@ -2930,75 +2936,71 @@
     const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB);
     field = GetInstanceField(object_type, field_idx);
   }
-  if (failure_ != VERIFY_ERROR_NONE) {
-    work_line_->SetRegisterType(dec_insn.vA, reg_types_.Unknown());
+  const char* descriptor;
+  const ClassLoader* loader;
+  if (field != NULL) {
+    descriptor = FieldHelper(field).GetTypeDescriptor();
+    loader = field->GetDeclaringClass()->GetClassLoader();
   } else {
-    const char* descriptor;
-    const ClassLoader* loader;
-    if (field != NULL) {
-      descriptor = FieldHelper(field).GetTypeDescriptor();
-      loader = field->GetDeclaringClass()->GetClassLoader();
+    const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
+    descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
+    loader = class_loader_;
+  }
+  const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
+  if (field != NULL) {
+    if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
+      Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
+                                      << " from other class " << GetDeclaringClass();
+      return;
+    }
+  }
+  if (is_primitive) {
+    // Primitive field assignability rules are weaker than regular assignability rules
+    bool instruction_compatible;
+    bool value_compatible;
+    const RegType& value_type = work_line_->GetRegisterType(dec_insn.vA);
+    if (field_type.IsIntegralTypes()) {
+      instruction_compatible = insn_type.IsIntegralTypes();
+      value_compatible = value_type.IsIntegralTypes();
+    } else if (field_type.IsFloat()) {
+      instruction_compatible = insn_type.IsInteger();  // no [is]put-float, so expect [is]put-int
+      value_compatible = value_type.IsFloatTypes();
+    } else if (field_type.IsLong()) {
+      instruction_compatible = insn_type.IsLong();
+      value_compatible = value_type.IsLongTypes();
+    } else if (field_type.IsDouble()) {
+      instruction_compatible = insn_type.IsLong();  // no [is]put-double, so expect [is]put-long
+      value_compatible = value_type.IsDoubleTypes();
     } else {
-      const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
-      descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
-      loader = method_->GetDeclaringClass()->GetClassLoader();
+      instruction_compatible = false;  // reference field with primitive store
+      value_compatible = false;  // unused
     }
-    const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
-    if (field != NULL) {
-      if (field->IsFinal() && field->GetDeclaringClass() != method_->GetDeclaringClass()) {
-        Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
-                               << " from other class " << PrettyClass(method_->GetDeclaringClass());
-        return;
-      }
+    if (!instruction_compatible) {
+      // This is a global failure rather than a class change failure as the instructions and
+      // the descriptors for the type should have been consistent within the same file at
+      // compile time
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+                                        << " to be of type '" << insn_type
+                                        << "' but found type '" << field_type
+                                        << "' in put";
+      return;
     }
-    if (is_primitive) {
-      // Primitive field assignability rules are weaker than regular assignability rules
-      bool instruction_compatible;
-      bool value_compatible;
-      const RegType& value_type = work_line_->GetRegisterType(dec_insn.vA);
-      if (field_type.IsIntegralTypes()) {
-        instruction_compatible = insn_type.IsIntegralTypes();
-        value_compatible = value_type.IsIntegralTypes();
-      } else if (field_type.IsFloat()) {
-        instruction_compatible = insn_type.IsInteger();  // no [is]put-float, so expect [is]put-int
-        value_compatible = value_type.IsFloatTypes();
-      } else if (field_type.IsLong()) {
-        instruction_compatible = insn_type.IsLong();
-        value_compatible = value_type.IsLongTypes();
-      } else if (field_type.IsDouble()) {
-        instruction_compatible = insn_type.IsLong();  // no [is]put-double, so expect [is]put-long
-        value_compatible = value_type.IsDoubleTypes();
-      } else {
-        instruction_compatible = false;  // reference field with primitive store
-        value_compatible = false;  // unused
-      }
-      if (!instruction_compatible) {
-        // This is a global failure rather than a class change failure as the instructions and
-        // the descriptors for the type should have been consistent within the same file at
-        // compile time
-        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
-                                          << " to be of type '" << insn_type
-                                          << "' but found type '" << field_type
-                                          << "' in put";
-        return;
-      }
-      if (!value_compatible) {
-        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << dec_insn.vA
-                                          << " of type " << value_type
-                                          << " but expected " << field_type
-                                          << " for store to " << PrettyField(field) << " in put";
-        return;
-      }
-    } else {
-      if (!insn_type.IsAssignableFrom(field_type)) {
-        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                          << " to be compatible with type '" << insn_type
-                                          << "' but found type '" << field_type
-                                          << "' in put-object";
-        return;
-      }
-      work_line_->VerifyRegisterType(dec_insn.vA, field_type);
+    if (!value_compatible) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << dec_insn.vA
+          << " of type " << value_type
+          << " but expected " << field_type
+          << " for store to " << PrettyField(field) << " in put";
+      return;
     }
+  } else {
+    if (!insn_type.IsAssignableFrom(field_type)) {
+      Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
+                                        << " to be compatible with type '" << insn_type
+                                        << "' but found type '" << field_type
+                                        << "' in put-object";
+      return;
+    }
+    work_line_->VerifyRegisterType(dec_insn.vA, field_type);
   }
 }
 
@@ -3011,9 +3013,19 @@
 }
 
 void MethodVerifier::ReplaceFailingInstruction() {
+  // Pop the failure and clear the need for rewriting.
+  size_t failure_number = failures_.size();
+  CHECK_NE(failure_number, 0U);
+  DCHECK_EQ(failure_messages_.size(), failure_number);
+  std::ostringstream* failure_message = failure_messages_[failure_number - 1];
+  VerifyError failure = failures_[failure_number - 1];
+  failures_.pop_back();
+  failure_messages_.pop_back();
+  have_pending_rewrite_failure_ = false;
+
   if (Runtime::Current()->IsStarted()) {
-    LOG(ERROR) << "Verification attempting to replace instructions in " << PrettyMethod(method_)
-               << " " << fail_messages_.str();
+    LOG(ERROR) << "Verification attempting to replace instructions at runtime in "
+               << PrettyMethod(method_idx_, *dex_file_) << " " << failure_message->str();
     return;
   }
   const Instruction* inst = Instruction::At(code_item_->insns_ + work_insn_idx_);
@@ -3087,13 +3099,14 @@
   }
   // Encode the opcode, with the failure code in the high byte
   uint16_t new_instruction = Instruction::THROW_VERIFICATION_ERROR |
-                             (failure_ << 8) |  // AA - component
+                             (failure << 8) |  // AA - component
                              (ref_type << (8 + kVerifyErrorRefTypeShift));
   insns[work_insn_idx_] = new_instruction;
   // The 2nd code unit (higher in memory) with the reference in, comes from the instruction we
   // rewrote, so nothing to do here.
-  LOG(INFO) << "Verification error, replacing instructions in " << PrettyMethod(method_) << " "
-            << fail_messages_.str();
+  LOG(INFO) << "Verification error, replacing instructions in "
+            << PrettyMethod(method_idx_, *dex_file_) << " "
+            << failure_message->str();
   if (gDebugVerify) {
     std::cout << std::endl << info_messages_.str();
     Dump(std::cout);
@@ -3116,7 +3129,7 @@
       copy->CopyFromLine(target_line);
     }
     changed = target_line->MergeRegisters(merge_line);
-    if (failure_ != VERIFY_ERROR_NONE) {
+    if (have_pending_hard_failure_) {
       return false;
     }
     if (gDebugVerify && changed) {
@@ -3137,6 +3150,24 @@
   return &insn_flags_[work_insn_idx_];
 }
 
+const RegType& MethodVerifier::GetMethodReturnType() {
+  const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_);
+  const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
+  uint16_t return_type_idx = proto_id.return_type_idx_;
+  const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
+  return reg_types_.FromDescriptor(class_loader_, descriptor);
+}
+
+const RegType& MethodVerifier::GetDeclaringClass() {
+  if (foo_method_ != NULL) {
+    return reg_types_.FromClass(foo_method_->GetDeclaringClass());
+  } else {
+    const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_);
+    const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
+    return reg_types_.FromDescriptor(class_loader_, descriptor);
+  }
+}
+
 void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits,
                                     size_t* log2_max_gc_pc) {
   size_t local_gc_points = 0;
diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h
index fc5b7ea..512e26f 100644
--- a/src/verifier/method_verifier.h
+++ b/src/verifier/method_verifier.h
@@ -83,7 +83,6 @@
  * to be rewritten to fail at runtime.
  */
 enum VerifyError {
-  VERIFY_ERROR_NONE = 0,       // No error; must be zero.
   VERIFY_ERROR_BAD_CLASS_HARD, // VerifyError; hard error that skips compilation.
   VERIFY_ERROR_BAD_CLASS_SOFT, // VerifyError; soft error that verifies again at runtime.
 
@@ -153,11 +152,6 @@
  public:
   /* Verify a class. Returns "true" on success. */
   static bool VerifyClass(const Class* klass, std::string& error);
-
-  /*
-   * Structurally verify a class. Returns "true" on success. Used at compile time
-   * when the pointer for the method or declaring class can't be resolved.
-   */
   static bool VerifyClass(const DexFile* dex_file, DexCache* dex_cache,
       const ClassLoader* class_loader, uint32_t class_def_idx, std::string& error);
 
@@ -171,15 +165,18 @@
     return &reg_types_;
   }
 
-  // Verification failed
+  // Log a verification failure.
   std::ostream& Fail(VerifyError error);
 
-  // Log for verification information
+  // Log for verification information.
   std::ostream& LogVerifyInfo() {
-    return info_messages_ << "VFY: " << PrettyMethod(method_)
+    return info_messages_ << "VFY: " << PrettyMethod(method_idx_, *dex_file_)
                           << '[' << reinterpret_cast<void*>(work_insn_idx_) << "] : ";
   }
 
+  // Dump the failures encountered by the verifier.
+  std::ostream& DumpFailures(std::ostream& os);
+
   // Dump the state of the verifier, namely each instruction, what flags are set on it, register
   // information
   void Dump(std::ostream& os);
@@ -198,9 +195,15 @@
 
  private:
 
-  explicit MethodVerifier(Method* method);
   explicit MethodVerifier(const DexFile* dex_file, DexCache* dex_cache,
-      const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item);
+      const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+      uint32_t method_idx, Method* method, uint32_t access_flags);
+
+  // Adds the given string to the beginning of the last failure message.
+  void PrependToLastFailMessage(std::string);
+
+  // Adds the given string to the end of the last failure message.
+  void AppendToLastFailMessage(std::string);
 
   /*
    * Perform verification on a single method.
@@ -212,42 +215,15 @@
    *      operands.
    *  (3) Iterate through the method, checking type safety and looking
    *      for code flow problems.
-   *
-   * Some checks may be bypassed depending on the verification mode. We can't
-   * turn this stuff off completely if we want to do "exact" GC.
-   *
-   * Confirmed here:
-   * - code array must not be empty
-   * Confirmed by ComputeWidthsAndCountOps():
-   * - opcode of first instruction begins at index 0
-   * - only documented instructions may appear
-   * - each instruction follows the last
-   * - last byte of last instruction is at (code_length-1)
-   */
-  static bool VerifyMethod(Method* method);
-  static void VerifyMethodAndDump(Method* method);
-
-  /*
-   * Perform structural verification on a single method. Used at compile time
-   * when the pointer for the method or declaring class can't be resolved.
-   *
-   * We do this in two passes:
-   *  (1) Walk through all code units, determining instruction locations,
-   *      widths, and other characteristics.
-   *  (2) Walk through all code units, performing static checks on
-   *      operands.
-   *
-   * Code flow verification is skipped since a resolved method and class are
-   * necessary to perform all the checks.
    */
   static bool VerifyMethod(uint32_t method_idx, const DexFile* dex_file, DexCache* dex_cache,
-      const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item);
+      const ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+      Method* method, uint32_t method_access_flags);
+  static void VerifyMethodAndDump(Method* method);
 
-  /* Run both structural and code flow verification on the method. */
-  bool VerifyAll();
-
-  /* Perform structural verification on a single method. */
-  bool VerifyStructure();
+  // Run verification on the method. Returns true if verification completes and false if the input
+  // has an irrecoverable corruption.
+  bool Verify();
 
   /*
    * Compute the width of the instruction at each address in the instruction stream, and store it in
@@ -519,13 +495,6 @@
                                MethodType method_type, bool is_range, bool is_super);
 
   /*
-   * Return the register type for the method. We can't just use the already-computed
-   * DalvikJniReturnType, because if it's a reference type we need to do the class lookup.
-   * Returned references are assumed to be initialized. Returns kRegTypeUnknown for "void".
-   */
-  const RegType& GetMethodReturnType();
-
-  /*
    * Verify that the target instruction is not "move-exception". It's important that the only way
    * to execute a move-exception is as the first instruction of an exception handler.
    * Returns "true" if all is well, "false" if the target instruction is move-exception.
@@ -545,6 +514,22 @@
   */
   bool UpdateRegisters(uint32_t next_insn, const RegisterLine* merge_line);
 
+  // Is the method being verified a constructor?
+  bool IsConstructor() const {
+    return (method_access_flags_ & kAccConstructor) != 0;
+  }
+
+  // Is the method verified static?
+  bool IsStatic() const {
+    return (method_access_flags_ & kAccStatic) != 0;
+  }
+
+  // Return the register type for the method.
+  const RegType& GetMethodReturnType();
+
+  // Get a type representing the declaring class of the method.
+  const RegType& GetDeclaringClass();
+
 #if defined(ART_USE_LLVM_COMPILER)
   /*
    * Generate the inferred register category for LLVM-based code generator.
@@ -601,7 +586,9 @@
   // Storage for the register status we're saving for later.
   UniquePtr<RegisterLine> saved_line_;
 
-  Method* method_;  // The method we're working on.
+  uint32_t method_idx_;  // The method we're working on.
+  Method* foo_method_;  // Its object representation if known.
+  uint32_t method_access_flags_;  // Method's access flags.
   const DexFile* dex_file_;  // The dex file containing the method.
   DexCache* dex_cache_;  // The dex_cache for the declaring class of the method.
   const ClassLoader* class_loader_;  // The class loader for the declaring class of the method.
@@ -609,12 +596,16 @@
   const DexFile::CodeItem* code_item_;  // The code item containing the code for the method.
   UniquePtr<InsnFlags[]> insn_flags_;  // Instruction widths and flags, one entry per code unit.
 
-  // The type of any error that occurs
-  VerifyError failure_;
+  // The types of any error that occurs.
+  std::vector<VerifyError> failures_;
+  // Error messages associated with failures.
+  std::vector<std::ostringstream*> failure_messages_;
+  // Is there a pending hard failure?
+  bool have_pending_hard_failure_;
+  // Is there a pending failure that will cause dex opcodes to be rewritten.
+  bool have_pending_rewrite_failure_;
 
-  // Failure message log
-  std::ostringstream fail_messages_;
-  // Info message log
+  // Info message log use primarily for verifier diagnostics.
   std::ostringstream info_messages_;
 
   // The number of occurrences of specific opcodes.
diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc
index f72ad6a..6598458 100644
--- a/src/verifier/reg_type.cc
+++ b/src/verifier/reg_type.cc
@@ -23,7 +23,7 @@
 namespace verifier {
 
 static const char* type_strings[] = {
-    "Unknown",
+    "Undefined",
     "Conflict",
     "Boolean",
     "Byte",
@@ -42,11 +42,12 @@
     "Uninitialized Reference",
     "Uninitialized This Reference",
     "Unresolved And Uninitialized Reference",
+    "Unresolved And Uninitialized This Reference",
     "Reference",
 };
 
 std::string RegType::Dump() const {
-  DCHECK(type_ >=  kRegTypeUnknown && type_ <= kRegTypeReference);
+  DCHECK(type_ >=  kRegTypeUndefined && type_ <= kRegTypeReference);
   std::string result;
   if (IsConstant()) {
     uint32_t val = ConstantValue();
@@ -84,69 +85,44 @@
   }
 }
 
-/*
- * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
- * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
- * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J
- * is the deepest (lowest upper bound) parent of S and T).
- *
- * This operation applies for regular classes and arrays, however, for interface types there needn't
- * be a partial ordering on the types. We could solve the problem of a lack of a partial order by
- * introducing sets of types, however, the only operation permissible on an interface is
- * invoke-interface. In the tradition of Java verifiers we defer the verification of interface
- * types until an invoke-interface call on the interface typed reference at runtime and allow
- * the perversion of any Object being assignable to an interface type (note, however, that we don't
- * allow assignment of Object or Interface to any concrete subclass of Object and are therefore type
- * safe; further the Join on a Object cannot result in a sub-class by definition).
- */
-Class* RegType::ClassJoin(Class* s, Class* t) {
-  DCHECK(!s->IsPrimitive()) << PrettyClass(s);
-  DCHECK(!t->IsPrimitive()) << PrettyClass(t);
-  if (s == t) {
-    return s;
-  } else if (s->IsAssignableFrom(t)) {
-    return s;
-  } else if (t->IsAssignableFrom(s)) {
-    return t;
-  } else if (s->IsArrayClass() && t->IsArrayClass()) {
-    Class* s_ct = s->GetComponentType();
-    Class* t_ct = t->GetComponentType();
-    if (s_ct->IsPrimitive() || t_ct->IsPrimitive()) {
-      // Given the types aren't the same, if either array is of primitive types then the only
-      // common parent is java.lang.Object
-      Class* result = s->GetSuperClass();  // short-cut to java.lang.Object
-      DCHECK(result->IsObjectClass());
-      return result;
-    }
-    Class* common_elem = ClassJoin(s_ct, t_ct);
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    const ClassLoader* class_loader = s->GetClassLoader();
-    std::string descriptor("[");
-    descriptor += ClassHelper(common_elem).GetDescriptor();
-    Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
-    DCHECK(array_class != NULL);
-    return array_class;
-  } else {
-    size_t s_depth = s->Depth();
-    size_t t_depth = t->Depth();
-    // Get s and t to the same depth in the hierarchy
-    if (s_depth > t_depth) {
-      while (s_depth > t_depth) {
-        s = s->GetSuperClass();
-        s_depth--;
-      }
+const RegType& RegType::GetSuperClass(RegTypeCache* cache) const {
+  if (!IsUnresolvedTypes()) {
+    Class* super_klass = GetClass()->GetSuperClass();
+    if (super_klass != NULL) {
+      return cache->FromClass(super_klass);
     } else {
-      while (t_depth > s_depth) {
-        t = t->GetSuperClass();
-        t_depth--;
-      }
+      return cache->Zero();
     }
-    // Go up the hierarchy until we get to the common parent
-    while (s != t) {
-      s = s->GetSuperClass();
-      t = t->GetSuperClass();
+  } else {
+    // TODO: handle unresolved type cases better?
+    return cache->Conflict();
+  }
+}
+
+bool RegType::CanAccess(const RegType& other) const {
+  if (Equals(other)) {
+    return true;  // Trivial accessibility.
+  } else {
+    bool this_unresolved = IsUnresolvedTypes();
+    bool other_unresolved = other.IsUnresolvedTypes();
+    if (!this_unresolved && !other_unresolved) {
+      return GetClass()->CanAccess(other.GetClass());
+    } else if (!other_unresolved) {
+      return other.GetClass()->IsPublic();  // Be conservative, only allow if other is public.
+    } else {
+      return false; // More complicated test not possible on unresolved types, be conservative.
     }
-    return s;
+  }
+}
+
+bool RegType::CanAccessMember(Class* klass, uint32_t access_flags) const {
+  if (access_flags & kAccPublic) {
+    return true;
+  }
+  if (!IsUnresolvedTypes()) {
+    return GetClass()->CanAccessMember(klass, access_flags);
+  } else {
+    return false;  // More complicated test not possible on unresolved types, be conservative.
   }
 }
 
@@ -181,6 +157,11 @@
                    GetClass()->IsAssignableFrom(src.GetClass())) {
           // We're assignable from the Class point-of-view
           return true;
+        } else if (IsUnresolvedTypes() && src.IsUnresolvedTypes() &&
+                   GetDescriptor() == src.GetDescriptor()) {
+          // Two unresolved types (maybe one is uninitialized), we're clearly assignable if the
+          // descriptor is the same.
+          return true;
         } else {
           return false;
         }
@@ -194,13 +175,13 @@
 
 const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_types) const {
   DCHECK(!Equals(incoming_type));  // Trivial equality handled by caller
-  if (IsUnknown() && incoming_type.IsUnknown()) {
-    return *this;  // Unknown MERGE Unknown => Unknown
+  if (IsUndefined() && incoming_type.IsUndefined()) {
+    return *this;  // Undefined MERGE Undefined => Undefined
   } else if (IsConflict()) {
     return *this;  // Conflict MERGE * => Conflict
   } else if (incoming_type.IsConflict()) {
     return incoming_type;  // * MERGE Conflict => Conflict
-  } else if (IsUnknown() || incoming_type.IsUnknown()) {
+  } else if (IsUndefined() || incoming_type.IsUndefined()) {
     return reg_types->Conflict();  // Unknown MERGE * => Conflict
   } else if (IsConstant() && incoming_type.IsConstant()) {
     int32_t val1 = ConstantValue();
@@ -291,6 +272,58 @@
   }
 }
 
+// See comment in reg_type.h
+Class* RegType::ClassJoin(Class* s, Class* t) {
+  DCHECK(!s->IsPrimitive()) << PrettyClass(s);
+  DCHECK(!t->IsPrimitive()) << PrettyClass(t);
+  if (s == t) {
+    return s;
+  } else if (s->IsAssignableFrom(t)) {
+    return s;
+  } else if (t->IsAssignableFrom(s)) {
+    return t;
+  } else if (s->IsArrayClass() && t->IsArrayClass()) {
+    Class* s_ct = s->GetComponentType();
+    Class* t_ct = t->GetComponentType();
+    if (s_ct->IsPrimitive() || t_ct->IsPrimitive()) {
+      // Given the types aren't the same, if either array is of primitive types then the only
+      // common parent is java.lang.Object
+      Class* result = s->GetSuperClass();  // short-cut to java.lang.Object
+      DCHECK(result->IsObjectClass());
+      return result;
+    }
+    Class* common_elem = ClassJoin(s_ct, t_ct);
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    const ClassLoader* class_loader = s->GetClassLoader();
+    std::string descriptor("[");
+    descriptor += ClassHelper(common_elem).GetDescriptor();
+    Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+    DCHECK(array_class != NULL);
+    return array_class;
+  } else {
+    size_t s_depth = s->Depth();
+    size_t t_depth = t->Depth();
+    // Get s and t to the same depth in the hierarchy
+    if (s_depth > t_depth) {
+      while (s_depth > t_depth) {
+        s = s->GetSuperClass();
+        s_depth--;
+      }
+    } else {
+      while (t_depth > s_depth) {
+        t = t->GetSuperClass();
+        t_depth--;
+      }
+    }
+    // Go up the hierarchy until we get to the common parent
+    while (s != t) {
+      s = s->GetSuperClass();
+      t = t->GetSuperClass();
+    }
+    return s;
+  }
+}
+
 std::ostream& operator<<(std::ostream& os, const RegType& rhs) {
   os << rhs.Dump();
   return os;
diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h
index 3b4e10b..9f89d07 100644
--- a/src/verifier/reg_type.h
+++ b/src/verifier/reg_type.h
@@ -33,8 +33,11 @@
 class RegType {
  public:
   enum Type {
-    kRegTypeUnknown = 0,    // Initial state.
-    kRegTypeConflict,       // Merge clash makes this reg's type unknowable.
+    // A special state that identifies a register as undefined.
+    kRegTypeUndefined = 0,
+    // The bottom type, used to denote the type of operations such as returning a void, throwing
+    // an exception or merging incompatible types, such as an int and a long.
+    kRegTypeConflict,
     kRegTypeBoolean,        // Z.
     kRegType1nrSTART = kRegTypeBoolean,
     kRegTypeIntegralSTART = kRegTypeBoolean,
@@ -57,6 +60,8 @@
     kRegTypeUninitializedReference,     // Freshly allocated reference type.
     kRegTypeUninitializedThisReference, // Freshly allocated reference passed as "this".
     kRegTypeUnresolvedAndUninitializedReference, // Freshly allocated unresolved reference type.
+                                        // Freshly allocated unresolved reference passed as "this".
+    kRegTypeUnresolvedAndUninitializedThisReference,
     kRegTypeReference,                  // Reference type.
   };
 
@@ -64,7 +69,7 @@
     return type_;
   }
 
-  bool IsUnknown() const { return type_ == kRegTypeUnknown; }
+  bool IsUndefined() const { return type_ == kRegTypeUndefined; }
   bool IsConflict() const { return type_ == kRegTypeConflict; }
   bool IsBoolean() const { return type_ == kRegTypeBoolean; }
   bool IsByte()    const { return type_ == kRegTypeByte; }
@@ -80,13 +85,17 @@
   bool IsUnresolvedAndUninitializedReference() const {
     return type_ == kRegTypeUnresolvedAndUninitializedReference;
   }
+  bool IsUnresolvedAndUninitializedThisReference() const {
+    return type_ == kRegTypeUnresolvedAndUninitializedThisReference;
+  }
   bool IsReference() const { return type_ == kRegTypeReference; }
   bool IsUninitializedTypes() const {
     return IsUninitializedReference() || IsUninitializedThisReference() ||
-        IsUnresolvedAndUninitializedReference();
+        IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference();
   }
   bool IsUnresolvedTypes() const {
-    return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference();
+    return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference() ||
+        IsUnresolvedAndUninitializedThisReference();
   }
   bool IsLowHalf() const { return type_ == kRegTypeLongLo ||
                                   type_ == kRegTypeDoubleLo ||
@@ -135,12 +144,14 @@
   }
 
   bool IsReferenceTypes() const {
-    return IsReference() || IsUnresolvedReference() || IsUninitializedReference() ||
-        IsUninitializedThisReference() || IsUnresolvedAndUninitializedReference() || IsZero();
+    return IsReference() || IsUnresolvedReference() ||  IsZero() ||
+        IsUninitializedReference() ||  IsUninitializedThisReference() ||
+        IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference();
   }
   bool IsNonZeroReferenceTypes() const {
-    return IsReference() || IsUnresolvedReference() || IsUninitializedReference() ||
-        IsUninitializedThisReference();
+    return IsReference() || IsUnresolvedReference() ||  IsZero() ||
+        IsUninitializedReference() ||  IsUninitializedThisReference() ||
+        IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference();
   }
   bool IsCategory1Types() const {
     return (type_ >= kRegType1nrSTART && type_ <= kRegType1nrEND) || IsConstant();
@@ -259,14 +270,23 @@
     return cache_id_;
   }
 
+  const RegType& GetSuperClass(RegTypeCache* cache) const;
+
   std::string Dump() const;
 
+  // Can this type access other?
+  bool CanAccess(const RegType& other) const;
+  // Can this type access a member with the given properties?
+  bool CanAccessMember(Class* klass, uint32_t access_flags) const;
+
+  // Can this type be assigned by src?
   bool IsAssignableFrom(const RegType& src) const;
 
-  const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const;
-
   bool Equals(const RegType& other) const { return GetId() == other.GetId(); }
 
+  // Compute the merge of this register from one edge (path) with incoming_type from another.
+  const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const;
+
   /*
    * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
    * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
@@ -292,7 +312,7 @@
     type_(type), klass_or_descriptor_(klass_or_descriptor), allocation_pc_or_constant_(allocation_pc_or_constant),
     cache_id_(cache_id) {
     DCHECK(IsConstant() || IsUninitializedTypes() || allocation_pc_or_constant == 0);
-    if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUnknown() &&
+    if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUndefined() &&
         !IsConflict()) {
       DCHECK(klass_or_descriptor != NULL);
       DCHECK(IsUnresolvedTypes() || klass_or_descriptor_->IsClass());
diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc
index d06377a..53b7a76 100644
--- a/src/verifier/reg_type_cache.cc
+++ b/src/verifier/reg_type_cache.cc
@@ -32,7 +32,7 @@
     case Primitive::kPrimFloat:   return RegType::kRegTypeFloat;
     case Primitive::kPrimDouble:  return RegType::kRegTypeDoubleLo;
     case Primitive::kPrimVoid:
-    default:                      return RegType::kRegTypeUnknown;
+    default:                      return RegType::kRegTypeConflict;
   }
 }
 
@@ -48,12 +48,12 @@
       case 'F': return RegType::kRegTypeFloat;
       case 'D': return RegType::kRegTypeDoubleLo;
       case 'V':
-      default:  return RegType::kRegTypeUnknown;
+      default:  return RegType::kRegTypeConflict;
     }
   } else if (descriptor[0] == 'L' || descriptor[0] == '[') {
     return RegType::kRegTypeReference;
   } else {
-    return RegType::kRegTypeUnknown;
+    return RegType::kRegTypeConflict;
   }
 }
 
@@ -115,7 +115,7 @@
       } else {
         // The descriptor is broken return the unknown type as there's nothing sensible that
         // could be done at runtime
-        return Unknown();
+        return Conflict();
       }
     }
   }
@@ -201,15 +201,30 @@
   return *entry;
 }
 
-const RegType& RegTypeCache::UninitializedThisArgument(Class* klass) {
-  for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
-    RegType* cur_entry = entries_[i];
-    if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
-      return *cur_entry;
+const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
+  // TODO: implement descriptor version.
+  RegType* entry;
+  if (type.IsUnresolvedTypes()) {
+    String* descriptor = type.GetDescriptor();
+    for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+      RegType* cur_entry = entries_[i];
+      if (cur_entry->IsUnresolvedAndUninitializedThisReference() &&
+          cur_entry->GetDescriptor() == descriptor) {
+        return *cur_entry;
+      }
     }
+    entry = new RegType(RegType::kRegTypeUnresolvedAndUninitializedThisReference, descriptor, 0,
+                        entries_.size());
+  } else {
+    Class* klass = type.GetClass();
+    for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+      RegType* cur_entry = entries_[i];
+      if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
+        return *cur_entry;
+      }
+    }
+    entry = new RegType(RegType::kRegTypeUninitializedThisReference, klass, 0, entries_.size());
   }
-  RegType* entry = new RegType(RegType::kRegTypeUninitializedThisReference, klass, 0,
-                               entries_.size());
   entries_.push_back(entry);
   return *entry;
 }
diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h
index 9c5e227..51eccd5 100644
--- a/src/verifier/reg_type_cache.h
+++ b/src/verifier/reg_type_cache.h
@@ -27,7 +27,7 @@
 class RegTypeCache {
  public:
   explicit RegTypeCache() : entries_(RegType::kRegTypeLastFixedLocation + 1) {
-    Unknown();  // ensure Unknown is initialized
+    Undefined();  // ensure Undefined is initialized
   }
   ~RegTypeCache() {
     STLDeleteElements(&entries_);
@@ -60,13 +60,14 @@
   const RegType& JavaLangString() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/String;"); }
   const RegType& JavaLangThrowable() { return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Throwable;"); }
 
-  const RegType& Unknown()  { return FromType(RegType::kRegTypeUnknown); }
+  const RegType& Undefined(){ return FromType(RegType::kRegTypeUndefined); }
   const RegType& Conflict() { return FromType(RegType::kRegTypeConflict); }
   const RegType& ConstLo()  { return FromType(RegType::kRegTypeConstLo); }
   const RegType& Zero()     { return FromCat1Const(0); }
 
   const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc);
-  const RegType& UninitializedThisArgument(Class* klass);
+  // Create an uninitialized 'this' argument for the given type.
+  const RegType& UninitializedThisArgument(const RegType& type);
   const RegType& FromUninitialized(const RegType& uninit_type);
 
   // Representatives of various constant types. When merging constants we can't infer a type,
diff --git a/src/verifier/reg_type_test.cc b/src/verifier/reg_type_test.cc
index 52e4228..18c1655 100644
--- a/src/verifier/reg_type_test.cc
+++ b/src/verifier/reg_type_test.cc
@@ -29,7 +29,7 @@
   RegTypeCache cache;
 
   const RegType& bool_reg_type = cache.Boolean();
-  EXPECT_FALSE(bool_reg_type.IsUnknown());
+  EXPECT_FALSE(bool_reg_type.IsUndefined());
   EXPECT_FALSE(bool_reg_type.IsConflict());
   EXPECT_FALSE(bool_reg_type.IsZero());
   EXPECT_FALSE(bool_reg_type.IsOne());
@@ -60,7 +60,7 @@
   EXPECT_TRUE(bool_reg_type.IsArrayIndexTypes());
 
   const RegType& byte_reg_type = cache.Byte();
-  EXPECT_FALSE(byte_reg_type.IsUnknown());
+  EXPECT_FALSE(byte_reg_type.IsUndefined());
   EXPECT_FALSE(byte_reg_type.IsConflict());
   EXPECT_FALSE(byte_reg_type.IsZero());
   EXPECT_FALSE(byte_reg_type.IsOne());
@@ -91,7 +91,7 @@
   EXPECT_TRUE(byte_reg_type.IsArrayIndexTypes());
 
   const RegType& char_reg_type = cache.Char();
-  EXPECT_FALSE(char_reg_type.IsUnknown());
+  EXPECT_FALSE(char_reg_type.IsUndefined());
   EXPECT_FALSE(char_reg_type.IsConflict());
   EXPECT_FALSE(char_reg_type.IsZero());
   EXPECT_FALSE(char_reg_type.IsOne());
@@ -122,7 +122,7 @@
   EXPECT_TRUE(char_reg_type.IsArrayIndexTypes());
 
   const RegType& short_reg_type = cache.Short();
-  EXPECT_FALSE(short_reg_type.IsUnknown());
+  EXPECT_FALSE(short_reg_type.IsUndefined());
   EXPECT_FALSE(short_reg_type.IsConflict());
   EXPECT_FALSE(short_reg_type.IsZero());
   EXPECT_FALSE(short_reg_type.IsOne());
@@ -153,7 +153,7 @@
   EXPECT_TRUE(short_reg_type.IsArrayIndexTypes());
 
   const RegType& int_reg_type = cache.Integer();
-  EXPECT_FALSE(int_reg_type.IsUnknown());
+  EXPECT_FALSE(int_reg_type.IsUndefined());
   EXPECT_FALSE(int_reg_type.IsConflict());
   EXPECT_FALSE(int_reg_type.IsZero());
   EXPECT_FALSE(int_reg_type.IsOne());
@@ -184,7 +184,7 @@
   EXPECT_TRUE(int_reg_type.IsArrayIndexTypes());
 
   const RegType& long_reg_type = cache.Long();
-  EXPECT_FALSE(long_reg_type.IsUnknown());
+  EXPECT_FALSE(long_reg_type.IsUndefined());
   EXPECT_FALSE(long_reg_type.IsConflict());
   EXPECT_FALSE(long_reg_type.IsZero());
   EXPECT_FALSE(long_reg_type.IsOne());
@@ -215,7 +215,7 @@
   EXPECT_FALSE(long_reg_type.IsArrayIndexTypes());
 
   const RegType& float_reg_type = cache.Float();
-  EXPECT_FALSE(float_reg_type.IsUnknown());
+  EXPECT_FALSE(float_reg_type.IsUndefined());
   EXPECT_FALSE(float_reg_type.IsConflict());
   EXPECT_FALSE(float_reg_type.IsZero());
   EXPECT_FALSE(float_reg_type.IsOne());
@@ -246,7 +246,7 @@
   EXPECT_FALSE(float_reg_type.IsArrayIndexTypes());
 
   const RegType& double_reg_type = cache.Double();
-  EXPECT_FALSE(double_reg_type.IsUnknown());
+  EXPECT_FALSE(double_reg_type.IsUndefined());
   EXPECT_FALSE(double_reg_type.IsConflict());
   EXPECT_FALSE(double_reg_type.IsZero());
   EXPECT_FALSE(double_reg_type.IsOne());
diff --git a/src/verifier/register_line.cc b/src/verifier/register_line.cc
index 6a86411..085a101 100644
--- a/src/verifier/register_line.cc
+++ b/src/verifier/register_line.cc
@@ -23,7 +23,8 @@
 
 bool RegisterLine::CheckConstructorReturn() const {
   for (size_t i = 0; i < num_regs_; i++) {
-    if (GetRegisterType(i).IsUninitializedThisReference()) {
+    if (GetRegisterType(i).IsUninitializedThisReference() ||
+        GetRegisterType(i).IsUnresolvedAndUninitializedThisReference()) {
       verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
           << "Constructor returning without calling superclass constructor";
       return false;
@@ -33,7 +34,7 @@
 }
 
 bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) {
-  DCHECK(vdst < num_regs_);
+  DCHECK_LT(vdst, num_regs_);
   if (new_type.IsLowHalf()) {
     line_[vdst] = new_type.GetId();
     line_[vdst + 1] = new_type.HighHalf(verifier_->GetRegTypeCache()).GetId();
@@ -53,9 +54,8 @@
 }
 
 void RegisterLine::SetResultTypeToUnknown() {
-  uint16_t unknown_id = verifier_->GetRegTypeCache()->Unknown().GetId();
-  result_[0] = unknown_id;
-  result_[1] = unknown_id;
+  result_[0] = RegType::kRegTypeUndefined;
+  result_[1] = RegType::kRegTypeUndefined;
 }
 
 void RegisterLine::SetResultRegisterType(const RegType& new_type) {
@@ -64,7 +64,7 @@
     DCHECK_EQ(new_type.HighHalf(verifier_->GetRegTypeCache()).GetId(), new_type.GetId() + 1);
     result_[1] = new_type.GetId() + 1;
   } else {
-    result_[1] = verifier_->GetRegTypeCache()->Unknown().GetId();
+    result_[1] = RegType::kRegTypeUndefined;
   }
 }
 
@@ -77,14 +77,14 @@
 const RegType& RegisterLine::GetInvocationThis(const DecodedInstruction& dec_insn) {
   if (dec_insn.vA < 1) {
     verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke lacks 'this'";
-    return verifier_->GetRegTypeCache()->Unknown();
+    return verifier_->GetRegTypeCache()->Conflict();
   }
   /* get the element type of the array held in vsrc */
   const RegType& this_type = GetRegisterType(dec_insn.vC);
   if (!this_type.IsReferenceTypes()) {
     verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "tried to get class from non-reference register v"
                                                  << dec_insn.vC << " (type=" << this_type << ")";
-    return verifier_->GetRegTypeCache()->Unknown();
+    return verifier_->GetRegTypeCache()->Conflict();
   }
   return this_type;
 }
@@ -168,9 +168,9 @@
     verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
         << "copyRes1 v" << vdst << "<- result0"  << " type=" << type;
   } else {
-    DCHECK(verifier_->GetRegTypeCache()->GetFromId(result_[1]).IsUnknown());
+    DCHECK(verifier_->GetRegTypeCache()->GetFromId(result_[1]).IsUndefined());
     SetRegisterType(vdst, type);
-    result_[0] = verifier_->GetRegTypeCache()->Unknown().GetId();
+    result_[0] = RegType::kRegTypeUndefined;
   }
 }
 
@@ -187,8 +187,8 @@
   } else {
     DCHECK(type_l.CheckWidePair(type_h));  // Set should never allow this case
     SetRegisterType(vdst, type_l);  // also sets the high
-    result_[0] = verifier_->GetRegTypeCache()->Unknown().GetId();
-    result_[1] = verifier_->GetRegTypeCache()->Unknown().GetId();
+    result_[0] = RegType::kRegTypeUndefined;
+    result_[1] = RegType::kRegTypeUndefined;
   }
 }
 
diff --git a/src/verifier/register_line.h b/src/verifier/register_line.h
index 6b921cc..f214b81 100644
--- a/src/verifier/register_line.h
+++ b/src/verifier/register_line.h
@@ -56,8 +56,8 @@
   RegisterLine(size_t num_regs, MethodVerifier* verifier) :
     line_(new uint16_t[num_regs]), verifier_(verifier), num_regs_(num_regs) {
     memset(line_.get(), 0, num_regs_ * sizeof(uint16_t));
-    result_[0] = RegType::kRegTypeUnknown;
-    result_[1] = RegType::kRegTypeUnknown;
+    result_[0] = RegType::kRegTypeUndefined;
+    result_[1] = RegType::kRegTypeUndefined;
   }
 
   // Implement category-1 "move" instructions. Copy a 32-bit value from "vsrc" to "vdst".
@@ -285,7 +285,7 @@
   MethodVerifier* verifier_;
 
   // Length of reg_types_
-  const size_t num_regs_;
+  const uint32_t num_regs_;
   // A stack of monitor enter locations
   std::deque<uint32_t> monitors_;
   // A map from register to a bit vector of indices into the monitors_ stack. As we pop the monitor