2
0
mirror of synced 2025-02-23 06:48:15 +00:00
mobile/bind/gengo.go
Hyang-Ah (Hana) Kim c261c465a9 bind: seq.Transact requires interface descriptor.
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>
2015-07-27 18:08:41 +00:00

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
}