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