github.com/bir3/gocompiler@v0.3.205/src/xvendor/golang.org/x/tools/internal/facts/facts.go (about) 1 // Copyright 2018 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 // Package facts defines a serializable set of analysis.Fact. 6 // 7 // It provides a partial implementation of the Fact-related parts of the 8 // analysis.Pass interface for use in analysis drivers such as "go vet" 9 // and other build systems. 10 // 11 // The serial format is unspecified and may change, so the same version 12 // of this package must be used for reading and writing serialized facts. 13 // 14 // The handling of facts in the analysis system parallels the handling 15 // of type information in the compiler: during compilation of package P, 16 // the compiler emits an export data file that describes the type of 17 // every object (named thing) defined in package P, plus every object 18 // indirectly reachable from one of those objects. Thus the downstream 19 // compiler of package Q need only load one export data file per direct 20 // import of Q, and it will learn everything about the API of package P 21 // and everything it needs to know about the API of P's dependencies. 22 // 23 // Similarly, analysis of package P emits a fact set containing facts 24 // about all objects exported from P, plus additional facts about only 25 // those objects of P's dependencies that are reachable from the API of 26 // package P; the downstream analysis of Q need only load one fact set 27 // per direct import of Q. 28 // 29 // The notion of "exportedness" that matters here is that of the 30 // compiler. According to the language spec, a method pkg.T.f is 31 // unexported simply because its name starts with lowercase. But the 32 // compiler must nonetheless export f so that downstream compilations can 33 // accurately ascertain whether pkg.T implements an interface pkg.I 34 // defined as interface{f()}. Exported thus means "described in export 35 // data". 36 package facts 37 38 import ( 39 "bytes" 40 "encoding/gob" 41 "fmt" 42 "github.com/bir3/gocompiler/src/go/types" 43 "io/ioutil" 44 "log" 45 "reflect" 46 "sort" 47 "sync" 48 49 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/tools/go/analysis" 50 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/tools/go/types/objectpath" 51 ) 52 53 const debug = false 54 55 // A Set is a set of analysis.Facts. 56 // 57 // Decode creates a Set of facts by reading from the imports of a given 58 // package, and Encode writes out the set. Between these operation, 59 // the Import and Export methods will query and update the set. 60 // 61 // All of Set's methods except String are safe to call concurrently. 62 type Set struct { 63 pkg *types.Package 64 mu sync.Mutex 65 m map[key]analysis.Fact 66 } 67 68 type key struct { 69 pkg *types.Package 70 obj types.Object // (object facts only) 71 t reflect.Type 72 } 73 74 // ImportObjectFact implements analysis.Pass.ImportObjectFact. 75 func (s *Set) ImportObjectFact(obj types.Object, ptr analysis.Fact) bool { 76 if obj == nil { 77 panic("nil object") 78 } 79 key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(ptr)} 80 s.mu.Lock() 81 defer s.mu.Unlock() 82 if v, ok := s.m[key]; ok { 83 reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem()) 84 return true 85 } 86 return false 87 } 88 89 // ExportObjectFact implements analysis.Pass.ExportObjectFact. 90 func (s *Set) ExportObjectFact(obj types.Object, fact analysis.Fact) { 91 if obj.Pkg() != s.pkg { 92 log.Panicf("in package %s: ExportObjectFact(%s, %T): can't set fact on object belonging another package", 93 s.pkg, obj, fact) 94 } 95 key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(fact)} 96 s.mu.Lock() 97 s.m[key] = fact // clobber any existing entry 98 s.mu.Unlock() 99 } 100 101 func (s *Set) AllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact { 102 var facts []analysis.ObjectFact 103 s.mu.Lock() 104 for k, v := range s.m { 105 if k.obj != nil && filter[k.t] { 106 facts = append(facts, analysis.ObjectFact{Object: k.obj, Fact: v}) 107 } 108 } 109 s.mu.Unlock() 110 return facts 111 } 112 113 // ImportPackageFact implements analysis.Pass.ImportPackageFact. 114 func (s *Set) ImportPackageFact(pkg *types.Package, ptr analysis.Fact) bool { 115 if pkg == nil { 116 panic("nil package") 117 } 118 key := key{pkg: pkg, t: reflect.TypeOf(ptr)} 119 s.mu.Lock() 120 defer s.mu.Unlock() 121 if v, ok := s.m[key]; ok { 122 reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem()) 123 return true 124 } 125 return false 126 } 127 128 // ExportPackageFact implements analysis.Pass.ExportPackageFact. 129 func (s *Set) ExportPackageFact(fact analysis.Fact) { 130 key := key{pkg: s.pkg, t: reflect.TypeOf(fact)} 131 s.mu.Lock() 132 s.m[key] = fact // clobber any existing entry 133 s.mu.Unlock() 134 } 135 136 func (s *Set) AllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact { 137 var facts []analysis.PackageFact 138 s.mu.Lock() 139 for k, v := range s.m { 140 if k.obj == nil && filter[k.t] { 141 facts = append(facts, analysis.PackageFact{Package: k.pkg, Fact: v}) 142 } 143 } 144 s.mu.Unlock() 145 return facts 146 } 147 148 // gobFact is the Gob declaration of a serialized fact. 149 type gobFact struct { 150 PkgPath string // path of package 151 Object objectpath.Path // optional path of object relative to package itself 152 Fact analysis.Fact // type and value of user-defined Fact 153 } 154 155 // A Decoder decodes the facts from the direct imports of the package 156 // provided to NewEncoder. A single decoder may be used to decode 157 // multiple fact sets (e.g. each for a different set of fact types) 158 // for the same package. Each call to Decode returns an independent 159 // fact set. 160 type Decoder struct { 161 pkg *types.Package 162 packages map[string]*types.Package 163 } 164 165 // NewDecoder returns a fact decoder for the specified package. 166 func NewDecoder(pkg *types.Package) *Decoder { 167 // Compute the import map for this package. 168 // See the package doc comment. 169 return &Decoder{pkg, importMap(pkg.Imports())} 170 } 171 172 // Decode decodes all the facts relevant to the analysis of package pkg. 173 // The read function reads serialized fact data from an external source 174 // for one of of pkg's direct imports. The empty file is a valid 175 // encoding of an empty fact set. 176 // 177 // It is the caller's responsibility to call gob.Register on all 178 // necessary fact types. 179 func (d *Decoder) Decode(read func(*types.Package) ([]byte, error)) (*Set, error) { 180 // Read facts from imported packages. 181 // Facts may describe indirectly imported packages, or their objects. 182 m := make(map[key]analysis.Fact) // one big bucket 183 for _, imp := range d.pkg.Imports() { 184 logf := func(format string, args ...interface{}) { 185 if debug { 186 prefix := fmt.Sprintf("in %s, importing %s: ", 187 d.pkg.Path(), imp.Path()) 188 log.Print(prefix, fmt.Sprintf(format, args...)) 189 } 190 } 191 192 // Read the gob-encoded facts. 193 data, err := read(imp) 194 if err != nil { 195 return nil, fmt.Errorf("in %s, can't import facts for package %q: %v", 196 d.pkg.Path(), imp.Path(), err) 197 } 198 if len(data) == 0 { 199 continue // no facts 200 } 201 var gobFacts []gobFact 202 if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&gobFacts); err != nil { 203 return nil, fmt.Errorf("decoding facts for %q: %v", imp.Path(), err) 204 } 205 if debug { 206 logf("decoded %d facts: %v", len(gobFacts), gobFacts) 207 } 208 209 // Parse each one into a key and a Fact. 210 for _, f := range gobFacts { 211 factPkg := d.packages[f.PkgPath] 212 if factPkg == nil { 213 // Fact relates to a dependency that was 214 // unused in this translation unit. Skip. 215 logf("no package %q; discarding %v", f.PkgPath, f.Fact) 216 continue 217 } 218 key := key{pkg: factPkg, t: reflect.TypeOf(f.Fact)} 219 if f.Object != "" { 220 // object fact 221 obj, err := objectpath.Object(factPkg, f.Object) 222 if err != nil { 223 // (most likely due to unexported object) 224 // TODO(adonovan): audit for other possibilities. 225 logf("no object for path: %v; discarding %s", err, f.Fact) 226 continue 227 } 228 key.obj = obj 229 logf("read %T fact %s for %v", f.Fact, f.Fact, key.obj) 230 } else { 231 // package fact 232 logf("read %T fact %s for %v", f.Fact, f.Fact, factPkg) 233 } 234 m[key] = f.Fact 235 } 236 } 237 238 return &Set{pkg: d.pkg, m: m}, nil 239 } 240 241 // Encode encodes a set of facts to a memory buffer. 242 // 243 // It may fail if one of the Facts could not be gob-encoded, but this is 244 // a sign of a bug in an Analyzer. 245 func (s *Set) Encode() []byte { 246 247 // TODO(adonovan): opt: use a more efficient encoding 248 // that avoids repeating PkgPath for each fact. 249 250 // Gather all facts, including those from imported packages. 251 var gobFacts []gobFact 252 253 s.mu.Lock() 254 for k, fact := range s.m { 255 if debug { 256 log.Printf("%v => %s\n", k, fact) 257 } 258 var object objectpath.Path 259 if k.obj != nil { 260 path, err := objectpath.For(k.obj) 261 if err != nil { 262 if debug { 263 log.Printf("discarding fact %s about %s\n", fact, k.obj) 264 } 265 continue // object not accessible from package API; discard fact 266 } 267 object = path 268 } 269 gobFacts = append(gobFacts, gobFact{ 270 PkgPath: k.pkg.Path(), 271 Object: object, 272 Fact: fact, 273 }) 274 } 275 s.mu.Unlock() 276 277 // Sort facts by (package, object, type) for determinism. 278 sort.Slice(gobFacts, func(i, j int) bool { 279 x, y := gobFacts[i], gobFacts[j] 280 if x.PkgPath != y.PkgPath { 281 return x.PkgPath < y.PkgPath 282 } 283 if x.Object != y.Object { 284 return x.Object < y.Object 285 } 286 tx := reflect.TypeOf(x.Fact) 287 ty := reflect.TypeOf(y.Fact) 288 if tx != ty { 289 return tx.String() < ty.String() 290 } 291 return false // equal 292 }) 293 294 var buf bytes.Buffer 295 if len(gobFacts) > 0 { 296 if err := gob.NewEncoder(&buf).Encode(gobFacts); err != nil { 297 // Fact encoding should never fail. Identify the culprit. 298 for _, gf := range gobFacts { 299 if err := gob.NewEncoder(ioutil.Discard).Encode(gf); err != nil { 300 fact := gf.Fact 301 pkgpath := reflect.TypeOf(fact).Elem().PkgPath() 302 log.Panicf("internal error: gob encoding of analysis fact %s failed: %v; please report a bug against fact %T in package %q", 303 fact, err, fact, pkgpath) 304 } 305 } 306 } 307 } 308 309 if debug { 310 log.Printf("package %q: encode %d facts, %d bytes\n", 311 s.pkg.Path(), len(gobFacts), buf.Len()) 312 } 313 314 return buf.Bytes() 315 } 316 317 // String is provided only for debugging, and must not be called 318 // concurrent with any Import/Export method. 319 func (s *Set) String() string { 320 var buf bytes.Buffer 321 buf.WriteString("{") 322 for k, f := range s.m { 323 if buf.Len() > 1 { 324 buf.WriteString(", ") 325 } 326 if k.obj != nil { 327 buf.WriteString(k.obj.String()) 328 } else { 329 buf.WriteString(k.pkg.Path()) 330 } 331 fmt.Fprintf(&buf, ": %v", f) 332 } 333 buf.WriteString("}") 334 return buf.String() 335 }