...

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 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(deref(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 *MakeClosure) String() string {
   178  	var b bytes.Buffer
   179  	fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v))
   180  	if v.Bindings != nil {
   181  		b.WriteString(" [")
   182  		for i, c := range v.Bindings {
   183  			if i > 0 {
   184  				b.WriteString(", ")
   185  			}
   186  			b.WriteString(relName(c, v))
   187  		}
   188  		b.WriteString("]")
   189  	}
   190  	return b.String()
   191  }
   192  
   193  func (v *MakeSlice) String() string {
   194  	from := v.Parent().relPkg()
   195  	return fmt.Sprintf("make %s %s %s",
   196  		relType(v.Type(), from),
   197  		relName(v.Len, v),
   198  		relName(v.Cap, v))
   199  }
   200  
   201  func (v *Slice) String() string {
   202  	var b bytes.Buffer
   203  	b.WriteString("slice ")
   204  	b.WriteString(relName(v.X, v))
   205  	b.WriteString("[")
   206  	if v.Low != nil {
   207  		b.WriteString(relName(v.Low, v))
   208  	}
   209  	b.WriteString(":")
   210  	if v.High != nil {
   211  		b.WriteString(relName(v.High, v))
   212  	}
   213  	if v.Max != nil {
   214  		b.WriteString(":")
   215  		b.WriteString(relName(v.Max, v))
   216  	}
   217  	b.WriteString("]")
   218  	return b.String()
   219  }
   220  
   221  func (v *MakeMap) String() string {
   222  	res := ""
   223  	if v.Reserve != nil {
   224  		res = relName(v.Reserve, v)
   225  	}
   226  	from := v.Parent().relPkg()
   227  	return fmt.Sprintf("make %s %s", relType(v.Type(), from), res)
   228  }
   229  
   230  func (v *MakeChan) String() string {
   231  	from := v.Parent().relPkg()
   232  	return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v))
   233  }
   234  
   235  func (v *FieldAddr) String() 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.X, v), name, v.Field)
   243  }
   244  
   245  func (v *Field) String() 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.X, v), name, v.Field)
   253  }
   254  
   255  func (v *IndexAddr) String() string {
   256  	return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v))
   257  }
   258  
   259  func (v *Index) String() string {
   260  	return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v))
   261  }
   262  
   263  func (v *Lookup) String() string {
   264  	return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk))
   265  }
   266  
   267  func (v *Range) String() string {
   268  	return "range " + relName(v.X, v)
   269  }
   270  
   271  func (v *Next) String() string {
   272  	return "next " + relName(v.Iter, v)
   273  }
   274  
   275  func (v *TypeAssert) String() string {
   276  	from := v.Parent().relPkg()
   277  	return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
   278  }
   279  
   280  func (v *Extract) String() string {
   281  	return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index)
   282  }
   283  
   284  func (s *Jump) String() 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  
   293  func (s *If) String() string {
   294  	// Be robust against malformed CFG.
   295  	tblock, fblock := -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.Cond, s), tblock, fblock)
   301  }
   302  
   303  func (s *Go) String() string {
   304  	return printCall(&s.Call, "go ", s)
   305  }
   306  
   307  func (s *Panic) String() string {
   308  	return "panic " + relName(s.X, s)
   309  }
   310  
   311  func (s *Return) String() string {
   312  	var b bytes.Buffer
   313  	b.WriteString("return")
   314  	for i, r := range s.Results {
   315  		if i == 0 {
   316  			b.WriteString(" ")
   317  		} else {
   318  			b.WriteString(", ")
   319  		}
   320  		b.WriteString(relName(r, s))
   321  	}
   322  	return b.String()
   323  }
   324  
   325  func (*RunDefers) String() string {
   326  	return "rundefers"
   327  }
   328  
   329  func (s *Send) String() string {
   330  	return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
   331  }
   332  
   333  func (s *Defer) String() string {
   334  	return printCall(&s.Call, "defer ", s)
   335  }
   336  
   337  func (s *Select) String() string {
   338  	var b bytes.Buffer
   339  	for i, st := 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.Chan, s))
   346  		} else {
   347  			b.WriteString(relName(st.Chan, s))
   348  			b.WriteString("<-")
   349  			b.WriteString(relName(st.Send, s))
   350  		}
   351  	}
   352  	non := ""
   353  	if !s.Blocking {
   354  		non = "non"
   355  	}
   356  	return fmt.Sprintf("select %sblocking [%s]", non, b.String())
   357  }
   358  
   359  func (s *Store) String() string {
   360  	return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s))
   361  }
   362  
   363  func (s *MapUpdate) String() string {
   364  	return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))
   365  }
   366  
   367  func (s *DebugRef) String() 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", addr, descr, p.Line, p.Column, s.X.Name())
   380  }
   381  
   382  func (p *Package) String() string {
   383  	return "package " + p.Pkg.Path()
   384  }
   385  
   386  var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer
   387  
   388  func (p *Package) WriteTo(w io.Writer) (int64, error) {
   389  	var buf bytes.Buffer
   390  	WritePackage(&buf, p)
   391  	n, err := w.Write(buf.Bytes())
   392  	return int64(n), err
   393  }
   394  
   395  // WritePackage writes to buf a human-readable summary of p.
   396  func WritePackage(buf *bytes.Buffer, p *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(names, name)
   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  				maxname, name, mem.Name(), mem.Value.RelString(from))
   415  
   416  		case *Function:
   417  			fmt.Fprintf(buf, "  func  %-*s %s\n",
   418  				maxname, name, relType(mem.Type(), from))
   419  
   420  		case *Type:
   421  			fmt.Fprintf(buf, "  type  %-*s %s\n",
   422  				maxname, name, relType(mem.Type().Underlying(), from))
   423  			for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
   424  				fmt.Fprintf(buf, "    %s\n", types.SelectionString(meth, types.RelativeTo(from)))
   425  			}
   426  
   427  		case *Global:
   428  			fmt.Fprintf(buf, "  var   %-*s %s\n",
   429  				maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from))
   430  		}
   431  	}
   432  
   433  	fmt.Fprintf(buf, "\n")
   434  }
   435  
   436  func commaOk(x bool) string {
   437  	if x {
   438  		return ",ok"
   439  	}
   440  	return ""
   441  }
   442  

View as plain text