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