github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/pointer/api14.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/token" 13 "io" 14 15 "golang.org/x/tools/container/intsets" 16 "golang.org/x/tools/go/callgraph" 17 "golang.org/x/tools/go/ssa" 18 "golang.org/x/tools/go/types/typeutil" 19 ) 20 21 // A Config formulates a pointer analysis problem for Analyze(). 22 type 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 67 // If Log is non-nil, log messages are written to it. 68 // Logging is extremely verbose. 69 Log io.Writer 70 } 71 72 type track uint32 73 74 const ( 75 trackChan track = 1 << iota // track 'chan' references 76 trackMap // track 'map' references 77 trackPtr // track regular pointers 78 trackSlice // track slice references 79 80 trackAll = ^track(0) 81 ) 82 83 // AddQuery adds v to Config.Queries. 84 // Precondition: CanPoint(v.Type()). 85 // TODO(adonovan): consider returning a new Pointer for this query, 86 // which will be initialized during analysis. That avoids the needs 87 // for the corresponding ssa.Value-keyed maps in Config and Result. 88 func (c *Config) AddQuery(v ssa.Value) { 89 if !CanPoint(v.Type()) { 90 panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type())) 91 } 92 if c.Queries == nil { 93 c.Queries = make(map[ssa.Value]struct{}) 94 } 95 c.Queries[v] = struct{}{} 96 } 97 98 // AddQuery adds v to Config.IndirectQueries. 99 // Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()). 100 func (c *Config) AddIndirectQuery(v ssa.Value) { 101 if c.IndirectQueries == nil { 102 c.IndirectQueries = make(map[ssa.Value]struct{}) 103 } 104 if !CanPoint(mustDeref(v.Type())) { 105 panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type())) 106 } 107 c.IndirectQueries[v] = struct{}{} 108 } 109 110 func (c *Config) prog() *ssa.Program { 111 for _, main := range c.Mains { 112 return main.Prog 113 } 114 panic("empty scope") 115 } 116 117 type Warning struct { 118 Pos token.Pos 119 Message string 120 } 121 122 // A Result contains the results of a pointer analysis. 123 // 124 // See Config for how to request the various Result components. 125 // 126 type Result struct { 127 CallGraph *callgraph.Graph // discovered call graph 128 Queries map[ssa.Value]Pointer // pts(v) for each v in Config.Queries. 129 IndirectQueries map[ssa.Value]Pointer // pts(*v) for each v in Config.IndirectQueries. 130 Warnings []Warning // warnings of unsoundness 131 } 132 133 // A Pointer is an equivalence class of pointer-like values. 134 // 135 // A Pointer doesn't have a unique type because pointers of distinct 136 // types may alias the same object. 137 // 138 type Pointer struct { 139 a *analysis 140 n nodeid 141 } 142 143 // A PointsToSet is a set of labels (locations or allocations). 144 type PointsToSet struct { 145 a *analysis // may be nil if pts is nil 146 pts *nodeset 147 } 148 149 func (s PointsToSet) String() string { 150 var buf bytes.Buffer 151 buf.WriteByte('[') 152 if s.pts != nil { 153 var space [50]int 154 for i, l := range s.pts.AppendTo(space[:0]) { 155 if i > 0 { 156 buf.WriteString(", ") 157 } 158 buf.WriteString(s.a.labelFor(nodeid(l)).String()) 159 } 160 } 161 buf.WriteByte(']') 162 return buf.String() 163 } 164 165 // PointsTo returns the set of labels that this points-to set 166 // contains. 167 func (s PointsToSet) Labels() []*Label { 168 var labels []*Label 169 if s.pts != nil { 170 var space [50]int 171 for _, l := range s.pts.AppendTo(space[:0]) { 172 labels = append(labels, s.a.labelFor(nodeid(l))) 173 } 174 } 175 return labels 176 } 177 178 // If this PointsToSet came from a Pointer of interface kind 179 // or a reflect.Value, DynamicTypes returns the set of dynamic 180 // types that it may contain. (For an interface, they will 181 // always be concrete types.) 182 // 183 // The result is a mapping whose keys are the dynamic types to which 184 // it may point. For each pointer-like key type, the corresponding 185 // map value is the PointsToSet for pointers of that type. 186 // 187 // The result is empty unless CanHaveDynamicTypes(T). 188 // 189 func (s PointsToSet) DynamicTypes() *typeutil.Map { 190 var tmap typeutil.Map 191 tmap.SetHasher(s.a.hasher) 192 if s.pts != nil { 193 var space [50]int 194 for _, x := range s.pts.AppendTo(space[:0]) { 195 ifaceObjId := nodeid(x) 196 if !s.a.isTaggedObject(ifaceObjId) { 197 continue // !CanHaveDynamicTypes(tDyn) 198 } 199 tDyn, v, indirect := s.a.taggedValue(ifaceObjId) 200 if indirect { 201 panic("indirect tagged object") // implement later 202 } 203 pts, ok := tmap.At(tDyn).(PointsToSet) 204 if !ok { 205 pts = PointsToSet{s.a, new(nodeset)} 206 tmap.Set(tDyn, pts) 207 } 208 pts.pts.addAll(&s.a.nodes[v].solve.pts) 209 } 210 } 211 return &tmap 212 } 213 214 // Intersects reports whether this points-to set and the 215 // argument points-to set contain common members. 216 func (x PointsToSet) Intersects(y PointsToSet) bool { 217 if x.pts == nil || y.pts == nil { 218 return false 219 } 220 // This takes Θ(|x|+|y|) time. 221 var z intsets.Sparse 222 z.Intersection(&x.pts.Sparse, &y.pts.Sparse) 223 return !z.IsEmpty() 224 } 225 226 func (p Pointer) String() string { 227 return fmt.Sprintf("n%d", p.n) 228 } 229 230 // PointsTo returns the points-to set of this pointer. 231 func (p Pointer) PointsTo() PointsToSet { 232 if p.n == 0 { 233 return PointsToSet{} 234 } 235 return PointsToSet{p.a, &p.a.nodes[p.n].solve.pts} 236 } 237 238 // MayAlias reports whether the receiver pointer may alias 239 // the argument pointer. 240 func (p Pointer) MayAlias(q Pointer) bool { 241 return p.PointsTo().Intersects(q.PointsTo()) 242 } 243 244 // DynamicTypes returns p.PointsTo().DynamicTypes(). 245 func (p Pointer) DynamicTypes() *typeutil.Map { 246 return p.PointsTo().DynamicTypes() 247 }