Fix exception throwing to support no detail message.

(The empty string as a detail message is distinct from a NULL detail message,
and is treated differently by Throwable.printStackTrace.)

Change-Id: I8c65deac9f18c5782dcf6e72e4c37e6dd4174fe9
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 1963985..eade908 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -61,7 +61,7 @@
   if (dex_cache) {
     msg << " (defined in " << dex_cache->GetLocation()->ToModifiedUtf8() << ")";
   }
-  Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", "%s", msg.str().c_str());
+  Thread::Current()->ThrowNewException("Ljava/lang/NoSuchMethodError;", msg.str().c_str());
 }
 
 void ThrowEarlierClassFailure(Class* c) {
@@ -76,7 +76,7 @@
   if (c->GetVerifyErrorClass() != NULL) {
     // TODO: change the verifier to store an _instance_, with a useful detail message?
     std::string error_descriptor(c->GetVerifyErrorClass()->GetDescriptor()->ToModifiedUtf8());
-    Thread::Current()->ThrowNewException(error_descriptor.c_str(), "%s",
+    Thread::Current()->ThrowNewException(error_descriptor.c_str(),
         PrettyDescriptor(c->GetDescriptor()).c_str());
   } else {
     ThrowNoClassDefFoundError("%s", PrettyDescriptor(c->GetDescriptor()).c_str());
@@ -816,7 +816,7 @@
     ObjectLock lock(klass);
     // Check for circular dependencies between classes.
     if (!klass->IsResolved() && klass->GetClinitThreadId() == self->GetTid()) {
-      self->ThrowNewException("Ljava/lang/ClassCircularityError;", "%s",
+      self->ThrowNewException("Ljava/lang/ClassCircularityError;",
           PrettyDescriptor(klass->GetDescriptor()).c_str());
       return NULL;
     }
@@ -1416,7 +1416,7 @@
     // "interruptShouldThrow" was set), bail out.
     if (self->IsExceptionPending()) {
       // TODO: set cause of ExceptionInInitializerError to self->GetException()
-      self->ThrowNewException("Ljava/lang/ExceptionInInitializerError;",
+      self->ThrowNewExceptionF("Ljava/lang/ExceptionInInitializerError;",
           "Exception %s thrown while initializing class %s",
           PrettyTypeOf(self->GetException()).c_str(),
           PrettyDescriptor(klass->GetDescriptor()).c_str());
@@ -1430,7 +1430,7 @@
     if (klass->IsErroneous()) {
       // The caller wants an exception, but it was thrown in a
       // different thread.  Synthesize one here.
-      self->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
+      self->ThrowNewExceptionF("Ljava/lang/NoClassDefFoundError;",
           "<clinit> failed for class %s; see exception in other thread",
           PrettyDescriptor(klass->GetDescriptor()).c_str());
       return false;
@@ -1722,7 +1722,7 @@
     // Verify
     if (!klass->CanAccess(interface)) {
       // TODO: the RI seemed to ignore this in my testing.
-      Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;",
+      Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
           "Interface %s implemented by class %s is inaccessible",
           PrettyDescriptor(interface->GetDescriptor()).c_str(),
           PrettyDescriptor(klass->GetDescriptor()).c_str());
@@ -1739,7 +1739,7 @@
   Class* super = klass->GetSuperClass();
   if (klass->GetDescriptor()->Equals("Ljava/lang/Object;")) {
     if (super != NULL) {
-      Thread::Current()->ThrowNewException("Ljava/lang/ClassFormatError;",
+      Thread::Current()->ThrowNewExceptionF("Ljava/lang/ClassFormatError;",
           "java.lang.Object must not have a superclass");
       return false;
     }
@@ -1753,7 +1753,7 @@
   }
   // Verify
   if (super->IsFinal() || super->IsInterface()) {
-    Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
         "Superclass %s of %s is %s",
         PrettyDescriptor(super->GetDescriptor()).c_str(),
         PrettyDescriptor(klass->GetDescriptor()).c_str(),
@@ -1761,7 +1761,7 @@
     return false;
   }
   if (!klass->CanAccess(super)) {
-    Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
         "Superclass %s is inaccessible by %s",
         PrettyDescriptor(super->GetDescriptor()).c_str(),
         PrettyDescriptor(klass->GetDescriptor()).c_str());
@@ -1894,7 +1894,7 @@
     Class* interface = klass->GetInterface(i);
     DCHECK(interface != NULL);
     if (!interface->IsInterface()) {
-      Thread::Current()->ThrowNewException("Ljava/lang/IncompatibleClassChangeError;",
+      Thread::Current()->ThrowNewExceptionF("Ljava/lang/IncompatibleClassChangeError;",
           "Class %s implements non-interface class %s",
           PrettyDescriptor(klass->GetDescriptor()).c_str(),
           PrettyDescriptor(interface->GetDescriptor()).c_str());
@@ -1936,7 +1936,7 @@
         Method* vtable_method = vtable->Get(k);
         if (interface_method->HasSameNameAndDescriptor(vtable_method)) {
           if (!vtable_method->IsPublic()) {
-            Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;",
+            Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
                 "Implementation not public: %s", PrettyMethod(vtable_method).c_str());
             return false;
           }
@@ -2250,7 +2250,7 @@
       Class* check = resolved->IsArrayClass() ? resolved->GetComponentType() : resolved;
       if (dex_cache != check->GetDexCache()) {
         if (check->GetClassLoader() != NULL) {
-          Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessError;",
+          Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
               "Class with type index %d resolved by unexpected .dex", type_idx);
           resolved = NULL;
         }
diff --git a/src/dalvik_system_VMRuntime.cc b/src/dalvik_system_VMRuntime.cc
index 1bc1e39..8d4aa8b 100644
--- a/src/dalvik_system_VMRuntime.cc
+++ b/src/dalvik_system_VMRuntime.cc
@@ -56,7 +56,7 @@
     return NULL;
   }
   if (length < 0) {
-    Thread::Current()->ThrowNewException("Ljava/lang/NegativeArraySizeException;", "%d", length);
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
     return NULL;
   }
 
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index de5315a..a404523 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -38,7 +38,7 @@
   // is especially handy for array types, since we want to avoid
   // auto-generating bogus array classes.
   if (!IsValidClassName(name.c_str(), true, true)) {
-    Thread::Current()->ThrowNewException("Ljava/lang/ClassNotFoundException;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
         "Invalid name: %s", name.c_str());
     return NULL;
   }
@@ -53,8 +53,7 @@
     // TODO: chain exceptions?
     DCHECK(env->ExceptionCheck());
     env->ExceptionClear();
-    Thread::Current()->ThrowNewException("Ljava/lang/ClassNotFoundException;",
-                                         "%s", name.c_str());
+    Thread::Current()->ThrowNewException("Ljava/lang/ClassNotFoundException;", name.c_str());
     return NULL;
   }
   if (initialize) {
@@ -373,7 +372,7 @@
 jobject Class_newInstanceImpl(JNIEnv* env, jobject javaThis) {
   Class* c = Decode<Class*>(env, javaThis);
   if (c->IsPrimitive() || c->IsInterface() || c->IsArrayClass() || c->IsAbstract()) {
-    Thread::Current()->ThrowNewException("Ljava/lang/InstantiationException;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
         "Class %s can not be instantiated", PrettyDescriptor(c->GetDescriptor()).c_str());
     return NULL;
   }
@@ -384,7 +383,7 @@
 
   Method* init = c->FindDirectMethod("<init>", "()V");
   if (init == NULL) {
-    Thread::Current()->ThrowNewException("Ljava/lang/InstantiationException;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
         "Class %s has no default <init>()V constructor", PrettyDescriptor(c->GetDescriptor()).c_str());
     return NULL;
   }
@@ -405,17 +404,17 @@
   Class* caller_class = caller_caller->GetDeclaringClass();
 
   if (!caller_class->CanAccess(c)) {
-    Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessException;",
-                                         "Class %s is not accessible from class %s",
-                                         PrettyDescriptor(c->GetDescriptor()).c_str(),
-                                         PrettyDescriptor(caller_class->GetDescriptor()).c_str());
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessException;",
+        "Class %s is not accessible from class %s",
+        PrettyDescriptor(c->GetDescriptor()).c_str(),
+        PrettyDescriptor(caller_class->GetDescriptor()).c_str());
     return NULL;
   }
   if (!CheckMemberAccess(caller_class, init->GetDeclaringClass(), init->GetAccessFlags())) {
-    Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessException;",
-                                         "%s is not accessible from class %s",
-                                         PrettyMethod(init).c_str(),
-                                         PrettyDescriptor(caller_class->GetDescriptor()).c_str());
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessException;",
+        "%s is not accessible from class %s",
+        PrettyMethod(init).c_str(),
+        PrettyDescriptor(caller_class->GetDescriptor()).c_str());
     return NULL;
   }
 
diff --git a/src/java_lang_System.cc b/src/java_lang_System.cc
index 42bae22..4a6e8db 100644
--- a/src/java_lang_System.cc
+++ b/src/java_lang_System.cc
@@ -106,7 +106,8 @@
 
 void ThrowArrayStoreException_NotAnArray(const char* identifier, Object* array) {
   std::string actualType(PrettyTypeOf(array));
-  Thread::Current()->ThrowNewException("Ljava/lang/ArrayStoreException;", "%s is not an array: %s", identifier, actualType.c_str());
+  Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
+      "%s is not an array: %s", identifier, actualType.c_str());
 }
 
 void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
@@ -140,7 +141,7 @@
 
   // Bounds checking.
   if (srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length) {
-    self->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+    self->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
         "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
         srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos, length);
     return;
@@ -155,7 +156,7 @@
     if (srcComponentType->IsPrimitive() != dstComponentType->IsPrimitive() || srcComponentType != dstComponentType) {
       std::string srcType(PrettyTypeOf(srcArray));
       std::string dstType(PrettyTypeOf(dstArray));
-      self->ThrowNewException("Ljava/lang/ArrayStoreException;",
+      self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
           "Incompatible types: src=%s, dst=%s", srcType.c_str(), dstType.c_str());
       return;
     }
@@ -231,7 +232,7 @@
   if (copyCount != length) {
     std::string actualSrcType(PrettyTypeOf(srcObj[copyCount]));
     std::string dstType(PrettyTypeOf(dstArray));
-    self->ThrowNewException("Ljava/lang/ArrayStoreException;",
+    self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
         "source[%d] of type %s cannot be stored in destination array of type %s",
         srcPos + copyCount, actualSrcType.c_str(), dstType.c_str());
     return;
diff --git a/src/java_lang_reflect_Constructor.cc b/src/java_lang_reflect_Constructor.cc
index 6cc8ea7..4d42450 100644
--- a/src/java_lang_reflect_Constructor.cc
+++ b/src/java_lang_reflect_Constructor.cc
@@ -35,7 +35,7 @@
 jobject Constructor_constructNative(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs, jclass javaDeclaringClass, jobjectArray javaParams, jint, jboolean) {
   Class* c = Decode<Class*>(env, javaDeclaringClass);
   if (c->IsAbstract()) {
-    Thread::Current()->ThrowNewException("Ljava/lang/InstantiationException;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
         "Can't instantiate abstract class %s", PrettyDescriptor(c->GetDescriptor()).c_str());
     return NULL;
   }
diff --git a/src/java_lang_reflect_Field.cc b/src/java_lang_reflect_Field.cc
index 185fabc..8e09adb 100644
--- a/src/java_lang_reflect_Field.cc
+++ b/src/java_lang_reflect_Field.cc
@@ -66,7 +66,7 @@
     // Never okay.
     break;
   }
-  Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+  Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
       "Not a primitive field: %s", PrettyField(f).c_str());
   return false;
 }
@@ -165,7 +165,7 @@
     // Else fall through to report an error.
   case Class::kPrimVoid:
     // Never okay.
-    Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
         "Not a primitive field: %s", PrettyField(f).c_str());
     return;
   }
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 144ec22..bc1de52 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -253,7 +253,7 @@
 
 void ThrowNoSuchMethodError(ScopedJniThreadState& ts, Class* c, const char* name, const char* sig, const char* kind) {
   std::string class_descriptor(c->GetDescriptor()->ToModifiedUtf8());
-  ts.Self()->ThrowNewException("Ljava/lang/NoSuchMethodError;",
+  ts.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
       "no %s method \"%s.%s%s\"", kind, class_descriptor.c_str(), name, sig);
 }
 
@@ -306,7 +306,7 @@
     DCHECK(ts.Self()->IsExceptionPending());
     ts.Self()->ClearException();
     std::string class_descriptor(c->GetDescriptor()->ToModifiedUtf8());
-    ts.Self()->ThrowNewException("Ljava/lang/NoSuchFieldError;",
+    ts.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
         "no type \"%s\" found and so no field \"%s\" could be found in class "
         "\"%s\" or its superclasses", sig, name, class_descriptor.c_str());
     return NULL;
@@ -318,7 +318,7 @@
   }
   if (field == NULL) {
     std::string class_descriptor(c->GetDescriptor()->ToModifiedUtf8());
-    ts.Self()->ThrowNewException("Ljava/lang/NoSuchFieldError;",
+    ts.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
         "no \"%s\" field \"%s\" in class \"%s\" or its superclasses", sig,
         name, class_descriptor.c_str());
     return NULL;
@@ -366,12 +366,12 @@
 
 void ThrowAIOOBE(ScopedJniThreadState& ts, Array* array, jsize start, jsize length, const char* identifier) {
   std::string type(PrettyTypeOf(array));
-  ts.Self()->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+  ts.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
       "%s offset=%d length=%d %s.length=%d",
       type.c_str(), start, length, identifier, array->GetLength());
 }
 void ThrowSIOOBE(ScopedJniThreadState& ts, jsize start, jsize length, jsize array_length) {
-  ts.Self()->ThrowNewException("Ljava/lang/StringIndexOutOfBoundsException;",
+  ts.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
       "offset=%d length=%d string.length()=%d", start, length, array_length);
 }
 
@@ -697,7 +697,7 @@
       return JNI_ERR;
     }
     ScopedLocalRef<jstring> s(env, env->NewStringUTF(msg));
-    if (s.get() == NULL) {
+    if (msg != NULL && s.get() == NULL) {
       return JNI_ERR;
     }
 
@@ -708,8 +708,6 @@
       return JNI_ERR;
     }
 
-    LOG(INFO) << "Throwing " << PrettyTypeOf(Decode<Throwable*>(ts, exception.get()))
-              << ": " << msg;
     ts.Self()->SetException(Decode<Throwable*>(ts, exception.get()));
 
     return JNI_OK;
@@ -2824,7 +2822,7 @@
   }
   // throwing can cause libraries_lock to be reacquired
   if (native_method == NULL) {
-    Thread::Current()->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", "%s", detail.c_str());
+    Thread::Current()->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
   }
   return native_method;
 }
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 74fdb0a..95e22ba 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -1360,9 +1360,17 @@
   jclass exception_class = env_->FindClass("java/lang/RuntimeException");
   ASSERT_TRUE(exception_class != NULL);
 
+  jthrowable thrown_exception;
+
   EXPECT_EQ(JNI_OK, env_->ThrowNew(exception_class, "hello world"));
   EXPECT_TRUE(env_->ExceptionCheck());
-  jthrowable thrown_exception = env_->ExceptionOccurred();
+  thrown_exception = env_->ExceptionOccurred();
+  env_->ExceptionClear();
+  EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class));
+
+  EXPECT_EQ(JNI_OK, env_->ThrowNew(exception_class, NULL));
+  EXPECT_TRUE(env_->ExceptionCheck());
+  thrown_exception = env_->ExceptionOccurred();
   env_->ExceptionClear();
   EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class));
 }
diff --git a/src/monitor.cc b/src/monitor.cc
index 4e71e45..6bd309d 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -367,7 +367,7 @@
 }
 
 void ThrowIllegalMonitorStateException(const char* msg) {
-  Thread::Current()->ThrowNewException("Ljava/lang/IllegalMonitorStateException;", "%s", msg);
+  Thread::Current()->ThrowNewException("Ljava/lang/IllegalMonitorStateException;", msg);
 }
 
 bool Monitor::Unlock(Thread* self) {
@@ -469,7 +469,7 @@
 
   // Enforce the timeout range.
   if (ms < 0 || ns < 0 || ns > 999999) {
-    Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
         "timeout arguments out of range: ms=%lld ns=%d", ms, ns);
     return;
   }
@@ -582,7 +582,7 @@
      */
     self->interrupted_ = false;
     if (interruptShouldThrow) {
-      Thread::Current()->ThrowNewException("Ljava/lang/InterruptedException;", "%s", "");
+      Thread::Current()->ThrowNewException("Ljava/lang/InterruptedException;", NULL);
     }
   }
 }
diff --git a/src/object.cc b/src/object.cc
index f8c9624..4a6e649 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -1225,13 +1225,13 @@
 }
 
 bool Array::ThrowArrayIndexOutOfBoundsException(int32_t index) const {
-  Thread::Current()->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+  Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
       "length=%i; index=%i", length_, index);
   return false;
 }
 
 bool Array::ThrowArrayStoreException(Object* object) const {
-  Thread::Current()->ThrowNewException("Ljava/lang/ArrayStoreException;",
+  Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
       "Can't store an element of type %s into an array of type %s",
       PrettyTypeOf(object).c_str(), PrettyTypeOf(this).c_str());
   return false;
@@ -1296,7 +1296,7 @@
   // bounds check itself.
   if (index < 0 || index >= count_) {
     Thread* self = Thread::Current();
-    self->ThrowNewException("Ljava/lang/StringIndexOutOfBoundsException;",
+    self->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
         "length=%i; index=%i", count_, index);
     return 0;
   }
diff --git a/src/reflection.cc b/src/reflection.cc
index c560053..1294c08 100644
--- a/src/reflection.cc
+++ b/src/reflection.cc
@@ -77,7 +77,7 @@
   ObjectArray<Class>* classes = Decode<ObjectArray<Class>*>(env, javaParams);
   int32_t arg_count = (objects != NULL) ? objects->GetLength() : 0;
   if (arg_count != classes->GetLength()) {
-    self->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+    self->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
         "wrong number of arguments; expected %d, got %d",
         classes->GetLength(), arg_count);
     return NULL;
@@ -219,7 +219,7 @@
   default:
     break;
   }
-  Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+  Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
       "invalid primitive conversion from %s to %s",
       PrettyDescriptor(src_class->GetDescriptor()).c_str(),
       PrettyDescriptor(dst_class->GetDescriptor()).c_str());
@@ -333,7 +333,7 @@
     src_class = class_linker->FindPrimitiveClass('S');
     boxed_value.s = primitive_field->GetShort(o);
   } else {
-    Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
         "%s is not a boxed primitive type", PrettyDescriptor(src_descriptor).c_str());
     return false;
   }
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index e5fbfd2..ca1fdea0 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -83,7 +83,7 @@
   // Place a special frame at the TOS that will save all callee saves
   *sp = Runtime::Current()->GetCalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
-  thread->ThrowNewException("Ljava/lang/NullPointerException;", "unexpected null reference");
+  thread->ThrowNewException("Ljava/lang/NullPointerException;", NULL);
   thread->DeliverException();
 }
 
@@ -101,8 +101,8 @@
   // Place a special frame at the TOS that will save all callee saves
   *sp = Runtime::Current()->GetCalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
-  thread->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
-                            "length=%d; index=%d", limit, index);
+  thread->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", "length=%d; index=%d",
+      limit, index);
   thread->DeliverException();
 }
 
@@ -110,9 +110,8 @@
 extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method** sp) {
   *sp = Runtime::Current()->GetCalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
-  thread->ThrowNewException("Ljava/lang/AbstractMethodError;",
-                            "abstract method \"%s\"",
-                            PrettyMethod(method).c_str());
+  thread->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;", "abstract method \"%s\"",
+      PrettyMethod(method).c_str());
   thread->DeliverException();
 }
 
@@ -122,9 +121,9 @@
   *sp = runtime->GetCalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
   thread->SetStackEndForStackOverflow();  // Allow space on the stack for constructor to execute
-  thread->ThrowNewException("Ljava/lang/StackOverflowError;",
-                            "stack size %zdkb; default stack size: %zdkb",
-                            thread->GetStackSize() / KB, runtime->GetDefaultStackSize() / KB);
+  thread->ThrowNewExceptionF("Ljava/lang/StackOverflowError;",
+      "stack size %zdkb; default stack size: %zdkb",
+      thread->GetStackSize() / KB, runtime->GetDefaultStackSize() / KB);
   thread->ResetDefaultStackEnd();  // Return to default stack size
   thread->DeliverException();
 }
@@ -135,8 +134,8 @@
   *sp = runtime->GetCalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
   LOG(WARNING) << "TODO: verifcation error detail message. src1=" << src1 << " ref=" << ref;
-  thread->ThrowNewException("Ljava/lang/VerifyError;",
-                            "TODO: verifcation error detail message. src1=%d; ref=%d", src1, ref);
+  thread->ThrowNewExceptionF("Ljava/lang/VerifyError;",
+      "TODO: verification error detail message. src1=%d; ref=%d", src1, ref);
   thread->DeliverException();
 }
 
@@ -146,7 +145,7 @@
   *sp = runtime->GetCalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
   LOG(WARNING) << "TODO: internal error detail message. errnum=" << errnum;
-  thread->ThrowNewException("Ljava/lang/InternalError;", "errnum=%d", errnum);
+  thread->ThrowNewExceptionF("Ljava/lang/InternalError;", "errnum=%d", errnum);
   thread->DeliverException();
 }
 
@@ -156,7 +155,7 @@
   *sp = runtime->GetCalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
   LOG(WARNING) << "TODO: runtime exception detail message. errnum=" << errnum;
-  thread->ThrowNewException("Ljava/lang/RuntimeException;", "errnum=%d", errnum);
+  thread->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "errnum=%d", errnum);
   thread->DeliverException();
 }
 
@@ -166,7 +165,7 @@
   *sp = runtime->GetCalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
   LOG(WARNING) << "TODO: no such method exception detail message. method_idx=" << method_idx;
-  thread->ThrowNewException("Ljava/lang/NoSuchMethodError;", "method_idx=%d", method_idx);
+  thread->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;", "method_idx=%d", method_idx);
   thread->DeliverException();
 }
 
@@ -176,7 +175,7 @@
   Runtime* runtime = Runtime::Current();
   *sp = runtime->GetCalleeSaveMethod();
   thread->SetTopOfStack(sp, 0);
-  thread->ThrowNewException("Ljava/lang/NegativeArraySizeException;", "%d", size);
+  thread->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", size);
   thread->DeliverException();
 }
 
@@ -226,7 +225,7 @@
 extern "C" Array* artCheckAndArrayAllocFromCode(uint32_t type_idx, Method* method,
                                                 int32_t component_count) {
   if (component_count < 0) {
-    Thread::Current()->ThrowNewException("Ljava/lang/NegativeArraySizeException;", "%d",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d",
                                          component_count);
     return NULL;  // Failure
   }
@@ -240,11 +239,11 @@
   }
   if (klass->IsPrimitive() && !klass->IsPrimitiveInt()) {
     if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
-      Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;",
+      Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
           "Bad filled array request for type %s",
           PrettyDescriptor(klass->GetDescriptor()).c_str());
     } else {
-      Thread::Current()->ThrowNewException("Ljava/lang/InternalError;",
+      Thread::Current()->ThrowNewExceptionF("Ljava/lang/InternalError;",
           "Found type %s; filled-new-array not implemented for anything but \'int\'",
           PrettyDescriptor(klass->GetDescriptor()).c_str());
     }
@@ -259,7 +258,7 @@
 // it cannot be resolved, throw an error. If it can, use it to create an array.
 extern "C" Array* artArrayAllocFromCode(uint32_t type_idx, Method* method, int32_t component_count) {
   if (component_count < 0) {
-    Thread::Current()->ThrowNewException("Ljava/lang/NegativeArraySizeException;", "%d",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d",
                                          component_count);
     return NULL;  // Failure
   }
@@ -282,7 +281,7 @@
   if (b->IsAssignableFrom(a)) {
     return 0;  // Success
   } else {
-    Thread::Current()->ThrowNewException("Ljava/lang/ClassCastException;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/ClassCastException;",
         "%s cannot be cast to %s",
         PrettyDescriptor(a->GetDescriptor()).c_str(),
         PrettyDescriptor(b->GetDescriptor()).c_str());
@@ -300,7 +299,7 @@
   if (component_type->IsAssignableFrom(element_class)) {
     return 0;  // Success
   } else {
-    Thread::Current()->ThrowNewException("Ljava/lang/ArrayStoreException;",
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
         "Cannot store an object of type %s in to an array of type %s",
         PrettyDescriptor(element_class->GetDescriptor()).c_str(),
         PrettyDescriptor(array_class->GetDescriptor()).c_str());
@@ -343,16 +342,15 @@
 extern "C" int artHandleFillArrayDataFromCode(Array* array, const uint16_t* table) {
   DCHECK_EQ(table[0], 0x0300);
   if (array == NULL) {
-    Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;",
-                                         "null array in fill array");
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
+        "null array in fill array");
     return -1;  // Error
   }
   DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
   uint32_t size = (uint32_t)table[2] | (((uint32_t)table[3]) << 16);
   if (static_cast<int32_t>(size) > array->GetLength()) {
-    Thread::Current()->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
-                                         "failed array fill. length=%d; index=%d",
-                                         array->GetLength(), size);
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
+        "failed array fill. length=%d; index=%d", array->GetLength(), size);
     return -1;  // Error
   }
   uint16_t width = table[1];
@@ -367,8 +365,8 @@
                                                           Method* caller_method) {
   Thread* thread = Thread::Current();
   if (this_object == NULL) {
-    thread->ThrowNewException("Ljava/lang/NullPointerException;",
-                              "null receiver during interface dispatch");
+    thread->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
+        "null receiver during interface dispatch");
     return 0;
   }
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
diff --git a/src/thread.cc b/src/thread.cc
index 0df4929..0dd3766 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1076,7 +1076,7 @@
   return result;
 }
 
-void Thread::ThrowNewException(const char* exception_class_descriptor, const char* fmt, ...) {
+void Thread::ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
   ThrowNewExceptionV(exception_class_descriptor, fmt, args);
@@ -1086,7 +1086,10 @@
 void Thread::ThrowNewExceptionV(const char* exception_class_descriptor, const char* fmt, va_list ap) {
   std::string msg;
   StringAppendV(&msg, fmt, ap);
+  ThrowNewException(exception_class_descriptor, msg.c_str());
+}
 
+void Thread::ThrowNewException(const char* exception_class_descriptor, const char* msg) {
   // Convert "Ljava/lang/Exception;" into JNI-style "java/lang/Exception".
   CHECK_EQ('L', exception_class_descriptor[0]);
   std::string descriptor(exception_class_descriptor + 1);
@@ -1096,7 +1099,7 @@
   JNIEnv* env = GetJniEnv();
   jclass exception_class = env->FindClass(descriptor.c_str());
   CHECK(exception_class != NULL) << "descriptor=\"" << descriptor << "\"";
-  int rc = env->ThrowNew(exception_class, msg.c_str());
+  int rc = env->ThrowNew(exception_class, msg);
   CHECK_EQ(rc, JNI_OK);
   env->DeleteLocalRef(exception_class);
 }
diff --git a/src/thread.h b/src/thread.h
index e9491dd..4f85b70 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -360,7 +360,10 @@
     top_of_managed_stack_pc_ = pc;
   }
 
-  void ThrowNewException(const char* exception_class_descriptor, const char* fmt, ...)
+  // 'msg' may be NULL.
+  void ThrowNewException(const char* exception_class_descriptor, const char* msg);
+
+  void ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...)
       __attribute__ ((format(printf, 3, 4)));
 
   void ThrowNewExceptionV(const char* exception_class_descriptor, const char* fmt, va_list ap);
diff --git a/test/034-call-null/expected.txt b/test/034-call-null/expected.txt
index 5ffbe05..7697a32 100644
--- a/test/034-call-null/expected.txt
+++ b/test/034-call-null/expected.txt
@@ -1,3 +1,2 @@
 java.lang.NullPointerException
 	at Main.main(Main.java:12)
-	at dalvik.system.NativeStart.main(Native Method)
diff --git a/test/038-inner-null/expected.txt b/test/038-inner-null/expected.txt
index 0be8ffd..6932c50 100644
--- a/test/038-inner-null/expected.txt
+++ b/test/038-inner-null/expected.txt
@@ -2,4 +2,3 @@
 java.lang.NullPointerException
 	at Main$Special.callInner(Main.java:17)
 	at Main.main(Main.java:6)
-	at dalvik.system.NativeStart.main(Native Method)
diff --git a/test/054-uncaught/expected.txt b/test/054-uncaught/expected.txt
index e7473be..e443a07 100644
--- a/test/054-uncaught/expected.txt
+++ b/test/054-uncaught/expected.txt
@@ -18,4 +18,3 @@
 java.lang.NullPointerException: Hi diddly-ho, neighborino.
 	at Main.catchTheUncaught(Main.java:49)
 	at Main.main(Main.java:12)
-	at dalvik.system.NativeStart.main(Native Method)