...

Source file src/golang.org/x/tools/go/ssa/print.go

Documentation: golang.org/x/tools/go/ssa

     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 *types.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(typeparams.MustDeref(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  	// Be robust against a bad index.
   263  	name := "?"
   264  	if fld := fieldOf(typeparams.MustDeref(v.X.Type()), v.Field); fld != nil {
   265  		name = fld.Name()
   266  	}
   267  	return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
   268  }
   269  
   270  func (v *Field) String() string {
   271  	// Be robust against a bad index.
   272  	name := "?"
   273  	if fld := fieldOf(v.X.Type(), v.Field); fld != nil {
   274  		name = fld.Name()
   275  	}
   276  	return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
   277  }
   278  
   279  func (v *IndexAddr) String() string {
   280  	return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v))
   281  }
   282  
   283  func (v *Index) String() string {
   284  	return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v))
   285  }
   286  
   287  func (v *Lookup) String() string {
   288  	return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk))
   289  }
   290  
   291  func (v *Range) String() string {
   292  	return "range " + relName(v.X, v)
   293  }
   294  
   295  func (v *Next) String() string {
   296  	return "next " + relName(v.Iter, v)
   297  }
   298  
   299  func (v *TypeAssert) String() string {
   300  	from := v.Parent().relPkg()
   301  	return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
   302  }
   303  
   304  func (v *Extract) String() string {
   305  	return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index)
   306  }
   307  
   308  func (s *Jump) String() string {
   309  	// Be robust against malformed CFG.
   310  	block := -1
   311  	if s.block != nil && len(s.block.Succs) == 1 {
   312  		block = s.block.Succs[0].Index
   313  	}
   314  	return fmt.Sprintf("jump %d", block)
   315  }
   316  
   317  func (s *If) String() string {
   318  	// Be robust against malformed CFG.
   319  	tblock, fblock := -1, -1
   320  	if s.block != nil && len(s.block.Succs) == 2 {
   321  		tblock = s.block.Succs[0].Index
   322  		fblock = s.block.Succs[1].Index
   323  	}
   324  	return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock)
   325  }
   326  
   327  func (s *Go) String() string {
   328  	return printCall(&s.Call, "go ", s)
   329  }
   330  
   331  func (s *Panic) String() string {
   332  	return "panic " + relName(s.X, s)
   333  }
   334  
   335  func (s *Return) String() string {
   336  	var b bytes.Buffer
   337  	b.WriteString("return")
   338  	for i, r := range s.Results {
   339  		if i == 0 {
   340  			b.WriteString(" ")
   341  		} else {
   342  			b.WriteString(", ")
   343  		}
   344  		b.WriteString(relName(r, s))
   345  	}
   346  	return b.String()
   347  }
   348  
   349  func (*RunDefers) String() string {
   350  	return "rundefers"
   351  }
   352  
   353  func (s *Send) String() string {
   354  	return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
   355  }
   356  
   357  func (s *Defer) String() string {
   358  	return printCall(&s.Call, "defer ", s)
   359  }
   360  
   361  func (s *Select) String() string {
   362  	var b bytes.Buffer
   363  	for i, st := range s.States {
   364  		if i > 0 {
   365  			b.WriteString(", ")
   366  		}
   367  		if st.Dir == types.RecvOnly {
   368  			b.WriteString("<-")
   369  			b.WriteString(relName(st.Chan, s))
   370  		} else {
   371  			b.WriteString(relName(st.Chan, s))
   372  			b.WriteString("<-")
   373  			b.WriteString(relName(st.Send, s))
   374  		}
   375  	}
   376  	non := ""
   377  	if !s.Blocking {
   378  		non = "non"
   379  	}
   380  	return fmt.Sprintf("select %sblocking [%s]", non, b.String())
   381  }
   382  
   383  func (s *Store) String() string {
   384  	return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s))
   385  }
   386  
   387  func (s *MapUpdate) String() string {
   388  	return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))
   389  }
   390  
   391  func (s *DebugRef) String() string {
   392  	p := s.Parent().Prog.Fset.Position(s.Pos())
   393  	var descr interface{}
   394  	if s.object != nil {
   395  		descr = s.object // e.g. "var x int"
   396  	} else {
   397  		descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr"
   398  	}
   399  	var addr string
   400  	if s.IsAddr {
   401  		addr = "address of "
   402  	}
   403  	return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name())
   404  }
   405  
   406  func (p *Package) String() string {
   407  	return "package " + p.Pkg.Path()
   408  }
   409  
   410  var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer
   411  
   412  func (p *Package) WriteTo(w io.Writer) (int64, error) {
   413  	var buf bytes.Buffer
   414  	WritePackage(&buf, p)
   415  	n, err := w.Write(buf.Bytes())
   416  	return int64(n), err
   417  }
   418  
   419  // WritePackage writes to buf a human-readable summary of p.
   420  func WritePackage(buf *bytes.Buffer, p *Package) {
   421  	fmt.Fprintf(buf, "%s:\n", p)
   422  
   423  	var names []string
   424  	maxname := 0
   425  	for name := range p.Members {
   426  		if l := len(name); l > maxname {
   427  			maxname = l
   428  		}
   429  		names = append(names, name)
   430  	}
   431  
   432  	from := p.Pkg
   433  	sort.Strings(names)
   434  	for _, name := range names {
   435  		switch mem := p.Members[name].(type) {
   436  		case *NamedConst:
   437  			fmt.Fprintf(buf, "  const %-*s %s = %s\n",
   438  				maxname, name, mem.Name(), mem.Value.RelString(from))
   439  
   440  		case *Function:
   441  			fmt.Fprintf(buf, "  func  %-*s %s\n",
   442  				maxname, name, relType(mem.Type(), from))
   443  
   444  		case *Type:
   445  			fmt.Fprintf(buf, "  type  %-*s %s\n",
   446  				maxname, name, relType(mem.Type().Underlying(), from))
   447  			for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
   448  				fmt.Fprintf(buf, "    %s\n", types.SelectionString(meth, types.RelativeTo(from)))
   449  			}
   450  
   451  		case *Global:
   452  			fmt.Fprintf(buf, "  var   %-*s %s\n",
   453  				maxname, name, relType(typeparams.MustDeref(mem.Type()), from))
   454  		}
   455  	}
   456  
   457  	fmt.Fprintf(buf, "\n")
   458  }
   459  
   460  func commaOk(x bool) string {
   461  	if x {
   462  		return ",ok"
   463  	}
   464  	return ""
   465  }
   466  

View as plain text