| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package bind |
| |
| import ( |
| "fmt" |
| "path" |
| "reflect" |
| "strings" |
| "unicode" |
| "unicode/utf8" |
| |
| "golang.org/x/mobile/internal/importers" |
| "golang.org/x/mobile/internal/importers/java" |
| ) |
| |
| type ( |
| // ClassGen generates Go and C stubs for Java classes so import statements |
| // on the form |
| // |
| // |
| // import "Java/classpath/to/Class" |
| // |
| // will work. |
| ClassGen struct { |
| *Printer |
| // JavaPkg is the Java package prefix for the generated classes. The prefix is prepended to the Go |
| // package name to create the full Java package name. |
| JavaPkg string |
| imported map[string]struct{} |
| // The list of imported Java classes |
| classes []*java.Class |
| // The list of Go package paths with Java interfaces inside |
| jpkgs []string |
| // For each Go package path, the list of Java classes. |
| typePkgs map[string][]*java.Class |
| // For each Go package path, the Java class with static functions |
| // or constants. |
| clsPkgs map[string]*java.Class |
| // goClsMap is the map of Java class names to Go type names, qualified with package name. Go types |
| // that implement Java classes need Super methods and Unwrap methods. |
| goClsMap map[string]string |
| // goClsImports is the list of imports of user packages that contains the Go types implementing Java |
| // classes. |
| goClsImports []string |
| } |
| ) |
| |
| func (g *ClassGen) isSupported(t *java.Type) bool { |
| switch t.Kind { |
| case java.Array: |
| // TODO: Support all array types |
| return t.Elem.Kind == java.Byte |
| default: |
| return true |
| } |
| } |
| |
| func (g *ClassGen) isFuncSetSupported(fs *java.FuncSet) bool { |
| for _, f := range fs.Funcs { |
| if g.isFuncSupported(f) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func (g *ClassGen) isFuncSupported(f *java.Func) bool { |
| for _, a := range f.Params { |
| if !g.isSupported(a) { |
| return false |
| } |
| } |
| if f.Ret != nil { |
| return g.isSupported(f.Ret) |
| } |
| return true |
| } |
| |
| func (g *ClassGen) goType(t *java.Type, local bool) string { |
| if t == nil { |
| // interface{} is used for parameters types for overloaded methods |
| // where no common ancestor type exists. |
| return "interface{}" |
| } |
| switch t.Kind { |
| case java.Int: |
| return "int32" |
| case java.Boolean: |
| return "bool" |
| case java.Short: |
| return "int16" |
| case java.Char: |
| return "uint16" |
| case java.Byte: |
| return "byte" |
| case java.Long: |
| return "int64" |
| case java.Float: |
| return "float32" |
| case java.Double: |
| return "float64" |
| case java.String: |
| return "string" |
| case java.Array: |
| return "[]" + g.goType(t.Elem, local) |
| case java.Object: |
| name := goClsName(t.Class) |
| if !local { |
| name = "Java." + name |
| } |
| return name |
| default: |
| panic("invalid kind") |
| } |
| } |
| |
| // Init initializes the class wrapper generator. Classes is the |
| // list of classes to wrap, goClasses is the list of Java classes |
| // implemented in Go. |
| func (g *ClassGen) Init(classes []*java.Class, goClasses []importers.Struct) { |
| g.goClsMap = make(map[string]string) |
| impMap := make(map[string]struct{}) |
| for _, s := range goClasses { |
| n := s.Pkg + "." + s.Name |
| jn := n |
| if g.JavaPkg != "" { |
| jn = g.JavaPkg + "." + jn |
| } |
| g.goClsMap[jn] = n |
| if _, exists := impMap[s.PkgPath]; !exists { |
| impMap[s.PkgPath] = struct{}{} |
| g.goClsImports = append(g.goClsImports, s.PkgPath) |
| } |
| } |
| g.classes = classes |
| g.imported = make(map[string]struct{}) |
| g.typePkgs = make(map[string][]*java.Class) |
| g.clsPkgs = make(map[string]*java.Class) |
| pkgSet := make(map[string]struct{}) |
| for _, cls := range classes { |
| g.imported[cls.Name] = struct{}{} |
| clsPkg := strings.Replace(cls.Name, ".", "/", -1) |
| g.clsPkgs[clsPkg] = cls |
| typePkg := path.Dir(clsPkg) |
| g.typePkgs[typePkg] = append(g.typePkgs[typePkg], cls) |
| if _, exists := pkgSet[clsPkg]; !exists { |
| pkgSet[clsPkg] = struct{}{} |
| g.jpkgs = append(g.jpkgs, clsPkg) |
| } |
| if _, exists := pkgSet[typePkg]; !exists { |
| pkgSet[typePkg] = struct{}{} |
| g.jpkgs = append(g.jpkgs, typePkg) |
| } |
| } |
| } |
| |
| // Packages return the list of Go packages to be generated. |
| func (g *ClassGen) Packages() []string { |
| return g.jpkgs |
| } |
| |
| func (g *ClassGen) GenPackage(idx int) { |
| jpkg := g.jpkgs[idx] |
| g.Printf(gobindPreamble) |
| g.Printf("package %s\n\n", path.Base(jpkg)) |
| g.Printf("import \"Java\"\n\n") |
| g.Printf("const _ = Java.Dummy\n\n") |
| for _, cls := range g.typePkgs[jpkg] { |
| g.Printf("type %s Java.%s\n", cls.PkgName, goClsName(cls.Name)) |
| } |
| if cls, ok := g.clsPkgs[jpkg]; ok { |
| g.Printf("const (\n") |
| g.Indent() |
| // Constants |
| for _, v := range cls.Vars { |
| if g.isSupported(v.Type) && v.Constant() { |
| g.Printf("%s = %s\n", initialUpper(v.Name), v.Val) |
| } |
| } |
| g.Outdent() |
| g.Printf(")\n\n") |
| |
| g.Printf("var (\n") |
| g.Indent() |
| // Functions |
| loop: |
| for _, fs := range cls.Funcs { |
| for _, f := range fs.Funcs { |
| if f.Public && g.isFuncSupported(f) { |
| g.Printf("%s func", fs.GoName) |
| g.genFuncDecl(false, fs) |
| g.Printf("\n") |
| continue loop |
| } |
| } |
| } |
| 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") |
| } |
| } |
| |
| func (g *ClassGen) GenGo() { |
| g.Printf(classesGoHeader) |
| for _, cls := range g.classes { |
| pkgName := strings.Replace(cls.Name, ".", "/", -1) |
| g.Printf("import %q\n", "Java/"+pkgName) |
| } |
| for _, imp := range g.goClsImports { |
| g.Printf("import %q\n", imp) |
| } |
| 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") |
| g.Printf("var _ = _seq.FromRefNum\n") |
| g.Printf("const _ = Java.Dummy\n\n") |
| g.Printf("//export initClasses\n") |
| g.Printf("func initClasses() {\n") |
| g.Indent() |
| g.Printf("C.init_proxies()\n") |
| for _, cls := range g.classes { |
| g.Printf("init_%s()\n", cls.JNIName) |
| } |
| g.Outdent() |
| g.Printf("}\n\n") |
| for _, cls := range g.classes { |
| g.genGo(cls) |
| } |
| } |
| |
| func (g *ClassGen) GenH() { |
| g.Printf(classesHHeader) |
| for _, tn := range []string{"jint", "jboolean", "jshort", "jchar", "jbyte", "jlong", "jfloat", "jdouble", "nstring", "nbyteslice"} { |
| g.Printf("typedef struct ret_%s {\n", tn) |
| g.Printf(" %s res;\n", tn) |
| g.Printf(" jint exc;\n") |
| g.Printf("} ret_%s;\n", tn) |
| } |
| g.Printf("\n") |
| for _, cls := range g.classes { |
| for _, fs := range cls.AllMethods { |
| for _, f := range fs.Funcs { |
| if !g.isFuncSupported(f) { |
| continue |
| } |
| g.Printf("extern ") |
| g.genCMethodDecl("cproxy", cls.JNIName, f) |
| g.Printf(";\n") |
| if _, ok := g.goClsMap[cls.Name]; ok { |
| g.Printf("extern ") |
| g.genCMethodDecl("csuper", cls.JNIName, f) |
| g.Printf(";\n") |
| } |
| } |
| } |
| } |
| for _, cls := range g.classes { |
| g.genH(cls) |
| } |
| } |
| |
| func (g *ClassGen) GenC() { |
| g.Printf(classesCHeader) |
| for _, cls := range g.classes { |
| g.Printf("static jclass class_%s;\n", cls.JNIName) |
| if _, ok := g.goClsMap[cls.Name]; ok { |
| g.Printf("static jclass sclass_%s;\n", cls.JNIName) |
| } |
| for _, fs := range cls.Funcs { |
| for _, f := range fs.Funcs { |
| if !f.Public || !g.isFuncSupported(f) { |
| continue |
| } |
| g.Printf("static jmethodID m_s_%s_%s;\n", cls.JNIName, f.JNIName) |
| } |
| } |
| for _, fs := range cls.AllMethods { |
| for _, f := range fs.Funcs { |
| if g.isFuncSupported(f) { |
| g.Printf("static jmethodID m_%s_%s;\n", cls.JNIName, f.JNIName) |
| if _, ok := g.goClsMap[cls.Name]; ok { |
| g.Printf("static jmethodID sm_%s_%s;\n", cls.JNIName, f.JNIName) |
| } |
| } |
| } |
| } |
| g.genC(cls) |
| } |
| g.Printf("\n") |
| g.Printf("void init_proxies() {\n") |
| g.Indent() |
| g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", len(g.classes)) |
| g.Printf("jclass clazz;\n") |
| for _, cls := range g.classes { |
| g.Printf("clazz = go_seq_find_class(%q);\n", strings.Replace(cls.FindName, ".", "/", -1)) |
| g.Printf("if (clazz != NULL) {\n") |
| g.Indent() |
| g.Printf("class_%s = (*env)->NewGlobalRef(env, clazz);\n", cls.JNIName) |
| if _, ok := g.goClsMap[cls.Name]; ok { |
| g.Printf("sclass_%s = (*env)->GetSuperclass(env, clazz);\n", cls.JNIName) |
| g.Printf("sclass_%s = (*env)->NewGlobalRef(env, sclass_%s);\n", cls.JNIName, cls.JNIName) |
| } |
| for _, fs := range cls.Funcs { |
| for _, f := range fs.Funcs { |
| if !f.Public || !g.isFuncSupported(f) { |
| continue |
| } |
| g.Printf("m_s_%s_%s = ", cls.JNIName, f.JNIName) |
| if f.Constructor { |
| g.Printf("go_seq_get_method_id(clazz, \"<init>\", %q);\n", f.Desc) |
| } else { |
| g.Printf("go_seq_get_static_method_id(clazz, %q, %q);\n", f.Name, f.Desc) |
| } |
| } |
| } |
| for _, fs := range cls.AllMethods { |
| for _, f := range fs.Funcs { |
| if g.isFuncSupported(f) { |
| g.Printf("m_%s_%s = go_seq_get_method_id(clazz, %q, %q);\n", cls.JNIName, f.JNIName, f.Name, f.Desc) |
| if _, ok := g.goClsMap[cls.Name]; ok { |
| g.Printf("sm_%s_%s = go_seq_get_method_id(sclass_%s, %q, %q);\n", cls.JNIName, f.JNIName, cls.JNIName, f.Name, f.Desc) |
| } |
| } |
| } |
| } |
| g.Outdent() |
| g.Printf("}\n") |
| } |
| g.Printf("go_seq_pop_local_frame(env);\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| for _, cls := range g.classes { |
| for _, fs := range cls.AllMethods { |
| for _, f := range fs.Funcs { |
| if !g.isFuncSupported(f) { |
| continue |
| } |
| g.genCMethodDecl("cproxy", cls.JNIName, f) |
| g.genCMethodBody(cls, f, false) |
| if _, ok := g.goClsMap[cls.Name]; ok { |
| g.genCMethodDecl("csuper", cls.JNIName, f) |
| g.genCMethodBody(cls, f, true) |
| } |
| } |
| } |
| } |
| } |
| |
| func (g *ClassGen) GenInterfaces() { |
| g.Printf(classesPkgHeader) |
| for _, cls := range g.classes { |
| g.genInterface(cls) |
| } |
| } |
| |
| func (g *ClassGen) genCMethodBody(cls *java.Class, f *java.Func, virtual bool) { |
| g.Printf(" {\n") |
| g.Indent() |
| // Add 1 for the 'this' argument |
| g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", len(f.Params)+1) |
| g.Printf("// Must be a Java object\n") |
| g.Printf("jobject _this = go_seq_from_refnum(env, this, NULL, NULL);\n") |
| for i, a := range f.Params { |
| g.genCToJava(fmt.Sprintf("a%d", i), a) |
| } |
| if f.Ret != nil { |
| g.Printf("%s res = ", f.Ret.JNIType()) |
| } |
| g.Printf("(*env)->Call") |
| if virtual { |
| g.Printf("Nonvirtual") |
| } |
| if f.Ret != nil { |
| g.Printf(f.Ret.JNICallType()) |
| } else { |
| g.Printf("Void") |
| } |
| g.Printf("Method(env, _this, ") |
| if virtual { |
| g.Printf("sclass_%s, sm_%s_%s", cls.JNIName, cls.JNIName, f.JNIName) |
| } else { |
| g.Printf("m_%s_%s", cls.JNIName, f.JNIName) |
| } |
| for i := range f.Params { |
| g.Printf(", _a%d", i) |
| } |
| g.Printf(");\n") |
| g.Printf("jobject _exc = go_seq_get_exception(env);\n") |
| g.Printf("int32_t _exc_ref = go_seq_to_refnum(env, _exc);\n") |
| if f.Ret != nil { |
| g.genCRetClear("res", f.Ret, "_exc") |
| g.genJavaToC("res", f.Ret) |
| } |
| g.Printf("go_seq_pop_local_frame(env);\n") |
| if f.Ret != nil { |
| g.Printf("ret_%s __res = {_res, _exc_ref};\n", f.Ret.CType()) |
| g.Printf("return __res;\n") |
| } else { |
| g.Printf("return _exc_ref;\n") |
| } |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func initialUpper(s string) string { |
| if s == "" { |
| return "" |
| } |
| r, n := utf8.DecodeRuneInString(s) |
| return string(unicode.ToUpper(r)) + s[n:] |
| } |
| |
| func (g *ClassGen) genFuncDecl(local bool, fs *java.FuncSet) { |
| g.Printf("(") |
| for i, a := range fs.Params { |
| if i > 0 { |
| g.Printf(", ") |
| } |
| g.Printf("a%d ", i) |
| if i == len(fs.Params)-1 && fs.Variadic { |
| g.Printf("...") |
| } |
| g.Printf(g.goType(a, local)) |
| } |
| g.Printf(")") |
| if fs.Throws { |
| if fs.HasRet { |
| g.Printf(" (%s, error)", g.goType(fs.Ret, local)) |
| } else { |
| g.Printf(" error") |
| } |
| } else if fs.HasRet { |
| g.Printf(" %s", g.goType(fs.Ret, local)) |
| } |
| } |
| |
| func (g *ClassGen) genC(cls *java.Class) { |
| for _, fs := range cls.Funcs { |
| for _, f := range fs.Funcs { |
| if !f.Public || !g.isFuncSupported(f) { |
| continue |
| } |
| g.genCFuncDecl(cls.JNIName, f) |
| g.Printf(" {\n") |
| g.Indent() |
| g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", len(f.Params)) |
| for i, a := range f.Params { |
| g.genCToJava(fmt.Sprintf("a%d", i), a) |
| } |
| if f.Constructor { |
| g.Printf("jobject res = (*env)->NewObject(env") |
| } else if f.Ret != nil { |
| g.Printf("%s res = (*env)->CallStatic%sMethod(env", f.Ret.JNIType(), f.Ret.JNICallType()) |
| } else { |
| g.Printf("(*env)->CallStaticVoidMethod(env") |
| } |
| g.Printf(", class_%s, m_s_%s_%s", cls.JNIName, cls.JNIName, f.JNIName) |
| for i := range f.Params { |
| g.Printf(", _a%d", i) |
| } |
| g.Printf(");\n") |
| g.Printf("jobject _exc = go_seq_get_exception(env);\n") |
| g.Printf("int32_t _exc_ref = go_seq_to_refnum(env, _exc);\n") |
| if f.Ret != nil { |
| g.genCRetClear("res", f.Ret, "_exc") |
| g.genJavaToC("res", f.Ret) |
| } |
| g.Printf("go_seq_pop_local_frame(env);\n") |
| if f.Ret != nil { |
| g.Printf("ret_%s __res = {_res, _exc_ref};\n", f.Ret.CType()) |
| g.Printf("return __res;\n") |
| } else { |
| g.Printf("return _exc_ref;\n") |
| } |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| } |
| } |
| |
| func (g *ClassGen) genH(cls *java.Class) { |
| for _, fs := range cls.Funcs { |
| for _, f := range fs.Funcs { |
| if !f.Public || !g.isFuncSupported(f) { |
| continue |
| } |
| g.Printf("extern ") |
| g.genCFuncDecl(cls.JNIName, f) |
| g.Printf(";\n") |
| } |
| } |
| } |
| |
| func (g *ClassGen) genCMethodDecl(prefix, jniName string, f *java.Func) { |
| if f.Ret != nil { |
| g.Printf("ret_%s", f.Ret.CType()) |
| } else { |
| // Return only the exception, if any |
| g.Printf("jint") |
| } |
| g.Printf(" %s_%s_%s(jint this", prefix, jniName, f.JNIName) |
| for i, a := range f.Params { |
| g.Printf(", %s a%d", a.CType(), i) |
| } |
| g.Printf(")") |
| } |
| |
| func (g *ClassGen) genCFuncDecl(jniName string, f *java.Func) { |
| if f.Ret != nil { |
| g.Printf("ret_%s", f.Ret.CType()) |
| } else { |
| // Return only the exception, if any |
| g.Printf("jint") |
| } |
| g.Printf(" cproxy_s_%s_%s(", jniName, f.JNIName) |
| for i, a := range f.Params { |
| if i > 0 { |
| g.Printf(", ") |
| } |
| g.Printf("%s a%d", a.CType(), i) |
| } |
| g.Printf(")") |
| } |
| |
| 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)) |
| g.Printf("clazz := C.go_seq_find_class(cls)\n") |
| g.Printf("C.free(unsafe.Pointer(cls))\n") |
| // Before Go 1.11 clazz was a pointer value, an uintptr after. |
| g.Printf("if uintptr(clazz) == 0 {\n") |
| g.Printf(" return\n") |
| g.Printf("}\n") |
| g.Printf("class_%s = clazz\n", cls.JNIName) |
| for _, fs := range cls.Funcs { |
| var supported bool |
| for _, f := range fs.Funcs { |
| if f.Public && g.isFuncSupported(f) { |
| supported = true |
| break |
| } |
| } |
| if !supported { |
| continue |
| } |
| g.Printf("%s.%s = func", cls.PkgName, fs.GoName) |
| g.genFuncDecl(false, fs) |
| g.genFuncBody(cls, fs, "cproxy_s", true) |
| } |
| 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) |
| g.Printf("func (p *proxy_class_%s) Bind_proxy_refnum__() int32 {\n", cls.JNIName) |
| g.Indent() |
| g.Printf("return (*_seq.Ref)(p).Bind_IncNum()\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| for _, fs := range cls.AllMethods { |
| if !g.isFuncSetSupported(fs) { |
| continue |
| } |
| g.Printf("func (p *proxy_class_%s) %s", cls.JNIName, fs.GoName) |
| g.genFuncDecl(false, fs) |
| g.genFuncBody(cls, fs, "cproxy", false) |
| } |
| if cls.Throwable { |
| g.Printf("func (p *proxy_class_%s) Error() string {\n", cls.JNIName) |
| g.Printf(" return p.ToString()\n") |
| g.Printf("}\n") |
| } |
| if goName, ok := g.goClsMap[cls.Name]; ok { |
| g.Printf("func (p *proxy_class_%s) Super() Java.%s {\n", cls.JNIName, goClsName(cls.Name)) |
| g.Printf(" return &super_%s{p}\n", cls.JNIName) |
| g.Printf("}\n\n") |
| g.Printf("type super_%s struct {*proxy_class_%[1]s}\n\n", cls.JNIName) |
| g.Printf("func (p *proxy_class_%s) Unwrap() interface{} {\n", cls.JNIName) |
| g.Indent() |
| g.Printf("goRefnum := C.go_seq_unwrap(C.jint(p.Bind_proxy_refnum__()))\n") |
| g.Printf("return _seq.FromRefNum(int32(goRefnum)).Get().(*%s)\n", goName) |
| g.Outdent() |
| g.Printf("}\n\n") |
| for _, fs := range cls.AllMethods { |
| if !g.isFuncSetSupported(fs) { |
| continue |
| } |
| g.Printf("func (p *super_%s) %s", cls.JNIName, fs.GoName) |
| g.genFuncDecl(false, fs) |
| g.genFuncBody(cls, fs, "csuper", false) |
| } |
| } |
| } |
| |
| // genFuncBody generated a Go function body for a FuncSet. It resolves overloading dynamically, |
| // by inspecting the number of arguments (if the FuncSet contains varying parameter counts), |
| // and their types. |
| func (g *ClassGen) genFuncBody(cls *java.Class, fs *java.FuncSet, prefix string, static bool) { |
| maxp := len(fs.Funcs[0].Params) |
| minp := maxp |
| // sort the function variants into argument sizes. |
| buckets := make(map[int][]*java.Func) |
| numF := 0 |
| for _, f := range fs.Funcs { |
| if !g.isFuncSupported(f) { |
| continue |
| } |
| numF++ |
| n := len(f.Params) |
| if n < minp { |
| minp = n |
| } else if n > maxp { |
| maxp = n |
| } |
| buckets[n] = append(buckets[n], f) |
| } |
| g.Printf(" {\n") |
| g.Indent() |
| if len(buckets) != 1 { |
| // Switch over the number of arguments. |
| g.Printf("switch %d + len(a%d) {\n", minp, minp) |
| } |
| for i := minp; i <= maxp; i++ { |
| funcs := buckets[i] |
| if len(funcs) == 0 { |
| continue |
| } |
| if len(buckets) != 1 { |
| g.Printf("case %d:\n", i) |
| g.Indent() |
| } |
| for _, f := range funcs { |
| if len(funcs) > 1 { |
| g.Printf("{\n") |
| g.Indent() |
| } |
| var argNames []string |
| var preds []string |
| for i, a := range f.Params { |
| var ct *java.Type |
| var argName string |
| if i >= minp { |
| argName = fmt.Sprintf("a%d[%d]", minp, i-minp) |
| ct = fs.Params[minp] |
| } else { |
| argName = fmt.Sprintf("a%d", i) |
| ct = fs.Params[i] |
| } |
| if !reflect.DeepEqual(ct, a) { |
| g.Printf("_a%d, ok%d := %s.(%s)\n", i, i, argName, g.goType(a, false)) |
| argName = fmt.Sprintf("_a%d", i) |
| preds = append(preds, fmt.Sprintf("ok%d", i)) |
| } |
| argNames = append(argNames, argName) |
| } |
| if len(preds) > 0 { |
| g.Printf("if %s {\n", strings.Join(preds, " && ")) |
| g.Indent() |
| } |
| for i, a := range f.Params { |
| g.genWrite(fmt.Sprintf("__a%d", i), argNames[i], a, modeTransient) |
| } |
| g.Printf("res := C.%s_%s_%s(", prefix, cls.JNIName, f.JNIName) |
| if !static { |
| g.Printf("C.jint(p.Bind_proxy_refnum__())") |
| } |
| for i := range f.Params { |
| if !static || i > 0 { |
| g.Printf(", ") |
| } |
| g.Printf("__a%d", i) |
| } |
| g.Printf(")\n") |
| g.genFuncRet(fs, f, numF > 1) |
| if len(preds) > 0 { |
| g.Outdent() |
| g.Printf("}\n") |
| } |
| if len(funcs) > 1 { |
| g.Outdent() |
| g.Printf("}\n") |
| } |
| } |
| if len(buckets) != 1 { |
| g.Outdent() |
| } |
| } |
| if len(buckets) != 1 { |
| g.Printf("}\n") |
| } |
| if numF > 1 { |
| g.Printf("panic(\"no overloaded method found for %s.%s that matched the arguments\")\n", cls.Name, fs.Name) |
| } |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func (g *ClassGen) genFuncRet(fs *java.FuncSet, f *java.Func, mustReturn bool) { |
| if f.Ret != nil { |
| g.genRead("_res", "res.res", f.Ret, modeRetained) |
| g.genRefRead("_exc", "res.exc", "error", "proxy_error", true) |
| } else { |
| g.genRefRead("_exc", "res", "error", "proxy_error", true) |
| } |
| if !fs.Throws { |
| g.Printf("if (_exc != nil) { panic(_exc) }\n") |
| if fs.HasRet { |
| if f.Ret != nil { |
| g.Printf("return _res\n") |
| } else { |
| // The variant doesn't return a value, but the common |
| // signature does. Use nil as a placeholder return value. |
| g.Printf("return nil\n") |
| } |
| } else if mustReturn { |
| // If there are overloaded variants, return here to avoid the fallback |
| // panic generated in genFuncBody. |
| g.Printf("return\n") |
| } |
| } else { |
| if fs.HasRet { |
| if f.Ret != nil { |
| g.Printf("return _res, _exc\n") |
| } else { |
| // As above, use a nil placeholder return value. |
| g.Printf("return nil, _exc\n") |
| } |
| } else { |
| g.Printf("return _exc\n") |
| } |
| } |
| } |
| |
| func (g *ClassGen) genRead(to, from string, t *java.Type, mode varMode) { |
| switch t.Kind { |
| case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double: |
| g.Printf("%s := %s(%s)\n", to, g.goType(t, false), from) |
| case java.Boolean: |
| g.Printf("%s := %s != C.JNI_FALSE\n", to, from) |
| case java.String: |
| g.Printf("%s := decodeString(%s)\n", to, from) |
| case java.Array: |
| if t.Elem.Kind != java.Byte { |
| panic("unsupported array type") |
| } |
| g.Printf("%s := toSlice(%s, %v)\n", to, from, mode == modeRetained) |
| case java.Object: |
| _, hasProxy := g.imported[t.Class] |
| g.genRefRead(to, from, g.goType(t, false), "proxy_class_"+flattenName(t.Class), hasProxy) |
| default: |
| panic("invalid kind") |
| } |
| } |
| |
| func (g *ClassGen) genRefRead(to, from string, intfName, proxyName string, hasProxy bool) { |
| g.Printf("var %s %s\n", to, intfName) |
| g.Printf("%s_ref := _seq.FromRefNum(int32(%s))\n", to, from) |
| g.Printf("if %s_ref != nil {\n", to) |
| g.Printf(" if %s < 0 { // go object\n", from) |
| g.Printf(" %s = %s_ref.Get().(%s)\n", to, to, intfName) |
| g.Printf(" } else { // foreign object\n") |
| if hasProxy { |
| g.Printf(" %s = (*%s)(%s_ref)\n", to, proxyName, to) |
| } else { |
| g.Printf(" %s = %s_ref\n", to, to) |
| } |
| g.Printf(" }\n") |
| g.Printf("}\n") |
| } |
| |
| func (g *ClassGen) genWrite(dst, v string, t *java.Type, mode varMode) { |
| switch t.Kind { |
| case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double: |
| g.Printf("%s := C.%s(%s)\n", dst, t.CType(), v) |
| case java.Boolean: |
| g.Printf("%s := C.jboolean(C.JNI_FALSE)\n", dst) |
| g.Printf("if %s {\n", v) |
| g.Printf(" %s = C.jboolean(C.JNI_TRUE)\n", dst) |
| g.Printf("}\n") |
| case java.String: |
| g.Printf("%s := encodeString(%s)\n", dst, v) |
| case java.Array: |
| if t.Elem.Kind != java.Byte { |
| panic("unsupported array type") |
| } |
| g.Printf("%s := fromSlice(%s, %v)\n", dst, v, mode == modeRetained) |
| case java.Object: |
| g.Printf("var %s C.jint = _seq.NullRefNum\n", dst) |
| g.Printf("if %s != nil {\n", v) |
| g.Printf(" %s = C.jint(_seq.ToRefNum(%s))\n", dst, v) |
| g.Printf("}\n") |
| default: |
| panic("invalid kind") |
| } |
| } |
| |
| // genCRetClear clears the result value from a JNI call if an exception was |
| // raised. |
| func (g *ClassGen) genCRetClear(v string, t *java.Type, exc string) { |
| g.Printf("if (%s != NULL) {\n", exc) |
| g.Indent() |
| switch t.Kind { |
| case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double, java.Boolean: |
| g.Printf("%s = 0;\n", v) |
| default: |
| // Assume a nullable type. It will break if we missed a type. |
| g.Printf("%s = NULL;\n", v) |
| } |
| g.Outdent() |
| g.Printf("}\n") |
| } |
| |
| func (g *ClassGen) genJavaToC(v string, t *java.Type) { |
| switch t.Kind { |
| case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double, java.Boolean: |
| g.Printf("%s _%s = %s;\n", t.JNIType(), v, v) |
| case java.String: |
| g.Printf("nstring _%s = go_seq_from_java_string(env, %s);\n", v, v) |
| case java.Array: |
| if t.Elem.Kind != java.Byte { |
| panic("unsupported array type") |
| } |
| g.Printf("nbyteslice _%s = go_seq_from_java_bytearray(env, %s, 1);\n", v, v) |
| case java.Object: |
| g.Printf("jint _%s = go_seq_to_refnum(env, %s);\n", v, v) |
| default: |
| panic("invalid kind") |
| } |
| } |
| |
| func (g *ClassGen) genCToJava(v string, t *java.Type) { |
| switch t.Kind { |
| case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double, java.Boolean: |
| g.Printf("%s _%s = %s;\n", t.JNIType(), v, v) |
| case java.String: |
| g.Printf("jstring _%s = go_seq_to_java_string(env, %s);\n", v, v) |
| case java.Array: |
| if t.Elem.Kind != java.Byte { |
| panic("unsupported array type") |
| } |
| g.Printf("jbyteArray _%s = go_seq_to_java_bytearray(env, %s, 0);\n", v, v) |
| case java.Object: |
| g.Printf("jobject _%s = go_seq_from_refnum(env, %s, NULL, NULL);\n", v, v) |
| default: |
| panic("invalid kind") |
| } |
| } |
| |
| func goClsName(n string) string { |
| return initialUpper(strings.Replace(n, ".", "_", -1)) |
| } |
| |
| func (g *ClassGen) genInterface(cls *java.Class) { |
| g.Printf("type %s interface {\n", goClsName(cls.Name)) |
| g.Indent() |
| // Methods |
| for _, fs := range cls.AllMethods { |
| if !g.isFuncSetSupported(fs) { |
| continue |
| } |
| g.Printf(fs.GoName) |
| g.genFuncDecl(true, fs) |
| g.Printf("\n") |
| } |
| if goName, ok := g.goClsMap[cls.Name]; ok { |
| g.Printf("Super() %s\n", goClsName(cls.Name)) |
| g.Printf("// Unwrap returns the Go object this Java instance\n") |
| g.Printf("// is wrapping.\n") |
| g.Printf("// The return value is a %s, but the delclared type is\n", goName) |
| g.Printf("// interface{} to avoid import cycles.\n") |
| g.Printf("Unwrap() interface{}\n") |
| } |
| if cls.Throwable { |
| g.Printf("Error() string\n") |
| } |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| // Flatten java class names. "java.package.Class$Inner" is converted to |
| // "java_package_Class_Inner" |
| func flattenName(n string) string { |
| return strings.Replace(strings.Replace(n, ".", "_", -1), "$", "_", -1) |
| } |
| |
| var ( |
| classesPkgHeader = gobindPreamble + ` |
| package Java |
| |
| // Used to silence this package not used errors |
| const Dummy = 0 |
| |
| ` |
| classesCHeader = gobindPreamble + ` |
| #include <jni.h> |
| #include "seq.h" |
| #include "classes.h" |
| |
| ` |
| classesHHeader = gobindPreamble + ` |
| #include <jni.h> |
| #include "seq.h" |
| |
| extern void init_proxies(); |
| |
| ` |
| |
| javaImplHeader = gobindPreamble |
| |
| classesGoHeader = gobindPreamble + ` |
| package main |
| |
| /* |
| #include <stdlib.h> // for free() |
| #include <jni.h> |
| #include "seq.h" |
| #include "classes.h" |
| */ |
| import "C" |
| |
| import ( |
| "Java" |
| _seq "golang.org/x/mobile/bind/seq" |
| ) |
| |
| ` |
| ) |