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