golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/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 *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 prefix := "defer " 359 if s._DeferStack != nil { 360 prefix += "[" + relName(s._DeferStack, s) + "] " 361 } 362 c := printCall(&s.Call, prefix, s) 363 return c 364 } 365 366 func (s *Select) String() string { 367 var b bytes.Buffer 368 for i, st := range s.States { 369 if i > 0 { 370 b.WriteString(", ") 371 } 372 if st.Dir == types.RecvOnly { 373 b.WriteString("<-") 374 b.WriteString(relName(st.Chan, s)) 375 } else { 376 b.WriteString(relName(st.Chan, s)) 377 b.WriteString("<-") 378 b.WriteString(relName(st.Send, s)) 379 } 380 } 381 non := "" 382 if !s.Blocking { 383 non = "non" 384 } 385 return fmt.Sprintf("select %sblocking [%s]", non, b.String()) 386 } 387 388 func (s *Store) String() string { 389 return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s)) 390 } 391 392 func (s *MapUpdate) String() string { 393 return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s)) 394 } 395 396 func (s *DebugRef) String() string { 397 p := s.Parent().Prog.Fset.Position(s.Pos()) 398 var descr interface{} 399 if s.object != nil { 400 descr = s.object // e.g. "var x int" 401 } else { 402 descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr" 403 } 404 var addr string 405 if s.IsAddr { 406 addr = "address of " 407 } 408 return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name()) 409 } 410 411 func (p *Package) String() string { 412 return "package " + p.Pkg.Path() 413 } 414 415 var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer 416 417 func (p *Package) WriteTo(w io.Writer) (int64, error) { 418 var buf bytes.Buffer 419 WritePackage(&buf, p) 420 n, err := w.Write(buf.Bytes()) 421 return int64(n), err 422 } 423 424 // WritePackage writes to buf a human-readable summary of p. 425 func WritePackage(buf *bytes.Buffer, p *Package) { 426 fmt.Fprintf(buf, "%s:\n", p) 427 428 var names []string 429 maxname := 0 430 for name := range p.Members { 431 if l := len(name); l > maxname { 432 maxname = l 433 } 434 names = append(names, name) 435 } 436 437 from := p.Pkg 438 sort.Strings(names) 439 for _, name := range names { 440 switch mem := p.Members[name].(type) { 441 case *NamedConst: 442 fmt.Fprintf(buf, " const %-*s %s = %s\n", 443 maxname, name, mem.Name(), mem.Value.RelString(from)) 444 445 case *Function: 446 fmt.Fprintf(buf, " func %-*s %s\n", 447 maxname, name, relType(mem.Type(), from)) 448 449 case *Type: 450 fmt.Fprintf(buf, " type %-*s %s\n", 451 maxname, name, relType(mem.Type().Underlying(), from)) 452 for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) { 453 fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from))) 454 } 455 456 case *Global: 457 fmt.Fprintf(buf, " var %-*s %s\n", 458 maxname, name, relType(typeparams.MustDeref(mem.Type()), from)) 459 } 460 } 461 462 fmt.Fprintf(buf, "\n") 463 } 464 465 func commaOk(x bool) string { 466 if x { 467 return ",ok" 468 } 469 return "" 470 }