Make it easier to debug jni.

The ExceptionDescribe call dumps the java stacktrace, which is
really useful when you're trying to figure out what exactly
failed.

Also, jValue used to return a bool, swallowing errors for no
apparent reason.  I've changed that to return an error.

MultiPart: 1/2
Change-Id: I9c4451fd739bf6108b753ba13fc73adaafdc1423
diff --git a/util/call.go b/util/call.go
index 55004d7..ab55bc5 100644
--- a/util/call.go
+++ b/util/call.go
@@ -36,10 +36,10 @@
 	}
 	for i, arg := range args {
 		sign := argSigns[i]
-		jVal, ok := jValue(env, arg, sign)
-		if !ok {
+		jVal, err := jValue(env, arg, sign)
+		if err != nil {
 			freeFunc()
-			return nil, nil, fmt.Errorf("couldn't get Java value for argument #%d [%#v] of expected type %v", i, arg, sign)
+			return nil, nil, fmt.Errorf("couldn't get Java value for argument #%d [%#v] of expected type %v: %v", i, arg, sign, err)
 		}
 		C.setJValueArrayElement(jArr, C.int(i), jVal)
 	}
diff --git a/util/jni_wrapper.c b/util/jni_wrapper.c
index 054a94c..0be09ba 100644
--- a/util/jni_wrapper.c
+++ b/util/jni_wrapper.c
@@ -182,6 +182,10 @@
   return (*env)->ExceptionClear(env);
 }
 
+void ExceptionDescribe(JNIEnv* env) {
+  return (*env)->ExceptionDescribe(env);
+}
+
 jint AttachCurrentThread(JavaVM* jvm, JNIEnv** env, void* args) {
   return (*jvm)->AttachCurrentThread(jvm, (void**) env, args);
 }
diff --git a/util/jni_wrapper.h b/util/jni_wrapper.h
index ef66a03..941013d 100644
--- a/util/jni_wrapper.h
+++ b/util/jni_wrapper.h
@@ -129,6 +129,10 @@
 // Clears any exception that is currently being thrown.
 void ExceptionClear(JNIEnv* env);
 
+// Prints an exception and a backtrace of the stack to a system error-reporting
+// channel, such as stderr.
+void ExceptionDescribe(JNIEnv* env);
+
 // Attaches the current thread to a Java VM.
 jint AttachCurrentThread(JavaVM* jvm, JNIEnv** env, void* args);
 
diff --git a/util/util.go b/util/util.go
index 70ae2f4..264c7eb 100644
--- a/util/util.go
+++ b/util/util.go
@@ -151,25 +151,25 @@
 }
 
 // JThrowV throws a new Java VException corresponding to the given error.
-func JThrowV(env Env, err error) {
-	if err == nil {
+func JThrowV(env Env, native error) {
+	if native == nil {
 		log.Printf("Couldn't throw exception: nil error")
 		return
 	}
-	obj, e := JVomCopy(env, err, jVExceptionClass)
-	if e != nil {
-		log.Printf("Couldn't throw exception %q: %v", err, e)
+	obj, err := JVomCopy(env, native, jVExceptionClass)
+	if err != nil {
+		log.Printf("Couldn't throw exception %#v: %v", native, err)
 		return
 	}
 	C.Throw(env.value(), C.jthrowable(obj.value()))
 }
 
 // JVException returns the Java VException given the Go error.
-func JVException(env Env, err error) (Object, error) {
-	if err == nil {
+func JVException(env Env, native error) (Object, error) {
+	if native == nil {
 		return NullObject, nil
 	}
-	return JVomCopy(env, err, jVExceptionClass)
+	return JVomCopy(env, native, jVExceptionClass)
 }
 
 // JExceptionMsg returns the exception message as a Go error, if an exception
@@ -179,6 +179,7 @@
 	if jException.IsNull() { // no exception
 		return nil
 	}
+	C.ExceptionDescribe(env.value())
 	C.ExceptionClear(env.value())
 	return GoError(env, jException)
 }
diff --git a/util/value.go b/util/value.go
index f9819ec..a68d1d4 100644
--- a/util/value.go
+++ b/util/value.go
@@ -7,6 +7,7 @@
 package util
 
 import (
+	"fmt"
 	"reflect"
 	"time"
 )
@@ -54,7 +55,7 @@
 var errJValue = C.jObjectValue(nil)
 
 // jValue converts a Go value into a Java value with the given sign.
-func jValue(env Env, v interface{}, sign Sign) (C.jvalue, bool) {
+func jValue(env Env, v interface{}, sign Sign) (C.jvalue, error) {
 	switch sign {
 	case BoolSign:
 		return jBoolValue(v)
@@ -87,153 +88,153 @@
 	}
 }
 
-func jBoolValue(v interface{}) (C.jvalue, bool) {
+func jBoolValue(v interface{}) (C.jvalue, error) {
 	val, ok := v.(bool)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't bool", v)
 	}
 	jBool := C.jboolean(C.JNI_FALSE)
 	if val {
 		jBool = C.jboolean(C.JNI_TRUE)
 	}
-	return C.jBoolValue(jBool), true
+	return C.jBoolValue(jBool), nil
 }
 
-func jByteValue(v interface{}) (C.jvalue, bool) {
+func jByteValue(v interface{}) (C.jvalue, error) {
 	val, ok := intValue(v)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't byte", v)
 	}
-	return C.jByteValue(C.jbyte(val)), true
+	return C.jByteValue(C.jbyte(val)), nil
 }
 
-func jCharValue(v interface{}) (C.jvalue, bool) {
+func jCharValue(v interface{}) (C.jvalue, error) {
 	val, ok := intValue(v)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't char", v)
 	}
-	return C.jCharValue(C.jchar(val)), true
+	return C.jCharValue(C.jchar(val)), nil
 }
 
-func jShortValue(v interface{}) (C.jvalue, bool) {
+func jShortValue(v interface{}) (C.jvalue, error) {
 	val, ok := intValue(v)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't short", v)
 	}
-	return C.jShortValue(C.jshort(val)), true
+	return C.jShortValue(C.jshort(val)), nil
 }
 
-func jIntValue(v interface{}) (C.jvalue, bool) {
+func jIntValue(v interface{}) (C.jvalue, error) {
 	val, ok := intValue(v)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't int", v)
 	}
-	return C.jIntValue(C.jint(val)), true
+	return C.jIntValue(C.jint(val)), nil
 }
 
-func jLongValue(v interface{}) (C.jvalue, bool) {
+func jLongValue(v interface{}) (C.jvalue, error) {
 	val, ok := intValue(v)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't long", v)
 	}
-	return C.jLongValue(C.jlong(val)), true
+	return C.jLongValue(C.jlong(val)), nil
 }
 
-func jStringValue(env Env, v interface{}) (C.jvalue, bool) {
+func jStringValue(env Env, v interface{}) (C.jvalue, error) {
 	str, ok := v.(string)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't string", v)
 	}
 	return jObjectValue(JString(env, str))
 }
 
-func jDateTimeValue(env Env, v interface{}) (C.jvalue, bool) {
+func jDateTimeValue(env Env, v interface{}) (C.jvalue, error) {
 	t, ok := v.(time.Time)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't time.Time", v)
 	}
 	jTime, err := JTime(env, t)
 	if err != nil {
-		return errJValue, false
+		return errJValue, err
 	}
 	return jObjectValue(jTime)
 }
 
-func jDurationValue(env Env, v interface{}) (C.jvalue, bool) {
+func jDurationValue(env Env, v interface{}) (C.jvalue, error) {
 	d, ok := v.(time.Duration)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't time.Duration", v)
 	}
 	jDuration, err := JDuration(env, d)
 	if err != nil {
-		return errJValue, false
+		return errJValue, err
 	}
 	return jObjectValue(jDuration)
 }
 
-func jVExceptionValue(env Env, v interface{}) (C.jvalue, bool) {
+func jVExceptionValue(env Env, v interface{}) (C.jvalue, error) {
 	if v == nil {
-		return C.jObjectValue(nil), true
+		return C.jObjectValue(nil), nil
 	}
-	err, ok := v.(error)
+	native, ok := v.(error)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't error", v)
 	}
-	jVException, err := JVException(env, err)
+	jVException, err := JVException(env, native)
 	if err != nil {
-		return errJValue, false
+		return errJValue, err
 	}
 	return jObjectValue(jVException)
 }
 
-func jByteArrayValue(env Env, v interface{}) (C.jvalue, bool) {
+func jByteArrayValue(env Env, v interface{}) (C.jvalue, error) {
 	arr, ok := v.([]byte)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't []byte", v)
 	}
 	jArr, err := JByteArray(env, arr)
 	if err != nil {
-		return errJValue, false
+		return errJValue, err
 	}
 	return jObjectValue(jArr)
 }
 
-func jStringArrayValue(env Env, v interface{}) (C.jvalue, bool) {
+func jStringArrayValue(env Env, v interface{}) (C.jvalue, error) {
 	arr, ok := v.([]string)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't []string", v)
 	}
 	jArr, err := JStringArray(env, arr)
 	if err != nil {
-		return errJValue, false
+		return errJValue, err
 	}
 	return jObjectValue(jArr)
 }
 
-func jByteArrayArrayValue(env Env, v interface{}) (C.jvalue, bool) {
+func jByteArrayArrayValue(env Env, v interface{}) (C.jvalue, error) {
 	arr, ok := v.([][]byte)
 	if !ok {
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't [][]byte", v)
 	}
 	jArr, err := JByteArrayArray(env, arr)
 	if err != nil {
-		return errJValue, false
+		return errJValue, err
 	}
 	return jObjectValue(jArr)
 }
 
-func jObjectValue(v interface{}) (C.jvalue, bool) {
+func jObjectValue(v interface{}) (C.jvalue, error) {
 	rv := reflect.ValueOf(v)
 	if !rv.IsValid() { // nil value
-		return C.jObjectValue(nil), true
+		return C.jObjectValue(nil), nil
 	}
 	switch val := v.(type) {
 	case Object:
-		return C.jObjectValue(val.value()), true
+		return C.jObjectValue(val.value()), nil
 	case Class:
-		return C.jObjectValue(C.jobject(val.value())), true
+		return C.jObjectValue(C.jobject(val.value())), nil
 	default:
-		return errJValue, false
+		return errJValue, fmt.Errorf("%#v isn't Object or Class", v)
 	}
 }