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  }