Source file src/cmd/cgo/ast.go
1
2
3
4
5
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, 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
25
26
27
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
43
44
45
46
47 func (f *File) ParseGo(name string, src []byte) {
48
49
50
51 if aname, err := filepath.Abs(name); err == nil {
52 name = aname
53 }
54
55
56
57
58
59
60
61
62
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
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
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
136
137
138 f.Edit.Replace(f.offset(s.Path.Pos()), f.offset(s.Path.End()), `_ "unsafe"`)
139 }
140 }
141 }
142 }
143
144
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
152
153
154
155
156
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
165
166 func commentText(g *ast.CommentGroup) string {
167 var pieces []string
168 for _, com := range g.List {
169 c := com.Text
170
171
172 switch c[1] {
173 case '/':
174
175 c = c[2:] + "\n"
176 case '*':
177
178 c = c[2 : len(c)-2]
179 }
180 pieces = append(pieces, c)
181 }
182 return strings.Join(pieces, "")
183 }
184
185 func (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
194 func (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
207 func (f *File) saveRef(n *ast.Expr, context astContext) {
208 sel := (*n).(*ast.SelectorExpr)
209
210
211
212
213
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
251 func (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
264 func (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
304 func (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
318 type astContext int
319
320 const (
321 ctxProg astContext = iota
322 ctxEmbedType
323 ctxType
324 ctxStmt
325 ctxExpr
326 ctxField
327 ctxParam
328 ctxAssign2
329 ctxSwitch
330 ctxTypeSwitch
331 ctxFile
332 ctxDecl
333 ctxSpec
334 ctxDefer
335 ctxCall
336 ctxCall2
337 ctxSelector
338 )
339
340
341 func (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
348 default:
349 error_(token.NoPos, "unexpected type %T in walk", x)
350 panic("unexpected type")
351
352 case nil:
353
354
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
View as plain text