From e76ec530212353d474cd9ff744f75a44a7d2e189 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Fri, 30 Sep 2016 14:56:20 +0200 Subject: [PATCH] bind: support casting of Java objects Generate Cast functions that take a proxy for a Java class or interface, and return a new proxy with the same reference. The Cast functions panic if the underlying Java object is not an instance of the expected type. Change-Id: I08a5bf9a79139f0fac5dd102c7b028c8c989fc6d Reviewed-on: https://go-review.googlesource.com/30095 Reviewed-by: David Crawshaw --- bind/genclasses.go | 28 +++++++++---- bind/java/ClassesTest.java | 22 +++++++++++ bind/java/seq.h | 1 + bind/java/seq_android.c.support | 8 ++++ bind/testdata/classes.go.golden | 69 +++++++++++++++++++++++++++++++++ bind/testdata/java.go.golden | 69 +++++++++++++++++++++++++++++++++ bind/testpkg/javapkg/classes.go | 18 +++++++++ 7 files changed, 208 insertions(+), 7 deletions(-) diff --git a/bind/genclasses.go b/bind/genclasses.go index 465a0c8..9c2e4aa 100644 --- a/bind/genclasses.go +++ b/bind/genclasses.go @@ -155,6 +155,10 @@ func (g *ClassGen) GenPackage(idx int) { g.genFuncDecl(false, f) g.Printf("\n") } + g.Printf("// Cast takes a proxy for a Java object and converts it to a %s proxy.\n", cls.Name) + g.Printf("// Cast panics if the argument is not a proxy or if the underlying object does\n") + g.Printf("// not extend or implement %s.\n", cls.Name) + g.Printf("Cast func(v interface{}) Java.%s\n", goClsName(cls.Name)) g.Outdent() g.Printf(")\n\n") } @@ -163,16 +167,13 @@ func (g *ClassGen) GenPackage(idx int) { func (g *ClassGen) GenGo() { g.Printf(classesGoHeader) for _, cls := range g.classes { - for _, f := range cls.Funcs { - if f.Public && g.isFuncSupported(f) { - pkgName := strings.Replace(cls.Name, ".", "/", -1) - g.Printf("import %q\n", "Java/"+pkgName) - break - } - } + pkgName := strings.Replace(cls.Name, ".", "/", -1) + g.Printf("import %q\n", "Java/"+pkgName) } if len(g.classes) > 0 { g.Printf("import \"unsafe\"\n\n") + g.Printf("import \"reflect\"\n\n") + g.Printf("import \"fmt\"\n\n") } g.Printf("type proxy interface { Bind_proxy_refnum__() int32 }\n\n") g.Printf("// Suppress unused package error\n\n") @@ -427,6 +428,7 @@ func (g *ClassGen) genCFuncDecl(jniName string, f *java.Func) { } func (g *ClassGen) genGo(cls *java.Class) { + g.Printf("var class_%s C.jclass\n\n", cls.JNIName) g.Printf("func init_%s() {\n", cls.JNIName) g.Indent() g.Printf("cls := C.CString(%q)\n", strings.Replace(cls.FindName, ".", "/", -1)) @@ -435,6 +437,7 @@ func (g *ClassGen) genGo(cls *java.Class) { g.Printf("if clazz == nil {\n") g.Printf(" return\n") g.Printf("}\n") + g.Printf("class_%s = clazz\n", cls.JNIName) for _, f := range cls.Funcs { if !f.Public || !g.isFuncSupported(f) { continue @@ -476,6 +479,17 @@ func (g *ClassGen) genGo(cls *java.Class) { g.Outdent() g.Printf("}\n") } + g.Printf("%s.Cast = func(v interface{}) Java.%s {\n", cls.PkgName, goClsName(cls.Name)) + g.Indent() + g.Printf("t := reflect.TypeOf((*proxy_class_%s)(nil))\n", cls.JNIName) + g.Printf("cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_%s)\n", cls.JNIName) + g.Printf("ref := C.jint(_seq.ToRefNum(cv))\n") + g.Printf("if C.go_seq_isinstanceof(ref, class_%s) != 1 {\n", cls.JNIName) + g.Printf(" panic(fmt.Errorf(\"%%T is not an instance of %%s\", v, %q))\n", cls.Name) + g.Printf("}\n") + g.Printf("return cv\n") + g.Outdent() + g.Printf("}\n") g.Outdent() g.Printf("}\n\n") g.Printf("type proxy_class_%s _seq.Ref\n\n", cls.JNIName) diff --git a/bind/java/ClassesTest.java b/bind/java/ClassesTest.java index 720fc75..897fa2f 100644 --- a/bind/java/ClassesTest.java +++ b/bind/java/ClassesTest.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.Random; import go.javapkg.Javapkg; +import go.javapkg.I; import go.javapkg.GoObject; import go.javapkg.GoRunnable; import go.javapkg.GoSubset; @@ -127,4 +128,25 @@ public class ClassesTest extends InstrumentationTestCase { Integer i = Javapkg.newJavaInteger(); assertEquals("new Integer(42)", 42, i.intValue()); } + + private static class InterfaceRunnable implements I, Runnable { + @Override public void run() { + } + } + + public void testCast() { + Runnable r1 = new GoRunnable(); + Runnable r1c = Javapkg.castRunnable((Object)r1); + assertTrue("Casting Go object", r1c != null); + Runnable r2 = new Runnable() { + @Override public void run() { + } + }; + Runnable r2c = Javapkg.castRunnable((Object)r2); + assertTrue("Casting Java object", r2c != null); + Runnable r3c = Javapkg.castInterface(new InterfaceRunnable()); + assertTrue("Casting Go interface implementation", r3c != null); + Runnable r4c = Javapkg.castRunnable(new Object()); + assertTrue("Invalid cast", r4c == null); + } } diff --git a/bind/java/seq.h b/bind/java/seq.h index 363056d..1f36a1c 100644 --- a/bind/java/seq.h +++ b/bind/java/seq.h @@ -59,5 +59,6 @@ extern void go_seq_pop_local_frame(JNIEnv *env); extern jclass go_seq_find_class(const char *name); extern jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, const char *sig); extern jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig); +extern int go_seq_isinstanceof(jint refnum, jclass clazz); #endif // __GO_SEQ_HDR__ diff --git a/bind/java/seq_android.c.support b/bind/java/seq_android.c.support index 495b9e2..8b4e60f 100644 --- a/bind/java/seq_android.c.support +++ b/bind/java/seq_android.c.support @@ -382,3 +382,11 @@ void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr) { (*env)->ReleaseByteArrayElements(env, arr, ptr, 0); } } + +int go_seq_isinstanceof(jint refnum, jclass clazz) { + JNIEnv *env = go_seq_push_local_frame(0); + jobject obj = go_seq_from_refnum(env, refnum, NULL, NULL); + jboolean isinst = (*env)->IsInstanceOf(env, obj, clazz); + go_seq_pop_local_frame(env); + return isinst; +} diff --git a/bind/testdata/classes.go.golden b/bind/testdata/classes.go.golden index d769639..7f04d12 100644 --- a/bind/testdata/classes.go.golden +++ b/bind/testdata/classes.go.golden @@ -44,8 +44,17 @@ import ( _seq "golang.org/x/mobile/bind/seq" ) +import "Java/java/lang/Runnable" +import "Java/java/io/InputStream" +import "Java/java/util/concurrent/Future" +import "Java/java/lang/Object" +import "Java/java/util/concurrent/TimeUnit" import "unsafe" +import "reflect" + +import "fmt" + type proxy interface { Bind_proxy_refnum__() int32 } // Suppress unused package error @@ -63,6 +72,8 @@ func initClasses() { init_java_util_concurrent_TimeUnit() } +var class_java_lang_Runnable C.jclass + func init_java_lang_Runnable() { cls := C.CString("java/lang/Runnable") clazz := C.go_seq_find_class(cls) @@ -70,6 +81,16 @@ func init_java_lang_Runnable() { if clazz == nil { return } + class_java_lang_Runnable = clazz + Runnable.Cast = func(v interface{}) Java.Java_lang_Runnable { + t := reflect.TypeOf((*proxy_class_java_lang_Runnable)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_lang_Runnable) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_lang_Runnable) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.lang.Runnable")) + } + return cv + } } type proxy_class_java_lang_Runnable _seq.Ref @@ -90,6 +111,8 @@ func (p *proxy_class_java_lang_Runnable) Run() { if (_exc != nil) { panic(_exc) } } +var class_java_io_InputStream C.jclass + func init_java_io_InputStream() { cls := C.CString("java/io/InputStream") clazz := C.go_seq_find_class(cls) @@ -97,6 +120,16 @@ func init_java_io_InputStream() { if clazz == nil { return } + class_java_io_InputStream = clazz + InputStream.Cast = func(v interface{}) Java.Java_io_InputStream { + t := reflect.TypeOf((*proxy_class_java_io_InputStream)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_io_InputStream) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_io_InputStream) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.io.InputStream")) + } + return cv + } } type proxy_class_java_io_InputStream _seq.Ref @@ -139,6 +172,8 @@ func (p *super_java_io_InputStream) Read() (int32, error) { return _res, _exc } +var class_java_util_concurrent_Future C.jclass + func init_java_util_concurrent_Future() { cls := C.CString("java/util/concurrent/Future") clazz := C.go_seq_find_class(cls) @@ -146,6 +181,16 @@ func init_java_util_concurrent_Future() { if clazz == nil { return } + class_java_util_concurrent_Future = clazz + Future.Cast = func(v interface{}) Java.Java_util_concurrent_Future { + t := reflect.TypeOf((*proxy_class_java_util_concurrent_Future)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_concurrent_Future) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_util_concurrent_Future) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.concurrent.Future")) + } + return cv + } } type proxy_class_java_util_concurrent_Future _seq.Ref @@ -203,6 +248,8 @@ func (p *proxy_class_java_util_concurrent_Future) Get2(a0 int64, a1 Java.Java_ut return _res, _exc } +var class_java_lang_Object C.jclass + func init_java_lang_Object() { cls := C.CString("java/lang/Object") clazz := C.go_seq_find_class(cls) @@ -210,6 +257,16 @@ func init_java_lang_Object() { if clazz == nil { return } + class_java_lang_Object = clazz + Object.Cast = func(v interface{}) Java.Java_lang_Object { + t := reflect.TypeOf((*proxy_class_java_lang_Object)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_lang_Object) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_lang_Object) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.lang.Object")) + } + return cv + } } type proxy_class_java_lang_Object _seq.Ref @@ -222,6 +279,8 @@ func (p *proxy_class_java_lang_Object) Super() Java.Java_lang_Object { type super_java_lang_Object struct {*proxy_class_java_lang_Object} +var class_java_util_concurrent_TimeUnit C.jclass + func init_java_util_concurrent_TimeUnit() { cls := C.CString("java/util/concurrent/TimeUnit") clazz := C.go_seq_find_class(cls) @@ -229,6 +288,16 @@ func init_java_util_concurrent_TimeUnit() { if clazz == nil { return } + class_java_util_concurrent_TimeUnit = clazz + TimeUnit.Cast = func(v interface{}) Java.Java_util_concurrent_TimeUnit { + t := reflect.TypeOf((*proxy_class_java_util_concurrent_TimeUnit)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_concurrent_TimeUnit) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_util_concurrent_TimeUnit) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.concurrent.TimeUnit")) + } + return cv + } } type proxy_class_java_util_concurrent_TimeUnit _seq.Ref diff --git a/bind/testdata/java.go.golden b/bind/testdata/java.go.golden index 43e67c2..0ba36a4 100644 --- a/bind/testdata/java.go.golden +++ b/bind/testdata/java.go.golden @@ -39,8 +39,17 @@ import ( _seq "golang.org/x/mobile/bind/seq" ) +import "Java/java/lang/Float" +import "Java/java/lang/Object" +import "Java/java/lang/Runnable" +import "Java/java/lang/Character" +import "Java/java/lang/Character/Subset" import "unsafe" +import "reflect" + +import "fmt" + type proxy interface { Bind_proxy_refnum__() int32 } // Suppress unused package error @@ -58,6 +67,8 @@ func initClasses() { init_java_lang_Character_Subset() } +var class_java_lang_Float C.jclass + func init_java_lang_Float() { cls := C.CString("java/lang/Float") clazz := C.go_seq_find_class(cls) @@ -65,12 +76,24 @@ func init_java_lang_Float() { if clazz == nil { return } + class_java_lang_Float = clazz + Float.Cast = func(v interface{}) Java.Java_lang_Float { + t := reflect.TypeOf((*proxy_class_java_lang_Float)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_lang_Float) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_lang_Float) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.lang.Float")) + } + return cv + } } type proxy_class_java_lang_Float _seq.Ref func (p *proxy_class_java_lang_Float) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() } +var class_java_lang_Object C.jclass + func init_java_lang_Object() { cls := C.CString("java/lang/Object") clazz := C.go_seq_find_class(cls) @@ -78,6 +101,16 @@ func init_java_lang_Object() { if clazz == nil { return } + class_java_lang_Object = clazz + Object.Cast = func(v interface{}) Java.Java_lang_Object { + t := reflect.TypeOf((*proxy_class_java_lang_Object)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_lang_Object) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_lang_Object) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.lang.Object")) + } + return cv + } } type proxy_class_java_lang_Object _seq.Ref @@ -90,6 +123,8 @@ func (p *proxy_class_java_lang_Object) Super() Java.Java_lang_Object { type super_java_lang_Object struct {*proxy_class_java_lang_Object} +var class_java_lang_Runnable C.jclass + func init_java_lang_Runnable() { cls := C.CString("java/lang/Runnable") clazz := C.go_seq_find_class(cls) @@ -97,12 +132,24 @@ func init_java_lang_Runnable() { if clazz == nil { return } + class_java_lang_Runnable = clazz + Runnable.Cast = func(v interface{}) Java.Java_lang_Runnable { + t := reflect.TypeOf((*proxy_class_java_lang_Runnable)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_lang_Runnable) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_lang_Runnable) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.lang.Runnable")) + } + return cv + } } type proxy_class_java_lang_Runnable _seq.Ref func (p *proxy_class_java_lang_Runnable) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() } +var class_java_lang_Character C.jclass + func init_java_lang_Character() { cls := C.CString("java/lang/Character") clazz := C.go_seq_find_class(cls) @@ -110,12 +157,24 @@ func init_java_lang_Character() { if clazz == nil { return } + class_java_lang_Character = clazz + Character.Cast = func(v interface{}) Java.Java_lang_Character { + t := reflect.TypeOf((*proxy_class_java_lang_Character)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_lang_Character) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_lang_Character) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.lang.Character")) + } + return cv + } } type proxy_class_java_lang_Character _seq.Ref func (p *proxy_class_java_lang_Character) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() } +var class_java_lang_Character_Subset C.jclass + func init_java_lang_Character_Subset() { cls := C.CString("java/lang/Character$Subset") clazz := C.go_seq_find_class(cls) @@ -123,6 +182,16 @@ func init_java_lang_Character_Subset() { if clazz == nil { return } + class_java_lang_Character_Subset = clazz + Subset.Cast = func(v interface{}) Java.Java_lang_Character_Subset { + t := reflect.TypeOf((*proxy_class_java_lang_Character_Subset)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_lang_Character_Subset) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_lang_Character_Subset) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.lang.Character.Subset")) + } + return cv + } } type proxy_class_java_lang_Character_Subset _seq.Ref diff --git a/bind/testpkg/javapkg/classes.go b/bind/testpkg/javapkg/classes.go index c492c62..891ebbe 100644 --- a/bind/testpkg/javapkg/classes.go +++ b/bind/testpkg/javapkg/classes.go @@ -12,6 +12,7 @@ import ( "Java/java/lang/Character" "Java/java/lang/Integer" "Java/java/lang/Object" + "Java/java/lang/Runnable" "Java/java/util" "Java/java/util/concurrent" ) @@ -167,3 +168,20 @@ type GoRand struct { func (_ *GoRand) Next(this util.Random, i int32) int32 { return this.Super().Next(i) } + +type I interface{} + +func CastInterface(intf I) lang.Runnable { + var r lang.Runnable = Runnable.Cast(intf) + r.Run() + return r +} + +func CastRunnable(o lang.Object) lang.Runnable { + defer func() { + recover() // swallow the panic + }() + var r lang.Runnable = Runnable.Cast(o) + r.Run() + return r +}