bind: remove error wrappers to preserve error instance identity

CL 24800 changed the error representation from strings to objects.
However, since native errors types are not immediately compatible
across languages, wrapper types were introduced to bridge the gap.

This CL remove those wrappers and instead special case the error
proxy types to conform to their language error protocol.

Specifically:

 - The ObjC proxy for Go errors now extends NSError and calls
   initWithDomain to store the error message.
 - The Go proxy for ObjC NSError return the localizedDescription
    property for calls to Error.
 - The Java proxy for Go errors ow extends Exception and
   overrides getMessage() to return the error message.
 - The Go proxy for Java Exceptions returns getMessage whenever
   Error is called.

The end result is that error values behave more like normal objects
across the language boundary. In particular, instance identity is
now preserved: an error passed across the boundary and back will
result in the same instance.

There are two semantic changes that followed this change:

 - The domain for wrapped Go errors is now always "go".
   The domain wasn't useful before this CL: the domains were set to
   the package name of function or method where the error happened
   to cross the language boundary.
 - If a Go method that returns an error is implemented in ObjC, the
   implementation must now both return NO _and_ set the error result
   for the calling Go code to receive a non-nil error.
   Before this CL, because errors were always wrapped, a nil ObjC
   could be represented with a non-nil wrapper.

Change-Id: Idb415b6b13ecf79ccceb60f675059942bfc48fec
Reviewed-on: https://go-review.googlesource.com/29298
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Elias Naur
2016-10-04 09:11:42 +00:00
parent 84d710de20
commit 89b8360218
42 changed files with 433 additions and 284 deletions
+5 -25
View File
@@ -25,13 +25,11 @@ static JavaVM *jvm;
static pthread_key_t jnienvs;
static jclass seq_class;
static jmethodID seq_throw_exc;
static jmethodID seq_getRef;
static jmethodID seq_decRef;
static jmethodID seq_incRef;
static jmethodID seq_incGoObjectRef;
static jmethodID seq_incRefnum;
static jmethodID seq_wrapThrowable;
static jfieldID ref_objField;
@@ -60,23 +58,19 @@ static JNIEnv *go_seq_get_thread_env(void) {
return env;
}
void go_seq_maybe_throw_exception(JNIEnv *env, jobject msg) {
if (msg != NULL) {
if ((*env)->IsInstanceOf(env, msg, throwable_class)) {
(*env)->Throw(env, msg);
} else {
(*env)->CallStaticVoidMethod(env, seq_class, seq_throw_exc, msg);
}
void go_seq_maybe_throw_exception(JNIEnv *env, jobject exc) {
if (exc != NULL) {
(*env)->Throw(env, exc);
}
}
jobject go_seq_wrap_exception(JNIEnv *env) {
jobject go_seq_get_exception(JNIEnv *env) {
jthrowable exc = (*env)->ExceptionOccurred(env);
if (!exc) {
return NULL;
}
(*env)->ExceptionClear(env);
return (*env)->CallStaticObjectMethod(env, seq_class, seq_wrapThrowable, exc);
return exc;
}
jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy) {
@@ -310,11 +304,6 @@ Java_go_Seq_init(JNIEnv *env, jclass clazz) {
}
seq_class = (*env)->NewGlobalRef(env, clazz);
seq_throw_exc = (*env)->GetStaticMethodID(env, seq_class, "throwException", "(Lgo/error;)V");
if (seq_throw_exc == NULL) {
LOG_FATAL("failed to find method Seq.throwException");
}
seq_getRef = (*env)->GetStaticMethodID(env, seq_class, "getRef", "(I)Lgo/Seq$Ref;");
if (seq_getRef == NULL) {
LOG_FATAL("failed to find method Seq.getRef");
@@ -335,15 +324,6 @@ Java_go_Seq_init(JNIEnv *env, jclass clazz) {
if (seq_incGoObjectRef == NULL) {
LOG_FATAL("failed to find method Seq.incGoObjectRef");
}
seq_wrapThrowable = (*env)->GetStaticMethodID(env, seq_class, "wrapThrowable", "(Ljava/lang/Throwable;)Lgo/error;");
if (seq_wrapThrowable == NULL) {
LOG_FATAL("failed to find method Seq.wrapThrowable");
}
throwable_class = (*env)->FindClass(env, "java/lang/Throwable");
if (throwable_class == NULL) {
LOG_FATAL("failed to find Throwable class");
}
throwable_class = (*env)->NewGlobalRef(env, throwable_class);
jclass ref_class = (*env)->FindClass(env, "go/Seq$Ref");
if (ref_class == NULL) {
LOG_FATAL("failed to find the Seq.Ref class");