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 }