summaryrefslogtreecommitdiff
path: root/src/cmd/cgo
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2010-04-09 13:31:05 -0700
committerIan Lance Taylor <iant@golang.org>2010-04-09 13:31:05 -0700
commit6dcdbbd6b9a46c29524ae9c1d4dced289e23fde4 (patch)
tree6cf30c19057d3d553958fea67419fd2da3ff0482 /src/cmd/cgo
parent0b75b44531202fcf43f4cfd39bcc853415cc6b57 (diff)
downloadgolang-6dcdbbd6b9a46c29524ae9c1d4dced289e23fde4.tar.gz
Add //export to cgo.
The new //export comment marks a Go function as callable from C. The syntax is "//export NAME" where NAME is the name of the function as seen from C. If such a comment is seen, cgo will generate two new files: _cgo_export.h and _cgo_export.c. The _cgo_export.h file provides declarations which C code may use to call Go functions. The _cgo_export.c file contains wrappers, and is to be compiled with gcc. The changes to Make.pkg support using this from a Go Makefile, though it could probably be more convenient. R=rsc CC=golang-dev http://codereview.appspot.com/853042
Diffstat (limited to 'src/cmd/cgo')
-rw-r--r--src/cmd/cgo/ast.go45
-rw-r--r--src/cmd/cgo/out.go308
2 files changed, 352 insertions, 1 deletions
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
index 0dfe9217a..580a72a95 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -13,6 +13,7 @@ import (
"go/parser"
"go/scanner"
"os"
+ "strings"
)
// A Cref refers to an expression of the form C.xxx in the AST.
@@ -25,6 +26,12 @@ type Cref struct {
FuncType *FuncType
}
+// A ExpFunc is an exported function, callable from C.
+type ExpFunc struct {
+ Func *ast.FuncDecl
+ ExpName string // name to use from C
+}
+
// A Prog collects information about a cgo program.
type Prog struct {
AST *ast.File // parsed AST
@@ -37,6 +44,7 @@ type Prog struct {
Funcdef map[string]*FuncType
Enumdef map[string]int64
Constdef map[string]string
+ ExpFuncs []*ExpFunc
PtrSize int64
GccOptions []string
OutDefs map[string]bool
@@ -303,6 +311,8 @@ func walk(x interface{}, p *Prog, context string) {
walk(n.Body, p, "stmt")
}
+ checkExpFunc(n, p)
+
case *ast.File:
walk(n.Decls, p, "decl")
@@ -329,3 +339,38 @@ func walk(x interface{}, p *Prog, context string) {
}
}
}
+
+// If a function should be exported add it to ExpFuncs.
+func checkExpFunc(n *ast.FuncDecl, p *Prog) {
+ if n.Doc == nil {
+ return
+ }
+ for _, c := range n.Doc.List {
+ if string(c.Text[0:9]) != "//export " {
+ continue
+ }
+
+ name := strings.TrimSpace(string(c.Text[9:]))
+ if name == "" {
+ error(c.Position, "export missing name")
+ }
+
+ if p.ExpFuncs == nil {
+ p.ExpFuncs = make([]*ExpFunc, 0, 8)
+ }
+ i := len(p.ExpFuncs)
+ if i >= cap(p.ExpFuncs) {
+ new := make([]*ExpFunc, 2*i)
+ for j, v := range p.ExpFuncs {
+ new[j] = v
+ }
+ p.ExpFuncs = new
+ }
+ p.ExpFuncs = p.ExpFuncs[0 : i+1]
+ p.ExpFuncs[i] = &ExpFunc{
+ Func: n,
+ ExpName: name,
+ }
+ break
+ }
+}
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 31df5a0c4..2fae48fe4 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -8,6 +8,7 @@ import (
"fmt"
"go/ast"
"go/printer"
+ "go/token"
"os"
"strings"
)
@@ -47,7 +48,7 @@ func (p *Prog) writeDefs() {
}
fmt.Fprintf(fgo2, "type _C_void [0]byte\n")
- fmt.Fprintf(fc, cProlog, pkgroot, pkgroot, pkgroot, pkgroot)
+ fmt.Fprintf(fc, cProlog, pkgroot, pkgroot, pkgroot, pkgroot, pkgroot)
for name, def := range p.Vardef {
fmt.Fprintf(fc, "#pragma dynimport ·_C_%s %s \"%s.so\"\n", name, name, path)
@@ -140,6 +141,8 @@ func (p *Prog) writeDefs() {
fmt.Fprintf(fc, "\n")
}
+ p.writeExports(fgo2, fc)
+
fgo2.Close()
fc.Close()
}
@@ -245,6 +248,294 @@ func (p *Prog) writeOutput(srcfile string) {
fgcc.Close()
}
+// Write out the various stubs we need to support functions exported
+// from Go so that they are callable from C.
+func (p *Prog) writeExports(fgo2, fc *os.File) {
+ if len(p.ExpFuncs) == 0 {
+ return
+ }
+
+ fgcc := creat("_cgo_export.c")
+ fgcch := creat("_cgo_export.h")
+
+ fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n")
+ fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog)
+
+ fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
+ fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n")
+
+ for _, exp := range p.ExpFuncs {
+ fn := exp.Func
+
+ // Construct a gcc struct matching the 6c argument and
+ // result frame.
+ structType := "struct {\n"
+ off := int64(0)
+ npad := 0
+ if fn.Recv != nil {
+ t := p.cgoType(fn.Recv.List[0].Type)
+ structType += fmt.Sprintf("\t\t%s recv;\n", t.C)
+ off += t.Size
+ }
+ fntype := fn.Type
+ forFieldList(fntype.Params,
+ func(i int, atype ast.Expr) {
+ t := p.cgoType(atype)
+ if off%t.Align != 0 {
+ pad := t.Align - off%t.Align
+ structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad)
+ off += pad
+ npad++
+ }
+ structType += fmt.Sprintf("\t\t%s p%d;\n", t.C, i)
+ off += t.Size
+ })
+ if off%p.PtrSize != 0 {
+ pad := p.PtrSize - off%p.PtrSize
+ structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad)
+ off += pad
+ npad++
+ }
+ forFieldList(fntype.Results,
+ func(i int, atype ast.Expr) {
+ t := p.cgoType(atype)
+ if off%t.Align != 0 {
+ pad := t.Align - off%t.Align
+ structType += fmt.Sprintf("\t\tchar __pad%d[%d]\n", npad, pad)
+ off += pad
+ npad++
+ }
+ structType += fmt.Sprintf("\t\t%s r%d;\n", t.C, i)
+ off += t.Size
+ })
+ if off%p.PtrSize != 0 {
+ pad := p.PtrSize - off%p.PtrSize
+ structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad)
+ off += pad
+ npad++
+ }
+ if structType == "struct {\n" {
+ structType += "\t\tchar unused;\n" // avoid empty struct
+ off++
+ }
+ structType += "\t}"
+
+ // Get the return type of the wrapper function
+ // compiled by gcc.
+ gccResult := ""
+ if fntype.Results == nil || len(fntype.Results.List) == 0 {
+ gccResult = "void"
+ } else if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 {
+ gccResult = p.cgoType(fntype.Results.List[0].Type).C
+ } else {
+ fmt.Fprintf(fgcch, "\n/* Return type for %s */\n", exp.ExpName)
+ fmt.Fprintf(fgcch, "struct %s_return {\n", exp.ExpName)
+ forFieldList(fntype.Results,
+ func(i int, atype ast.Expr) {
+ fmt.Fprintf(fgcch, "\t%s r%d;\n", p.cgoType(atype).C, i)
+ })
+ fmt.Fprintf(fgcch, "};\n")
+ gccResult = "struct " + exp.ExpName + "_return"
+ }
+
+ // Build the wrapper function compiled by gcc.
+ s := fmt.Sprintf("%s %s(", gccResult, exp.ExpName)
+ if fn.Recv != nil {
+ s += p.cgoType(fn.Recv.List[0].Type).C
+ s += " recv"
+ }
+ forFieldList(fntype.Params,
+ func(i int, atype ast.Expr) {
+ if i > 0 || fn.Recv != nil {
+ s += ", "
+ }
+ s += fmt.Sprintf("%s p%d", p.cgoType(atype).C, i)
+ })
+ s += ")"
+ fmt.Fprintf(fgcch, "\nextern %s;\n", s)
+
+ fmt.Fprintf(fgcc, "extern _cgoexp_%s(void *, int);\n", exp.ExpName)
+ fmt.Fprintf(fgcc, "\n%s\n", s)
+ fmt.Fprintf(fgcc, "{\n")
+ fmt.Fprintf(fgcc, "\t%s a;\n", structType)
+ if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
+ fmt.Fprintf(fgcc, "\t%s r;\n", gccResult)
+ }
+ if fn.Recv != nil {
+ fmt.Fprintf(fgcc, "\ta.recv = recv;\n")
+ }
+ forFieldList(fntype.Params,
+ func(i int, atype ast.Expr) {
+ fmt.Fprintf(fgcc, "\ta.p%d = p%d;\n", i, i)
+ })
+ fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp_%s, &a, (int) sizeof a);\n", exp.ExpName)
+ if gccResult != "void" {
+ if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 {
+ fmt.Fprintf(fgcc, "\treturn a.r0;\n")
+ } else {
+ forFieldList(fntype.Results,
+ func(i int, atype ast.Expr) {
+ fmt.Fprintf(fgcc, "\tr.r%d = a.r%d;\n", i, i)
+ })
+ fmt.Fprintf(fgcc, "\treturn r;\n")
+ }
+ }
+ fmt.Fprintf(fgcc, "}\n")
+
+ // Build the wrapper function compiled by 6c/8c
+ goname := exp.Func.Name.Name()
+ if fn.Recv != nil {
+ goname = "_cgoexpwrap_" + fn.Recv.List[0].Names[0].Name() + "_" + goname
+ }
+ fmt.Fprintf(fc, "#pragma dynexport _cgoexp_%s _cgoexp_%s\n", exp.ExpName, exp.ExpName)
+ fmt.Fprintf(fc, "extern void ·%s();\n", goname)
+ fmt.Fprintf(fc, "\nvoid\n")
+ fmt.Fprintf(fc, "_cgoexp_%s(void *a, int32 n)\n", exp.ExpName)
+ fmt.Fprintf(fc, "{\n")
+ fmt.Fprintf(fc, "\tcgocallback(·%s, a, n);\n", goname)
+ fmt.Fprintf(fc, "}\n")
+
+ // Calling a function with a receiver from C requires
+ // a Go wrapper function.
+ if fn.Recv != nil {
+ fmt.Fprintf(fgo2, "func %s(recv ", goname)
+ printer.Fprint(fgo2, fn.Recv.List[0].Type)
+ forFieldList(fntype.Params,
+ func(i int, atype ast.Expr) {
+ fmt.Fprintf(fgo2, ", p%d", i)
+ printer.Fprint(fgo2, atype)
+ })
+ fmt.Fprintf(fgo2, ")")
+ if gccResult != "void" {
+ fmt.Fprint(fgo2, " (")
+ forFieldList(fntype.Results,
+ func(i int, atype ast.Expr) {
+ if i > 0 {
+ fmt.Fprint(fgo2, ", ")
+ }
+ printer.Fprint(fgo2, atype)
+ })
+ fmt.Fprint(fgo2, ")")
+ }
+ fmt.Fprint(fgo2, " {\n")
+ fmt.Fprint(fgo2, "\t")
+ if gccResult != "void" {
+ fmt.Fprint(fgo2, "return ")
+ }
+ fmt.Fprintf(fgo2, "recv.%s(", exp.Func.Name)
+ forFieldList(fntype.Params,
+ func(i int, atype ast.Expr) {
+ if i > 0 {
+ fmt.Fprint(fgo2, ", ")
+ }
+ fmt.Fprintf(fgo2, "p%d", i)
+ })
+ fmt.Fprint(fgo2, ")\n")
+ fmt.Fprint(fgo2, "}\n")
+ }
+ }
+}
+
+// Call a function for each entry in an ast.FieldList, passing the
+// index into the list and the type.
+func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) {
+ if fl == nil {
+ return
+ }
+ i := 0
+ for _, r := range fl.List {
+ if r.Names == nil {
+ fn(i, r.Type)
+ i++
+ } else {
+ for _ = range r.Names {
+ fn(i, r.Type)
+ i++
+ }
+ }
+ }
+}
+
+// Map predeclared Go types to Type.
+var goTypes = map[string]*Type{
+ "int": &Type{Size: 4, Align: 4, C: "int"},
+ "uint": &Type{Size: 4, Align: 4, C: "uint"},
+ "int8": &Type{Size: 1, Align: 1, C: "schar"},
+ "uint8": &Type{Size: 1, Align: 1, C: "uchar"},
+ "int16": &Type{Size: 2, Align: 2, C: "short"},
+ "uint16": &Type{Size: 2, Align: 2, C: "ushort"},
+ "int32": &Type{Size: 4, Align: 4, C: "int"},
+ "uint32": &Type{Size: 4, Align: 4, C: "uint"},
+ "int64": &Type{Size: 8, Align: 8, C: "int64"},
+ "uint64": &Type{Size: 8, Align: 8, C: "uint64"},
+ "float": &Type{Size: 4, Align: 4, C: "float"},
+ "float32": &Type{Size: 4, Align: 4, C: "float"},
+ "float64": &Type{Size: 8, Align: 8, C: "double"},
+ "complex": &Type{Size: 8, Align: 8, C: "__complex float"},
+ "complex64": &Type{Size: 8, Align: 8, C: "__complex float"},
+ "complex128": &Type{Size: 16, Align: 16, C: "__complex double"},
+}
+
+// Map an ast type to a Type.
+func (p *Prog) cgoType(e ast.Expr) *Type {
+ switch t := e.(type) {
+ case *ast.StarExpr:
+ x := p.cgoType(t.X)
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: x.C + "*"}
+ case *ast.ArrayType:
+ if t.Len == nil {
+ return &Type{Size: p.PtrSize + 8, Align: p.PtrSize, C: "GoSlice"}
+ }
+ case *ast.StructType:
+ // TODO
+ case *ast.FuncType:
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "void*"}
+ case *ast.InterfaceType:
+ return &Type{Size: 3 * p.PtrSize, Align: p.PtrSize, C: "GoInterface"}
+ case *ast.MapType:
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "GoMap"}
+ case *ast.ChanType:
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "GoChan"}
+ case *ast.Ident:
+ // Look up the type in the top level declarations.
+ // TODO: Handle types defined within a function.
+ for _, d := range p.AST.Decls {
+ gd, ok := d.(*ast.GenDecl)
+ if !ok || gd.Tok != token.TYPE {
+ continue
+ }
+ for _, spec := range gd.Specs {
+ ts, ok := spec.(*ast.TypeSpec)
+ if !ok {
+ continue
+ }
+ if ts.Name.Name() == t.Name() {
+ return p.cgoType(ts.Type)
+ }
+ }
+ }
+ for name, def := range p.Typedef {
+ if name == t.Name() {
+ return p.cgoType(def)
+ }
+ }
+ if t.Name() == "uintptr" {
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "uintptr"}
+ }
+ if t.Name() == "string" {
+ return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: "GoString"}
+ }
+ if r, ok := goTypes[t.Name()]; ok {
+ if r.Align > p.PtrSize {
+ r.Align = p.PtrSize
+ }
+ return r
+ }
+ }
+ error(e.Pos(), "unrecognized Go type %v", e)
+ return &Type{Size: 4, Align: 4, C: "int"}
+}
+
const gccProlog = `
// Usual nonsense: if x and y are not equal, the type will be invalid
// (have a negative array count) and an inscrutable error will come
@@ -275,6 +566,7 @@ const cProlog = `
#pragma dynimport initcgo initcgo "%s/libcgo.so"
#pragma dynimport libcgo_thread_start libcgo_thread_start "%s/libcgo.so"
+#pragma dynimport libcgo_set_scheduler libcgo_set_scheduler "%s/libcgo.so"
#pragma dynimport _cgo_malloc _cgo_malloc "%s/libcgo.so"
#pragma dynimport _cgo_free free "%s/libcgo.so"
@@ -294,3 +586,17 @@ void
FLUSH(&p);
}
`
+
+const gccExportHeaderProlog = `
+typedef unsigned int uint;
+typedef signed char schar;
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef long long int64;
+typedef unsigned long long uint64;
+
+typedef struct { char *p; int n; } GoString;
+typedef void *GoMap;
+typedef void *GoChan;
+typedef struct { void *t; void *v; } GoInterface;
+`