1
2
3
4
5 package ssa
6
7
8
9
10 import (
11 "bytes"
12 "fmt"
13 "go/types"
14 "io"
15 "reflect"
16 "sort"
17 "strings"
18
19 "golang.org/x/tools/go/types/typeutil"
20 "golang.org/x/tools/internal/typeparams"
21 )
22
23
24
25
26
27
28 func relName(v Value, i Instruction) string {
29 var from *types.Package
30 if i != nil {
31 from = i.Parent().relPkg()
32 }
33 switch v := v.(type) {
34 case Member:
35 return v.RelString(from)
36 case *Const:
37 return v.RelString(from)
38 }
39 return v.Name()
40 }
41
42
43
44 var normalizeAnyForTesting bool
45
46 func relType(t types.Type, from *types.Package) string {
47 s := types.TypeString(t, types.RelativeTo(from))
48 if normalizeAnyForTesting {
49 s = strings.ReplaceAll(s, "interface{}", "any")
50 }
51 return s
52 }
53
54 func relTerm(term *types.Term, from *types.Package) string {
55 s := relType(term.Type(), from)
56 if term.Tilde() {
57 return "~" + s
58 }
59 return s
60 }
61
62 func relString(m Member, from *types.Package) string {
63
64
65 if pkg := m.Package().Pkg; pkg != nil && pkg != from {
66 return fmt.Sprintf("%s.%s", pkg.Path(), m.Name())
67 }
68 return m.Name()
69 }
70
71
72
73
74
75
76 func (v *Parameter) String() string {
77 from := v.Parent().relPkg()
78 return fmt.Sprintf("parameter %s : %s", v.Name(), relType(v.Type(), from))
79 }
80
81 func (v *FreeVar) String() string {
82 from := v.Parent().relPkg()
83 return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from))
84 }
85
86 func (v *Builtin) String() string {
87 return fmt.Sprintf("builtin %s", v.Name())
88 }
89
90
91
92 func (v *Alloc) String() string {
93 op := "local"
94 if v.Heap {
95 op = "new"
96 }
97 from := v.Parent().relPkg()
98 return fmt.Sprintf("%s %s (%s)", op, relType(typeparams.MustDeref(v.Type()), from), v.Comment)
99 }
100
101 func (v *Phi) String() string {
102 var b bytes.Buffer
103 b.WriteString("phi [")
104 for i, edge := range v.Edges {
105 if i > 0 {
106 b.WriteString(", ")
107 }
108
109 if v.block == nil {
110 b.WriteString("??")
111 continue
112 }
113 block := -1
114 if i < len(v.block.Preds) {
115 block = v.block.Preds[i].Index
116 }
117 fmt.Fprintf(&b, "%d: ", block)
118 edgeVal := "<nil>"
119 if edge != nil {
120 edgeVal = relName(edge, v)
121 }
122 b.WriteString(edgeVal)
123 }
124 b.WriteString("]")
125 if v.Comment != "" {
126 b.WriteString(" #")
127 b.WriteString(v.Comment)
128 }
129 return b.String()
130 }
131
132 func printCall(v *CallCommon, prefix string, instr Instruction) string {
133 var b bytes.Buffer
134 b.WriteString(prefix)
135 if !v.IsInvoke() {
136 b.WriteString(relName(v.Value, instr))
137 } else {
138 fmt.Fprintf(&b, "invoke %s.%s", relName(v.Value, instr), v.Method.Name())
139 }
140 b.WriteString("(")
141 for i, arg := range v.Args {
142 if i > 0 {
143 b.WriteString(", ")
144 }
145 b.WriteString(relName(arg, instr))
146 }
147 if v.Signature().Variadic() {
148 b.WriteString("...")
149 }
150 b.WriteString(")")
151 return b.String()
152 }
153
154 func (c *CallCommon) String() string {
155 return printCall(c, "", nil)
156 }
157
158 func (v *Call) String() string {
159 return printCall(&v.Call, "", v)
160 }
161
162 func (v *BinOp) String() string {
163 return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v))
164 }
165
166 func (v *UnOp) String() string {
167 return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk))
168 }
169
170 func printConv(prefix string, v, x Value) string {
171 from := v.Parent().relPkg()
172 return fmt.Sprintf("%s %s <- %s (%s)",
173 prefix,
174 relType(v.Type(), from),
175 relType(x.Type(), from),
176 relName(x, v.(Instruction)))
177 }
178
179 func (v *ChangeType) String() string { return printConv("changetype", v, v.X) }
180 func (v *Convert) String() string { return printConv("convert", v, v.X) }
181 func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) }
182 func (v *SliceToArrayPointer) String() string { return printConv("slice to array pointer", v, v.X) }
183 func (v *MakeInterface) String() string { return printConv("make", v, v.X) }
184
185 func (v *MultiConvert) String() string {
186 from := v.Parent().relPkg()
187
188 var b strings.Builder
189 b.WriteString(printConv("multiconvert", v, v.X))
190 b.WriteString(" [")
191 for i, s := range v.from {
192 for j, d := range v.to {
193 if i != 0 || j != 0 {
194 b.WriteString(" | ")
195 }
196 fmt.Fprintf(&b, "%s <- %s", relTerm(d, from), relTerm(s, from))
197 }
198 }
199 b.WriteString("]")
200 return b.String()
201 }
202
203 func (v *MakeClosure) String() string {
204 var b bytes.Buffer
205 fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v))
206 if v.Bindings != nil {
207 b.WriteString(" [")
208 for i, c := range v.Bindings {
209 if i > 0 {
210 b.WriteString(", ")
211 }
212 b.WriteString(relName(c, v))
213 }
214 b.WriteString("]")
215 }
216 return b.String()
217 }
218
219 func (v *MakeSlice) String() string {
220 from := v.Parent().relPkg()
221 return fmt.Sprintf("make %s %s %s",
222 relType(v.Type(), from),
223 relName(v.Len, v),
224 relName(v.Cap, v))
225 }
226
227 func (v *Slice) String() string {
228 var b bytes.Buffer
229 b.WriteString("slice ")
230 b.WriteString(relName(v.X, v))
231 b.WriteString("[")
232 if v.Low != nil {
233 b.WriteString(relName(v.Low, v))
234 }
235 b.WriteString(":")
236 if v.High != nil {
237 b.WriteString(relName(v.High, v))
238 }
239 if v.Max != nil {
240 b.WriteString(":")
241 b.WriteString(relName(v.Max, v))
242 }
243 b.WriteString("]")
244 return b.String()
245 }
246
247 func (v *MakeMap) String() string {
248 res := ""
249 if v.Reserve != nil {
250 res = relName(v.Reserve, v)
251 }
252 from := v.Parent().relPkg()
253 return fmt.Sprintf("make %s %s", relType(v.Type(), from), res)
254 }
255
256 func (v *MakeChan) String() string {
257 from := v.Parent().relPkg()
258 return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v))
259 }
260
261 func (v *FieldAddr) String() string {
262
263 name := "?"
264 if fld := fieldOf(typeparams.MustDeref(v.X.Type()), v.Field); fld != nil {
265 name = fld.Name()
266 }
267 return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
268 }
269
270 func (v *Field) String() string {
271
272 name := "?"
273 if fld := fieldOf(v.X.Type(), v.Field); fld != nil {
274 name = fld.Name()
275 }
276 return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
277 }
278
279 func (v *IndexAddr) String() string {
280 return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v))
281 }
282
283 func (v *Index) String() string {
284 return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v))
285 }
286
287 func (v *Lookup) String() string {
288 return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk))
289 }
290
291 func (v *Range) String() string {
292 return "range " + relName(v.X, v)
293 }
294
295 func (v *Next) String() string {
296 return "next " + relName(v.Iter, v)
297 }
298
299 func (v *TypeAssert) String() string {
300 from := v.Parent().relPkg()
301 return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
302 }
303
304 func (v *Extract) String() string {
305 return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index)
306 }
307
308 func (s *Jump) String() string {
309
310 block := -1
311 if s.block != nil && len(s.block.Succs) == 1 {
312 block = s.block.Succs[0].Index
313 }
314 return fmt.Sprintf("jump %d", block)
315 }
316
317 func (s *If) String() string {
318
319 tblock, fblock := -1, -1
320 if s.block != nil && len(s.block.Succs) == 2 {
321 tblock = s.block.Succs[0].Index
322 fblock = s.block.Succs[1].Index
323 }
324 return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock)
325 }
326
327 func (s *Go) String() string {
328 return printCall(&s.Call, "go ", s)
329 }
330
331 func (s *Panic) String() string {
332 return "panic " + relName(s.X, s)
333 }
334
335 func (s *Return) String() string {
336 var b bytes.Buffer
337 b.WriteString("return")
338 for i, r := range s.Results {
339 if i == 0 {
340 b.WriteString(" ")
341 } else {
342 b.WriteString(", ")
343 }
344 b.WriteString(relName(r, s))
345 }
346 return b.String()
347 }
348
349 func (*RunDefers) String() string {
350 return "rundefers"
351 }
352
353 func (s *Send) String() string {
354 return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
355 }
356
357 func (s *Defer) String() string {
358 return printCall(&s.Call, "defer ", s)
359 }
360
361 func (s *Select) String() string {
362 var b bytes.Buffer
363 for i, st := range s.States {
364 if i > 0 {
365 b.WriteString(", ")
366 }
367 if st.Dir == types.RecvOnly {
368 b.WriteString("<-")
369 b.WriteString(relName(st.Chan, s))
370 } else {
371 b.WriteString(relName(st.Chan, s))
372 b.WriteString("<-")
373 b.WriteString(relName(st.Send, s))
374 }
375 }
376 non := ""
377 if !s.Blocking {
378 non = "non"
379 }
380 return fmt.Sprintf("select %sblocking [%s]", non, b.String())
381 }
382
383 func (s *Store) String() string {
384 return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s))
385 }
386
387 func (s *MapUpdate) String() string {
388 return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))
389 }
390
391 func (s *DebugRef) String() string {
392 p := s.Parent().Prog.Fset.Position(s.Pos())
393 var descr interface{}
394 if s.object != nil {
395 descr = s.object
396 } else {
397 descr = reflect.TypeOf(s.Expr)
398 }
399 var addr string
400 if s.IsAddr {
401 addr = "address of "
402 }
403 return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name())
404 }
405
406 func (p *Package) String() string {
407 return "package " + p.Pkg.Path()
408 }
409
410 var _ io.WriterTo = (*Package)(nil)
411
412 func (p *Package) WriteTo(w io.Writer) (int64, error) {
413 var buf bytes.Buffer
414 WritePackage(&buf, p)
415 n, err := w.Write(buf.Bytes())
416 return int64(n), err
417 }
418
419
420 func WritePackage(buf *bytes.Buffer, p *Package) {
421 fmt.Fprintf(buf, "%s:\n", p)
422
423 var names []string
424 maxname := 0
425 for name := range p.Members {
426 if l := len(name); l > maxname {
427 maxname = l
428 }
429 names = append(names, name)
430 }
431
432 from := p.Pkg
433 sort.Strings(names)
434 for _, name := range names {
435 switch mem := p.Members[name].(type) {
436 case *NamedConst:
437 fmt.Fprintf(buf, " const %-*s %s = %s\n",
438 maxname, name, mem.Name(), mem.Value.RelString(from))
439
440 case *Function:
441 fmt.Fprintf(buf, " func %-*s %s\n",
442 maxname, name, relType(mem.Type(), from))
443
444 case *Type:
445 fmt.Fprintf(buf, " type %-*s %s\n",
446 maxname, name, relType(mem.Type().Underlying(), from))
447 for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
448 fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from)))
449 }
450
451 case *Global:
452 fmt.Fprintf(buf, " var %-*s %s\n",
453 maxname, name, relType(typeparams.MustDeref(mem.Type()), from))
454 }
455 }
456
457 fmt.Fprintf(buf, "\n")
458 }
459
460 func commaOk(x bool) string {
461 if x {
462 return ",ok"
463 }
464 return ""
465 }
466
View as plain text