| 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 pointer |
| 6 | |
| 7 | import ( |
| 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 | type Label struct { |
| 37 | obj *object // the addressable memory location containing this label |
| 38 | subelement *fieldInfo // subelement path within obj, e.g. ".a.b[*].c" |
| 39 | } |
| 40 | |
| 41 | // Value returns the ssa.Value that allocated this label's object, if any. |
| 42 | func (l Label) Value() ssa.Value { |
| 43 | val, _ := l.obj.data.(ssa.Value) |
| 44 | return val |
| 45 | } |
| 46 | |
| 47 | // ReflectType returns the type represented by this label if it is an |
| 48 | // reflect.rtype instance object or *reflect.rtype-tagged object. |
| 49 | func (l Label) ReflectType() types.Type { |
| 50 | rtype, _ := l.obj.data.(types.Type) |
| 51 | return rtype |
| 52 | } |
| 53 | |
| 54 | // Path returns the path to the subelement of the object containing |
| 55 | // this label. For example, ".x[*].y". |
| 56 | func (l Label) Path() string { |
| 57 | return l.subelement.path() |
| 58 | } |
| 59 | |
| 60 | // Pos returns the position of this label, if known, zero otherwise. |
| 61 | func (l Label) Pos() token.Pos { |
| 62 | switch data := l.obj.data.(type) { |
| 63 | case ssa.Value: |
| 64 | return data.Pos() |
| 65 | case types.Type: |
| 66 | if nt, ok := deref(data).(*types.Named); ok { |
| 67 | return nt.Obj().Pos() |
| 68 | } |
| 69 | } |
| 70 | if cgn := l.obj.cgn; cgn != nil { |
| 71 | return cgn.fn.Pos() |
| 72 | } |
| 73 | return token.NoPos |
| 74 | } |
| 75 | |
| 76 | // String returns the printed form of this label. |
| 77 | // |
| 78 | // Examples: Object type: |
| 79 | // |
| 80 | // x (a variable) |
| 81 | // (sync.Mutex).Lock (a function) |
| 82 | // convert (array created by conversion) |
| 83 | // makemap (map allocated via make) |
| 84 | // makechan (channel allocated via make) |
| 85 | // makeinterface (tagged object allocated by makeinterface) |
| 86 | // <alloc in reflect.Zero> (allocation in instrinsic) |
| 87 | // sync.Mutex (a reflect.rtype instance) |
| 88 | // <command-line arguments> (an intrinsic object) |
| 89 | // |
| 90 | // Labels within compound objects have subelement paths: |
| 91 | // |
| 92 | // x.y[*].z (a struct variable, x) |
| 93 | // append.y[*].z (array allocated by append) |
| 94 | // makeslice.y[*].z (array allocated via make) |
| 95 | // |
| 96 | // TODO(adonovan): expose func LabelString(*types.Package, Label). |
| 97 | func (l Label) String() string { |
| 98 | var s string |
| 99 | switch v := l.obj.data.(type) { |
| 100 | case types.Type: |
| 101 | return v.String() |
| 102 | |
| 103 | case string: |
| 104 | s = v // an intrinsic object (e.g. os.Args[*]) |
| 105 | |
| 106 | case nil: |
| 107 | if l.obj.cgn != nil { |
| 108 | // allocation by intrinsic or reflective operation |
| 109 | s = fmt.Sprintf("<alloc in %s>", l.obj.cgn.fn) |
| 110 | } else { |
| 111 | s = "<unknown>" // should be unreachable |
| 112 | } |
| 113 | |
| 114 | case *ssa.Function: |
| 115 | s = v.String() |
| 116 | |
| 117 | case *ssa.Global: |
| 118 | s = v.String() |
| 119 | |
| 120 | case *ssa.Const: |
| 121 | s = v.Name() |
| 122 | |
| 123 | case *ssa.Alloc: |
| 124 | s = v.Comment |
| 125 | if s == "" { |
| 126 | s = "alloc" |
| 127 | } |
| 128 | |
| 129 | case *ssa.Call: |
| 130 | // Currently only calls to append can allocate objects. |
| 131 | if v.Call.Value.(*ssa.Builtin).Object().Name() != "append" { |
| 132 | panic("unhandled *ssa.Call label: " + v.Name()) |
| 133 | } |
| 134 | s = "append" |
| 135 | |
| 136 | case *ssa.MakeMap, *ssa.MakeChan, *ssa.MakeSlice, *ssa.Convert: |
| 137 | s = strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")) |
| 138 | |
| 139 | case *ssa.MakeInterface: |
| 140 | // MakeInterface is usually implicit in Go source (so |
| 141 | // Pos()==0), and tagged objects may be allocated |
| 142 | // synthetically (so no *MakeInterface data). |
| 143 | s = "makeinterface:" + v.X.Type().String() |
| 144 | |
| 145 | default: |
| 146 | panic(fmt.Sprintf("unhandled object data type: %T", v)) |
| 147 | } |
| 148 | |
| 149 | return s + l.subelement.path() |
| 150 | } |
| 151 |
Members