diff --git a/bind/bind.go b/bind/bind.go index d87b629..c49e1d8 100644 --- a/bind/bind.go +++ b/bind/bind.go @@ -96,16 +96,25 @@ func (p *Package) getDoc(o types.Object) string { func GenCPython(w io.Writer, fset *token.FileSet, pkg *Package) error { buf := new(bytes.Buffer) gen := &cpyGen{ - printer: &printer{buf: buf, indentEach: []byte("\t")}, - fset: fset, - pkg: pkg, + decl: &printer{buf: buf, indentEach: []byte("\t")}, + impl: &printer{buf: buf, indentEach: []byte("\t")}, + fset: fset, + pkg: pkg, } err := gen.gen() if err != nil { return err } - _, err = io.Copy(w, gen.buf) + _, err = io.Copy(w, gen.decl.buf) + if err != nil { + return err + } + + _, err = io.Copy(w, gen.impl.buf) + if err != nil { + return err + } return err } diff --git a/bind/gencpy.go b/bind/gencpy.go index 54b84c5..cad715a 100644 --- a/bind/gencpy.go +++ b/bind/gencpy.go @@ -7,6 +7,7 @@ package bind import ( "fmt" "go/token" + "path/filepath" "strings" "golang.org/x/tools/go/types" @@ -14,92 +15,55 @@ import ( const ( cPreamble = `/* - C stubs for package py_%[1]s. - gopy-gen -lang=python %[1]s + C stubs for package %[1]s. + gopy gen -lang=python %[1]s - File is generated by gopy-gen. Do not edit. + File is generated by gopy gen. Do not edit. */ -#include "_cgo_export.h" +#ifdef _POSIX_C_SOURCE +#undef _POSIX_C_SOURCE +#endif #include "Python.h" +#include "structmember.h" -/* stdlib */ -#include -#include -#include -#include - -// helpers for CGo runtime - -typedef struct { char *p; int n; } _cgopy_gostring_; -typedef struct { void* array; unsigned int len; unsigned int cap; } _cgopy_goslice_; - - -extern void crosscall2(void (*fn)(void *, int), void *, int); -extern void _cgo_allocate(void *, int); -extern void _cgo_panic(void *, int); - -static void *_cgopy_goallocate(size_t len) { - struct { - size_t len; - void *ret; - } a; - a.len = len; - crosscall2(_cgo_allocate, &a, (int) sizeof a); - return a.ret; -} - -static void _cgopy_gopanic(const char *p) { - struct { - const char *p; - } a; - a.p = p; - crosscall2(_cgo_panic, &a, (int) sizeof a); -} - -static GoString _cgopy_makegostringn(const char *p, size_t l) { - _cgopy_gostring_ ret; - ret.p = (char*)_cgopy_goallocate(l + 1); - memcpy(ret.p, p, l); - ret.n = l; - return *(GoString*)(&ret); -} - -static GoString _cgopy_makegostring(const char *p) { - _cgopy_gostring_ ret; - size_t l; - l = strlen(p); - ret.p = (char*)_cgopy_goallocate(l); - memcpy(ret.p, p, l); - ret.n = l; - return *(GoString*)(&ret); -} - -static char* _cgopy_makecstring(const GoString *gostr) { - _cgopy_gostring_ *s = (_cgopy_gostring_*)(gostr); - char* cstr = (char*)malloc((s->n+1) * sizeof(char)); - memcpy(cstr, s->p, s->n); - cstr[s->n] = '\0'; - return cstr; -} +// header exported from 'go tool cgo' +#include "%[3]s.h" ` ) type cpyGen struct { - *printer + decl *printer + impl *printer fset *token.FileSet pkg *Package err ErrorList } +type cpyFunc struct { + typ *types.Func +} + +type cpyStruct struct { + obj *types.TypeName + typ *types.Struct +} + +func (c *cpyStruct) prefix(s string) string { + pkgname := c.obj.Pkg().Name() + return pkgname + s + c.obj.Name() +} + func (g *cpyGen) gen() error { g.genPreamble() - var funcs []string + var funcs []cpyFunc + var structs []cpyStruct + docs := make(map[string]string) scope := g.pkg.pkg.Scope() @@ -121,16 +85,15 @@ func (g *cpyGen) gen() error { panic(fmt.Errorf("not yet supported: %v (%T)", obj, obj)) case *types.Func: - funcs = append(funcs, obj.Name()) - g.genFunc(obj) + funcs = append(funcs, cpyFunc{typ: obj}) docs[obj.Name()] = g.pkg.getDoc(obj) case *types.TypeName: named := obj.Type().(*types.Named) switch typ := named.Underlying().(type) { case *types.Struct: - // TODO(sbinet) - panic(fmt.Errorf("not yet supported: %v (%T)", typ, obj)) + structs = append(structs, cpyStruct{typ: typ, obj: obj}) + case *types.Interface: // TODO(sbinet) panic(fmt.Errorf("not yet supported: %v (%T)", typ, obj)) @@ -146,26 +109,52 @@ func (g *cpyGen) gen() error { } } - g.Printf("static PyMethodDef GoPy_%s_Methods[] = {\n", g.pkg.pkg.Name()) - g.Indent() - for _, name := range funcs { + // first, process structs + for _, s := range structs { + g.genStruct(s) + } + + for _, f := range funcs { + g.genFunc(f.typ) + } + + g.impl.Printf("static PyMethodDef GoPy_%s_Methods[] = {\n", g.pkg.pkg.Name()) + g.impl.Indent() + for _, f := range funcs { + name := f.typ.Name() //obj := scope.Lookup(name) - g.Printf("{%[1]q, %[2]s, METH_VARARGS, %[3]q},\n", + g.impl.Printf("{%[1]q, %[2]s, METH_VARARGS, %[3]q},\n", name, "gopy_"+name, docs[name], ) } - g.Printf("{NULL, NULL, 0, NULL} /* Sentinel */\n") - g.Outdent() - g.Printf("};\n\n") + g.impl.Printf("{NULL, NULL, 0, NULL} /* Sentinel */\n") + g.impl.Outdent() + g.impl.Printf("};\n\n") + + g.impl.Printf("PyMODINIT_FUNC\ninit%[1]s(void)\n{\n", g.pkg.pkg.Name()) + g.impl.Indent() + g.impl.Printf("PyObject *module = NULL;\n\n") - g.Printf("PyMODINIT_FUNC\ninit%[1]s(void)\n{\n", g.pkg.pkg.Name()) - g.Indent() - g.Printf("(void) Py_InitModule3(%[1]q, GoPy_%[1]s_Methods, %[2]q);\n", + for _, s := range structs { + prefix := s.prefix("_") + g.impl.Printf("if (PyType_Ready(&_gopy_%sType) < 0) { return; }\n", prefix) + } + + g.impl.Printf("module = Py_InitModule3(%[1]q, GoPy_%[1]s_Methods, %[2]q);\n\n", g.pkg.pkg.Name(), g.pkg.doc.Doc, ) - g.Outdent() - g.Printf("}\n\n") + + for _, s := range structs { + prefix := s.prefix("_") + + g.impl.Printf("Py_INCREF(&_gopy_%sType);\n", prefix) + g.impl.Printf("PyModule_AddObject(module, %q, (PyObject*)&_gopy_%sType);\n\n", + s.obj.Name(), prefix, + ) + } + g.impl.Outdent() + g.impl.Printf("}\n\n") if len(g.err) > 0 { return g.err @@ -176,7 +165,7 @@ func (g *cpyGen) gen() error { func (g *cpyGen) genFunc(o *types.Func) { - g.Printf(` + g.impl.Printf(` /* pythonization of: %[2]s.%[1]s */ static PyObject* gopy_%[1]s(PyObject *self, PyObject *args) { @@ -184,10 +173,10 @@ gopy_%[1]s(PyObject *self, PyObject *args) { o.Name(), g.pkg.pkg.Name(), ) - g.Indent() + g.impl.Indent() g.genFuncBody(o) - g.Outdent() - g.Printf("}\n\n") + g.impl.Outdent() + g.impl.Printf("}\n\n") } func (g *cpyGen) genFuncBody(o *types.Func) { @@ -198,7 +187,7 @@ func (g *cpyGen) genFuncBody(o *types.Func) { res := newVars(sig.Results()) args := newVars(sig.Params()) for _, arg := range args { - arg.genDecl(g) + arg.genDecl(g.impl) funcArgs = append(funcArgs, arg.getFuncArg()) } @@ -207,9 +196,9 @@ func (g *cpyGen) genFuncBody(o *types.Func) { switch len(res) { case 1: ret := res[0] - ret.genRetDecl(g) + ret.genRetDecl(g.impl) default: - g.Printf("struct %[1]s_return c_gopy_ret;\n", o.Name()) + g.impl.Printf("struct %[1]s_return c_gopy_ret;\n", o.Name()) /* for i := 0; i < res.Len(); i++ { ret := res.At(i) @@ -217,16 +206,16 @@ func (g *cpyGen) genFuncBody(o *types.Func) { if n == "" { n = "gopy_" + strconv.Itoa(i) } - g.Printf("%[1]s c_%[2]s;\n", ctypeName(ret.Type()), n) + g.impl.Printf("%[1]s c_%[2]s;\n", ctypeName(ret.Type()), n) } */ } } - g.Printf("\n") + g.impl.Printf("\n") if len(args) > 0 { - g.Printf("if (!PyArg_ParseTuple(args, ") + g.impl.Printf("if (!PyArg_ParseTuple(args, ") format := []string{} pyaddrs := []string{} for _, arg := range args { @@ -234,30 +223,30 @@ func (g *cpyGen) genFuncBody(o *types.Func) { format = append(format, pyfmt) pyaddrs = append(pyaddrs, addr) } - g.Printf("%q, %s)) {\n", strings.Join(format, ""), strings.Join(pyaddrs, ", ")) - g.Indent() - g.Printf("return NULL;\n") - g.Outdent() - g.Printf("}\n\n") + g.impl.Printf("%q, %s)) {\n", strings.Join(format, ""), strings.Join(pyaddrs, ", ")) + g.impl.Indent() + g.impl.Printf("return NULL;\n") + g.impl.Outdent() + g.impl.Printf("}\n\n") } if len(args) > 0 { for _, arg := range args { - arg.genFuncPreamble(g) + arg.genFuncPreamble(g.impl) } - g.Printf("\n") + g.impl.Printf("\n") } if len(res) > 0 { - g.Printf("c_gopy_ret = ") + g.impl.Printf("c_gopy_ret = ") } - g.Printf("GoPy_%[1]s(%[2]s);\n", o.Name(), strings.Join(funcArgs, ", ")) + g.impl.Printf("GoPy_%[1]s(%[2]s);\n", o.Name(), strings.Join(funcArgs, ", ")) - g.Printf("\n") + g.impl.Printf("\n") if len(res) <= 0 { - g.Printf("Py_INCREF(Py_None);\nreturn Py_None;\n") + g.impl.Printf("Py_INCREF(Py_None);\nreturn Py_None;\n") return } @@ -277,14 +266,286 @@ func (g *cpyGen) genFuncBody(o *types.Func) { } } - g.Printf("return Py_BuildValue(%q, %s);\n", + g.impl.Printf("return Py_BuildValue(%q, %s);\n", strings.Join(format, ""), strings.Join(funcArgs, ", "), ) - //g.Printf("return NULL;\n") + //g.impl.Printf("return NULL;\n") +} + +func (g *cpyGen) genStruct(cpy cpyStruct) { + obj := cpy.obj + + pkgname := obj.Pkg().Name() + prefix := cpy.prefix("_") + + //fmt.Printf("obj: %#v\ntyp: %#v\n", obj, typ) + g.decl.Printf("/* --- decls for struct %s.%v --- */\n", pkgname, obj.Name()) + g.decl.Printf("typedef void* GoPy_%s;\n\n", obj.Name()) + g.decl.Printf("/* type for struct %s.%v\n", pkgname, obj.Name()) + g.decl.Printf(" */\ntypedef struct {\n") + g.decl.Indent() + g.decl.Printf("PyObject_HEAD\n") + g.decl.Printf("GoPy_%[1]s cgopy; /* unsafe.Pointer to %[1]s */\n", obj.Name()) + g.decl.Outdent() + g.decl.Printf("} _gopy_%s;\n", prefix) + g.decl.Printf("\n\n") + + g.impl.Printf("/* --- impl for %s.%v */\n\n", pkgname, obj.Name()) + + g.genStructNew(cpy) + g.genStructDealloc(cpy) + g.genStructInit(cpy) + g.genStructMembers(cpy) + g.genStructMethods(cpy) + + g.impl.Printf("static PyTypeObject _gopy_%sType = {\n", prefix) + g.impl.Indent() + g.impl.Printf("PyObject_HEAD_INIT(NULL)\n") + g.impl.Printf("0,\t/*ob_size*/\n") + g.impl.Printf("\"%s.%s\",\t/*tp_name*/\n", pkgname, obj.Name()) + g.impl.Printf("sizeof(_gopy_%s),\t/*tp_basicsize*/\n", prefix) + g.impl.Printf("0,\t/*tp_itemsize*/\n") + g.impl.Printf("(destructor)_gopy_%s_dealloc,\t/*tp_dealloc*/\n", prefix) + g.impl.Printf("0,\t/*tp_print*/\n") + g.impl.Printf("0,\t/*tp_getattr*/\n") + g.impl.Printf("0,\t/*tp_setattr*/\n") + g.impl.Printf("0,\t/*tp_compare*/\n") + g.impl.Printf("0,\t/*tp_repr*/\n") + g.impl.Printf("0,\t/*tp_as_number*/\n") + g.impl.Printf("0,\t/*tp_as_sequence*/\n") + g.impl.Printf("0,\t/*tp_as_mapping*/\n") + g.impl.Printf("0,\t/*tp_hash */\n") + g.impl.Printf("0,\t/*tp_call*/\n") + g.impl.Printf("0,\t/*tp_str*/\n") + g.impl.Printf("0,\t/*tp_getattro*/\n") + g.impl.Printf("0,\t/*tp_setattro*/\n") + g.impl.Printf("0,\t/*tp_as_buffer*/\n") + g.impl.Printf("Py_TPFLAGS_DEFAULT,\t/*tp_flags*/\n") + g.impl.Printf("%q,\t/* tp_doc */\n", g.pkg.getDoc(obj)) + g.impl.Printf("0,\t/* tp_traverse */\n") + g.impl.Printf("0,\t/* tp_clear */\n") + g.impl.Printf("0,\t/* tp_richcompare */\n") + g.impl.Printf("0,\t/* tp_weaklistoffset */\n") + g.impl.Printf("0,\t/* tp_iter */\n") + g.impl.Printf("0,\t/* tp_iternext */\n") + g.impl.Printf("_gopy_%s_methods, /* tp_methods */\n", prefix) + g.impl.Printf("0,\t/* tp_members */\n") + g.impl.Printf("_gopy_%s_getsets,\t/* tp_getset */\n", prefix) + g.impl.Printf("0,\t/* tp_base */\n") + g.impl.Printf("0,\t/* tp_dict */\n") + g.impl.Printf("0,\t/* tp_descr_get */\n") + g.impl.Printf("0,\t/* tp_descr_set */\n") + g.impl.Printf("0,\t/* tp_dictoffset */\n") + g.impl.Printf("(initproc)_gopy_%s_init, /* tp_init */\n", prefix) + g.impl.Printf("0, /* tp_alloc */\n") + g.impl.Printf("_gopy_%s_new,\t/* tp_new */\n", prefix) + g.impl.Outdent() + g.impl.Printf("};\n\n") + +} + +func (g *cpyGen) genStructDealloc(cpy cpyStruct) { + obj := cpy.obj + pkgname := obj.Pkg().Name() + prefix := cpy.prefix("_") + + g.decl.Printf("/* tp_dealloc for %s.%v */\n", pkgname, obj.Name()) + g.decl.Printf("static void\n_gopy_%[1]s_dealloc(_gopy_%[1]s *self);\n", prefix) + + g.impl.Printf("/* tp_dealloc for %s.%v */\n", pkgname, obj.Name()) + g.impl.Printf("static void\n_gopy_%[1]s_dealloc(_gopy_%[1]s *self) {\n", prefix) + g.impl.Indent() + g.impl.Printf("self->ob_type->tp_free((PyObject*)self);\n") + g.impl.Outdent() + g.impl.Printf("}\n\n") +} + +func (g *cpyGen) genStructNew(cpy cpyStruct) { + obj := cpy.obj + pkgname := obj.Pkg().Name() + prefix := cpy.prefix("_") + + g.decl.Printf("/* tp_new for %s.%v */\n", pkgname, obj.Name()) + g.decl.Printf("static PyObject*\n_gopy_%s_new(PyTypeObject *type, PyObject *args, PyObject *kwds);\n", prefix) + + g.impl.Printf("/* tp_new */\n") + g.impl.Printf("static PyObject*\n_gopy_%s_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {\n", prefix) + g.impl.Indent() + g.impl.Printf("_gopy_%s *self;\n", prefix) + g.impl.Printf("self = (_gopy_%s *)type->tp_alloc(type, 0);\n", prefix) + g.impl.Printf("self->cgopy = GoPy_%s_new();\n", obj.Name()) + g.impl.Printf("return (PyObject*)self;\n") + g.impl.Outdent() + g.impl.Printf("}\n\n") +} + +func (g *cpyGen) genStructInit(cpy cpyStruct) { + obj := cpy.obj + pkgname := obj.Pkg().Name() + prefix := cpy.prefix("_") + + g.decl.Printf("/* tp_init for %s.%v */\n", pkgname, obj.Name()) + g.decl.Printf( + "static int\n_gopy_%[1]s_init(_gopy_%[1]s *self, PyObject *args, PyObject *kwds);\n", + prefix, + ) + + g.impl.Printf("/* tp_init */\n") + g.impl.Printf( + "static int\n_gopy_%[1]s_init(_gopy_%[1]s *self, PyObject *args, PyObject *kwds) {\n", + prefix, + ) + g.impl.Indent() + g.impl.Printf("return 0;\n") + g.impl.Outdent() + g.impl.Printf("}\n\n") +} + +func (g *cpyGen) genStructMembers(cpy cpyStruct) { + + obj := cpy.obj + pkgname := obj.Pkg().Name() + prefix := cpy.prefix("_") + + g.decl.Printf("/* tp_getset for %s.%v */\n", pkgname, obj.Name()) + for i := 0; i < cpy.typ.NumFields(); i++ { + f := cpy.typ.Field(i) + if !f.Exported() { + continue + } + ft := f.Type() + g.decl.Printf("static PyObject*\n") + g.decl.Printf( + "_gopy_%[1]s_getter_%[2]d(_gopy_%[1]s *self, void *closure); /* %[3]s */\n", + prefix, + i+1, + f.Name(), + ) + + g.impl.Printf("static PyObject*\n") + g.impl.Printf( + "_gopy_%[1]s_getter_%[2]d(_gopy_%[1]s *self, void *closure) /* %[3]s */ {\n", + prefix, + i+1, + f.Name(), + ) + g.impl.Indent() + ftname := cgoTypeName(ft) + if needWrapType(ft) { + ftname = fmt.Sprintf("GoPy_%[1]s_field_%d", obj.Name(), i+1) + g.impl.Printf( + "%[1]s ret = GoPy_%[2]s_getter_%[3]d(self->cgopy);\n", + ftname, + obj.Name(), + i+1, + ) + } else { + g.impl.Printf( + "%[1]s ret = GoPy_%[2]s_getter_%[3]d(self->cgopy);\n", + ftname, + obj.Name(), + i+1, + ) + } + g.impl.Printf("Py_RETURN_NONE;\n") // FIXME(sbinet) + g.impl.Outdent() + g.impl.Printf("}\n\n") + + g.decl.Printf("static int\n") + g.decl.Printf( + "_gopy_%[1]s_setter_%[2]d(_gopy_%[1]s *self, PyObject *value, void *closure);\n", + prefix, + i+1, + ) + + g.impl.Printf("static int\n") + g.impl.Printf( + "_gopy_%[1]s_setter_%[2]d(_gopy_%[1]s *self, PyObject *value, void *closure) {\n", + prefix, + i+1, + ) + g.impl.Indent() + g.impl.Printf("return 0;\n") // FIXME(sbinet) + g.impl.Outdent() + g.impl.Printf("}\n\n") + } + + g.impl.Printf("/* tp_getset for %s.%v */\n", pkgname, obj.Name()) + g.impl.Printf("static PyGetSetDef _gopy_%s_getsets[] = {\n", prefix) + g.impl.Indent() + for i := 0; i < cpy.typ.NumFields(); i++ { + f := cpy.typ.Field(i) + if !f.Exported() { + continue + } + doc := "doc for " + f.Name() // FIXME(sbinet) retrieve doc for fields + g.impl.Printf("{%q, ", f.Name()) + g.impl.Printf("(getter)_gopy_%[1]s_getter_%[2]d, ", prefix, i+1) + g.impl.Printf("(setter)_gopy_%[1]s_setter_%[2]d, ", prefix, i+1) + g.impl.Printf("%q, NULL},\n", doc) + } + g.impl.Printf("{NULL} /* Sentinel */\n") + g.impl.Outdent() + g.impl.Printf("};\n\n") + /* + static PyGetSetDef Noddy_getseters[] = { + {"first", + (getter)Noddy_getfirst, (setter)Noddy_setfirst, + "first name", + NULL}, + {"last", + (getter)Noddy_getlast, (setter)Noddy_setlast, + "last name", + NULL}, + {NULL} + }; + */ + /* + static PyObject * + Noddy_getfirst(Noddy *self, void *closure) + { + Py_INCREF(self->first); + return self->first; + } + + static int + Noddy_setfirst(Noddy *self, PyObject *value, void *closure) + { + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); + return -1; + } + + if (! PyString_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "The first attribute value must be a string"); + return -1; + } + + Py_DECREF(self->first); + Py_INCREF(value); + self->first = value; + + return 0; + } + */ + +} + +func (g *cpyGen) genStructMethods(cpy cpyStruct) { + + prefix := cpy.prefix("_") + + g.impl.Printf("static PyMethodDef _gopy_%s_methods[] = {\n", prefix) + g.impl.Indent() + g.impl.Printf("{NULL} /* sentinel */\n") + g.impl.Outdent() + g.impl.Printf("};\n\n") + } func (g *cpyGen) genPreamble() { n := g.pkg.pkg.Name() - g.Printf(cPreamble, n, g.pkg.pkg.Path()) + g.decl.Printf(cPreamble, n, g.pkg.pkg.Path(), filepath.Base(n)) } diff --git a/bind/gengo.go b/bind/gengo.go index e620839..056a1ba 100644 --- a/bind/gengo.go +++ b/bind/gengo.go @@ -7,25 +7,45 @@ package bind import ( "fmt" "go/token" + "path/filepath" "strings" "golang.org/x/tools/go/types" ) const ( - goPreamble = `// Package py_%[1]s is an autogenerated binder stub for package %[1]s. -// gopy-gen -lang=go %[1]s + 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 py_%[1]s +// File is generated by gopy gen. Do not edit. +package main -// void init%[1]s(void); +//#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) +} + +// --- end cgo helpers --- ` ) @@ -41,8 +61,6 @@ func (g *goGen) gen() error { g.genPreamble() - var funcs []string - scope := g.pkg.pkg.Scope() names := scope.Names() for _, name := range names { @@ -62,15 +80,14 @@ func (g *goGen) gen() error { panic(fmt.Errorf("not yet supported: %v (%T)", obj, obj)) case *types.Func: - funcs = append(funcs, obj.Name()) g.genFunc(obj) case *types.TypeName: named := obj.Type().(*types.Named) switch typ := named.Underlying().(type) { case *types.Struct: - // TODO(sbinet) - panic(fmt.Errorf("not yet supported: %v (%T)", typ, obj)) + g.genStruct(obj, typ) + case *types.Interface: // TODO(sbinet) panic(fmt.Errorf("not yet supported: %v (%T)", typ, obj)) @@ -86,9 +103,11 @@ func (g *goGen) gen() error { } } - g.Printf("// Register registers the generated CPython module with the CPython runtime\nfunc Register() {\n") + g.Printf("// buildmode=c-shared needs a 'main'\n\nfunc main() {}\n") + g.Printf("// tickle cgo\nfunc init() {\n") g.Indent() - g.Printf("C.init%[1]s()\n", g.pkg.pkg.Name()) + g.Printf("str := C.CString(%q)\n", g.pkg.Name()) + g.Printf("C.free(unsafe.Pointer(str))\n") g.Outdent() g.Printf("}\n") @@ -102,13 +121,23 @@ func (g *goGen) gen() error { func (g *goGen) genFunc(o *types.Func) { sig := o.Type().(*types.Signature) + params := "(" + g.tupleString(sig.Params()) + ")" + ret := g.tupleString(sig.Results()) + if sig.Results().Len() > 1 { + ret = "(" + ret + ") " + } else { + ret += " " + } + + //funcName := o.Name() g.Printf(` //export GoPy_%[1]s // GoPy_%[1]s wraps %[2]s -func GoPy_%[1]s%[3]v { +func GoPy_%[1]s%[3]v%[4]v{ `, o.Name(), o.FullName(), - strings.TrimLeft(sig.String(), "func "), + params, + ret, ) g.Indent() @@ -120,17 +149,18 @@ func GoPy_%[1]s%[3]v { func (g *goGen) genFuncBody(o *types.Func) { sig := o.Type().(*types.Signature) - ret := "" - if sig.Results().Len() > 0 { - ret = "return " + results := newVars(sig.Results()) + for i := range results { + if i > 0 { + g.Printf(", ") + } + g.Printf("_gopy_%03d", i) + } + if len(results) > 0 { + g.Printf(" := ") } - g.Printf( - "%[3]s%[1]s.%[2]s(", - g.pkg.pkg.Name(), - o.Name(), - ret, - ) + g.Printf("%s.%s(", g.pkg.Name(), o.Name()) args := sig.Params() for i := 0; i < args.Len(); i++ { @@ -143,9 +173,118 @@ func (g *goGen) genFuncBody(o *types.Func) { } 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(obj *types.TypeName, typ *types.Struct) { + //fmt.Printf("obj: %#v\ntyp: %#v\n", obj, typ) + pkgname := obj.Pkg().Name() + g.Printf("//export GoPy_%[1]s\n", obj.Name()) + g.Printf("type GoPy_%[1]s unsafe.Pointer\n\n", obj.Name()) + + 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", obj.Name(), i+1) + g.Printf("//export %s\n", ftname) + g.Printf("type %s unsafe.Pointer\n\n", ftname) + } + + g.Printf("//export GoPy_%[1]s_getter_%[2]d\n", obj.Name(), i+1) + g.Printf("func GoPy_%[1]s_getter_%[2]d(self GoPy_%[1]s) %[3]s {\n", + obj.Name(), i+1, + ftname, + ) + g.Indent() + g.Printf( + "ret := (*%[1]s)(unsafe.Pointer(self))\n", + pkgname+"."+obj.Name(), + ) + + if needWrapType(f.Type()) { + dt := getTypedesc(f.Type()) + 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") + } + + g.Printf("//export GoPy_%[1]s_new\n", obj.Name()) + g.Printf("func GoPy_%[1]s_new() GoPy_%[1]s {\n", obj.Name()) + g.Indent() + g.Printf("return (GoPy_%[1]s)(unsafe.Pointer(&%[2]s.%[1]s{}))\n", + obj.Name(), + pkgname, + ) + g.Outdent() + g.Printf("}\n\n") } func (g *goGen) genPreamble() { n := g.pkg.pkg.Name() - g.Printf(goPreamble, n, g.pkg.pkg.Path()) + g.Printf(goPreamble, n, g.pkg.pkg.Path(), filepath.Base(n)) +} + +func (g *goGen) tupleString(tuple *types.Tuple) string { + n := tuple.Len() + if n <= 0 { + return "" + } + + str := make([]string, 0, n) + for i := 0; i < tuple.Len(); i++ { + v := tuple.At(i) + n := v.Name() + typ := v.Type() + 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() + //return obj.Pkg().Name() + "." + obj.Name() + return "GoPy_" + obj.Name() + switch typ := typ.Underlying().(type) { + case *types.Struct: + return typ.String() + default: + return "GoPy_ooops_" + obj.Name() + } + } + + return fmt.Sprintf("%#T", typ) } diff --git a/bind/types.go b/bind/types.go index 15c1d6e..4dc3a3d 100644 --- a/bind/types.go +++ b/bind/types.go @@ -8,6 +8,19 @@ import ( "golang.org/x/tools/go/types" ) +func needWrapType(typ types.Type) bool { + switch typ.(type) { + case *types.Struct: + return true + case *types.Named: + switch typ.Underlying().(type) { + case *types.Struct: + return true + } + } + return false +} + func cTypeName(typ types.Type) string { switch typ := typ.(type) { case *types.Basic: diff --git a/bind/vars.go b/bind/vars.go index 8579331..ae353e1 100644 --- a/bind/vars.go +++ b/bind/vars.go @@ -5,6 +5,8 @@ package bind import ( + "fmt" + "golang.org/x/tools/go/types" ) @@ -22,17 +24,35 @@ func newVars(tuple *types.Tuple) []*Var { } func newVar(v *types.Var) *Var { - vv := &Var{ - Var: v, + return &Var{ + Var: v, + dtype: getTypedesc(v.Type()), } - switch typ := v.Type().(type) { +} + +func getTypedesc(t types.Type) typedesc { + switch typ := t.(type) { case *types.Basic: dtype, ok := typedescr[typ.Kind()] if ok { - vv.dtype = dtype + return dtype } + case *types.Named: + switch typ.Underlying().(type) { + case *types.Struct: + return typedesc{ + ctype: "GoPy_" + typ.Obj().Name(), + cgotype: "GoPy_" + typ.Obj().Name(), + pyfmt: "N", + } + } + case *types.Pointer: + elem := typ.Elem() + return getTypedesc(elem) + default: + panic(fmt.Errorf("unhandled type: %#v\n", typ)) } - return vv + return typedesc{} } func (v *Var) GoType() types.Type { @@ -59,14 +79,14 @@ func (v *Var) isGoString() bool { return false } -func (v *Var) genDecl(g *cpyGen) { +func (v *Var) genDecl(g *printer) { if v.isGoString() { g.Printf("const char* cgopy_%s;\n", v.Var.Name()) } g.Printf("%[1]s c_%[2]s;\n", v.CGoType(), v.Var.Name()) } -func (v *Var) genRetDecl(g *cpyGen) { +func (v *Var) genRetDecl(g *printer) { if v.isGoString() { g.Printf("const char* cgopy_gopy_ret;\n") } @@ -81,12 +101,17 @@ func (v *Var) getArgParse() (string, string) { return v.dtype.pyfmt, addr } -func (v *Var) genFuncPreamble(g *cpyGen) { +func (v *Var) genFuncPreamble(g *printer) { if v.isGoString() { - g.Printf("c_%[1]s = _cgopy_makegostring(cgopy_%[1]s);\n", v.Var.Name()) + g.Printf("c_%[1]s = CGoPy_GoString((char*)cgopy_%[1]s);\n", v.Var.Name()) } } func (v *Var) getFuncArg() string { return "c_" + v.Var.Name() } + +func (v *Var) needWrap() bool { + typ := v.GoType() + return needWrapType(typ) +}