| // Copyright 2014 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" |
| "go/constant" |
| "go/types" |
| "html" |
| "math" |
| "reflect" |
| "regexp" |
| "strings" |
| |
| "golang.org/x/mobile/internal/importers/java" |
| ) |
| |
| // TODO(crawshaw): disallow basic android java type names in exported symbols. |
| // TODO(crawshaw): consider introducing Java functions for casting to and from interfaces at runtime. |
| |
| type JavaGen struct { |
| // 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 |
| |
| *Generator |
| |
| jstructs map[*types.TypeName]*javaClassInfo |
| clsMap map[string]*java.Class |
| // Constructors is a map from Go struct types to a list |
| // of exported constructor functions for the type, on the form |
| // func New<Type>(...) *Type |
| constructors map[*types.TypeName][]*types.Func |
| } |
| |
| type javaClassInfo struct { |
| // The Java class this class extends. |
| extends *java.Class |
| // All Java classes and interfaces this class extends and implements. |
| supers []*java.Class |
| methods map[string]*java.FuncSet |
| // Does the class need a default no-arg constructor |
| genNoargCon bool |
| } |
| |
| // Init intializes the embedded Generator and initializes the Java class information |
| // needed to generate structs that extend Java classes and interfaces. |
| func (g *JavaGen) Init(classes []*java.Class) { |
| g.Generator.Init() |
| g.clsMap = make(map[string]*java.Class) |
| for _, cls := range classes { |
| g.clsMap[cls.Name] = cls |
| } |
| g.jstructs = make(map[*types.TypeName]*javaClassInfo) |
| g.constructors = make(map[*types.TypeName][]*types.Func) |
| for _, s := range g.structs { |
| classes := embeddedJavaClasses(s.t) |
| if len(classes) == 0 { |
| continue |
| } |
| inf := &javaClassInfo{ |
| methods: make(map[string]*java.FuncSet), |
| genNoargCon: true, // java.lang.Object has a no-arg constructor |
| } |
| for _, n := range classes { |
| cls := g.clsMap[n] |
| for _, fs := range cls.AllMethods { |
| hasMeth := false |
| for _, f := range fs.Funcs { |
| if !f.Final { |
| hasMeth = true |
| } |
| } |
| if hasMeth { |
| inf.methods[fs.GoName] = fs |
| } |
| } |
| inf.supers = append(inf.supers, cls) |
| if !cls.Interface { |
| if inf.extends != nil { |
| g.errorf("%s embeds more than one Java class; only one is allowed.", s.obj) |
| } |
| if cls.Final { |
| g.errorf("%s embeds final Java class %s", s.obj, cls.Name) |
| } |
| inf.extends = cls |
| inf.genNoargCon = cls.HasNoArgCon |
| } |
| } |
| g.jstructs[s.obj] = inf |
| } |
| for _, f := range g.funcs { |
| if t := g.constructorType(f); t != nil { |
| jinf := g.jstructs[t] |
| if jinf != nil { |
| sig := f.Type().(*types.Signature) |
| jinf.genNoargCon = jinf.genNoargCon && sig.Params().Len() > 0 |
| } |
| g.constructors[t] = append(g.constructors[t], f) |
| } |
| } |
| } |
| |
| func (j *javaClassInfo) toJavaType(T types.Type) *java.Type { |
| switch T := T.(type) { |
| case *types.Basic: |
| var kind java.TypeKind |
| switch T.Kind() { |
| case types.Bool, types.UntypedBool: |
| kind = java.Boolean |
| case types.Uint8: |
| kind = java.Byte |
| case types.Int16: |
| kind = java.Short |
| case types.Int32, types.UntypedRune: // types.Rune |
| kind = java.Int |
| case types.Int64, types.UntypedInt: |
| kind = java.Long |
| case types.Float32: |
| kind = java.Float |
| case types.Float64, types.UntypedFloat: |
| kind = java.Double |
| case types.String, types.UntypedString: |
| kind = java.String |
| default: |
| return nil |
| } |
| return &java.Type{Kind: kind} |
| case *types.Slice: |
| switch e := T.Elem().(type) { |
| case *types.Basic: |
| switch e.Kind() { |
| case types.Uint8: // Byte. |
| return &java.Type{Kind: java.Array, Elem: &java.Type{Kind: java.Byte}} |
| } |
| } |
| return nil |
| case *types.Named: |
| if isJavaType(T) { |
| return &java.Type{Kind: java.Object, Class: classNameFor(T)} |
| } |
| } |
| return nil |
| } |
| |
| // lookupMethod searches the Java class descriptor for a method |
| // that matches the Go method. |
| func (j *javaClassInfo) lookupMethod(m *types.Func, hasThis bool) *java.Func { |
| jm := j.methods[m.Name()] |
| if jm == nil { |
| // If an exact match is not found, try the method with trailing underscores |
| // stripped. This way, name clashes can be avoided when overriding multiple |
| // overloaded methods from Go. |
| base := strings.TrimRight(m.Name(), "_") |
| jm = j.methods[base] |
| if jm == nil { |
| return nil |
| } |
| } |
| // A name match was found. Now use the parameter and return types to locate |
| // the correct variant. |
| sig := m.Type().(*types.Signature) |
| params := sig.Params() |
| // Convert Go parameter types to their Java counterparts, if possible. |
| var jparams []*java.Type |
| i := 0 |
| if hasThis { |
| i = 1 |
| } |
| for ; i < params.Len(); i++ { |
| jparams = append(jparams, j.toJavaType(params.At(i).Type())) |
| } |
| var ret *java.Type |
| var throws bool |
| if results := sig.Results(); results.Len() > 0 { |
| ret = j.toJavaType(results.At(0).Type()) |
| if results.Len() > 1 { |
| throws = isErrorType(results.At(1).Type()) |
| } |
| } |
| loop: |
| for _, f := range jm.Funcs { |
| if len(f.Params) != len(jparams) { |
| continue |
| } |
| if throws != (f.Throws != "") { |
| continue |
| } |
| if !reflect.DeepEqual(ret, f.Ret) { |
| continue |
| } |
| for i, p := range f.Params { |
| if !reflect.DeepEqual(p, jparams[i]) { |
| continue loop |
| } |
| } |
| return f |
| } |
| return nil |
| } |
| |
| // ClassNames returns the list of names of the generated Java classes and interfaces. |
| func (g *JavaGen) ClassNames() []string { |
| var names []string |
| for _, s := range g.structs { |
| names = append(names, g.javaTypeName(s.obj.Name())) |
| } |
| for _, iface := range g.interfaces { |
| names = append(names, g.javaTypeName(iface.obj.Name())) |
| } |
| return names |
| } |
| |
| func (g *JavaGen) GenClass(idx int) error { |
| ns := len(g.structs) |
| if idx < ns { |
| s := g.structs[idx] |
| g.genStruct(s) |
| } else { |
| iface := g.interfaces[idx-ns] |
| g.genInterface(iface) |
| } |
| if len(g.err) > 0 { |
| return g.err |
| } |
| return nil |
| } |
| |
| func (g *JavaGen) genProxyImpl(name string) { |
| g.Printf("private final int refnum;\n\n") |
| g.Printf("@Override public final int incRefnum() {\n") |
| g.Printf(" Seq.incGoRef(refnum, this);\n") |
| g.Printf(" return refnum;\n") |
| g.Printf("}\n\n") |
| } |
| |
| func (g *JavaGen) genStruct(s structInfo) { |
| pkgPath := "" |
| if g.Pkg != nil { |
| pkgPath = g.Pkg.Path() |
| } |
| n := g.javaTypeName(s.obj.Name()) |
| g.Printf(javaPreamble, g.javaPkgName(g.Pkg), n, g.gobindOpts(), pkgPath) |
| |
| fields := exportedFields(s.t) |
| methods := exportedMethodSet(types.NewPointer(s.obj.Type())) |
| |
| var impls []string |
| jinf := g.jstructs[s.obj] |
| if jinf != nil { |
| impls = append(impls, "Seq.GoObject") |
| for _, cls := range jinf.supers { |
| if cls.Interface { |
| impls = append(impls, g.javaTypeName(cls.Name)) |
| } |
| } |
| } else { |
| impls = append(impls, "Seq.Proxy") |
| } |
| |
| pT := types.NewPointer(s.obj.Type()) |
| for _, iface := range g.allIntf { |
| if types.AssignableTo(pT, iface.obj.Type()) { |
| n := iface.obj.Name() |
| if p := iface.obj.Pkg(); p != g.Pkg { |
| if n == JavaClassName(p) { |
| n = n + "_" |
| } |
| n = fmt.Sprintf("%s.%s", g.javaPkgName(p), n) |
| } else { |
| n = g.javaTypeName(n) |
| } |
| impls = append(impls, n) |
| } |
| } |
| |
| doc := g.docs[n] |
| g.javadoc(doc.Doc()) |
| g.Printf("public final class %s", n) |
| if jinf != nil { |
| if jinf.extends != nil { |
| g.Printf(" extends %s", g.javaTypeName(jinf.extends.Name)) |
| } |
| } |
| if len(impls) > 0 { |
| g.Printf(" implements %s", strings.Join(impls, ", ")) |
| } |
| g.Printf(" {\n") |
| g.Indent() |
| |
| g.Printf("static { %s.touch(); }\n\n", g.className()) |
| g.genProxyImpl(n) |
| cons := g.constructors[s.obj] |
| for _, f := range cons { |
| if !g.isSigSupported(f.Type()) { |
| g.Printf("// skipped constructor %s.%s with unsupported parameter or return types\n\n", n, f.Name()) |
| continue |
| } |
| g.genConstructor(f, n, jinf != nil) |
| } |
| if jinf == nil || jinf.genNoargCon { |
| // constructor for Go instantiated instances. |
| g.Printf("%s(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); }\n\n", n) |
| if len(cons) == 0 { |
| // Generate default no-arg constructor |
| g.Printf("public %s() { this.refnum = __New(); Seq.trackGoRef(refnum, this); }\n\n", n) |
| g.Printf("private static native int __New();\n\n") |
| } |
| } |
| |
| for _, f := range fields { |
| if t := f.Type(); !g.isSupported(t) { |
| g.Printf("// skipped field %s.%s with unsupported type: %s\n\n", n, f.Name(), t) |
| continue |
| } |
| |
| fdoc := doc.Member(f.Name()) |
| g.javadoc(fdoc) |
| g.Printf("public final native %s get%s();\n", g.javaType(f.Type()), f.Name()) |
| g.javadoc(fdoc) |
| g.Printf("public final native void set%s(%s v);\n\n", f.Name(), g.javaType(f.Type())) |
| } |
| |
| var isStringer bool |
| for _, m := range methods { |
| if !g.isSigSupported(m.Type()) { |
| g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", n, m.Name()) |
| continue |
| } |
| g.javadoc(doc.Member(m.Name())) |
| var jm *java.Func |
| hasThis := false |
| if jinf != nil { |
| hasThis = g.hasThis(n, m) |
| jm = jinf.lookupMethod(m, hasThis) |
| if jm != nil { |
| g.Printf("@Override ") |
| } |
| } |
| g.Printf("public native ") |
| g.genFuncSignature(m, jm, hasThis) |
| t := m.Type().(*types.Signature) |
| isStringer = isStringer || (m.Name() == "String" && t.Params().Len() == 0 && t.Results().Len() == 1 && |
| types.Identical(t.Results().At(0).Type(), types.Typ[types.String])) |
| } |
| |
| if jinf == nil { |
| g.genObjectMethods(n, fields, isStringer) |
| } |
| |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| // javaTypeName returns the class name of a given Go type name. If |
| // the type name clashes with the package class name, an underscore is |
| // appended. |
| func (g *JavaGen) javaTypeName(n string) string { |
| if n == JavaClassName(g.Pkg) { |
| return n + "_" |
| } |
| return n |
| } |
| |
| func (g *JavaGen) javadoc(doc string) { |
| if doc == "" { |
| return |
| } |
| // JavaDoc expects HTML-escaped documentation. |
| g.Printf("/**\n * %s */\n", html.EscapeString(doc)) |
| } |
| |
| // hasThis returns whether a method has an implicit "this" parameter. |
| func (g *JavaGen) hasThis(sName string, m *types.Func) bool { |
| sig := m.Type().(*types.Signature) |
| params := sig.Params() |
| if params.Len() == 0 { |
| return false |
| } |
| v := params.At(0) |
| if v.Name() != "this" { |
| return false |
| } |
| t, ok := v.Type().(*types.Named) |
| if !ok { |
| return false |
| } |
| obj := t.Obj() |
| pkg := obj.Pkg() |
| if pkgFirstElem(pkg) != "Java" { |
| return false |
| } |
| clsName := classNameFor(t) |
| exp := g.javaPkgName(g.Pkg) + "." + sName |
| if clsName != exp { |
| g.errorf("the type %s of the `this` argument to method %s.%s is not %s", clsName, sName, m.Name(), exp) |
| return false |
| } |
| return true |
| } |
| |
| func (g *JavaGen) genConstructor(f *types.Func, n string, jcls bool) { |
| g.javadoc(g.docs[f.Name()].Doc()) |
| g.Printf("public %s(", n) |
| g.genFuncArgs(f, nil, false) |
| g.Printf(") {\n") |
| g.Indent() |
| sig := f.Type().(*types.Signature) |
| params := sig.Params() |
| if jcls { |
| g.Printf("super(") |
| for i := 0; i < params.Len(); i++ { |
| if i > 0 { |
| g.Printf(", ") |
| } |
| g.Printf(g.paramName(params, i)) |
| } |
| g.Printf(");\n") |
| } |
| g.Printf("this.refnum = ") |
| g.Printf("__%s(", f.Name()) |
| for i := 0; i < params.Len(); i++ { |
| if i > 0 { |
| g.Printf(", ") |
| } |
| g.Printf(g.paramName(params, i)) |
| } |
| g.Printf(");\n") |
| g.Printf("Seq.trackGoRef(refnum, this);\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| g.Printf("private static native int __%s(", f.Name()) |
| g.genFuncArgs(f, nil, false) |
| g.Printf(");\n\n") |
| } |
| |
| // genFuncArgs generated Java function arguments declaration for the function f. |
| // If the supplied overridden java function is supplied, genFuncArgs omits the implicit |
| // this argument. |
| func (g *JavaGen) genFuncArgs(f *types.Func, jm *java.Func, hasThis bool) { |
| sig := f.Type().(*types.Signature) |
| params := sig.Params() |
| first := 0 |
| if hasThis { |
| // Skip the implicit this argument to the Go method |
| first = 1 |
| } |
| for i := first; i < params.Len(); i++ { |
| if i > first { |
| g.Printf(", ") |
| } |
| v := params.At(i) |
| name := g.paramName(params, i) |
| jt := g.javaType(v.Type()) |
| g.Printf("%s %s", jt, name) |
| } |
| } |
| |
| func (g *JavaGen) genObjectMethods(n string, fields []*types.Var, isStringer bool) { |
| g.Printf("@Override public boolean equals(Object o) {\n") |
| g.Indent() |
| g.Printf("if (o == null || !(o instanceof %s)) {\n return false;\n}\n", n) |
| g.Printf("%s that = (%s)o;\n", n, n) |
| for _, f := range fields { |
| if t := f.Type(); !g.isSupported(t) { |
| g.Printf("// skipped field %s.%s with unsupported type: %s\n\n", n, f.Name(), t) |
| continue |
| } |
| nf := f.Name() |
| g.Printf("%s this%s = get%s();\n", g.javaType(f.Type()), nf, nf) |
| g.Printf("%s that%s = that.get%s();\n", g.javaType(f.Type()), nf, nf) |
| if isJavaPrimitive(f.Type()) { |
| g.Printf("if (this%s != that%s) {\n return false;\n}\n", nf, nf) |
| } else { |
| g.Printf("if (this%s == null) {\n", nf) |
| g.Indent() |
| g.Printf("if (that%s != null) {\n return false;\n}\n", nf) |
| g.Outdent() |
| g.Printf("} else if (!this%s.equals(that%s)) {\n return false;\n}\n", nf, nf) |
| } |
| } |
| g.Printf("return true;\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| |
| g.Printf("@Override public int hashCode() {\n") |
| g.Printf(" return java.util.Arrays.hashCode(new Object[] {") |
| idx := 0 |
| for _, f := range fields { |
| if t := f.Type(); !g.isSupported(t) { |
| continue |
| } |
| if idx > 0 { |
| g.Printf(", ") |
| } |
| idx++ |
| g.Printf("get%s()", f.Name()) |
| } |
| g.Printf("});\n") |
| g.Printf("}\n\n") |
| |
| g.Printf("@Override public String toString() {\n") |
| g.Indent() |
| if isStringer { |
| g.Printf("return string();\n") |
| } else { |
| g.Printf("StringBuilder b = new StringBuilder();\n") |
| g.Printf(`b.append("%s").append("{");`, n) |
| g.Printf("\n") |
| for _, f := range fields { |
| if t := f.Type(); !g.isSupported(t) { |
| continue |
| } |
| n := f.Name() |
| g.Printf(`b.append("%s:").append(get%s()).append(",");`, n, n) |
| g.Printf("\n") |
| } |
| g.Printf(`return b.append("}").toString();`) |
| g.Printf("\n") |
| } |
| g.Outdent() |
| g.Printf("}\n") |
| } |
| |
| func (g *JavaGen) genInterface(iface interfaceInfo) { |
| pkgPath := "" |
| if g.Pkg != nil { |
| pkgPath = g.Pkg.Path() |
| } |
| g.Printf(javaPreamble, g.javaPkgName(g.Pkg), g.javaTypeName(iface.obj.Name()), g.gobindOpts(), pkgPath) |
| |
| var exts []string |
| numM := iface.t.NumMethods() |
| for _, other := range g.allIntf { |
| // Only extend interfaces with fewer methods to avoid circular references |
| if other.t.NumMethods() < numM && types.AssignableTo(iface.t, other.t) { |
| n := other.obj.Name() |
| if p := other.obj.Pkg(); p != g.Pkg { |
| if n == JavaClassName(p) { |
| n = n + "_" |
| } |
| n = fmt.Sprintf("%s.%s", g.javaPkgName(p), n) |
| } else { |
| n = g.javaTypeName(n) |
| } |
| exts = append(exts, n) |
| } |
| } |
| doc := g.docs[iface.obj.Name()] |
| g.javadoc(doc.Doc()) |
| g.Printf("public interface %s", g.javaTypeName(iface.obj.Name())) |
| if len(exts) > 0 { |
| g.Printf(" extends %s", strings.Join(exts, ", ")) |
| } |
| g.Printf(" {\n") |
| g.Indent() |
| |
| for _, m := range iface.summary.callable { |
| if !g.isSigSupported(m.Type()) { |
| g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", iface.obj.Name(), m.Name()) |
| continue |
| } |
| g.javadoc(doc.Member(m.Name())) |
| g.Printf("public ") |
| g.genFuncSignature(m, nil, false) |
| } |
| |
| g.Printf("\n") |
| |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func isJavaPrimitive(T types.Type) bool { |
| b, ok := T.(*types.Basic) |
| if !ok { |
| return false |
| } |
| switch b.Kind() { |
| case types.Bool, types.Uint8, types.Float32, types.Float64, |
| types.Int, types.Int8, types.Int16, types.Int32, types.Int64: |
| return true |
| } |
| return false |
| } |
| |
| // jniType returns a string that can be used as a JNI type. |
| func (g *JavaGen) jniType(T types.Type) string { |
| switch T := T.(type) { |
| case *types.Basic: |
| switch T.Kind() { |
| case types.Bool, types.UntypedBool: |
| return "jboolean" |
| case types.Int: |
| return "jlong" |
| case types.Int8: |
| return "jbyte" |
| case types.Int16: |
| return "jshort" |
| case types.Int32, types.UntypedRune: // types.Rune |
| return "jint" |
| case types.Int64, types.UntypedInt: |
| return "jlong" |
| case types.Uint8: // types.Byte |
| // TODO(crawshaw): Java bytes are signed, so this is |
| // questionable, but vital. |
| return "jbyte" |
| // TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64: |
| case types.Float32: |
| return "jfloat" |
| case types.Float64, types.UntypedFloat: |
| return "jdouble" |
| case types.String, types.UntypedString: |
| return "jstring" |
| default: |
| g.errorf("unsupported basic type: %s", T) |
| return "TODO" |
| } |
| case *types.Slice: |
| return "jbyteArray" |
| |
| case *types.Pointer: |
| if _, ok := T.Elem().(*types.Named); ok { |
| return g.jniType(T.Elem()) |
| } |
| g.errorf("unsupported pointer to type: %s", T) |
| case *types.Named: |
| return "jobject" |
| default: |
| g.errorf("unsupported jniType: %#+v, %s\n", T, T) |
| } |
| return "TODO" |
| } |
| |
| func (g *JavaGen) javaBasicType(T *types.Basic) string { |
| switch T.Kind() { |
| case types.Bool, types.UntypedBool: |
| return "boolean" |
| case types.Int: |
| return "long" |
| case types.Int8: |
| return "byte" |
| case types.Int16: |
| return "short" |
| case types.Int32, types.UntypedRune: // types.Rune |
| return "int" |
| case types.Int64, types.UntypedInt: |
| return "long" |
| case types.Uint8: // types.Byte |
| // TODO(crawshaw): Java bytes are signed, so this is |
| // questionable, but vital. |
| return "byte" |
| // TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64: |
| case types.Float32: |
| return "float" |
| case types.Float64, types.UntypedFloat: |
| return "double" |
| case types.String, types.UntypedString: |
| return "String" |
| default: |
| g.errorf("unsupported basic type: %s", T) |
| return "TODO" |
| } |
| } |
| |
| // javaType returns a string that can be used as a Java type. |
| func (g *JavaGen) javaType(T types.Type) string { |
| if isErrorType(T) { |
| // The error type is usually translated into an exception in |
| // Java, however the type can be exposed in other ways, such |
| // as an exported field. |
| return "java.lang.Exception" |
| } else if isJavaType(T) { |
| return classNameFor(T) |
| } |
| switch T := T.(type) { |
| case *types.Basic: |
| return g.javaBasicType(T) |
| case *types.Slice: |
| elem := g.javaType(T.Elem()) |
| return elem + "[]" |
| |
| case *types.Pointer: |
| if _, ok := T.Elem().(*types.Named); ok { |
| return g.javaType(T.Elem()) |
| } |
| g.errorf("unsupported pointer to type: %s", T) |
| case *types.Named: |
| n := T.Obj() |
| nPkg := n.Pkg() |
| if !isErrorType(T) && !g.validPkg(nPkg) { |
| g.errorf("type %s is in %s, which is not bound", n.Name(), nPkg) |
| break |
| } |
| // TODO(crawshaw): more checking here |
| clsName := n.Name() |
| if nPkg != g.Pkg { |
| if clsName == JavaClassName(nPkg) { |
| clsName += "_" |
| } |
| return fmt.Sprintf("%s.%s", g.javaPkgName(nPkg), clsName) |
| } else { |
| return g.javaTypeName(clsName) |
| } |
| default: |
| g.errorf("unsupported javaType: %#+v, %s\n", T, T) |
| } |
| return "TODO" |
| } |
| |
| func (g *JavaGen) genJNIFuncSignature(o *types.Func, sName string, jm *java.Func, proxy, isjava bool) { |
| sig := o.Type().(*types.Signature) |
| res := sig.Results() |
| |
| var ret string |
| switch res.Len() { |
| case 2: |
| ret = g.jniType(res.At(0).Type()) |
| case 1: |
| if isErrorType(res.At(0).Type()) { |
| ret = "void" |
| } else { |
| ret = g.jniType(res.At(0).Type()) |
| } |
| case 0: |
| ret = "void" |
| default: |
| g.errorf("too many result values: %s", o) |
| return |
| } |
| |
| g.Printf("JNIEXPORT %s JNICALL\n", ret) |
| g.Printf("Java_%s_", g.jniPkgName()) |
| if sName != "" { |
| if proxy { |
| g.Printf(g.className()) |
| // 0024 is the mangled form of $, for naming inner classes. |
| g.Printf("_00024proxy%s", sName) |
| } else { |
| g.Printf(java.JNIMangle(g.javaTypeName(sName))) |
| } |
| } else { |
| g.Printf(g.className()) |
| } |
| g.Printf("_") |
| if jm != nil { |
| g.Printf(jm.JNIName) |
| } else { |
| oName := javaNameReplacer(lowerFirst(o.Name())) |
| g.Printf(java.JNIMangle(oName)) |
| } |
| g.Printf("(JNIEnv* env, ") |
| if sName != "" { |
| g.Printf("jobject __this__") |
| } else { |
| g.Printf("jclass _clazz") |
| } |
| params := sig.Params() |
| i := 0 |
| if isjava && params.Len() > 0 && params.At(0).Name() == "this" { |
| // Skip the implicit this argument, if any. |
| i = 1 |
| } |
| for ; i < params.Len(); i++ { |
| g.Printf(", ") |
| v := sig.Params().At(i) |
| name := g.paramName(params, i) |
| jt := g.jniType(v.Type()) |
| g.Printf("%s %s", jt, name) |
| } |
| g.Printf(")") |
| } |
| |
| func (g *JavaGen) jniPkgName() string { |
| return strings.Replace(java.JNIMangle(g.javaPkgName(g.Pkg)), ".", "_", -1) |
| } |
| |
| var javaLetterDigitRE = regexp.MustCompile(`[0-9a-zA-Z$_]`) |
| |
| func (g *JavaGen) paramName(params *types.Tuple, pos int) string { |
| name := basicParamName(params, pos) |
| if !javaLetterDigitRE.MatchString(name) { |
| name = fmt.Sprintf("p%d", pos) |
| } |
| return javaNameReplacer(name) |
| } |
| |
| func (g *JavaGen) genFuncSignature(o *types.Func, jm *java.Func, hasThis bool) { |
| sig := o.Type().(*types.Signature) |
| res := sig.Results() |
| |
| var returnsError bool |
| var ret string |
| switch res.Len() { |
| case 2: |
| if !isErrorType(res.At(1).Type()) { |
| g.errorf("second result value must be of type error: %s", o) |
| return |
| } |
| returnsError = true |
| ret = g.javaType(res.At(0).Type()) |
| case 1: |
| if isErrorType(res.At(0).Type()) { |
| returnsError = true |
| ret = "void" |
| } else { |
| ret = g.javaType(res.At(0).Type()) |
| } |
| case 0: |
| ret = "void" |
| default: |
| g.errorf("too many result values: %s", o) |
| return |
| } |
| |
| g.Printf("%s ", ret) |
| if jm != nil { |
| g.Printf(jm.Name) |
| } else { |
| g.Printf(javaNameReplacer(lowerFirst(o.Name()))) |
| } |
| g.Printf("(") |
| g.genFuncArgs(o, jm, hasThis) |
| g.Printf(")") |
| if returnsError { |
| if jm != nil { |
| if jm.Throws == "" { |
| g.errorf("%s declares an error return value but the overriden method does not throw", o) |
| return |
| } |
| g.Printf(" throws %s", jm.Throws) |
| } else { |
| g.Printf(" throws Exception") |
| } |
| } |
| g.Printf(";\n") |
| } |
| |
| func (g *JavaGen) genVar(o *types.Var) { |
| if t := o.Type(); !g.isSupported(t) { |
| g.Printf("// skipped variable %s with unsupported type: %s\n\n", o.Name(), t) |
| return |
| } |
| jType := g.javaType(o.Type()) |
| |
| doc := g.docs[o.Name()].Doc() |
| // setter |
| g.javadoc(doc) |
| g.Printf("public static native void set%s(%s v);\n", o.Name(), jType) |
| |
| // getter |
| g.javadoc(doc) |
| g.Printf("public static native %s get%s();\n\n", jType, o.Name()) |
| } |
| |
| // genCRetClear clears the result value from a JNI call if an exception was |
| // raised. |
| func (g *JavaGen) genCRetClear(varName string, t types.Type, exc string) { |
| g.Printf("if (%s != NULL) {\n", exc) |
| g.Indent() |
| switch t := t.(type) { |
| case *types.Basic: |
| switch t.Kind() { |
| case types.String: |
| g.Printf("%s = NULL;\n", varName) |
| default: |
| g.Printf("%s = 0;\n", varName) |
| } |
| case *types.Slice, *types.Named, *types.Pointer: |
| g.Printf("%s = NULL;\n", varName) |
| } |
| g.Outdent() |
| g.Printf("}\n") |
| } |
| |
| func (g *JavaGen) genJavaToC(varName string, t types.Type, mode varMode) { |
| switch t := t.(type) { |
| case *types.Basic: |
| switch t.Kind() { |
| case types.String: |
| g.Printf("nstring _%s = go_seq_from_java_string(env, %s);\n", varName, varName) |
| default: |
| g.Printf("%s _%s = (%s)%s;\n", g.cgoType(t), varName, g.cgoType(t), varName) |
| } |
| case *types.Slice: |
| switch e := t.Elem().(type) { |
| case *types.Basic: |
| switch e.Kind() { |
| case types.Uint8: // Byte. |
| g.Printf("nbyteslice _%s = go_seq_from_java_bytearray(env, %s, %d);\n", varName, varName, toCFlag(mode == modeRetained)) |
| default: |
| g.errorf("unsupported type: %s", t) |
| } |
| default: |
| g.errorf("unsupported type: %s", t) |
| } |
| case *types.Named: |
| switch u := t.Underlying().(type) { |
| case *types.Interface: |
| g.Printf("int32_t _%s = go_seq_to_refnum(env, %s);\n", varName, varName) |
| default: |
| g.errorf("unsupported named type: %s / %T", u, u) |
| } |
| case *types.Pointer: |
| g.Printf("int32_t _%s = go_seq_to_refnum(env, %s);\n", varName, varName) |
| default: |
| g.Printf("%s _%s = (%s)%s;\n", g.cgoType(t), varName, g.cgoType(t), varName) |
| } |
| } |
| |
| func (g *JavaGen) genCToJava(toName, fromName string, t types.Type, mode varMode) { |
| switch t := t.(type) { |
| case *types.Basic: |
| switch t.Kind() { |
| case types.String: |
| g.Printf("jstring %s = go_seq_to_java_string(env, %s);\n", toName, fromName) |
| case types.Bool: |
| g.Printf("jboolean %s = %s ? JNI_TRUE : JNI_FALSE;\n", toName, fromName) |
| default: |
| g.Printf("%s %s = (%s)%s;\n", g.jniType(t), toName, g.jniType(t), fromName) |
| } |
| case *types.Slice: |
| switch e := t.Elem().(type) { |
| case *types.Basic: |
| switch e.Kind() { |
| case types.Uint8: // Byte. |
| g.Printf("jbyteArray %s = go_seq_to_java_bytearray(env, %s, %d);\n", toName, fromName, toCFlag(mode == modeRetained)) |
| default: |
| g.errorf("unsupported type: %s", t) |
| } |
| default: |
| g.errorf("unsupported type: %s", t) |
| } |
| case *types.Pointer: |
| // TODO(crawshaw): test *int |
| // TODO(crawshaw): test **Generator |
| switch t := t.Elem().(type) { |
| case *types.Named: |
| g.genFromRefnum(toName, fromName, t, t.Obj()) |
| default: |
| g.errorf("unsupported type %s", t) |
| } |
| case *types.Named: |
| switch t.Underlying().(type) { |
| case *types.Interface, *types.Pointer: |
| g.genFromRefnum(toName, fromName, t, t.Obj()) |
| default: |
| g.errorf("unsupported, direct named type %s", t) |
| } |
| default: |
| g.Printf("%s %s = (%s)%s;\n", g.jniType(t), toName, g.jniType(t), fromName) |
| } |
| } |
| |
| func (g *JavaGen) genFromRefnum(toName, fromName string, t types.Type, o *types.TypeName) { |
| oPkg := o.Pkg() |
| isJava := isJavaType(o.Type()) |
| if !isErrorType(o.Type()) && !g.validPkg(oPkg) && !isJava { |
| g.errorf("type %s is defined in package %s, which is not bound", t, oPkg) |
| return |
| } |
| p := pkgPrefix(oPkg) |
| g.Printf("jobject %s = go_seq_from_refnum(env, %s, ", toName, fromName) |
| if isJava { |
| g.Printf("NULL, NULL") |
| } else { |
| g.Printf("proxy_class_%s_%s, proxy_class_%s_%s_cons", p, o.Name(), p, o.Name()) |
| } |
| g.Printf(");\n") |
| } |
| |
| func (g *JavaGen) gobindOpts() string { |
| opts := []string{"-lang=java"} |
| if g.JavaPkg != "" { |
| opts = append(opts, "-javapkg="+g.JavaPkg) |
| } |
| return strings.Join(opts, " ") |
| } |
| |
| var javaNameReplacer = newNameSanitizer([]string{ |
| "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", |
| "class", "const", "continue", "default", "do", "double", "else", "enum", |
| "extends", "final", "finally", "float", "for", "goto", "if", "implements", |
| "import", "instanceof", "int", "interface", "long", "native", "new", "package", |
| "private", "protected", "public", "return", "short", "static", "strictfp", |
| "super", "switch", "synchronized", "this", "throw", "throws", "transient", |
| "try", "void", "volatile", "while", "false", "null", "true"}) |
| |
| func (g *JavaGen) javaPkgName(pkg *types.Package) string { |
| return JavaPkgName(g.JavaPkg, pkg) |
| } |
| |
| // JavaPkgName returns the Java package name for a Go package |
| // given a pkg prefix. If the prefix is empty, "go" is used |
| // instead. |
| func JavaPkgName(pkgPrefix string, pkg *types.Package) string { |
| if pkg == nil { |
| return "go" |
| } |
| s := javaNameReplacer(pkg.Name()) |
| if pkgPrefix == "" { |
| return s |
| } |
| return pkgPrefix + "." + s |
| } |
| |
| func (g *JavaGen) className() string { |
| return JavaClassName(g.Pkg) |
| } |
| |
| // JavaClassName returns the name of the Java class that |
| // contains Go package level identifiers. |
| func JavaClassName(pkg *types.Package) string { |
| if pkg == nil { |
| return "Universe" |
| } |
| return javaNameReplacer(strings.Title(pkg.Name())) |
| } |
| |
| func (g *JavaGen) genConst(o *types.Const) { |
| if _, ok := o.Type().(*types.Basic); !ok || !g.isSupported(o.Type()) { |
| g.Printf("// skipped const %s with unsupported type: %s\n\n", o.Name(), o.Type()) |
| return |
| } |
| // TODO(hyangah): should const names use upper cases + "_"? |
| // TODO(hyangah): check invalid names. |
| jType := g.javaType(o.Type()) |
| val := o.Val().ExactString() |
| switch b := o.Type().(*types.Basic); b.Kind() { |
| case types.Int64, types.UntypedInt: |
| i, exact := constant.Int64Val(o.Val()) |
| if !exact { |
| g.errorf("const value %s for %s cannot be represented as %s", val, o.Name(), jType) |
| return |
| } |
| val = fmt.Sprintf("%dL", i) |
| |
| case types.Float32: |
| f, _ := constant.Float32Val(o.Val()) |
| val = fmt.Sprintf("%gf", f) |
| |
| case types.Float64, types.UntypedFloat: |
| f, _ := constant.Float64Val(o.Val()) |
| if math.IsInf(f, 0) || math.Abs(f) > math.MaxFloat64 { |
| g.errorf("const value %s for %s cannot be represented as %s", val, o.Name(), jType) |
| return |
| } |
| val = fmt.Sprintf("%g", f) |
| } |
| g.javadoc(g.docs[o.Name()].Doc()) |
| g.Printf("public static final %s %s = %s;\n", g.javaType(o.Type()), o.Name(), val) |
| } |
| |
| func (g *JavaGen) genJNIField(o *types.TypeName, f *types.Var) { |
| if t := f.Type(); !g.isSupported(t) { |
| g.Printf("// skipped field %s with unsupported type: %s\n\n", o.Name(), t) |
| return |
| } |
| n := java.JNIMangle(g.javaTypeName(o.Name())) |
| // setter |
| g.Printf("JNIEXPORT void JNICALL\n") |
| g.Printf("Java_%s_%s_set%s(JNIEnv *env, jobject this, %s v) {\n", g.jniPkgName(), n, java.JNIMangle(f.Name()), g.jniType(f.Type())) |
| g.Indent() |
| g.Printf("int32_t o = go_seq_to_refnum_go(env, this);\n") |
| g.genJavaToC("v", f.Type(), modeRetained) |
| g.Printf("proxy%s_%s_%s_Set(o, _v);\n", g.pkgPrefix, o.Name(), f.Name()) |
| g.genRelease("v", f.Type(), modeRetained) |
| g.Outdent() |
| g.Printf("}\n\n") |
| |
| // getter |
| g.Printf("JNIEXPORT %s JNICALL\n", g.jniType(f.Type())) |
| g.Printf("Java_%s_%s_get%s(JNIEnv *env, jobject this) {\n", g.jniPkgName(), n, java.JNIMangle(f.Name())) |
| g.Indent() |
| g.Printf("int32_t o = go_seq_to_refnum_go(env, this);\n") |
| g.Printf("%s r0 = ", g.cgoType(f.Type())) |
| g.Printf("proxy%s_%s_%s_Get(o);\n", g.pkgPrefix, o.Name(), f.Name()) |
| g.genCToJava("_r0", "r0", f.Type(), modeRetained) |
| g.Printf("return _r0;\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func (g *JavaGen) genJNIVar(o *types.Var) { |
| if t := o.Type(); !g.isSupported(t) { |
| g.Printf("// skipped variable %s with unsupported type: %s\n\n", o.Name(), t) |
| return |
| } |
| n := java.JNIMangle(g.javaTypeName(o.Name())) |
| // setter |
| g.Printf("JNIEXPORT void JNICALL\n") |
| g.Printf("Java_%s_%s_set%s(JNIEnv *env, jclass clazz, %s v) {\n", g.jniPkgName(), g.className(), n, g.jniType(o.Type())) |
| g.Indent() |
| g.genJavaToC("v", o.Type(), modeRetained) |
| g.Printf("var_set%s_%s(_v);\n", g.pkgPrefix, o.Name()) |
| g.genRelease("v", o.Type(), modeRetained) |
| g.Outdent() |
| g.Printf("}\n\n") |
| |
| // getter |
| g.Printf("JNIEXPORT %s JNICALL\n", g.jniType(o.Type())) |
| g.Printf("Java_%s_%s_get%s(JNIEnv *env, jclass clazz) {\n", g.jniPkgName(), g.className(), n) |
| g.Indent() |
| g.Printf("%s r0 = ", g.cgoType(o.Type())) |
| g.Printf("var_get%s_%s();\n", g.pkgPrefix, o.Name()) |
| g.genCToJava("_r0", "r0", o.Type(), modeRetained) |
| g.Printf("return _r0;\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func (g *JavaGen) genJNIConstructor(f *types.Func, sName string) { |
| if !g.isSigSupported(f.Type()) { |
| return |
| } |
| sig := f.Type().(*types.Signature) |
| res := sig.Results() |
| |
| g.Printf("JNIEXPORT jint JNICALL\n") |
| g.Printf("Java_%s_%s_%s(JNIEnv *env, jclass clazz", g.jniPkgName(), java.JNIMangle(g.javaTypeName(sName)), java.JNIMangle("__"+f.Name())) |
| params := sig.Params() |
| for i := 0; i < params.Len(); i++ { |
| v := params.At(i) |
| jt := g.jniType(v.Type()) |
| g.Printf(", %s %s", jt, g.paramName(params, i)) |
| } |
| g.Printf(") {\n") |
| g.Indent() |
| for i := 0; i < params.Len(); i++ { |
| name := g.paramName(params, i) |
| g.genJavaToC(name, params.At(i).Type(), modeTransient) |
| } |
| // Constructors always return a mandatory *T and an optional error |
| if res.Len() == 1 { |
| g.Printf("int32_t refnum = proxy%s__%s(", g.pkgPrefix, f.Name()) |
| } else { |
| g.Printf("struct proxy%s__%s_return res = proxy%s__%s(", g.pkgPrefix, f.Name(), g.pkgPrefix, f.Name()) |
| } |
| for i := 0; i < params.Len(); i++ { |
| if i > 0 { |
| g.Printf(", ") |
| } |
| g.Printf("_%s", g.paramName(params, i)) |
| } |
| g.Printf(");\n") |
| for i := 0; i < params.Len(); i++ { |
| g.genRelease(g.paramName(params, i), params.At(i).Type(), modeTransient) |
| } |
| // Extract multi returns and handle errors |
| if res.Len() == 2 { |
| g.Printf("int32_t refnum = res.r0;\n") |
| g.genCToJava("_err", "res.r1", res.At(1).Type(), modeRetained) |
| g.Printf("go_seq_maybe_throw_exception(env, _err);\n") |
| } |
| g.Printf("return refnum;\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func (g *JavaGen) genJNIFunc(o *types.Func, sName string, jm *java.Func, proxy, isjava bool) { |
| if !g.isSigSupported(o.Type()) { |
| n := o.Name() |
| if sName != "" { |
| n = sName + "." + n |
| } |
| g.Printf("// skipped function %s with unsupported parameter or return types\n\n", n) |
| return |
| } |
| g.genJNIFuncSignature(o, sName, jm, proxy, isjava) |
| |
| g.Printf(" {\n") |
| g.Indent() |
| g.genJNIFuncBody(o, sName, jm, isjava) |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func (g *JavaGen) genJNIFuncBody(o *types.Func, sName string, jm *java.Func, isjava bool) { |
| sig := o.Type().(*types.Signature) |
| res := sig.Results() |
| if sName != "" { |
| g.Printf("int32_t o = go_seq_to_refnum_go(env, __this__);\n") |
| } |
| params := sig.Params() |
| first := 0 |
| if isjava && params.Len() > 0 && params.At(0).Name() == "this" { |
| // Start after the implicit this argument. |
| first = 1 |
| g.Printf("int32_t _%s = go_seq_to_refnum(env, __this__);\n", g.paramName(params, 0)) |
| } |
| for i := first; i < params.Len(); i++ { |
| name := g.paramName(params, i) |
| g.genJavaToC(name, params.At(i).Type(), modeTransient) |
| } |
| resPrefix := "" |
| if res.Len() > 0 { |
| if res.Len() == 1 { |
| g.Printf("%s r0 = ", g.cgoType(res.At(0).Type())) |
| } else { |
| resPrefix = "res." |
| g.Printf("struct proxy%s_%s_%s_return res = ", g.pkgPrefix, sName, o.Name()) |
| } |
| } |
| g.Printf("proxy%s_%s_%s(", g.pkgPrefix, sName, o.Name()) |
| if sName != "" { |
| g.Printf("o") |
| } |
| // Pass all arguments, including the implicit this argument. |
| for i := 0; i < params.Len(); i++ { |
| if i > 0 || sName != "" { |
| g.Printf(", ") |
| } |
| g.Printf("_%s", g.paramName(params, i)) |
| } |
| g.Printf(");\n") |
| for i := first; i < params.Len(); i++ { |
| g.genRelease(g.paramName(params, i), params.At(i).Type(), modeTransient) |
| } |
| for i := 0; i < res.Len(); i++ { |
| tn := fmt.Sprintf("_r%d", i) |
| t := res.At(i).Type() |
| g.genCToJava(tn, fmt.Sprintf("%sr%d", resPrefix, i), t, modeRetained) |
| } |
| // Go backwards so that any exception is thrown before |
| // the return. |
| for i := res.Len() - 1; i >= 0; i-- { |
| t := res.At(i).Type() |
| if !isErrorType(t) { |
| g.Printf("return _r%d;\n", i) |
| } else { |
| g.Printf("go_seq_maybe_throw_exception(env, _r%d);\n", i) |
| } |
| } |
| } |
| |
| // genRelease cleans up arguments that weren't copied in genJavaToC. |
| func (g *JavaGen) genRelease(varName string, t types.Type, mode varMode) { |
| switch t := t.(type) { |
| case *types.Basic: |
| case *types.Slice: |
| switch e := t.Elem().(type) { |
| case *types.Basic: |
| switch e.Kind() { |
| case types.Uint8: // Byte. |
| if mode == modeTransient { |
| g.Printf("go_seq_release_byte_array(env, %s, _%s.ptr);\n", varName, varName) |
| } |
| } |
| } |
| } |
| } |
| |
| func (g *JavaGen) genMethodInterfaceProxy(oName string, m *types.Func) { |
| if !g.isSigSupported(m.Type()) { |
| g.Printf("// skipped method %s with unsupported parameter or return types\n\n", oName) |
| return |
| } |
| sig := m.Type().(*types.Signature) |
| params := sig.Params() |
| res := sig.Results() |
| g.genInterfaceMethodSignature(m, oName, false, g.paramName) |
| g.Indent() |
| g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", params.Len()) |
| g.Printf("jobject o = go_seq_from_refnum(env, refnum, proxy_class_%s_%s, proxy_class_%s_%s_cons);\n", g.pkgPrefix, oName, g.pkgPrefix, oName) |
| for i := 0; i < params.Len(); i++ { |
| pn := g.paramName(params, i) |
| g.genCToJava("_"+pn, pn, params.At(i).Type(), modeTransient) |
| } |
| if res.Len() > 0 && !isErrorType(res.At(0).Type()) { |
| t := res.At(0).Type() |
| g.Printf("%s res = (*env)->Call%sMethod(env, o, ", g.jniType(t), g.jniCallType(t)) |
| } else { |
| g.Printf("(*env)->CallVoidMethod(env, o, ") |
| } |
| g.Printf("mid_%s_%s", oName, m.Name()) |
| for i := 0; i < params.Len(); i++ { |
| g.Printf(", _%s", g.paramName(params, i)) |
| } |
| g.Printf(");\n") |
| var retName string |
| if res.Len() > 0 { |
| t := res.At(0).Type() |
| if res.Len() == 2 || isErrorType(t) { |
| g.Printf("jobject exc = go_seq_get_exception(env);\n") |
| errType := types.Universe.Lookup("error").Type() |
| g.genJavaToC("exc", errType, modeRetained) |
| retName = "_exc" |
| } |
| if !isErrorType(t) { |
| if res.Len() == 2 { |
| g.genCRetClear("res", t, "exc") |
| } |
| g.genJavaToC("res", t, modeRetained) |
| retName = "_res" |
| } |
| |
| if res.Len() > 1 { |
| g.Printf("cproxy%s_%s_%s_return sres = {\n", g.pkgPrefix, oName, m.Name()) |
| g.Printf(" _res, _exc\n") |
| g.Printf("};\n") |
| retName = "sres" |
| } |
| } |
| g.Printf("go_seq_pop_local_frame(env);\n") |
| if retName != "" { |
| g.Printf("return %s;\n", retName) |
| } |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func (g *JavaGen) GenH() error { |
| pkgPath := "" |
| if g.Pkg != nil { |
| pkgPath = g.Pkg.Path() |
| } |
| g.Printf(hPreamble, g.gobindOpts(), pkgPath, g.className()) |
| for _, iface := range g.interfaces { |
| g.Printf("extern jclass proxy_class_%s_%s;\n", g.pkgPrefix, iface.obj.Name()) |
| g.Printf("extern jmethodID proxy_class_%s_%s_cons;\n", g.pkgPrefix, iface.obj.Name()) |
| g.Printf("\n") |
| for _, m := range iface.summary.callable { |
| if !g.isSigSupported(m.Type()) { |
| g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", iface.obj.Name(), m.Name()) |
| continue |
| } |
| g.genInterfaceMethodSignature(m, iface.obj.Name(), true, g.paramName) |
| g.Printf("\n") |
| } |
| } |
| for _, s := range g.structs { |
| g.Printf("extern jclass proxy_class_%s_%s;\n", g.pkgPrefix, s.obj.Name()) |
| g.Printf("extern jmethodID proxy_class_%s_%s_cons;\n", g.pkgPrefix, s.obj.Name()) |
| } |
| g.Printf("#endif\n") |
| if len(g.err) > 0 { |
| return g.err |
| } |
| return nil |
| } |
| |
| func (g *JavaGen) jniCallType(t types.Type) string { |
| switch t := t.(type) { |
| case *types.Basic: |
| switch t.Kind() { |
| case types.Bool, types.UntypedBool: |
| return "Boolean" |
| case types.Int: |
| return "Long" |
| case types.Int8, types.Uint8: // types.Byte |
| return "Byte" |
| case types.Int16: |
| return "Short" |
| case types.Int32, types.UntypedRune: // types.Rune |
| return "Int" |
| case types.Int64, types.UntypedInt: |
| return "Long" |
| case types.Float32: |
| return "Float" |
| case types.Float64, types.UntypedFloat: |
| return "Double" |
| case types.String, types.UntypedString: |
| return "Object" |
| default: |
| g.errorf("unsupported basic type: %s", t) |
| } |
| case *types.Slice: |
| return "Object" |
| case *types.Pointer: |
| if _, ok := t.Elem().(*types.Named); ok { |
| return g.jniCallType(t.Elem()) |
| } |
| g.errorf("unsupported pointer to type: %s", t) |
| case *types.Named: |
| return "Object" |
| default: |
| return "Object" |
| } |
| return "TODO" |
| } |
| |
| func (g *JavaGen) jniClassSigPrefix(pkg *types.Package) string { |
| return strings.Replace(g.javaPkgName(pkg), ".", "/", -1) + "/" |
| } |
| |
| func (g *JavaGen) jniSigType(T types.Type) string { |
| if isErrorType(T) { |
| return "Ljava/lang/Exception;" |
| } |
| switch T := T.(type) { |
| case *types.Basic: |
| switch T.Kind() { |
| case types.Bool, types.UntypedBool: |
| return "Z" |
| case types.Int: |
| return "J" |
| case types.Int8: |
| return "B" |
| case types.Int16: |
| return "S" |
| case types.Int32, types.UntypedRune: // types.Rune |
| return "I" |
| case types.Int64, types.UntypedInt: |
| return "J" |
| case types.Uint8: // types.Byte |
| return "B" |
| case types.Float32: |
| return "F" |
| case types.Float64, types.UntypedFloat: |
| return "D" |
| case types.String, types.UntypedString: |
| return "Ljava/lang/String;" |
| default: |
| g.errorf("unsupported basic type: %s", T) |
| return "TODO" |
| } |
| case *types.Slice: |
| return "[" + g.jniSigType(T.Elem()) |
| case *types.Pointer: |
| if _, ok := T.Elem().(*types.Named); ok { |
| return g.jniSigType(T.Elem()) |
| } |
| g.errorf("unsupported pointer to type: %s", T) |
| case *types.Named: |
| return "L" + g.jniClassSigPrefix(T.Obj().Pkg()) + g.javaTypeName(T.Obj().Name()) + ";" |
| default: |
| g.errorf("unsupported jniType: %#+v, %s\n", T, T) |
| } |
| return "TODO" |
| } |
| |
| func (g *JavaGen) GenC() error { |
| var pkgName, pkgPath string |
| if g.Pkg != nil { |
| pkgName = g.Pkg.Name() |
| pkgPath = g.Pkg.Path() |
| } else { |
| pkgName = "universe" |
| } |
| g.Printf(cPreamble, g.gobindOpts(), pkgPath) |
| g.Printf("#include %q\n", pkgName+".h") |
| if g.Pkg != nil { |
| for _, pkg := range g.Pkg.Imports() { |
| if g.validPkg(pkg) { |
| g.Printf("#include \"%s.h\"\n", pkg.Name()) |
| } |
| } |
| } |
| g.Printf("\n") |
| |
| for _, iface := range g.interfaces { |
| g.Printf("jclass proxy_class_%s_%s;\n", g.pkgPrefix, iface.obj.Name()) |
| g.Printf("jmethodID proxy_class_%s_%s_cons;\n", g.pkgPrefix, iface.obj.Name()) |
| for _, m := range iface.summary.callable { |
| if !g.isSigSupported(m.Type()) { |
| g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", iface.obj.Name(), m.Name()) |
| continue |
| } |
| g.Printf("static jmethodID mid_%s_%s;\n", iface.obj.Name(), m.Name()) |
| } |
| } |
| for _, s := range g.structs { |
| g.Printf("jclass proxy_class_%s_%s;\n", g.pkgPrefix, s.obj.Name()) |
| g.Printf("jmethodID proxy_class_%s_%s_cons;\n", g.pkgPrefix, s.obj.Name()) |
| } |
| g.Printf("\n") |
| g.Printf("JNIEXPORT void JNICALL\n") |
| g.Printf("Java_%s_%s__1init(JNIEnv *env, jclass _unused) {\n", g.jniPkgName(), java.JNIMangle(g.className())) |
| g.Indent() |
| g.Printf("jclass clazz;\n") |
| for _, s := range g.structs { |
| if jinf, ok := g.jstructs[s.obj]; ok { |
| // Leave the class and constructor NULL for Java classes with no |
| // default constructor. |
| if !jinf.genNoargCon { |
| continue |
| } |
| } |
| g.Printf("clazz = (*env)->FindClass(env, %q);\n", g.jniClassSigPrefix(s.obj.Pkg())+g.javaTypeName(s.obj.Name())) |
| g.Printf("proxy_class_%s_%s = (*env)->NewGlobalRef(env, clazz);\n", g.pkgPrefix, s.obj.Name()) |
| g.Printf("proxy_class_%s_%s_cons = (*env)->GetMethodID(env, clazz, \"<init>\", \"(I)V\");\n", g.pkgPrefix, s.obj.Name()) |
| } |
| for _, iface := range g.interfaces { |
| pkg := iface.obj.Pkg() |
| g.Printf("clazz = (*env)->FindClass(env, %q);\n", g.jniClassSigPrefix(pkg)+JavaClassName(pkg)+"$proxy"+iface.obj.Name()) |
| g.Printf("proxy_class_%s_%s = (*env)->NewGlobalRef(env, clazz);\n", g.pkgPrefix, iface.obj.Name()) |
| g.Printf("proxy_class_%s_%s_cons = (*env)->GetMethodID(env, clazz, \"<init>\", \"(I)V\");\n", g.pkgPrefix, iface.obj.Name()) |
| if isErrorType(iface.obj.Type()) { |
| // As a special case, Java Exceptions are passed to Go pretending to implement the Go error interface. |
| // To complete the illusion, use the Throwable.getMessage method for proxied calls to the error.Error method. |
| g.Printf("clazz = (*env)->FindClass(env, \"java/lang/Throwable\");\n") |
| g.Printf("mid_error_Error = (*env)->GetMethodID(env, clazz, \"getMessage\", \"()Ljava/lang/String;\");\n") |
| continue |
| } |
| g.Printf("clazz = (*env)->FindClass(env, %q);\n", g.jniClassSigPrefix(pkg)+g.javaTypeName(iface.obj.Name())) |
| for _, m := range iface.summary.callable { |
| if !g.isSigSupported(m.Type()) { |
| g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", iface.obj.Name(), m.Name()) |
| continue |
| } |
| sig := m.Type().(*types.Signature) |
| res := sig.Results() |
| retSig := "V" |
| if res.Len() > 0 { |
| if t := res.At(0).Type(); !isErrorType(t) { |
| retSig = g.jniSigType(t) |
| } |
| } |
| var jniParams string |
| params := sig.Params() |
| for i := 0; i < params.Len(); i++ { |
| jniParams += g.jniSigType(params.At(i).Type()) |
| } |
| g.Printf("mid_%s_%s = (*env)->GetMethodID(env, clazz, %q, \"(%s)%s\");\n", |
| iface.obj.Name(), m.Name(), javaNameReplacer(lowerFirst(m.Name())), jniParams, retSig) |
| } |
| g.Printf("\n") |
| } |
| g.Outdent() |
| g.Printf("}\n\n") |
| for _, f := range g.funcs { |
| g.genJNIFunc(f, "", nil, false, false) |
| } |
| for _, s := range g.structs { |
| sName := s.obj.Name() |
| cons := g.constructors[s.obj] |
| jinf := g.jstructs[s.obj] |
| for _, f := range cons { |
| g.genJNIConstructor(f, sName) |
| } |
| if len(cons) == 0 && (jinf == nil || jinf.genNoargCon) { |
| g.Printf("JNIEXPORT jint JNICALL\n") |
| g.Printf("Java_%s_%s_%s(JNIEnv *env, jclass clazz) {\n", g.jniPkgName(), java.JNIMangle(g.javaTypeName(sName)), java.JNIMangle("__New")) |
| g.Indent() |
| g.Printf("return new_%s_%s();\n", g.pkgPrefix, sName) |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| for _, m := range exportedMethodSet(types.NewPointer(s.obj.Type())) { |
| var jm *java.Func |
| if jinf != nil { |
| jm = jinf.lookupMethod(m, g.hasThis(s.obj.Name(), m)) |
| } |
| g.genJNIFunc(m, sName, jm, false, jinf != nil) |
| } |
| for _, f := range exportedFields(s.t) { |
| g.genJNIField(s.obj, f) |
| } |
| } |
| for _, iface := range g.interfaces { |
| for _, m := range iface.summary.callable { |
| g.genJNIFunc(m, iface.obj.Name(), nil, true, false) |
| g.genMethodInterfaceProxy(iface.obj.Name(), m) |
| } |
| } |
| for _, v := range g.vars { |
| g.genJNIVar(v) |
| } |
| if len(g.err) > 0 { |
| return g.err |
| } |
| return nil |
| } |
| |
| func (g *JavaGen) GenJava() error { |
| pkgPath := "" |
| if g.Pkg != nil { |
| pkgPath = g.Pkg.Path() |
| } |
| g.Printf(javaPreamble, g.javaPkgName(g.Pkg), g.className(), g.gobindOpts(), pkgPath) |
| |
| g.Printf("public abstract class %s {\n", g.className()) |
| g.Indent() |
| g.Printf("static {\n") |
| g.Indent() |
| g.Printf("Seq.touch(); // for loading the native library\n") |
| if g.Pkg != nil { |
| for _, p := range g.Pkg.Imports() { |
| if g.validPkg(p) { |
| g.Printf("%s.%s.touch();\n", g.javaPkgName(p), JavaClassName(p)) |
| } |
| } |
| } |
| g.Printf("_init();\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| g.Printf("private %s() {} // uninstantiable\n\n", g.className()) |
| g.Printf("// touch is called from other bound packages to initialize this package\n") |
| g.Printf("public static void touch() {}\n\n") |
| g.Printf("private static native void _init();\n\n") |
| |
| for _, iface := range g.interfaces { |
| n := iface.obj.Name() |
| g.Printf("private static final class proxy%s", n) |
| if isErrorType(iface.obj.Type()) { |
| g.Printf(" extends Exception") |
| } |
| g.Printf(" implements Seq.Proxy, %s {\n", g.javaTypeName(n)) |
| g.Indent() |
| g.genProxyImpl("proxy" + n) |
| g.Printf("proxy%s(int refnum) { this.refnum = refnum; Seq.trackGoRef(refnum, this); }\n\n", n) |
| |
| if isErrorType(iface.obj.Type()) { |
| g.Printf("@Override public String getMessage() { return error(); }\n\n") |
| } |
| for _, m := range iface.summary.callable { |
| if !g.isSigSupported(m.Type()) { |
| g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", n, m.Name()) |
| continue |
| } |
| g.Printf("public native ") |
| g.genFuncSignature(m, nil, false) |
| } |
| |
| g.Outdent() |
| g.Printf("}\n") |
| } |
| |
| g.Printf("\n") |
| |
| for _, c := range g.constants { |
| g.genConst(c) |
| } |
| g.Printf("\n") |
| for _, v := range g.vars { |
| g.genVar(v) |
| } |
| for _, f := range g.funcs { |
| if !g.isSigSupported(f.Type()) { |
| g.Printf("// skipped function %s with unsupported parameter or return types\n\n", f.Name()) |
| continue |
| } |
| g.Printf("public static native ") |
| g.genFuncSignature(f, nil, false) |
| } |
| |
| g.Outdent() |
| g.Printf("}\n") |
| |
| if len(g.err) > 0 { |
| return g.err |
| } |
| return nil |
| } |
| |
| // embeddedJavaClasses returns the possible empty list of Java types embedded |
| // in the given struct type. |
| func embeddedJavaClasses(t *types.Struct) []string { |
| clsSet := make(map[string]struct{}) |
| var classes []string |
| for i := 0; i < t.NumFields(); i++ { |
| f := t.Field(i) |
| if !f.Exported() { |
| continue |
| } |
| if t := f.Type(); isJavaType(t) { |
| cls := classNameFor(t) |
| if _, exists := clsSet[cls]; !exists { |
| clsSet[cls] = struct{}{} |
| classes = append(classes, cls) |
| } |
| } |
| } |
| return classes |
| } |
| |
| func classNameFor(t types.Type) string { |
| obj := t.(*types.Named).Obj() |
| pkg := obj.Pkg() |
| return strings.Replace(pkg.Path()[len("Java/"):], "/", ".", -1) + "." + obj.Name() |
| } |
| |
| func isJavaType(t types.Type) bool { |
| return typePkgFirstElem(t) == "Java" |
| } |
| |
| const ( |
| javaPreamble = `// Java class %[1]s.%[2]s is a proxy for talking to a Go program. |
| // gobind %[3]s %[4]s |
| // |
| // File is generated by gobind. Do not edit. |
| package %[1]s; |
| |
| import go.Seq; |
| |
| ` |
| cPreamble = `// JNI functions for the Go <=> Java bridge. |
| // gobind %[1]s %[2]s |
| // |
| // File is generated by gobind. Do not edit. |
| |
| #include <android/log.h> |
| #include <stdint.h> |
| #include "seq.h" |
| #include "_cgo_export.h" |
| ` |
| |
| hPreamble = `// JNI function headers for the Go <=> Java bridge. |
| // gobind %[1]s %[2]s |
| // |
| // File is generated by gobind. Do not edit. |
| |
| #ifndef __%[3]s_H__ |
| #define __%[3]s_H__ |
| |
| #include <jni.h> |
| |
| ` |
| ) |