Accept Java API interface types as arguments and return values from bound Go package functions and methods. Also, allow Go structs to extend Java classes and implement Java interfaces as well as override and implement methods. This is the third and final part of the implementation of the golang/go#16876 proposal. Fixes golang/go#16876 Change-Id: I6951dd87235553ce09abe5117a39a503466163c0 Reviewed-on: https://go-review.googlesource.com/28597 Reviewed-by: David Crawshaw <crawshaw@golang.org>
540 lines
15 KiB
Go
540 lines
15 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 (
|
|
"bytes"
|
|
"fmt"
|
|
"go/types"
|
|
"strings"
|
|
)
|
|
|
|
type goGen struct {
|
|
*Generator
|
|
imports map[string]struct{}
|
|
}
|
|
|
|
const (
|
|
goPreamble = `// Package gomobile_bind is an autogenerated binder stub for package %[1]s.
|
|
// gobind -lang=go %[2]s
|
|
//
|
|
// File is generated by gobind. Do not edit.
|
|
package gomobile_bind
|
|
|
|
/*
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include "seq.h"
|
|
#include "%[1]s.h"
|
|
|
|
*/
|
|
import "C"
|
|
|
|
`
|
|
)
|
|
|
|
func (g *goGen) genFuncBody(o *types.Func, selectorLHS string) {
|
|
sig := o.Type().(*types.Signature)
|
|
params := sig.Params()
|
|
for i := 0; i < params.Len(); i++ {
|
|
p := params.At(i)
|
|
pn := "param_" + paramName(params, i)
|
|
g.genRead("_"+pn, pn, p.Type(), modeTransient)
|
|
}
|
|
|
|
res := sig.Results()
|
|
if res.Len() > 2 || res.Len() == 2 && !isErrorType(res.At(1).Type()) {
|
|
g.errorf("functions and methods must return either zero or one values, and optionally an error")
|
|
return
|
|
}
|
|
if res.Len() > 0 {
|
|
for i := 0; i < res.Len(); i++ {
|
|
if i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("res_%d", i)
|
|
}
|
|
g.Printf(" := ")
|
|
}
|
|
|
|
g.Printf("%s%s(", selectorLHS, o.Name())
|
|
for i := 0; i < params.Len(); i++ {
|
|
if i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("_param_%s", paramName(params, i))
|
|
}
|
|
g.Printf(")\n")
|
|
|
|
for i := 0; i < res.Len(); i++ {
|
|
pn := fmt.Sprintf("res_%d", i)
|
|
g.genWrite("_"+pn, pn, res.At(i).Type(), modeRetained)
|
|
}
|
|
if res.Len() > 0 {
|
|
g.Printf("return ")
|
|
for i := 0; i < res.Len(); i++ {
|
|
if i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("_res_%d", i)
|
|
}
|
|
g.Printf("\n")
|
|
}
|
|
}
|
|
|
|
func (g *goGen) genWrite(toVar, fromVar string, t types.Type, mode varMode) {
|
|
switch t := t.(type) {
|
|
case *types.Basic:
|
|
switch t.Kind() {
|
|
case types.String:
|
|
g.Printf("%s := encodeString(%s)\n", toVar, fromVar)
|
|
case types.Bool:
|
|
g.Printf("var %s C.%s = 0\n", toVar, g.cgoType(t))
|
|
g.Printf("if %s { %s = 1 }\n", fromVar, toVar)
|
|
default:
|
|
g.Printf("%s := C.%s(%s)\n", toVar, g.cgoType(t), fromVar)
|
|
}
|
|
case *types.Slice:
|
|
switch e := t.Elem().(type) {
|
|
case *types.Basic:
|
|
switch e.Kind() {
|
|
case types.Uint8: // Byte.
|
|
g.Printf("%s := fromSlice(%s, %v)\n", toVar, fromVar, 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.genToRefNum(toVar, fromVar)
|
|
default:
|
|
g.errorf("unsupported type %s", t)
|
|
}
|
|
case *types.Named:
|
|
switch u := t.Underlying().(type) {
|
|
case *types.Interface, *types.Pointer:
|
|
g.genToRefNum(toVar, fromVar)
|
|
default:
|
|
g.errorf("unsupported, direct named type %s: %s", t, u)
|
|
}
|
|
default:
|
|
g.errorf("unsupported type %s", t)
|
|
}
|
|
}
|
|
|
|
// genToRefNum generates Go code for converting a variable to its refnum.
|
|
// Note that the nil-check cannot be lifted into seq.ToRefNum, because a nil
|
|
// struct pointer does not convert to a nil interface.
|
|
func (g *goGen) genToRefNum(toVar, fromVar string) {
|
|
g.Printf("var %s C.int32_t = _seq.NullRefNum\n", toVar)
|
|
g.Printf("if %s != nil {\n", fromVar)
|
|
g.Printf(" %s = C.int32_t(_seq.ToRefNum(%s))\n", toVar, fromVar)
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
func (g *goGen) genFuncSignature(o *types.Func, objName string) {
|
|
g.Printf("//export proxy%s_%s_%s\n", g.pkgPrefix, objName, o.Name())
|
|
g.Printf("func proxy%s_%s_%s(", g.pkgPrefix, objName, o.Name())
|
|
if objName != "" {
|
|
g.Printf("refnum C.int32_t")
|
|
}
|
|
sig := o.Type().(*types.Signature)
|
|
params := sig.Params()
|
|
for i := 0; i < params.Len(); i++ {
|
|
if objName != "" || i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
p := params.At(i)
|
|
g.Printf("param_%s C.%s", paramName(params, i), g.cgoType(p.Type()))
|
|
}
|
|
g.Printf(") ")
|
|
res := sig.Results()
|
|
if res.Len() > 0 {
|
|
g.Printf("(")
|
|
for i := 0; i < res.Len(); i++ {
|
|
if i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("C.%s", g.cgoType(res.At(i).Type()))
|
|
}
|
|
g.Printf(") ")
|
|
}
|
|
g.Printf("{\n")
|
|
}
|
|
|
|
func (g *goGen) genFunc(o *types.Func) {
|
|
if !g.isSigSupported(o.Type()) {
|
|
g.Printf("// skipped function %s with unsupported parameter or result types\n", o.Name())
|
|
return
|
|
}
|
|
g.genFuncSignature(o, "")
|
|
g.Indent()
|
|
g.genFuncBody(o, g.pkgName(g.Pkg))
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (g *goGen) genStruct(obj *types.TypeName, T *types.Struct) {
|
|
fields := exportedFields(T)
|
|
methods := exportedMethodSet(types.NewPointer(obj.Type()))
|
|
|
|
for _, f := range fields {
|
|
if t := f.Type(); !g.isSupported(t) {
|
|
g.Printf("// skipped field %s.%s with unsupported type: %T\n\n", obj.Name(), f.Name(), t)
|
|
continue
|
|
}
|
|
g.Printf("//export proxy%s_%s_%s_Set\n", g.pkgPrefix, obj.Name(), f.Name())
|
|
g.Printf("func proxy%s_%s_%s_Set(refnum C.int32_t, v C.%s) {\n", g.pkgPrefix, obj.Name(), f.Name(), g.cgoType(f.Type()))
|
|
g.Indent()
|
|
g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
|
|
g.genRead("_v", "v", f.Type(), modeRetained)
|
|
g.Printf("ref.Get().(*%s%s).%s = _v\n", g.pkgName(g.Pkg), obj.Name(), f.Name())
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
|
|
g.Printf("//export proxy%s_%s_%s_Get\n", g.pkgPrefix, obj.Name(), f.Name())
|
|
g.Printf("func proxy%s_%s_%s_Get(refnum C.int32_t) C.%s {\n", g.pkgPrefix, obj.Name(), f.Name(), g.cgoType(f.Type()))
|
|
g.Indent()
|
|
g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
|
|
g.Printf("v := ref.Get().(*%s%s).%s\n", g.pkgName(g.Pkg), obj.Name(), f.Name())
|
|
g.genWrite("_v", "v", f.Type(), modeRetained)
|
|
g.Printf("return _v\n")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
for _, m := range methods {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
g.genFuncSignature(m, obj.Name())
|
|
g.Indent()
|
|
g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
|
|
g.Printf("v := ref.Get().(*%s%s)\n", g.pkgName(g.Pkg), obj.Name())
|
|
g.genFuncBody(m, "v.")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
// Export constructor for ObjC and Java default no-arg constructors
|
|
g.Printf("//export new_%s_%s\n", g.Pkg.Name(), obj.Name())
|
|
g.Printf("func new_%s_%s() C.int32_t {\n", g.Pkg.Name(), obj.Name())
|
|
g.Indent()
|
|
g.Printf("return C.int32_t(_seq.ToRefNum(new(%s%s)))\n", g.pkgName(g.Pkg), obj.Name())
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
func (g *goGen) 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
|
|
}
|
|
// TODO(hyangah): non-struct pointer types (*int), struct type.
|
|
|
|
v := fmt.Sprintf("%s%s", g.pkgName(g.Pkg), o.Name())
|
|
|
|
// var I int
|
|
//
|
|
// func var_setI(v int)
|
|
g.Printf("//export var_set%s_%s\n", g.pkgPrefix, o.Name())
|
|
g.Printf("func var_set%s_%s(v C.%s) {\n", g.pkgPrefix, o.Name(), g.cgoType(o.Type()))
|
|
g.Indent()
|
|
g.genRead("_v", "v", o.Type(), modeRetained)
|
|
g.Printf("%s = _v\n", v)
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
|
|
// func var_getI() int
|
|
g.Printf("//export var_get%s_%s\n", g.pkgPrefix, o.Name())
|
|
g.Printf("func var_get%s_%s() C.%s {\n", g.pkgPrefix, o.Name(), g.cgoType(o.Type()))
|
|
g.Indent()
|
|
g.Printf("v := %s\n", v)
|
|
g.genWrite("_v", "v", o.Type(), modeRetained)
|
|
g.Printf("return _v\n")
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
}
|
|
|
|
func (g *goGen) genInterface(obj *types.TypeName) {
|
|
iface := obj.Type().(*types.Named).Underlying().(*types.Interface)
|
|
|
|
summary := makeIfaceSummary(iface)
|
|
|
|
// Define the entry points.
|
|
for _, m := range summary.callable {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
g.genFuncSignature(m, obj.Name())
|
|
g.Indent()
|
|
g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
|
|
g.Printf("v := ref.Get().(%s%s)\n", g.pkgName(g.Pkg), obj.Name())
|
|
g.genFuncBody(m, "v.")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
// Define a proxy interface.
|
|
if !summary.implementable {
|
|
// The interface defines an unexported method or a method that
|
|
// uses an unexported type. We cannot generate a proxy object
|
|
// for such a type.
|
|
return
|
|
}
|
|
g.Printf("type proxy%s_%s _seq.Ref\n\n", g.pkgPrefix, obj.Name())
|
|
|
|
g.Printf("func (p *proxy%s_%s) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }\n\n", g.pkgPrefix, obj.Name())
|
|
|
|
for _, m := range summary.callable {
|
|
if !g.isSigSupported(m.Type()) {
|
|
g.Printf("// skipped method %s.%s with unsupported parameter or result types\n", obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
sig := m.Type().(*types.Signature)
|
|
params := sig.Params()
|
|
res := sig.Results()
|
|
|
|
if res.Len() > 2 ||
|
|
(res.Len() == 2 && !isErrorType(res.At(1).Type())) {
|
|
g.errorf("functions and methods must return either zero or one value, and optionally an error: %s.%s", obj.Name(), m.Name())
|
|
continue
|
|
}
|
|
|
|
g.Printf("func (p *proxy%s_%s) %s(", g.pkgPrefix, obj.Name(), m.Name())
|
|
for i := 0; i < params.Len(); i++ {
|
|
if i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("param_%s %s", paramName(params, i), g.typeString(params.At(i).Type()))
|
|
}
|
|
g.Printf(") ")
|
|
|
|
if res.Len() == 1 {
|
|
g.Printf(g.typeString(res.At(0).Type()))
|
|
} else if res.Len() == 2 {
|
|
g.Printf("(%s, error)", g.typeString(res.At(0).Type()))
|
|
}
|
|
g.Printf(" {\n")
|
|
g.Indent()
|
|
|
|
for i := 0; i < params.Len(); i++ {
|
|
pn := "param_" + paramName(params, i)
|
|
g.genWrite("_"+pn, pn, params.At(i).Type(), modeTransient)
|
|
}
|
|
|
|
if res.Len() > 0 {
|
|
g.Printf("res := ")
|
|
}
|
|
g.Printf("C.cproxy%s_%s_%s(C.int32_t(p.Bind_proxy_refnum__())", g.pkgPrefix, obj.Name(), m.Name())
|
|
for i := 0; i < params.Len(); i++ {
|
|
g.Printf(", _param_%s", paramName(params, i))
|
|
}
|
|
g.Printf(")\n")
|
|
var retName string
|
|
if res.Len() > 0 {
|
|
if res.Len() == 1 {
|
|
T := res.At(0).Type()
|
|
g.genRead("_res", "res", T, modeRetained)
|
|
retName = "_res"
|
|
} else {
|
|
var rvs []string
|
|
for i := 0; i < res.Len(); i++ {
|
|
rv := fmt.Sprintf("res_%d", i)
|
|
g.genRead(rv, fmt.Sprintf("res.r%d", i), res.At(i).Type(), modeRetained)
|
|
rvs = append(rvs, rv)
|
|
}
|
|
retName = strings.Join(rvs, ", ")
|
|
}
|
|
g.Printf("return %s\n", retName)
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
}
|
|
|
|
func (g *goGen) genRead(toVar, fromVar string, typ types.Type, mode varMode) {
|
|
switch t := typ.(type) {
|
|
case *types.Basic:
|
|
switch t.Kind() {
|
|
case types.String:
|
|
g.Printf("%s := decodeString(%s)\n", toVar, fromVar)
|
|
case types.Bool:
|
|
g.Printf("%s := %s != 0\n", toVar, fromVar)
|
|
default:
|
|
g.Printf("%s := %s(%s)\n", toVar, t.Underlying().String(), fromVar)
|
|
}
|
|
case *types.Slice:
|
|
switch e := t.Elem().(type) {
|
|
case *types.Basic:
|
|
switch e.Kind() {
|
|
case types.Uint8: // Byte.
|
|
g.Printf("%s := toSlice(%s, %v)\n", toVar, fromVar, mode == modeRetained)
|
|
default:
|
|
g.errorf("unsupported type: %s", t)
|
|
}
|
|
default:
|
|
g.errorf("unsupported type: %s", t)
|
|
}
|
|
case *types.Pointer:
|
|
switch u := t.Elem().(type) {
|
|
case *types.Named:
|
|
o := u.Obj()
|
|
oPkg := o.Pkg()
|
|
if !g.validPkg(oPkg) {
|
|
g.errorf("type %s is defined in %s, which is not bound", u, oPkg)
|
|
return
|
|
}
|
|
g.Printf("// Must be a Go object\n")
|
|
g.Printf("%s_ref := _seq.FromRefNum(int32(%s))\n", toVar, fromVar)
|
|
g.Printf("%s := %s_ref.Get().(*%s%s)\n", toVar, toVar, g.pkgName(oPkg), o.Name())
|
|
default:
|
|
g.errorf("unsupported pointer type %s", t)
|
|
}
|
|
case *types.Named:
|
|
switch t.Underlying().(type) {
|
|
case *types.Interface, *types.Pointer:
|
|
hasProxy := true
|
|
if iface, ok := t.Underlying().(*types.Interface); ok {
|
|
hasProxy = makeIfaceSummary(iface).implementable
|
|
}
|
|
isJava := isJavaType(t)
|
|
o := t.Obj()
|
|
oPkg := o.Pkg()
|
|
if !isErrorType(t) && !g.validPkg(oPkg) && !isJava {
|
|
g.errorf("type %s is defined in %s, which is not bound", t, oPkg)
|
|
return
|
|
}
|
|
g.Printf("var %s %s\n", toVar, g.typeString(t))
|
|
g.Printf("%s_ref := _seq.FromRefNum(int32(%s))\n", toVar, fromVar)
|
|
g.Printf("if %s_ref != nil {\n", toVar)
|
|
g.Printf(" if %s < 0 { // go object \n", fromVar)
|
|
g.Printf(" %s = %s_ref.Get().(%s%s)\n", toVar, toVar, g.pkgName(oPkg), o.Name())
|
|
if hasProxy {
|
|
g.Printf(" } else { // foreign object \n")
|
|
if isJava {
|
|
clsName := flattenName(classNameFor(t))
|
|
g.Printf(" %s = (*proxy_class_%s)(%s_ref)\n", toVar, clsName, toVar)
|
|
} else {
|
|
g.Printf(" %s = (*proxy%s_%s)(%s_ref)\n", toVar, pkgPrefix(oPkg), o.Name(), toVar)
|
|
}
|
|
}
|
|
g.Printf(" }\n")
|
|
g.Printf("}\n")
|
|
default:
|
|
g.errorf("unsupported named type %s", t)
|
|
}
|
|
default:
|
|
g.errorf("unsupported type: %s", typ)
|
|
}
|
|
}
|
|
|
|
func (g *goGen) typeString(typ types.Type) string {
|
|
pkg := g.Pkg
|
|
|
|
switch t := typ.(type) {
|
|
case *types.Named:
|
|
obj := t.Obj()
|
|
if obj.Pkg() == nil { // e.g. error type is *types.Named.
|
|
return types.TypeString(typ, types.RelativeTo(pkg))
|
|
}
|
|
oPkg := obj.Pkg()
|
|
if !g.validPkg(oPkg) && !isJavaType(t) {
|
|
g.errorf("type %s is defined in %s, which is not bound", t, oPkg)
|
|
return "TODO"
|
|
}
|
|
|
|
switch t.Underlying().(type) {
|
|
case *types.Interface, *types.Struct:
|
|
return fmt.Sprintf("%s%s", g.pkgName(oPkg), types.TypeString(typ, types.RelativeTo(oPkg)))
|
|
default:
|
|
g.errorf("unsupported named type %s / %T", t, t)
|
|
}
|
|
case *types.Pointer:
|
|
switch t := t.Elem().(type) {
|
|
case *types.Named:
|
|
return fmt.Sprintf("*%s", g.typeString(t))
|
|
default:
|
|
g.errorf("not yet supported, pointer type %s / %T", t, t)
|
|
}
|
|
default:
|
|
return types.TypeString(typ, types.RelativeTo(pkg))
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// genPreamble generates the preamble. It is generated after everything
|
|
// else, where we know which bound packages to import.
|
|
func (g *goGen) genPreamble() {
|
|
pkgName := ""
|
|
pkgPath := ""
|
|
if g.Pkg != nil {
|
|
pkgName = g.Pkg.Name()
|
|
pkgPath = g.Pkg.Path()
|
|
} else {
|
|
pkgName = "universe"
|
|
}
|
|
g.Printf(goPreamble, pkgName, pkgPath)
|
|
g.Printf("import (\n")
|
|
g.Indent()
|
|
g.Printf("_seq \"golang.org/x/mobile/bind/seq\"\n")
|
|
for path := range g.imports {
|
|
g.Printf("%q\n", path)
|
|
}
|
|
g.Outdent()
|
|
g.Printf(")\n\n")
|
|
}
|
|
|
|
func (g *goGen) gen() error {
|
|
g.imports = make(map[string]struct{})
|
|
|
|
// Switch to a temporary buffer so the preamble can be
|
|
// written last.
|
|
oldBuf := g.Printer.Buf
|
|
newBuf := new(bytes.Buffer)
|
|
g.Printer.Buf = newBuf
|
|
g.Printf("// suppress the error if seq ends up unused\n")
|
|
g.Printf("var _ = _seq.FromRefNum\n")
|
|
|
|
for _, s := range g.structs {
|
|
g.genStruct(s.obj, s.t)
|
|
}
|
|
for _, intf := range g.interfaces {
|
|
g.genInterface(intf.obj)
|
|
}
|
|
for _, v := range g.vars {
|
|
g.genVar(v)
|
|
}
|
|
for _, f := range g.funcs {
|
|
g.genFunc(f)
|
|
}
|
|
// Switch to the original buffer, write the preamble
|
|
// and append the rest of the file.
|
|
g.Printer.Buf = oldBuf
|
|
g.genPreamble()
|
|
g.Printer.Buf.Write(newBuf.Bytes())
|
|
if len(g.err) > 0 {
|
|
return g.err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// pkgName retuns the package name and adds the package to the list of
|
|
// imports.
|
|
func (g *goGen) pkgName(pkg *types.Package) string {
|
|
// The error type has no package
|
|
if pkg == nil {
|
|
return ""
|
|
}
|
|
g.imports[pkg.Path()] = struct{}{}
|
|
return pkg.Name() + "."
|
|
}
|