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.
17 "golang.org/x/tools/container/intsets"
20 // CanPoint reports whether the type T is pointerlike,
21 // for the purposes of this analysis.
22 func CanPoint(T types.Type) bool {
23 switch T := T.(type) {
25 if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
26 return true // treat reflect.Value like interface{}
28 return CanPoint(T.Underlying())
29 case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice:
33 return false // array struct tuple builtin basic
36 // CanHaveDynamicTypes reports whether the type T can "hold" dynamic types,
37 // i.e. is an interface (incl. reflect.Type) or a reflect.Value.
39 func CanHaveDynamicTypes(T types.Type) bool {
40 switch T := T.(type) {
42 if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
43 return true // reflect.Value
45 return CanHaveDynamicTypes(T.Underlying())
46 case *types.Interface:
52 func isInterface(T types.Type) bool { return types.IsInterface(T) }
54 // mustDeref returns the element type of its argument, which must be a
55 // pointer; panic ensues otherwise.
56 func mustDeref(typ types.Type) types.Type {
57 return typ.Underlying().(*types.Pointer).Elem()
60 // deref returns a pointer's element type; otherwise it returns typ.
61 func deref(typ types.Type) types.Type {
62 if p, ok := typ.Underlying().(*types.Pointer); ok {
68 // A fieldInfo describes one subelement (node) of the flattening-out
69 // of a type T: the subelement's type and its path from the root of T.
71 // For example, for this type:
72 // type line struct{ points []struct{x, y int} }
73 // flatten() of the inner struct yields the following []fieldInfo:
74 // struct{ x, y int } ""
77 // and flatten(line) yields:
78 // struct{ points []struct{x, y int} } ""
79 // struct{ x, y int } ".points[*]"
83 type fieldInfo struct {
86 // op and tail describe the path to the element (e.g. ".a#2.b[*].c").
87 op interface{} // *Array: true; *Tuple: int; *Struct: *types.Var; *Named: nil
91 // path returns a user-friendly string describing the subelement path.
93 func (fi *fieldInfo) path() string {
95 for p := fi; p != nil; p = p.tail {
96 switch op := p.op.(type) {
98 fmt.Fprintf(&buf, "[*]")
100 fmt.Fprintf(&buf, "#%d", op)
102 fmt.Fprintf(&buf, ".%s", op.Name())
108 // flatten returns a list of directly contained fields in the preorder
109 // traversal of the type tree of t. The resulting elements are all
110 // scalars (basic types or pointerlike types), except for struct/array
111 // "identity" nodes, whose type is that of the aggregate.
113 // reflect.Value is considered pointerlike, similar to interface{}.
115 // Callers must not mutate the result.
117 func (a *analysis) flatten(t types.Type) []*fieldInfo {
118 fl, ok := a.flattenMemo[t]
120 switch t := t.(type) {
124 // Debuggability hack: don't remove
125 // the named type from interfaces as
126 // they're very verbose.
127 fl = append(fl, &fieldInfo{typ: t})
139 fl = append(fl, &fieldInfo{typ: t})
142 fl = append(fl, &fieldInfo{typ: t}) // identity node
143 for _, fi := range a.flatten(t.Elem()) {
144 fl = append(fl, &fieldInfo{typ: fi.typ, op: true, tail: fi})
148 fl = append(fl, &fieldInfo{typ: t}) // identity node
149 for i, n := 0, t.NumFields(); i < n; i++ {
151 for _, fi := range a.flatten(f.Type()) {
152 fl = append(fl, &fieldInfo{typ: fi.typ, op: f, tail: fi})
157 // No identity node: tuples are never address-taken.
160 // Don't add a fieldInfo link for singletons,
161 // e.g. in params/results.
162 fl = append(fl, a.flatten(t.At(0).Type())...)
164 for i := 0; i < n; i++ {
166 for _, fi := range a.flatten(f.Type()) {
167 fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi})
173 panic(fmt.Sprintf("cannot flatten unsupported type %T", t))
176 a.flattenMemo[t] = fl
182 // sizeof returns the number of pointerlike abstractions (nodes) in the type t.
183 func (a *analysis) sizeof(t types.Type) uint32 {
184 return uint32(len(a.flatten(t)))
187 // shouldTrack reports whether object type T contains (recursively)
188 // any fields whose addresses should be tracked.
189 func (a *analysis) shouldTrack(T types.Type) bool {
190 if a.track == trackAll {
191 return true // fast path
193 track, ok := a.trackTypes[T]
195 a.trackTypes[T] = true // break cycles conservatively
196 // NB: reflect.Value, reflect.Type are pre-populated to true.
197 for _, fi := range a.flatten(T) {
198 switch ft := fi.typ.Underlying().(type) {
199 case *types.Interface, *types.Signature:
200 track = true // needed for callgraph
204 track = a.track&trackChan != 0 || a.shouldTrack(ft.Elem())
206 track = a.track&trackMap != 0 || a.shouldTrack(ft.Key()) || a.shouldTrack(ft.Elem())
208 track = a.track&trackSlice != 0 || a.shouldTrack(ft.Elem())
210 track = a.track&trackPtr != 0 || a.shouldTrack(ft.Elem())
211 case *types.Array, *types.Struct:
212 // No need to look at field types since they will follow (flattened).
214 // Includes *types.Tuple, which are never address-taken.
221 a.trackTypes[T] = track
222 if !track && a.log != nil {
223 fmt.Fprintf(a.log, "\ttype not tracked: %s\n", T)
229 // offsetOf returns the (abstract) offset of field index within struct
231 func (a *analysis) offsetOf(typ types.Type, index int) uint32 {
233 switch t := typ.Underlying().(type) {
235 for i := 0; i < index; i++ {
236 offset += a.sizeof(t.At(i).Type())
239 offset++ // the node for the struct itself
240 for i := 0; i < index; i++ {
241 offset += a.sizeof(t.Field(i).Type())
244 panic(fmt.Sprintf("offsetOf(%s : %T)", typ, typ))
249 // sliceToArray returns the type representing the arrays to which
250 // slice type slice points.
251 func sliceToArray(slice types.Type) *types.Array {
252 return types.NewArray(slice.Underlying().(*types.Slice).Elem(), 1)
255 // Node set -------------------------------------------------------------------
257 type nodeset struct {
261 func (ns *nodeset) String() string {
265 for i, n := range ns.AppendTo(space[:0]) {
267 buf.WriteString(", ")
270 fmt.Fprintf(&buf, "%d", n)
276 func (ns *nodeset) add(n nodeid) bool {
277 return ns.Sparse.Insert(int(n))
280 func (ns *nodeset) addAll(y *nodeset) bool {
281 return ns.UnionWith(&y.Sparse)
284 // Profiling & debugging -------------------------------------------------------
286 var timers = make(map[string]time.Time)
288 func start(name string) {
290 timers[name] = time.Now()
291 log.Printf("%s...\n", name)
295 func stop(name string) {
297 log.Printf("%s took %s\n", name, time.Since(timers[name]))
301 // diff runs the command "diff a b" and reports its success.
302 func diff(a, b string) bool {
304 switch runtime.GOOS {
306 cmd = exec.Command("/bin/diff", "-c", a, b)
308 cmd = exec.Command("/usr/bin/diff", "-u", a, b)
310 cmd.Stdout = os.Stderr
311 cmd.Stderr = os.Stderr
312 return cmd.Run() == nil