|  | // 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/parser" | 
|  | "go/scanner" | 
|  | "go/token" | 
|  | "os" | 
|  | "path/filepath" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | func parse(name string, flags parser.Mode) *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) { | 
|  | // Create absolute path for file, so that it will be used in error | 
|  | // messages and recorded in debug line number information. | 
|  | // This matches the rest of the toolchain. See golang.org/issue/5122. | 
|  | if aname, err := filepath.Abs(name); err == nil { | 
|  | name = aname | 
|  | } | 
|  |  | 
|  | // 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 || 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 += commentText(cg) + "\n" | 
|  | f.Preamble += "#line 1 \"cgo-generated-wrapper\"\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 || 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).saveExprs) | 
|  |  | 
|  | // 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.Comments = ast1.Comments | 
|  | f.AST = ast2 | 
|  | } | 
|  |  | 
|  | // Like ast.CommentGroup's Text method but preserves | 
|  | // leading blank lines, so that line numbers line up. | 
|  | func commentText(g *ast.CommentGroup) string { | 
|  | if g == nil { | 
|  | return "" | 
|  | } | 
|  | var pieces []string | 
|  | for _, com := range g.List { | 
|  | c := com.Text | 
|  | // Remove comment markers. | 
|  | // The parser has given us exactly the comment text. | 
|  | switch c[1] { | 
|  | case '/': | 
|  | //-style comment (no newline at the end) | 
|  | c = c[2:] + "\n" | 
|  | case '*': | 
|  | /*-style comment */ | 
|  | c = c[2 : len(c)-2] | 
|  | } | 
|  | pieces = append(pieces, c) | 
|  | } | 
|  | return strings.Join(pieces, "") | 
|  | } | 
|  |  | 
|  | // Save various references we are going to need later. | 
|  | func (f *File) saveExprs(x interface{}, context string) { | 
|  | switch x := x.(type) { | 
|  | case *ast.Expr: | 
|  | switch (*x).(type) { | 
|  | case *ast.SelectorExpr: | 
|  | f.saveRef(x, context) | 
|  | } | 
|  | case *ast.CallExpr: | 
|  | f.saveCall(x, context) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Save references to C.xxx for later processing. | 
|  | func (f *File) saveRef(n *ast.Expr, context string) { | 
|  | sel := (*n).(*ast.SelectorExpr) | 
|  | // 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" { | 
|  | return | 
|  | } | 
|  | if context == "as2" { | 
|  | context = "expr" | 
|  | } | 
|  | if context == "embed-type" { | 
|  | error_(sel.Pos(), "cannot embed C type") | 
|  | } | 
|  | goname := sel.Sel.Name | 
|  | if goname == "errno" { | 
|  | error_(sel.Pos(), "cannot refer to errno directly; see documentation") | 
|  | return | 
|  | } | 
|  | if goname == "_CMalloc" { | 
|  | error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc") | 
|  | return | 
|  | } | 
|  | if goname == "malloc" { | 
|  | goname = "_CMalloc" | 
|  | } | 
|  | 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, | 
|  | }) | 
|  | } | 
|  |  | 
|  | // Save calls to C.xxx for later processing. | 
|  | func (f *File) saveCall(call *ast.CallExpr, context string) { | 
|  | sel, ok := call.Fun.(*ast.SelectorExpr) | 
|  | if !ok { | 
|  | return | 
|  | } | 
|  | if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" { | 
|  | return | 
|  | } | 
|  | c := &Call{Call: call, Deferred: context == "defer"} | 
|  | f.Calls = append(f.Calls, c) | 
|  | } | 
|  |  | 
|  | // 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(c.Text, "//export ") { | 
|  | continue | 
|  | } | 
|  |  | 
|  | name := strings.TrimSpace(c.Text[9:]) | 
|  | if name == "" { | 
|  | error_(c.Pos(), "export missing name") | 
|  | } | 
|  |  | 
|  | if name != n.Name.Name { | 
|  | error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name) | 
|  | } | 
|  |  | 
|  | doc := "" | 
|  | for _, c1 := range n.Doc.List { | 
|  | if c1 != c { | 
|  | doc += c1.Text + "\n" | 
|  | } | 
|  | } | 
|  |  | 
|  | f.ExpFunc = append(f.ExpFunc, &ExpFunc{ | 
|  | Func:    n, | 
|  | ExpName: name, | 
|  | Doc:     doc, | 
|  | }) | 
|  | 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) | 
|  | panic("unexpected type") | 
|  |  | 
|  | case nil: | 
|  |  | 
|  | // These are ordered and grouped to match ../../go/ast/ast.go | 
|  | case *ast.Field: | 
|  | if len(n.Names) == 0 && context == "field" { | 
|  | f.walk(&n.Type, "embed-type", visit) | 
|  | } else { | 
|  | 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) | 
|  | } | 
|  | if n.Max != nil { | 
|  | f.walk(&n.Max, "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, "param", visit) | 
|  | if n.Results != nil { | 
|  | f.walk(n.Results, "param", 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, "defer", 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) | 
|  | if len(n.Names) == 2 && len(n.Values) == 1 { | 
|  | f.walk(&n.Values[0], "as2", visit) | 
|  | } else { | 
|  | 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, "param", 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) | 
|  | } | 
|  | } | 
|  | } |