bind_test.go compares the generated Go files against golden files checked in the repository. The bind package formats some of the generated Go files, so any changes in the go formatter can break the tests. This change makes the test more robust by applying formatting based on the currently used go version. Since a golden file often includes multiple go files generated by the bind, the `gofmt` function splits the golden file using the gobindPreamble marker and then run format.Source for each chunk. In order to ease the golden file splitting, this CL also moves the gobindPreamble to the beginning of each generated file consistently. It turned out bind omits formatting for some go files (generated for reverse binding). That needs to be fixed but it is a much bigger fix. Thus, in this CL, we apply the formatting on the bind's output as well. This CL also updates the gobindPreamble to follow the style guide for generated code. https://golang.org/s/generatedcode Fixes golang/go#34619 Change-Id: Ia2957693154face2848e051ebbb2373e95d79593 Reviewed-on: https://go-review.googlesource.com/c/mobile/+/198322 Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
1733 lines
47 KiB
Go
1733 lines
47 KiB
Go
// 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.isConsSigSupported(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")
|
|
}
|
|
|
|
// isConsSigSupported reports whether the generators can handle a given
|
|
// constructor signature.
|
|
func (g *JavaGen) isConsSigSupported(t types.Type) bool {
|
|
if !g.isSigSupported(t) {
|
|
return false
|
|
}
|
|
// Skip constructors taking a single int32 argument
|
|
// since they clash with the proxy constructors that
|
|
// take a refnum.
|
|
params := t.(*types.Signature).Params()
|
|
if params.Len() != 1 {
|
|
return true
|
|
}
|
|
if t, ok := params.At(0).Type().(*types.Basic); ok {
|
|
switch t.Kind() {
|
|
case types.Int32, types.Uint32:
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// 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 reports 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(java.JNIMangle(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(java.JNIMangle(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(), java.JNIMangle(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(), java.JNIMangle(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.isConsSigSupported(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.javadoc(g.docs[f.Name()].Doc())
|
|
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 = gobindPreamble + `// Java class %[1]s.%[2]s is a proxy for talking to a Go program.
|
|
//
|
|
// autogenerated by gobind %[3]s %[4]s
|
|
package %[1]s;
|
|
|
|
import go.Seq;
|
|
|
|
`
|
|
cPreamble = gobindPreamble + `// JNI functions for the Go <=> Java bridge.
|
|
//
|
|
// autogenerated by gobind %[1]s %[2]s
|
|
|
|
#include <android/log.h>
|
|
#include <stdint.h>
|
|
#include "seq.h"
|
|
#include "_cgo_export.h"
|
|
`
|
|
|
|
hPreamble = gobindPreamble + `// JNI function headers for the Go <=> Java bridge.
|
|
//
|
|
// autogenerated by gobind %[1]s %[2]s
|
|
|
|
#ifndef __%[3]s_H__
|
|
#define __%[3]s_H__
|
|
|
|
#include <jni.h>
|
|
|
|
`
|
|
)
|