github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/pointer/util.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 // +build go1.5 6 7 package pointer 8 9 import ( 10 "bytes" 11 "fmt" 12 "go/types" 13 "log" 14 "os" 15 "os/exec" 16 "runtime" 17 "time" 18 19 "golang.org/x/tools/container/intsets" 20 ) 21 22 // CanPoint reports whether the type T is pointerlike, 23 // for the purposes of this analysis. 24 func CanPoint(T types.Type) bool { 25 switch T := T.(type) { 26 case *types.Named: 27 if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { 28 return true // treat reflect.Value like interface{} 29 } 30 return CanPoint(T.Underlying()) 31 32 case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice: 33 return true 34 } 35 36 return false // array struct tuple builtin basic 37 } 38 39 // CanHaveDynamicTypes reports whether the type T can "hold" dynamic types, 40 // i.e. is an interface (incl. reflect.Type) or a reflect.Value. 41 // 42 func CanHaveDynamicTypes(T types.Type) bool { 43 switch T := T.(type) { 44 case *types.Named: 45 if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { 46 return true // reflect.Value 47 } 48 return CanHaveDynamicTypes(T.Underlying()) 49 case *types.Interface: 50 return true 51 } 52 return false 53 } 54 55 func isInterface(T types.Type) bool { return types.IsInterface(T) } 56 57 // mustDeref returns the element type of its argument, which must be a 58 // pointer; panic ensues otherwise. 59 func mustDeref(typ types.Type) types.Type { 60 return typ.Underlying().(*types.Pointer).Elem() 61 } 62 63 // deref returns a pointer's element type; otherwise it returns typ. 64 func deref(typ types.Type) types.Type { 65 if p, ok := typ.Underlying().(*types.Pointer); ok { 66 return p.Elem() 67 } 68 return typ 69 } 70 71 // A fieldInfo describes one subelement (node) of the flattening-out 72 // of a type T: the subelement's type and its path from the root of T. 73 // 74 // For example, for this type: 75 // type line struct{ points []struct{x, y int} } 76 // flatten() of the inner struct yields the following []fieldInfo: 77 // struct{ x, y int } "" 78 // int ".x" 79 // int ".y" 80 // and flatten(line) yields: 81 // struct{ points []struct{x, y int} } "" 82 // struct{ x, y int } ".points[*]" 83 // int ".points[*].x 84 // int ".points[*].y" 85 // 86 type fieldInfo struct { 87 typ types.Type 88 89 // op and tail describe the path to the element (e.g. ".a#2.b[*].c"). 90 op interface{} // *Array: true; *Tuple: int; *Struct: *types.Var; *Named: nil 91 tail *fieldInfo 92 } 93 94 // path returns a user-friendly string describing the subelement path. 95 // 96 func (fi *fieldInfo) path() string { 97 var buf bytes.Buffer 98 for p := fi; p != nil; p = p.tail { 99 switch op := p.op.(type) { 100 case bool: 101 fmt.Fprintf(&buf, "[*]") 102 case int: 103 fmt.Fprintf(&buf, "#%d", op) 104 case *types.Var: 105 fmt.Fprintf(&buf, ".%s", op.Name()) 106 } 107 } 108 return buf.String() 109 } 110 111 // flatten returns a list of directly contained fields in the preorder 112 // traversal of the type tree of t. The resulting elements are all 113 // scalars (basic types or pointerlike types), except for struct/array 114 // "identity" nodes, whose type is that of the aggregate. 115 // 116 // reflect.Value is considered pointerlike, similar to interface{}. 117 // 118 // Callers must not mutate the result. 119 // 120 func (a *analysis) flatten(t types.Type) []*fieldInfo { 121 fl, ok := a.flattenMemo[t] 122 if !ok { 123 switch t := t.(type) { 124 case *types.Named: 125 u := t.Underlying() 126 if isInterface(u) { 127 // Debuggability hack: don't remove 128 // the named type from interfaces as 129 // they're very verbose. 130 fl = append(fl, &fieldInfo{typ: t}) 131 } else { 132 fl = a.flatten(u) 133 } 134 135 case *types.Basic, 136 *types.Signature, 137 *types.Chan, 138 *types.Map, 139 *types.Interface, 140 *types.Slice, 141 *types.Pointer: 142 fl = append(fl, &fieldInfo{typ: t}) 143 144 case *types.Array: 145 fl = append(fl, &fieldInfo{typ: t}) // identity node 146 for _, fi := range a.flatten(t.Elem()) { 147 fl = append(fl, &fieldInfo{typ: fi.typ, op: true, tail: fi}) 148 } 149 150 case *types.Struct: 151 fl = append(fl, &fieldInfo{typ: t}) // identity node 152 for i, n := 0, t.NumFields(); i < n; i++ { 153 f := t.Field(i) 154 for _, fi := range a.flatten(f.Type()) { 155 fl = append(fl, &fieldInfo{typ: fi.typ, op: f, tail: fi}) 156 } 157 } 158 159 case *types.Tuple: 160 // No identity node: tuples are never address-taken. 161 n := t.Len() 162 if n == 1 { 163 // Don't add a fieldInfo link for singletons, 164 // e.g. in params/results. 165 fl = append(fl, a.flatten(t.At(0).Type())...) 166 } else { 167 for i := 0; i < n; i++ { 168 f := t.At(i) 169 for _, fi := range a.flatten(f.Type()) { 170 fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi}) 171 } 172 } 173 } 174 175 default: 176 panic(t) 177 } 178 179 a.flattenMemo[t] = fl 180 } 181 182 return fl 183 } 184 185 // sizeof returns the number of pointerlike abstractions (nodes) in the type t. 186 func (a *analysis) sizeof(t types.Type) uint32 { 187 return uint32(len(a.flatten(t))) 188 } 189 190 // shouldTrack reports whether object type T contains (recursively) 191 // any fields whose addresses should be tracked. 192 func (a *analysis) shouldTrack(T types.Type) bool { 193 if a.track == trackAll { 194 return true // fast path 195 } 196 track, ok := a.trackTypes[T] 197 if !ok { 198 a.trackTypes[T] = true // break cycles conservatively 199 // NB: reflect.Value, reflect.Type are pre-populated to true. 200 for _, fi := range a.flatten(T) { 201 switch ft := fi.typ.Underlying().(type) { 202 case *types.Interface, *types.Signature: 203 track = true // needed for callgraph 204 case *types.Basic: 205 // no-op 206 case *types.Chan: 207 track = a.track&trackChan != 0 || a.shouldTrack(ft.Elem()) 208 case *types.Map: 209 track = a.track&trackMap != 0 || a.shouldTrack(ft.Key()) || a.shouldTrack(ft.Elem()) 210 case *types.Slice: 211 track = a.track&trackSlice != 0 || a.shouldTrack(ft.Elem()) 212 case *types.Pointer: 213 track = a.track&trackPtr != 0 || a.shouldTrack(ft.Elem()) 214 case *types.Array, *types.Struct: 215 // No need to look at field types since they will follow (flattened). 216 default: 217 // Includes *types.Tuple, which are never address-taken. 218 panic(ft) 219 } 220 if track { 221 break 222 } 223 } 224 a.trackTypes[T] = track 225 if !track && a.log != nil { 226 fmt.Fprintf(a.log, "\ttype not tracked: %s\n", T) 227 } 228 } 229 return track 230 } 231 232 // offsetOf returns the (abstract) offset of field index within struct 233 // or tuple typ. 234 func (a *analysis) offsetOf(typ types.Type, index int) uint32 { 235 var offset uint32 236 switch t := typ.Underlying().(type) { 237 case *types.Tuple: 238 for i := 0; i < index; i++ { 239 offset += a.sizeof(t.At(i).Type()) 240 } 241 case *types.Struct: 242 offset++ // the node for the struct itself 243 for i := 0; i < index; i++ { 244 offset += a.sizeof(t.Field(i).Type()) 245 } 246 default: 247 panic(fmt.Sprintf("offsetOf(%s : %T)", typ, typ)) 248 } 249 return offset 250 } 251 252 // sliceToArray returns the type representing the arrays to which 253 // slice type slice points. 254 func sliceToArray(slice types.Type) *types.Array { 255 return types.NewArray(slice.Underlying().(*types.Slice).Elem(), 1) 256 } 257 258 // Node set ------------------------------------------------------------------- 259 260 type nodeset struct { 261 intsets.Sparse 262 } 263 264 func (ns *nodeset) String() string { 265 var buf bytes.Buffer 266 buf.WriteRune('{') 267 var space [50]int 268 for i, n := range ns.AppendTo(space[:0]) { 269 if i > 0 { 270 buf.WriteString(", ") 271 } 272 buf.WriteRune('n') 273 fmt.Fprintf(&buf, "%d", n) 274 } 275 buf.WriteRune('}') 276 return buf.String() 277 } 278 279 func (ns *nodeset) add(n nodeid) bool { 280 return ns.Sparse.Insert(int(n)) 281 } 282 283 func (x *nodeset) addAll(y *nodeset) bool { 284 return x.UnionWith(&y.Sparse) 285 } 286 287 // Profiling & debugging ------------------------------------------------------- 288 289 var timers = make(map[string]time.Time) 290 291 func start(name string) { 292 if debugTimers { 293 timers[name] = time.Now() 294 log.Printf("%s...\n", name) 295 } 296 } 297 298 func stop(name string) { 299 if debugTimers { 300 log.Printf("%s took %s\n", name, time.Since(timers[name])) 301 } 302 } 303 304 // diff runs the command "diff a b" and reports its success. 305 func diff(a, b string) bool { 306 var cmd *exec.Cmd 307 switch runtime.GOOS { 308 case "plan9": 309 cmd = exec.Command("/bin/diff", "-c", a, b) 310 default: 311 cmd = exec.Command("/usr/bin/diff", "-u", a, b) 312 } 313 cmd.Stdout = os.Stderr 314 cmd.Stderr = os.Stderr 315 return cmd.Run() == nil 316 }