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