// Copyright 2015 The go-python 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" "path/filepath" "strings" "golang.org/x/tools/go/types" ) const ( goPreamble = `// Package main is an autogenerated binder stub for package %[1]s. // gopy gen -lang=go %[1]s // // File is generated by gopy gen. Do not edit. package main //#cgo pkg-config: python2 --cflags --libs //#include //#include import "C" import ( "unsafe" %[2]q ) var _ = unsafe.Pointer(nil) // --- begin cgo helpers --- //export CGoPy_GoString func CGoPy_GoString(str *C.char) string { return C.GoString(str) } //export CGoPy_CString func CGoPy_CString(s string) *C.char { return C.CString(s) } //export CGoPy_ErrorIsNil func CGoPy_ErrorIsNil(err error) bool { return err == nil } //export CGoPy_ErrorString func CGoPy_ErrorString(err error) *C.char { return C.CString(err.Error()) } // --- end cgo helpers --- ` ) type goGen struct { *printer fset *token.FileSet pkg *Package err ErrorList } func (g *goGen) gen() error { g.genPreamble() for _, s := range g.pkg.structs { g.genStruct(s) } // expose ctors at module level // FIXME(sbinet): attach them to structs? // -> problem is if one has 2 or more ctors with exactly the same signature. for _, s := range g.pkg.structs { for _, ctor := range s.ctors { g.genFunc(ctor) } } for _, f := range g.pkg.funcs { g.genFunc(f) } g.Printf("// buildmode=c-shared needs a 'main'\n\nfunc main() {}\n") g.Printf("// tickle cgo\nfunc init() {\n") g.Indent() g.Printf("str := C.CString(%q)\n", g.pkg.Name()) g.Printf("C.free(unsafe.Pointer(str))\n") g.Outdent() g.Printf("}\n") if len(g.err) > 0 { return g.err } return nil } func (g *goGen) genFunc(f Func) { sig := f.Signature() params := "(" + g.tupleString(sig.Params()) + ")" ret := g.tupleString(sig.Results()) if len(sig.Results()) > 1 { ret = "(" + ret + ") " } else { ret += " " } //funcName := o.Name() g.Printf(` //export GoPy_%[1]s // GoPy_%[1]s wraps %[2]s.%[3]s func GoPy_%[1]s%[4]v%[5]v{ `, f.ID(), f.Package().Name(), f.GoName(), params, ret, ) g.Indent() g.genFuncBody(f) g.Outdent() g.Printf("}\n\n") } func (g *goGen) genFuncBody(f Func) { sig := f.Signature() results := sig.Results() for i := range results { if i > 0 { g.Printf(", ") } g.Printf("_gopy_%03d", i) } if len(results) > 0 { g.Printf(" := ") } g.Printf("%s.%s(", g.pkg.Name(), f.GoName()) args := sig.Params() for i, arg := range args { tail := "" if i+1 < len(args) { tail = ", " } g.Printf("%s%s", arg.Name(), tail) } g.Printf(")\n") if len(results) <= 0 { return } g.Printf("return ") for i, res := range results { if i > 0 { g.Printf(", ") } // if needWrap(res.GoType()) { // g.Printf("") // } if res.needWrap() { g.Printf("%s(unsafe.Pointer(&", res.dtype.cgotype) } g.Printf("_gopy_%03d /* %#v %v */", i, res, res.GoType().Underlying()) if res.needWrap() { g.Printf("))") } } g.Printf("\n") } func (g *goGen) genStruct(s Struct) { //fmt.Printf("obj: %#v\ntyp: %#v\n", obj, typ) typ := s.Struct() pkgname := s.Package().Name() g.Printf("//export GoPy_%[1]s\n", s.ID()) g.Printf("type GoPy_%[1]s unsafe.Pointer\n\n", s.ID()) for i := 0; i < typ.NumFields(); i++ { f := typ.Field(i) if !f.Exported() { continue } ft := f.Type() ftname := g.qualifiedType(ft) if needWrapType(ft) { ftname = fmt.Sprintf("GoPy_%[1]s_field_%d", s.ID(), i+1) g.Printf("//export %s\n", ftname) g.Printf("type %s unsafe.Pointer\n\n", ftname) } // -- getter -- g.Printf("//export GoPy_%[1]s_getter_%[2]d\n", s.ID(), i+1) g.Printf("func GoPy_%[1]s_getter_%[2]d(self GoPy_%[1]s) %[3]s {\n", s.ID(), i+1, ftname, ) g.Indent() g.Printf( "ret := (*%[1]s)(unsafe.Pointer(self))\n", pkgname+"."+s.GoName(), ) if needWrapType(ft) { dt := getTypedesc(ft) g.Printf("%s(unsafe.Pointer(&ret.%s))\n", dt.cgotype, f.Name()) } else { g.Printf("return ret.%s\n", f.Name()) } g.Outdent() g.Printf("}\n\n") // -- setter -- g.Printf("//export GoPy_%[1]s_setter_%[2]d\n", s.ID(), i+1) g.Printf("func GoPy_%[1]s_setter_%[2]d(self GoPy_%[1]s, v %[3]s) {\n", s.ID(), i+1, ftname, ) g.Indent() fset := "v" if needWrapType(ft) { dt := getTypedesc(ft) fset = fmt.Sprintf("%s(unsafe.Pointer(&v))", dt.cgotype) } g.Printf( "(*%[1]s)(unsafe.Pointer(self)).%[2]s = %[3]s\n", pkgname+"."+s.GoName(), f.Name(), fset, ) g.Outdent() g.Printf("}\n\n") } for _, m := range s.meths { g.genMethod(s, m) } g.Printf("//export GoPy_%[1]s_new\n", s.ID()) g.Printf("func GoPy_%[1]s_new() GoPy_%[1]s {\n", s.ID()) g.Indent() g.Printf("return (GoPy_%[1]s)(unsafe.Pointer(&%[2]s.%[3]s{}))\n", s.ID(), pkgname, s.GoName(), ) g.Outdent() g.Printf("}\n\n") } func (g *goGen) genMethod(s Struct, m Func) { sig := m.Signature() params := "(self GoPy_" + s.ID() if len(sig.Params()) > 0 { params += ", " + g.tupleString(sig.Params()) } params += ")" ret := g.tupleString(sig.Results()) if len(sig.Results()) > 1 { ret = "(" + ret + ")" } else { ret += " " } g.Printf("//export GoPy_%[1]s\n", m.ID()) g.Printf("func GoPy_%[1]s%[2]s%[3]s{\n", m.ID(), params, ret, ) g.Indent() g.genMethodBody(s, m) g.Outdent() g.Printf("}\n\n") } func (g *goGen) genMethodBody(s Struct, m Func) { sig := m.Signature() results := sig.Results() for i := range results { if i > 0 { g.Printf(", ") } g.Printf("_gopy_%03d", i) } if len(results) > 0 { g.Printf(" := ") } g.Printf("(*%s.%s)(unsafe.Pointer(self)).%s(", g.pkg.Name(), s.GoName(), m.GoName(), ) args := sig.Params() for i, arg := range args { tail := "" if i+1 < len(args) { tail = ", " } g.Printf("%s%s", arg.Name(), tail) } g.Printf(")\n") if len(results) <= 0 { return } g.Printf("return ") for i, res := range results { if i > 0 { g.Printf(", ") } // if needWrap(res.GoType()) { // g.Printf("") // } if res.needWrap() { g.Printf("%s(unsafe.Pointer(&", res.dtype.cgotype) } g.Printf("_gopy_%03d /* %#v %v */", i, res, res.GoType().Underlying()) if res.needWrap() { g.Printf("))") } } g.Printf("\n") } func (g *goGen) genPreamble() { n := g.pkg.pkg.Name() g.Printf(goPreamble, n, g.pkg.pkg.Path(), filepath.Base(n)) } func (g *goGen) tupleString(tuple []*Var) string { n := len(tuple) if n <= 0 { return "" } str := make([]string, 0, n) for _, v := range tuple { n := v.Name() typ := v.GoType() str = append(str, n+" "+g.qualifiedType(typ)) } return strings.Join(str, ", ") } func (g *goGen) qualifiedType(typ types.Type) string { switch typ := typ.(type) { case *types.Basic: return typ.Name() case *types.Named: obj := typ.Obj() switch typ.Underlying().(type) { case *types.Struct: return "GoPy_" + obj.Pkg().Name() + "_" + obj.Name() case *types.Interface: if obj.Name() == "error" { return "error" } return "GoPy_" + obj.Name() default: return "GoPy_ooops_" + obj.Name() } } return fmt.Sprintf("%#T", typ) }