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