From fe0977739a89a889a55c57b7eb87878a5ac991ce Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Sun, 1 Jan 2017 19:55:42 +0100 Subject: [PATCH] bind: generate reverse bindings for implicit Java types Before this CL, Java types that were only implicitly referenced were represented as interface{}. However, if a value of such an implicit type were passed to Java, a runtime crash would occur because there would be no wrapper class to unwrap. Fix this by generating implicit types, fixing the crashes, gaining type safety, and removing the interface{} special case in the generator. While we're here, remove a redundant insert to the clsMap map in java.go. Change-Id: Ic50125da3d7cd6075899bf628d419b084c630490 Reviewed-on: https://go-review.googlesource.com/34777 Reviewed-by: David Crawshaw --- bind/genclasses.go | 3 - bind/java/ClassesTest.java | 6 - bind/testdata/classes.go | 7 ++ bind/testdata/classes.go.golden | 175 +++++++++++++++++++++++++++- bind/testdata/classes.java.c.golden | 41 ++++++- bind/testdata/classes.java.h.golden | 2 + bind/testpkg/javapkg/classes.go | 8 -- internal/importers/java/java.go | 74 +++++++++--- 8 files changed, 280 insertions(+), 36 deletions(-) diff --git a/bind/genclasses.go b/bind/genclasses.go index c7a144c..6bb8970 100644 --- a/bind/genclasses.go +++ b/bind/genclasses.go @@ -84,9 +84,6 @@ func (g *ClassGen) goType(t *java.Type, local bool) string { case java.Array: return "[]" + g.goType(t.Elem, local) case java.Object: - if _, exists := g.imported[t.Class]; !exists { - return "interface{}" - } name := goClsName(t.Class) if !local { name = "Java." + name diff --git a/bind/java/ClassesTest.java b/bind/java/ClassesTest.java index 83e66a4..ca38391 100644 --- a/bind/java/ClassesTest.java +++ b/bind/java/ClassesTest.java @@ -114,12 +114,6 @@ public class ClassesTest extends InstrumentationTestCase { assertEquals("IOException message", Javapkg.IOExceptionMessage, exc.getMessage()); } - public void testUnknownType() { - GoObject o = new GoObject(); - o.toString(); // Set this - assertTrue("GoObject.getClass not null", o.checkClass()); - } - public void testInnerClass() { Character.Subset s = new Character.Subset(""){}; Character.Subset s2 = new GoSubset(""); diff --git a/bind/testdata/classes.go b/bind/testdata/classes.go index 8e531c0..d110eb9 100644 --- a/bind/testdata/classes.go +++ b/bind/testdata/classes.go @@ -8,6 +8,7 @@ import ( gopkg "Java/go/java" "Java/java/io" "Java/java/lang" + "Java/java/lang/System" "Java/java/util/Spliterators" "Java/java/util/concurrent" ) @@ -52,3 +53,9 @@ func innerClassTypes() { // for the return value as well as parameters. Spliterators.Iterator_Ljava_util_Spliterator_00024OfInt_2(nil) } + +func returnType() { + // Implicit types (java.io.Console) should be wrapped. + cons := System.Console() + cons.Flush() +} diff --git a/bind/testdata/classes.go.golden b/bind/testdata/classes.go.golden index fc10f31..ff637a8 100644 --- a/bind/testdata/classes.go.golden +++ b/bind/testdata/classes.go.golden @@ -27,6 +27,9 @@ type Java_util_concurrent_TimeUnit interface { type Java_util_Spliterators interface { } +type Java_lang_System interface { +} + type Go_java_Future interface { Get() (Java_lang_Object, error) Get2(a0 int64, a1 Java_util_concurrent_TimeUnit) (Java_lang_Object, error) @@ -47,6 +50,16 @@ type Go_java_Runnable interface { Super() Go_java_Runnable } +type Java_util_PrimitiveIterator_OfInt interface { +} + +type Java_util_Spliterator_OfInt interface { +} + +type Java_io_Console interface { + Flush() error +} + // File is generated by gobind. Do not edit. package gomobile_bind @@ -70,10 +83,14 @@ import "Java/java/util/concurrent/Future" import "Java/java/lang/Object" import "Java/java/util/concurrent/TimeUnit" import "Java/java/util/Spliterators" +import "Java/java/lang/System" import "Java/go/java/Future" import "Java/go/java/InputStream" import "Java/go/java/Object" import "Java/go/java/Runnable" +import "Java/java/util/PrimitiveIterator/OfInt" +import "Java/java/util/Spliterator/OfInt" +import "Java/java/io/Console" import "unsafe" import "reflect" @@ -96,10 +113,14 @@ func initClasses() { init_java_lang_Object() init_java_util_concurrent_TimeUnit() init_java_util_Spliterators() + init_java_lang_System() init_go_java_Future() init_go_java_InputStream() init_go_java_Object() init_go_java_Runnable() + init_java_util_PrimitiveIterator_OfInt() + init_java_util_Spliterator_OfInt() + init_java_io_Console() } var class_java_lang_Runnable C.jclass @@ -324,19 +345,19 @@ func init_java_util_Spliterators() { C.free(unsafe.Pointer(fn)) C.free(unsafe.Pointer(fd)) if m != nil { - Spliterators.Iterator_Ljava_util_Spliterator_00024OfInt_2 = func(a0 interface{}) interface{} { + Spliterators.Iterator_Ljava_util_Spliterator_00024OfInt_2 = func(a0 Java.Java_util_Spliterator_OfInt) Java.Java_util_PrimitiveIterator_OfInt { var _a0 C.jint = _seq.NullRefNum if a0 != nil { _a0 = C.jint(_seq.ToRefNum(a0)) } res := C.cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2(clazz, m, _a0) - var _res interface{} + var _res Java.Java_util_PrimitiveIterator_OfInt _res_ref := _seq.FromRefNum(int32(res.res)) if _res_ref != nil { if res.res < 0 { // go object - _res = _res_ref.Get().(interface{}) + _res = _res_ref.Get().(Java.Java_util_PrimitiveIterator_OfInt) } else { // foreign object - _res = _res_ref + _res = (*proxy_class_java_util_PrimitiveIterator_OfInt)(_res_ref) } } var _exc error @@ -368,6 +389,63 @@ type proxy_class_java_util_Spliterators _seq.Ref func (p *proxy_class_java_util_Spliterators) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() } +var class_java_lang_System C.jclass + +func init_java_lang_System() { + cls := C.CString("java/lang/System") + clazz := C.go_seq_find_class(cls) + C.free(unsafe.Pointer(cls)) + if clazz == nil { + return + } + class_java_lang_System = clazz + { + fn := C.CString("console") + fd := C.CString("()Ljava/io/Console;") + m := C.go_seq_get_static_method_id(clazz, fn, fd) + C.free(unsafe.Pointer(fn)) + C.free(unsafe.Pointer(fd)) + if m != nil { + System.Console = func() Java.Java_io_Console { + res := C.cproxy_s_java_lang_System_console(clazz, m) + var _res Java.Java_io_Console + _res_ref := _seq.FromRefNum(int32(res.res)) + if _res_ref != nil { + if res.res < 0 { // go object + _res = _res_ref.Get().(Java.Java_io_Console) + } else { // foreign object + _res = (*proxy_class_java_io_Console)(_res_ref) + } + } + var _exc error + _exc_ref := _seq.FromRefNum(int32(res.exc)) + if _exc_ref != nil { + if res.exc < 0 { // go object + _exc = _exc_ref.Get().(error) + } else { // foreign object + _exc = (*proxy_error)(_exc_ref) + } + } + if (_exc != nil) { panic(_exc) } + return _res + } + } + } + System.Cast = func(v interface{}) Java.Java_lang_System { + t := reflect.TypeOf((*proxy_class_java_lang_System)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_lang_System) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_lang_System) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.lang.System")) + } + return cv + } +} + +type proxy_class_java_lang_System _seq.Ref + +func (p *proxy_class_java_lang_System) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() } + var class_go_java_Future C.jclass func init_go_java_Future() { @@ -652,6 +730,95 @@ func (p *super_go_java_Runnable) Run() { if (_exc != nil) { panic(_exc) } } +var class_java_util_PrimitiveIterator_OfInt C.jclass + +func init_java_util_PrimitiveIterator_OfInt() { + cls := C.CString("java/util/PrimitiveIterator$OfInt") + clazz := C.go_seq_find_class(cls) + C.free(unsafe.Pointer(cls)) + if clazz == nil { + return + } + class_java_util_PrimitiveIterator_OfInt = clazz + OfInt.Cast = func(v interface{}) Java.Java_util_PrimitiveIterator_OfInt { + t := reflect.TypeOf((*proxy_class_java_util_PrimitiveIterator_OfInt)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_PrimitiveIterator_OfInt) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_util_PrimitiveIterator_OfInt) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.PrimitiveIterator.OfInt")) + } + return cv + } +} + +type proxy_class_java_util_PrimitiveIterator_OfInt _seq.Ref + +func (p *proxy_class_java_util_PrimitiveIterator_OfInt) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() } + +var class_java_util_Spliterator_OfInt C.jclass + +func init_java_util_Spliterator_OfInt() { + cls := C.CString("java/util/Spliterator$OfInt") + clazz := C.go_seq_find_class(cls) + C.free(unsafe.Pointer(cls)) + if clazz == nil { + return + } + class_java_util_Spliterator_OfInt = clazz + OfInt.Cast = func(v interface{}) Java.Java_util_Spliterator_OfInt { + t := reflect.TypeOf((*proxy_class_java_util_Spliterator_OfInt)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_Spliterator_OfInt) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_util_Spliterator_OfInt) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.Spliterator.OfInt")) + } + return cv + } +} + +type proxy_class_java_util_Spliterator_OfInt _seq.Ref + +func (p *proxy_class_java_util_Spliterator_OfInt) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() } + +var class_java_io_Console C.jclass + +func init_java_io_Console() { + cls := C.CString("java/io/Console") + clazz := C.go_seq_find_class(cls) + C.free(unsafe.Pointer(cls)) + if clazz == nil { + return + } + class_java_io_Console = clazz + Console.Cast = func(v interface{}) Java.Java_io_Console { + t := reflect.TypeOf((*proxy_class_java_io_Console)(nil)) + cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_io_Console) + ref := C.jint(_seq.ToRefNum(cv)) + if C.go_seq_isinstanceof(ref, class_java_io_Console) != 1 { + panic(fmt.Errorf("%T is not an instance of %s", v, "java.io.Console")) + } + return cv + } +} + +type proxy_class_java_io_Console _seq.Ref + +func (p *proxy_class_java_io_Console) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() } + +func (p *proxy_class_java_io_Console) Flush() error { + res := C.cproxy_java_io_Console_flush(C.jint(p.Bind_proxy_refnum__())) + var _exc error + _exc_ref := _seq.FromRefNum(int32(res)) + if _exc_ref != nil { + if res < 0 { // go object + _exc = _exc_ref.Get().(error) + } else { // foreign object + _exc = (*proxy_error)(_exc_ref) + } + } + return _exc +} + // Package gomobile_bind is an autogenerated binder stub for package java. // gobind -lang=go classes // diff --git a/bind/testdata/classes.java.c.golden b/bind/testdata/classes.java.c.golden index 2e7f7d8..e738c12 100644 --- a/bind/testdata/classes.java.c.golden +++ b/bind/testdata/classes.java.c.golden @@ -29,6 +29,21 @@ ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024O } static jclass class_java_util_Spliterators; +ret_jint cproxy_s_java_lang_System_console(jclass clazz, jmethodID m) { + JNIEnv *env = go_seq_push_local_frame(0); + jobject res = (*env)->CallStaticObjectMethod(env, clazz, m); + jobject _exc = go_seq_get_exception(env); + int32_t _exc_ref = go_seq_to_refnum(env, _exc); + if (_exc != NULL) { + res = NULL; + } + jint _res = go_seq_to_refnum(env, res); + go_seq_pop_local_frame(env); + ret_jint __res = {_res, _exc_ref}; + return __res; +} + +static jclass class_java_lang_System; static jclass class_go_java_Future; static jclass sclass_go_java_Future; static jmethodID m_go_java_Future_get__; @@ -45,9 +60,13 @@ static jclass class_go_java_Runnable; static jclass sclass_go_java_Runnable; static jmethodID m_go_java_Runnable_run; static jmethodID sm_go_java_Runnable_run; +static jclass class_java_util_PrimitiveIterator_OfInt; +static jclass class_java_util_Spliterator_OfInt; +static jclass class_java_io_Console; +static jmethodID m_java_io_Console_flush; void init_proxies() { - JNIEnv *env = go_seq_push_local_frame(10); + JNIEnv *env = go_seq_push_local_frame(14); jclass clazz; clazz = (*env)->FindClass(env, "java/lang/Runnable"); class_java_lang_Runnable = (*env)->NewGlobalRef(env, clazz); @@ -65,6 +84,8 @@ void init_proxies() { class_java_util_concurrent_TimeUnit = (*env)->NewGlobalRef(env, clazz); clazz = (*env)->FindClass(env, "java/util/Spliterators"); class_java_util_Spliterators = (*env)->NewGlobalRef(env, clazz); + clazz = (*env)->FindClass(env, "java/lang/System"); + class_java_lang_System = (*env)->NewGlobalRef(env, clazz); clazz = (*env)->FindClass(env, "go/java/Future"); class_go_java_Future = (*env)->NewGlobalRef(env, clazz); sclass_go_java_Future = (*env)->GetSuperclass(env, clazz); @@ -89,6 +110,13 @@ void init_proxies() { sclass_go_java_Runnable = (*env)->NewGlobalRef(env, sclass_go_java_Runnable); m_go_java_Runnable_run = go_seq_get_method_id(clazz, "run", "()V"); sm_go_java_Runnable_run = go_seq_get_method_id(sclass_go_java_Runnable, "run", "()V"); + clazz = (*env)->FindClass(env, "java/util/PrimitiveIterator$OfInt"); + class_java_util_PrimitiveIterator_OfInt = (*env)->NewGlobalRef(env, clazz); + clazz = (*env)->FindClass(env, "java/util/Spliterator$OfInt"); + class_java_util_Spliterator_OfInt = (*env)->NewGlobalRef(env, clazz); + clazz = (*env)->FindClass(env, "java/io/Console"); + class_java_io_Console = (*env)->NewGlobalRef(env, clazz); + m_java_io_Console_flush = go_seq_get_method_id(clazz, "flush", "()V"); go_seq_pop_local_frame(env); } @@ -275,6 +303,17 @@ jint csuper_go_java_Runnable_run(jint this) { return _exc_ref; } +jint cproxy_java_io_Console_flush(jint this) { + JNIEnv *env = go_seq_push_local_frame(1); + // Must be a Java object + jobject _this = go_seq_from_refnum(env, this, NULL, NULL); + (*env)->CallVoidMethod(env, _this, m_java_io_Console_flush); + jobject _exc = go_seq_get_exception(env); + int32_t _exc_ref = go_seq_to_refnum(env, _exc); + go_seq_pop_local_frame(env); + return _exc_ref; +} + // JNI functions for the Go <=> Java bridge. // gobind -lang=java classes // diff --git a/bind/testdata/classes.java.h.golden b/bind/testdata/classes.java.h.golden index a110ec3..9421910 100644 --- a/bind/testdata/classes.java.h.golden +++ b/bind/testdata/classes.java.h.golden @@ -58,7 +58,9 @@ extern ret_jint cproxy_go_java_InputStream_read__(jint this); extern ret_jint csuper_go_java_InputStream_read__(jint this); extern jint cproxy_go_java_Runnable_run(jint this); extern jint csuper_go_java_Runnable_run(jint this); +extern jint cproxy_java_io_Console_flush(jint this); extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2(jclass clazz, jmethodID m, jint a0); +extern ret_jint cproxy_s_java_lang_System_console(jclass clazz, jmethodID m); // JNI function headers for the Go <=> Java bridge. // gobind -lang=java classes // diff --git a/bind/testpkg/javapkg/classes.go b/bind/testpkg/javapkg/classes.go index 4c79199..f18b920 100644 --- a/bind/testpkg/javapkg/classes.go +++ b/bind/testpkg/javapkg/classes.go @@ -92,14 +92,6 @@ func (o *GoObject) ToString(this gopkg.GoObject) string { return ToStringPrefix + this.Super().ToString() } -func (r *GoObject) CheckClass() bool { - // Verify that GetClass returns interface{} because java.lang.Class - // is unreferenced. - var f func() interface{} = r.this.GetClass - // But it should return a value - return f() != nil -} - func (_ *GoObject) HashCode() int32 { return 42 } diff --git a/internal/importers/java/java.go b/internal/importers/java/java.go index 890ec46..a5a898e 100644 --- a/internal/importers/java/java.go +++ b/internal/importers/java/java.go @@ -196,16 +196,52 @@ func (j *Importer) Import(refs *importers.References) ([]*Class, error) { classes = append(classes, cls) j.clsMap[cls.Name] = cls } + j.initClasses(classes, refs) + // Include implicit classes that are used in parameter or return values. + newClasses := classes + for len(newClasses) > 0 { + var impNames []string + var impClasses []*Class + for _, cls := range newClasses { + for _, funcs := range [][]*Func{cls.Funcs, cls.AllMethods} { + for _, f := range funcs { + names := j.implicitFuncTypes(f) + for _, name := range names { + if _, exists := clsSet[name]; exists { + continue + } + clsSet[name] = struct{}{} + if cls, exists := j.clsMap[name]; exists { + impClasses = append(impClasses, cls) + } else { + impNames = append(impNames, name) + } + } + } + } + } + imports, err := j.importClasses(impNames, false) + if err != nil { + return nil, err + } + impClasses = append(impClasses, imports...) + j.initClasses(impClasses, refs) + classes = append(classes, impClasses...) + newClasses = impClasses + } + j.fillThrowables(classes) + return classes, nil +} + +func (j *Importer) initClasses(classes []*Class, refs *importers.References) { for _, cls := range classes { j.fillAllMethods(cls) } - j.fillThrowables() for _, cls := range classes { j.mangleOverloads(cls.AllMethods) j.mangleOverloads(cls.Funcs) } j.filterReferences(classes, refs) - return classes, nil } func (v *Var) Constant() bool { @@ -411,7 +447,7 @@ func (j *Importer) importClasses(names []string, allowMissingClasses bool) ([]*C classes = append(classes, cls) j.clsMap[name] = cls } - // Include the methods from classes extended or implemented + // Include methods from extended or implemented classes. unkCls := classes for { var unknown []string @@ -425,14 +461,24 @@ func (j *Importer) importClasses(names []string, allowMissingClasses bool) ([]*C if err != nil { return nil, err } - for _, cls := range newCls { - j.clsMap[cls.Name] = cls - } unkCls = newCls } return classes, nil } +func (j *Importer) implicitFuncTypes(f *Func) []string { + var unk []string + if rt := f.Ret; rt != nil && rt.Kind == Object { + unk = append(unk, rt.Class) + } + for _, t := range f.Params { + if t.Kind == Object { + unk = append(unk, t.Class) + } + } + return unk +} + func (j *Importer) unknownSuperClasses(cls *Class, unk []string) []string { loop: for _, n := range cls.Supers { @@ -727,14 +773,14 @@ func (j *Importer) scanMethod(decl, desc string, parenIdx int) (*Func, error) { return f, nil } -func (j *Importer) fillThrowables() { +func (j *Importer) fillThrowables(classes []*Class) { thrCls, ok := j.clsMap["java.lang.Throwable"] if !ok { // If Throwable isn't in the class map // no imported class inherits from Throwable return } - for _, cls := range j.clsMap { + for _, cls := range classes { j.fillThrowableFor(cls, thrCls) } } @@ -769,10 +815,7 @@ func (j *Importer) fillAllMethods(cls *Class) { for _, f := range super.AllMethods { if _, exists := methods[f.FuncSig]; !exists { methods[f.FuncSig] = struct{}{} - // Copy function so each class can have its own - // JNI name mangling. - cpf := *f - cls.AllMethods = append(cls.AllMethods, &cpf) + cls.AllMethods = append(cls.AllMethods, f) } } } @@ -788,8 +831,11 @@ func (j *Importer) fillAllMethods(cls *Class) { // the method signature is appended in JNI mangled form. func (j *Importer) mangleOverloads(allFuncs []*Func) { overloads := make(map[string][]*Func) - for _, f := range allFuncs { - overloads[f.Name] = append(overloads[f.Name], f) + for i, f := range allFuncs { + // Name mangling is per class so copy the function first. + f := *f + allFuncs[i] = &f + overloads[f.Name] = append(overloads[f.Name], &f) } for _, funcs := range overloads { for _, f := range funcs {