github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/ssa/print.go (about)

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