]> Cypherpunks repositories - gostls13.git/commitdiff
cgo: can look up C identifier kind (type or value) and type
authorRuss Cox <rsc@golang.org>
Fri, 18 Sep 2009 18:52:00 +0000 (11:52 -0700)
committerRuss Cox <rsc@golang.org>
Fri, 18 Sep 2009 18:52:00 +0000 (11:52 -0700)
gmp.go:197:4:  type mpz_t          C type  mpz_t
gmp.go:205:2:  call mpz_init       C value func(mpz_ptr) void
gmp.go:206:2:  call mpz_set        C value func(mpz_ptr, mpz_srcptr) void
gmp.go:221:2:  call mpz_init       C value func(mpz_ptr) void
gmp.go:227:7:  call size_t         C type  size_t
gmp.go:228:2:  call mpz_export     C value func(*void, *size_t, int, size_t, int, size_t, mpz_srcptr) *void
gmp.go:235:13: call mpz_sizeinbase C value func(mpz_srcptr, int) size_t
gmp.go:241:2:  call mpz_set        C value func(mpz_ptr, mpz_srcptr) void
gmp.go:252:3:  call mpz_import     C value func(mpz_ptr, size_t, int, size_t, int, size_t, *const void) void
gmp.go:261:2:  call mpz_set_si     C value func(mpz_ptr, long int) void
gmp.go:273:5:  call mpz_set_str    C value func(mpz_ptr, *const char, int) int
gmp.go:282:9:  call mpz_get_str    C value func(*char, int, mpz_srcptr) *char
gmp.go:287:3:  call mpz_clear      C value func(mpz_ptr) void
gmp.go:302:2:  call mpz_add        C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void
gmp.go:311:2:  call mpz_sub        C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void
gmp.go:320:2:  call mpz_mul        C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void
gmp.go:329:2:  call mpz_tdiv_q     C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void
gmp.go:339:2:  call mpz_tdiv_r     C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void
gmp.go:348:2:  call mpz_mul_2exp   C value func(mpz_ptr, mpz_srcptr, long unsigned int) void
gmp.go:356:2:  call mpz_div_2exp   C value func(mpz_ptr, mpz_srcptr, long unsigned int) void
gmp.go:367:3:  call mpz_pow_ui     C value func(mpz_ptr, mpz_srcptr, long unsigned int) void
gmp.go:369:3:  call mpz_powm       C value func(mpz_ptr, mpz_srcptr, mpz_srcptr, mpz_srcptr) void
gmp.go:378:2:  call mpz_neg        C value func(mpz_ptr, mpz_srcptr) void
gmp.go:386:2:  call mpz_abs        C value func(mpz_ptr, mpz_srcptr) void
gmp.go:404:9:  call mpz_cmp        C value func(mpz_srcptr, mpz_srcptr) int
gmp.go:413:2:  call mpz_tdiv_qr    C value func(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr) void
gmp.go:426:2:  call mpz_gcdext     C value func(mpz_ptr, mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr) void

R=r
DELTA=938  (628 added, 308 deleted, 2 changed)
OCL=34733
CL=34791

src/cmd/cgo/Makefile
src/cmd/cgo/ast.go [new file with mode: 0644]
src/cmd/cgo/cgo.go [deleted file]
src/cmd/cgo/gcc.go [new file with mode: 0644]
src/cmd/cgo/gmp.go
src/cmd/cgo/main.go [new file with mode: 0644]
src/cmd/cgo/util.go [new file with mode: 0644]

index 8d9e11e539c3677113359852f9e34c042ded6cfb..c7a2ed9c4c4d125a6b834c2668d64bef01728aa1 100644 (file)
@@ -6,6 +6,9 @@ include $(GOROOT)/src/Make.$(GOARCH)
 
 TARG=cgo
 GOFILES=\
-       cgo.go\
+       ast.go\
+       gcc.go\
+       main.go\
+       util.go\
 
 include $(GOROOT)/src/Make.cmd
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
new file mode 100644 (file)
index 0000000..a5f4995
--- /dev/null
@@ -0,0 +1,288 @@
+// 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 (
+       "debug/dwarf";
+       "fmt";
+       "go/ast";
+       "go/doc";
+       "go/parser";
+       "go/scanner";
+       "os";
+)
+
+// A Cref refers to an expression of the form C.xxx in the AST.
+type Cref struct {
+       Name string;
+       Expr *ast.Expr;
+       Context string; // "type", "expr", or "call"
+       TypeName bool;  // whether xxx is a C type name
+       DebugType dwarf.Type;   // the type of xxx
+}
+
+// A Prog collects information about a cgo program.
+type Prog struct {
+       AST *ast.File;  // parsed AST
+       Preamble string;        // C preamble (doc comment on import "C")
+       Crefs []*Cref;
+}
+
+func openProg(name string) *Prog {
+       p := new(Prog);
+       var err os.Error;
+       p.AST, err = parser.ParsePkgFile("", name, parser.ParseComments);
+       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);
+               }
+               fatal("parsing %s: %s", name, err);
+       }
+
+       // Find the import "C" line and get any extra C preamble.
+       found := false;
+       for _, d := range p.AST.Decls {
+               d, ok := d.(*ast.GenDecl);
+               if !ok {
+                       continue;
+               }
+               for _, s := range d.Specs {
+                       s, ok := s.(*ast.ImportSpec);
+                       if !ok {
+                               continue;
+                       }
+                       if len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` {
+                               continue;
+                       }
+                       found = true;
+                       if s.Name != nil {
+                               error(s.Path[0].Pos(), `cannot rename import "C"`);
+                       }
+                       if s.Doc != nil {
+                               p.Preamble += doc.CommentText(s.Doc) + "\n";
+                       }
+                       else if len(d.Specs) == 1 && d.Doc != nil {
+                               p.Preamble += doc.CommentText(d.Doc) + "\n";
+                       }
+               }
+       }
+       if !found {
+               error(noPos, `cannot find import "C"`);
+       }
+
+       // Accumulate pointers to uses of C.x.
+       p.Crefs = make([]*Cref, 0, 8);
+       walk(p.AST, p, "prog");
+       return p;
+}
+
+func walk(x interface{}, p *Prog, context string) {
+       switch n := x.(type) {
+       case *ast.Expr:
+               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.Value == "C" {
+                               i := len(p.Crefs);
+                               if i >= cap(p.Crefs) {
+                                       new := make([]*Cref, 2*i);
+                                       for j, v := range p.Crefs {
+                                               new[j] = v;
+                                       }
+                                       p.Crefs = new;
+                               }
+                               p.Crefs = p.Crefs[0:i+1];
+                               p.Crefs[i] = &Cref{
+                                       Name: sel.Sel.Value,
+                                       Expr: n,
+                                       Context: context
+                               };
+                               break;
+                       }
+               }
+               walk(*n, p, context);
+
+       // everything else just recurs
+       default:
+               error(noPos, "unexpected type %T in walk", x);
+               panic();
+
+       case nil:
+
+       // These are ordered and grouped to match ../../pkg/go/ast/ast.go
+       case *ast.Field:
+               walk(&n.Type, p, "type");
+       case *ast.BadExpr:
+       case *ast.Ident:
+       case *ast.Ellipsis:
+       case *ast.BasicLit:
+       case *ast.StringList:
+       case *ast.FuncLit:
+               walk(n.Type, p, "type");
+               walk(n.Body, p, "stmt");
+       case *ast.CompositeLit:
+               walk(&n.Type, p, "type");
+               walk(n.Elts, p, "expr");
+       case *ast.ParenExpr:
+               walk(&n.X, p, context);
+       case *ast.SelectorExpr:
+               walk(&n.X, p, "selector");
+       case *ast.IndexExpr:
+               walk(&n.X, p, "expr");
+               walk(&n.Index, p, "expr");
+               if n.End != nil {
+                       walk(&n.End, p, "expr");
+               }
+       case *ast.TypeAssertExpr:
+               walk(&n.X, p, "expr");
+               walk(&n.Type, p, "type");
+       case *ast.CallExpr:
+               walk(&n.Fun, p, "call");
+               walk(n.Args, p, "expr");
+       case *ast.StarExpr:
+               walk(&n.X, p, context);
+       case *ast.UnaryExpr:
+               walk(&n.X, p, "expr");
+       case *ast.BinaryExpr:
+               walk(&n.X, p, "expr");
+               walk(&n.Y, p, "expr");
+       case *ast.KeyValueExpr:
+               walk(&n.Key, p, "expr");
+               walk(&n.Value, p, "expr");
+
+       case *ast.ArrayType:
+               walk(&n.Len, p, "expr");
+               walk(&n.Elt, p, "type");
+       case *ast.StructType:
+               walk(n.Fields, p, "field");
+       case *ast.FuncType:
+               walk(n.Params, p, "field");
+               walk(n.Results, p, "field");
+       case *ast.InterfaceType:
+               walk(n.Methods, p, "field");
+       case *ast.MapType:
+               walk(&n.Key, p, "type");
+               walk(&n.Value, p, "type");
+       case *ast.ChanType:
+               walk(&n.Value, p, "type");
+
+       case *ast.BadStmt:
+       case *ast.DeclStmt:
+               walk(n.Decl, p, "decl");
+       case *ast.EmptyStmt:
+       case *ast.LabeledStmt:
+               walk(n.Stmt, p, "stmt");
+       case *ast.ExprStmt:
+               walk(&n.X, p, "expr");
+       case *ast.IncDecStmt:
+               walk(&n.X, p, "expr");
+       case *ast.AssignStmt:
+               walk(n.Lhs, p, "expr");
+               walk(n.Rhs, p, "expr");
+       case *ast.GoStmt:
+               walk(&n.Call, p, "expr");
+       case *ast.DeferStmt:
+               walk(&n.Call, p, "expr");
+       case *ast.ReturnStmt:
+               walk(n.Results, p, "expr");
+       case *ast.BranchStmt:
+       case *ast.BlockStmt:
+               walk(n.List, p, "stmt");
+       case *ast.IfStmt:
+               walk(n.Init, p, "stmt");
+               walk(&n.Cond, p, "expr");
+               walk(n.Body, p, "stmt");
+               walk(n.Else, p, "stmt");
+       case *ast.CaseClause:
+               walk(n.Values, p, "expr");
+               walk(n.Body, p, "stmt");
+       case *ast.SwitchStmt:
+               walk(n.Init, p, "stmt");
+               walk(&n.Tag, p, "expr");
+               walk(n.Body, p, "stmt");
+       case *ast.TypeCaseClause:
+               walk(n.Types, p, "type");
+               walk(n.Body, p, "stmt");
+       case *ast.TypeSwitchStmt:
+               walk(n.Init, p, "stmt");
+               walk(n.Assign, p, "stmt");
+               walk(n.Body, p, "stmt");
+       case *ast.CommClause:
+               walk(n.Lhs, p, "expr");
+               walk(n.Rhs, p, "expr");
+               walk(n.Body, p, "stmt");
+       case *ast.SelectStmt:
+               walk(n.Body, p, "stmt");
+       case *ast.ForStmt:
+               walk(n.Init, p, "stmt");
+               walk(&n.Cond, p, "expr");
+               walk(n.Post, p, "stmt");
+               walk(n.Body, p, "stmt");
+       case *ast.RangeStmt:
+               walk(&n.Key, p, "expr");
+               walk(&n.Value, p, "expr");
+               walk(&n.X, p, "expr");
+               walk(n.Body, p, "stmt");
+
+       case *ast.ImportSpec:
+       case *ast.ValueSpec:
+               walk(&n.Type, p, "type");
+               walk(n.Values, p, "expr");
+       case *ast.TypeSpec:
+               walk(&n.Type, p, "type");
+
+       case *ast.BadDecl:
+       case *ast.GenDecl:
+               walk(n.Specs, p, "spec");
+       case *ast.FuncDecl:
+               if n.Recv != nil {
+                       walk(n.Recv, p, "field");
+               }
+               walk(n.Type, p, "type");
+               walk(n.Body, p, "stmt");
+
+       case *ast.File:
+               walk(n.Decls, p, "decl");
+
+       case *ast.Package:
+               for _, f := range n.Files {
+                       walk(f, p, "file");
+               }
+
+       case []ast.Decl:
+               for _, d := range n {
+                       walk(d, p, context);
+               }
+       case []ast.Expr:
+               for i := range n {
+                       walk(&n[i], p, context);
+               }
+       case []*ast.Field:
+               for _, f := range n {
+                       walk(f, p, context);
+               }
+       case []ast.Stmt:
+               for _, s := range n {
+                       walk(s, p, context);
+               }
+       case []ast.Spec:
+               for _, s := range n {
+                       walk(s, p, context);
+               }
+       }
+}
+
diff --git a/src/cmd/cgo/cgo.go b/src/cmd/cgo/cgo.go
deleted file mode 100644 (file)
index f174b78..0000000
+++ /dev/null
@@ -1,308 +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 (
-       "bufio";
-       "container/vector";
-       "debug/dwarf";
-       "debug/elf";
-       "flag";
-       "fmt";
-       "go/ast";
-       "go/doc";
-       "go/parser";
-       "go/scanner";
-       "go/token";
-       "io";
-       "os";
-)
-
-// Map of uses of C.xxx.  The key is the pointer
-// to the use (a pointer so it can be rewritten)
-// and the value is the context ("call", "expr", "type").
-type cmap map[*ast.Expr] string
-
-var noPos token.Position
-
-func usage() {
-       fmt.Fprint(os.Stderr, "usage: cgo [options] file.cgo\n");
-       flag.PrintDefaults();
-}
-
-func main() {
-       flag.Usage = usage;
-       flag.Parse();
-
-       args := flag.Args();
-       if len(args) != 1 {
-               flag.Usage();
-       }
-       filename := args[0];
-
-       prog, err := parser.ParsePkgFile("", filename, parser.ParseComments);
-       if err != nil {
-               fatal(err);
-       }
-
-       // Find the import "C" line and get any extra C preamble.
-       preamble := "";
-       found := false;
-       for _, d := range prog.Decls {
-               d, ok := d.(*ast.GenDecl);
-               if !ok {
-                       continue;
-               }
-               for _, s := range d.Specs {
-                       s, ok := s.(*ast.ImportSpec);
-                       if !ok {
-                               continue;
-                       }
-                       if len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` {
-                               continue;
-                       }
-                       found = true;
-                       if s.Name != nil {
-                               error(s.Path[0].Pos(), `cannot rename import "C"`);
-                       }
-                       if s.Doc != nil {
-                               preamble += doc.CommentText(s.Doc) + "\n";
-                       }
-                       else if len(d.Specs) == 1 && d.Doc != nil {
-                               preamble += doc.CommentText(d.Doc) + "\n";
-                       }
-               }
-       }
-       if !found {
-               error(noPos, `cannot find import "C"`);
-       }
-
-       // Accumulate pointers to uses of C.x.
-       m := make(cmap);
-       walk(prog, m, "prog");
-
-       fmt.Print(preamble);
-       for p, context := range m {
-               sel := (*p).(*ast.SelectorExpr);
-               fmt.Printf("%s: %s as %s\n", sel.Pos(), sel.Sel.Value, context);
-       }
-}
-
-func walk(x interface{}, m cmap, context string) {
-       switch n := x.(type) {
-       case *ast.Expr:
-               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.Value == "C" {
-                               m[n] = context;
-                               break;
-                       }
-               }
-               walk(*n, m, context);
-
-       // everything else just recurs
-       default:
-               error(noPos, "unexpected type %T in walk", x);
-               panic();
-
-       case nil:
-
-       // These are ordered and grouped to match ../../pkg/go/ast/ast.go
-       case *ast.Field:
-               walk(&n.Type, m, "type");
-       case *ast.BadExpr:
-       case *ast.Ident:
-       case *ast.Ellipsis:
-       case *ast.BasicLit:
-       case *ast.StringList:
-       case *ast.FuncLit:
-               walk(n.Type, m, "type");
-               walk(n.Body, m, "stmt");
-       case *ast.CompositeLit:
-               walk(&n.Type, m, "type");
-               walk(n.Elts, m, "expr");
-       case *ast.ParenExpr:
-               walk(&n.X, m, context);
-       case *ast.SelectorExpr:
-               walk(&n.X, m, "selector");
-       case *ast.IndexExpr:
-               walk(&n.X, m, "expr");
-               walk(&n.Index, m, "expr");
-               if n.End != nil {
-                       walk(&n.End, m, "expr");
-               }
-       case *ast.TypeAssertExpr:
-               walk(&n.X, m, "expr");
-               walk(&n.Type, m, "type");
-       case *ast.CallExpr:
-               walk(&n.Fun, m, "call");
-               walk(n.Args, m, "expr");
-       case *ast.StarExpr:
-               walk(&n.X, m, context);
-       case *ast.UnaryExpr:
-               walk(&n.X, m, "expr");
-       case *ast.BinaryExpr:
-               walk(&n.X, m, "expr");
-               walk(&n.Y, m, "expr");
-       case *ast.KeyValueExpr:
-               walk(&n.Key, m, "expr");
-               walk(&n.Value, m, "expr");
-
-       case *ast.ArrayType:
-               walk(&n.Len, m, "expr");
-               walk(&n.Elt, m, "type");
-       case *ast.StructType:
-               walk(n.Fields, m, "field");
-       case *ast.FuncType:
-               walk(n.Params, m, "field");
-               walk(n.Results, m, "field");
-       case *ast.InterfaceType:
-               walk(n.Methods, m, "field");
-       case *ast.MapType:
-               walk(&n.Key, m, "type");
-               walk(&n.Value, m, "type");
-       case *ast.ChanType:
-               walk(&n.Value, m, "type");
-
-       case *ast.BadStmt:
-       case *ast.DeclStmt:
-               walk(n.Decl, m, "decl");
-       case *ast.EmptyStmt:
-       case *ast.LabeledStmt:
-               walk(n.Stmt, m, "stmt");
-       case *ast.ExprStmt:
-               walk(&n.X, m, "expr");
-       case *ast.IncDecStmt:
-               walk(&n.X, m, "expr");
-       case *ast.AssignStmt:
-               walk(n.Lhs, m, "expr");
-               walk(n.Rhs, m, "expr");
-       case *ast.GoStmt:
-               walk(&n.Call, m, "expr");
-       case *ast.DeferStmt:
-               walk(&n.Call, m, "expr");
-       case *ast.ReturnStmt:
-               walk(n.Results, m, "expr");
-       case *ast.BranchStmt:
-       case *ast.BlockStmt:
-               walk(n.List, m, "stmt");
-       case *ast.IfStmt:
-               walk(n.Init, m, "stmt");
-               walk(&n.Cond, m, "expr");
-               walk(n.Body, m, "stmt");
-               walk(n.Else, m, "stmt");
-       case *ast.CaseClause:
-               walk(n.Values, m, "expr");
-               walk(n.Body, m, "stmt");
-       case *ast.SwitchStmt:
-               walk(n.Init, m, "stmt");
-               walk(&n.Tag, m, "expr");
-               walk(n.Body, m, "stmt");
-       case *ast.TypeCaseClause:
-               walk(n.Types, m, "type");
-               walk(n.Body, m, "stmt");
-       case *ast.TypeSwitchStmt:
-               walk(n.Init, m, "stmt");
-               walk(n.Assign, m, "stmt");
-               walk(n.Body, m, "stmt");
-       case *ast.CommClause:
-               walk(n.Lhs, m, "expr");
-               walk(n.Rhs, m, "expr");
-               walk(n.Body, m, "stmt");
-       case *ast.SelectStmt:
-               walk(n.Body, m, "stmt");
-       case *ast.ForStmt:
-               walk(n.Init, m, "stmt");
-               walk(&n.Cond, m, "expr");
-               walk(n.Post, m, "stmt");
-               walk(n.Body, m, "stmt");
-       case *ast.RangeStmt:
-               walk(&n.Key, m, "expr");
-               walk(&n.Value, m, "expr");
-               walk(&n.X, m, "expr");
-               walk(n.Body, m, "stmt");
-
-       case *ast.ImportSpec:
-       case *ast.ValueSpec:
-               walk(&n.Type, m, "type");
-               walk(n.Values, m, "expr");
-       case *ast.TypeSpec:
-               walk(&n.Type, m, "type");
-
-       case *ast.BadDecl:
-       case *ast.GenDecl:
-               walk(n.Specs, m, "spec");
-       case *ast.FuncDecl:
-               if n.Recv != nil {
-                       walk(n.Recv, m, "field");
-               }
-               walk(n.Type, m, "type");
-               walk(n.Body, m, "stmt");
-
-       case *ast.File:
-               walk(n.Decls, m, "decl");
-
-       case *ast.Package:
-               for _, f := range n.Files {
-                       walk(f, m, "file");
-               }
-
-       case []ast.Decl:
-               for _, d := range n {
-                       walk(d, m, context);
-               }
-       case []ast.Expr:
-               for i := range n {
-                       walk(&n[i], m, context);
-               }
-       case []*ast.Field:
-               for _, f := range n {
-                       walk(f, m, context);
-               }
-       case []ast.Stmt:
-               for _, s := range n {
-                       walk(s, m, context);
-               }
-       case []ast.Spec:
-               for _, s := range n {
-                       walk(s, m, context);
-               }
-       }
-}
-
-func fatal(err os.Error) {
-       // 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.
-       if list, ok := err.(scanner.ErrorList); ok {
-               for _, e := range list {
-                       fmt.Fprintln(os.Stderr, e);
-               }
-       } else {
-               fmt.Fprintln(os.Stderr, err);
-       }
-       os.Exit(2);
-}
-
-var nerrors int
-
-func error(pos token.Position, msg string, args ...) {
-       nerrors++;
-       if pos.IsValid() {
-               fmt.Fprintf(os.Stderr, "%s: ", pos);
-       }
-       fmt.Fprintf(os.Stderr, msg, args);
-       fmt.Fprintf(os.Stderr, "\n");
-}
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
new file mode 100644 (file)
index 0000000..adf1960
--- /dev/null
@@ -0,0 +1,203 @@
+// 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 Crefs in Prog with C types by parsing gcc debug output.
+
+package main
+
+import (
+       "debug/dwarf";
+       "debug/elf";
+       "debug/macho";
+       "fmt";
+       "os";
+       "strconv";
+       "strings";
+)
+
+func (p *Prog) loadDebugInfo() {
+       // Construct a slice of unique names from p.Crefs.
+       m := make(map[string]int);
+       for _, c := range p.Crefs {
+               m[c.Name] = -1;
+       }
+       names := make([]string, 0, len(m));
+       for name, _ := range m {
+               i := len(names);
+               names = names[0:i+1];
+               names[i] = name;
+               m[name] = i;
+       }
+
+       // 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:
+       //      x.c:2: warning: useless type name in empty declaration
+       // If name is a value, gcc will print
+       //      x.c:2: warning: statement with no effect
+       // If name is undeclared, gcc will print
+       //      x.c: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.
+       var b strings.Buffer;
+       b.WriteString(p.Preamble);
+       b.WriteString("void f(void) {\n");
+       b.WriteString("#line 0 \"cgo-test\"\n");
+       for _, n := range names {
+               b.WriteString(n);
+               b.WriteString(";\n");
+       }
+       b.WriteString("}\n");
+
+       kind := make(map[string]string);
+       _, stderr := gccDebug(b.Bytes());
+       if stderr == "" {
+               fatal("gcc produced no output");
+       }
+       for _, line := range strings.Split(stderr, "\n", 0) {
+               if len(line) < 9 || line[0:9] != "cgo-test:" {
+                       continue;
+               }
+               line = line[9:len(line)];
+               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.Index(line, "warning: useless type name in empty declaration") >= 0:
+                       what = "type";
+               case strings.Index(line, "warning: statement with no effect") >= 0:
+                       what = "value";
+               case strings.Index(line, "undeclared") >= 0:
+                       what = "error";
+               }
+               if old, ok := kind[names[i]]; ok && old != what {
+                       error(noPos, "inconsistent gcc output about C.%s", names[i]);
+               }
+               kind[names[i]] = what;
+       }
+       for _, n := range names {
+               if _, ok := kind[n]; !ok {
+                       error(noPos, "could not determine kind of name for C.%s", n);
+               }
+       }
+
+       // 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.
+       b.Reset();
+       b.WriteString(p.Preamble);
+       for i, n := range names {
+               fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n, i);
+       }
+       d, stderr := gccDebug(b.Bytes());
+       if d == nil {
+               fatal("gcc failed:\n%s\non input:\n%s", stderr, b.Bytes());
+       }
+
+       // Scan DWARF info for  top-level TagVariable entries with AttrName __cgo__i.
+       types := make([]dwarf.Type, len(names));
+       r := d.Reader();
+       for {
+               e, err := r.Next();
+               if err != nil {
+                       fatal("reading DWARF entry: %s", err);
+               }
+               if e == nil {
+                       break;
+               }
+               if e.Tag != dwarf.TagVariable {
+                       goto Continue;
+               }
+               name, _ := e.Val(dwarf.AttrName).(string);
+               typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset);
+               if name == "" || typOff == 0 {
+                       fatal("malformed DWARF TagVariable entry");
+               }
+               if !strings.HasPrefix(name, "__cgo__") {
+                       goto Continue;
+               }
+               typ, err := d.Type(typOff);
+               if err != nil {
+                       fatal("loading DWARF type: %s", err);
+               }
+               t, ok := typ.(*dwarf.PtrType);
+               if !ok || t == nil {
+                       fatal("internal error: %s has non-pointer type", name);
+               }
+               i, err := strconv.Atoi(name[7:len(name)]);
+               if err != nil {
+                       fatal("malformed __cgo__ name: %s", name);
+               }
+               types[i] = t.Type;
+
+       Continue:
+               if e.Tag != dwarf.TagCompileUnit {
+                       r.SkipChildren();
+               }
+       }
+
+       // Apply types to Crefs.
+       for _, c := range p.Crefs {
+               i := m[c.Name];
+               c.TypeName = kind[c.Name] == "type";
+               c.DebugType = types[i];
+       }
+}
+
+// gccDebug runs gcc -gdwarf-2 over the C program stdin and
+// returns the corresponding DWARF data and any messages
+// printed to standard error.
+func gccDebug(stdin []byte) (*dwarf.Data, string) {
+       machine := "-m32";
+       if os.Getenv("GOARCH") == "amd64" {
+               machine = "-m64";
+       }
+
+       tmp := "_cgo_.o";
+       _, stderr, ok := run(stdin, []string{
+               "gcc",
+               machine,
+               "-Wall",        // many warnings
+               "-Werror",      // warnings are errors
+               "-o"+tmp,       // write object to tmp
+               "-gdwarf-2",    // generate DWARF v2 debugging symbols
+               "-c",   // do not link
+               "-xc",  // input language is C
+               "-",    // read input from standard input
+       });
+       if !ok {
+               return nil, string(stderr);
+       }
+
+       // Try to parse f as ELF and Mach-O and hope one works.
+       var f interface{DWARF() (*dwarf.Data, os.Error)};
+       var err os.Error;
+       if f, err = elf.Open(tmp); err != nil {
+               if f, err = macho.Open(tmp); err != nil {
+                       fatal("cannot parse gcc output %s as ELF or Mach-O object", tmp);
+               }
+       }
+
+       d, err := f.DWARF();
+       if err != nil {
+               fatal("cannot load DWARF debug information from %s: %s", tmp, err);
+       }
+       return d, "";
+}
+
index 82e4b98a15ba1b4e42a5078d58648d78fe337de5..cb1f258997d7cbe6f5146f4d6ec51bbc40864bac 100644 (file)
@@ -364,7 +364,7 @@ func (z *Int) Exp(x, y, m *Int) *Int {
        y.doinit();
        z.doinit();
        if m == nil {
-               C.mpz_pow(&z.i, &x.i, &y.i);
+               C.mpz_pow_ui(&z.i, &x.i, mpz_get_ui(&y.i));
        } else {
                C.mpz_powm(&z.i, &x.i, &y.i, &m.i);
        }
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
new file mode 100644 (file)
index 0000000..e336d03
--- /dev/null
@@ -0,0 +1,46 @@
+// 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 (
+       "flag";
+       "fmt";
+       "os";
+       "tabwriter";
+)
+
+// Cgo; see gmp.go for an overview.
+
+// TODO(rsc):
+//     Emit correct line number annotations.
+//     Make 6g understand the annotations.
+
+func usage() {
+       fmt.Fprint(os.Stderr, "usage: cgo file.cgo\n");
+       flag.PrintDefaults();
+}
+
+func main() {
+       flag.Usage = usage;
+       flag.Parse();
+
+       args := flag.Args();
+       if len(args) != 1 {
+               usage();
+               os.Exit(2);
+       }
+       p := openProg(args[0]);
+       p.loadDebugInfo();
+
+       tw := tabwriter.NewWriter(os.Stdout, 1, 1, ' ', 0);
+       for _, cref := range p.Crefs {
+               what := "value";
+               if cref.TypeName {
+                       what = "type";
+               }
+               fmt.Fprintf(tw, "%s:\t%s %s\tC %s\t%s\n", (*cref.Expr).Pos(), cref.Context, cref.Name, what, cref.DebugType);
+       }
+       tw.Flush();
+}
diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go
new file mode 100644 (file)
index 0000000..3f2fd01
--- /dev/null
@@ -0,0 +1,96 @@
+// 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";
+       "exec";
+       "fmt";
+       "go/token";
+       "io";
+       "os";
+)
+
+// A ByteReaderAt implements io.ReadAt using a slice of bytes.
+type ByteReaderAt []byte
+
+func (r ByteReaderAt) ReadAt(p []byte, off int64) (n int, err os.Error) {
+       if off >= int64(len(r)) || off < 0 {
+               return 0, os.EOF;
+       }
+       return bytes.Copy(p, r[off:len(r)]), nil;
+}
+
+// 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 {
+               fatal("exec %s: %s", argv[0], err);
+       }
+       r0, w0, err := os.Pipe();
+       if err != nil {
+               fatal("%s", err);
+       }
+       r1, w1, err := os.Pipe();
+       if err != nil {
+               fatal("%s", err);
+       }
+       r2, w2, err := os.Pipe();
+       if err != nil {
+               fatal("%s", err);
+       }
+       pid, err := os.ForkExec(cmd, argv, os.Environ(), "", []*os.File{r0, w1, w2});
+       if err != nil {
+               fatal("%s", err);
+       }
+       r0.Close();
+       w1.Close();
+       w2.Close();
+       c := make(chan bool);
+       go func() {
+               w0.Write(stdin);
+               w0.Close();
+               c <- true;
+       }();
+       var xstdout []byte;     // TODO(rsc): delete after 6g can take address of out parameter
+       go func() {
+               xstdout, _ = io.ReadAll(r1);
+               r1.Close();
+               c <- true;
+       }();
+       stderr, _ = io.ReadAll(r2);
+       r2.Close();
+       <-c;
+       <-c;
+       stdout = xstdout;
+
+       w, err := os.Wait(pid, 0);
+       if err != nil {
+               fatal("%s", err);
+       }
+       ok = w.Exited() && w.ExitStatus() == 0;
+       return;
+}
+
+// Die with an error message.
+func fatal(msg string, args ...) {
+       fmt.Fprintf(os.Stderr, msg+"\n", args);
+       os.Exit(2);
+}
+
+var nerrors int
+var noPos token.Position
+
+func error(pos token.Position, msg string, args ...) {
+       nerrors++;
+       if pos.IsValid() {
+               fmt.Fprintf(os.Stderr, "%s: ", pos);
+       }
+       fmt.Fprintf(os.Stderr, msg, args);
+       fmt.Fprintf(os.Stderr, "\n");
+}
+