If a Go struct or interface has the same name as its package class, append an underscore to the generated Java class name. Fixes golang/go#23327. Change-Id: Ib680af35c956801073a0effb510a3ed9bbb8b9d1 Reviewed-on: https://go-review.googlesource.com/87656 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
1719 lines
46 KiB
Go
1719 lines
46 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 Seq.Ref ref;\n\n")
|
|
g.Printf("@Override public final int incRefnum() {\n")
|
|
g.Printf(" int refnum = ref.refnum;\n")
|
|
g.Printf(" Seq.incGoRef(refnum);\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(Seq.Ref ref) { this.ref = ref; }\n\n", n)
|
|
if len(cons) == 0 {
|
|
// Generate default no-arg constructor
|
|
g.Printf("public %s() { this.ref = __New(); }\n\n", n)
|
|
g.Printf("private static native Seq.Ref __New();\n\n")
|
|
}
|
|
}
|
|
|
|
for _, f := range fields {
|
|
if t := f.Type(); !g.isSupported(t) {
|
|
g.Printf("// skipped field %s.%s with unsupported type: %T\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.ref = ")
|
|
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.Outdent()
|
|
g.Printf("}\n\n")
|
|
g.Printf("private static native Seq.Ref __%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: %T\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()))
|
|
if strings.HasSuffix(oName, "_") {
|
|
oName += "1" // JNI doesn't like methods ending with underscore, needs the _1 suffixing
|
|
}
|
|
g.Printf(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(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: %T\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.Printf("// skipped const %s with unsupported type: %T\n\n", o.Name(), o)
|
|
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: %T\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, 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, 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: %T\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 jobject 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")
|
|
}
|
|
// Pass no proxy class so that the Seq.Ref is returned instead.
|
|
g.Printf("return go_seq_from_refnum(env, refnum, NULL, NULL);\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(), 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>\", \"(Lgo/Seq$Ref;)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>\", \"(Lgo/Seq$Ref;)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 jobject 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("int32_t refnum = new_%s_%s();\n", g.pkgPrefix, sName)
|
|
// Pass no proxy class so that the Seq.Ref is returned instead.
|
|
g.Printf("return go_seq_from_refnum(env, refnum, NULL, NULL);\n")
|
|
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(Seq.Ref ref) { this.ref = ref; }\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>
|
|
|
|
`
|
|
)
|