GoPLS Viewer

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