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 | // normalizeAnyFortesting controls whether we replace occurrences of |
43 | // interface{} with any. It is only used for normalizing test output. |
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 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(deref(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 *MakeClosure) String() string { |
178 | var b bytes.Buffer |
179 | fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v)) |
180 | if v.Bindings != nil { |
181 | b.WriteString(" [") |
182 | for i, c := range v.Bindings { |
183 | if i > 0 { |
184 | b.WriteString(", ") |
185 | } |
186 | b.WriteString(relName(c, v)) |
187 | } |
188 | b.WriteString("]") |
189 | } |
190 | return b.String() |
191 | } |
192 | |
193 | func (v *MakeSlice) String() string { |
194 | from := v.Parent().relPkg() |
195 | return fmt.Sprintf("make %s %s %s", |
196 | relType(v.Type(), from), |
197 | relName(v.Len, v), |
198 | relName(v.Cap, v)) |
199 | } |
200 | |
201 | func (v *Slice) String() string { |
202 | var b bytes.Buffer |
203 | b.WriteString("slice ") |
204 | b.WriteString(relName(v.X, v)) |
205 | b.WriteString("[") |
206 | if v.Low != nil { |
207 | b.WriteString(relName(v.Low, v)) |
208 | } |
209 | b.WriteString(":") |
210 | if v.High != nil { |
211 | b.WriteString(relName(v.High, v)) |
212 | } |
213 | if v.Max != nil { |
214 | b.WriteString(":") |
215 | b.WriteString(relName(v.Max, v)) |
216 | } |
217 | b.WriteString("]") |
218 | return b.String() |
219 | } |
220 | |
221 | func (v *MakeMap) String() string { |
222 | res := "" |
223 | if v.Reserve != nil { |
224 | res = relName(v.Reserve, v) |
225 | } |
226 | from := v.Parent().relPkg() |
227 | return fmt.Sprintf("make %s %s", relType(v.Type(), from), res) |
228 | } |
229 | |
230 | func (v *MakeChan) String() string { |
231 | from := v.Parent().relPkg() |
232 | return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v)) |
233 | } |
234 | |
235 | func (v *FieldAddr) String() string { |
236 | st := typeparams.CoreType(deref(v.X.Type())).(*types.Struct) |
237 | // Be robust against a bad index. |
238 | name := "?" |
239 | if 0 <= v.Field && v.Field < st.NumFields() { |
240 | name = st.Field(v.Field).Name() |
241 | } |
242 | return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field) |
243 | } |
244 | |
245 | func (v *Field) String() string { |
246 | st := typeparams.CoreType(v.X.Type()).(*types.Struct) |
247 | // Be robust against a bad index. |
248 | name := "?" |
249 | if 0 <= v.Field && v.Field < st.NumFields() { |
250 | name = st.Field(v.Field).Name() |
251 | } |
252 | return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field) |
253 | } |
254 | |
255 | func (v *IndexAddr) String() string { |
256 | return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v)) |
257 | } |
258 | |
259 | func (v *Index) String() string { |
260 | return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v)) |
261 | } |
262 | |
263 | func (v *Lookup) String() string { |
264 | return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk)) |
265 | } |
266 | |
267 | func (v *Range) String() string { |
268 | return "range " + relName(v.X, v) |
269 | } |
270 | |
271 | func (v *Next) String() string { |
272 | return "next " + relName(v.Iter, v) |
273 | } |
274 | |
275 | func (v *TypeAssert) String() string { |
276 | from := v.Parent().relPkg() |
277 | return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from)) |
278 | } |
279 | |
280 | func (v *Extract) String() string { |
281 | return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index) |
282 | } |
283 | |
284 | func (s *Jump) String() string { |
285 | // Be robust against malformed CFG. |
286 | block := -1 |
287 | if s.block != nil && len(s.block.Succs) == 1 { |
288 | block = s.block.Succs[0].Index |
289 | } |
290 | return fmt.Sprintf("jump %d", block) |
291 | } |
292 | |
293 | func (s *If) String() string { |
294 | // Be robust against malformed CFG. |
295 | tblock, fblock := -1, -1 |
296 | if s.block != nil && len(s.block.Succs) == 2 { |
297 | tblock = s.block.Succs[0].Index |
298 | fblock = s.block.Succs[1].Index |
299 | } |
300 | return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock) |
301 | } |
302 | |
303 | func (s *Go) String() string { |
304 | return printCall(&s.Call, "go ", s) |
305 | } |
306 | |
307 | func (s *Panic) String() string { |
308 | return "panic " + relName(s.X, s) |
309 | } |
310 | |
311 | func (s *Return) String() string { |
312 | var b bytes.Buffer |
313 | b.WriteString("return") |
314 | for i, r := range s.Results { |
315 | if i == 0 { |
316 | b.WriteString(" ") |
317 | } else { |
318 | b.WriteString(", ") |
319 | } |
320 | b.WriteString(relName(r, s)) |
321 | } |
322 | return b.String() |
323 | } |
324 | |
325 | func (*RunDefers) String() string { |
326 | return "rundefers" |
327 | } |
328 | |
329 | func (s *Send) String() string { |
330 | return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s)) |
331 | } |
332 | |
333 | func (s *Defer) String() string { |
334 | return printCall(&s.Call, "defer ", s) |
335 | } |
336 | |
337 | func (s *Select) String() string { |
338 | var b bytes.Buffer |
339 | for i, st := range s.States { |
340 | if i > 0 { |
341 | b.WriteString(", ") |
342 | } |
343 | if st.Dir == types.RecvOnly { |
344 | b.WriteString("<-") |
345 | b.WriteString(relName(st.Chan, s)) |
346 | } else { |
347 | b.WriteString(relName(st.Chan, s)) |
348 | b.WriteString("<-") |
349 | b.WriteString(relName(st.Send, s)) |
350 | } |
351 | } |
352 | non := "" |
353 | if !s.Blocking { |
354 | non = "non" |
355 | } |
356 | return fmt.Sprintf("select %sblocking [%s]", non, b.String()) |
357 | } |
358 | |
359 | func (s *Store) String() string { |
360 | return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s)) |
361 | } |
362 | |
363 | func (s *MapUpdate) String() string { |
364 | return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s)) |
365 | } |
366 | |
367 | func (s *DebugRef) String() string { |
368 | p := s.Parent().Prog.Fset.Position(s.Pos()) |
369 | var descr interface{} |
370 | if s.object != nil { |
371 | descr = s.object // e.g. "var x int" |
372 | } else { |
373 | descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr" |
374 | } |
375 | var addr string |
376 | if s.IsAddr { |
377 | addr = "address of " |
378 | } |
379 | return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name()) |
380 | } |
381 | |
382 | func (p *Package) String() string { |
383 | return "package " + p.Pkg.Path() |
384 | } |
385 | |
386 | var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer |
387 | |
388 | func (p *Package) WriteTo(w io.Writer) (int64, error) { |
389 | var buf bytes.Buffer |
390 | WritePackage(&buf, p) |
391 | n, err := w.Write(buf.Bytes()) |
392 | return int64(n), err |
393 | } |
394 | |
395 | // WritePackage writes to buf a human-readable summary of p. |
396 | func WritePackage(buf *bytes.Buffer, p *Package) { |
397 | fmt.Fprintf(buf, "%s:\n", p) |
398 | |
399 | var names []string |
400 | maxname := 0 |
401 | for name := range p.Members { |
402 | if l := len(name); l > maxname { |
403 | maxname = l |
404 | } |
405 | names = append(names, name) |
406 | } |
407 | |
408 | from := p.Pkg |
409 | sort.Strings(names) |
410 | for _, name := range names { |
411 | switch mem := p.Members[name].(type) { |
412 | case *NamedConst: |
413 | fmt.Fprintf(buf, " const %-*s %s = %s\n", |
414 | maxname, name, mem.Name(), mem.Value.RelString(from)) |
415 | |
416 | case *Function: |
417 | fmt.Fprintf(buf, " func %-*s %s\n", |
418 | maxname, name, relType(mem.Type(), from)) |
419 | |
420 | case *Type: |
421 | fmt.Fprintf(buf, " type %-*s %s\n", |
422 | maxname, name, relType(mem.Type().Underlying(), from)) |
423 | for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) { |
424 | fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from))) |
425 | } |
426 | |
427 | case *Global: |
428 | fmt.Fprintf(buf, " var %-*s %s\n", |
429 | maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from)) |
430 | } |
431 | } |
432 | |
433 | fmt.Fprintf(buf, "\n") |
434 | } |
435 | |
436 | func commaOk(x bool) string { |
437 | if x { |
438 | return ",ok" |
439 | } |
440 | return "" |
441 | } |
442 |
Members