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 7package main 8 9import ( 10 "fmt" 11 "go/ast" 12 "go/parser" 13 "go/scanner" 14 "go/token" 15 "os" 16 "path/filepath" 17 "strings" 18) 19 20func parse(name string, src []byte, flags parser.Mode) *ast.File { 21 ast1, err := parser.ParseFile(fset, name, src, 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 38func sourceLine(n ast.Node) int { 39 return fset.Position(n.Pos()).Line 40} 41 42// ParseGo populates f with information learned from the Go source code 43// which was read from the named file. 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. 47func (f *File) ParseGo(name string, src []byte) { 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 // In cgo mode, we ignore ast2 and just apply edits directly 62 // the text behind ast1. In godefs mode we modify and print ast2. 63 ast1 := parse(name, src, parser.ParseComments) 64 ast2 := parse(name, src, 0) 65 66 f.Package = ast1.Name.Name 67 f.Name = make(map[string]*Name) 68 f.NamePos = make(map[*Name]token.Pos) 69 70 // In ast1, find the import "C" line and get any extra C preamble. 71 sawC := false 72 for _, decl := range ast1.Decls { 73 d, ok := decl.(*ast.GenDecl) 74 if !ok { 75 continue 76 } 77 for _, spec := range d.Specs { 78 s, ok := spec.(*ast.ImportSpec) 79 if !ok || s.Path.Value != `"C"` { 80 continue 81 } 82 sawC = true 83 if s.Name != nil { 84 error_(s.Path.Pos(), `cannot rename import "C"`) 85 } 86 cg := s.Doc 87 if cg == nil && len(d.Specs) == 1 { 88 cg = d.Doc 89 } 90 if cg != nil { 91 f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) 92 f.Preamble += commentText(cg) + "\n" 93 f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n" 94 } 95 } 96 } 97 if !sawC { 98 error_(ast1.Package, `cannot find import "C"`) 99 } 100 101 // In ast2, strip the import "C" line. 102 if *godefs { 103 w := 0 104 for _, decl := range ast2.Decls { 105 d, ok := decl.(*ast.GenDecl) 106 if !ok { 107 ast2.Decls[w] = decl 108 w++ 109 continue 110 } 111 ws := 0 112 for _, spec := range d.Specs { 113 s, ok := spec.(*ast.ImportSpec) 114 if !ok || s.Path.Value != `"C"` { 115 d.Specs[ws] = spec 116 ws++ 117 } 118 } 119 if ws == 0 { 120 continue 121 } 122 d.Specs = d.Specs[0:ws] 123 ast2.Decls[w] = d 124 w++ 125 } 126 ast2.Decls = ast2.Decls[0:w] 127 } else { 128 for _, decl := range ast2.Decls { 129 d, ok := decl.(*ast.GenDecl) 130 if !ok { 131 continue 132 } 133 for _, spec := range d.Specs { 134 if s, ok := spec.(*ast.ImportSpec); ok && s.Path.Value == `"C"` { 135 // Replace "C" with _ "unsafe", to keep program valid. 136 // (Deleting import statement or clause is not safe if it is followed 137 // in the source by an explicit semicolon.) 138 f.Edit.Replace(f.offset(s.Path.Pos()), f.offset(s.Path.End()), `_ "unsafe"`) 139 } 140 } 141 } 142 } 143 144 // Accumulate pointers to uses of C.x. 145 if f.Ref == nil { 146 f.Ref = make([]*Ref, 0, 8) 147 } 148 f.walk(ast2, ctxProg, (*File).validateIdents) 149 f.walk(ast2, ctxProg, (*File).saveExprs) 150 151 // Accumulate exported functions. 152 // The comments are only on ast1 but we need to 153 // save the function bodies from ast2. 154 // The first walk fills in ExpFunc, and the 155 // second walk changes the entries to 156 // refer to ast2 instead. 157 f.walk(ast1, ctxProg, (*File).saveExport) 158 f.walk(ast2, ctxProg, (*File).saveExport2) 159 160 f.Comments = ast1.Comments 161 f.AST = ast2 162} 163 164// Like ast.CommentGroup's Text method but preserves 165// leading blank lines, so that line numbers line up. 166func commentText(g *ast.CommentGroup) string { 167 var pieces []string 168 for _, com := range g.List { 169 c := com.Text 170 // Remove comment markers. 171 // The parser has given us exactly the comment text. 172 switch c[1] { 173 case '/': 174 //-style comment (no newline at the end) 175 c = c[2:] + "\n" 176 case '*': 177 /*-style comment */ 178 c = c[2 : len(c)-2] 179 } 180 pieces = append(pieces, c) 181 } 182 return strings.Join(pieces, "") 183} 184 185func (f *File) validateIdents(x interface{}, context astContext) { 186 if x, ok := x.(*ast.Ident); ok { 187 if f.isMangledName(x.Name) { 188 error_(x.Pos(), "identifier %q may conflict with identifiers generated by cgo", x.Name) 189 } 190 } 191} 192 193// Save various references we are going to need later. 194func (f *File) saveExprs(x interface{}, context astContext) { 195 switch x := x.(type) { 196 case *ast.Expr: 197 switch (*x).(type) { 198 case *ast.SelectorExpr: 199 f.saveRef(x, context) 200 } 201 case *ast.CallExpr: 202 f.saveCall(x, context) 203 } 204} 205 206// Save references to C.xxx for later processing. 207func (f *File) saveRef(n *ast.Expr, context astContext) { 208 sel := (*n).(*ast.SelectorExpr) 209 // For now, assume that the only instance of capital C is when 210 // used as the imported package identifier. 211 // The parser should take care of scoping in the future, so 212 // that we will be able to distinguish a "top-level C" from a 213 // local C. 214 if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" { 215 return 216 } 217 if context == ctxAssign2 { 218 context = ctxExpr 219 } 220 if context == ctxEmbedType { 221 error_(sel.Pos(), "cannot embed C type") 222 } 223 goname := sel.Sel.Name 224 if goname == "errno" { 225 error_(sel.Pos(), "cannot refer to errno directly; see documentation") 226 return 227 } 228 if goname == "_CMalloc" { 229 error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc") 230 return 231 } 232 if goname == "malloc" { 233 goname = "_CMalloc" 234 } 235 name := f.Name[goname] 236 if name == nil { 237 name = &Name{ 238 Go: goname, 239 } 240 f.Name[goname] = name 241 f.NamePos[name] = sel.Pos() 242 } 243 f.Ref = append(f.Ref, &Ref{ 244 Name: name, 245 Expr: n, 246 Context: context, 247 }) 248} 249 250// Save calls to C.xxx for later processing. 251func (f *File) saveCall(call *ast.CallExpr, context astContext) { 252 sel, ok := call.Fun.(*ast.SelectorExpr) 253 if !ok { 254 return 255 } 256 if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" { 257 return 258 } 259 c := &Call{Call: call, Deferred: context == ctxDefer} 260 f.Calls = append(f.Calls, c) 261} 262 263// If a function should be exported add it to ExpFunc. 264func (f *File) saveExport(x interface{}, context astContext) { 265 n, ok := x.(*ast.FuncDecl) 266 if !ok { 267 return 268 } 269 270 if n.Doc == nil { 271 return 272 } 273 for _, c := range n.Doc.List { 274 if !strings.HasPrefix(c.Text, "//export ") { 275 continue 276 } 277 278 name := strings.TrimSpace(c.Text[9:]) 279 if name == "" { 280 error_(c.Pos(), "export missing name") 281 } 282 283 if name != n.Name.Name { 284 error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name) 285 } 286 287 doc := "" 288 for _, c1 := range n.Doc.List { 289 if c1 != c { 290 doc += c1.Text + "\n" 291 } 292 } 293 294 f.ExpFunc = append(f.ExpFunc, &ExpFunc{ 295 Func: n, 296 ExpName: name, 297 Doc: doc, 298 }) 299 break 300 } 301} 302 303// Make f.ExpFunc[i] point at the Func from this AST instead of the other one. 304func (f *File) saveExport2(x interface{}, context astContext) { 305 n, ok := x.(*ast.FuncDecl) 306 if !ok { 307 return 308 } 309 310 for _, exp := range f.ExpFunc { 311 if exp.Func.Name.Name == n.Name.Name { 312 exp.Func = n 313 break 314 } 315 } 316} 317 318type astContext int 319 320const ( 321 ctxProg astContext = iota 322 ctxEmbedType 323 ctxType 324 ctxStmt 325 ctxExpr 326 ctxField 327 ctxParam 328 ctxAssign2 // assignment of a single expression to two variables 329 ctxSwitch 330 ctxTypeSwitch 331 ctxFile 332 ctxDecl 333 ctxSpec 334 ctxDefer 335 ctxCall // any function call other than ctxCall2 336 ctxCall2 // function call whose result is assigned to two variables 337 ctxSelector 338) 339 340// walk walks the AST x, calling visit(f, x, context) for each node. 341func (f *File) walk(x interface{}, context astContext, visit func(*File, interface{}, astContext)) { 342 visit(f, x, context) 343 switch n := x.(type) { 344 case *ast.Expr: 345 f.walk(*n, context, visit) 346 347 // everything else just recurs 348 default: 349 error_(token.NoPos, "unexpected type %T in walk", x) 350 panic("unexpected type") 351 352 case nil: 353 354 // These are ordered and grouped to match ../../go/ast/ast.go 355 case *ast.Field: 356 if len(n.Names) == 0 && context == ctxField { 357 f.walk(&n.Type, ctxEmbedType, visit) 358 } else { 359 f.walk(&n.Type, ctxType, visit) 360 } 361 case *ast.FieldList: 362 for _, field := range n.List { 363 f.walk(field, context, visit) 364 } 365 case *ast.BadExpr: 366 case *ast.Ident: 367 case *ast.Ellipsis: 368 f.walk(&n.Elt, ctxType, visit) 369 case *ast.BasicLit: 370 case *ast.FuncLit: 371 f.walk(n.Type, ctxType, visit) 372 f.walk(n.Body, ctxStmt, visit) 373 case *ast.CompositeLit: 374 f.walk(&n.Type, ctxType, visit) 375 f.walk(n.Elts, ctxExpr, visit) 376 case *ast.ParenExpr: 377 f.walk(&n.X, context, visit) 378 case *ast.SelectorExpr: 379 f.walk(&n.X, ctxSelector, visit) 380 case *ast.IndexExpr: 381 f.walk(&n.X, ctxExpr, visit) 382 f.walk(&n.Index, ctxExpr, visit) 383 case *ast.SliceExpr: 384 f.walk(&n.X, ctxExpr, visit) 385 if n.Low != nil { 386 f.walk(&n.Low, ctxExpr, visit) 387 } 388 if n.High != nil { 389 f.walk(&n.High, ctxExpr, visit) 390 } 391 if n.Max != nil { 392 f.walk(&n.Max, ctxExpr, visit) 393 } 394 case *ast.TypeAssertExpr: 395 f.walk(&n.X, ctxExpr, visit) 396 f.walk(&n.Type, ctxType, visit) 397 case *ast.CallExpr: 398 if context == ctxAssign2 { 399 f.walk(&n.Fun, ctxCall2, visit) 400 } else { 401 f.walk(&n.Fun, ctxCall, visit) 402 } 403 f.walk(n.Args, ctxExpr, visit) 404 case *ast.StarExpr: 405 f.walk(&n.X, context, visit) 406 case *ast.UnaryExpr: 407 f.walk(&n.X, ctxExpr, visit) 408 case *ast.BinaryExpr: 409 f.walk(&n.X, ctxExpr, visit) 410 f.walk(&n.Y, ctxExpr, visit) 411 case *ast.KeyValueExpr: 412 f.walk(&n.Key, ctxExpr, visit) 413 f.walk(&n.Value, ctxExpr, visit) 414 415 case *ast.ArrayType: 416 f.walk(&n.Len, ctxExpr, visit) 417 f.walk(&n.Elt, ctxType, visit) 418 case *ast.StructType: 419 f.walk(n.Fields, ctxField, visit) 420 case *ast.FuncType: 421 f.walk(n.Params, ctxParam, visit) 422 if n.Results != nil { 423 f.walk(n.Results, ctxParam, visit) 424 } 425 case *ast.InterfaceType: 426 f.walk(n.Methods, ctxField, visit) 427 case *ast.MapType: 428 f.walk(&n.Key, ctxType, visit) 429 f.walk(&n.Value, ctxType, visit) 430 case *ast.ChanType: 431 f.walk(&n.Value, ctxType, visit) 432 433 case *ast.BadStmt: 434 case *ast.DeclStmt: 435 f.walk(n.Decl, ctxDecl, visit) 436 case *ast.EmptyStmt: 437 case *ast.LabeledStmt: 438 f.walk(n.Stmt, ctxStmt, visit) 439 case *ast.ExprStmt: 440 f.walk(&n.X, ctxExpr, visit) 441 case *ast.SendStmt: 442 f.walk(&n.Chan, ctxExpr, visit) 443 f.walk(&n.Value, ctxExpr, visit) 444 case *ast.IncDecStmt: 445 f.walk(&n.X, ctxExpr, visit) 446 case *ast.AssignStmt: 447 f.walk(n.Lhs, ctxExpr, visit) 448 if len(n.Lhs) == 2 && len(n.Rhs) == 1 { 449 f.walk(n.Rhs, ctxAssign2, visit) 450 } else { 451 f.walk(n.Rhs, ctxExpr, visit) 452 } 453 case *ast.GoStmt: 454 f.walk(n.Call, ctxExpr, visit) 455 case *ast.DeferStmt: 456 f.walk(n.Call, ctxDefer, visit) 457 case *ast.ReturnStmt: 458 f.walk(n.Results, ctxExpr, visit) 459 case *ast.BranchStmt: 460 case *ast.BlockStmt: 461 f.walk(n.List, context, visit) 462 case *ast.IfStmt: 463 f.walk(n.Init, ctxStmt, visit) 464 f.walk(&n.Cond, ctxExpr, visit) 465 f.walk(n.Body, ctxStmt, visit) 466 f.walk(n.Else, ctxStmt, visit) 467 case *ast.CaseClause: 468 if context == ctxTypeSwitch { 469 context = ctxType 470 } else { 471 context = ctxExpr 472 } 473 f.walk(n.List, context, visit) 474 f.walk(n.Body, ctxStmt, visit) 475 case *ast.SwitchStmt: 476 f.walk(n.Init, ctxStmt, visit) 477 f.walk(&n.Tag, ctxExpr, visit) 478 f.walk(n.Body, ctxSwitch, visit) 479 case *ast.TypeSwitchStmt: 480 f.walk(n.Init, ctxStmt, visit) 481 f.walk(n.Assign, ctxStmt, visit) 482 f.walk(n.Body, ctxTypeSwitch, visit) 483 case *ast.CommClause: 484 f.walk(n.Comm, ctxStmt, visit) 485 f.walk(n.Body, ctxStmt, visit) 486 case *ast.SelectStmt: 487 f.walk(n.Body, ctxStmt, visit) 488 case *ast.ForStmt: 489 f.walk(n.Init, ctxStmt, visit) 490 f.walk(&n.Cond, ctxExpr, visit) 491 f.walk(n.Post, ctxStmt, visit) 492 f.walk(n.Body, ctxStmt, visit) 493 case *ast.RangeStmt: 494 f.walk(&n.Key, ctxExpr, visit) 495 f.walk(&n.Value, ctxExpr, visit) 496 f.walk(&n.X, ctxExpr, visit) 497 f.walk(n.Body, ctxStmt, visit) 498 499 case *ast.ImportSpec: 500 case *ast.ValueSpec: 501 f.walk(&n.Type, ctxType, visit) 502 if len(n.Names) == 2 && len(n.Values) == 1 { 503 f.walk(&n.Values[0], ctxAssign2, visit) 504 } else { 505 f.walk(n.Values, ctxExpr, visit) 506 } 507 case *ast.TypeSpec: 508 f.walk(&n.Type, ctxType, visit) 509 510 case *ast.BadDecl: 511 case *ast.GenDecl: 512 f.walk(n.Specs, ctxSpec, visit) 513 case *ast.FuncDecl: 514 if n.Recv != nil { 515 f.walk(n.Recv, ctxParam, visit) 516 } 517 f.walk(n.Type, ctxType, visit) 518 if n.Body != nil { 519 f.walk(n.Body, ctxStmt, visit) 520 } 521 522 case *ast.File: 523 f.walk(n.Decls, ctxDecl, visit) 524 525 case *ast.Package: 526 for _, file := range n.Files { 527 f.walk(file, ctxFile, visit) 528 } 529 530 case []ast.Decl: 531 for _, d := range n { 532 f.walk(d, context, visit) 533 } 534 case []ast.Expr: 535 for i := range n { 536 f.walk(&n[i], context, visit) 537 } 538 case []ast.Stmt: 539 for _, s := range n { 540 f.walk(s, context, visit) 541 } 542 case []ast.Spec: 543 for _, s := range n { 544 f.walk(s, context, visit) 545 } 546 } 547} 548