You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
8.2 KiB

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