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 5package pointer 6 7import ( 8 "fmt" 9 "go/token" 10 "go/types" 11 "strings" 12 13 "golang.org/x/tools/go/ssa" 14) 15 16// A Label is an entity that may be pointed to by a pointer, map, 17// channel, 'func', slice or interface. 18// 19// Labels include: 20// - functions 21// - globals 22// - tagged objects, representing interfaces and reflect.Values 23// - arrays created by conversions (e.g. []byte("foo"), []byte(s)) 24// - stack- and heap-allocated variables (including composite literals) 25// - channels, maps and arrays created by make() 26// - intrinsic or reflective operations that allocate (e.g. append, reflect.New) 27// - intrinsic objects, e.g. the initial array behind os.Args. 28// - and their subelements, e.g. "alloc.y[*].z" 29// 30// Labels are so varied that they defy good generalizations; 31// some have no value, no callgraph node, or no position. 32// Many objects have types that are inexpressible in Go: 33// maps, channels, functions, tagged objects. 34// 35// At most one of Value() or ReflectType() may return non-nil. 36// 37type Label struct { 38 obj *object // the addressable memory location containing this label 39 subelement *fieldInfo // subelement path within obj, e.g. ".a.b[*].c" 40} 41 42// Value returns the ssa.Value that allocated this label's object, if any. 43func (l Label) Value() ssa.Value { 44 val, _ := l.obj.data.(ssa.Value) 45 return val 46} 47 48// ReflectType returns the type represented by this label if it is an 49// reflect.rtype instance object or *reflect.rtype-tagged object. 50// 51func (l Label) ReflectType() types.Type { 52 rtype, _ := l.obj.data.(types.Type) 53 return rtype 54} 55 56// Path returns the path to the subelement of the object containing 57// this label. For example, ".x[*].y". 58// 59func (l Label) Path() string { 60 return l.subelement.path() 61} 62 63// Pos returns the position of this label, if known, zero otherwise. 64func (l Label) Pos() token.Pos { 65 switch data := l.obj.data.(type) { 66 case ssa.Value: 67 return data.Pos() 68 case types.Type: 69 if nt, ok := deref(data).(*types.Named); ok { 70 return nt.Obj().Pos() 71 } 72 } 73 if cgn := l.obj.cgn; cgn != nil { 74 return cgn.fn.Pos() 75 } 76 return token.NoPos 77} 78 79// String returns the printed form of this label. 80// 81// Examples: Object type: 82// x (a variable) 83// (sync.Mutex).Lock (a function) 84// convert (array created by conversion) 85// makemap (map allocated via make) 86// makechan (channel allocated via make) 87// makeinterface (tagged object allocated by makeinterface) 88// <alloc in reflect.Zero> (allocation in instrinsic) 89// sync.Mutex (a reflect.rtype instance) 90// <command-line arguments> (an intrinsic object) 91// 92// Labels within compound objects have subelement paths: 93// x.y[*].z (a struct variable, x) 94// append.y[*].z (array allocated by append) 95// makeslice.y[*].z (array allocated via make) 96// 97// TODO(adonovan): expose func LabelString(*types.Package, Label). 98// 99func (l Label) String() string { 100 var s string 101 switch v := l.obj.data.(type) { 102 case types.Type: 103 return v.String() 104 105 case string: 106 s = v // an intrinsic object (e.g. os.Args[*]) 107 108 case nil: 109 if l.obj.cgn != nil { 110 // allocation by intrinsic or reflective operation 111 s = fmt.Sprintf("<alloc in %s>", l.obj.cgn.fn) 112 } else { 113 s = "<unknown>" // should be unreachable 114 } 115 116 case *ssa.Function: 117 s = v.String() 118 119 case *ssa.Global: 120 s = v.String() 121 122 case *ssa.Const: 123 s = v.Name() 124 125 case *ssa.Alloc: 126 s = v.Comment 127 if s == "" { 128 s = "alloc" 129 } 130 131 case *ssa.Call: 132 // Currently only calls to append can allocate objects. 133 if v.Call.Value.(*ssa.Builtin).Object().Name() != "append" { 134 panic("unhandled *ssa.Call label: " + v.Name()) 135 } 136 s = "append" 137 138 case *ssa.MakeMap, *ssa.MakeChan, *ssa.MakeSlice, *ssa.Convert: 139 s = strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")) 140 141 case *ssa.MakeInterface: 142 // MakeInterface is usually implicit in Go source (so 143 // Pos()==0), and tagged objects may be allocated 144 // synthetically (so no *MakeInterface data). 145 s = "makeinterface:" + v.X.Type().String() 146 147 default: 148 panic(fmt.Sprintf("unhandled object data type: %T", v)) 149 } 150 151 return s + l.subelement.path() 152} 153