src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

print.go (11055B)


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