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  }