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 relString(m Member, from *types.Package) string {
55
56
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
64
65
66
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
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
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>"
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
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
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
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
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
372 } else {
373 descr = reflect.TypeOf(s.Expr)
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)
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
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
View as plain text