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