340 lines
8.4 KiB
Go
340 lines
8.4 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"
|
|
|
|
"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)
|
|
t := seqType(p.Type())
|
|
if t == "Ref" {
|
|
name := p.Type().(*types.Named).Obj().Name()
|
|
g.Printf("var param_%s %s.%s\n", p.Name(), g.pkg.Name(), name)
|
|
g.Printf("param_%s_ref := in.ReadRef()\n", p.Name())
|
|
g.Printf("if param_%s_ref.Num < 0 {\n", p.Name())
|
|
g.Printf(" param_%s = param_%s_ref.Get().(%s.%s)\n", p.Name(), p.Name(), g.pkg.Name(), name)
|
|
g.Printf("} else {\n")
|
|
g.Printf(" param_%s = (*proxy%s)(param_%s_ref)\n", p.Name(), name, p.Name())
|
|
g.Printf("}\n")
|
|
} else {
|
|
g.Printf("param_%s := in.Read%s()\n", p.Name(), t)
|
|
}
|
|
}
|
|
|
|
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", params.At(i).Name())
|
|
}
|
|
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.WriteUTF16(\"\");\n", seqName)
|
|
g.Printf("} else {\n")
|
|
g.Printf(" %s.WriteUTF16(%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%sDescriptor = \"go.%s.%s\"\n", obj.Name(), g.pkg.Name(), obj.Name())
|
|
for i, f := range fields {
|
|
g.Printf("proxy%s%sGetCode = 0x%x0f\n", obj.Name(), f.Name(), i)
|
|
g.Printf("proxy%s%sSetCode = 0x%x1f\n", obj.Name(), f.Name(), i)
|
|
}
|
|
for i, m := range methods {
|
|
g.Printf("proxy%s%sCode = 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 {
|
|
g.Printf("func proxy%s%sSet(out, in *seq.Buffer) {\n", obj.Name(), f.Name())
|
|
g.Indent()
|
|
g.Printf("ref := in.ReadRef()\n")
|
|
g.Printf("v := in.Read%s()\n", seqType(f.Type()))
|
|
// 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%sGet(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())
|
|
g.Printf("out.Write%s(v)\n", seqType(f.Type()))
|
|
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%sDescriptor, proxy%s%sSetCode, proxy%s%sSet)\n", obj.Name(), obj.Name(), n, obj.Name(), n)
|
|
g.Printf("seq.Register(proxy%sDescriptor, proxy%s%sGetCode, proxy%s%sGet)\n", obj.Name(), obj.Name(), n, obj.Name(), n)
|
|
}
|
|
for _, m := range methods {
|
|
n := m.Name()
|
|
g.Printf("seq.Register(proxy%sDescriptor, proxy%s%sCode, 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)
|
|
|
|
g.Printf("const (\n")
|
|
g.Indent()
|
|
g.Printf("proxy%sDescriptor = \"go.%s.%s\"\n", obj.Name(), g.pkg.Name(), obj.Name())
|
|
for i := 0; i < iface.NumMethods(); i++ {
|
|
g.Printf("proxy%s%sCode = 0x%x0a\n", obj.Name(), iface.Method(i).Name(), i+1)
|
|
}
|
|
g.Outdent()
|
|
g.Printf(")\n\n")
|
|
|
|
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()
|
|
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), params.At(i).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("res_%d %s", i, res.At(i).Type())
|
|
}
|
|
if res.Len() > 0 {
|
|
g.Printf(")")
|
|
}
|
|
g.Printf(" {\n")
|
|
g.Indent()
|
|
|
|
g.Printf("out := new(seq.Buffer)\n")
|
|
for i := 0; i < params.Len(); i++ {
|
|
p := params.At(i)
|
|
g.Printf("out.Write%s\n", seqWrite(p.Type(), paramName(params, i)))
|
|
}
|
|
g.Printf("seq.Transact((*seq.Ref)(p), proxy%s%sCode, out)\n", obj.Name(), m.Name())
|
|
|
|
g.Outdent()
|
|
g.Printf("}\n\n")
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|