 9aea4aee1c
			
		
	
	
		9aea4aee1c
		
			
		
	
	
	
		
			
	
		
	
	
		
			Some checks failed
		
		
	
	Extra / Vulnerability scanner (push) Has been cancelled
				CI / go-mod (push) Has been cancelled
				CI / golangci-lint (push) Has been cancelled
				Release a tag / release (push) Has been cancelled
				CI / tests-on-windows (push) Has been cancelled
				CI / tests-on-macos (push) Has been cancelled
				CI / tests-on-unix (1.14) (push) Has been cancelled
				CI / tests-on-unix (1.15) (push) Has been cancelled
				CI / tests-on-unix (1.16) (push) Has been cancelled
				CI / check_generated (push) Has been cancelled
				Release a tag / docker-release (map[Dockerfile:build/Dockerfile.alpine]) (push) Has been cancelled
				Release a tag / docker-release (map[Dockerfile:build/Dockerfile]) (push) Has been cancelled
				
		
			
				
	
	
		
			126 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package goanalysis
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/gob"
 | |
| 	"fmt"
 | |
| 	"go/types"
 | |
| 	"reflect"
 | |
| 
 | |
| 	"golang.org/x/tools/go/analysis"
 | |
| )
 | |
| 
 | |
| type objectFactKey struct {
 | |
| 	obj types.Object
 | |
| 	typ reflect.Type
 | |
| }
 | |
| 
 | |
| type packageFactKey struct {
 | |
| 	pkg *types.Package
 | |
| 	typ reflect.Type
 | |
| }
 | |
| 
 | |
| type Fact struct {
 | |
| 	Path string // non-empty only for object facts
 | |
| 	Fact analysis.Fact
 | |
| }
 | |
| 
 | |
| // inheritFacts populates act.facts with
 | |
| // those it obtains from its dependency, dep.
 | |
| func inheritFacts(act, dep *action) {
 | |
| 	serialize := false
 | |
| 
 | |
| 	for key, fact := range dep.objectFacts {
 | |
| 		// Filter out facts related to objects
 | |
| 		// that are irrelevant downstream
 | |
| 		// (equivalently: not in the compiler export data).
 | |
| 		if !exportedFrom(key.obj, dep.pkg.Types) {
 | |
| 			factsInheritDebugf("%v: discarding %T fact from %s for %s: %s", act, fact, dep, key.obj, fact)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// Optionally serialize/deserialize fact
 | |
| 		// to verify that it works across address spaces.
 | |
| 		if serialize {
 | |
| 			var err error
 | |
| 			fact, err = codeFact(fact)
 | |
| 			if err != nil {
 | |
| 				act.r.log.Panicf("internal error: encoding of %T fact failed in %v", fact, act)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		factsInheritDebugf("%v: inherited %T fact for %s: %s", act, fact, key.obj, fact)
 | |
| 		act.objectFacts[key] = fact
 | |
| 	}
 | |
| 
 | |
| 	for key, fact := range dep.packageFacts {
 | |
| 		// TODO: filter out facts that belong to
 | |
| 		// packages not mentioned in the export data
 | |
| 		// to prevent side channels.
 | |
| 
 | |
| 		// Optionally serialize/deserialize fact
 | |
| 		// to verify that it works across address spaces
 | |
| 		// and is deterministic.
 | |
| 		if serialize {
 | |
| 			var err error
 | |
| 			fact, err = codeFact(fact)
 | |
| 			if err != nil {
 | |
| 				act.r.log.Panicf("internal error: encoding of %T fact failed in %v", fact, act)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		factsInheritDebugf("%v: inherited %T fact for %s: %s", act, fact, key.pkg.Path(), fact)
 | |
| 		act.packageFacts[key] = fact
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // codeFact encodes then decodes a fact,
 | |
| // just to exercise that logic.
 | |
| func codeFact(fact analysis.Fact) (analysis.Fact, error) {
 | |
| 	// We encode facts one at a time.
 | |
| 	// A real modular driver would emit all facts
 | |
| 	// into one encoder to improve gob efficiency.
 | |
| 	var buf bytes.Buffer
 | |
| 	if err := gob.NewEncoder(&buf).Encode(fact); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// Encode it twice and assert that we get the same bits.
 | |
| 	// This helps detect nondeterministic Gob encoding (e.g. of maps).
 | |
| 	var buf2 bytes.Buffer
 | |
| 	if err := gob.NewEncoder(&buf2).Encode(fact); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if !bytes.Equal(buf.Bytes(), buf2.Bytes()) {
 | |
| 		return nil, fmt.Errorf("encoding of %T fact is nondeterministic", fact)
 | |
| 	}
 | |
| 
 | |
| 	newFact := reflect.New(reflect.TypeOf(fact).Elem()).Interface().(analysis.Fact)
 | |
| 	if err := gob.NewDecoder(&buf).Decode(newFact); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return newFact, nil
 | |
| }
 | |
| 
 | |
| // exportedFrom reports whether obj may be visible to a package that imports pkg.
 | |
| // This includes not just the exported members of pkg, but also unexported
 | |
| // constants, types, fields, and methods, perhaps belonging to other packages,
 | |
| // that find there way into the API.
 | |
| // This is an over-approximation of the more accurate approach used by
 | |
| // gc export data, which walks the type graph, but it's much simpler.
 | |
| //
 | |
| // TODO(adonovan): do more accurate filtering by walking the type graph.
 | |
| func exportedFrom(obj types.Object, pkg *types.Package) bool {
 | |
| 	switch obj := obj.(type) {
 | |
| 	case *types.Func:
 | |
| 		return obj.Exported() && obj.Pkg() == pkg ||
 | |
| 			obj.Type().(*types.Signature).Recv() != nil
 | |
| 	case *types.Var:
 | |
| 		return obj.Exported() && obj.Pkg() == pkg ||
 | |
| 			obj.IsField()
 | |
| 	case *types.TypeName, *types.Const:
 | |
| 		return true
 | |
| 	}
 | |
| 	return false // Nil, Builtin, Label, or PkgName
 | |
| }
 |