diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:11:55 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:11:55 +0200 |
commit | 80f18fc933cf3f3e829c5455a1023d69f7b86e52 (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/cmd/cgo | |
parent | 28592ee1ea1f5cdffcf85472f9de0285d928cf12 (diff) | |
download | golang-80f18fc933cf3f3e829c5455a1023d69f7b86e52.tar.gz |
Imported Upstream version 60
Diffstat (limited to 'src/cmd/cgo')
-rw-r--r-- | src/cmd/cgo/Makefile | 15 | ||||
-rw-r--r-- | src/cmd/cgo/ast.go | 410 | ||||
-rw-r--r-- | src/cmd/cgo/doc.go | 80 | ||||
-rw-r--r-- | src/cmd/cgo/gcc.go | 1367 | ||||
-rw-r--r-- | src/cmd/cgo/main.go | 268 | ||||
-rw-r--r-- | src/cmd/cgo/out.go | 730 | ||||
-rw-r--r-- | src/cmd/cgo/util.go | 110 |
7 files changed, 0 insertions, 2980 deletions
diff --git a/src/cmd/cgo/Makefile b/src/cmd/cgo/Makefile deleted file mode 100644 index 5458c3e4f..000000000 --- a/src/cmd/cgo/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2009 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. - -include ../../Make.inc - -TARG=cgo -GOFILES=\ - ast.go\ - gcc.go\ - main.go\ - out.go\ - util.go\ - -include ../../Make.cmd diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go deleted file mode 100644 index 46e33686d..000000000 --- a/src/cmd/cgo/ast.go +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright 2009 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. - -// Parse input AST and prepare Prog structure. - -package main - -import ( - "fmt" - "go/ast" - "go/doc" - "go/parser" - "go/scanner" - "go/token" - "os" - "strings" -) - -func parse(name string, flags uint) *ast.File { - ast1, err := parser.ParseFile(fset, name, nil, flags) - if err != nil { - if list, ok := err.(scanner.ErrorList); ok { - // If err is a scanner.ErrorList, its String will print just - // the first error and then (+n more errors). - // Instead, turn it into a new Error that will return - // details for all the errors. - for _, e := range list { - fmt.Fprintln(os.Stderr, e) - } - os.Exit(2) - } - fatalf("parsing %s: %s", name, err) - } - return ast1 -} - -func sourceLine(n ast.Node) int { - return fset.Position(n.Pos()).Line -} - -// ReadGo populates f with information learned from reading the -// Go source file with the given file name. It gathers the C preamble -// attached to the import "C" comment, a list of references to C.xxx, -// a list of exported functions, and the actual AST, to be rewritten and -// printed. -func (f *File) ReadGo(name string) { - // Two different parses: once with comments, once without. - // The printer is not good enough at printing comments in the - // right place when we start editing the AST behind its back, - // so we use ast1 to look for the doc comments on import "C" - // and on exported functions, and we use ast2 for translating - // and reprinting. - ast1 := parse(name, parser.ParseComments) - ast2 := parse(name, 0) - - f.Package = ast1.Name.Name - f.Name = make(map[string]*Name) - - // In ast1, find the import "C" line and get any extra C preamble. - sawC := false - for _, decl := range ast1.Decls { - d, ok := decl.(*ast.GenDecl) - if !ok { - continue - } - for _, spec := range d.Specs { - s, ok := spec.(*ast.ImportSpec) - if !ok || string(s.Path.Value) != `"C"` { - continue - } - sawC = true - if s.Name != nil { - error(s.Path.Pos(), `cannot rename import "C"`) - } - cg := s.Doc - if cg == nil && len(d.Specs) == 1 { - cg = d.Doc - } - if cg != nil { - f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) - f.Preamble += doc.CommentText(cg) + "\n" - } - } - } - if !sawC { - error(token.NoPos, `cannot find import "C"`) - } - - // In ast2, strip the import "C" line. - w := 0 - for _, decl := range ast2.Decls { - d, ok := decl.(*ast.GenDecl) - if !ok { - ast2.Decls[w] = decl - w++ - continue - } - ws := 0 - for _, spec := range d.Specs { - s, ok := spec.(*ast.ImportSpec) - if !ok || string(s.Path.Value) != `"C"` { - d.Specs[ws] = spec - ws++ - } - } - if ws == 0 { - continue - } - d.Specs = d.Specs[0:ws] - ast2.Decls[w] = d - w++ - } - ast2.Decls = ast2.Decls[0:w] - - // Accumulate pointers to uses of C.x. - if f.Ref == nil { - f.Ref = make([]*Ref, 0, 8) - } - f.walk(ast2, "prog", (*File).saveRef) - - // Accumulate exported functions. - // The comments are only on ast1 but we need to - // save the function bodies from ast2. - // The first walk fills in ExpFunc, and the - // second walk changes the entries to - // refer to ast2 instead. - f.walk(ast1, "prog", (*File).saveExport) - f.walk(ast2, "prog", (*File).saveExport2) - - f.AST = ast2 -} - -// Save references to C.xxx for later processing. -func (f *File) saveRef(x interface{}, context string) { - n, ok := x.(*ast.Expr) - if !ok { - return - } - if sel, ok := (*n).(*ast.SelectorExpr); ok { - // For now, assume that the only instance of capital C is - // when used as the imported package identifier. - // The parser should take care of scoping in the future, - // so that we will be able to distinguish a "top-level C" - // from a local C. - if l, ok := sel.X.(*ast.Ident); ok && l.Name == "C" { - if context == "as2" { - context = "expr" - } - goname := sel.Sel.Name - if goname == "errno" { - error(sel.Pos(), "cannot refer to errno directly; see documentation") - return - } - name := f.Name[goname] - if name == nil { - name = &Name{ - Go: goname, - } - f.Name[goname] = name - } - f.Ref = append(f.Ref, &Ref{ - Name: name, - Expr: n, - Context: context, - }) - return - } - } -} - -// If a function should be exported add it to ExpFunc. -func (f *File) saveExport(x interface{}, context string) { - n, ok := x.(*ast.FuncDecl) - if !ok { - return - } - - if n.Doc == nil { - return - } - for _, c := range n.Doc.List { - if !strings.HasPrefix(string(c.Text), "//export ") { - continue - } - - name := strings.TrimSpace(string(c.Text[9:])) - if name == "" { - error(c.Pos(), "export missing name") - } - - f.ExpFunc = append(f.ExpFunc, &ExpFunc{ - Func: n, - ExpName: name, - }) - break - } -} - -// Make f.ExpFunc[i] point at the Func from this AST instead of the other one. -func (f *File) saveExport2(x interface{}, context string) { - n, ok := x.(*ast.FuncDecl) - if !ok { - return - } - - for _, exp := range f.ExpFunc { - if exp.Func.Name.Name == n.Name.Name { - exp.Func = n - break - } - } -} - -// walk walks the AST x, calling visit(f, x, context) for each node. -func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) { - visit(f, x, context) - switch n := x.(type) { - case *ast.Expr: - f.walk(*n, context, visit) - - // everything else just recurs - default: - error(token.NoPos, "unexpected type %T in walk", x, visit) - panic("unexpected type") - - case nil: - - // These are ordered and grouped to match ../../pkg/go/ast/ast.go - case *ast.Field: - f.walk(&n.Type, "type", visit) - case *ast.FieldList: - for _, field := range n.List { - f.walk(field, context, visit) - } - case *ast.BadExpr: - case *ast.Ident: - case *ast.Ellipsis: - case *ast.BasicLit: - case *ast.FuncLit: - f.walk(n.Type, "type", visit) - f.walk(n.Body, "stmt", visit) - case *ast.CompositeLit: - f.walk(&n.Type, "type", visit) - f.walk(n.Elts, "expr", visit) - case *ast.ParenExpr: - f.walk(&n.X, context, visit) - case *ast.SelectorExpr: - f.walk(&n.X, "selector", visit) - case *ast.IndexExpr: - f.walk(&n.X, "expr", visit) - f.walk(&n.Index, "expr", visit) - case *ast.SliceExpr: - f.walk(&n.X, "expr", visit) - if n.Low != nil { - f.walk(&n.Low, "expr", visit) - } - if n.High != nil { - f.walk(&n.High, "expr", visit) - } - case *ast.TypeAssertExpr: - f.walk(&n.X, "expr", visit) - f.walk(&n.Type, "type", visit) - case *ast.CallExpr: - if context == "as2" { - f.walk(&n.Fun, "call2", visit) - } else { - f.walk(&n.Fun, "call", visit) - } - f.walk(n.Args, "expr", visit) - case *ast.StarExpr: - f.walk(&n.X, context, visit) - case *ast.UnaryExpr: - f.walk(&n.X, "expr", visit) - case *ast.BinaryExpr: - f.walk(&n.X, "expr", visit) - f.walk(&n.Y, "expr", visit) - case *ast.KeyValueExpr: - f.walk(&n.Key, "expr", visit) - f.walk(&n.Value, "expr", visit) - - case *ast.ArrayType: - f.walk(&n.Len, "expr", visit) - f.walk(&n.Elt, "type", visit) - case *ast.StructType: - f.walk(n.Fields, "field", visit) - case *ast.FuncType: - f.walk(n.Params, "field", visit) - if n.Results != nil { - f.walk(n.Results, "field", visit) - } - case *ast.InterfaceType: - f.walk(n.Methods, "field", visit) - case *ast.MapType: - f.walk(&n.Key, "type", visit) - f.walk(&n.Value, "type", visit) - case *ast.ChanType: - f.walk(&n.Value, "type", visit) - - case *ast.BadStmt: - case *ast.DeclStmt: - f.walk(n.Decl, "decl", visit) - case *ast.EmptyStmt: - case *ast.LabeledStmt: - f.walk(n.Stmt, "stmt", visit) - case *ast.ExprStmt: - f.walk(&n.X, "expr", visit) - case *ast.SendStmt: - f.walk(&n.Chan, "expr", visit) - f.walk(&n.Value, "expr", visit) - case *ast.IncDecStmt: - f.walk(&n.X, "expr", visit) - case *ast.AssignStmt: - f.walk(n.Lhs, "expr", visit) - if len(n.Lhs) == 2 && len(n.Rhs) == 1 { - f.walk(n.Rhs, "as2", visit) - } else { - f.walk(n.Rhs, "expr", visit) - } - case *ast.GoStmt: - f.walk(n.Call, "expr", visit) - case *ast.DeferStmt: - f.walk(n.Call, "expr", visit) - case *ast.ReturnStmt: - f.walk(n.Results, "expr", visit) - case *ast.BranchStmt: - case *ast.BlockStmt: - f.walk(n.List, context, visit) - case *ast.IfStmt: - f.walk(n.Init, "stmt", visit) - f.walk(&n.Cond, "expr", visit) - f.walk(n.Body, "stmt", visit) - f.walk(n.Else, "stmt", visit) - case *ast.CaseClause: - if context == "typeswitch" { - context = "type" - } else { - context = "expr" - } - f.walk(n.List, context, visit) - f.walk(n.Body, "stmt", visit) - case *ast.SwitchStmt: - f.walk(n.Init, "stmt", visit) - f.walk(&n.Tag, "expr", visit) - f.walk(n.Body, "switch", visit) - case *ast.TypeSwitchStmt: - f.walk(n.Init, "stmt", visit) - f.walk(n.Assign, "stmt", visit) - f.walk(n.Body, "typeswitch", visit) - case *ast.CommClause: - f.walk(n.Comm, "stmt", visit) - f.walk(n.Body, "stmt", visit) - case *ast.SelectStmt: - f.walk(n.Body, "stmt", visit) - case *ast.ForStmt: - f.walk(n.Init, "stmt", visit) - f.walk(&n.Cond, "expr", visit) - f.walk(n.Post, "stmt", visit) - f.walk(n.Body, "stmt", visit) - case *ast.RangeStmt: - f.walk(&n.Key, "expr", visit) - f.walk(&n.Value, "expr", visit) - f.walk(&n.X, "expr", visit) - f.walk(n.Body, "stmt", visit) - - case *ast.ImportSpec: - case *ast.ValueSpec: - f.walk(&n.Type, "type", visit) - f.walk(n.Values, "expr", visit) - case *ast.TypeSpec: - f.walk(&n.Type, "type", visit) - - case *ast.BadDecl: - case *ast.GenDecl: - f.walk(n.Specs, "spec", visit) - case *ast.FuncDecl: - if n.Recv != nil { - f.walk(n.Recv, "field", visit) - } - f.walk(n.Type, "type", visit) - if n.Body != nil { - f.walk(n.Body, "stmt", visit) - } - - case *ast.File: - f.walk(n.Decls, "decl", visit) - - case *ast.Package: - for _, file := range n.Files { - f.walk(file, "file", visit) - } - - case []ast.Decl: - for _, d := range n { - f.walk(d, context, visit) - } - case []ast.Expr: - for i := range n { - f.walk(&n[i], context, visit) - } - case []ast.Stmt: - for _, s := range n { - f.walk(s, context, visit) - } - case []ast.Spec: - for _, s := range n { - f.walk(s, context, visit) - } - } -} diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go deleted file mode 100644 index 064725c1d..000000000 --- a/src/cmd/cgo/doc.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2009 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. - -/* - -Cgo enables the creation of Go packages that call C code. - -Usage: cgo [compiler options] file.go - -The compiler options are passed through uninterpreted when -invoking gcc to compile the C parts of the package. - -The input file.go is a syntactically valid Go source file that imports -the pseudo-package "C" and then refers to types such as C.size_t, -variables such as C.stdout, or functions such as C.putchar. - -If the import of "C" is immediately preceded by a comment, that -comment is used as a header when compiling the C parts of -the package. For example: - - // #include <stdio.h> - // #include <errno.h> - import "C" - -CFLAGS and LDFLAGS may be defined with pseudo #cgo directives -within these comments to tweak the behavior of gcc. Values defined -in multiple directives are concatenated together. Options prefixed -by $GOOS, $GOARCH, or $GOOS/$GOARCH are only defined in matching -systems. For example: - - // #cgo CFLAGS: -DPNG_DEBUG=1 - // #cgo linux CFLAGS: -DLINUX=1 - // #cgo LDFLAGS: -lpng - // #include <png.h> - import "C" - -Alternatively, CFLAGS and LDFLAGS may be obtained via the pkg-config -tool using a '#cgo pkg-config:' directive followed by the package names. -For example: - - // #cgo pkg-config: png cairo - // #include <png.h> - import "C" - -Within the Go file, C identifiers or field names that are keywords in Go -can be accessed by prefixing them with an underscore: if x points at a C -struct with a field named "type", x._type accesses the field. - -The standard C numeric types are available under the names -C.char, C.schar (signed char), C.uchar (unsigned char), -C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int), -C.long, C.ulong (unsigned long), C.longlong (long long), -C.ulonglong (unsigned long long), C.float, C.double. - -To access a struct, union, or enum type directly, prefix it with -struct_, union_, or enum_, as in C.struct_stat. - -Any C function that returns a value may be called in a multiple -assignment context to retrieve both the return value and the -C errno variable as an os.Error. For example: - - n, err := C.atoi("abc") - -In C, a function argument written as a fixed size array -actually requires a pointer to the first element of the array. -C compilers are aware of this calling convention and adjust -the call accordingly, but Go cannot. In Go, you must pass -the pointer to the first element explicitly: C.f(&x[0]). - -Cgo transforms the input file into four output files: two Go source -files, a C file for 6c (or 8c or 5c), and a C file for gcc. - -The standard package makefile rules in Make.pkg automate the -process of using cgo. See $GOROOT/misc/cgo/stdio and -$GOROOT/misc/cgo/gmp for examples. - -Cgo does not yet work with gccgo. -*/ -package documentation diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go deleted file mode 100644 index a4d83f1e7..000000000 --- a/src/cmd/cgo/gcc.go +++ /dev/null @@ -1,1367 +0,0 @@ -// Copyright 2009 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. - -// Annotate Ref in Prog with C types by parsing gcc debug output. -// Conversion of debug output to Go types. - -package main - -import ( - "bytes" - "debug/dwarf" - "debug/elf" - "debug/macho" - "debug/pe" - "encoding/binary" - "flag" - "fmt" - "go/ast" - "go/parser" - "go/token" - "os" - "runtime" - "strconv" - "strings" - "unicode" -) - -var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") -var debugGcc = flag.Bool("debug-gcc", false, "print gcc invocations") - -var nameToC = map[string]string{ - "schar": "signed char", - "uchar": "unsigned char", - "ushort": "unsigned short", - "uint": "unsigned int", - "ulong": "unsigned long", - "longlong": "long long", - "ulonglong": "unsigned long long", - "complexfloat": "float complex", - "complexdouble": "double complex", -} - -// cname returns the C name to use for C.s. -// The expansions are listed in nameToC and also -// struct_foo becomes "struct foo", and similarly for -// union and enum. -func cname(s string) string { - if t, ok := nameToC[s]; ok { - return t - } - - if strings.HasPrefix(s, "struct_") { - return "struct " + s[len("struct_"):] - } - if strings.HasPrefix(s, "union_") { - return "union " + s[len("union_"):] - } - if strings.HasPrefix(s, "enum_") { - return "enum " + s[len("enum_"):] - } - return s -} - -// ParseFlags extracts #cgo CFLAGS and LDFLAGS options from the file -// preamble. Multiple occurrences are concatenated with a separating space, -// even across files. -func (p *Package) ParseFlags(f *File, srcfile string) { - linesIn := strings.Split(f.Preamble, "\n") - linesOut := make([]string, 0, len(linesIn)) - -NextLine: - for _, line := range linesIn { - l := strings.TrimSpace(line) - if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) { - linesOut = append(linesOut, line) - continue - } - - l = strings.TrimSpace(l[4:]) - fields := strings.SplitN(l, ":", 2) - if len(fields) != 2 { - fatalf("%s: bad #cgo line: %s", srcfile, line) - } - - var k string - kf := strings.Fields(fields[0]) - switch len(kf) { - case 1: - k = kf[0] - case 2: - k = kf[1] - switch kf[0] { - case runtime.GOOS: - case runtime.GOARCH: - case runtime.GOOS + "/" + runtime.GOARCH: - default: - continue NextLine - } - default: - fatalf("%s: bad #cgo option: %s", srcfile, fields[0]) - } - - args, err := splitQuoted(fields[1]) - if err != nil { - fatalf("%s: bad #cgo option %s: %s", srcfile, k, err) - } - for _, arg := range args { - if !safeName(arg) { - fatalf("%s: #cgo option %s is unsafe: %s", srcfile, k, arg) - } - } - - switch k { - - case "CFLAGS", "LDFLAGS": - p.addToFlag(k, args) - - case "pkg-config": - cflags, ldflags, err := pkgConfig(args) - if err != nil { - fatalf("%s: bad #cgo option %s: %s", srcfile, k, err) - } - p.addToFlag("CFLAGS", cflags) - p.addToFlag("LDFLAGS", ldflags) - - default: - fatalf("%s: unsupported #cgo option %s", srcfile, k) - - } - } - f.Preamble = strings.Join(linesOut, "\n") -} - -// addToFlag appends args to flag. All flags are later written out onto the -// _cgo_flags file for the build system to use. -func (p *Package) addToFlag(flag string, args []string) { - if oldv, ok := p.CgoFlags[flag]; ok { - p.CgoFlags[flag] = oldv + " " + strings.Join(args, " ") - } else { - p.CgoFlags[flag] = strings.Join(args, " ") - } - if flag == "CFLAGS" { - // We'll also need these when preprocessing for dwarf information. - p.GccOptions = append(p.GccOptions, args...) - } -} - -// pkgConfig runs pkg-config and extracts --libs and --cflags information -// for packages. -func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { - for _, name := range packages { - if len(name) == 0 || name[0] == '-' { - return nil, nil, os.NewError(fmt.Sprintf("invalid name: %q", name)) - } - } - - args := append([]string{"pkg-config", "--cflags"}, packages...) - stdout, stderr, ok := run(nil, args) - if !ok { - os.Stderr.Write(stderr) - return nil, nil, os.NewError("pkg-config failed") - } - cflags, err = splitQuoted(string(stdout)) - if err != nil { - return - } - - args = append([]string{"pkg-config", "--libs"}, packages...) - stdout, stderr, ok = run(nil, args) - if !ok { - os.Stderr.Write(stderr) - return nil, nil, os.NewError("pkg-config failed") - } - ldflags, err = splitQuoted(string(stdout)) - return -} - -// splitQuoted splits the string s around each instance of one or more consecutive -// white space characters while taking into account quotes and escaping, and -// returns an array of substrings of s or an empty list if s contains only white space. -// Single quotes and double quotes are recognized to prevent splitting within the -// quoted region, and are removed from the resulting substrings. If a quote in s -// isn't closed err will be set and r will have the unclosed argument as the -// last element. The backslash is used for escaping. -// -// For example, the following string: -// -// `a b:"c d" 'e''f' "g\""` -// -// Would be parsed as: -// -// []string{"a", "b:c d", "ef", `g"`} -// -func splitQuoted(s string) (r []string, err os.Error) { - var args []string - arg := make([]int, len(s)) - escaped := false - quoted := false - quote := 0 - i := 0 - for _, rune := range s { - switch { - case escaped: - escaped = false - case rune == '\\': - escaped = true - continue - case quote != 0: - if rune == quote { - quote = 0 - continue - } - case rune == '"' || rune == '\'': - quoted = true - quote = rune - continue - case unicode.IsSpace(rune): - if quoted || i > 0 { - quoted = false - args = append(args, string(arg[:i])) - i = 0 - } - continue - } - arg[i] = rune - i++ - } - if quoted || i > 0 { - args = append(args, string(arg[:i])) - } - if quote != 0 { - err = os.NewError("unclosed quote") - } else if escaped { - err = os.NewError("unfinished escaping") - } - return args, err -} - -var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz") - -func safeName(s string) bool { - if s == "" { - return false - } - for i := 0; i < len(s); i++ { - if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 { - return false - } - } - return true -} - -// Translate rewrites f.AST, the original Go input, to remove -// references to the imported package C, replacing them with -// references to the equivalent Go types, functions, and variables. -func (p *Package) Translate(f *File) { - for _, cref := range f.Ref { - // Convert C.ulong to C.unsigned long, etc. - cref.Name.C = cname(cref.Name.Go) - } - p.loadDefines(f) - needType := p.guessKinds(f) - if len(needType) > 0 { - p.loadDWARF(f, needType) - } - p.rewriteRef(f) -} - -// loadDefines coerces gcc into spitting out the #defines in use -// in the file f and saves relevant renamings in f.Name[name].Define. -func (p *Package) loadDefines(f *File) { - var b bytes.Buffer - b.WriteString(builtinProlog) - b.WriteString(f.Preamble) - stdout := p.gccDefines(b.Bytes()) - - for _, line := range strings.Split(stdout, "\n") { - if len(line) < 9 || line[0:7] != "#define" { - continue - } - - line = strings.TrimSpace(line[8:]) - - var key, val string - spaceIndex := strings.Index(line, " ") - tabIndex := strings.Index(line, "\t") - - if spaceIndex == -1 && tabIndex == -1 { - continue - } else if tabIndex == -1 || (spaceIndex != -1 && spaceIndex < tabIndex) { - key = line[0:spaceIndex] - val = strings.TrimSpace(line[spaceIndex:]) - } else { - key = line[0:tabIndex] - val = strings.TrimSpace(line[tabIndex:]) - } - - if n := f.Name[key]; n != nil { - if *debugDefine { - fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val) - } - n.Define = val - } - } -} - -// guessKinds tricks gcc into revealing the kind of each -// name xxx for the references C.xxx in the Go input. -// The kind is either a constant, type, or variable. -func (p *Package) guessKinds(f *File) []*Name { - // Coerce gcc into telling us whether each name is - // a type, a value, or undeclared. We compile a function - // containing the line: - // name; - // If name is a type, gcc will print: - // cgo-test:2: warning: useless type name in empty declaration - // If name is a value, gcc will print - // cgo-test:2: warning: statement with no effect - // If name is undeclared, gcc will print - // cgo-test:2: error: 'name' undeclared (first use in this function) - // A line number directive causes the line number to - // correspond to the index in the names array. - // - // The line also has an enum declaration: - // name; enum { _cgo_enum_1 = name }; - // If name is not a constant, gcc will print: - // cgo-test:4: error: enumerator value for '_cgo_enum_4' is not an integer constant - // we assume lines without that error are constants. - - // Make list of names that need sniffing, type lookup. - toSniff := make([]*Name, 0, len(f.Name)) - needType := make([]*Name, 0, len(f.Name)) - - for _, n := range f.Name { - // If we've already found this name as a #define - // and we can translate it as a constant value, do so. - if n.Define != "" { - ok := false - if _, err := strconv.Atoi(n.Define); err == nil { - ok = true - } else if n.Define[0] == '"' || n.Define[0] == '\'' { - _, err := parser.ParseExpr(fset, "", n.Define) - if err == nil { - ok = true - } - } - if ok { - n.Kind = "const" - n.Const = n.Define - continue - } - - if isName(n.Define) { - n.C = n.Define - } - } - - // If this is a struct, union, or enum type name, - // record the kind but also that we need type information. - if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") { - n.Kind = "type" - i := len(needType) - needType = needType[0 : i+1] - needType[i] = n - continue - } - - i := len(toSniff) - toSniff = toSniff[0 : i+1] - toSniff[i] = n - } - - if len(toSniff) == 0 { - return needType - } - - var b bytes.Buffer - b.WriteString(builtinProlog) - b.WriteString(f.Preamble) - b.WriteString("void __cgo__f__(void) {\n") - b.WriteString("#line 0 \"cgo-test\"\n") - for i, n := range toSniff { - fmt.Fprintf(&b, "%s; enum { _cgo_enum_%d = %s }; /* cgo-test:%d */\n", n.C, i, n.C, i) - } - b.WriteString("}\n") - stderr := p.gccErrors(b.Bytes()) - if stderr == "" { - fatalf("gcc produced no output\non input:\n%s", b.Bytes()) - } - - names := make([]*Name, len(toSniff)) - copy(names, toSniff) - - isConst := make([]bool, len(toSniff)) - for i := range isConst { - isConst[i] = true // until proven otherwise - } - - for _, line := range strings.Split(stderr, "\n") { - if len(line) < 9 || line[0:9] != "cgo-test:" { - // the user will see any compiler errors when the code is compiled later. - continue - } - line = line[9:] - colon := strings.Index(line, ":") - if colon < 0 { - continue - } - i, err := strconv.Atoi(line[0:colon]) - if err != nil { - continue - } - what := "" - switch { - default: - continue - case strings.Contains(line, ": useless type name in empty declaration"): - what = "type" - isConst[i] = false - case strings.Contains(line, ": statement with no effect"): - what = "not-type" // const or func or var - case strings.Contains(line, "undeclared"): - error(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) - case strings.Contains(line, "is not an integer constant"): - isConst[i] = false - continue - } - n := toSniff[i] - if n == nil { - continue - } - toSniff[i] = nil - n.Kind = what - - j := len(needType) - needType = needType[0 : j+1] - needType[j] = n - } - for i, b := range isConst { - if b { - names[i].Kind = "const" - } - } - for _, n := range toSniff { - if n == nil { - continue - } - if n.Kind != "" { - continue - } - error(token.NoPos, "could not determine kind of name for C.%s", n.Go) - } - if nerrors > 0 { - fatalf("unresolved names") - } - return needType -} - -// loadDWARF parses the DWARF debug information generated -// by gcc to learn the details of the constants, variables, and types -// being referred to as C.xxx. -func (p *Package) loadDWARF(f *File, names []*Name) { - // Extract the types from the DWARF section of an object - // from a well-formed C program. Gcc only generates DWARF info - // for symbols in the object file, so it is not enough to print the - // preamble and hope the symbols we care about will be there. - // Instead, emit - // typeof(names[i]) *__cgo__i; - // for each entry in names and then dereference the type we - // learn for __cgo__i. - var b bytes.Buffer - b.WriteString(builtinProlog) - b.WriteString(f.Preamble) - for i, n := range names { - fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n.C, i) - if n.Kind == "const" { - fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C) - } - } - - // Apple's LLVM-based gcc does not include the enumeration - // names and values in its DWARF debug output. In case we're - // using such a gcc, create a data block initialized with the values. - // We can read them out of the object file. - fmt.Fprintf(&b, "long long __cgodebug_data[] = {\n") - for _, n := range names { - if n.Kind == "const" { - fmt.Fprintf(&b, "\t%s,\n", n.C) - } else { - fmt.Fprintf(&b, "\t0,\n") - } - } - fmt.Fprintf(&b, "\t0\n") - fmt.Fprintf(&b, "};\n") - - d, bo, debugData := p.gccDebug(b.Bytes()) - enumVal := make([]int64, len(debugData)/8) - for i := range enumVal { - enumVal[i] = int64(bo.Uint64(debugData[i*8:])) - } - - // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i. - types := make([]dwarf.Type, len(names)) - enums := make([]dwarf.Offset, len(names)) - nameToIndex := make(map[*Name]int) - for i, n := range names { - nameToIndex[n] = i - } - r := d.Reader() - for { - e, err := r.Next() - if err != nil { - fatalf("reading DWARF entry: %s", err) - } - if e == nil { - break - } - switch e.Tag { - case dwarf.TagEnumerationType: - offset := e.Offset - for { - e, err := r.Next() - if err != nil { - fatalf("reading DWARF entry: %s", err) - } - if e.Tag == 0 { - break - } - if e.Tag == dwarf.TagEnumerator { - entryName := e.Val(dwarf.AttrName).(string) - if strings.HasPrefix(entryName, "__cgo_enum__") { - n, _ := strconv.Atoi(entryName[len("__cgo_enum__"):]) - if 0 <= n && n < len(names) { - enums[n] = offset - } - } - } - } - case dwarf.TagVariable: - name, _ := e.Val(dwarf.AttrName).(string) - typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset) - if name == "" || typOff == 0 { - fatalf("malformed DWARF TagVariable entry") - } - if !strings.HasPrefix(name, "__cgo__") { - break - } - typ, err := d.Type(typOff) - if err != nil { - fatalf("loading DWARF type: %s", err) - } - t, ok := typ.(*dwarf.PtrType) - if !ok || t == nil { - fatalf("internal error: %s has non-pointer type", name) - } - i, err := strconv.Atoi(name[7:]) - if err != nil { - fatalf("malformed __cgo__ name: %s", name) - } - if enums[i] != 0 { - t, err := d.Type(enums[i]) - if err != nil { - fatalf("loading DWARF type: %s", err) - } - types[i] = t - } else { - types[i] = t.Type - } - } - if e.Tag != dwarf.TagCompileUnit { - r.SkipChildren() - } - } - - // Record types and typedef information. - var conv typeConv - conv.Init(p.PtrSize) - for i, n := range names { - f, fok := types[i].(*dwarf.FuncType) - if n.Kind != "type" && fok { - n.Kind = "func" - n.FuncType = conv.FuncType(f) - } else { - n.Type = conv.Type(types[i]) - if enums[i] != 0 && n.Type.EnumValues != nil { - k := fmt.Sprintf("__cgo_enum__%d", i) - n.Kind = "const" - n.Const = strconv.Itoa64(n.Type.EnumValues[k]) - // Remove injected enum to ensure the value will deep-compare - // equally in future loads of the same constant. - n.Type.EnumValues[k] = 0, false - } else if n.Kind == "const" && i < len(enumVal) { - n.Const = strconv.Itoa64(enumVal[i]) - } - } - } - -} - -// rewriteRef rewrites all the C.xxx references in f.AST to refer to the -// Go equivalents, now that we have figured out the meaning of all -// the xxx. -func (p *Package) rewriteRef(f *File) { - // Assign mangled names. - for _, n := range f.Name { - if n.Kind == "not-type" { - n.Kind = "var" - } - if n.Mangle == "" { - n.Mangle = "_C" + n.Kind + "_" + n.Go - } - } - - // Now that we have all the name types filled in, - // scan through the Refs to identify the ones that - // are trying to do a ,err call. Also check that - // functions are only used in calls. - for _, r := range f.Ref { - if r.Name.Kind == "const" && r.Name.Const == "" { - error(r.Pos(), "unable to find value of constant C.%s", r.Name.Go) - } - var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default - switch r.Context { - case "call", "call2": - if r.Name.Kind != "func" { - if r.Name.Kind == "type" { - r.Context = "type" - expr = r.Name.Type.Go - break - } - error(r.Pos(), "call of non-function C.%s", r.Name.Go) - break - } - if r.Context == "call2" { - if r.Name.FuncType.Result == nil { - error(r.Pos(), "assignment count mismatch: 2 = 0") - } - // Invent new Name for the two-result function. - n := f.Name["2"+r.Name.Go] - if n == nil { - n = new(Name) - *n = *r.Name - n.AddError = true - n.Mangle = "_C2func_" + n.Go - f.Name["2"+r.Name.Go] = n - } - expr = ast.NewIdent(n.Mangle) - r.Name = n - break - } - case "expr": - if r.Name.Kind == "func" { - error(r.Pos(), "must call C.%s", r.Name.Go) - } - if r.Name.Kind == "type" { - // Okay - might be new(T) - expr = r.Name.Type.Go - } - if r.Name.Kind == "var" { - expr = &ast.StarExpr{X: expr} - } - - case "type": - if r.Name.Kind != "type" { - error(r.Pos(), "expression C.%s used as type", r.Name.Go) - } else { - expr = r.Name.Type.Go - } - default: - if r.Name.Kind == "func" { - error(r.Pos(), "must call C.%s", r.Name.Go) - } - } - *r.Expr = expr - } -} - -// gccName returns the name of the compiler to run. Use $GCC if set in -// the environment, otherwise just "gcc". - -func (p *Package) gccName() (ret string) { - if ret = os.Getenv("GCC"); ret == "" { - ret = "gcc" - } - return -} - -// gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". -func (p *Package) gccMachine() []string { - switch runtime.GOARCH { - case "amd64": - return []string{"-m64"} - case "386": - return []string{"-m32"} - } - return nil -} - -var gccTmp = objDir + "_cgo_.o" - -// gccCmd returns the gcc command line to use for compiling -// the input. -func (p *Package) gccCmd() []string { - c := []string{ - p.gccName(), - "-Wall", // many warnings - "-Werror", // warnings are errors - "-o" + gccTmp, // write object to tmp - "-gdwarf-2", // generate DWARF v2 debugging symbols - "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise - "-c", // do not link - "-xc", // input language is C - } - c = append(c, p.GccOptions...) - c = append(c, p.gccMachine()...) - c = append(c, "-") //read input from standard input - return c -} - -// gccDebug runs gcc -gdwarf-2 over the C program stdin and -// returns the corresponding DWARF data and, if present, debug data block. -func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) { - runGcc(stdin, p.gccCmd()) - - if f, err := macho.Open(gccTmp); err == nil { - d, err := f.DWARF() - if err != nil { - fatalf("cannot load DWARF output from %s: %v", gccTmp, err) - } - var data []byte - if f.Symtab != nil { - for i := range f.Symtab.Syms { - s := &f.Symtab.Syms[i] - // Mach-O still uses a leading _ to denote non-assembly symbols. - if s.Name == "_"+"__cgodebug_data" { - // Found it. Now find data section. - if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) { - sect := f.Sections[i] - if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { - if sdat, err := sect.Data(); err == nil { - data = sdat[s.Value-sect.Addr:] - } - } - } - } - } - } - return d, f.ByteOrder, data - } - - // Can skip debug data block in ELF and PE for now. - // The DWARF information is complete. - - if f, err := elf.Open(gccTmp); err == nil { - d, err := f.DWARF() - if err != nil { - fatalf("cannot load DWARF output from %s: %v", gccTmp, err) - } - return d, f.ByteOrder, nil - } - - if f, err := pe.Open(gccTmp); err == nil { - d, err := f.DWARF() - if err != nil { - fatalf("cannot load DWARF output from %s: %v", gccTmp, err) - } - return d, binary.LittleEndian, nil - } - - fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp) - panic("not reached") -} - -// gccDefines runs gcc -E -dM -xc - over the C program stdin -// and returns the corresponding standard output, which is the -// #defines that gcc encountered while processing the input -// and its included files. -func (p *Package) gccDefines(stdin []byte) string { - base := []string{p.gccName(), "-E", "-dM", "-xc"} - base = append(base, p.gccMachine()...) - stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-")) - return stdout -} - -// gccErrors runs gcc over the C program stdin and returns -// the errors that gcc prints. That is, this function expects -// gcc to fail. -func (p *Package) gccErrors(stdin []byte) string { - // TODO(rsc): require failure - args := p.gccCmd() - if *debugGcc { - fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " ")) - os.Stderr.Write(stdin) - fmt.Fprint(os.Stderr, "EOF\n") - } - stdout, stderr, _ := run(stdin, args) - if *debugGcc { - os.Stderr.Write(stdout) - os.Stderr.Write(stderr) - } - return string(stderr) -} - -// runGcc runs the gcc command line args with stdin on standard input. -// If the command exits with a non-zero exit status, runGcc prints -// details about what was run and exits. -// Otherwise runGcc returns the data written to standard output and standard error. -// Note that for some of the uses we expect useful data back -// on standard error, but for those uses gcc must still exit 0. -func runGcc(stdin []byte, args []string) (string, string) { - if *debugGcc { - fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " ")) - os.Stderr.Write(stdin) - fmt.Fprint(os.Stderr, "EOF\n") - } - stdout, stderr, ok := run(stdin, args) - if *debugGcc { - os.Stderr.Write(stdout) - os.Stderr.Write(stderr) - } - if !ok { - os.Stderr.Write(stderr) - os.Exit(2) - } - return string(stdout), string(stderr) -} - -// A typeConv is a translator from dwarf types to Go types -// with equivalent memory layout. -type typeConv struct { - // Cache of already-translated or in-progress types. - m map[dwarf.Type]*Type - typedef map[string]ast.Expr - - // Predeclared types. - bool ast.Expr - byte ast.Expr // denotes padding - int8, int16, int32, int64 ast.Expr - uint8, uint16, uint32, uint64, uintptr ast.Expr - float32, float64 ast.Expr - complex64, complex128 ast.Expr - void ast.Expr - unsafePointer ast.Expr - string ast.Expr - - ptrSize int64 -} - -var tagGen int -var typedef = make(map[string]ast.Expr) - -func (c *typeConv) Init(ptrSize int64) { - c.ptrSize = ptrSize - c.m = make(map[dwarf.Type]*Type) - c.bool = c.Ident("bool") - c.byte = c.Ident("byte") - c.int8 = c.Ident("int8") - c.int16 = c.Ident("int16") - c.int32 = c.Ident("int32") - c.int64 = c.Ident("int64") - c.uint8 = c.Ident("uint8") - c.uint16 = c.Ident("uint16") - c.uint32 = c.Ident("uint32") - c.uint64 = c.Ident("uint64") - c.uintptr = c.Ident("uintptr") - c.float32 = c.Ident("float32") - c.float64 = c.Ident("float64") - c.complex64 = c.Ident("complex64") - c.complex128 = c.Ident("complex128") - c.unsafePointer = c.Ident("unsafe.Pointer") - c.void = c.Ident("void") - c.string = c.Ident("string") -} - -// base strips away qualifiers and typedefs to get the underlying type -func base(dt dwarf.Type) dwarf.Type { - for { - if d, ok := dt.(*dwarf.QualType); ok { - dt = d.Type - continue - } - if d, ok := dt.(*dwarf.TypedefType); ok { - dt = d.Type - continue - } - break - } - return dt -} - -// Map from dwarf text names to aliases we use in package "C". -var dwarfToName = map[string]string{ - "long int": "long", - "long unsigned int": "ulong", - "unsigned int": "uint", - "short unsigned int": "ushort", - "short int": "short", - "long long int": "longlong", - "long long unsigned int": "ulonglong", - "signed char": "schar", - "float complex": "complexfloat", - "double complex": "complexdouble", -} - -const signedDelta = 64 - -// String returns the current type representation. Format arguments -// are assembled within this method so that any changes in mutable -// values are taken into account. -func (tr *TypeRepr) String() string { - if len(tr.Repr) == 0 { - return "" - } - if len(tr.FormatArgs) == 0 { - return tr.Repr - } - return fmt.Sprintf(tr.Repr, tr.FormatArgs...) -} - -// Empty returns true if the result of String would be "". -func (tr *TypeRepr) Empty() bool { - return len(tr.Repr) == 0 -} - -// Set modifies the type representation. -// If fargs are provided, repr is used as a format for fmt.Sprintf. -// Otherwise, repr is used unprocessed as the type representation. -func (tr *TypeRepr) Set(repr string, fargs ...interface{}) { - tr.Repr = repr - tr.FormatArgs = fargs -} - -// Type returns a *Type with the same memory layout as -// dtype when used as the type of a variable or a struct field. -func (c *typeConv) Type(dtype dwarf.Type) *Type { - if t, ok := c.m[dtype]; ok { - if t.Go == nil { - fatalf("type conversion loop at %s", dtype) - } - return t - } - - t := new(Type) - t.Size = dtype.Size() - t.Align = -1 - t.C = &TypeRepr{Repr: dtype.Common().Name} - c.m[dtype] = t - - if t.Size < 0 { - // Unsized types are [0]byte - t.Size = 0 - t.Go = c.Opaque(0) - if t.C.Empty() { - t.C.Set("void") - } - return t - } - - switch dt := dtype.(type) { - default: - fatalf("unexpected type: %s", dtype) - - case *dwarf.AddrType: - if t.Size != c.ptrSize { - fatalf("unexpected: %d-byte address type - %s", t.Size, dtype) - } - t.Go = c.uintptr - t.Align = t.Size - - case *dwarf.ArrayType: - if dt.StrideBitSize > 0 { - // Cannot represent bit-sized elements in Go. - t.Go = c.Opaque(t.Size) - break - } - gt := &ast.ArrayType{ - Len: c.intExpr(dt.Count), - } - t.Go = gt // publish before recursive call - sub := c.Type(dt.Type) - t.Align = sub.Align - gt.Elt = sub.Go - t.C.Set("typeof(%s[%d])", sub.C, dt.Count) - - case *dwarf.BoolType: - t.Go = c.bool - t.Align = c.ptrSize - - case *dwarf.CharType: - if t.Size != 1 { - fatalf("unexpected: %d-byte char type - %s", t.Size, dtype) - } - t.Go = c.int8 - t.Align = 1 - - case *dwarf.EnumType: - if t.Align = t.Size; t.Align >= c.ptrSize { - t.Align = c.ptrSize - } - t.C.Set("enum " + dt.EnumName) - signed := 0 - t.EnumValues = make(map[string]int64) - for _, ev := range dt.Val { - t.EnumValues[ev.Name] = ev.Val - if ev.Val < 0 { - signed = signedDelta - } - } - switch t.Size + int64(signed) { - default: - fatalf("unexpected: %d-byte enum type - %s", t.Size, dtype) - case 1: - t.Go = c.uint8 - case 2: - t.Go = c.uint16 - case 4: - t.Go = c.uint32 - case 8: - t.Go = c.uint64 - case 1 + signedDelta: - t.Go = c.int8 - case 2 + signedDelta: - t.Go = c.int16 - case 4 + signedDelta: - t.Go = c.int32 - case 8 + signedDelta: - t.Go = c.int64 - } - - case *dwarf.FloatType: - switch t.Size { - default: - fatalf("unexpected: %d-byte float type - %s", t.Size, dtype) - case 4: - t.Go = c.float32 - case 8: - t.Go = c.float64 - } - if t.Align = t.Size; t.Align >= c.ptrSize { - t.Align = c.ptrSize - } - - case *dwarf.ComplexType: - switch t.Size { - default: - fatalf("unexpected: %d-byte complex type - %s", t.Size, dtype) - case 8: - t.Go = c.complex64 - case 16: - t.Go = c.complex128 - } - if t.Align = t.Size; t.Align >= c.ptrSize { - t.Align = c.ptrSize - } - - case *dwarf.FuncType: - // No attempt at translation: would enable calls - // directly between worlds, but we need to moderate those. - t.Go = c.uintptr - t.Align = c.ptrSize - - case *dwarf.IntType: - if dt.BitSize > 0 { - fatalf("unexpected: %d-bit int type - %s", dt.BitSize, dtype) - } - switch t.Size { - default: - fatalf("unexpected: %d-byte int type - %s", t.Size, dtype) - case 1: - t.Go = c.int8 - case 2: - t.Go = c.int16 - case 4: - t.Go = c.int32 - case 8: - t.Go = c.int64 - } - if t.Align = t.Size; t.Align >= c.ptrSize { - t.Align = c.ptrSize - } - - case *dwarf.PtrType: - t.Align = c.ptrSize - - // Translate void* as unsafe.Pointer - if _, ok := base(dt.Type).(*dwarf.VoidType); ok { - t.Go = c.unsafePointer - t.C.Set("void*") - break - } - - gt := &ast.StarExpr{} - t.Go = gt // publish before recursive call - sub := c.Type(dt.Type) - gt.X = sub.Go - t.C.Set("%s*", sub.C) - - case *dwarf.QualType: - // Ignore qualifier. - t = c.Type(dt.Type) - c.m[dtype] = t - return t - - case *dwarf.StructType: - // Convert to Go struct, being careful about alignment. - // Have to give it a name to simulate C "struct foo" references. - tag := dt.StructName - if tag == "" { - tag = "__" + strconv.Itoa(tagGen) - tagGen++ - } else if t.C.Empty() { - t.C.Set(dt.Kind + " " + tag) - } - name := c.Ident("_Ctype_" + dt.Kind + "_" + tag) - t.Go = name // publish before recursive calls - switch dt.Kind { - case "union", "class": - typedef[name.Name] = c.Opaque(t.Size) - if t.C.Empty() { - t.C.Set("typeof(unsigned char[%d])", t.Size) - } - case "struct": - g, csyntax, align := c.Struct(dt) - if t.C.Empty() { - t.C.Set(csyntax) - } - t.Align = align - typedef[name.Name] = g - } - - case *dwarf.TypedefType: - // Record typedef for printing. - if dt.Name == "_GoString_" { - // Special C name for Go string type. - // Knows string layout used by compilers: pointer plus length, - // which rounds up to 2 pointers after alignment. - t.Go = c.string - t.Size = c.ptrSize * 2 - t.Align = c.ptrSize - break - } - name := c.Ident("_Ctypedef_" + dt.Name) - t.Go = name // publish before recursive call - sub := c.Type(dt.Type) - t.Size = sub.Size - t.Align = sub.Align - if _, ok := typedef[name.Name]; !ok { - typedef[name.Name] = sub.Go - } - - case *dwarf.UcharType: - if t.Size != 1 { - fatalf("unexpected: %d-byte uchar type - %s", t.Size, dtype) - } - t.Go = c.uint8 - t.Align = 1 - - case *dwarf.UintType: - if dt.BitSize > 0 { - fatalf("unexpected: %d-bit uint type - %s", dt.BitSize, dtype) - } - switch t.Size { - default: - fatalf("unexpected: %d-byte uint type - %s", t.Size, dtype) - case 1: - t.Go = c.uint8 - case 2: - t.Go = c.uint16 - case 4: - t.Go = c.uint32 - case 8: - t.Go = c.uint64 - } - if t.Align = t.Size; t.Align >= c.ptrSize { - t.Align = c.ptrSize - } - - case *dwarf.VoidType: - t.Go = c.void - t.C.Set("void") - } - - switch dtype.(type) { - case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType: - s := dtype.Common().Name - if s != "" { - if ss, ok := dwarfToName[s]; ok { - s = ss - } - s = strings.Join(strings.Split(s, " "), "") // strip spaces - name := c.Ident("_Ctype_" + s) - typedef[name.Name] = t.Go - t.Go = name - } - } - - if t.C.Empty() { - fatalf("internal error: did not create C name for %s", dtype) - } - - return t -} - -// FuncArg returns a Go type with the same memory layout as -// dtype when used as the type of a C function argument. -func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { - t := c.Type(dtype) - switch dt := dtype.(type) { - case *dwarf.ArrayType: - // Arrays are passed implicitly as pointers in C. - // In Go, we must be explicit. - tr := &TypeRepr{} - tr.Set("%s*", t.C) - return &Type{ - Size: c.ptrSize, - Align: c.ptrSize, - Go: &ast.StarExpr{X: t.Go}, - C: tr, - } - case *dwarf.TypedefType: - // C has much more relaxed rules than Go for - // implicit type conversions. When the parameter - // is type T defined as *X, simulate a little of the - // laxness of C by making the argument *X instead of T. - if ptr, ok := base(dt.Type).(*dwarf.PtrType); ok { - // Unless the typedef happens to point to void* since - // Go has special rules around using unsafe.Pointer. - if _, void := base(ptr.Type).(*dwarf.VoidType); !void { - return c.Type(ptr) - } - } - } - return t -} - -// FuncType returns the Go type analogous to dtype. -// There is no guarantee about matching memory layout. -func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType { - p := make([]*Type, len(dtype.ParamType)) - gp := make([]*ast.Field, len(dtype.ParamType)) - for i, f := range dtype.ParamType { - // gcc's DWARF generator outputs a single DotDotDotType parameter for - // function pointers that specify no parameters (e.g. void - // (*__cgo_0)()). Treat this special case as void. This case is - // invalid according to ISO C anyway (i.e. void (*__cgo_1)(...) is not - // legal). - if _, ok := f.(*dwarf.DotDotDotType); ok && i == 0 { - p, gp = nil, nil - break - } - p[i] = c.FuncArg(f) - gp[i] = &ast.Field{Type: p[i].Go} - } - var r *Type - var gr []*ast.Field - if _, ok := dtype.ReturnType.(*dwarf.VoidType); !ok && dtype.ReturnType != nil { - r = c.Type(dtype.ReturnType) - gr = []*ast.Field{&ast.Field{Type: r.Go}} - } - return &FuncType{ - Params: p, - Result: r, - Go: &ast.FuncType{ - Params: &ast.FieldList{List: gp}, - Results: &ast.FieldList{List: gr}, - }, - } -} - -// Identifier -func (c *typeConv) Ident(s string) *ast.Ident { - return ast.NewIdent(s) -} - -// Opaque type of n bytes. -func (c *typeConv) Opaque(n int64) ast.Expr { - return &ast.ArrayType{ - Len: c.intExpr(n), - Elt: c.byte, - } -} - -// Expr for integer n. -func (c *typeConv) intExpr(n int64) ast.Expr { - return &ast.BasicLit{ - Kind: token.INT, - Value: strconv.Itoa64(n), - } -} - -// Add padding of given size to fld. -func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field { - n := len(fld) - fld = fld[0 : n+1] - fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident("_")}, Type: c.Opaque(size)} - return fld -} - -// Struct conversion: return Go and (6g) C syntax for type. -func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax string, align int64) { - var buf bytes.Buffer - buf.WriteString("struct {") - fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field - off := int64(0) - - // Rename struct fields that happen to be named Go keywords into - // _{keyword}. Create a map from C ident -> Go ident. The Go ident will - // be mangled. Any existing identifier that already has the same name on - // the C-side will cause the Go-mangled version to be prefixed with _. - // (e.g. in a struct with fields '_type' and 'type', the latter would be - // rendered as '__type' in Go). - ident := make(map[string]string) - used := make(map[string]bool) - for _, f := range dt.Field { - ident[f.Name] = f.Name - used[f.Name] = true - } - for cid, goid := range ident { - if token.Lookup([]byte(goid)).IsKeyword() { - // Avoid keyword - goid = "_" + goid - - // Also avoid existing fields - for _, exist := used[goid]; exist; _, exist = used[goid] { - goid = "_" + goid - } - - used[goid] = true - ident[cid] = goid - } - } - - for _, f := range dt.Field { - if f.BitSize > 0 && f.BitSize != f.ByteSize*8 { - continue - } - if f.ByteOffset > off { - fld = c.pad(fld, f.ByteOffset-off) - off = f.ByteOffset - } - t := c.Type(f.Type) - n := len(fld) - fld = fld[0 : n+1] - - fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go} - off += t.Size - buf.WriteString(t.C.String()) - buf.WriteString(" ") - buf.WriteString(f.Name) - buf.WriteString("; ") - if t.Align > align { - align = t.Align - } - } - if off < dt.ByteSize { - fld = c.pad(fld, dt.ByteSize-off) - off = dt.ByteSize - } - if off != dt.ByteSize { - fatalf("struct size calculation error") - } - buf.WriteString("}") - csyntax = buf.String() - expr = &ast.StructType{Fields: &ast.FieldList{List: fld}} - return -} diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go deleted file mode 100644 index be9c2bc4f..000000000 --- a/src/cmd/cgo/main.go +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2009 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. - -// Cgo; see gmp.go for an overview. - -// TODO(rsc): -// Emit correct line number annotations. -// Make 6g understand the annotations. - -package main - -import ( - "crypto/md5" - "flag" - "fmt" - "go/ast" - "go/token" - "io" - "os" - "path/filepath" - "reflect" - "strings" -) - -// A Package collects information about the package we're going to write. -type Package struct { - PackageName string // name of package - PackagePath string - PtrSize int64 - GccOptions []string - CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS) - Written map[string]bool - Name map[string]*Name // accumulated Name from Files - Typedef map[string]ast.Expr // accumulated Typedef from Files - ExpFunc []*ExpFunc // accumulated ExpFunc from Files - Decl []ast.Decl - GoFiles []string // list of Go files - GccFiles []string // list of gcc output files -} - -// A File collects information about a single Go input file. -type File struct { - AST *ast.File // parsed AST - Package string // Package name - Preamble string // C preamble (doc comment on import "C") - Ref []*Ref // all references to C.xxx in AST - ExpFunc []*ExpFunc // exported functions for this file - Name map[string]*Name // map from Go name to Name - Typedef map[string]ast.Expr // translations of all necessary types from C -} - -// A Ref refers to an expression of the form C.xxx in the AST. -type Ref struct { - Name *Name - Expr *ast.Expr - Context string // "type", "expr", "call", or "call2" -} - -func (r *Ref) Pos() token.Pos { - return (*r.Expr).Pos() -} - -// A Name collects information about C.xxx. -type Name struct { - Go string // name used in Go referring to package C - Mangle string // name used in generated Go - C string // name used in C - Define string // #define expansion - Kind string // "const", "type", "var", "func", "not-type" - Type *Type // the type of xxx - FuncType *FuncType - AddError bool - Const string // constant definition -} - -// A ExpFunc is an exported function, callable from C. -// Such functions are identified in the Go input file -// by doc comments containing the line //export ExpName -type ExpFunc struct { - Func *ast.FuncDecl - ExpName string // name to use from C -} - -// A TypeRepr contains the string representation of a type. -type TypeRepr struct { - Repr string - FormatArgs []interface{} -} - -// A Type collects information about a type in both the C and Go worlds. -type Type struct { - Size int64 - Align int64 - C *TypeRepr - Go ast.Expr - EnumValues map[string]int64 -} - -// A FuncType collects information about a function type in both the C and Go worlds. -type FuncType struct { - Params []*Type - Result *Type - Go *ast.FuncType -} - -func usage() { - fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n") - flag.PrintDefaults() - os.Exit(2) -} - -var ptrSizeMap = map[string]int64{ - "386": 4, - "amd64": 8, - "arm": 4, -} - -var cPrefix string - -var fset = token.NewFileSet() - -var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") - -func main() { - flag.Usage = usage - flag.Parse() - - if *dynobj != "" { - // cgo -dynimport is essentially a separate helper command - // built into the cgo binary. It scans a gcc-produced executable - // and dumps information about the imported symbols and the - // imported libraries. The Make.pkg rules for cgo prepare an - // appropriate executable and then use its import information - // instead of needing to make the linkers duplicate all the - // specialized knowledge gcc has about where to look for imported - // symbols and which ones to use. - dynimport(*dynobj) - return - } - - args := flag.Args() - if len(args) < 1 { - usage() - } - - // Find first arg that looks like a go file and assume everything before - // that are options to pass to gcc. - var i int - for i = len(args); i > 0; i-- { - if !strings.HasSuffix(args[i-1], ".go") { - break - } - } - if i == len(args) { - usage() - } - - // Copy it to a new slice so it can grow. - gccOptions := make([]string, i) - copy(gccOptions, args[0:i]) - - goFiles := args[i:] - - arch := os.Getenv("GOARCH") - if arch == "" { - fatalf("$GOARCH is not set") - } - ptrSize := ptrSizeMap[arch] - if ptrSize == 0 { - fatalf("unknown $GOARCH %q", arch) - } - - // Clear locale variables so gcc emits English errors [sic]. - os.Setenv("LANG", "en_US.UTF-8") - os.Setenv("LC_ALL", "C") - os.Setenv("LC_CTYPE", "C") - - p := &Package{ - PtrSize: ptrSize, - GccOptions: gccOptions, - CgoFlags: make(map[string]string), - Written: make(map[string]bool), - } - - // Need a unique prefix for the global C symbols that - // we use to coordinate between gcc and ourselves. - // We already put _cgo_ at the beginning, so the main - // concern is other cgo wrappers for the same functions. - // Use the beginning of the md5 of the input to disambiguate. - h := md5.New() - for _, input := range goFiles { - f, err := os.Open(input) - if err != nil { - fatalf("%s", err) - } - io.Copy(h, f) - f.Close() - } - cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6]) - - fs := make([]*File, len(goFiles)) - for i, input := range goFiles { - // Parse flags for all files before translating due to CFLAGS. - f := new(File) - f.ReadGo(input) - p.ParseFlags(f, input) - fs[i] = f - } - - // make sure that _obj directory exists, so that we can write - // all the output files there. - os.Mkdir("_obj", 0777) - - for i, input := range goFiles { - f := fs[i] - p.Translate(f) - for _, cref := range f.Ref { - switch cref.Context { - case "call", "call2": - if cref.Name.Kind != "type" { - break - } - *cref.Expr = cref.Name.Type.Go - } - } - if nerrors > 0 { - os.Exit(2) - } - pkg := f.Package - if dir := os.Getenv("CGOPKGPATH"); dir != "" { - pkg = filepath.Join(dir, pkg) - } - p.PackagePath = pkg - p.writeOutput(f, input) - - p.Record(f) - } - - p.writeDefs() - if nerrors > 0 { - os.Exit(2) - } -} - -// Record what needs to be recorded about f. -func (p *Package) Record(f *File) { - if p.PackageName == "" { - p.PackageName = f.Package - } else if p.PackageName != f.Package { - error(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package) - } - - if p.Name == nil { - p.Name = f.Name - } else { - for k, v := range f.Name { - if p.Name[k] == nil { - p.Name[k] = v - } else if !reflect.DeepEqual(p.Name[k], v) { - error(token.NoPos, "inconsistent definitions for C.%s", k) - } - } - } - - p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) - p.Decl = append(p.Decl, f.AST.Decls...) -} diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go deleted file mode 100644 index 6802dd1cf..000000000 --- a/src/cmd/cgo/out.go +++ /dev/null @@ -1,730 +0,0 @@ -// Copyright 2009 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 main - -import ( - "bytes" - "debug/elf" - "debug/macho" - "debug/pe" - "fmt" - "go/ast" - "go/printer" - "go/token" - "os" - "path/filepath" - "strings" -) - -var objDir = "_obj" + string(filepath.Separator) - -// writeDefs creates output files to be compiled by 6g, 6c, and gcc. -// (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) -func (p *Package) writeDefs() { - fgo2 := creat(objDir + "_cgo_gotypes.go") - fc := creat(objDir + "_cgo_defun.c") - fm := creat(objDir + "_cgo_main.c") - - fflg := creat(objDir + "_cgo_flags") - for k, v := range p.CgoFlags { - fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) - } - fflg.Close() - - // Write C main file for using gcc to resolve imports. - fmt.Fprintf(fm, "int main() { return 0; }\n") - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") - fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") - fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") - - // Write second Go output: definitions of _C_xxx. - // In a separate file so that the import of "unsafe" does not - // pollute the original file. - fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") - fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) - fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") - fmt.Fprintf(fgo2, "import \"os\"\n\n") - fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") - fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n") - fmt.Fprintf(fgo2, "func _Cerrno(dst *os.Error, x int) { *dst = os.Errno(x) }\n") - - for name, def := range typedef { - fmt.Fprintf(fgo2, "type %s ", name) - printer.Fprint(fgo2, fset, def) - fmt.Fprintf(fgo2, "\n") - } - fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") - - fmt.Fprintf(fc, cProlog) - - var cVars []string - for _, n := range p.Name { - if n.Kind != "var" { - continue - } - cVars = append(cVars, n.C) - - fmt.Fprintf(fm, "extern char %s[];\n", n.C) - fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) - - fmt.Fprintf(fc, "extern byte *%s;\n", n.C) - fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) - fmt.Fprintf(fc, "\n") - - fmt.Fprintf(fgo2, "var %s ", n.Mangle) - printer.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go}) - fmt.Fprintf(fgo2, "\n") - } - fmt.Fprintf(fc, "\n") - - for _, n := range p.Name { - if n.Const != "" { - fmt.Fprintf(fgo2, "const _Cconst_%s = %s\n", n.Go, n.Const) - } - } - fmt.Fprintf(fgo2, "\n") - - for _, n := range p.Name { - if n.FuncType != nil { - p.writeDefsFunc(fc, fgo2, n) - } - } - - p.writeExports(fgo2, fc, fm) - - fgo2.Close() - fc.Close() -} - -func dynimport(obj string) { - if f, err := elf.Open(obj); err == nil { - sym, err := f.ImportedSymbols() - if err != nil { - fatalf("cannot load imported symbols from ELF file %s: %v", obj, err) - } - for _, s := range sym { - targ := s.Name - if s.Version != "" { - targ += "@" + s.Version - } - fmt.Printf("#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) - } - lib, err := f.ImportedLibraries() - if err != nil { - fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) - } - for _, l := range lib { - fmt.Printf("#pragma dynimport _ _ %q\n", l) - } - return - } - - if f, err := macho.Open(obj); err == nil { - sym, err := f.ImportedSymbols() - if err != nil { - fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err) - } - for _, s := range sym { - if len(s) > 0 && s[0] == '_' { - s = s[1:] - } - fmt.Printf("#pragma dynimport %s %s %q\n", s, s, "") - } - lib, err := f.ImportedLibraries() - if err != nil { - fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) - } - for _, l := range lib { - fmt.Printf("#pragma dynimport _ _ %q\n", l) - } - return - } - - if f, err := pe.Open(obj); err == nil { - sym, err := f.ImportedSymbols() - if err != nil { - fatalf("cannot load imported symbols from PE file %s: %v", obj, err) - } - for _, s := range sym { - ss := strings.Split(s, ":") - fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) - } - return - } - - fatalf("cannot parse %s as ELF, Mach-O or PE", obj) -} - -// Construct a gcc struct matching the 6c argument frame. -// Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. -// These assumptions are checked by the gccProlog. -// Also assumes that 6c convention is to word-align the -// input and output parameters. -func (p *Package) structType(n *Name) (string, int64) { - var buf bytes.Buffer - fmt.Fprint(&buf, "struct {\n") - off := int64(0) - for i, t := range n.FuncType.Params { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) - off += pad - } - fmt.Fprintf(&buf, "\t\t%s p%d;\n", t.C, i) - off += t.Size - } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) - off += pad - } - if t := n.FuncType.Result; t != nil { - if off%t.Align != 0 { - pad := t.Align - off%t.Align - fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) - off += pad - } - qual := "" - if c := t.C.String(); c[len(c)-1] == '*' { - qual = "const " - } - fmt.Fprintf(&buf, "\t\t%s%s r;\n", qual, t.C) - off += t.Size - } - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) - off += pad - } - if n.AddError { - fmt.Fprint(&buf, "\t\tvoid *e[2]; /* os.Error */\n") - off += 2 * p.PtrSize - } - if off == 0 { - fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct - } - fmt.Fprintf(&buf, "\t}") - return buf.String(), off -} - -func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { - name := n.Go - gtype := n.FuncType.Go - if n.AddError { - // Add "os.Error" to return type list. - // Type list is known to be 0 or 1 element - it's a C function. - err := &ast.Field{Type: ast.NewIdent("os.Error")} - l := gtype.Results.List - if len(l) == 0 { - l = []*ast.Field{err} - } else { - l = []*ast.Field{l[0], err} - } - t := new(ast.FuncType) - *t = *gtype - t.Results = &ast.FieldList{List: l} - gtype = t - } - - // Go func declaration. - d := &ast.FuncDecl{ - Name: ast.NewIdent(n.Mangle), - Type: gtype, - } - printer.Fprint(fgo2, fset, d) - fmt.Fprintf(fgo2, "\n") - - if name == "CString" || name == "GoString" || name == "GoStringN" { - // The builtins are already defined in the C prolog. - return - } - - var argSize int64 - _, argSize = p.structType(n) - - // C wrapper calls into gcc, passing a pointer to the argument frame. - fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) - fmt.Fprintf(fc, "\n") - fmt.Fprintf(fc, "void\n") - if argSize == 0 { - argSize++ - } - fmt.Fprintf(fc, "·%s(struct{uint8 x[%d];}p)\n", n.Mangle, argSize) - fmt.Fprintf(fc, "{\n") - fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle) - if n.AddError { - // gcc leaves errno in first word of interface at end of p. - // check whether it is zero; if so, turn interface into nil. - // if not, turn interface into errno. - // Go init function initializes ·_Cerrno with an os.Errno - // for us to copy. - fmt.Fprintln(fc, ` { - int32 e; - void **v; - v = (void**)(&p+1) - 2; /* v = final two void* of p */ - e = *(int32*)v; - v[0] = (void*)0xdeadbeef; - v[1] = (void*)0xdeadbeef; - if(e == 0) { - /* nil interface */ - v[0] = 0; - v[1] = 0; - } else { - ·_Cerrno(v, e); /* fill in v as os.Error for errno e */ - } - }`) - } - fmt.Fprintf(fc, "}\n") - fmt.Fprintf(fc, "\n") -} - -// writeOutput creates stubs for a specific source file to be compiled by 6g -// (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) -func (p *Package) writeOutput(f *File, srcfile string) { - base := srcfile - if strings.HasSuffix(base, ".go") { - base = base[0 : len(base)-3] - } - base = strings.Map(slashToUnderscore, base) - fgo1 := creat(objDir + base + ".cgo1.go") - fgcc := creat(objDir + base + ".cgo2.c") - - p.GoFiles = append(p.GoFiles, base+".cgo1.go") - p.GccFiles = append(p.GccFiles, base+".cgo2.c") - - // Write Go output: Go input with rewrites of C.xxx to _C_xxx. - fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n") - fmt.Fprintf(fgo1, "//line %s:1\n", srcfile) - printer.Fprint(fgo1, fset, f.AST) - - // While we process the vars and funcs, also write 6c and gcc output. - // Gcc output starts with the preamble. - fmt.Fprintf(fgcc, "%s\n", f.Preamble) - fmt.Fprintf(fgcc, "%s\n", gccProlog) - - for _, n := range f.Name { - if n.FuncType != nil { - p.writeOutputFunc(fgcc, n) - } - } - - fgo1.Close() - fgcc.Close() -} - -func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { - name := n.Mangle - if name == "_Cfunc_CString" || name == "_Cfunc_GoString" || name == "_Cfunc_GoStringN" || p.Written[name] { - // The builtins are already defined in the C prolog, and we don't - // want to duplicate function definitions we've already done. - return - } - p.Written[name] = true - - ctype, _ := p.structType(n) - - // Gcc wrapper unpacks the C argument struct - // and calls the actual C function. - fmt.Fprintf(fgcc, "void\n") - fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle) - fmt.Fprintf(fgcc, "{\n") - if n.AddError { - fmt.Fprintf(fgcc, "\tint e;\n") // assuming 32 bit (see comment above structType) - fmt.Fprintf(fgcc, "\terrno = 0;\n") - } - // We're trying to write a gcc struct that matches 6c/8c/5c's layout. - // Use packed attribute to force no padding in this struct in case - // gcc has different packing requirements. For example, - // on 386 Windows, gcc wants to 8-align int64s, but 8c does not. - fmt.Fprintf(fgcc, "\t%s __attribute__((__packed__)) *a = v;\n", ctype) - fmt.Fprintf(fgcc, "\t") - if t := n.FuncType.Result; t != nil { - fmt.Fprintf(fgcc, "a->r = ") - if c := t.C.String(); c[len(c)-1] == '*' { - fmt.Fprintf(fgcc, "(const %s) ", t.C) - } - } - fmt.Fprintf(fgcc, "%s(", n.C) - for i := range n.FuncType.Params { - if i > 0 { - fmt.Fprintf(fgcc, ", ") - } - fmt.Fprintf(fgcc, "a->p%d", i) - } - fmt.Fprintf(fgcc, ");\n") - if n.AddError { - fmt.Fprintf(fgcc, "\t*(int*)(a->e) = errno;\n") - } - fmt.Fprintf(fgcc, "}\n") - fmt.Fprintf(fgcc, "\n") -} - -// Write out the various stubs we need to support functions exported -// from Go so that they are callable from C. -func (p *Package) writeExports(fgo2, fc, fm *os.File) { - fgcc := creat(objDir + "_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.ExpFunc { - fn := exp.Func - - // Construct a gcc struct matching the 6c argument and - // result frame. The gcc struct will be compiled with - // __attribute__((packed)) so all padding must be accounted - // for explicitly. - ctype := "struct {\n" - off := int64(0) - npad := 0 - if fn.Recv != nil { - t := p.cgoType(fn.Recv.List[0].Type) - ctype += 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 - ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - ctype += 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 - ctype += 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 - ctype += fmt.Sprintf("\t\tchar __pad%d[%d]\n", npad, pad) - off += pad - npad++ - } - ctype += 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 - ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - if ctype == "struct {\n" { - ctype += "\t\tchar unused;\n" // avoid empty struct - } - ctype += "\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.String() - } 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.String() - 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_%s(void *, int);\n", cPrefix, exp.ExpName) - fmt.Fprintf(fgcc, "\n%s\n", s) - fmt.Fprintf(fgcc, "{\n") - fmt.Fprintf(fgcc, "\t%s __attribute__((packed)) a;\n", ctype) - 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_%s, &a, %d);\n", cPrefix, exp.ExpName, off) - 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" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname - } - fmt.Fprintf(fc, "extern void ·%s();\n", goname) - fmt.Fprintf(fc, "\nvoid\n") - fmt.Fprintf(fc, "_cgoexp%s_%s(void *a, int32 n)\n", cPrefix, exp.ExpName) - fmt.Fprintf(fc, "{\n") - fmt.Fprintf(fc, "\truntime·cgocallback(·%s, a, n);\n", goname) - fmt.Fprintf(fc, "}\n") - - fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName) - - // 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, fset, fn.Recv.List[0].Type) - forFieldList(fntype.Params, - func(i int, atype ast.Expr) { - fmt.Fprintf(fgo2, ", p%d ", i) - printer.Fprint(fgo2, fset, 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, fset, 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++ - } - } - } -} - -func c(repr string, args ...interface{}) *TypeRepr { - return &TypeRepr{repr, args} -} - -// Map predeclared Go types to Type. -var goTypes = map[string]*Type{ - "int": &Type{Size: 4, Align: 4, C: c("int")}, - "uint": &Type{Size: 4, Align: 4, C: c("uint")}, - "int8": &Type{Size: 1, Align: 1, C: c("schar")}, - "uint8": &Type{Size: 1, Align: 1, C: c("uchar")}, - "int16": &Type{Size: 2, Align: 2, C: c("short")}, - "uint16": &Type{Size: 2, Align: 2, C: c("ushort")}, - "int32": &Type{Size: 4, Align: 4, C: c("int")}, - "uint32": &Type{Size: 4, Align: 4, C: c("uint")}, - "int64": &Type{Size: 8, Align: 8, C: c("int64")}, - "uint64": &Type{Size: 8, Align: 8, C: c("uint64")}, - "float": &Type{Size: 4, Align: 4, C: c("float")}, - "float32": &Type{Size: 4, Align: 4, C: c("float")}, - "float64": &Type{Size: 8, Align: 8, C: c("double")}, - "complex": &Type{Size: 8, Align: 8, C: c("__complex float")}, - "complex64": &Type{Size: 8, Align: 8, C: c("__complex float")}, - "complex128": &Type{Size: 16, Align: 16, C: c("__complex double")}, -} - -// Map an ast type to a Type. -func (p *Package) 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: c("%s*", x.C)} - case *ast.ArrayType: - if t.Len == nil { - return &Type{Size: p.PtrSize + 8, Align: p.PtrSize, C: c("GoSlice")} - } - case *ast.StructType: - // TODO - case *ast.FuncType: - return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")} - case *ast.InterfaceType: - return &Type{Size: 3 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} - case *ast.MapType: - return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoMap")} - case *ast.ChanType: - return &Type{Size: p.PtrSize, Align: p.PtrSize, C: 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.Decl { - 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 typedef { - if name == t.Name { - return p.cgoType(def) - } - } - if t.Name == "uintptr" { - return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("uintptr")} - } - if t.Name == "string" { - return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: c("GoString")} - } - if r, ok := goTypes[t.Name]; ok { - if r.Align > p.PtrSize { - r.Align = p.PtrSize - } - return r - } - case *ast.SelectorExpr: - id, ok := t.X.(*ast.Ident) - if ok && id.Name == "unsafe" && t.Sel.Name == "Pointer" { - return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")} - } - } - error(e.Pos(), "unrecognized Go type %T", e) - return &Type{Size: 4, Align: 4, C: 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 -// out of the compiler and hopefully mention "name". -#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1]; - -// Check at compile time that the sizes we use match our expectations. -#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n) - -__cgo_size_assert(char, 1) -__cgo_size_assert(short, 2) -__cgo_size_assert(int, 4) -typedef long long __cgo_long_long; -__cgo_size_assert(__cgo_long_long, 8) -__cgo_size_assert(float, 4) -__cgo_size_assert(double, 8) - -#include <errno.h> -#include <string.h> -` - -const builtinProlog = ` -typedef struct { char *p; int n; } _GoString_; -_GoString_ GoString(char *p); -_GoString_ GoStringN(char *p, int l); -char *CString(_GoString_); -` - -const cProlog = ` -#include "runtime.h" -#include "cgocall.h" - -void ·_Cerrno(void*, int32); - -void -·_Cfunc_GoString(int8 *p, String s) -{ - s = runtime·gostring((byte*)p); - FLUSH(&s); -} - -void -·_Cfunc_GoStringN(int8 *p, int32 l, String s) -{ - s = runtime·gostringn((byte*)p, l); - FLUSH(&s); -} - -void -·_Cfunc_CString(String s, int8 *p) -{ - p = runtime·cmalloc(s.len+1); - runtime·mcpy((byte*)p, s.str, s.len); - p[s.len] = 0; - 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 __SIZE_TYPE__ uintptr; - -typedef struct { char *p; int n; } GoString; -typedef void *GoMap; -typedef void *GoChan; -typedef struct { void *t; void *v; } GoInterface; -` diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go deleted file mode 100644 index e79b0e1bf..000000000 --- a/src/cmd/cgo/util.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2009 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 main - -import ( - "exec" - "fmt" - "go/token" - "io/ioutil" - "os" -) - -// run runs the command argv, feeding in stdin on standard input. -// It returns the output to standard output and standard error. -// ok indicates whether the command exited successfully. -func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { - cmd, err := exec.LookPath(argv[0]) - if err != nil { - fatalf("exec %s: %s", argv[0], err) - } - r0, w0, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - r1, w1, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - r2, w2, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - p, err := os.StartProcess(cmd, argv, &os.ProcAttr{Files: []*os.File{r0, w1, w2}}) - if err != nil { - fatalf("%s", err) - } - defer p.Release() - r0.Close() - w1.Close() - w2.Close() - c := make(chan bool) - go func() { - w0.Write(stdin) - w0.Close() - c <- true - }() - go func() { - stdout, _ = ioutil.ReadAll(r1) - r1.Close() - c <- true - }() - stderr, _ = ioutil.ReadAll(r2) - r2.Close() - <-c - <-c - - w, err := p.Wait(0) - if err != nil { - fatalf("%s", err) - } - ok = w.Exited() && w.ExitStatus() == 0 - return -} - -// Die with an error message. -func fatalf(msg string, args ...interface{}) { - fmt.Fprintf(os.Stderr, msg+"\n", args...) - os.Exit(2) -} - -var nerrors int - -func error(pos token.Pos, msg string, args ...interface{}) { - nerrors++ - if pos.IsValid() { - fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String()) - } - fmt.Fprintf(os.Stderr, msg, args...) - fmt.Fprintf(os.Stderr, "\n") -} - -// isName returns true if s is a valid C identifier -func isName(s string) bool { - for i, v := range s { - if v != '_' && (v < 'A' || v > 'Z') && (v < 'a' || v > 'z') && (v < '0' || v > '9') { - return false - } - if i == 0 && '0' <= v && v <= '9' { - return false - } - } - return s != "" -} - -func creat(name string) *os.File { - f, err := os.Create(name) - if err != nil { - fatalf("%s", err) - } - return f -} - -func slashToUnderscore(c int) int { - if c == '/' || c == '\\' || c == ':' { - c = '_' - } - return c -} |