github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/ir/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 ir 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 18 "github.com/amarpal/go-tools/go/types/typeutil" 19 ) 20 21 // relName returns the name of v relative to i. 22 // In most cases, this is identical to v.Name(), but references to 23 // Functions (including methods) and Globals use RelString and 24 // all types are displayed with relType, so that only cross-package 25 // references are package-qualified. 26 func relName(v Value, i Instruction) string { 27 if v == nil { 28 return "<nil>" 29 } 30 var from *types.Package 31 if i != nil { 32 from = i.Parent().pkg() 33 } 34 switch v := v.(type) { 35 case Member: // *Function or *Global 36 return v.RelString(from) 37 } 38 return v.Name() 39 } 40 41 func relType(t types.Type, from *types.Package) string { 42 return types.TypeString(t, types.RelativeTo(from)) 43 } 44 45 func relString(m Member, from *types.Package) string { 46 // NB: not all globals have an Object (e.g. init$guard), 47 // so use Package().Object not Object.Package(). 48 if pkg := m.Package().Pkg; pkg != nil && pkg != from { 49 return fmt.Sprintf("%s.%s", pkg.Path(), m.Name()) 50 } 51 return m.Name() 52 } 53 54 // Value.String() 55 // 56 // This method is provided only for debugging. 57 // It never appears in disassembly, which uses Value.Name(). 58 59 func (v *Parameter) String() string { 60 from := v.Parent().pkg() 61 return fmt.Sprintf("Parameter <%s> {%s}", relType(v.Type(), from), v.name) 62 } 63 64 func (v *FreeVar) String() string { 65 from := v.Parent().pkg() 66 return fmt.Sprintf("FreeVar <%s> %s", relType(v.Type(), from), v.Name()) 67 } 68 69 func (v *Builtin) String() string { 70 return fmt.Sprintf("Builtin %s", v.Name()) 71 } 72 73 // Instruction.String() 74 75 func (v *Alloc) String() string { 76 from := v.Parent().pkg() 77 storage := "Stack" 78 if v.Heap { 79 storage = "Heap" 80 } 81 return fmt.Sprintf("%sAlloc <%s>", storage, relType(v.Type(), from)) 82 } 83 84 func (v *Sigma) String() string { 85 from := v.Parent().pkg() 86 s := fmt.Sprintf("Sigma <%s> [b%d] %s", relType(v.Type(), from), v.From.Index, v.X.Name()) 87 return s 88 } 89 90 func (v *Phi) String() string { 91 var b bytes.Buffer 92 fmt.Fprintf(&b, "Phi <%s>", v.Type()) 93 for i, edge := range v.Edges { 94 b.WriteString(" ") 95 // Be robust against malformed CFG. 96 if v.block == nil { 97 b.WriteString("??") 98 continue 99 } 100 block := -1 101 if i < len(v.block.Preds) { 102 block = v.block.Preds[i].Index 103 } 104 fmt.Fprintf(&b, "%d:", block) 105 edgeVal := "<nil>" // be robust 106 if edge != nil { 107 edgeVal = relName(edge, v) 108 } 109 b.WriteString(edgeVal) 110 } 111 return b.String() 112 } 113 114 func printCall(v *CallCommon, prefix string, instr Instruction) string { 115 var b bytes.Buffer 116 if !v.IsInvoke() { 117 if value, ok := instr.(Value); ok { 118 fmt.Fprintf(&b, "%s <%s> %s", prefix, relType(value.Type(), instr.Parent().pkg()), relName(v.Value, instr)) 119 } else { 120 fmt.Fprintf(&b, "%s %s", prefix, relName(v.Value, instr)) 121 } 122 } else { 123 if value, ok := instr.(Value); ok { 124 fmt.Fprintf(&b, "%sInvoke <%s> %s.%s", prefix, relType(value.Type(), instr.Parent().pkg()), relName(v.Value, instr), v.Method.Name()) 125 } else { 126 fmt.Fprintf(&b, "%sInvoke %s.%s", prefix, relName(v.Value, instr), v.Method.Name()) 127 } 128 } 129 for _, arg := range v.TypeArgs { 130 b.WriteString(" ") 131 b.WriteString(relType(arg, instr.Parent().pkg())) 132 } 133 for _, arg := range v.Args { 134 b.WriteString(" ") 135 b.WriteString(relName(arg, instr)) 136 } 137 return b.String() 138 } 139 140 func (c *CallCommon) String() string { 141 return printCall(c, "", nil) 142 } 143 144 func (v *Call) String() string { 145 return printCall(&v.Call, "Call", v) 146 } 147 148 func (v *BinOp) String() string { 149 return fmt.Sprintf("BinOp <%s> {%s} %s %s", relType(v.Type(), v.Parent().pkg()), v.Op.String(), relName(v.X, v), relName(v.Y, v)) 150 } 151 152 func (v *UnOp) String() string { 153 return fmt.Sprintf("UnOp <%s> {%s} %s", relType(v.Type(), v.Parent().pkg()), v.Op.String(), relName(v.X, v)) 154 } 155 156 func (v *Load) String() string { 157 return fmt.Sprintf("Load <%s> %s", relType(v.Type(), v.Parent().pkg()), relName(v.X, v)) 158 } 159 160 func (v *Copy) String() string { 161 return fmt.Sprintf("Copy <%s> %s", relType(v.Type(), v.Parent().pkg()), relName(v.X, v)) 162 } 163 164 func printConv(prefix string, v, x Value) string { 165 from := v.Parent().pkg() 166 return fmt.Sprintf("%s <%s> %s", 167 prefix, 168 relType(v.Type(), from), 169 relName(x, v.(Instruction))) 170 } 171 172 func (v *ChangeType) String() string { return printConv("ChangeType", v, v.X) } 173 func (v *Convert) String() string { return printConv("Convert", v, v.X) } 174 func (v *ChangeInterface) String() string { return printConv("ChangeInterface", v, v.X) } 175 func (v *SliceToArrayPointer) String() string { return printConv("SliceToArrayPointer", v, v.X) } 176 func (v *SliceToArray) String() string { return printConv("SliceToArray", v, v.X) } 177 func (v *MakeInterface) String() string { return printConv("MakeInterface", v, v.X) } 178 179 func (v *MakeClosure) String() string { 180 from := v.Parent().pkg() 181 var b bytes.Buffer 182 fmt.Fprintf(&b, "MakeClosure <%s> %s", relType(v.Type(), from), relName(v.Fn, v)) 183 if v.Bindings != nil { 184 for _, c := range v.Bindings { 185 b.WriteString(" ") 186 b.WriteString(relName(c, v)) 187 } 188 } 189 return b.String() 190 } 191 192 func (v *MakeSlice) String() string { 193 from := v.Parent().pkg() 194 return fmt.Sprintf("MakeSlice <%s> %s %s", 195 relType(v.Type(), from), 196 relName(v.Len, v), 197 relName(v.Cap, v)) 198 } 199 200 func (v *Slice) String() string { 201 from := v.Parent().pkg() 202 return fmt.Sprintf("Slice <%s> %s %s %s %s", 203 relType(v.Type(), from), relName(v.X, v), relName(v.Low, v), relName(v.High, v), relName(v.Max, v)) 204 } 205 206 func (v *MakeMap) String() string { 207 res := "" 208 if v.Reserve != nil { 209 res = relName(v.Reserve, v) 210 } 211 from := v.Parent().pkg() 212 return fmt.Sprintf("MakeMap <%s> %s", relType(v.Type(), from), res) 213 } 214 215 func (v *MakeChan) String() string { 216 from := v.Parent().pkg() 217 return fmt.Sprintf("MakeChan <%s> %s", relType(v.Type(), from), relName(v.Size, v)) 218 } 219 220 func (v *FieldAddr) String() string { 221 from := v.Parent().pkg() 222 // v.X.Type() might be a pointer to a type parameter whose core type is a pointer to a struct 223 st := deref(typeutil.CoreType(deref(v.X.Type()))).Underlying().(*types.Struct) 224 // Be robust against a bad index. 225 name := "?" 226 if 0 <= v.Field && v.Field < st.NumFields() { 227 name = st.Field(v.Field).Name() 228 } 229 return fmt.Sprintf("FieldAddr <%s> [%d] (%s) %s", relType(v.Type(), from), v.Field, name, relName(v.X, v)) 230 } 231 232 func (v *Field) String() string { 233 st := typeutil.CoreType(v.X.Type()).Underlying().(*types.Struct) 234 // Be robust against a bad index. 235 name := "?" 236 if 0 <= v.Field && v.Field < st.NumFields() { 237 name = st.Field(v.Field).Name() 238 } 239 from := v.Parent().pkg() 240 return fmt.Sprintf("Field <%s> [%d] (%s) %s", relType(v.Type(), from), v.Field, name, relName(v.X, v)) 241 } 242 243 func (v *IndexAddr) String() string { 244 from := v.Parent().pkg() 245 return fmt.Sprintf("IndexAddr <%s> %s %s", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v)) 246 } 247 248 func (v *Index) String() string { 249 from := v.Parent().pkg() 250 return fmt.Sprintf("Index <%s> %s %s", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v)) 251 } 252 253 func (v *MapLookup) String() string { 254 from := v.Parent().pkg() 255 return fmt.Sprintf("MapLookup <%s> %s %s", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v)) 256 } 257 258 func (v *StringLookup) String() string { 259 from := v.Parent().pkg() 260 return fmt.Sprintf("StringLookup <%s> %s %s", relType(v.Type(), from), relName(v.X, v), relName(v.Index, v)) 261 } 262 263 func (v *Range) String() string { 264 from := v.Parent().pkg() 265 return fmt.Sprintf("Range <%s> %s", relType(v.Type(), from), relName(v.X, v)) 266 } 267 268 func (v *Next) String() string { 269 from := v.Parent().pkg() 270 return fmt.Sprintf("Next <%s> %s", relType(v.Type(), from), relName(v.Iter, v)) 271 } 272 273 func (v *TypeAssert) String() string { 274 from := v.Parent().pkg() 275 return fmt.Sprintf("TypeAssert <%s> %s", relType(v.Type(), from), relName(v.X, v)) 276 } 277 278 func (v *Extract) String() string { 279 from := v.Parent().pkg() 280 name := v.Tuple.Type().(*types.Tuple).At(v.Index).Name() 281 return fmt.Sprintf("Extract <%s> [%d] (%s) %s", relType(v.Type(), from), v.Index, name, relName(v.Tuple, v)) 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 str := fmt.Sprintf("Jump → b%d", block) 291 if s.Comment() != "" { 292 str = fmt.Sprintf("%s # %s", str, s.Comment()) 293 } 294 return str 295 } 296 297 func (s *Unreachable) String() string { 298 // Be robust against malformed CFG. 299 block := -1 300 if s.block != nil && len(s.block.Succs) == 1 { 301 block = s.block.Succs[0].Index 302 } 303 return fmt.Sprintf("Unreachable → b%d", block) 304 } 305 306 func (s *If) String() string { 307 // Be robust against malformed CFG. 308 tblock, fblock := -1, -1 309 if s.block != nil && len(s.block.Succs) == 2 { 310 tblock = s.block.Succs[0].Index 311 fblock = s.block.Succs[1].Index 312 } 313 return fmt.Sprintf("If %s → b%d b%d", relName(s.Cond, s), tblock, fblock) 314 } 315 316 func (s *ConstantSwitch) String() string { 317 var b bytes.Buffer 318 fmt.Fprintf(&b, "ConstantSwitch %s", relName(s.Tag, s)) 319 for _, cond := range s.Conds { 320 fmt.Fprintf(&b, " %s", relName(cond, s)) 321 } 322 fmt.Fprint(&b, " →") 323 for _, succ := range s.block.Succs { 324 fmt.Fprintf(&b, " b%d", succ.Index) 325 } 326 return b.String() 327 } 328 329 func (v *CompositeValue) String() string { 330 var b bytes.Buffer 331 from := v.Parent().pkg() 332 fmt.Fprintf(&b, "CompositeValue <%s>", relType(v.Type(), from)) 333 if v.NumSet >= len(v.Values) { 334 // All values provided 335 fmt.Fprint(&b, " [all]") 336 } else if v.Bitmap.BitLen() == 0 { 337 // No values provided 338 fmt.Fprint(&b, " [none]") 339 } else { 340 // Some values provided 341 bits := []byte(fmt.Sprintf("%0*b", len(v.Values), &v.Bitmap)) 342 for i := 0; i < len(bits)/2; i++ { 343 o := len(bits) - 1 - i 344 bits[i], bits[o] = bits[o], bits[i] 345 } 346 fmt.Fprintf(&b, " [%s]", bits) 347 } 348 for _, vv := range v.Values { 349 fmt.Fprintf(&b, " %s", relName(vv, v)) 350 } 351 return b.String() 352 } 353 354 func (s *TypeSwitch) String() string { 355 from := s.Parent().pkg() 356 var b bytes.Buffer 357 fmt.Fprintf(&b, "TypeSwitch <%s> %s", relType(s.typ, from), relName(s.Tag, s)) 358 for _, cond := range s.Conds { 359 fmt.Fprintf(&b, " %q", relType(cond, s.block.parent.pkg())) 360 } 361 return b.String() 362 } 363 364 func (s *Go) String() string { 365 return printCall(&s.Call, "Go", s) 366 } 367 368 func (s *Panic) String() string { 369 // Be robust against malformed CFG. 370 block := -1 371 if s.block != nil && len(s.block.Succs) == 1 { 372 block = s.block.Succs[0].Index 373 } 374 return fmt.Sprintf("Panic %s → b%d", relName(s.X, s), block) 375 } 376 377 func (s *Return) String() string { 378 var b bytes.Buffer 379 b.WriteString("Return") 380 for _, r := range s.Results { 381 b.WriteString(" ") 382 b.WriteString(relName(r, s)) 383 } 384 return b.String() 385 } 386 387 func (*RunDefers) String() string { 388 return "RunDefers" 389 } 390 391 func (s *Send) String() string { 392 return fmt.Sprintf("Send %s %s", relName(s.Chan, s), relName(s.X, s)) 393 } 394 395 func (recv *Recv) String() string { 396 from := recv.Parent().pkg() 397 return fmt.Sprintf("Recv <%s> %s", relType(recv.Type(), from), relName(recv.Chan, recv)) 398 } 399 400 func (s *Defer) String() string { 401 return printCall(&s.Call, "Defer", s) 402 } 403 404 func (s *Select) String() string { 405 var b bytes.Buffer 406 for i, st := range s.States { 407 if i > 0 { 408 b.WriteString(", ") 409 } 410 if st.Dir == types.RecvOnly { 411 b.WriteString("<-") 412 b.WriteString(relName(st.Chan, s)) 413 } else { 414 b.WriteString(relName(st.Chan, s)) 415 b.WriteString("<-") 416 b.WriteString(relName(st.Send, s)) 417 } 418 } 419 non := "" 420 if !s.Blocking { 421 non = "Non" 422 } 423 from := s.Parent().pkg() 424 return fmt.Sprintf("Select%sBlocking <%s> [%s]", non, relType(s.Type(), from), b.String()) 425 } 426 427 func (s *Store) String() string { 428 return fmt.Sprintf("Store {%s} %s %s", 429 s.Val.Type(), relName(s.Addr, s), relName(s.Val, s)) 430 } 431 432 func (s *BlankStore) String() string { 433 return fmt.Sprintf("BlankStore %s", relName(s.Val, s)) 434 } 435 436 func (s *MapUpdate) String() string { 437 return fmt.Sprintf("MapUpdate %s %s %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s)) 438 } 439 440 func (s *DebugRef) String() string { 441 p := s.Parent().Prog.Fset.Position(s.Pos()) 442 var descr interface{} 443 if s.object != nil { 444 descr = s.object // e.g. "var x int" 445 } else { 446 descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr" 447 } 448 var addr string 449 if s.IsAddr { 450 addr = "address of " 451 } 452 return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name()) 453 } 454 455 func (p *Package) String() string { 456 return "package " + p.Pkg.Path() 457 } 458 459 var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer 460 461 func (p *Package) WriteTo(w io.Writer) (int64, error) { 462 var buf bytes.Buffer 463 WritePackage(&buf, p) 464 n, err := w.Write(buf.Bytes()) 465 return int64(n), err 466 } 467 468 // WritePackage writes to buf a human-readable summary of p. 469 func WritePackage(buf *bytes.Buffer, p *Package) { 470 fmt.Fprintf(buf, "%s:\n", p) 471 472 var names []string 473 maxname := 0 474 for name := range p.Members { 475 if l := len(name); l > maxname { 476 maxname = l 477 } 478 names = append(names, name) 479 } 480 481 from := p.Pkg 482 sort.Strings(names) 483 for _, name := range names { 484 switch mem := p.Members[name].(type) { 485 case *NamedConst: 486 fmt.Fprintf(buf, " const %-*s %s = %s\n", 487 maxname, name, mem.Name(), mem.Value.RelString(from)) 488 489 case *Function: 490 fmt.Fprintf(buf, " func %-*s %s\n", 491 maxname, name, relType(mem.Type(), from)) 492 493 case *Type: 494 fmt.Fprintf(buf, " type %-*s %s\n", 495 maxname, name, relType(mem.Type().Underlying(), from)) 496 for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) { 497 fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from))) 498 } 499 500 case *Global: 501 fmt.Fprintf(buf, " var %-*s %s\n", 502 maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from)) 503 } 504 } 505 506 fmt.Fprintf(buf, "\n") 507 }