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