| // 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" | 
 | 	"strings" | 
 | ) | 
 |  | 
 | func parse(name string, src []byte, flags parser.Mode) *ast.File { | 
 | 	ast1, err := parser.ParseFile(fset, name, src, 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 | 
 | } | 
 |  | 
 | // ParseGo populates f with information learned from the Go source code | 
 | // which was read from the named file. 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) ParseGo(abspath string, src []byte) { | 
 | 	// 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. | 
 | 	// In cgo mode, we ignore ast2 and just apply edits directly | 
 | 	// the text behind ast1. In godefs mode we modify and print ast2. | 
 | 	ast1 := parse(abspath, src, parser.ParseComments) | 
 | 	ast2 := parse(abspath, src, 0) | 
 |  | 
 | 	f.Package = ast1.Name.Name | 
 | 	f.Name = make(map[string]*Name) | 
 | 	f.NamePos = make(map[*Name]token.Pos) | 
 |  | 
 | 	// 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), abspath) | 
 | 				f.Preamble += commentText(cg) + "\n" | 
 | 				f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n" | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	if !sawC { | 
 | 		error_(ast1.Package, `cannot find import "C"`) | 
 | 	} | 
 |  | 
 | 	// In ast2, strip the import "C" line. | 
 | 	if *godefs { | 
 | 		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] | 
 | 	} else { | 
 | 		for _, decl := range ast2.Decls { | 
 | 			d, ok := decl.(*ast.GenDecl) | 
 | 			if !ok { | 
 | 				continue | 
 | 			} | 
 | 			for _, spec := range d.Specs { | 
 | 				if s, ok := spec.(*ast.ImportSpec); ok && s.Path.Value == `"C"` { | 
 | 					// Replace "C" with _ "unsafe", to keep program valid. | 
 | 					// (Deleting import statement or clause is not safe if it is followed | 
 | 					// in the source by an explicit semicolon.) | 
 | 					f.Edit.Replace(f.offset(s.Path.Pos()), f.offset(s.Path.End()), `_ "unsafe"`) | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// Accumulate pointers to uses of C.x. | 
 | 	if f.Ref == nil { | 
 | 		f.Ref = make([]*Ref, 0, 8) | 
 | 	} | 
 | 	f.walk(ast2, ctxProg, (*File).validateIdents) | 
 | 	f.walk(ast2, ctxProg, (*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, ctxProg, (*File).saveExport) | 
 | 	f.walk(ast2, ctxProg, (*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 { | 
 | 	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, "") | 
 | } | 
 |  | 
 | func (f *File) validateIdents(x interface{}, context astContext) { | 
 | 	if x, ok := x.(*ast.Ident); ok { | 
 | 		if f.isMangledName(x.Name) { | 
 | 			error_(x.Pos(), "identifier %q may conflict with identifiers generated by cgo", x.Name) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // Save various references we are going to need later. | 
 | func (f *File) saveExprs(x interface{}, context astContext) { | 
 | 	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 astContext) { | 
 | 	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 == ctxAssign2 { | 
 | 		context = ctxExpr | 
 | 	} | 
 | 	if context == ctxEmbedType { | 
 | 		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.NamePos[name] = sel.Pos() | 
 | 	} | 
 | 	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 astContext) { | 
 | 	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 == ctxDefer} | 
 | 	f.Calls = append(f.Calls, c) | 
 | } | 
 |  | 
 | // If a function should be exported add it to ExpFunc. | 
 | func (f *File) saveExport(x interface{}, context astContext) { | 
 | 	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 astContext) { | 
 | 	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 | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | type astContext int | 
 |  | 
 | const ( | 
 | 	ctxProg astContext = iota | 
 | 	ctxEmbedType | 
 | 	ctxType | 
 | 	ctxStmt | 
 | 	ctxExpr | 
 | 	ctxField | 
 | 	ctxParam | 
 | 	ctxAssign2 // assignment of a single expression to two variables | 
 | 	ctxSwitch | 
 | 	ctxTypeSwitch | 
 | 	ctxFile | 
 | 	ctxDecl | 
 | 	ctxSpec | 
 | 	ctxDefer | 
 | 	ctxCall  // any function call other than ctxCall2 | 
 | 	ctxCall2 // function call whose result is assigned to two variables | 
 | 	ctxSelector | 
 | ) | 
 |  | 
 | // walk walks the AST x, calling visit(f, x, context) for each node. | 
 | func (f *File) walk(x interface{}, context astContext, visit func(*File, interface{}, astContext)) { | 
 | 	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 == ctxField { | 
 | 			f.walk(&n.Type, ctxEmbedType, visit) | 
 | 		} else { | 
 | 			f.walk(&n.Type, ctxType, visit) | 
 | 		} | 
 | 	case *ast.FieldList: | 
 | 		for _, field := range n.List { | 
 | 			f.walk(field, context, visit) | 
 | 		} | 
 | 	case *ast.BadExpr: | 
 | 	case *ast.Ident: | 
 | 	case *ast.Ellipsis: | 
 | 		f.walk(&n.Elt, ctxType, visit) | 
 | 	case *ast.BasicLit: | 
 | 	case *ast.FuncLit: | 
 | 		f.walk(n.Type, ctxType, visit) | 
 | 		f.walk(n.Body, ctxStmt, visit) | 
 | 	case *ast.CompositeLit: | 
 | 		f.walk(&n.Type, ctxType, visit) | 
 | 		f.walk(n.Elts, ctxExpr, visit) | 
 | 	case *ast.ParenExpr: | 
 | 		f.walk(&n.X, context, visit) | 
 | 	case *ast.SelectorExpr: | 
 | 		f.walk(&n.X, ctxSelector, visit) | 
 | 	case *ast.IndexExpr: | 
 | 		f.walk(&n.X, ctxExpr, visit) | 
 | 		f.walk(&n.Index, ctxExpr, visit) | 
 | 	case *ast.SliceExpr: | 
 | 		f.walk(&n.X, ctxExpr, visit) | 
 | 		if n.Low != nil { | 
 | 			f.walk(&n.Low, ctxExpr, visit) | 
 | 		} | 
 | 		if n.High != nil { | 
 | 			f.walk(&n.High, ctxExpr, visit) | 
 | 		} | 
 | 		if n.Max != nil { | 
 | 			f.walk(&n.Max, ctxExpr, visit) | 
 | 		} | 
 | 	case *ast.TypeAssertExpr: | 
 | 		f.walk(&n.X, ctxExpr, visit) | 
 | 		f.walk(&n.Type, ctxType, visit) | 
 | 	case *ast.CallExpr: | 
 | 		if context == ctxAssign2 { | 
 | 			f.walk(&n.Fun, ctxCall2, visit) | 
 | 		} else { | 
 | 			f.walk(&n.Fun, ctxCall, visit) | 
 | 		} | 
 | 		f.walk(n.Args, ctxExpr, visit) | 
 | 	case *ast.StarExpr: | 
 | 		f.walk(&n.X, context, visit) | 
 | 	case *ast.UnaryExpr: | 
 | 		f.walk(&n.X, ctxExpr, visit) | 
 | 	case *ast.BinaryExpr: | 
 | 		f.walk(&n.X, ctxExpr, visit) | 
 | 		f.walk(&n.Y, ctxExpr, visit) | 
 | 	case *ast.KeyValueExpr: | 
 | 		f.walk(&n.Key, ctxExpr, visit) | 
 | 		f.walk(&n.Value, ctxExpr, visit) | 
 |  | 
 | 	case *ast.ArrayType: | 
 | 		f.walk(&n.Len, ctxExpr, visit) | 
 | 		f.walk(&n.Elt, ctxType, visit) | 
 | 	case *ast.StructType: | 
 | 		f.walk(n.Fields, ctxField, visit) | 
 | 	case *ast.FuncType: | 
 | 		f.walk(n.Params, ctxParam, visit) | 
 | 		if n.Results != nil { | 
 | 			f.walk(n.Results, ctxParam, visit) | 
 | 		} | 
 | 	case *ast.InterfaceType: | 
 | 		f.walk(n.Methods, ctxField, visit) | 
 | 	case *ast.MapType: | 
 | 		f.walk(&n.Key, ctxType, visit) | 
 | 		f.walk(&n.Value, ctxType, visit) | 
 | 	case *ast.ChanType: | 
 | 		f.walk(&n.Value, ctxType, visit) | 
 |  | 
 | 	case *ast.BadStmt: | 
 | 	case *ast.DeclStmt: | 
 | 		f.walk(n.Decl, ctxDecl, visit) | 
 | 	case *ast.EmptyStmt: | 
 | 	case *ast.LabeledStmt: | 
 | 		f.walk(n.Stmt, ctxStmt, visit) | 
 | 	case *ast.ExprStmt: | 
 | 		f.walk(&n.X, ctxExpr, visit) | 
 | 	case *ast.SendStmt: | 
 | 		f.walk(&n.Chan, ctxExpr, visit) | 
 | 		f.walk(&n.Value, ctxExpr, visit) | 
 | 	case *ast.IncDecStmt: | 
 | 		f.walk(&n.X, ctxExpr, visit) | 
 | 	case *ast.AssignStmt: | 
 | 		f.walk(n.Lhs, ctxExpr, visit) | 
 | 		if len(n.Lhs) == 2 && len(n.Rhs) == 1 { | 
 | 			f.walk(n.Rhs, ctxAssign2, visit) | 
 | 		} else { | 
 | 			f.walk(n.Rhs, ctxExpr, visit) | 
 | 		} | 
 | 	case *ast.GoStmt: | 
 | 		f.walk(n.Call, ctxExpr, visit) | 
 | 	case *ast.DeferStmt: | 
 | 		f.walk(n.Call, ctxDefer, visit) | 
 | 	case *ast.ReturnStmt: | 
 | 		f.walk(n.Results, ctxExpr, visit) | 
 | 	case *ast.BranchStmt: | 
 | 	case *ast.BlockStmt: | 
 | 		f.walk(n.List, context, visit) | 
 | 	case *ast.IfStmt: | 
 | 		f.walk(n.Init, ctxStmt, visit) | 
 | 		f.walk(&n.Cond, ctxExpr, visit) | 
 | 		f.walk(n.Body, ctxStmt, visit) | 
 | 		f.walk(n.Else, ctxStmt, visit) | 
 | 	case *ast.CaseClause: | 
 | 		if context == ctxTypeSwitch { | 
 | 			context = ctxType | 
 | 		} else { | 
 | 			context = ctxExpr | 
 | 		} | 
 | 		f.walk(n.List, context, visit) | 
 | 		f.walk(n.Body, ctxStmt, visit) | 
 | 	case *ast.SwitchStmt: | 
 | 		f.walk(n.Init, ctxStmt, visit) | 
 | 		f.walk(&n.Tag, ctxExpr, visit) | 
 | 		f.walk(n.Body, ctxSwitch, visit) | 
 | 	case *ast.TypeSwitchStmt: | 
 | 		f.walk(n.Init, ctxStmt, visit) | 
 | 		f.walk(n.Assign, ctxStmt, visit) | 
 | 		f.walk(n.Body, ctxTypeSwitch, visit) | 
 | 	case *ast.CommClause: | 
 | 		f.walk(n.Comm, ctxStmt, visit) | 
 | 		f.walk(n.Body, ctxStmt, visit) | 
 | 	case *ast.SelectStmt: | 
 | 		f.walk(n.Body, ctxStmt, visit) | 
 | 	case *ast.ForStmt: | 
 | 		f.walk(n.Init, ctxStmt, visit) | 
 | 		f.walk(&n.Cond, ctxExpr, visit) | 
 | 		f.walk(n.Post, ctxStmt, visit) | 
 | 		f.walk(n.Body, ctxStmt, visit) | 
 | 	case *ast.RangeStmt: | 
 | 		f.walk(&n.Key, ctxExpr, visit) | 
 | 		f.walk(&n.Value, ctxExpr, visit) | 
 | 		f.walk(&n.X, ctxExpr, visit) | 
 | 		f.walk(n.Body, ctxStmt, visit) | 
 |  | 
 | 	case *ast.ImportSpec: | 
 | 	case *ast.ValueSpec: | 
 | 		f.walk(&n.Type, ctxType, visit) | 
 | 		if len(n.Names) == 2 && len(n.Values) == 1 { | 
 | 			f.walk(&n.Values[0], ctxAssign2, visit) | 
 | 		} else { | 
 | 			f.walk(n.Values, ctxExpr, visit) | 
 | 		} | 
 | 	case *ast.TypeSpec: | 
 | 		f.walk(&n.Type, ctxType, visit) | 
 |  | 
 | 	case *ast.BadDecl: | 
 | 	case *ast.GenDecl: | 
 | 		f.walk(n.Specs, ctxSpec, visit) | 
 | 	case *ast.FuncDecl: | 
 | 		if n.Recv != nil { | 
 | 			f.walk(n.Recv, ctxParam, visit) | 
 | 		} | 
 | 		f.walk(n.Type, ctxType, visit) | 
 | 		if n.Body != nil { | 
 | 			f.walk(n.Body, ctxStmt, visit) | 
 | 		} | 
 |  | 
 | 	case *ast.File: | 
 | 		f.walk(n.Decls, ctxDecl, visit) | 
 |  | 
 | 	case *ast.Package: | 
 | 		for _, file := range n.Files { | 
 | 			f.walk(file, ctxFile, 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) | 
 | 		} | 
 | 	} | 
 | } |