mirror of
https://github.com/netbirdio/gomobile-tvos-fork.git
synced 2026-05-22 18:43:29 -07:00
bind,cmd: accept Java API in bound packages
Accept Java API interface types as arguments and return values from bound Go package functions and methods. Also, allow Go structs to extend Java classes and implement Java interfaces as well as override and implement methods. This is the third and final part of the implementation of the golang/go#16876 proposal. Fixes golang/go#16876 Change-Id: I6951dd87235553ce09abe5117a39a503466163c0 Reviewed-on: https://go-review.googlesource.com/28597 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
@@ -13,6 +13,10 @@ import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import go.javapkg.Javapkg;
|
||||
import go.javapkg.GoObject;
|
||||
import go.javapkg.GoRunnable;
|
||||
import go.javapkg.GoSubset;
|
||||
import go.javapkg.GoInputStream;
|
||||
|
||||
public class ClassesTest extends InstrumentationTestCase {
|
||||
public void testConst() {
|
||||
@@ -65,4 +69,56 @@ public class ClassesTest extends InstrumentationTestCase {
|
||||
}
|
||||
assertNotNull("RuntimeException", exc);
|
||||
}
|
||||
|
||||
public void testGoObject() {
|
||||
Runnable r = new GoRunnable();
|
||||
r.run();
|
||||
assertTrue("GoRunnable.toString", r.toString().equals(Javapkg.ToStringPrefix));
|
||||
Runnable r2 = ((GoRunnable)r).getThis();
|
||||
assertTrue("GoObject.this", r == r2);
|
||||
Object o = new GoObject();
|
||||
assertEquals("GoObject hashCode", 42, o.hashCode());
|
||||
Object o2 = Javapkg.constructGoObject();
|
||||
assertEquals("GoObject hashCode", 42, o2.hashCode());
|
||||
assertTrue("GoObject.toString", o.toString().startsWith(Javapkg.ToStringPrefix));
|
||||
Javapkg.runRunnable(r);
|
||||
final boolean[] ran = new boolean[1];
|
||||
Runnable r3 = new Runnable(){
|
||||
@Override public void run() {
|
||||
ran[0] = true;
|
||||
}
|
||||
};
|
||||
Javapkg.runRunnable(r3);
|
||||
assertTrue("RunRunnable", ran[0]);
|
||||
assertTrue("RunnableRoundtrip Java", r3 == Javapkg.runnableRoundtrip(r3));
|
||||
assertTrue("RunnableRoundtrip Go", r == Javapkg.runnableRoundtrip(r));
|
||||
Runnable r5 = Javapkg.constructGoRunnable();
|
||||
r5.run();
|
||||
}
|
||||
|
||||
public void testTypedException() {
|
||||
InputStream is = new GoInputStream();
|
||||
Exception exc = null;
|
||||
try {
|
||||
is.read();
|
||||
} catch (IOException e) {
|
||||
exc = e;
|
||||
}
|
||||
assertNotNull("IOException", exc);
|
||||
assertEquals("IOException message", Javapkg.IOExceptionMessage, exc.getMessage());
|
||||
}
|
||||
|
||||
public void testInnerClass() {
|
||||
Character.Subset s = new Character.Subset(""){};
|
||||
Character.Subset s2 = new GoSubset("");
|
||||
Javapkg.callSubset(s);
|
||||
Javapkg.callSubset(s2);
|
||||
}
|
||||
|
||||
public void testNew() {
|
||||
Object o = Javapkg.newJavaObject();
|
||||
assertTrue("new Object()", o != null);
|
||||
Integer i = Javapkg.newJavaInteger();
|
||||
assertEquals("new Integer(42)", 42, i.intValue());
|
||||
}
|
||||
}
|
||||
|
||||
+27
-8
@@ -67,16 +67,24 @@ public class Seq {
|
||||
tracker.incRefnum(refnum);
|
||||
}
|
||||
|
||||
// incRef increments the reference count of Java objects.
|
||||
// For proxies for Go objects, it calls into the Proxy method
|
||||
// incRefnum() to make sure the Go reference count is positive
|
||||
// even if the Proxy is garbage collected and its Ref is finalized.
|
||||
public static int incRef(Object o) {
|
||||
return tracker.inc(o);
|
||||
}
|
||||
|
||||
public static int incGoObjectRef(GoObject o) {
|
||||
return o.incRefnum();
|
||||
}
|
||||
|
||||
public static Ref getRef(int refnum) {
|
||||
return tracker.get(refnum);
|
||||
}
|
||||
|
||||
// Increment the Go reference count before sending over a refnum.
|
||||
static native void incGoRef(int refnum);
|
||||
public static native void incGoRef(int refnum);
|
||||
|
||||
// Informs the Go ref tracker that Java is done with this ref.
|
||||
static native void destroyRef(int refnum);
|
||||
@@ -86,20 +94,31 @@ public class Seq {
|
||||
tracker.dec(refnum);
|
||||
}
|
||||
|
||||
// A Proxy is a Java object that proxies a Go object.
|
||||
public static abstract class Proxy {
|
||||
// A GoObject is a Java class implemented in Go. When a GoObject
|
||||
// is passed to Go, it is wrapped in a Go proxy, to make it behave
|
||||
// the same as passing a regular Java class.
|
||||
public interface GoObject {
|
||||
// Increment refcount and return the refnum of the proxy.
|
||||
//
|
||||
// The Go reference count need to be bumped while the
|
||||
// refnum is passed to Go, to avoid finalizing and
|
||||
// invalidating it before being translated on the Go side.
|
||||
int incRefnum();
|
||||
}
|
||||
|
||||
// A Proxy is a Java object that proxies a Go object. Proxies, unlike
|
||||
// GoObjects, are unwrapped to their Go counterpart when deserialized
|
||||
// in Go.
|
||||
public static abstract class Proxy implements GoObject {
|
||||
private final Ref ref;
|
||||
|
||||
protected Proxy(Ref ref) {
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
public final int incRefnum() {
|
||||
// The Go reference count need to be bumped while the
|
||||
// refnum is passed to Go, to avoid finalizing and
|
||||
// invalidating it before being translated on the Go side.
|
||||
@Override public final int incRefnum() {
|
||||
int refnum = ref.refnum;
|
||||
incGoRef(refnum);
|
||||
Seq.incGoRef(refnum);
|
||||
return refnum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ typedef jlong nint;
|
||||
extern void go_seq_dec_ref(int32_t ref);
|
||||
extern void go_seq_inc_ref(int32_t ref);
|
||||
extern int32_t go_seq_to_refnum(JNIEnv *env, jobject o);
|
||||
extern int32_t go_seq_to_refnum_go(JNIEnv *env, jobject o);
|
||||
extern jobject go_seq_from_refnum(JNIEnv *env, int32_t refnum, jclass proxy_class, jmethodID proxy_cons);
|
||||
|
||||
extern void go_seq_maybe_throw_exception(JNIEnv *env, jobject msg);
|
||||
@@ -44,6 +45,7 @@ extern jobject go_seq_wrap_exception(JNIEnv *env);
|
||||
|
||||
extern jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy);
|
||||
extern nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray s, int copy);
|
||||
extern void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr);
|
||||
|
||||
extern jstring go_seq_to_java_string(JNIEnv *env, nstring str);
|
||||
extern nstring go_seq_from_java_string(JNIEnv *env, jstring s);
|
||||
|
||||
@@ -29,13 +29,14 @@ 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 jmethodID throwable_getMessage;
|
||||
|
||||
static jfieldID ref_objField;
|
||||
|
||||
static jclass throwable_class;
|
||||
|
||||
// env_destructor is registered as a thread data key destructor to
|
||||
// clean up a Go thread that is attached to the JVM.
|
||||
static void env_destructor(void *env) {
|
||||
@@ -61,7 +62,11 @@ static JNIEnv *go_seq_get_thread_env(void) {
|
||||
|
||||
void go_seq_maybe_throw_exception(JNIEnv *env, jobject msg) {
|
||||
if (msg != NULL) {
|
||||
(*env)->CallStaticVoidMethod(env, seq_class, seq_throw_exc, msg);
|
||||
if ((*env)->IsInstanceOf(env, msg, throwable_class)) {
|
||||
(*env)->Throw(env, msg);
|
||||
} else {
|
||||
(*env)->CallStaticVoidMethod(env, seq_class, seq_throw_exc, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +225,13 @@ nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray arr, int copy) {
|
||||
return res;
|
||||
}
|
||||
|
||||
int32_t go_seq_to_refnum_go(JNIEnv *env, jobject o) {
|
||||
if (o == NULL) {
|
||||
return NULL_REFNUM;
|
||||
}
|
||||
return (int32_t)(*env)->CallStaticIntMethod(env, seq_class, seq_incGoObjectRef, o);
|
||||
}
|
||||
|
||||
int32_t go_seq_to_refnum(JNIEnv *env, jobject o) {
|
||||
if (o == NULL) {
|
||||
return NULL_REFNUM;
|
||||
@@ -241,9 +253,12 @@ jobject go_seq_from_refnum(JNIEnv *env, int32_t refnum, jclass proxy_class, jmet
|
||||
(*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)refnum);
|
||||
// return ref.obj
|
||||
return (*env)->GetObjectField(env, ref, ref_objField);
|
||||
} else {
|
||||
} else if (proxy_class != NULL) {
|
||||
// return new <Proxy>(ref)
|
||||
return (*env)->NewObject(env, proxy_class, proxy_cons, ref);
|
||||
} else {
|
||||
// We're inside a Java proxy constructor and only need the Seq.Ref
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,9 +273,13 @@ jstring go_seq_to_java_string(JNIEnv *env, nstring str) {
|
||||
|
||||
// go_seq_push_local_frame retrieves or creates the JNIEnv* for the current thread
|
||||
// and pushes a JNI reference frame. Must be matched with call to go_seq_pop_local_frame.
|
||||
JNIEnv *go_seq_push_local_frame(jint cap) {
|
||||
JNIEnv *go_seq_push_local_frame(jint nargs) {
|
||||
JNIEnv *env = go_seq_get_thread_env();
|
||||
if ((*env)->PushLocalFrame(env, cap) < 0) {
|
||||
// Given the number of function arguments, compute a conservative bound for the minimal frame size.
|
||||
// Assume two slots for each per parameter (Seq.Ref and Seq.Object) and add extra
|
||||
// extra space for the receiver, the return value, and exception (if any).
|
||||
jint frameSize = 2*nargs + 10;
|
||||
if ((*env)->PushLocalFrame(env, frameSize) < 0) {
|
||||
LOG_FATAL("PushLocalFrame failed");
|
||||
}
|
||||
return env;
|
||||
@@ -312,18 +331,19 @@ Java_go_Seq_init(JNIEnv *env, jclass clazz) {
|
||||
if (seq_incRef == NULL) {
|
||||
LOG_FATAL("failed to find method Seq.incRef");
|
||||
}
|
||||
seq_incGoObjectRef = (*env)->GetStaticMethodID(env, seq_class, "incGoObjectRef", "(Lgo/Seq$GoObject;)I");
|
||||
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");
|
||||
}
|
||||
jclass throwable_class = (*env)->FindClass(env, "java/lang/Throwable");
|
||||
throwable_class = (*env)->FindClass(env, "java/lang/Throwable");
|
||||
if (throwable_class == NULL) {
|
||||
LOG_FATAL("failed to find Throwable class");
|
||||
}
|
||||
throwable_getMessage = (*env)->GetMethodID(env, throwable_class, "getMessage", "()Ljava/lang/String;");
|
||||
if (throwable_getMessage == NULL) {
|
||||
LOG_FATAL("failed to find method Throwable.getMessage");
|
||||
}
|
||||
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");
|
||||
@@ -376,3 +396,9 @@ jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig)
|
||||
go_seq_pop_local_frame(env);
|
||||
return m;
|
||||
}
|
||||
|
||||
void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr) {
|
||||
if (ptr != NULL) {
|
||||
(*env)->ReleaseByteArrayElements(env, arr, ptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user