Source file
src/cmd/internal/dwarf/putvarabbrevgen_test.go
1
2
3
4
5 package dwarf
6
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "go/ast"
12 "go/format"
13 "go/parser"
14 "go/printer"
15 "go/token"
16 "os"
17 "strconv"
18 "strings"
19 "testing"
20 )
21
22 const pvagenfile = "./putvarabbrevgen.go"
23
24 var pvaDoGenerate bool
25
26 func TestMain(m *testing.M) {
27 flag.BoolVar(&pvaDoGenerate, "generate", false, "regenerates "+pvagenfile)
28 flag.Parse()
29 os.Exit(m.Run())
30
31 }
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 func TestPutVarAbbrevGenerator(t *testing.T) {
57 spvagenfile := pvagenerate(t)
58
59 if pvaDoGenerate {
60 err := os.WriteFile(pvagenfile, []byte(spvagenfile), 0660)
61 if err != nil {
62 t.Fatal(err)
63 }
64 return
65 }
66
67 slurp := func(name string) string {
68 out, err := os.ReadFile(name)
69 if err != nil {
70 t.Fatal(err)
71 }
72 return string(out)
73 }
74
75 if spvagenfile != slurp(pvagenfile) {
76 t.Error(pvagenfile + " is out of date")
77 }
78
79 }
80
81 func pvagenerate(t *testing.T) string {
82 var fset token.FileSet
83 f, err := parser.ParseFile(&fset, "./dwarf.go", nil, parser.ParseComments)
84 if err != nil {
85 t.Fatal(err)
86 }
87 cm := ast.NewCommentMap(&fset, f, f.Comments)
88 abbrevs := make(map[string]int)
89 funcs := map[string]ast.Stmt{}
90 for _, decl := range f.Decls {
91 decl, ok := decl.(*ast.FuncDecl)
92 if !ok || decl.Body == nil {
93 continue
94 }
95 if decl.Name.Name == "putvar" || decl.Name.Name == "putAbstractVar" {
96
97 pvagraph, _ := pvacfgbody(t, &fset, cm, decl.Body.List)
98 funcs[decl.Name.Name+"Abbrev"] = pvacfgvisit(pvagraph, abbrevs)
99 }
100 }
101 abbrevslice := make([]string, len(abbrevs))
102 for abbrev, n := range abbrevs {
103 abbrevslice[n] = abbrev
104 }
105
106 buf := new(bytes.Buffer)
107 fmt.Fprint(buf, `// Code generated by TestPutVarAbbrevGenerator. DO NOT EDIT.
108 // Regenerate using go test -run TestPutVarAbbrevGenerator -generate instead.
109
110 package dwarf
111
112 var putvarAbbrevs = []dwAbbrev{
113 `)
114
115 for _, abbrev := range abbrevslice {
116 fmt.Fprint(buf, abbrev+",\n")
117 }
118
119 fmt.Fprint(buf, "\n}\n\n")
120
121 fmt.Fprint(buf, "func putAbstractVarAbbrev(v *Var) int {\n")
122 format.Node(buf, &token.FileSet{}, funcs["putAbstractVarAbbrev"])
123 fmt.Fprint(buf, "}\n\n")
124
125 fmt.Fprint(buf, "func putvarAbbrev(v *Var, concrete, withLoclist bool) int {\n")
126 format.Node(buf, &token.FileSet{}, funcs["putvarAbbrev"])
127 fmt.Fprint(buf, "}\n")
128
129 out, err := format.Source(buf.Bytes())
130 if err != nil {
131 t.Log(string(buf.Bytes()))
132 t.Fatal(err)
133 }
134
135 return string(out)
136 }
137
138 type pvacfgnode struct {
139 attr, form string
140
141 cond ast.Expr
142 then, els *pvacfgnode
143 }
144
145
146
147 func pvacfgbody(t *testing.T, fset *token.FileSet, cm ast.CommentMap, body []ast.Stmt) (start, end *pvacfgnode) {
148 add := func(n *pvacfgnode) {
149 if start == nil || end == nil {
150 start = n
151 end = n
152 } else {
153 end.then = n
154 end = n
155 }
156 }
157 for _, stmt := range body {
158 switch stmt := stmt.(type) {
159 case *ast.ExprStmt:
160 if x, _ := stmt.X.(*ast.CallExpr); x != nil {
161 funstr := exprToString(x.Fun)
162 if funstr == "putattr" {
163 form, _ := x.Args[3].(*ast.Ident)
164 if form == nil {
165 t.Fatalf("%s invalid use of putattr", fset.Position(x.Pos()))
166 }
167 cmt := findLineComment(cm, stmt)
168 if cmt == nil {
169 t.Fatalf("%s invalid use of putattr (no comment containing the attribute name)", fset.Position(x.Pos()))
170 }
171 add(&pvacfgnode{attr: strings.TrimSpace(cmt.Text[2:]), form: form.Name})
172 }
173 }
174 case *ast.IfStmt:
175 ifStart, ifEnd := pvacfgif(t, fset, cm, stmt)
176 if ifStart != nil {
177 add(ifStart)
178 end = ifEnd
179 }
180 default:
181
182 ast.Inspect(stmt, func(n ast.Node) bool {
183 if call, _ := n.(*ast.CallExpr); call != nil {
184 if exprToString(call.Fun) == "putattr" {
185 t.Fatalf("%s use of putattr in unsupported block", fset.Position(call.Pos()))
186 }
187 }
188 return true
189 })
190 }
191 }
192 return start, end
193 }
194
195 func pvacfgif(t *testing.T, fset *token.FileSet, cm ast.CommentMap, ifstmt *ast.IfStmt) (start, end *pvacfgnode) {
196 thenStart, thenEnd := pvacfgbody(t, fset, cm, ifstmt.Body.List)
197 var elseStart, elseEnd *pvacfgnode
198 if ifstmt.Else != nil {
199 switch els := ifstmt.Else.(type) {
200 case *ast.IfStmt:
201 elseStart, elseEnd = pvacfgif(t, fset, cm, els)
202 case *ast.BlockStmt:
203 elseStart, elseEnd = pvacfgbody(t, fset, cm, els.List)
204 default:
205 t.Fatalf("%s: unexpected statement %T", fset.Position(els.Pos()), els)
206 }
207 }
208
209 if thenStart != nil && elseStart != nil && thenStart == thenEnd && elseStart == elseEnd && thenStart.form == elseStart.form && thenStart.attr == elseStart.attr {
210 return thenStart, thenEnd
211 }
212
213 if thenStart != nil || elseStart != nil {
214 start = &pvacfgnode{cond: ifstmt.Cond}
215 end = &pvacfgnode{}
216 if thenStart != nil {
217 start.then = thenStart
218 thenEnd.then = end
219 } else {
220 start.then = end
221 }
222 if elseStart != nil {
223 start.els = elseStart
224 elseEnd.then = end
225 } else {
226 start.els = end
227 }
228 }
229 return start, end
230 }
231
232 func exprToString(t ast.Expr) string {
233 var buf bytes.Buffer
234 printer.Fprint(&buf, token.NewFileSet(), t)
235 return buf.String()
236 }
237
238
239 func findLineComment(cm ast.CommentMap, stmt *ast.ExprStmt) *ast.Comment {
240 var r *ast.Comment
241 for _, cmtg := range cm[stmt] {
242 for _, cmt := range cmtg.List {
243 if cmt.Slash > stmt.Pos() {
244 if r != nil {
245 return nil
246 }
247 r = cmt
248 }
249 }
250 }
251 return r
252 }
253
254
255
256
257 func pvacfgvisit(pvacfg *pvacfgnode, abbrevs map[string]int) ast.Stmt {
258 r := &ast.IfStmt{Cond: &ast.BinaryExpr{
259 Op: token.EQL,
260 X: &ast.SelectorExpr{X: &ast.Ident{Name: "v"}, Sel: &ast.Ident{Name: "Tag"}},
261 Y: &ast.Ident{Name: "DW_TAG_variable"}}}
262 r.Body = &ast.BlockStmt{List: []ast.Stmt{
263 pvacfgvisitnode(pvacfg, "DW_TAG_variable", []*pvacfgnode{}, abbrevs),
264 }}
265 r.Else = &ast.BlockStmt{List: []ast.Stmt{
266 pvacfgvisitnode(pvacfg, "DW_TAG_formal_parameter", []*pvacfgnode{}, abbrevs),
267 }}
268 return r
269 }
270
271 func pvacfgvisitnode(pvacfg *pvacfgnode, tag string, path []*pvacfgnode, abbrevs map[string]int) ast.Stmt {
272 if pvacfg == nil {
273 abbrev := toabbrev(tag, path)
274 if _, ok := abbrevs[abbrev]; !ok {
275 abbrevs[abbrev] = len(abbrevs)
276 }
277 return &ast.ReturnStmt{
278 Results: []ast.Expr{&ast.BinaryExpr{
279 Op: token.ADD,
280 X: &ast.Ident{Name: "DW_ABRV_PUTVAR_START"},
281 Y: &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(abbrevs[abbrev])}}}}
282 }
283 if pvacfg.attr != "" {
284 return pvacfgvisitnode(pvacfg.then, tag, append(path, pvacfg), abbrevs)
285 } else if pvacfg.cond != nil {
286 if bx, _ := pvacfg.cond.(*ast.BinaryExpr); bx != nil && bx.Op == token.EQL && exprToString(bx.X) == "v.Tag" {
287
288 y := exprToString(bx.Y)
289 if y == tag {
290 return pvacfgvisitnode(pvacfg.then, tag, path, abbrevs)
291 } else {
292 return pvacfgvisitnode(pvacfg.els, tag, path, abbrevs)
293 }
294 } else {
295 r := &ast.IfStmt{Cond: pvacfg.cond}
296 r.Body = &ast.BlockStmt{List: []ast.Stmt{pvacfgvisitnode(pvacfg.then, tag, path, abbrevs)}}
297 r.Else = &ast.BlockStmt{List: []ast.Stmt{pvacfgvisitnode(pvacfg.els, tag, path, abbrevs)}}
298 return r
299 }
300 } else {
301 return pvacfgvisitnode(pvacfg.then, tag, path, abbrevs)
302 }
303 }
304
305 func toabbrev(tag string, path []*pvacfgnode) string {
306 buf := new(bytes.Buffer)
307 fmt.Fprintf(buf, "{\n%s,\nDW_CHILDREN_no,\n[]dwAttrForm{\n", tag)
308 for _, node := range path {
309 if node.cond == nil {
310 fmt.Fprintf(buf, "{%s, %s},\n", node.attr, node.form)
311
312 }
313 }
314 fmt.Fprint(buf, "},\n}")
315 return buf.String()
316 }
317
View as plain text