Home | History | Annotate | Download | only in cgo
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Parse input AST and prepare Prog structure.
      6 
      7 package main
      8 
      9 import (
     10 	"fmt"
     11 	"go/ast"
     12 	"go/parser"
     13 	"go/scanner"
     14 	"go/token"
     15 	"os"
     16 	"path/filepath"
     17 	"strings"
     18 )
     19 
     20 func parse(name string, flags parser.Mode) *ast.File {
     21 	ast1, err := parser.ParseFile(fset, name, nil, flags)
     22 	if err != nil {
     23 		if list, ok := err.(scanner.ErrorList); ok {
     24 			// If err is a scanner.ErrorList, its String will print just
     25 			// the first error and then (+n more errors).
     26 			// Instead, turn it into a new Error that will return
     27 			// details for all the errors.
     28 			for _, e := range list {
     29 				fmt.Fprintln(os.Stderr, e)
     30 			}
     31 			os.Exit(2)
     32 		}
     33 		fatalf("parsing %s: %s", name, err)
     34 	}
     35 	return ast1
     36 }
     37 
     38 func sourceLine(n ast.Node) int {
     39 	return fset.Position(n.Pos()).Line
     40 }
     41 
     42 // ReadGo populates f with information learned from reading the
     43 // Go source file with the given file name. It gathers the C preamble
     44 // attached to the import "C" comment, a list of references to C.xxx,
     45 // a list of exported functions, and the actual AST, to be rewritten and
     46 // printed.
     47 func (f *File) ReadGo(name string) {
     48 	// Create absolute path for file, so that it will be used in error
     49 	// messages and recorded in debug line number information.
     50 	// This matches the rest of the toolchain. See golang.org/issue/5122.
     51 	if aname, err := filepath.Abs(name); err == nil {
     52 		name = aname
     53 	}
     54 
     55 	// Two different parses: once with comments, once without.
     56 	// The printer is not good enough at printing comments in the
     57 	// right place when we start editing the AST behind its back,
     58 	// so we use ast1 to look for the doc comments on import "C"
     59 	// and on exported functions, and we use ast2 for translating
     60 	// and reprinting.
     61 	ast1 := parse(name, parser.ParseComments)
     62 	ast2 := parse(name, 0)
     63 
     64 	f.Package = ast1.Name.Name
     65 	f.Name = make(map[string]*Name)
     66 
     67 	// In ast1, find the import "C" line and get any extra C preamble.
     68 	sawC := false
     69 	for _, decl := range ast1.Decls {
     70 		d, ok := decl.(*ast.GenDecl)
     71 		if !ok {
     72 			continue
     73 		}
     74 		for _, spec := range d.Specs {
     75 			s, ok := spec.(*ast.ImportSpec)
     76 			if !ok || s.Path.Value != `"C"` {
     77 				continue
     78 			}
     79 			sawC = true
     80 			if s.Name != nil {
     81 				error_(s.Path.Pos(), `cannot rename import "C"`)
     82 			}
     83 			cg := s.Doc
     84 			if cg == nil && len(d.Specs) == 1 {
     85 				cg = d.Doc
     86 			}
     87 			if cg != nil {
     88 				f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name)
     89 				f.Preamble += commentText(cg) + "\n"
     90 				f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
     91 			}
     92 		}
     93 	}
     94 	if !sawC {
     95 		error_(token.NoPos, `cannot find import "C"`)
     96 	}
     97 
     98 	// In ast2, strip the import "C" line.
     99 	w := 0
    100 	for _, decl := range ast2.Decls {
    101 		d, ok := decl.(*ast.GenDecl)
    102 		if !ok {
    103 			ast2.Decls[w] = decl
    104 			w++
    105 			continue
    106 		}
    107 		ws := 0
    108 		for _, spec := range d.Specs {
    109 			s, ok := spec.(*ast.ImportSpec)
    110 			if !ok || s.Path.Value != `"C"` {
    111 				d.Specs[ws] = spec
    112 				ws++
    113 			}
    114 		}
    115 		if ws == 0 {
    116 			continue
    117 		}
    118 		d.Specs = d.Specs[0:ws]
    119 		ast2.Decls[w] = d
    120 		w++
    121 	}
    122 	ast2.Decls = ast2.Decls[0:w]
    123 
    124 	// Accumulate pointers to uses of C.x.
    125 	if f.Ref == nil {
    126 		f.Ref = make([]*Ref, 0, 8)
    127 	}
    128 	f.walk(ast2, "prog", (*File).saveExprs)
    129 
    130 	// Accumulate exported functions.
    131 	// The comments are only on ast1 but we need to
    132 	// save the function bodies from ast2.
    133 	// The first walk fills in ExpFunc, and the
    134 	// second walk changes the entries to
    135 	// refer to ast2 instead.
    136 	f.walk(ast1, "prog", (*File).saveExport)
    137 	f.walk(ast2, "prog", (*File).saveExport2)
    138 
    139 	f.Comments = ast1.Comments
    140 	f.AST = ast2
    141 }
    142 
    143 // Like ast.CommentGroup's Text method but preserves
    144 // leading blank lines, so that line numbers line up.
    145 func commentText(g *ast.CommentGroup) string {
    146 	if g == nil {
    147 		return ""
    148 	}
    149 	var pieces []string
    150 	for _, com := range g.List {
    151 		c := com.Text
    152 		// Remove comment markers.
    153 		// The parser has given us exactly the comment text.
    154 		switch c[1] {
    155 		case '/':
    156 			//-style comment (no newline at the end)
    157 			c = c[2:] + "\n"
    158 		case '*':
    159 			/*-style comment */
    160 			c = c[2 : len(c)-2]
    161 		}
    162 		pieces = append(pieces, c)
    163 	}
    164 	return strings.Join(pieces, "")
    165 }
    166 
    167 // Save various references we are going to need later.
    168 func (f *File) saveExprs(x interface{}, context string) {
    169 	switch x := x.(type) {
    170 	case *ast.Expr:
    171 		switch (*x).(type) {
    172 		case *ast.SelectorExpr:
    173 			f.saveRef(x, context)
    174 		}
    175 	case *ast.CallExpr:
    176 		f.saveCall(x, context)
    177 	}
    178 }
    179 
    180 // Save references to C.xxx for later processing.
    181 func (f *File) saveRef(n *ast.Expr, context string) {
    182 	sel := (*n).(*ast.SelectorExpr)
    183 	// For now, assume that the only instance of capital C is when
    184 	// used as the imported package identifier.
    185 	// The parser should take care of scoping in the future, so
    186 	// that we will be able to distinguish a "top-level C" from a
    187 	// local C.
    188 	if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
    189 		return
    190 	}
    191 	if context == "as2" {
    192 		context = "expr"
    193 	}
    194 	if context == "embed-type" {
    195 		error_(sel.Pos(), "cannot embed C type")
    196 	}
    197 	goname := sel.Sel.Name
    198 	if goname == "errno" {
    199 		error_(sel.Pos(), "cannot refer to errno directly; see documentation")
    200 		return
    201 	}
    202 	if goname == "_CMalloc" {
    203 		error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
    204 		return
    205 	}
    206 	if goname == "malloc" {
    207 		goname = "_CMalloc"
    208 	}
    209 	name := f.Name[goname]
    210 	if name == nil {
    211 		name = &Name{
    212 			Go: goname,
    213 		}
    214 		f.Name[goname] = name
    215 	}
    216 	f.Ref = append(f.Ref, &Ref{
    217 		Name:    name,
    218 		Expr:    n,
    219 		Context: context,
    220 	})
    221 }
    222 
    223 // Save calls to C.xxx for later processing.
    224 func (f *File) saveCall(call *ast.CallExpr, context string) {
    225 	sel, ok := call.Fun.(*ast.SelectorExpr)
    226 	if !ok {
    227 		return
    228 	}
    229 	if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
    230 		return
    231 	}
    232 	c := &Call{Call: call, Deferred: context == "defer"}
    233 	f.Calls = append(f.Calls, c)
    234 }
    235 
    236 // If a function should be exported add it to ExpFunc.
    237 func (f *File) saveExport(x interface{}, context string) {
    238 	n, ok := x.(*ast.FuncDecl)
    239 	if !ok {
    240 		return
    241 	}
    242 
    243 	if n.Doc == nil {
    244 		return
    245 	}
    246 	for _, c := range n.Doc.List {
    247 		if !strings.HasPrefix(c.Text, "//export ") {
    248 			continue
    249 		}
    250 
    251 		name := strings.TrimSpace(c.Text[9:])
    252 		if name == "" {
    253 			error_(c.Pos(), "export missing name")
    254 		}
    255 
    256 		if name != n.Name.Name {
    257 			error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
    258 		}
    259 
    260 		doc := ""
    261 		for _, c1 := range n.Doc.List {
    262 			if c1 != c {
    263 				doc += c1.Text + "\n"
    264 			}
    265 		}
    266 
    267 		f.ExpFunc = append(f.ExpFunc, &ExpFunc{
    268 			Func:    n,
    269 			ExpName: name,
    270 			Doc:     doc,
    271 		})
    272 		break
    273 	}
    274 }
    275 
    276 // Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
    277 func (f *File) saveExport2(x interface{}, context string) {
    278 	n, ok := x.(*ast.FuncDecl)
    279 	if !ok {
    280 		return
    281 	}
    282 
    283 	for _, exp := range f.ExpFunc {
    284 		if exp.Func.Name.Name == n.Name.Name {
    285 			exp.Func = n
    286 			break
    287 		}
    288 	}
    289 }
    290 
    291 // walk walks the AST x, calling visit(f, x, context) for each node.
    292 func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) {
    293 	visit(f, x, context)
    294 	switch n := x.(type) {
    295 	case *ast.Expr:
    296 		f.walk(*n, context, visit)
    297 
    298 	// everything else just recurs
    299 	default:
    300 		error_(token.NoPos, "unexpected type %T in walk", x)
    301 		panic("unexpected type")
    302 
    303 	case nil:
    304 
    305 	// These are ordered and grouped to match ../../go/ast/ast.go
    306 	case *ast.Field:
    307 		if len(n.Names) == 0 && context == "field" {
    308 			f.walk(&n.Type, "embed-type", visit)
    309 		} else {
    310 			f.walk(&n.Type, "type", visit)
    311 		}
    312 	case *ast.FieldList:
    313 		for _, field := range n.List {
    314 			f.walk(field, context, visit)
    315 		}
    316 	case *ast.BadExpr:
    317 	case *ast.Ident:
    318 	case *ast.Ellipsis:
    319 	case *ast.BasicLit:
    320 	case *ast.FuncLit:
    321 		f.walk(n.Type, "type", visit)
    322 		f.walk(n.Body, "stmt", visit)
    323 	case *ast.CompositeLit:
    324 		f.walk(&n.Type, "type", visit)
    325 		f.walk(n.Elts, "expr", visit)
    326 	case *ast.ParenExpr:
    327 		f.walk(&n.X, context, visit)
    328 	case *ast.SelectorExpr:
    329 		f.walk(&n.X, "selector", visit)
    330 	case *ast.IndexExpr:
    331 		f.walk(&n.X, "expr", visit)
    332 		f.walk(&n.Index, "expr", visit)
    333 	case *ast.SliceExpr:
    334 		f.walk(&n.X, "expr", visit)
    335 		if n.Low != nil {
    336 			f.walk(&n.Low, "expr", visit)
    337 		}
    338 		if n.High != nil {
    339 			f.walk(&n.High, "expr", visit)
    340 		}
    341 		if n.Max != nil {
    342 			f.walk(&n.Max, "expr", visit)
    343 		}
    344 	case *ast.TypeAssertExpr:
    345 		f.walk(&n.X, "expr", visit)
    346 		f.walk(&n.Type, "type", visit)
    347 	case *ast.CallExpr:
    348 		if context == "as2" {
    349 			f.walk(&n.Fun, "call2", visit)
    350 		} else {
    351 			f.walk(&n.Fun, "call", visit)
    352 		}
    353 		f.walk(n.Args, "expr", visit)
    354 	case *ast.StarExpr:
    355 		f.walk(&n.X, context, visit)
    356 	case *ast.UnaryExpr:
    357 		f.walk(&n.X, "expr", visit)
    358 	case *ast.BinaryExpr:
    359 		f.walk(&n.X, "expr", visit)
    360 		f.walk(&n.Y, "expr", visit)
    361 	case *ast.KeyValueExpr:
    362 		f.walk(&n.Key, "expr", visit)
    363 		f.walk(&n.Value, "expr", visit)
    364 
    365 	case *ast.ArrayType:
    366 		f.walk(&n.Len, "expr", visit)
    367 		f.walk(&n.Elt, "type", visit)
    368 	case *ast.StructType:
    369 		f.walk(n.Fields, "field", visit)
    370 	case *ast.FuncType:
    371 		f.walk(n.Params, "param", visit)
    372 		if n.Results != nil {
    373 			f.walk(n.Results, "param", visit)
    374 		}
    375 	case *ast.InterfaceType:
    376 		f.walk(n.Methods, "field", visit)
    377 	case *ast.MapType:
    378 		f.walk(&n.Key, "type", visit)
    379 		f.walk(&n.Value, "type", visit)
    380 	case *ast.ChanType:
    381 		f.walk(&n.Value, "type", visit)
    382 
    383 	case *ast.BadStmt:
    384 	case *ast.DeclStmt:
    385 		f.walk(n.Decl, "decl", visit)
    386 	case *ast.EmptyStmt:
    387 	case *ast.LabeledStmt:
    388 		f.walk(n.Stmt, "stmt", visit)
    389 	case *ast.ExprStmt:
    390 		f.walk(&n.X, "expr", visit)
    391 	case *ast.SendStmt:
    392 		f.walk(&n.Chan, "expr", visit)
    393 		f.walk(&n.Value, "expr", visit)
    394 	case *ast.IncDecStmt:
    395 		f.walk(&n.X, "expr", visit)
    396 	case *ast.AssignStmt:
    397 		f.walk(n.Lhs, "expr", visit)
    398 		if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
    399 			f.walk(n.Rhs, "as2", visit)
    400 		} else {
    401 			f.walk(n.Rhs, "expr", visit)
    402 		}
    403 	case *ast.GoStmt:
    404 		f.walk(n.Call, "expr", visit)
    405 	case *ast.DeferStmt:
    406 		f.walk(n.Call, "defer", visit)
    407 	case *ast.ReturnStmt:
    408 		f.walk(n.Results, "expr", visit)
    409 	case *ast.BranchStmt:
    410 	case *ast.BlockStmt:
    411 		f.walk(n.List, context, visit)
    412 	case *ast.IfStmt:
    413 		f.walk(n.Init, "stmt", visit)
    414 		f.walk(&n.Cond, "expr", visit)
    415 		f.walk(n.Body, "stmt", visit)
    416 		f.walk(n.Else, "stmt", visit)
    417 	case *ast.CaseClause:
    418 		if context == "typeswitch" {
    419 			context = "type"
    420 		} else {
    421 			context = "expr"
    422 		}
    423 		f.walk(n.List, context, visit)
    424 		f.walk(n.Body, "stmt", visit)
    425 	case *ast.SwitchStmt:
    426 		f.walk(n.Init, "stmt", visit)
    427 		f.walk(&n.Tag, "expr", visit)
    428 		f.walk(n.Body, "switch", visit)
    429 	case *ast.TypeSwitchStmt:
    430 		f.walk(n.Init, "stmt", visit)
    431 		f.walk(n.Assign, "stmt", visit)
    432 		f.walk(n.Body, "typeswitch", visit)
    433 	case *ast.CommClause:
    434 		f.walk(n.Comm, "stmt", visit)
    435 		f.walk(n.Body, "stmt", visit)
    436 	case *ast.SelectStmt:
    437 		f.walk(n.Body, "stmt", visit)
    438 	case *ast.ForStmt:
    439 		f.walk(n.Init, "stmt", visit)
    440 		f.walk(&n.Cond, "expr", visit)
    441 		f.walk(n.Post, "stmt", visit)
    442 		f.walk(n.Body, "stmt", visit)
    443 	case *ast.RangeStmt:
    444 		f.walk(&n.Key, "expr", visit)
    445 		f.walk(&n.Value, "expr", visit)
    446 		f.walk(&n.X, "expr", visit)
    447 		f.walk(n.Body, "stmt", visit)
    448 
    449 	case *ast.ImportSpec:
    450 	case *ast.ValueSpec:
    451 		f.walk(&n.Type, "type", visit)
    452 		if len(n.Names) == 2 && len(n.Values) == 1 {
    453 			f.walk(&n.Values[0], "as2", visit)
    454 		} else {
    455 			f.walk(n.Values, "expr", visit)
    456 		}
    457 	case *ast.TypeSpec:
    458 		f.walk(&n.Type, "type", visit)
    459 
    460 	case *ast.BadDecl:
    461 	case *ast.GenDecl:
    462 		f.walk(n.Specs, "spec", visit)
    463 	case *ast.FuncDecl:
    464 		if n.Recv != nil {
    465 			f.walk(n.Recv, "param", visit)
    466 		}
    467 		f.walk(n.Type, "type", visit)
    468 		if n.Body != nil {
    469 			f.walk(n.Body, "stmt", visit)
    470 		}
    471 
    472 	case *ast.File:
    473 		f.walk(n.Decls, "decl", visit)
    474 
    475 	case *ast.Package:
    476 		for _, file := range n.Files {
    477 			f.walk(file, "file", visit)
    478 		}
    479 
    480 	case []ast.Decl:
    481 		for _, d := range n {
    482 			f.walk(d, context, visit)
    483 		}
    484 	case []ast.Expr:
    485 		for i := range n {
    486 			f.walk(&n[i], context, visit)
    487 		}
    488 	case []ast.Stmt:
    489 		for _, s := range n {
    490 			f.walk(s, context, visit)
    491 		}
    492 	case []ast.Spec:
    493 		for _, s := range n {
    494 			f.walk(s, context, visit)
    495 		}
    496 	}
    497 }
    498