blob: 673c743e70e59d4486943ffaf83e005bf95a5a0b [file] [log] [blame]
// 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)
// Descriptor and code for interface methods.
g.Printf("const (\n")
g.Indent()
g.Printf("proxy%s_Descriptor = \"go.%s.%s\"\n", obj.Name(), g.pkg.Name(), obj.Name())
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), proxy%s_%s_Code, in)\n", obj.Name(), m.Name())
} else {
g.Printf("out := seq.Transact((*seq.Ref)(p), proxy%s_%s_Code, in)\n", 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(pkg, typ)
}
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(pkg, typ))
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(pkg, typ)
}
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
}