| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- // Copyright 2015 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/ast"
- "go/token"
- "go/types"
- "io"
- "regexp"
- "strings"
- "unicode"
- "unicode/utf8"
- )
- type (
- ErrorList []error
- // varMode describes the lifetime of an argument or
- // return value. Modes are used to guide the conversion
- // of string and byte slice values across the language
- // barrier. The same conversion mode must be used for
- // both the conversion before a foreign call and the
- // corresponding conversion after the call.
- // See the mode* constants for a description of
- // each mode.
- varMode int
- )
- const (
- // modeTransient are for function arguments that
- // are not used after the function returns.
- // Transient byte slices don't need copying
- // when passed across the language barrier.
- modeTransient varMode = iota
- // modeRetained are for returned values and for function
- // arguments that are used after the function returns.
- // Retained byte slices need an intermediate copy.
- modeRetained
- )
- func (list ErrorList) Error() string {
- buf := new(bytes.Buffer)
- for i, err := range list {
- if i > 0 {
- buf.WriteRune('\n')
- }
- io.WriteString(buf, err.Error())
- }
- return buf.String()
- }
- // interfaceInfo comes from Init and collects the auxiliary information
- // needed to generate bindings for an exported Go interface in a bound
- // package.
- type interfaceInfo struct {
- obj *types.TypeName
- t *types.Interface
- summary ifaceSummary
- }
- // structInfo comes from Init and collects the auxiliary information
- // needed to generate bindings for an exported Go struct in a bound
- // package.
- type structInfo struct {
- obj *types.TypeName
- t *types.Struct
- }
- // Generator contains the common Go package information
- // needed for the specific Go, Java, ObjC generators.
- //
- // After setting Printer, Fset, AllPkg, Pkg, the Init
- // method is used to initialize the auxiliary information
- // about the package to be generated, Pkg.
- type Generator struct {
- *Printer
- Fset *token.FileSet
- AllPkg []*types.Package
- Files []*ast.File
- Pkg *types.Package
- err ErrorList
- // fields set by init.
- pkgName string
- pkgPrefix string
- funcs []*types.Func
- constants []*types.Const
- vars []*types.Var
- interfaces []interfaceInfo
- structs []structInfo
- otherNames []*types.TypeName
- // allIntf contains interfaces from all bound packages.
- allIntf []interfaceInfo
- docs pkgDocs
- }
- // A pkgDocs maps the name of each exported package-level declaration to its extracted documentation.
- type pkgDocs map[string]*pkgDoc
- type pkgDoc struct {
- doc string
- // Struct or interface fields and methods.
- members map[string]string
- }
- // pkgPrefix returns a prefix that disambiguates symbol names for binding
- // multiple packages.
- //
- // TODO(elias.naur): Avoid (and test) name clashes from multiple packages
- // with the same name. Perhaps use the index from the order the package is
- // generated.
- func pkgPrefix(pkg *types.Package) string {
- // The error type has no package
- if pkg == nil {
- return ""
- }
- return pkg.Name()
- }
- func (g *Generator) Init() {
- if g.Pkg != nil {
- g.pkgName = g.Pkg.Name()
- }
- g.pkgPrefix = pkgPrefix(g.Pkg)
- if g.Pkg != nil {
- g.parseDocs()
- scope := g.Pkg.Scope()
- hasExported := false
- for _, name := range scope.Names() {
- obj := scope.Lookup(name)
- if !obj.Exported() {
- continue
- }
- hasExported = true
- switch obj := obj.(type) {
- case *types.Func:
- if isCallable(obj) {
- g.funcs = append(g.funcs, obj)
- }
- case *types.TypeName:
- named, ok := obj.Type().(*types.Named)
- if !ok {
- continue
- }
- switch t := named.Underlying().(type) {
- case *types.Struct:
- g.structs = append(g.structs, structInfo{obj, t})
- case *types.Interface:
- g.interfaces = append(g.interfaces, interfaceInfo{obj, t, makeIfaceSummary(t)})
- default:
- g.otherNames = append(g.otherNames, obj)
- }
- case *types.Const:
- g.constants = append(g.constants, obj)
- case *types.Var:
- g.vars = append(g.vars, obj)
- default:
- g.errorf("unsupported exported type for %s: %T", obj.Name(), obj)
- }
- }
- if !hasExported {
- g.errorf("no exported names in the package %q", g.Pkg.Path())
- }
- } else {
- // Bind the single supported type from the universe scope, error.
- errType := types.Universe.Lookup("error").(*types.TypeName)
- t := errType.Type().Underlying().(*types.Interface)
- g.interfaces = append(g.interfaces, interfaceInfo{errType, t, makeIfaceSummary(t)})
- }
- for _, p := range g.AllPkg {
- scope := p.Scope()
- for _, name := range scope.Names() {
- obj := scope.Lookup(name)
- if !obj.Exported() {
- continue
- }
- if obj, ok := obj.(*types.TypeName); ok {
- named, ok := obj.Type().(*types.Named)
- if !ok {
- continue
- }
- if t, ok := named.Underlying().(*types.Interface); ok {
- g.allIntf = append(g.allIntf, interfaceInfo{obj, t, makeIfaceSummary(t)})
- }
- }
- }
- }
- }
- // parseDocs extracts documentation from a package in a form useful for lookups.
- func (g *Generator) parseDocs() {
- d := make(pkgDocs)
- for _, f := range g.Files {
- for _, decl := range f.Decls {
- switch decl := decl.(type) {
- case *ast.GenDecl:
- for _, spec := range decl.Specs {
- switch spec := spec.(type) {
- case *ast.TypeSpec:
- d.addType(spec, decl.Doc)
- case *ast.ValueSpec:
- d.addValue(spec, decl.Doc)
- }
- }
- case *ast.FuncDecl:
- d.addFunc(decl)
- }
- }
- }
- g.docs = d
- }
- func (d pkgDocs) addValue(t *ast.ValueSpec, outerDoc *ast.CommentGroup) {
- for _, n := range t.Names {
- if !ast.IsExported(n.Name) {
- continue
- }
- doc := t.Doc
- if doc == nil {
- doc = outerDoc
- }
- if doc != nil {
- d[n.Name] = &pkgDoc{doc: doc.Text()}
- }
- }
- }
- func (d pkgDocs) addFunc(f *ast.FuncDecl) {
- doc := f.Doc
- if doc == nil {
- return
- }
- fn := f.Name.Name
- if !ast.IsExported(fn) {
- return
- }
- if r := f.Recv; r != nil {
- // f is a method.
- n := typeName(r.List[0].Type)
- pd, exists := d[n]
- if !exists {
- pd = &pkgDoc{members: make(map[string]string)}
- d[n] = pd
- }
- pd.members[fn] = doc.Text()
- } else {
- // f is a function.
- d[fn] = &pkgDoc{doc: doc.Text()}
- }
- }
- func (d pkgDocs) addType(t *ast.TypeSpec, outerDoc *ast.CommentGroup) {
- if !ast.IsExported(t.Name.Name) {
- return
- }
- doc := t.Doc
- if doc == nil {
- doc = outerDoc
- }
- pd := d[t.Name.Name]
- pd = &pkgDoc{members: make(map[string]string)}
- d[t.Name.Name] = pd
- if doc != nil {
- pd.doc = doc.Text()
- }
- var fields *ast.FieldList
- switch t := t.Type.(type) {
- case *ast.StructType:
- fields = t.Fields
- case *ast.InterfaceType:
- fields = t.Methods
- }
- if fields != nil {
- for _, field := range fields.List {
- if field.Doc != nil {
- if field.Names == nil {
- // Anonymous field. Extract name from its type.
- if n := typeName(field.Type); ast.IsExported(n) {
- pd.members[n] = field.Doc.Text()
- }
- }
- for _, n := range field.Names {
- if ast.IsExported(n.Name) {
- pd.members[n.Name] = field.Doc.Text()
- }
- }
- }
- }
- }
- }
- // typeName returns the type name T for expressions on the
- // T, *T, **T (etc.) form.
- func typeName(t ast.Expr) string {
- switch t := t.(type) {
- case *ast.StarExpr:
- return typeName(t.X)
- case *ast.Ident:
- return t.Name
- case *ast.SelectorExpr:
- return t.Sel.Name
- default:
- return ""
- }
- }
- func (d *pkgDoc) Doc() string {
- if d == nil {
- return ""
- }
- return d.doc
- }
- func (d *pkgDoc) Member(n string) string {
- if d == nil {
- return ""
- }
- return d.members[n]
- }
- // constructorType returns the type T for a function of the forms:
- //
- // func NewT...(...) *T
- // func NewT...(...) (*T, error)
- func (g *Generator) constructorType(f *types.Func) *types.TypeName {
- sig := f.Type().(*types.Signature)
- res := sig.Results()
- if res.Len() != 1 && !(res.Len() == 2 && isErrorType(res.At(1).Type())) {
- return nil
- }
- rt := res.At(0).Type()
- pt, ok := rt.(*types.Pointer)
- if !ok {
- return nil
- }
- nt, ok := pt.Elem().(*types.Named)
- if !ok {
- return nil
- }
- obj := nt.Obj()
- if !strings.HasPrefix(f.Name(), "New"+obj.Name()) {
- return nil
- }
- return obj
- }
- func toCFlag(v bool) int {
- if v {
- return 1
- }
- return 0
- }
- func (g *Generator) errorf(format string, args ...interface{}) {
- g.err = append(g.err, fmt.Errorf(format, args...))
- }
- // cgoType returns the name of a Cgo type suitable for converting a value of
- // the given type.
- func (g *Generator) cgoType(t types.Type) string {
- switch t := t.(type) {
- case *types.Basic:
- switch t.Kind() {
- case types.Bool, types.UntypedBool:
- return "char"
- case types.Int:
- return "nint"
- case types.Int8:
- return "int8_t"
- case types.Int16:
- return "int16_t"
- case types.Int32, types.UntypedRune: // types.Rune
- return "int32_t"
- case types.Int64, types.UntypedInt:
- return "int64_t"
- case types.Uint8: // types.Byte
- return "uint8_t"
- // TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64:
- case types.Float32:
- return "float"
- case types.Float64, types.UntypedFloat:
- return "double"
- case types.String:
- return "nstring"
- default:
- g.errorf("unsupported basic type: %s", t)
- }
- case *types.Slice:
- switch e := t.Elem().(type) {
- case *types.Basic:
- switch e.Kind() {
- case types.Uint8: // Byte.
- return "nbyteslice"
- default:
- g.errorf("unsupported slice type: %s", t)
- }
- default:
- g.errorf("unsupported slice type: %s", t)
- }
- case *types.Pointer:
- if _, ok := t.Elem().(*types.Named); ok {
- return g.cgoType(t.Elem())
- }
- g.errorf("unsupported pointer to type: %s", t)
- case *types.Named:
- return "int32_t"
- default:
- g.errorf("unsupported type: %s", t)
- }
- return "TODO"
- }
- func (g *Generator) genInterfaceMethodSignature(m *types.Func, iName string, header bool, g_paramName func(*types.Tuple, int) string) {
- sig := m.Type().(*types.Signature)
- params := sig.Params()
- res := sig.Results()
- if res.Len() == 0 {
- g.Printf("void ")
- } else {
- if res.Len() == 1 {
- g.Printf("%s ", g.cgoType(res.At(0).Type()))
- } else {
- if header {
- g.Printf("typedef struct cproxy%s_%s_%s_return {\n", g.pkgPrefix, iName, m.Name())
- g.Indent()
- for i := 0; i < res.Len(); i++ {
- t := res.At(i).Type()
- g.Printf("%s r%d;\n", g.cgoType(t), i)
- }
- g.Outdent()
- g.Printf("} cproxy%s_%s_%s_return;\n", g.pkgPrefix, iName, m.Name())
- }
- g.Printf("struct cproxy%s_%s_%s_return ", g.pkgPrefix, iName, m.Name())
- }
- }
- g.Printf("cproxy%s_%s_%s(int32_t refnum", g.pkgPrefix, iName, m.Name())
- for i := 0; i < params.Len(); i++ {
- t := params.At(i).Type()
- g.Printf(", %s %s", g.cgoType(t), g_paramName(params, i))
- }
- g.Printf(")")
- if header {
- g.Printf(";\n")
- } else {
- g.Printf(" {\n")
- }
- }
- func (g *Generator) validPkg(pkg *types.Package) bool {
- for _, p := range g.AllPkg {
- if p == pkg {
- return true
- }
- }
- return false
- }
- // isSigSupported reports whether the generators can handle a given
- // function signature.
- func (g *Generator) isSigSupported(t types.Type) bool {
- sig := t.(*types.Signature)
- params := sig.Params()
- for i := 0; i < params.Len(); i++ {
- if !g.isSupported(params.At(i).Type()) {
- return false
- }
- }
- res := sig.Results()
- for i := 0; i < res.Len(); i++ {
- if !g.isSupported(res.At(i).Type()) {
- return false
- }
- }
- return true
- }
- // isSupported reports whether the generators can handle the type.
- func (g *Generator) isSupported(t types.Type) bool {
- if isErrorType(t) || isWrapperType(t) {
- return true
- }
- switch t := t.(type) {
- case *types.Basic:
- switch t.Kind() {
- case types.Bool, types.UntypedBool,
- types.Int,
- types.Int8, types.Uint8, // types.Byte
- types.Int16,
- types.Int32, types.UntypedRune, // types.Rune
- types.Int64, types.UntypedInt,
- types.Float32,
- types.Float64, types.UntypedFloat,
- types.String, types.UntypedString:
- return true
- }
- return false
- case *types.Slice:
- switch e := t.Elem().(type) {
- case *types.Basic:
- return e.Kind() == types.Uint8
- }
- case *types.Pointer:
- switch t := t.Elem().(type) {
- case *types.Named:
- return g.validPkg(t.Obj().Pkg())
- }
- case *types.Named:
- switch t.Underlying().(type) {
- case *types.Interface, *types.Pointer:
- return g.validPkg(t.Obj().Pkg())
- }
- }
- return false
- }
- var paramRE = regexp.MustCompile(`^p[0-9]*$`)
- // basicParamName replaces incompatible name with a p0-pN name.
- // Missing names, or existing names of the form p[0-9] are incompatible.
- func basicParamName(params *types.Tuple, pos int) string {
- name := params.At(pos).Name()
- if name == "" || name[0] == '_' || paramRE.MatchString(name) {
- name = fmt.Sprintf("p%d", pos)
- }
- return name
- }
- func lowerFirst(s string) string {
- if s == "" {
- return ""
- }
- var conv []rune
- for len(s) > 0 {
- r, n := utf8.DecodeRuneInString(s)
- if !unicode.IsUpper(r) {
- if l := len(conv); l > 1 {
- conv[l-1] = unicode.ToUpper(conv[l-1])
- }
- return string(conv) + s
- }
- conv = append(conv, unicode.ToLower(r))
- s = s[n:]
- }
- return string(conv)
- }
- // newNameSanitizer returns a functions that replaces all dashes and dots
- // with underscores, as well as avoiding reserved words by suffixing such
- // identifiers with underscores.
- func newNameSanitizer(res []string) func(s string) string {
- reserved := make(map[string]bool)
- for _, word := range res {
- reserved[word] = true
- }
- symbols := strings.NewReplacer(
- "-", "_",
- ".", "_",
- )
- return func(s string) string {
- if reserved[s] {
- return s + "_"
- }
- return symbols.Replace(s)
- }
- }
|