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 || string(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 			}
     91 		}
     92 	}
     93 	if !sawC {
     94 		error_(token.NoPos, `cannot find import "C"`)
     95 	}
     96 
     97 	// In ast2, strip the import "C" line.
     98 	w := 0
     99 	for _, decl := range ast2.Decls {
    100 		d, ok := decl.(*ast.GenDecl)
    101 		if !ok {
    102 			ast2.Decls[w] = decl
    103 			w++
    104 			continue
    105 		}
    106 		ws := 0
    107 		for _, spec := range d.Specs {
    108 			s, ok := spec.(*ast.ImportSpec)
    109 			if !ok || string(s.Path.Value) != `"C"` {
    110 				d.Specs[ws] = spec
    111 				ws++
    112 			}
    113 		}
    114 		if ws == 0 {
    115 			continue
    116 		}
    117 		d.Specs = d.Specs[0:ws]
    118 		ast2.Decls[w] = d
    119 		w++
    120 	}
    121 	ast2.Decls = ast2.Decls[0:w]
    122 
    123 	// Accumulate pointers to uses of C.x.
    124 	if f.Ref == nil {
    125 		f.Ref = make([]*Ref, 0, 8)
    126 	}
    127 	f.walk(ast2, "prog", (*File).saveRef)
    128 
    129 	// Accumulate exported functions.
    130 	// The comments are only on ast1 but we need to
    131 	// save the function bodies from ast2.
    132 	// The first walk fills in ExpFunc, and the
    133 	// second walk changes the entries to
    134 	// refer to ast2 instead.
    135 	f.walk(ast1, "prog", (*File).saveExport)
    136 	f.walk(ast2, "prog", (*File).saveExport2)
    137 
    138 	f.Comments = ast1.Comments
    139 	f.AST = ast2
    140 }
    141 
    142 // Like ast.CommentGroup's Text method but preserves
    143 // leading blank lines, so that line numbers line up.
    144 func commentText(g *ast.CommentGroup) string {
    145 	if g == nil {
    146 		return ""
    147 	}
    148 	var pieces []string
    149 	for _, com := range g.List {
    150 		c := string(com.Text)
    151 		// Remove comment markers.
    152 		// The parser has given us exactly the comment text.
    153 		switch c[1] {
    154 		case '/':
    155 			//-style comment (no newline at the end)
    156 			c = c[2:] + "\n"
    157 		case '*':
    158 			/*-style comment */
    159 			c = c[2 : len(c)-2]
    160 		}
    161 		pieces = append(pieces, c)
    162 	}
    163 	return strings.Join(pieces, "")
    164 }
    165 
    166 // Save references to C.xxx for later processing.
    167 func (f *File) saveRef(x interface{}, context string) {
    168 	n, ok := x.(*ast.Expr)
    169 	if !ok {
    170 		return
    171 	}
    172 	if sel, ok := (*n).(*ast.SelectorExpr); ok {
    173 		// For now, assume that the only instance of capital C is
    174 		// when used as the imported package identifier.
    175 		// The parser should take care of scoping in the future,
    176 		// so that we will be able to distinguish a "top-level C"
    177 		// from a local C.
    178 		if l, ok := sel.X.(*ast.Ident); ok && l.Name == "C" {
    179 			if context == "as2" {
    180 				context = "expr"
    181 			}
    182 			if context == "embed-type" {
    183 				error_(sel.Pos(), "cannot embed C type")
    184 			}
    185 			goname := sel.Sel.Name
    186 			if goname == "errno" {
    187 				error_(sel.Pos(), "cannot refer to errno directly; see documentation")
    188 				return
    189 			}
    190 			if goname == "_CMalloc" {
    191 				error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
    192 				return
    193 			}
    194 			if goname == "malloc" {
    195 				goname = "_CMalloc"
    196 			}
    197 			name := f.Name[goname]
    198 			if name == nil {
    199 				name = &Name{
    200 					Go: goname,
    201 				}
    202 				f.Name[goname] = name
    203 			}
    204 			f.Ref = append(f.Ref, &Ref{
    205 				Name:    name,
    206 				Expr:    n,
    207 				Context: context,
    208 			})
    209 			return
    210 		}
    211 	}
    212 }
    213 
    214 // If a function should be exported add it to ExpFunc.
    215 func (f *File) saveExport(x interface{}, context string) {
    216 	n, ok := x.(*ast.FuncDecl)
    217 	if !ok {
    218 		return
    219 	}
    220 
    221 	if n.Doc == nil {
    222 		return
    223 	}
    224 	for _, c := range n.Doc.List {
    225 		if !strings.HasPrefix(string(c.Text), "//export ") {
    226 			continue
    227 		}
    228 
    229 		name := strings.TrimSpace(string(c.Text[9:]))
    230 		if name == "" {
    231 			error_(c.Pos(), "export missing name")
    232 		}
    233 
    234 		if name != n.Name.Name {
    235 			error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
    236 		}
    237 
    238 		doc := ""
    239 		for _, c1 := range n.Doc.List {
    240 			if c1 != c {
    241 				doc += c1.Text + "\n"
    242 			}
    243 		}
    244 
    245 		f.ExpFunc = append(f.ExpFunc, &ExpFunc{
    246 			Func:    n,
    247 			ExpName: name,
    248 			Doc:     doc,
    249 		})
    250 		break
    251 	}
    252 }
    253 
    254 // Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
    255 func (f *File) saveExport2(x interface{}, context string) {
    256 	n, ok := x.(*ast.FuncDecl)
    257 	if !ok {
    258 		return
    259 	}
    260 
    261 	for _, exp := range f.ExpFunc {
    262 		if exp.Func.Name.Name == n.Name.Name {
    263 			exp.Func = n
    264 			break
    265 		}
    266 	}
    267 }
    268 
    269 // walk walks the AST x, calling visit(f, x, context) for each node.
    270 func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) {
    271 	visit(f, x, context)
    272 	switch n := x.(type) {
    273 	case *ast.Expr:
    274 		f.walk(*n, context, visit)
    275 
    276 	// everything else just recurs
    277 	default:
    278 		error_(token.NoPos, "unexpected type %T in walk", x, visit)
    279 		panic("unexpected type")
    280 
    281 	case nil:
    282 
    283 	// These are ordered and grouped to match ../../go/ast/ast.go
    284 	case *ast.Field:
    285 		if len(n.Names) == 0 && context == "field" {
    286 			f.walk(&n.Type, "embed-type", visit)
    287 		} else {
    288 			f.walk(&n.Type, "type", visit)
    289 		}
    290 	case *ast.FieldList:
    291 		for _, field := range n.List {
    292 			f.walk(field, context, visit)
    293 		}
    294 	case *ast.BadExpr:
    295 	case *ast.Ident:
    296 	case *ast.Ellipsis:
    297 	case *ast.BasicLit:
    298 	case *ast.FuncLit:
    299 		f.walk(n.Type, "type", visit)
    300 		f.walk(n.Body, "stmt", visit)
    301 	case *ast.CompositeLit:
    302 		f.walk(&n.Type, "type", visit)
    303 		f.walk(n.Elts, "expr", visit)
    304 	case *ast.ParenExpr:
    305 		f.walk(&n.X, context, visit)
    306 	case *ast.SelectorExpr:
    307 		f.walk(&n.X, "selector", visit)
    308 	case *ast.IndexExpr:
    309 		f.walk(&n.X, "expr", visit)
    310 		f.walk(&n.Index, "expr", visit)
    311 	case *ast.SliceExpr:
    312 		f.walk(&n.X, "expr", visit)
    313 		if n.Low != nil {
    314 			f.walk(&n.Low, "expr", visit)
    315 		}
    316 		if n.High != nil {
    317 			f.walk(&n.High, "expr", visit)
    318 		}
    319 		if n.Max != nil {
    320 			f.walk(&n.Max, "expr", visit)
    321 		}
    322 	case *ast.TypeAssertExpr:
    323 		f.walk(&n.X, "expr", visit)
    324 		f.walk(&n.Type, "type", visit)
    325 	case *ast.CallExpr:
    326 		if context == "as2" {
    327 			f.walk(&n.Fun, "call2", visit)
    328 		} else {
    329 			f.walk(&n.Fun, "call", visit)
    330 		}
    331 		f.walk(n.Args, "expr", visit)
    332 	case *ast.StarExpr:
    333 		f.walk(&n.X, context, visit)
    334 	case *ast.UnaryExpr:
    335 		f.walk(&n.X, "expr", visit)
    336 	case *ast.BinaryExpr:
    337 		f.walk(&n.X, "expr", visit)
    338 		f.walk(&n.Y, "expr", visit)
    339 	case *ast.KeyValueExpr:
    340 		f.walk(&n.Key, "expr", visit)
    341 		f.walk(&n.Value, "expr", visit)
    342 
    343 	case *ast.ArrayType:
    344 		f.walk(&n.Len, "expr", visit)
    345 		f.walk(&n.Elt, "type", visit)
    346 	case *ast.StructType:
    347 		f.walk(n.Fields, "field", visit)
    348 	case *ast.FuncType:
    349 		f.walk(n.Params, "param", visit)
    350 		if n.Results != nil {
    351 			f.walk(n.Results, "param", visit)
    352 		}
    353 	case *ast.InterfaceType:
    354 		f.walk(n.Methods, "field", visit)
    355 	case *ast.MapType:
    356 		f.walk(&n.Key, "type", visit)
    357 		f.walk(&n.Value, "type", visit)
    358 	case *ast.ChanType:
    359 		f.walk(&n.Value, "type", visit)
    360 
    361 	case *ast.BadStmt:
    362 	case *ast.DeclStmt:
    363 		f.walk(n.Decl, "decl", visit)
    364 	case *ast.EmptyStmt:
    365 	case *ast.LabeledStmt:
    366 		f.walk(n.Stmt, "stmt", visit)
    367 	case *ast.ExprStmt:
    368 		f.walk(&n.X, "expr", visit)
    369 	case *ast.SendStmt:
    370 		f.walk(&n.Chan, "expr", visit)
    371 		f.walk(&n.Value, "expr", visit)
    372 	case *ast.IncDecStmt:
    373 		f.walk(&n.X, "expr", visit)
    374 	case *ast.AssignStmt:
    375 		f.walk(n.Lhs, "expr", visit)
    376 		if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
    377 			f.walk(n.Rhs, "as2", visit)
    378 		} else {
    379 			f.walk(n.Rhs, "expr", visit)
    380 		}
    381 	case *ast.GoStmt:
    382 		f.walk(n.Call, "expr", visit)
    383 	case *ast.DeferStmt:
    384 		f.walk(n.Call, "expr", visit)
    385 	case *ast.ReturnStmt:
    386 		f.walk(n.Results, "expr", visit)
    387 	case *ast.BranchStmt:
    388 	case *ast.BlockStmt:
    389 		f.walk(n.List, context, visit)
    390 	case *ast.IfStmt:
    391 		f.walk(n.Init, "stmt", visit)
    392 		f.walk(&n.Cond, "expr", visit)
    393 		f.walk(n.Body, "stmt", visit)
    394 		f.walk(n.Else, "stmt", visit)
    395 	case *ast.CaseClause:
    396 		if context == "typeswitch" {
    397 			context = "type"
    398 		} else {
    399 			context = "expr"
    400 		}
    401 		f.walk(n.List, context, visit)
    402 		f.walk(n.Body, "stmt", visit)
    403 	case *ast.SwitchStmt:
    404 		f.walk(n.Init, "stmt", visit)
    405 		f.walk(&n.Tag, "expr", visit)
    406 		f.walk(n.Body, "switch", visit)
    407 	case *ast.TypeSwitchStmt:
    408 		f.walk(n.Init, "stmt", visit)
    409 		f.walk(n.Assign, "stmt", visit)
    410 		f.walk(n.Body, "typeswitch", visit)
    411 	case *ast.CommClause:
    412 		f.walk(n.Comm, "stmt", visit)
    413 		f.walk(n.Body, "stmt", visit)
    414 	case *ast.SelectStmt:
    415 		f.walk(n.Body, "stmt", visit)
    416 	case *ast.ForStmt:
    417 		f.walk(n.Init, "stmt", visit)
    418 		f.walk(&n.Cond, "expr", visit)
    419 		f.walk(n.Post, "stmt", visit)
    420 		f.walk(n.Body, "stmt", visit)
    421 	case *ast.RangeStmt:
    422 		f.walk(&n.Key, "expr", visit)
    423 		f.walk(&n.Value, "expr", visit)
    424 		f.walk(&n.X, "expr", visit)
    425 		f.walk(n.Body, "stmt", visit)
    426 
    427 	case *ast.ImportSpec:
    428 	case *ast.ValueSpec:
    429 		f.walk(&n.Type, "type", visit)
    430 		f.walk(n.Values, "expr", visit)
    431 	case *ast.TypeSpec:
    432 		f.walk(&n.Type, "type", visit)
    433 
    434 	case *ast.BadDecl:
    435 	case *ast.GenDecl:
    436 		f.walk(n.Specs, "spec", visit)
    437 	case *ast.FuncDecl:
    438 		if n.Recv != nil {
    439 			f.walk(n.Recv, "param", visit)
    440 		}
    441 		f.walk(n.Type, "type", visit)
    442 		if n.Body != nil {
    443 			f.walk(n.Body, "stmt", visit)
    444 		}
    445 
    446 	case *ast.File:
    447 		f.walk(n.Decls, "decl", visit)
    448 
    449 	case *ast.Package:
    450 		for _, file := range n.Files {
    451 			f.walk(file, "file", visit)
    452 		}
    453 
    454 	case []ast.Decl:
    455 		for _, d := range n {
    456 			f.walk(d, context, visit)
    457 		}
    458 	case []ast.Expr:
    459 		for i := range n {
    460 			f.walk(&n[i], context, visit)
    461 		}
    462 	case []ast.Stmt:
    463 		for _, s := range n {
    464 			f.walk(s, context, visit)
    465 		}
    466 	case []ast.Spec:
    467 		for _, s := range n {
    468 			f.walk(s, context, visit)
    469 		}
    470 	}
    471 }
    472