seq.Transact is called when Go calls a method of a foreign object that implements a Go interface. Currently, we assume that the foreign object has an instance method that can conduct the message routing, so the object id and the method code is sufficient for transact. Passing the interface descriptor (e.g. go.testpkg.I) however allows the bind internal to use non-instance methods to implement the routing. Change-Id: I1f61a04f919fbd09117ea332d678cd50e4861e46 Reviewed-on: https://go-review.googlesource.com/12685 Reviewed-by: David Crawshaw <crawshaw@golang.org>
450 lines
12 KiB
Go
450 lines
12 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/token"
|
|
"log"
|
|
"strings"
|
|
|
|
"golang.org/x/tools/go/types"
|
|
)
|
|
|
|
type goGen struct {
|
|
*printer
|
|
fset *token.FileSet
|
|
pkg *types.Package
|
|
err ErrorList
|
|
}
|
|
|
|
func (g *goGen) errorf(format string, args ...interface{}) {
|
|
g.err = append(g.err, fmt.Errorf(format, args...))
|
|
}
|
|
|
|
const goPreamble = `// Package go_%s is an autogenerated binder stub for package %s.
|
|
// gobind -lang=go %s
|
|
//
|
|
// File is generated by gobind. Do not edit.
|
|
package go_%s
|
|
|
|
import (
|
|
"golang.org/x/mobile/bind/seq"
|
|
%q
|
|
)
|
|
|
|
`
|
|
|
|
func (g *goGen) genPreamble() {
|
|
n := g.pkg.Name()
|
|
g.Printf(goPreamble, n, n, g.pkg.Path(), n, g.pkg.Path())
|
|
}
|
|
|
|
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)
|
|
g.genRead("param_"+paramName(params, i), "in", p.Type())
|
|
}
|
|
|
|
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
|
|
}
|
|
returnsValue := false
|
|
returnsError := false
|
|
if res.Len() == 1 {
|
|
if isErrorType(res.At(0).Type()) {
|
|
returnsError = true
|
|
g.Printf("err := ")
|
|
} else {
|
|
returnsValue = true
|
|
g.Printf("res := ")
|
|
}
|
|
} else if res.Len() == 2 {
|
|
returnsValue = true
|
|
returnsError = true
|
|
g.Printf("res, err := ")
|
|
}
|
|
|
|
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")
|
|
|
|
if returnsValue {
|
|
g.genWrite("res", "out", res.At(0).Type())
|
|
}
|
|
if returnsError {
|
|
g.genWrite("err", "out", res.At(res.Len()-1).Type())
|
|
}
|
|
}
|
|
|
|
func (g *goGen) genWrite(valName, seqName string, T types.Type) {
|
|
if isErrorType(T) {
|
|
g.Printf("if %s == nil {\n", valName)
|
|
g.Printf(" %s.WriteString(\"\");\n", seqName)
|
|
g.Printf("} else {\n")
|
|
g.Printf(" %s.WriteString(%s.Error());\n", seqName, valName)
|
|
g.Printf("}\n")
|
|
return
|
|
}
|
|
switch T := T.(type) {
|
|
case *types.Pointer:
|
|
// TODO(crawshaw): test *int
|
|
// TODO(crawshaw): test **Generator
|
|
switch T := T.Elem().(type) {
|
|
case *types.Named:
|
|
obj := T.Obj()
|
|
if obj.Pkg() != g.pkg {
|
|
g.errorf("type %s not defined in package %s", T, g.pkg)
|
|
return
|
|
}
|
|
g.Printf("%s.WriteGoRef(%s)\n", seqName, valName)
|
|
default:
|
|
g.errorf("unsupported type %s", T)
|
|
}
|
|
case *types.Named:
|
|
switch u := T.Underlying().(type) {
|
|
case *types.Interface, *types.Pointer:
|
|
g.Printf("%s.WriteGoRef(%s)\n", seqName, valName)
|
|
default:
|
|
g.errorf("unsupported, direct named type %s: %s", T, u)
|
|
}
|
|
default:
|
|
g.Printf("%s.Write%s(%s);\n", seqName, seqType(T), valName)
|
|
}
|
|
}
|
|
|
|
func (g *goGen) genFunc(o *types.Func) {
|
|
g.Printf("func proxy_%s(out, in *seq.Buffer) {\n", o.Name())
|
|
g.Indent()
|
|
g.genFuncBody(o, g.pkg.Name())
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func exportedMethodSet(T types.Type) []*types.Func {
|
|
var methods []*types.Func
|
|
methodset := types.NewMethodSet(T)
|
|
for i := 0; i < methodset.Len(); i++ {
|
|
obj := methodset.At(i).Obj()
|
|
if !obj.Exported() {
|
|
continue
|
|
}
|
|
switch obj := obj.(type) {
|
|
case *types.Func:
|
|
methods = append(methods, obj)
|
|
default:
|
|
log.Panicf("unexpected methodset obj: %s", obj)
|
|
}
|
|
}
|
|
return methods
|
|
}
|
|
|
|
func exportedFields(T *types.Struct) []*types.Var {
|
|
var fields []*types.Var
|
|
for i := 0; i < T.NumFields(); i++ {
|
|
f := T.Field(i)
|
|
if !f.Exported() {
|
|
continue
|
|
}
|
|
fields = append(fields, f)
|
|
}
|
|
return fields
|
|
}
|
|
|
|
func (g *goGen) genStruct(obj *types.TypeName, T *types.Struct) {
|
|
fields := exportedFields(T)
|
|
methods := exportedMethodSet(types.NewPointer(obj.Type()))
|
|
|
|
g.Printf("const (\n")
|
|
g.Indent()
|
|
g.Printf("proxy%s_Descriptor = \"go.%s.%s\"\n", obj.Name(), g.pkg.Name(), obj.Name())
|
|
for i, f := range fields {
|
|
g.Printf("proxy%s_%s_Get_Code = 0x%x0f\n", obj.Name(), f.Name(), i)
|
|
g.Printf("proxy%s_%s_Set_Code = 0x%x1f\n", obj.Name(), f.Name(), i)
|
|
}
|
|
for i, m := range methods {
|
|
g.Printf("proxy%s_%s_Code = 0x%x0c\n", obj.Name(), m.Name(), i)
|
|
}
|
|
g.Outdent()
|
|
g.Printf(")\n\n")
|
|
|
|
g.Printf("type proxy%s seq.Ref\n\n", obj.Name())
|
|
|
|
for _, f := range fields {
|
|
seqTyp := seqType(f.Type())
|
|
|
|
g.Printf("func proxy%s_%s_Set(out, in *seq.Buffer) {\n", obj.Name(), f.Name())
|
|
g.Indent()
|
|
g.Printf("ref := in.ReadRef()\n")
|
|
g.Printf("v := in.Read%s()\n", seqTyp)
|
|
if seqTyp == "Ref" {
|
|
g.Printf("ref.Get().(*%s.%s).%s = v.Get().(%s)\n", g.pkg.Name(), obj.Name(), f.Name(), g.typeString(f.Type()))
|
|
} else {
|
|
// TODO(crawshaw): other kinds of non-ptr types.
|
|
g.Printf("ref.Get().(*%s.%s).%s = v\n", g.pkg.Name(), obj.Name(), f.Name())
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
|
|
g.Printf("func proxy%s_%s_Get(out, in *seq.Buffer) {\n", obj.Name(), f.Name())
|
|
g.Indent()
|
|
g.Printf("ref := in.ReadRef()\n")
|
|
g.Printf("v := ref.Get().(*%s.%s).%s\n", g.pkg.Name(), obj.Name(), f.Name())
|
|
if seqTyp == "Ref" {
|
|
g.Printf("out.WriteGoRef(v)\n")
|
|
} else {
|
|
g.Printf("out.Write%s(v)\n", seqTyp)
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
for _, m := range methods {
|
|
g.Printf("func proxy%s_%s(out, in *seq.Buffer) {\n", obj.Name(), m.Name())
|
|
g.Indent()
|
|
g.Printf("ref := in.ReadRef()\n")
|
|
g.Printf("v := ref.Get().(*%s.%s)\n", g.pkg.Name(), obj.Name())
|
|
g.genFuncBody(m, "v")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
g.Printf("func init() {\n")
|
|
g.Indent()
|
|
for _, f := range fields {
|
|
n := f.Name()
|
|
g.Printf("seq.Register(proxy%s_Descriptor, proxy%s_%s_Set_Code, proxy%s_%s_Set)\n", obj.Name(), obj.Name(), n, obj.Name(), n)
|
|
g.Printf("seq.Register(proxy%s_Descriptor, proxy%s_%s_Get_Code, proxy%s_%s_Get)\n", obj.Name(), obj.Name(), n, obj.Name(), n)
|
|
}
|
|
for _, m := range methods {
|
|
n := m.Name()
|
|
g.Printf("seq.Register(proxy%s_Descriptor, proxy%s_%s_Code, proxy%s_%s)\n", obj.Name(), obj.Name(), n, obj.Name(), n)
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
func (g *goGen) genInterface(obj *types.TypeName) {
|
|
iface := obj.Type().(*types.Named).Underlying().(*types.Interface)
|
|
|
|
ifaceDesc := fmt.Sprintf("go.%s.%s", g.pkg.Name(), obj.Name())
|
|
|
|
// Descriptor and code for interface methods.
|
|
g.Printf("const (\n")
|
|
g.Indent()
|
|
g.Printf("proxy%s_Descriptor = %q\n", obj.Name(), ifaceDesc)
|
|
for i := 0; i < iface.NumMethods(); i++ {
|
|
g.Printf("proxy%s_%s_Code = 0x%x0a\n", obj.Name(), iface.Method(i).Name(), i+1)
|
|
}
|
|
g.Outdent()
|
|
g.Printf(")\n\n")
|
|
|
|
// Define the entry points.
|
|
for i := 0; i < iface.NumMethods(); i++ {
|
|
m := iface.Method(i)
|
|
g.Printf("func proxy%s_%s(out, in *seq.Buffer) {\n", obj.Name(), m.Name())
|
|
g.Indent()
|
|
g.Printf("ref := in.ReadRef()\n")
|
|
g.Printf("v := ref.Get().(%s.%s)\n", g.pkg.Name(), obj.Name())
|
|
g.genFuncBody(m, "v")
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
|
|
// Register the method entry points.
|
|
g.Printf("func init() {\n")
|
|
g.Indent()
|
|
for i := 0; i < iface.NumMethods(); i++ {
|
|
g.Printf("seq.Register(proxy%s_Descriptor, proxy%s_%s_Code, proxy%s_%s)\n",
|
|
obj.Name(), obj.Name(), iface.Method(i).Name(), obj.Name(), iface.Method(i).Name())
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
|
|
// Define a proxy interface.
|
|
g.Printf("type proxy%s seq.Ref\n\n", obj.Name())
|
|
|
|
for i := 0; i < iface.NumMethods(); i++ {
|
|
m := iface.Method(i)
|
|
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(", obj.Name(), m.Name())
|
|
for i := 0; i < params.Len(); i++ {
|
|
if i > 0 {
|
|
g.Printf(", ")
|
|
}
|
|
g.Printf("%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()
|
|
|
|
g.Printf("in := new(seq.Buffer)\n")
|
|
for i := 0; i < params.Len(); i++ {
|
|
g.genWrite(paramName(params, i), "in", params.At(i).Type())
|
|
}
|
|
|
|
if res.Len() == 0 {
|
|
g.Printf("seq.Transact((*seq.Ref)(p), %q, proxy%s_%s_Code, in)\n", ifaceDesc, obj.Name(), m.Name())
|
|
} else {
|
|
g.Printf("out := seq.Transact((*seq.Ref)(p), %q, proxy%s_%s_Code, in)\n", ifaceDesc, obj.Name(), m.Name())
|
|
var rvs []string
|
|
for i := 0; i < res.Len(); i++ {
|
|
rv := fmt.Sprintf("res_%d", i)
|
|
g.genRead(rv, "out", res.At(i).Type())
|
|
rvs = append(rvs, rv)
|
|
}
|
|
g.Printf("return %s\n", strings.Join(rvs, ","))
|
|
}
|
|
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
}
|
|
|
|
func (g *goGen) genRead(valName, seqName string, typ types.Type) {
|
|
if isErrorType(typ) {
|
|
g.Printf("%s := %s.ReadError()\n", valName, seqName)
|
|
return
|
|
}
|
|
switch t := typ.(type) {
|
|
case *types.Pointer:
|
|
switch u := t.Elem().(type) {
|
|
case *types.Named:
|
|
o := u.Obj()
|
|
if o.Pkg() != g.pkg {
|
|
g.errorf("type %s not defined in package %s", u, g.pkg)
|
|
return
|
|
}
|
|
g.Printf("// Must be a Go object\n")
|
|
g.Printf("%s_ref := %s.ReadRef()\n", valName, seqName)
|
|
g.Printf("%s := %s_ref.Get().(*%s.%s)\n", valName, valName, g.pkg.Name(), o.Name())
|
|
default:
|
|
g.errorf("unsupported type %s", t)
|
|
}
|
|
case *types.Named:
|
|
switch t.Underlying().(type) {
|
|
case *types.Interface, *types.Pointer:
|
|
o := t.Obj()
|
|
if o.Pkg() != g.pkg {
|
|
g.errorf("type %s not defined in package %s", t, g.pkg)
|
|
return
|
|
}
|
|
g.Printf("var %s %s\n", valName, g.typeString(t))
|
|
g.Printf("%s_ref := %s.ReadRef()\n", valName, seqName)
|
|
g.Printf("if %s_ref.Num < 0 { // go object \n", valName)
|
|
g.Printf(" %s = %s_ref.Get().(%s.%s)\n", valName, valName, g.pkg.Name(), o.Name())
|
|
g.Printf("} else { // foreign object \n")
|
|
g.Printf(" %s = (*proxy%s)(%s_ref)\n", valName, o.Name(), valName)
|
|
g.Printf("}\n")
|
|
}
|
|
default:
|
|
g.Printf("%s := %s.Read%s()\n", valName, seqName, seqType(t))
|
|
}
|
|
}
|
|
|
|
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))
|
|
}
|
|
if obj.Pkg() != g.pkg {
|
|
g.errorf("type %s not defined in package %s", t, g.pkg)
|
|
}
|
|
|
|
switch t.Underlying().(type) {
|
|
case *types.Interface, *types.Struct:
|
|
return fmt.Sprintf("%s.%s", pkg.Name(), types.TypeString(typ, types.RelativeTo(pkg)))
|
|
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 ""
|
|
}
|
|
|
|
func (g *goGen) gen() error {
|
|
g.genPreamble()
|
|
|
|
var funcs []string
|
|
|
|
scope := g.pkg.Scope()
|
|
names := scope.Names()
|
|
for _, name := range names {
|
|
obj := scope.Lookup(name)
|
|
if !obj.Exported() {
|
|
continue
|
|
}
|
|
|
|
switch obj := obj.(type) {
|
|
// TODO(crawshaw): case *types.Const:
|
|
// TODO(crawshaw): case *types.Var:
|
|
case *types.Func:
|
|
g.genFunc(obj)
|
|
funcs = append(funcs, obj.Name())
|
|
case *types.TypeName:
|
|
named := obj.Type().(*types.Named)
|
|
switch T := named.Underlying().(type) {
|
|
case *types.Struct:
|
|
g.genStruct(obj, T)
|
|
case *types.Interface:
|
|
g.genInterface(obj)
|
|
}
|
|
|
|
default:
|
|
g.errorf("not yet supported, name for %v / %T", obj, obj)
|
|
continue
|
|
}
|
|
}
|
|
|
|
g.Printf("func init() {\n")
|
|
g.Indent()
|
|
for i, name := range funcs {
|
|
g.Printf("seq.Register(%q, %d, proxy_%s)\n", g.pkg.Name(), i+1, name)
|
|
}
|
|
g.Outdent()
|
|
g.Printf("}\n")
|
|
|
|
if len(g.err) > 0 {
|
|
return g.err
|
|
}
|
|
return nil
|
|
}
|