GoPLS Viewer

Home|gopls/go/ssa/print.go
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    "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.
28func relName(v Valuei Instructionstring {
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.
44var normalizeAnyForTesting bool
45
46func relType(t types.Typefrom *types.Packagestring {
47    s := types.TypeString(ttypes.RelativeTo(from))
48    if normalizeAnyForTesting {
49        s = strings.ReplaceAll(s"interface{}""any")
50    }
51    return s
52}
53
54func relString(m Memberfrom *types.Packagestring {
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().Pkgpkg != 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
68func (v *ParameterString() string {
69    from := v.Parent().relPkg()
70    return fmt.Sprintf("parameter %s : %s"v.Name(), relType(v.Type(), from))
71}
72
73func (v *FreeVarString() string {
74    from := v.Parent().relPkg()
75    return fmt.Sprintf("freevar %s : %s"v.Name(), relType(v.Type(), from))
76}
77
78func (v *BuiltinString() string {
79    return fmt.Sprintf("builtin %s"v.Name())
80}
81
82// Instruction.String()
83
84func (v *AllocString() string {
85    op := "local"
86    if v.Heap {
87        op = "new"
88    }
89    from := v.Parent().relPkg()
90    return fmt.Sprintf("%s %s (%s)"oprelType(deref(v.Type()), from), v.Comment)
91}
92
93func (v *PhiString() string {
94    var b bytes.Buffer
95    b.WriteString("phi [")
96    for iedge := 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(edgev)
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
124func printCall(v *CallCommonprefix stringinstr Instructionstring {
125    var b bytes.Buffer
126    b.WriteString(prefix)
127    if !v.IsInvoke() {
128        b.WriteString(relName(v.Valueinstr))
129    } else {
130        fmt.Fprintf(&b"invoke %s.%s"relName(v.Valueinstr), v.Method.Name())
131    }
132    b.WriteString("(")
133    for iarg := range v.Args {
134        if i > 0 {
135            b.WriteString(", ")
136        }
137        b.WriteString(relName(arginstr))
138    }
139    if v.Signature().Variadic() {
140        b.WriteString("...")
141    }
142    b.WriteString(")")
143    return b.String()
144}
145
146func (c *CallCommonString() string {
147    return printCall(c""nil)
148}
149
150func (v *CallString() string {
151    return printCall(&v.Call""v)
152}
153
154func (v *BinOpString() string {
155    return fmt.Sprintf("%s %s %s"relName(v.Xv), v.Op.String(), relName(v.Yv))
156}
157
158func (v *UnOpString() string {
159    return fmt.Sprintf("%s%s%s"v.OprelName(v.Xv), commaOk(v.CommaOk))
160}
161
162func printConv(prefix stringvx Valuestring {
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(xv.(Instruction)))
169}
170
171func (v *ChangeTypeString() string          { return printConv("changetype"vv.X) }
172func (v *ConvertString() string             { return printConv("convert"vv.X) }
173func (v *ChangeInterfaceString() string     { return printConv("change interface"vv.X) }
174func (v *SliceToArrayPointerString() string { return printConv("slice to array pointer"vv.X) }
175func (v *MakeInterfaceString() string       { return printConv("make"vv.X) }
176
177func (v *MakeClosureString() string {
178    var b bytes.Buffer
179    fmt.Fprintf(&b"make closure %s"relName(v.Fnv))
180    if v.Bindings != nil {
181        b.WriteString(" [")
182        for ic := range v.Bindings {
183            if i > 0 {
184                b.WriteString(", ")
185            }
186            b.WriteString(relName(cv))
187        }
188        b.WriteString("]")
189    }
190    return b.String()
191}
192
193func (v *MakeSliceString() string {
194    from := v.Parent().relPkg()
195    return fmt.Sprintf("make %s %s %s",
196        relType(v.Type(), from),
197        relName(v.Lenv),
198        relName(v.Capv))
199}
200
201func (v *SliceString() string {
202    var b bytes.Buffer
203    b.WriteString("slice ")
204    b.WriteString(relName(v.Xv))
205    b.WriteString("[")
206    if v.Low != nil {
207        b.WriteString(relName(v.Lowv))
208    }
209    b.WriteString(":")
210    if v.High != nil {
211        b.WriteString(relName(v.Highv))
212    }
213    if v.Max != nil {
214        b.WriteString(":")
215        b.WriteString(relName(v.Maxv))
216    }
217    b.WriteString("]")
218    return b.String()
219}
220
221func (v *MakeMapString() string {
222    res := ""
223    if v.Reserve != nil {
224        res = relName(v.Reservev)
225    }
226    from := v.Parent().relPkg()
227    return fmt.Sprintf("make %s %s"relType(v.Type(), from), res)
228}
229
230func (v *MakeChanString() string {
231    from := v.Parent().relPkg()
232    return fmt.Sprintf("make %s %s"relType(v.Type(), from), relName(v.Sizev))
233}
234
235func (v *FieldAddrString() 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.Xv), namev.Field)
243}
244
245func (v *FieldString() 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.Xv), namev.Field)
253}
254
255func (v *IndexAddrString() string {
256    return fmt.Sprintf("&%s[%s]"relName(v.Xv), relName(v.Indexv))
257}
258
259func (v *IndexString() string {
260    return fmt.Sprintf("%s[%s]"relName(v.Xv), relName(v.Indexv))
261}
262
263func (v *LookupString() string {
264    return fmt.Sprintf("%s[%s]%s"relName(v.Xv), relName(v.Indexv), commaOk(v.CommaOk))
265}
266
267func (v *RangeString() string {
268    return "range " + relName(v.Xv)
269}
270
271func (v *NextString() string {
272    return "next " + relName(v.Iterv)
273}
274
275func (v *TypeAssertString() string {
276    from := v.Parent().relPkg()
277    return fmt.Sprintf("typeassert%s %s.(%s)"commaOk(v.CommaOk), relName(v.Xv), relType(v.AssertedTypefrom))
278}
279
280func (v *ExtractString() string {
281    return fmt.Sprintf("extract %s #%d"relName(v.Tuplev), v.Index)
282}
283
284func (s *JumpString() 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
293func (s *IfString() string {
294    // Be robust against malformed CFG.
295    tblockfblock := -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.Conds), tblockfblock)
301}
302
303func (s *GoString() string {
304    return printCall(&s.Call"go "s)
305}
306
307func (s *PanicString() string {
308    return "panic " + relName(s.Xs)
309}
310
311func (s *ReturnString() string {
312    var b bytes.Buffer
313    b.WriteString("return")
314    for ir := range s.Results {
315        if i == 0 {
316            b.WriteString(" ")
317        } else {
318            b.WriteString(", ")
319        }
320        b.WriteString(relName(rs))
321    }
322    return b.String()
323}
324
325func (*RunDefersString() string {
326    return "rundefers"
327}
328
329func (s *SendString() string {
330    return fmt.Sprintf("send %s <- %s"relName(s.Chans), relName(s.Xs))
331}
332
333func (s *DeferString() string {
334    return printCall(&s.Call"defer "s)
335}
336
337func (s *SelectString() string {
338    var b bytes.Buffer
339    for ist := 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.Chans))
346        } else {
347            b.WriteString(relName(st.Chans))
348            b.WriteString("<-")
349            b.WriteString(relName(st.Sends))
350        }
351    }
352    non := ""
353    if !s.Blocking {
354        non = "non"
355    }
356    return fmt.Sprintf("select %sblocking [%s]"nonb.String())
357}
358
359func (s *StoreString() string {
360    return fmt.Sprintf("*%s = %s"relName(s.Addrs), relName(s.Vals))
361}
362
363func (s *MapUpdateString() string {
364    return fmt.Sprintf("%s[%s] = %s"relName(s.Maps), relName(s.Keys), relName(s.Values))
365}
366
367func (s *DebugRefString() 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"addrdescrp.Linep.Columns.X.Name())
380}
381
382func (p *PackageString() string {
383    return "package " + p.Pkg.Path()
384}
385
386var _ io.WriterTo = (*Package)(nil// *Package implements io.Writer
387
388func (p *PackageWriteTo(w io.Writer) (int64error) {
389    var buf bytes.Buffer
390    WritePackage(&bufp)
391    nerr := w.Write(buf.Bytes())
392    return int64(n), err
393}
394
395// WritePackage writes to buf a human-readable summary of p.
396func WritePackage(buf *bytes.Bufferp *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(namesname)
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                maxnamenamemem.Name(), mem.Value.RelString(from))
415
416        case *Function:
417            fmt.Fprintf(buf"  func  %-*s %s\n",
418                maxnamenamerelType(mem.Type(), from))
419
420        case *Type:
421            fmt.Fprintf(buf"  type  %-*s %s\n",
422                maxnamenamerelType(mem.Type().Underlying(), from))
423            for _meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
424                fmt.Fprintf(buf"    %s\n"types.SelectionString(methtypes.RelativeTo(from)))
425            }
426
427        case *Global:
428            fmt.Fprintf(buf"  var   %-*s %s\n",
429                maxnamenamerelType(mem.Type().(*types.Pointer).Elem(), from))
430        }
431    }
432
433    fmt.Fprintf(buf"\n")
434}
435
436func commaOk(x boolstring {
437    if x {
438        return ",ok"
439    }
440    return ""
441}
442
MembersX
printConv
printConv.v
TypeAssert.String
Send.String.s
MapUpdate.String
DebugRef.String.p
Package.String.p
WritePackage.RangeStmt_9777.BlockStmt.l
relString.from
Parameter.String.v
Call.String
printCall.b
SliceToArrayPointer.String.v
Next.String.v
Jump.String.s
Defer.String
DebugRef.String.addr
Phi.String.v
MakeInterface.String.v
MakeChan.String.from
Go.String.s
Return.String.s
MapUpdate.String.s
printCall.instr
TypeAssert.String.v
If.String
relType.t
printCall.RangeStmt_3238.i
MakeMap.String.v
Defer.String.s
Slice.String.v
MakeChan.String.v
Index.String
IndexAddr.String
Next.String
Package.WriteTo.err
Builtin.String
printCall.RangeStmt_3238.arg
MakeMap.String
MakeMap.String.from
Range.String
DebugRef.String.s
FreeVar.String.v
Alloc.String.op
ChangeType.String
Jump.String
relString.m
Convert.String
MakeSlice.String.from
MakeSlice.String.v
Extract.String
WritePackage.buf
WritePackage.RangeStmt_9932.name
relName.v
Phi.String.RangeStmt_2435.i
MakeClosure.String.v
Select.String
Package.WriteTo.n
relName
relString.pkg
Builtin.String.v
Return.String
MakeClosure.String.BlockStmt.RangeStmt_4650.i
IndexAddr.String.v
Alloc.String
Phi.String.b
Call.String.v
Store.String
printConv.x
FieldAddr.String.name
Field.String.name
Phi.String.RangeStmt_2435.BlockStmt.edgeVal
printConv.from
If.String.s
Store.String.s
relType
Parameter.String.from
FreeVar.String
Field.String
Lookup.String.v
Select.String.RangeStmt_8279.st
Package.WriteTo.w
WritePackage.RangeStmt_9777.name
relString
FreeVar.String.from
printCall.v
commaOk.x
relType.from
MakeClosure.String
RunDefers.String
Phi.String
BinOp.String
MakeClosure.String.b
Field.String.v
Panic.String.s
Package.WriteTo
Alloc.String.v
ChangeType.String.v
ChangeInterface.String.v
BinOp.String.v
FieldAddr.String.v
Go.String
Return.String.b
WritePackage.names
relType.s
MakeInterface.String
MakeClosure.String.BlockStmt.RangeStmt_4650.c
Send.String
Select.String.b
Package.String
Package.WriteTo.buf
WritePackage.from
Alloc.String.from
MakeSlice.String
Slice.String
UnOp.String
Index.String.v
Select.String.RangeStmt_8279.i
normalizeAnyForTesting
Phi.String.RangeStmt_2435.edge
CallCommon.String
Select.String.non
Package.WriteTo.p
Parameter.String
printConv.prefix
ChangeInterface.String
TypeAssert.String.from
Return.String.RangeStmt_7817.i
WritePackage.p
printCall
MakeMap.String.res
Lookup.String
printCall.prefix
FieldAddr.String
WritePackage.maxname
relName.i
Panic.String
Return.String.RangeStmt_7817.r
Select.String.s
WritePackage
commaOk
relName.from
Slice.String.b
Range.String.v
Extract.String.v
CallCommon.String.c
UnOp.String.v
SliceToArrayPointer.String
WritePackage.RangeStmt_9932.BlockStmt.BlockStmt.RangeStmt_10350.meth
Convert.String.v
MakeChan.String
DebugRef.String
Members
X