Add notion of precise register types to verifier.

When a class may have sub-classes we don't know if a method is
overridden. Add an ability to the verifier to track when we have precise
type information. This allows the verifier differentiate the case of having
something that must be of the type from the case where something is that
type or its sub-classes.

Change-Id: I9995be7d6c147db3b22aa390b776d9c2eb93edd8
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index ad238b8..67507bc 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -1054,7 +1054,8 @@
     os << "Native method\n";
     return;
   }
-  DCHECK(code_item_ != NULL);
+  reg_types_.Dump(os);
+  os << "Dumping instructions and register lines:\n";
   const Instruction* inst = Instruction::At(code_item_->insns_);
   for (size_t dex_pc = 0; dex_pc < code_item_->insns_size_in_code_units_;
       dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) {
@@ -1128,7 +1129,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(class_loader_, descriptor);
+          const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
           reg_line->SetRegisterType(arg_start + cur_arg, reg_type);
         }
         break;
@@ -1527,7 +1528,8 @@
       const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB);
       // Register holds class, ie its type is class, on error it will hold Conflict.
       work_line_->SetRegisterType(dec_insn.vA,
-                                  res_type.IsConflict() ? res_type : reg_types_.JavaLangClass());
+                                  res_type.IsConflict() ? res_type
+                                                        : reg_types_.JavaLangClass(true));
       break;
     }
     case Instruction::MONITOR_ENTER:
@@ -1667,7 +1669,7 @@
       break;
     case Instruction::THROW: {
       const RegType& res_type = work_line_->GetRegisterType(dec_insn.vA);
-      if (!reg_types_.JavaLangThrowable().IsAssignableFrom(res_type)) {
+      if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) {
         Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "thrown class " << res_type << " not instanceof Throwable";
       }
       break;
@@ -1785,7 +1787,7 @@
       VerifyAGet(dec_insn, reg_types_.Long(), true);
       break;
     case Instruction::AGET_OBJECT:
-      VerifyAGet(dec_insn, reg_types_.JavaLangObject(), false);
+      VerifyAGet(dec_insn, reg_types_.JavaLangObject(false), false);
       break;
 
     case Instruction::APUT_BOOLEAN:
@@ -1807,7 +1809,7 @@
       VerifyAPut(dec_insn, reg_types_.Long(), true);
       break;
     case Instruction::APUT_OBJECT:
-      VerifyAPut(dec_insn, reg_types_.JavaLangObject(), false);
+      VerifyAPut(dec_insn, reg_types_.JavaLangObject(false), false);
       break;
 
     case Instruction::IGET_BOOLEAN:
@@ -1829,7 +1831,7 @@
       VerifyISGet(dec_insn, reg_types_.Long(), true, false);
       break;
     case Instruction::IGET_OBJECT:
-      VerifyISGet(dec_insn, reg_types_.JavaLangObject(), false, false);
+      VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, false);
       break;
 
     case Instruction::IPUT_BOOLEAN:
@@ -1851,7 +1853,7 @@
       VerifyISPut(dec_insn, reg_types_.Long(), true, false);
       break;
     case Instruction::IPUT_OBJECT:
-      VerifyISPut(dec_insn, reg_types_.JavaLangObject(), false, false);
+      VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, false);
       break;
 
     case Instruction::SGET_BOOLEAN:
@@ -1873,7 +1875,7 @@
       VerifyISGet(dec_insn, reg_types_.Long(), true, true);
       break;
     case Instruction::SGET_OBJECT:
-      VerifyISGet(dec_insn, reg_types_.JavaLangObject(), false, true);
+      VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, true);
       break;
 
     case Instruction::SPUT_BOOLEAN:
@@ -1895,7 +1897,7 @@
       VerifyISPut(dec_insn, reg_types_.Long(), true, true);
       break;
     case Instruction::SPUT_OBJECT:
-      VerifyISPut(dec_insn, reg_types_.JavaLangObject(), false, true);
+      VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, true);
       break;
 
     case Instruction::INVOKE_VIRTUAL:
@@ -1916,7 +1918,7 @@
       } else {
         descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
       }
-      const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor);
+      const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
       work_line_->SetResultRegisterType(return_type);
       just_set_result = true;
       break;
@@ -1977,7 +1979,8 @@
          */
         work_line_->MarkRefsAsInitialized(this_type);
       }
-      const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor);
+      const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor,
+                                                             false);
       work_line_->SetResultRegisterType(return_type);
       just_set_result = true;
       break;
@@ -1995,7 +1998,7 @@
         } else {
           descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
         }
-        const RegType& return_type =  reg_types_.FromDescriptor(class_loader_, descriptor);
+        const RegType& return_type =  reg_types_.FromDescriptor(class_loader_, descriptor, false);
         work_line_->SetResultRegisterType(return_type);
         just_set_result = true;
       }
@@ -2045,7 +2048,7 @@
       } else {
         descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor();
       }
-      const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor);
+      const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
       work_line_->SetResultRegisterType(return_type);
       work_line_->SetResultRegisterType(return_type);
       just_set_result = true;
@@ -2466,8 +2469,8 @@
   const RegType& referrer = GetDeclaringClass();
   Class* klass = dex_cache_->GetResolvedType(class_idx);
   const RegType& result =
-      klass != NULL ? reg_types_.FromClass(klass)
-                    : reg_types_.FromDescriptor(class_loader_, descriptor);
+      klass != NULL ? reg_types_.FromClass(klass, klass->IsFinal())
+                    : reg_types_.FromDescriptor(class_loader_, descriptor, false);
   if (result.IsConflict()) {
     Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor
         << "' in " << referrer;
@@ -2495,14 +2498,14 @@
       for (; iterator.HasNext(); iterator.Next()) {
         if (iterator.GetHandlerAddress() == (uint32_t) work_insn_idx_) {
           if (iterator.GetHandlerTypeIndex() == DexFile::kDexNoIndex16) {
-            common_super = &reg_types_.JavaLangThrowable();
+            common_super = &reg_types_.JavaLangThrowable(false);
           } else {
             const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
             if (common_super == NULL) {
               // Unconditionally assign for the first handler. We don't assert this is a Throwable
               // as that is caught at runtime
               common_super = &exception;
-            } else if (!reg_types_.JavaLangThrowable().IsAssignableFrom(exception)) {
+            } else if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) {
               // We don't know enough about the type and the common path merge will result in
               // Conflict. Fail here knowing the correct thing can be done at runtime.
               Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception;
@@ -2511,7 +2514,7 @@
               // odd case, but nothing to do
             } else {
               common_super = &common_super->Merge(exception, &reg_types_);
-              CHECK(reg_types_.JavaLangThrowable().IsAssignableFrom(*common_super));
+              CHECK(reg_types_.JavaLangThrowable(false).IsAssignableFrom(*common_super));
             }
           }
         }
@@ -2677,7 +2680,8 @@
       return NULL;
     }
     if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) {
-      const RegType& res_method_class = reg_types_.FromClass(res_method->GetDeclaringClass());
+      Class* klass = res_method->GetDeclaringClass();
+      const RegType& res_method_class = reg_types_.FromClass(klass, klass->IsFinal());
       if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
         Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
             << "' not instance of '" << res_method_class << "'";
@@ -2707,7 +2711,7 @@
           << " missing signature component";
       return NULL;
     }
-    const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor);
+    const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
     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 res_method;
@@ -2904,7 +2908,8 @@
     // Cannot infer and check type, however, access will cause null pointer exception
     return field;
   } else {
-    const RegType& field_klass = reg_types_.FromClass(field->GetDeclaringClass());
+    Class* klass = field->GetDeclaringClass();
+    const RegType& field_klass = reg_types_.FromClass(klass, klass->IsFinal());
     if (obj_type.IsUninitializedTypes() &&
         (!IsConstructor() || GetDeclaringClass().Equals(obj_type) ||
             !field_klass.Equals(GetDeclaringClass()))) {
@@ -2947,7 +2952,7 @@
     descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
     loader = class_loader_;
   }
-  const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
+  const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
   if (is_primitive) {
     if (field_type.Equals(insn_type) ||
         (field_type.IsFloat() && insn_type.IsIntegralTypes()) ||
@@ -2996,7 +3001,7 @@
     descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
     loader = class_loader_;
   }
-  const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor);
+  const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
   if (field != NULL) {
     if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
       Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
@@ -3104,16 +3109,17 @@
   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);
+  return reg_types_.FromDescriptor(class_loader_, descriptor, false);
 }
 
 const RegType& MethodVerifier::GetDeclaringClass() {
   if (foo_method_ != NULL) {
-    return reg_types_.FromClass(foo_method_->GetDeclaringClass());
+    Class* klass = foo_method_->GetDeclaringClass();
+    return reg_types_.FromClass(klass, klass->IsFinal());
   } 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);
+    return reg_types_.FromDescriptor(class_loader_, descriptor, false);
   }
 }
 
diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc
index f555223..e02fbf4 100644
--- a/src/verifier/reg_type.cc
+++ b/src/verifier/reg_type.cc
@@ -46,11 +46,12 @@
     "Unresolved Merged References",
     "Unresolved Super Class",
     "Reference",
+    "Precise Reference",
 };
 
 std::string RegType::Dump(const RegTypeCache* reg_types) const {
-  DCHECK(type_ >=  kRegTypeUndefined && type_ <= kRegTypeReference);
-  DCHECK(arraysize(type_strings) == (kRegTypeReference + 1));
+  DCHECK(type_ >=  kRegTypeUndefined && type_ <= kRegTypePreciseReference);
+  DCHECK(arraysize(type_strings) == (kRegTypePreciseReference + 1));
   std::string result;
   if (IsUnresolvedMergedReference()) {
     if (reg_types == NULL) {
@@ -142,7 +143,7 @@
   if (!IsUnresolvedTypes()) {
     Class* super_klass = GetClass()->GetSuperClass();
     if (super_klass != NULL) {
-      return cache->FromClass(super_klass);
+      return cache->FromClass(super_klass, IsPreciseReference());
     } else {
       return cache->Zero();
     }
@@ -150,7 +151,7 @@
     if (!IsUnresolvedMergedReference() && !IsUnresolvedSuperClass() &&
         GetDescriptor()->CharAt(0) == '[') {
       // Super class of all arrays is Object.
-      return cache->JavaLangObject();
+      return cache->JavaLangObject(true);
     } else {
       return cache->FromUnresolvedSuperClass(*this);
     }
@@ -301,7 +302,7 @@
     if (IsZero() || incoming_type.IsZero()) {
       return SelectNonConstant(*this, incoming_type);  // 0 MERGE ref => ref
     } else if (IsJavaLangObject() || incoming_type.IsJavaLangObject()) {
-      return reg_types->JavaLangObject();  // Object MERGE ref => Object
+      return reg_types->JavaLangObject(false);  // Object MERGE ref => Object
     } else if (IsUnresolvedTypes() || incoming_type.IsUnresolvedTypes()) {
       // We know how to merge an unresolved type with itself, 0 or Object. In this case we
       // have two sub-classes and don't know how to merge. Create a new string-based unresolved
@@ -319,12 +320,12 @@
       DCHECK(c1 != NULL && !c1->IsPrimitive());
       DCHECK(c2 != NULL && !c2->IsPrimitive());
       Class* join_class = ClassJoin(c1, c2);
-      if (c1 == join_class) {
+      if (c1 == join_class && !IsPreciseReference()) {
         return *this;
-      } else if (c2 == join_class) {
+      } else if (c2 == join_class && !incoming_type.IsPreciseReference()) {
         return incoming_type;
       } else {
-        return reg_types->FromClass(join_class);
+        return reg_types->FromClass(join_class, false);
       }
     }
   } else {
diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h
index 3064f30..205867d 100644
--- a/src/verifier/reg_type.h
+++ b/src/verifier/reg_type.h
@@ -65,6 +65,7 @@
     kRegTypeUnresolvedMergedReference,  // Tree of merged references (at least 1 is unresolved).
     kRegTypeUnresolvedSuperClass,       // Super class of an unresolved type.
     kRegTypeReference,                  // Reference type.
+    kRegTypePreciseReference,           // Precisely the given type.
   };
 
   Type GetType() const {
@@ -93,6 +94,8 @@
   bool IsUnresolvedMergedReference() const {  return type_ == kRegTypeUnresolvedMergedReference; }
   bool IsUnresolvedSuperClass() const {  return type_ == kRegTypeUnresolvedSuperClass; }
   bool IsReference() const { return type_ == kRegTypeReference; }
+  bool IsPreciseReference() const { return type_ == kRegTypePreciseReference; }
+
   bool IsUninitializedTypes() const {
     return IsUninitializedReference() || IsUninitializedThisReference() ||
         IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference();
@@ -154,7 +157,7 @@
   }
 
   bool IsNonZeroReferenceTypes() const {
-    return IsReference() || IsUnresolvedReference() ||
+    return IsReference() || IsPreciseReference() || IsUnresolvedReference() ||
         IsUninitializedReference() || IsUninitializedThisReference() ||
         IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference() ||
         IsUnresolvedMergedReference() || IsUnresolvedSuperClass();
@@ -198,6 +201,10 @@
     return allocation_pc_or_constant_or_merged_types_;
   }
 
+  bool HasClass() const {
+    return IsReference() || IsPreciseReference();
+  }
+
   Class* GetClass() const {
     DCHECK(!IsUnresolvedReference());
     DCHECK(klass_or_descriptor_ != NULL);
@@ -212,7 +219,7 @@
   bool IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
       return GetDescriptor()->CharAt(0) == '[';
-    } else if (IsReference()) {
+    } else if (HasClass()) {
       return GetClass()->IsArrayClass();
     } else {
       return false;
@@ -224,7 +231,7 @@
       // Primitive arrays will always resolve
       DCHECK(GetDescriptor()->CharAt(1) == 'L' || GetDescriptor()->CharAt(1) == '[');
       return GetDescriptor()->CharAt(0) == '[';
-    } else if (IsReference()) {
+    } else if (HasClass()) {
       Class* type = GetClass();
       return type->IsArrayClass() && !type->GetComponentType()->IsPrimitive();
     } else {
@@ -256,7 +263,7 @@
   }
 
   bool IsJavaLangObjectArray() const {
-    if (IsReference()) {
+    if (HasClass()) {
       Class* type = GetClass();
       return type->IsArrayClass() && type->GetComponentType()->IsObjectClass();
     }
diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc
index 37086c9..847cde9 100644
--- a/src/verifier/reg_type_cache.cc
+++ b/src/verifier/reg_type_cache.cc
@@ -57,11 +57,18 @@
   }
 }
 
-const RegType& RegTypeCache::FromDescriptor(ClassLoader* loader, const char* descriptor) {
-  return From(RegTypeFromDescriptor(descriptor), loader, descriptor);
+const RegType& RegTypeCache::FromDescriptor(ClassLoader* loader, const char* descriptor,
+                                            bool precise) {
+  return From(RegTypeFromDescriptor(descriptor), loader, descriptor, precise);
 }
 
-const RegType& RegTypeCache::From(RegType::Type type, ClassLoader* loader, const char* descriptor) {
+static bool MatchingPrecisionForClass(RegType* entry, bool precise)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  return (entry->IsPreciseReference() == precise) || (entry->GetClass()->IsFinal() && !precise);
+}
+
+const RegType& RegTypeCache::From(RegType::Type type, ClassLoader* loader, const char* descriptor,
+                                  bool precise) {
   if (type <= RegType::kRegTypeLastFixedLocation) {
     // entries should be sized greater than primitive types
     DCHECK_GT(entries_.size(), static_cast<size_t>(type));
@@ -76,14 +83,15 @@
     }
     return *entry;
   } else {
-    DCHECK(type == RegType::kRegTypeReference);
+    DCHECK(type == RegType::kRegTypeReference || type == RegType::kRegTypePreciseReference);
     ClassHelper kh;
     for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
       RegType* cur_entry = entries_[i];
       // check resolved and unresolved references, ignore uninitialized references
-      if (cur_entry->IsReference()) {
+      if (cur_entry->HasClass()) {
         kh.ChangeClass(cur_entry->GetClass());
-        if (strcmp(descriptor, kh.GetDescriptor()) == 0) {
+        if (MatchingPrecisionForClass(cur_entry, precise) &&
+            (strcmp(descriptor, kh.GetDescriptor()) == 0)) {
           return *cur_entry;
         }
       } else if (cur_entry->IsUnresolvedReference() &&
@@ -93,8 +101,11 @@
     }
     Class* klass = Runtime::Current()->GetClassLinker()->FindClass(descriptor, loader);
     if (klass != NULL) {
-      // Able to resolve so create resolved register type
-      RegType* entry = new RegType(type, klass, 0, entries_.size());
+      // Able to resolve so create resolved register type that is precise if we
+      // know the type is final.
+      RegType* entry = new RegType(klass->IsFinal() ? RegType::kRegTypePreciseReference
+                                                    : RegType::kRegTypeReference,
+                                   klass, 0, entries_.size());
       entries_.push_back(entry);
       return *entry;
     } else {
@@ -119,7 +130,7 @@
   }
 }
 
-const RegType& RegTypeCache::FromClass(Class* klass) {
+const RegType& RegTypeCache::FromClass(Class* klass, bool precise) {
   if (klass->IsPrimitive()) {
     RegType::Type type = RegTypeFromPrimitiveType(klass->GetPrimitiveType());
     // entries should be sized greater than primitive types
@@ -133,11 +144,14 @@
   } else {
     for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
       RegType* cur_entry = entries_[i];
-      if (cur_entry->IsReference() && cur_entry->GetClass() == klass) {
+      if ((cur_entry->HasClass()) &&
+          MatchingPrecisionForClass(cur_entry, precise) && cur_entry->GetClass() == klass) {
         return *cur_entry;
       }
     }
-    RegType* entry = new RegType(RegType::kRegTypeReference, klass, 0, entries_.size());
+    RegType* entry = new RegType(precise ? RegType::kRegTypePreciseReference
+                                         : RegType::kRegTypeReference,
+                                 klass, 0, entries_.size());
     entries_.push_back(entry);
     return *entry;
   }
@@ -243,11 +257,11 @@
     Class* klass = uninit_type.GetClass();
     for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
       RegType* cur_entry = entries_[i];
-      if (cur_entry->IsReference() && cur_entry->GetClass() == klass) {
+      if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) {
         return *cur_entry;
       }
     }
-    entry = new RegType(RegType::kRegTypeReference, klass, 0, entries_.size());
+    entry = new RegType(RegType::kRegTypePreciseReference, klass, 0, entries_.size());
   }
   entries_.push_back(entry);
   return *entry;
@@ -284,17 +298,17 @@
 const RegType& RegTypeCache::FromType(RegType::Type type) {
   CHECK(type < RegType::kRegTypeReference);
   switch (type) {
-    case RegType::kRegTypeBoolean:  return From(type, NULL, "Z");
-    case RegType::kRegTypeByte:     return From(type, NULL, "B");
-    case RegType::kRegTypeShort:    return From(type, NULL, "S");
-    case RegType::kRegTypeChar:     return From(type, NULL, "C");
-    case RegType::kRegTypeInteger:  return From(type, NULL, "I");
-    case RegType::kRegTypeFloat:    return From(type, NULL, "F");
+    case RegType::kRegTypeBoolean:  return From(type, NULL, "Z", true);
+    case RegType::kRegTypeByte:     return From(type, NULL, "B", true);
+    case RegType::kRegTypeShort:    return From(type, NULL, "S", true);
+    case RegType::kRegTypeChar:     return From(type, NULL, "C", true);
+    case RegType::kRegTypeInteger:  return From(type, NULL, "I", true);
+    case RegType::kRegTypeFloat:    return From(type, NULL, "F", true);
     case RegType::kRegTypeLongLo:
-    case RegType::kRegTypeLongHi:   return From(type, NULL, "J");
+    case RegType::kRegTypeLongHi:   return From(type, NULL, "J", true);
     case RegType::kRegTypeDoubleLo:
-    case RegType::kRegTypeDoubleHi: return From(type, NULL, "D");
-    default:                        return From(type, NULL, "");
+    case RegType::kRegTypeDoubleHi: return From(type, NULL, "D", true);
+    default:                        return From(type, NULL, "", true);
   }
 }
 
@@ -315,9 +329,20 @@
   if (array.IsUnresolvedTypes()) {
     std::string descriptor(array.GetDescriptor()->ToModifiedUtf8());
     std::string component(descriptor.substr(1, descriptor.size() - 1));
-    return FromDescriptor(loader, component.c_str());
+    return FromDescriptor(loader, component.c_str(), false);
   } else {
-    return FromClass(array.GetClass()->GetComponentType());
+    Class* klass = array.GetClass()->GetComponentType();
+    return FromClass(klass, klass->IsFinal());
+  }
+}
+
+void RegTypeCache::Dump(std::ostream& os) {
+  os << "Register Types:\n";
+  for (size_t i = 0; i < entries_.size(); i++) {
+    RegType* cur_entry = entries_[i];
+    if (cur_entry != NULL) {
+      os << "\t" << i << ": " << cur_entry->Dump() << "\n";
+    }
   }
 }
 
diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h
index 5a2c49c..36fd2c7 100644
--- a/src/verifier/reg_type_cache.h
+++ b/src/verifier/reg_type_cache.h
@@ -40,12 +40,12 @@
     return *result;
   }
 
-  const RegType& From(RegType::Type type, ClassLoader* loader, const char* descriptor)
+  const RegType& From(RegType::Type type, ClassLoader* loader, const char* descriptor, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const RegType& FromClass(Class* klass)
+  const RegType& FromClass(Class* klass, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const RegType& FromCat1Const(int32_t value);
-  const RegType& FromDescriptor(ClassLoader* loader, const char* descriptor)
+  const RegType& FromDescriptor(ClassLoader* loader, const char* descriptor, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const RegType& FromType(RegType::Type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -77,17 +77,24 @@
     return FromType(RegType::kRegTypeDoubleLo);
   }
 
-  const RegType& JavaLangClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Class;");
+  const RegType& JavaLangClass(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return From(precise ? RegType::kRegTypeReference
+                        : RegType::kRegTypePreciseReference,
+                NULL, "Ljava/lang/Class;", precise);
   }
-  const RegType& JavaLangObject() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Object;");
+  const RegType& JavaLangObject(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return From(precise ? RegType::kRegTypeReference
+                        : RegType::kRegTypePreciseReference,
+                NULL, "Ljava/lang/Object;", precise);
   }
   const RegType& JavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return From(RegType::kRegTypeReference, NULL, "Ljava/lang/String;");
+    // String is final and therefore always precise.
+    return From(RegType::kRegTypePreciseReference, NULL, "Ljava/lang/String;", true);
   }
-  const RegType& JavaLangThrowable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Throwable;");
+  const RegType& JavaLangThrowable(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return From(precise ? RegType::kRegTypeReference
+                        : RegType::kRegTypePreciseReference,
+                NULL, "Ljava/lang/Throwable;", precise);
   }
 
   const RegType& Undefined() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -118,6 +125,8 @@
   const RegType& GetComponentType(const RegType& array, ClassLoader* loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
  private:
   // The allocated entries
   std::vector<RegType*> entries_;