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.

285 lines
8.6 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/token"
  9. "io"
  10. "golang.org/x/tools/container/intsets"
  11. "golang.org/x/tools/go/callgraph"
  12. "golang.org/x/tools/go/ssa"
  13. "golang.org/x/tools/go/types/typeutil"
  14. )
  15. // A Config formulates a pointer analysis problem for Analyze. It is
  16. // only usable for a single invocation of Analyze and must not be
  17. // reused.
  18. type Config struct {
  19. // Mains contains the set of 'main' packages to analyze
  20. // Clients must provide the analysis with at least one
  21. // package defining a main() function.
  22. //
  23. // Non-main packages in the ssa.Program that are not
  24. // dependencies of any main package may still affect the
  25. // analysis result, because they contribute runtime types and
  26. // thus methods.
  27. // TODO(adonovan): investigate whether this is desirable.
  28. Mains []*ssa.Package
  29. // Reflection determines whether to handle reflection
  30. // operators soundly, which is currently rather slow since it
  31. // causes constraint to be generated during solving
  32. // proportional to the number of constraint variables, which
  33. // has not yet been reduced by presolver optimisation.
  34. Reflection bool
  35. // BuildCallGraph determines whether to construct a callgraph.
  36. // If enabled, the graph will be available in Result.CallGraph.
  37. BuildCallGraph bool
  38. // The client populates Queries[v] or IndirectQueries[v]
  39. // for each ssa.Value v of interest, to request that the
  40. // points-to sets pts(v) or pts(*v) be computed. If the
  41. // client needs both points-to sets, v may appear in both
  42. // maps.
  43. //
  44. // (IndirectQueries is typically used for Values corresponding
  45. // to source-level lvalues, e.g. an *ssa.Global.)
  46. //
  47. // The analysis populates the corresponding
  48. // Result.{Indirect,}Queries map when it creates the pointer
  49. // variable for v or *v. Upon completion the client can
  50. // inspect that map for the results.
  51. //
  52. // TODO(adonovan): this API doesn't scale well for batch tools
  53. // that want to dump the entire solution. Perhaps optionally
  54. // populate a map[*ssa.DebugRef]Pointer in the Result, one
  55. // entry per source expression.
  56. //
  57. Queries map[ssa.Value]struct{}
  58. IndirectQueries map[ssa.Value]struct{}
  59. extendedQueries map[ssa.Value][]*extendedQuery
  60. // If Log is non-nil, log messages are written to it.
  61. // Logging is extremely verbose.
  62. Log io.Writer
  63. }
  64. type track uint32
  65. const (
  66. trackChan track = 1 << iota // track 'chan' references
  67. trackMap // track 'map' references
  68. trackPtr // track regular pointers
  69. trackSlice // track slice references
  70. trackAll = ^track(0)
  71. )
  72. // AddQuery adds v to Config.Queries.
  73. // Precondition: CanPoint(v.Type()).
  74. func (c *Config) AddQuery(v ssa.Value) {
  75. if !CanPoint(v.Type()) {
  76. panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type()))
  77. }
  78. if c.Queries == nil {
  79. c.Queries = make(map[ssa.Value]struct{})
  80. }
  81. c.Queries[v] = struct{}{}
  82. }
  83. // AddQuery adds v to Config.IndirectQueries.
  84. // Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()).
  85. func (c *Config) AddIndirectQuery(v ssa.Value) {
  86. if c.IndirectQueries == nil {
  87. c.IndirectQueries = make(map[ssa.Value]struct{})
  88. }
  89. if !CanPoint(mustDeref(v.Type())) {
  90. panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type()))
  91. }
  92. c.IndirectQueries[v] = struct{}{}
  93. }
  94. // AddExtendedQuery adds an extended, AST-based query on v to the
  95. // analysis. The query, which must be a single Go expression, allows
  96. // destructuring the value.
  97. //
  98. // The query must operate on a variable named 'x', which represents
  99. // the value, and result in a pointer-like object. Only a subset of
  100. // Go expressions are permitted in queries, namely channel receives,
  101. // pointer dereferences, field selectors, array/slice/map/tuple
  102. // indexing and grouping with parentheses. The specific indices when
  103. // indexing arrays, slices and maps have no significance. Indices used
  104. // on tuples must be numeric and within bounds.
  105. //
  106. // All field selectors must be explicit, even ones usually elided
  107. // due to promotion of embedded fields.
  108. //
  109. // The query 'x' is identical to using AddQuery. The query '*x' is
  110. // identical to using AddIndirectQuery.
  111. //
  112. // On success, AddExtendedQuery returns a Pointer to the queried
  113. // value. This Pointer will be initialized during analysis. Using it
  114. // before analysis has finished has undefined behavior.
  115. //
  116. // Example:
  117. // // given v, which represents a function call to 'fn() (int, []*T)', and
  118. // // 'type T struct { F *int }', the following query will access the field F.
  119. // c.AddExtendedQuery(v, "x[1][0].F")
  120. func (c *Config) AddExtendedQuery(v ssa.Value, query string) (*Pointer, error) {
  121. ops, _, err := parseExtendedQuery(v.Type().Underlying(), query)
  122. if err != nil {
  123. return nil, fmt.Errorf("invalid query %q: %s", query, err)
  124. }
  125. if c.extendedQueries == nil {
  126. c.extendedQueries = make(map[ssa.Value][]*extendedQuery)
  127. }
  128. ptr := &Pointer{}
  129. c.extendedQueries[v] = append(c.extendedQueries[v], &extendedQuery{ops: ops, ptr: ptr})
  130. return ptr, nil
  131. }
  132. func (c *Config) prog() *ssa.Program {
  133. for _, main := range c.Mains {
  134. return main.Prog
  135. }
  136. panic("empty scope")
  137. }
  138. type Warning struct {
  139. Pos token.Pos
  140. Message string
  141. }
  142. // A Result contains the results of a pointer analysis.
  143. //
  144. // See Config for how to request the various Result components.
  145. //
  146. type Result struct {
  147. CallGraph *callgraph.Graph // discovered call graph
  148. Queries map[ssa.Value]Pointer // pts(v) for each v in Config.Queries.
  149. IndirectQueries map[ssa.Value]Pointer // pts(*v) for each v in Config.IndirectQueries.
  150. Warnings []Warning // warnings of unsoundness
  151. }
  152. // A Pointer is an equivalence class of pointer-like values.
  153. //
  154. // A Pointer doesn't have a unique type because pointers of distinct
  155. // types may alias the same object.
  156. //
  157. type Pointer struct {
  158. a *analysis
  159. n nodeid
  160. }
  161. // A PointsToSet is a set of labels (locations or allocations).
  162. type PointsToSet struct {
  163. a *analysis // may be nil if pts is nil
  164. pts *nodeset
  165. }
  166. func (s PointsToSet) String() string {
  167. var buf bytes.Buffer
  168. buf.WriteByte('[')
  169. if s.pts != nil {
  170. var space [50]int
  171. for i, l := range s.pts.AppendTo(space[:0]) {
  172. if i > 0 {
  173. buf.WriteString(", ")
  174. }
  175. buf.WriteString(s.a.labelFor(nodeid(l)).String())
  176. }
  177. }
  178. buf.WriteByte(']')
  179. return buf.String()
  180. }
  181. // PointsTo returns the set of labels that this points-to set
  182. // contains.
  183. func (s PointsToSet) Labels() []*Label {
  184. var labels []*Label
  185. if s.pts != nil {
  186. var space [50]int
  187. for _, l := range s.pts.AppendTo(space[:0]) {
  188. labels = append(labels, s.a.labelFor(nodeid(l)))
  189. }
  190. }
  191. return labels
  192. }
  193. // If this PointsToSet came from a Pointer of interface kind
  194. // or a reflect.Value, DynamicTypes returns the set of dynamic
  195. // types that it may contain. (For an interface, they will
  196. // always be concrete types.)
  197. //
  198. // The result is a mapping whose keys are the dynamic types to which
  199. // it may point. For each pointer-like key type, the corresponding
  200. // map value is the PointsToSet for pointers of that type.
  201. //
  202. // The result is empty unless CanHaveDynamicTypes(T).
  203. //
  204. func (s PointsToSet) DynamicTypes() *typeutil.Map {
  205. var tmap typeutil.Map
  206. tmap.SetHasher(s.a.hasher)
  207. if s.pts != nil {
  208. var space [50]int
  209. for _, x := range s.pts.AppendTo(space[:0]) {
  210. ifaceObjId := nodeid(x)
  211. if !s.a.isTaggedObject(ifaceObjId) {
  212. continue // !CanHaveDynamicTypes(tDyn)
  213. }
  214. tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
  215. if indirect {
  216. panic("indirect tagged object") // implement later
  217. }
  218. pts, ok := tmap.At(tDyn).(PointsToSet)
  219. if !ok {
  220. pts = PointsToSet{s.a, new(nodeset)}
  221. tmap.Set(tDyn, pts)
  222. }
  223. pts.pts.addAll(&s.a.nodes[v].solve.pts)
  224. }
  225. }
  226. return &tmap
  227. }
  228. // Intersects reports whether this points-to set and the
  229. // argument points-to set contain common members.
  230. func (x PointsToSet) Intersects(y PointsToSet) bool {
  231. if x.pts == nil || y.pts == nil {
  232. return false
  233. }
  234. // This takes Θ(|x|+|y|) time.
  235. var z intsets.Sparse
  236. z.Intersection(&x.pts.Sparse, &y.pts.Sparse)
  237. return !z.IsEmpty()
  238. }
  239. func (p Pointer) String() string {
  240. return fmt.Sprintf("n%d", p.n)
  241. }
  242. // PointsTo returns the points-to set of this pointer.
  243. func (p Pointer) PointsTo() PointsToSet {
  244. if p.n == 0 {
  245. return PointsToSet{}
  246. }
  247. return PointsToSet{p.a, &p.a.nodes[p.n].solve.pts}
  248. }
  249. // MayAlias reports whether the receiver pointer may alias
  250. // the argument pointer.
  251. func (p Pointer) MayAlias(q Pointer) bool {
  252. return p.PointsTo().Intersects(q.PointsTo())
  253. }
  254. // DynamicTypes returns p.PointsTo().DynamicTypes().
  255. func (p Pointer) DynamicTypes() *typeutil.Map {
  256. return p.PointsTo().DynamicTypes()
  257. }