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