Add detail message to null pointer exceptions.

Remove redundant location of NPE in detail message of throw NPE for
method access as the information is available via the stack.

Change-Id: Iee3400c21a0348f6c4e7530b990bc546a80a513c
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index f5edebf..2ecb02e 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -108,10 +108,81 @@
 }
 
 // Called by generated call to throw a NPE exception
-extern "C" void artThrowNullPointerExceptionFromCode(Thread* thread, Method** sp) {
-  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
-  thread->ThrowNewException("Ljava/lang/NullPointerException;", NULL);
-  thread->DeliverException();
+extern "C" void artThrowNullPointerExceptionFromCode(Thread* self, Method** sp) {
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  Frame fr = self->GetTopOfStack();
+  uintptr_t throw_native_pc = fr.GetReturnPC();
+  fr.Next();
+  Method* throw_method = fr.GetMethod();
+  uint32_t dex_pc = throw_method->ToDexPC(throw_native_pc - 2);
+  const DexFile::CodeItem* code = MethodHelper(throw_method).GetCodeItem();
+  CHECK_LT(dex_pc, code->insns_size_in_code_units_);
+  const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
+  DecodedInstruction dec_insn(instr);
+  switch (instr->Opcode()) {
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_DIRECT_RANGE:
+      ThrowNullPointerExceptionForMethodAccess(self, throw_method, dec_insn.vB, kDirect);
+      break;
+    case Instruction::INVOKE_VIRTUAL:
+    case Instruction::INVOKE_VIRTUAL_RANGE:
+      ThrowNullPointerExceptionForMethodAccess(self, throw_method, dec_insn.vB, kVirtual);
+      break;
+    case Instruction::IGET:
+    case Instruction::IGET_WIDE:
+    case Instruction::IGET_OBJECT:
+    case Instruction::IGET_BOOLEAN:
+    case Instruction::IGET_BYTE:
+    case Instruction::IGET_CHAR:
+    case Instruction::IGET_SHORT: {
+      Field* field =
+          Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false);
+      ThrowNullPointerExceptionForFieldAccess(self, field, true /* read */);
+      break;
+    }
+    case Instruction::IPUT:
+    case Instruction::IPUT_WIDE:
+    case Instruction::IPUT_OBJECT:
+    case Instruction::IPUT_BOOLEAN:
+    case Instruction::IPUT_BYTE:
+    case Instruction::IPUT_CHAR:
+    case Instruction::IPUT_SHORT: {
+      Field* field =
+          Runtime::Current()->GetClassLinker()->ResolveField(dec_insn.vC, throw_method, false);
+      ThrowNullPointerExceptionForFieldAccess(self, field, false /* write */);
+      break;
+    }
+    case Instruction::AGET:
+    case Instruction::AGET_WIDE:
+    case Instruction::AGET_OBJECT:
+    case Instruction::AGET_BOOLEAN:
+    case Instruction::AGET_BYTE:
+    case Instruction::AGET_CHAR:
+    case Instruction::AGET_SHORT:
+      self->ThrowNewException("Ljava/lang/NullPointerException;",
+                              "Attempt to read from null array");
+      break;
+    case Instruction::APUT:
+    case Instruction::APUT_WIDE:
+    case Instruction::APUT_OBJECT:
+    case Instruction::APUT_BOOLEAN:
+    case Instruction::APUT_BYTE:
+    case Instruction::APUT_CHAR:
+    case Instruction::APUT_SHORT:
+      self->ThrowNewException("Ljava/lang/NullPointerException;",
+                              "Attempt to write to null array");
+      break;
+    default: {
+      const DexFile& dex_file = Runtime::Current()->GetClassLinker()
+          ->FindDexFile(throw_method->GetDeclaringClass()->GetDexCache());
+      std::string message("Null pointer exception during instruction '");
+      message += instr->DumpString(&dex_file);
+      message += "'";
+      self->ThrowNewException("Ljava/lang/NullPointerException;", message.c_str());
+      break;
+    }
+  }
+  self->DeliverException();
 }
 
 // Called by generated call to throw an arithmetic divide by zero exception
diff --git a/src/runtime_support_common.h b/src/runtime_support_common.h
index 8ef9d93..c5121a9 100644
--- a/src/runtime_support_common.h
+++ b/src/runtime_support_common.h
@@ -104,10 +104,9 @@
   std::ostringstream type_stream;
   type_stream << type;
   self->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
-                           "Attempt to invoke %s method '%s' from '%s' on a null object reference",
+                           "Attempt to invoke %s method '%s' on a null object reference",
                            type_stream.str().c_str(),
-                           PrettyMethod(method_idx, dex_file, true).c_str(),
-                           PrettyMethod(caller).c_str());
+                           PrettyMethod(method_idx, dex_file, true).c_str());
 }
 
 // Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
diff --git a/test/034-call-null/expected.txt b/test/034-call-null/expected.txt
index 582b35e..7ed2215 100644
--- a/test/034-call-null/expected.txt
+++ b/test/034-call-null/expected.txt
@@ -1,2 +1,2 @@
-java.lang.NullPointerException
+java.lang.NullPointerException: Attempt to invoke direct method 'Main.doStuff()V' on a null object reference
 	at Main.main(Main.java:26)
diff --git a/test/038-inner-null/expected.txt b/test/038-inner-null/expected.txt
index d3aab89..37f984c 100644
--- a/test/038-inner-null/expected.txt
+++ b/test/038-inner-null/expected.txt
@@ -1,4 +1,4 @@
 new Special()
-java.lang.NullPointerException
+java.lang.NullPointerException: Attempt to invoke virtual method 'Main$Blort.repaint()V' on a null object reference
 	at Main$Special.callInner(Main.java:31)
 	at Main.main(Main.java:20)