| // 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" |
| "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 |
| Type *Type // the type of xxx |
| FuncType *FuncType |
| } |
| |
| // 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") |
| PackagePath string |
| Package string |
| Crefs []*Cref |
| Typedef map[string]ast.Expr |
| Vardef map[string]*Type |
| Funcdef map[string]*FuncType |
| Enumdef map[string]int64 |
| PtrSize int64 |
| GccOptions []string |
| OutDefs map[string]bool |
| } |
| |
| // A Type collects information about a type in both the C and Go worlds. |
| type Type struct { |
| Size int64 |
| Align int64 |
| C string |
| 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 openProg(name string, p *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) |
| } |
| p.Package = p.AST.Name.Value |
| |
| // Find the import "C" line and get any extra C preamble. |
| // Delete the import "C" line along the way. |
| sawC := false |
| w := 0 |
| for _, decl := range p.AST.Decls { |
| d, ok := decl.(*ast.GenDecl) |
| if !ok { |
| p.AST.Decls[w] = decl |
| w++ |
| continue |
| } |
| ws := 0 |
| for _, spec := range d.Specs { |
| s, ok := spec.(*ast.ImportSpec) |
| if !ok || len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` { |
| d.Specs[ws] = spec |
| ws++ |
| continue |
| } |
| sawC = 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 ws == 0 { |
| continue |
| } |
| d.Specs = d.Specs[0:ws] |
| p.AST.Decls[w] = d |
| w++ |
| } |
| p.AST.Decls = p.AST.Decls[0:w] |
| |
| if !sawC { |
| error(noPos, `cannot find import "C"`) |
| } |
| |
| // Accumulate pointers to uses of C.x. |
| p.Crefs = make([]*Cref, 0, 8) |
| walk(p.AST, p, "prog") |
| } |
| |
| 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") |
| case *ast.SliceExpr: |
| 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") |
| if n.Body != nil { |
| 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) |
| } |
| } |
| } |