// 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 //#include import "C" import ( "fmt" "sync" "unsafe" %[2]q ) var _ = unsafe.Pointer(nil) var _ = fmt.Sprintf // --- 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 --- // --- begin cref helpers --- type cobject struct { ptr unsafe.Pointer cnt int32 } // refs stores Go objects that have been passed to another language. var refs struct { sync.Mutex next int32 // next reference number to use for Go object, always negative refs map[unsafe.Pointer]int32 ptrs map[int32]cobject } //export cgopy_incref func cgopy_incref(ptr unsafe.Pointer) { refs.Lock() num, ok := refs.refs[ptr] if ok { s := refs.ptrs[num] refs.ptrs[num] = cobject{s.ptr, s.cnt + 1} } else { num = refs.next refs.next-- if refs.next > 0 { panic("refs.next underflow") } refs.refs[ptr] = num refs.ptrs[num] = cobject{ptr, 1} } refs.Unlock() } //export cgopy_decref func cgopy_decref(ptr unsafe.Pointer) { refs.Lock() num, ok := refs.refs[ptr] if !ok { panic("cgopy: decref untracked object") } s := refs.ptrs[num] if s.cnt - 1 <= 0 { delete(refs.ptrs, num) delete(refs.refs, ptr) refs.Unlock() return } refs.ptrs[num] = cobject{s.ptr, s.cnt - 1} refs.Unlock() } func init() { refs.Lock() refs.next = -24 // Go objects get negative reference numbers. Arbitrary starting point. refs.refs = make(map[unsafe.Pointer]int32) refs.ptrs = make(map[int32]cobject) refs.Unlock() // make sure cgo is used and cgo hooks are run str := C.CString(%[1]q) C.free(unsafe.Pointer(str)) } // --- end cref helpers --- ` ) type goGen struct { *printer fset *token.FileSet pkg *Package err ErrorList } func (g *goGen) gen() error { g.genPreamble() // process slices, arrays, ... for _, n := range g.pkg.syms.names() { sym := g.pkg.syms.sym(n) if !sym.isType() { continue } g.genType(sym) } 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) } for _, c := range g.pkg.consts { g.genConst(c) } for _, v := range g.pkg.vars { g.genVar(v) } g.Printf("// buildmode=c-shared needs a 'main'\nfunc main() {}\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()) + ") " //funcName := o.Name() g.Printf(` //export cgo_func_%[1]s // cgo_func_%[1]s wraps %[2]s.%[3]s func cgo_func_%[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 = ", " } head := arg.Name() if arg.needWrap() { head = fmt.Sprintf( "*(*%s)(unsafe.Pointer(%s))", types.TypeString( arg.GoType(), func(*types.Package) string { return g.pkg.Name() }, ), arg.Name(), ) } g.Printf("%s%s", head, tail) } g.Printf(")\n") if len(results) <= 0 { return } for i, res := range results { if !res.needWrap() { continue } g.Printf("cgopy_incref(unsafe.Pointer(&_gopy_%03d))\n", i) } 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.sym.cgoname) } g.Printf("_gopy_%03d", i) 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("\n// --- wrapping %s.%s ---\n\n", pkgname, s.sym.goname) g.Printf("//export %[1]s\n", s.sym.cgoname) g.Printf("// %[1]s wraps %[2]s.%[3]s\n", s.sym.cgoname, pkgname, s.sym.goname) g.Printf("type %[1]s unsafe.Pointer\n\n", s.sym.cgoname) for i := 0; i < typ.NumFields(); i++ { f := typ.Field(i) if !f.Exported() { continue } ft := f.Type() ftname := g.pkg.syms.symtype(ft).cgotypename() if needWrapType(ft) { ftname = fmt.Sprintf("cgo_type_%[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 cgo_func_%[1]s_getter_%[2]d\n", s.ID(), i+1) g.Printf("func cgo_func_%[1]s_getter_%[2]d(self cgo_type_%[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) { g.Printf("cgopy_incref(unsafe.Pointer(&ret.%s))\n", f.Name()) g.Printf("return %s(unsafe.Pointer(&ret.%s))\n", ftname, f.Name()) } else { g.Printf("return ret.%s\n", f.Name()) } g.Outdent() g.Printf("}\n\n") // -- setter -- g.Printf("//export cgo_func_%[1]s_setter_%[2]d\n", s.ID(), i+1) g.Printf("func cgo_func_%[1]s_setter_%[2]d(self cgo_type_%[1]s, v %[3]s) {\n", s.ID(), i+1, ftname, ) g.Indent() fset := "v" if needWrapType(ft) { fset = fmt.Sprintf("*(*%s.%s)(unsafe.Pointer(v))", f.Pkg().Name(), types.TypeString(f.Type(), types.RelativeTo(f.Pkg())), ) } 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 cgo_func_%[1]s_new\n", s.ID()) g.Printf("func cgo_func_%[1]s_new() cgo_type_%[1]s {\n", s.ID()) g.Indent() g.Printf("o := %[1]s.%[2]s{}\n", pkgname, s.GoName()) g.Printf("cgopy_incref(unsafe.Pointer(&o))\n") g.Printf("return (cgo_type_%[1]s)(unsafe.Pointer(&o))\n", s.ID()) g.Outdent() g.Printf("}\n\n") // support for __str__ g.Printf("//export cgo_func_%[1]s_str\n", s.ID()) g.Printf( "func cgo_func_%[1]s_str(self %[2]s) string {\n", s.ID(), s.sym.cgoname, ) g.Indent() if (s.prots & ProtoStringer) == 0 { g.Printf("return fmt.Sprintf(\"%%#v\", ") g.Printf("*(*%[1]s.%[2]s)(unsafe.Pointer(self)))\n", pkgname, s.GoName()) } else { g.Printf("return (*%[1]s.%[2]s)(unsafe.Pointer(self)).String()\n", pkgname, s.GoName(), ) } g.Outdent() g.Printf("}\n\n") } func (g *goGen) genMethod(s Struct, m Func) { sig := m.Signature() params := "(self cgo_type_" + s.ID() if len(sig.Params()) > 0 { params += ", " + g.tupleString(sig.Params()) } params += ")" ret := " (" + g.tupleString(sig.Results()) + ") " g.Printf("//export cgo_func_%[1]s\n", m.ID()) g.Printf("func cgo_func_%[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.sym.cgoname) } g.Printf("_gopy_%03d", i) if res.needWrap() { g.Printf("))") } } g.Printf("\n") } func (g *goGen) genConst(o Const) { sym := o.sym tname := types.TypeString(o.obj.Type(), types.RelativeTo(o.obj.Pkg())) if strings.HasPrefix(tname, "untyped ") { tname = string(tname[len("untyped "):]) } g.Printf("//export cgo_func_%s_get\n", o.id) g.Printf("func cgo_func_%[1]s_get() %[2]s {\n", o.id, sym.cgotypename()) g.Indent() g.Printf("return %s.%s\n", o.pkg.Name(), o.obj.Name()) g.Outdent() g.Printf("}\n\n") } func (g *goGen) genVar(o Var) { pkgname := o.pkg.Name() typ := o.GoType() ret := o.sym.cgotypename() g.Printf("//export cgo_func_%s_get\n", o.id) g.Printf("func cgo_func_%[1]s_get() %[2]s {\n", o.id, ret) g.Indent() if o.needWrap() { g.Printf("cgopy_incref(unsafe.Pointer(&%s.%s))\n", pkgname, o.Name()) } g.Printf("return ") if o.needWrap() { g.Printf("%s(unsafe.Pointer(&", ret) } g.Printf("%s.%s", pkgname, o.Name()) if o.needWrap() { g.Printf("))") } g.Printf("\n") g.Outdent() g.Printf("}\n\n") g.Printf("//export cgo_func_%s_set\n", o.id) g.Printf("func cgo_func_%[1]s_set(v %[2]s) {\n", o.id, ret) g.Indent() vset := "v" if needWrapType(typ) { vset = fmt.Sprintf("*(*%s)(unsafe.Pointer(v))", types.TypeString(typ, func(*types.Package) string { return pkgname }), ) } g.Printf( "%[1]s.%[2]s = %[3]s\n", pkgname, o.Name(), vset, ) g.Outdent() g.Printf("}\n\n") } func (g *goGen) genType(sym *symbol) { if !sym.isType() { return } if sym.isStruct() { return } if sym.isBasic() && !sym.isNamed() { return } pkgname := sym.pkgname() goname := sym.goname if sym.isNamed() { goname = pkgname + "." + goname } g.Printf("\n// --- wrapping %s.%s ---\n\n", pkgname, sym.goname) g.Printf("//export %[1]s\n", sym.cgoname) g.Printf("// %[1]s wraps %[2]s.%[3]s\n", sym.cgoname, pkgname, sym.goname) if sym.isBasic() { // we need to reach at the underlying type btyp := sym.GoType().Underlying().String() g.Printf("type %[1]s %[2]s\n\n", sym.cgoname, btyp) } else { g.Printf("type %[1]s unsafe.Pointer\n\n", sym.cgoname) } g.Printf("//export cgo_func_%[1]s_new\n", sym.id) g.Printf("func cgo_func_%[1]s_new() %[2]s {\n", sym.id, sym.cgoname) g.Indent() g.Printf("var o %[1]s\n", goname) if sym.isBasic() { g.Printf("return %[1]s(o)\n", sym.cgoname) } else { g.Printf("cgopy_incref(unsafe.Pointer(&o))\n") g.Printf("return (%[1]s)(unsafe.Pointer(&o))\n", sym.cgoname) } g.Outdent() g.Printf("}\n\n") // support for __str__ g.Printf("//export cgo_func_%[1]s_str\n", sym.id) g.Printf( "func cgo_func_%[1]s_str(self %[2]s) string {\n", sym.id, sym.cgoname, ) g.Indent() g.Printf("return fmt.Sprintf(\"%%#v\", ") if sym.isBasic() { g.Printf("%[1]s(self))\n", goname) } else { g.Printf("*(*%[1]s)(unsafe.Pointer(self)))\n", goname) } g.Outdent() g.Printf("}\n\n") if sym.isArray() || sym.isSlice() { var etyp types.Type switch typ := sym.GoType().(type) { case *types.Array: etyp = typ.Elem() case *types.Slice: etyp = typ.Elem() case *types.Named: switch typ := typ.Underlying().(type) { case *types.Array: etyp = typ.Elem() case *types.Slice: etyp = typ.Elem() default: panic(fmt.Errorf("gopy: unhandled type [%#v]", typ)) } default: panic(fmt.Errorf("gopy: unhandled type [%#v]", typ)) } esym := g.pkg.syms.symtype(etyp) if esym == nil { panic(fmt.Errorf("gopy: could not retrieve element type of %#v", sym, )) } // support for __getitem__ g.Printf("//export cgo_func_%[1]s_item\n", sym.id) g.Printf( "func cgo_func_%[1]s_item(self %[2]s, i int) %[3]s {\n", sym.id, sym.cgoname, esym.cgotypename(), ) g.Indent() g.Printf("arr := (*%[1]s)(unsafe.Pointer(self))\n", goname) g.Printf("elt := (*arr)[i]\n") if !esym.isBasic() { g.Printf("cgopy_incref(unsafe.Pointer(&elt))\n") g.Printf("return (%[1]s)(unsafe.Pointer(&elt))\n", esym.cgotypename()) } else { if esym.isNamed() { g.Printf("return %[1]s(elt)\n", esym.cgotypename()) } else { g.Printf("return elt\n") } } g.Outdent() g.Printf("}\n\n") // support for __setitem__ g.Printf("//export cgo_func_%[1]s_ass_item\n", sym.id) g.Printf("func cgo_func_%[1]s_ass_item(self %[2]s, i int, v %[3]s) {\n", sym.id, sym.cgoname, esym.cgotypename(), ) g.Indent() g.Printf("arr := (*%[1]s)(unsafe.Pointer(self))\n", goname) g.Printf("(*arr)[i] = ") if !esym.isBasic() { g.Printf("*(*%[1]s)(unsafe.Pointer(v))\n", esym.cgotypename()) } else { if esym.isNamed() { g.Printf("%[1]s.%[2]s(v)\n", esym.pkgname(), esym.goname) } else { g.Printf("v\n") } } g.Outdent() g.Printf("}\n\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() sym := v.sym //str = append(str, n+" "+qualifiedType(typ)) tname := sym.cgotypename() str = append(str, n+" "+tname) } return strings.Join(str, ", ") }