print.go raw

   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  }
 463