Debug tidying some verification fixes.

Weaken verification of signature types to allow unresolved types as
Object. Fix for interface and field store types.

Change-Id: I2dd5debc96fcaa8e0db7de5231b6926729c201fa
diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc
index eb8e96e..6a3f538 100644
--- a/src/dex_instruction.cc
+++ b/src/dex_instruction.cc
@@ -254,22 +254,25 @@
   return reinterpret_cast<const Instruction*>(ptr + current_size_in_bytes);
 }
 
-void Instruction::DumpHex(std::ostream& os, size_t code_units) const {
+std::string Instruction::DumpHex(size_t code_units) const {
   size_t inst_length = SizeInCodeUnits();
   if (inst_length > code_units) {
     inst_length = code_units;
   }
+  std::ostringstream os;
   const uint16_t* insn = reinterpret_cast<const uint16_t*>(this);
   for (size_t i = 0; i < inst_length; i++) {
-    os << "0x" << StringPrintf("0x%04X", insn[i]) << " ";
+    os << StringPrintf("0x%04x", insn[i]) << " ";
   }
   for (size_t i = inst_length; i < code_units; i++) {
     os << "       ";
   }
+  return os.str();
 }
 
-void Instruction::Dump(std::ostream& os, const DexFile* file) const {
+std::string Instruction::DumpString(const DexFile* file) const {
   DecodedInstruction insn(this);
+  std::ostringstream os;
   const char* opcode = kInstructionNames[insn.opcode_];
   switch (Format()) {
     case k10x: os << opcode; break;
@@ -318,12 +321,9 @@
     }
     case k3rc: os << opcode << " {v" << insn.vC_ << " .. v" << (insn.vC_+ insn.vA_ - 1) << "}, method@" << insn.vB_; break;
     case k51l: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_; break;
+    default: os << " unknown format (" << DumpHex(5) << ")"; break;
   }
-}
-
-std::ostream& operator<<(std::ostream& os, const Instruction& rhs) {
-  rhs.Dump(os, NULL);
-  return os;
+  return os.str();
 }
 
 }  // namespace art
diff --git a/src/dex_instruction.h b/src/dex_instruction.h
index 36ea295..5cfd7ef 100644
--- a/src/dex_instruction.h
+++ b/src/dex_instruction.h
@@ -189,11 +189,11 @@
              kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgRange | kVerifyError));
   }
 
-  // Dump code_units worth of this instruction, padding to code_units for shorter instructions
-  void DumpHex(std::ostream& os, size_t code_units) const;
-
   // Dump decoded version of instruction
-  void Dump(std::ostream& os, const DexFile*) const;
+  std::string DumpString(const DexFile*) const;
+
+  // Dump code_units worth of this instruction, padding to code_units for shorter instructions
+  std::string DumpHex(size_t code_units) const;
 
  private:
   static const char* const kInstructionNames[];
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 878069b..da7f63f 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -17,23 +17,27 @@
 namespace art {
 namespace verifier {
 
+static const bool gDebugVerify = false;
+
 std::ostream& operator<<(std::ostream& os, const VerifyError& rhs) {
   return os << (int)rhs;
 }
 
 static const char type_chars[RegType::kRegTypeMAX + 1 /* for '\0' */ ] = "UX01ZyYhHcibBsSCIFNnJjDdL";
 
-void RegType::Dump(std::ostream& os) const {
+std::string RegType::Dump() const {
   DCHECK(type_ < kRegTypeMAX);
+  std::ostringstream os;
   os << type_chars[type_];
   if (type_ == kRegTypeReference) {
     if (IsUninitializedReference()) {
-      os << "[uninitialized-" << PrettyClass(GetClass())
+      os << "[uninitialized-" << PrettyDescriptor(GetClass()->GetDescriptor())
           << ", allocated@" << allocation_pc_ <<"]";
     } else {
       os << "[" << PrettyDescriptor(GetClass()->GetDescriptor()) << "]";
     }
   }
+  return os.str();
 }
 
 const RegType& RegType::HighHalf(RegTypeCache* cache) const {
@@ -146,7 +150,10 @@
           return check_type;
         } else if (IsUninitializedReference()) {
           return reg_types->Conflict();  // Nonsensical to Join two uninitialized classes
-        } else if (IsReference() && check_type.GetClass()->IsAssignableFrom(GetClass())) {
+        } else if (IsReference() && (check_type.GetClass()->IsAssignableFrom(GetClass()) ||
+            (GetClass()->IsObjectClass() && check_type.GetClass()->IsInterface()))) {
+          // Either we're assignable or this is trying to assign Object to an Interface, which
+          // is allowed (see comment in ClassJoin)
           return check_type;
         } else {
           return reg_types->Conflict();
@@ -319,7 +326,7 @@
 }
 
 std::ostream& operator<<(std::ostream& os, const RegType& rhs) {
-  rhs.Dump(os);
+  os << rhs.Dump();
   return os;
 }
 
@@ -353,9 +360,14 @@
       }
     }
     Class* klass = Runtime::Current()->GetClassLinker()->FindClass(descriptor, loader);
-    RegType* entry = new RegType(type, klass, RegType::kInitArgAddr, entries_.size());
-    entries_.push_back(entry);
-    return *entry;
+    if (klass != NULL) {
+      RegType* entry = new RegType(type, klass, RegType::kInitArgAddr, entries_.size());
+      entries_.push_back(entry);
+      return *entry;
+    } else {
+      DCHECK(Thread::Current()->IsExceptionPending());
+      return FromType(RegType::kRegTypeUnknown);
+    }
   }
 }
 
@@ -778,7 +790,7 @@
 }
 
 std::ostream& operator<<(std::ostream& os, const RegisterLine& rhs) {
-  rhs.Dump(os);
+  os << rhs.Dump();
   return os;
 }
 
@@ -864,7 +876,9 @@
   } else {
     LOG(INFO) << "Verification error in " << PrettyMethod(method) << " "
                << verifier.fail_messages_.str() << std::endl << verifier.info_messages_.str();
-    verifier.Dump(std::cout);
+    if (gDebugVerify) {
+      verifier.Dump(std::cout);
+    }
     DCHECK_EQ(verifier.failure_, VERIFY_ERROR_GENERIC);
   }
   return success;
@@ -993,7 +1007,8 @@
   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)) {
-      Fail(VERIFY_ERROR_GENERIC) << "rejecting opcode " << inst->Name() << " at " << dex_pc;
+      DCHECK_NE(failure_, VERIFY_ERROR_NONE);
+      fail_messages_ << "Rejecting opcode " << inst->DumpString(dex_file_) << " at " << dex_pc;
       return false;
     }
     /* Flag instructions that are garbage collection points */
@@ -1399,7 +1414,8 @@
 
   /* Initialize register types of method arguments. */
   if (!SetTypesFromSignature()) {
-    Fail(VERIFY_ERROR_GENERIC) << "bad signature in " << PrettyMethod(method_);
+    DCHECK_NE(failure_, VERIFY_ERROR_NONE);
+    fail_messages_ << "Bad signature in " << PrettyMethod(method_);
     return false;
   }
   /* Perform code flow verification. */
@@ -1428,19 +1444,11 @@
   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()) {
-    std::ios::fmtflags saved_flags(os.flags());
-    os << std::hex << std::showbase << std::setfill('0') << std::setw(4) << dex_pc << ": ";
-    os.flags(saved_flags);
-    insn_flags_[dex_pc].Dump(os);
-    os << " ";
-    inst->DumpHex(os, 5);
-    os << " ";
-    inst->Dump(os, dex_file_);
-    os << std::endl;
+    os << StringPrintf("0x%04x", dex_pc) << ": " << insn_flags_[dex_pc].Dump()
+        << " " << inst->DumpHex(5) << " " << inst->DumpString(dex_file_) << std::endl;
     RegisterLine* reg_line = reg_table_.GetLine(dex_pc);
     if (reg_line != NULL) {
-      reg_line->Dump(os);
-      os << std::endl;
+      os << reg_line->Dump() << std::endl;
     }
     inst = inst->Next();
   }
@@ -1507,14 +1515,15 @@
         {
           const RegType& reg_type =
               reg_types_.FromDescriptor(method_->GetDeclaringClass()->GetClassLoader(), descriptor);
-          if (reg_type.IsUnknown()) {
+          if (!reg_type.IsUnknown()) {
+            reg_line->SetRegisterType(arg_start + cur_arg, reg_type);
+          } else {
             DCHECK(Thread::Current()->IsExceptionPending());
             Thread::Current()->ClearException();
-            Fail(VERIFY_ERROR_GENERIC)
-                << "Unable to resolve descriptor in signature " << descriptor;
-            return false;
+            LOG(WARNING) << "Unable to resolve descriptor in signature " << descriptor
+                << " using Object";
+            reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.JavaLangObject());
           }
-          reg_line->SetRegisterType(arg_start + cur_arg, reg_type);
         }
         break;
       case 'Z':
@@ -1726,9 +1735,9 @@
 
   int32_t branch_target = 0;
   bool just_set_result = false;
-  if (false) {
+  if (gDebugVerify) {
     // Generate processing back trace to debug verifier
-    LogVerifyInfo() << "Processing " << *inst << std::endl << *work_line_.get();
+    LogVerifyInfo() << "Processing " << inst->DumpString(dex_file_) << std::endl << *work_line_.get();
   }
 
   /*
@@ -2675,7 +2684,7 @@
     case Instruction::UNUSED_7A:
     case Instruction::UNUSED_EC:
     case Instruction::UNUSED_FF:
-      Fail(VERIFY_ERROR_GENERIC) << "Unexpected opcode " << *inst;
+      Fail(VERIFY_ERROR_GENERIC) << "Unexpected opcode " << inst->DumpString(dex_file_);
       break;
 
     /*
@@ -2687,11 +2696,11 @@
   if (failure_ != VERIFY_ERROR_NONE) {
     if (failure_ == VERIFY_ERROR_GENERIC) {
       /* immediate failure, reject class */
-      fail_messages_ << std::endl << "Rejecting opcode " << *inst;
+      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;
+      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_;
@@ -3103,9 +3112,12 @@
     if (reg_type.IsUnknown()) {
       DCHECK(Thread::Current()->IsExceptionPending());
       Thread::Current()->ClearException();
-      Fail(VERIFY_ERROR_GENERIC) << "Rejecting invocation of " << PrettyMethod(res_method)
-         << " bad descriptor '" << descriptor << "' in signature " << sig;
-      return NULL;
+      LOG(WARNING) << "Unable to resolve descriptor in signature " << descriptor
+          << " using Object";
+      uint32_t get_reg = is_range ? dec_insn.vC_ + actual_args : dec_insn.arg_[actual_args];
+      if (!work_line_->VerifyRegisterType(get_reg, reg_types_.JavaLangObject())) {
+        return NULL;
+      }
     } else {
       uint32_t get_reg = is_range ? dec_insn.vC_ + actual_args : dec_insn.arg_[actual_args];
       if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
@@ -3287,14 +3299,30 @@
       return;
     }
     Class* field_class = field->GetType();
+    const RegType& field_type = reg_types_.FromClass(field_class);
     Class* insn_class = insn_type.GetClass();
     if (is_primitive) {
-      if (field_class == insn_class ||
-          (field_class->IsPrimitiveFloat() && insn_class->IsPrimitiveInt()) ||
-          (field_class->IsPrimitiveDouble() && insn_class->IsPrimitiveLong())) {
-        // expected that read is of the correct primitive type or that int reads are reading
-        // floats or long reads are reading doubles
+      // 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 sput-float, so expect sput-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 sput-double, so expect sput-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
@@ -3303,6 +3331,13 @@
                                    << " but found type " << PrettyClass(field_class) << " in sput";
         return;
       }
+      if (!value_compatible) {
+        Fail(VERIFY_ERROR_GENERIC) << "unexpected value in v" << dec_insn.vA_
+                                   << " of type " << value_type
+                                   << " but expected " << field_type
+                                   << " for store to " << PrettyField(field) << " in sput";
+        return;
+      }
     } else {
       if (!insn_class->IsAssignableFrom(field_class)) {
         Fail(VERIFY_ERROR_GENERIC) << "expected field " << PrettyField(field)
@@ -3311,8 +3346,8 @@
                                   << " in sput-object";
         return;
       }
+      work_line_->VerifyRegisterType(dec_insn.vA_, field_type);
     }
-    work_line_->VerifyRegisterType(dec_insn.vA_, reg_types_.FromClass(field_class));
   }
 }
 
@@ -3364,7 +3399,6 @@
   const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB_);
   Field* field = GetInstanceField(object_type, dec_insn.vC_);
   if (field != NULL) {
-    DCHECK(field->GetDeclaringClass()->IsResolved());
     Class* field_class = field->GetType();
     Class* insn_class = insn_type.GetClass();
     if (is_primitive) {
@@ -3399,21 +3433,36 @@
   const RegType& object_type = work_line_->GetRegisterType(dec_insn.vB_);
   Field* field = GetInstanceField(object_type, dec_insn.vC_);
   if (field != NULL) {
-    DCHECK(field->GetDeclaringClass()->IsResolved());
     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;
     }
     Class* field_class = field->GetType();
+    const RegType& field_type = reg_types_.FromClass(field_class);
     Class* insn_class = insn_type.GetClass();
     if (is_primitive) {
-      if (field_class == insn_class ||
-          (field_class->IsPrimitiveFloat() && insn_class->IsPrimitiveInt()) ||
-          (field_class->IsPrimitiveDouble() && insn_class->IsPrimitiveLong())) {
-        // expected that read is of the correct primitive type or that int reads are reading
-        // floats or long reads are reading doubles
+      // 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 iput-float, so expect iput-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 iput-double, so expect iput-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
@@ -3422,16 +3471,23 @@
                                    << " but found type " << PrettyClass(field_class) << " in iput";
         return;
       }
+      if (!value_compatible) {
+        Fail(VERIFY_ERROR_GENERIC) << "unexpected value in v" << dec_insn.vA_
+                                   << " of type " << value_type
+                                   << " but expected " << field_type
+                                   << " for store to " << PrettyField(field) << " in iput";
+        return;
+      }
     } else {
       if (!insn_class->IsAssignableFrom(field_class)) {
         Fail(VERIFY_ERROR_GENERIC) << "expected field " << PrettyField(field)
-                                   << " to be compatible with type " << PrettyClass(insn_class)
-                                   << " but found type " << PrettyClass(field_class)
-                                   << " in iput-object";
+                                  << " to be compatible with type " << insn_type
+                                  << " but found type " << PrettyClass(field_class)
+                                  << " in iput-object";
         return;
       }
+      work_line_->VerifyRegisterType(dec_insn.vA_, field_type);
     }
-    work_line_->VerifyRegisterType(dec_insn.vA_, reg_types_.FromClass(field_class));
   }
 }
 
@@ -3525,7 +3581,7 @@
       ref_type = VERIFY_ERROR_REF_METHOD;
       break;
     default:
-      LOG(FATAL) << "Error: verifier asked to replace instruction " << *inst;
+      LOG(FATAL) << "Error: verifier asked to replace instruction " << inst->DumpString(dex_file_);
       return;
   }
   uint16_t* insns = const_cast<uint16_t*>(code_item_->insns_);
@@ -3565,7 +3621,7 @@
     if (failure_ != VERIFY_ERROR_NONE) {
       return false;
     }
-    if (changed) {
+    if (gDebugVerify && changed) {
       LogVerifyInfo() << "Merging at [" << (void*)work_insn_idx_ << "] to [" <<(void*)next_insn << "]: " << std::endl
                       << *copy.get() << "  MERGE" << std::endl
                       << *merge_line << "  ==" << std::endl
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index 1f28204..3a614d5 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -117,7 +117,7 @@
     return type_;
   }
 
-  void Dump(std::ostream& os) const;
+  std::string Dump() const;
 
   Class* GetClass() const {
     DCHECK(klass_ != NULL);
@@ -254,6 +254,7 @@
   RegType(Type type, Class* klass, uint32_t allocation_pc, uint16_t cache_id) :
     type_(type), klass_(klass), allocation_pc_(allocation_pc), cache_id_(cache_id) {
     DCHECK(type >= kRegTypeReference || allocation_pc_ == kInitArgAddr);
+    if (type >= kRegTypeReference) DCHECK(klass != NULL);
   }
 
   const Type type_;  // The current type of the register
@@ -411,7 +412,7 @@
     return IsVisited() || IsChanged();
   }
 
-  void Dump(std::ostream& os) {
+  std::string Dump() {
     char encoding[6];
     if (!IsOpcode()) {
       strncpy(encoding, "XXXXX", sizeof(encoding));
@@ -423,7 +424,7 @@
       if (IsVisited())      encoding[kInsnFlagVisited] = 'V';
       if (IsChanged())      encoding[kInsnFlagChanged] = 'C';
     }
-    os << encoding;
+    return std::string(encoding);
   }
  private:
   enum InsnFlag {
@@ -569,10 +570,12 @@
     reg_to_lock_depths_ = src->reg_to_lock_depths_;
   }
 
-  void Dump(std::ostream& os) const {
+  std::string Dump() const {
+    std::string result;
     for (size_t i = 0; i < num_regs_; i++) {
-      GetRegisterType(i).Dump(os);
+      result += GetRegisterType(i).Dump();
     }
+    return result;
   }
 
   void FillWithGarbage() {