...

Source file src/golang.org/x/tools/go/pointer/api.go

Documentation: golang.org/x/tools/go/pointer

     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  package pointer
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"go/token"
    11  	"io"
    12  
    13  	"golang.org/x/tools/container/intsets"
    14  	"golang.org/x/tools/go/callgraph"
    15  	"golang.org/x/tools/go/ssa"
    16  	"golang.org/x/tools/go/types/typeutil"
    17  )
    18  
    19  // A Config formulates a pointer analysis problem for Analyze. It is
    20  // only usable for a single invocation of Analyze and must not be
    21  // reused.
    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  	//
    32  	// TODO(adonovan): investigate whether this is desirable.
    33  	//
    34  	// Calls to generic functions will be unsound unless packages
    35  	// are built using the ssa.InstantiateGenerics builder mode.
    36  	Mains []*ssa.Package
    37  
    38  	// Reflection determines whether to handle reflection
    39  	// operators soundly, which is currently rather slow since it
    40  	// causes constraint to be generated during solving
    41  	// proportional to the number of constraint variables, which
    42  	// has not yet been reduced by presolver optimisation.
    43  	Reflection bool
    44  
    45  	// BuildCallGraph determines whether to construct a callgraph.
    46  	// If enabled, the graph will be available in Result.CallGraph.
    47  	BuildCallGraph bool
    48  
    49  	// The client populates Queries[v] or IndirectQueries[v]
    50  	// for each ssa.Value v of interest, to request that the
    51  	// points-to sets pts(v) or pts(*v) be computed.  If the
    52  	// client needs both points-to sets, v may appear in both
    53  	// maps.
    54  	//
    55  	// (IndirectQueries is typically used for Values corresponding
    56  	// to source-level lvalues, e.g. an *ssa.Global.)
    57  	//
    58  	// The analysis populates the corresponding
    59  	// Result.{Indirect,}Queries map when it creates the pointer
    60  	// variable for v or *v.  Upon completion the client can
    61  	// inspect that map for the results.
    62  	//
    63  	// TODO(adonovan): this API doesn't scale well for batch tools
    64  	// that want to dump the entire solution.  Perhaps optionally
    65  	// populate a map[*ssa.DebugRef]Pointer in the Result, one
    66  	// entry per source expression.
    67  	//
    68  	Queries         map[ssa.Value]struct{}
    69  	IndirectQueries map[ssa.Value]struct{}
    70  	extendedQueries map[ssa.Value][]*extendedQuery
    71  
    72  	// If Log is non-nil, log messages are written to it.
    73  	// Logging is extremely verbose.
    74  	Log io.Writer
    75  }
    76  
    77  type track uint32
    78  
    79  const (
    80  	trackChan  track = 1 << iota // track 'chan' references
    81  	trackMap                     // track 'map' references
    82  	trackPtr                     // track regular pointers
    83  	trackSlice                   // track slice references
    84  
    85  	trackAll = ^track(0)
    86  )
    87  
    88  // AddQuery adds v to Config.Queries.
    89  // Precondition: CanPoint(v.Type()).
    90  func (c *Config) AddQuery(v ssa.Value) {
    91  	if !CanPoint(v.Type()) {
    92  		panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type()))
    93  	}
    94  	if c.Queries == nil {
    95  		c.Queries = make(map[ssa.Value]struct{})
    96  	}
    97  	c.Queries[v] = struct{}{}
    98  }
    99  
   100  // AddQuery adds v to Config.IndirectQueries.
   101  // Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()).
   102  func (c *Config) AddIndirectQuery(v ssa.Value) {
   103  	if c.IndirectQueries == nil {
   104  		c.IndirectQueries = make(map[ssa.Value]struct{})
   105  	}
   106  	if !CanPoint(mustDeref(v.Type())) {
   107  		panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type()))
   108  	}
   109  	c.IndirectQueries[v] = struct{}{}
   110  }
   111  
   112  // AddExtendedQuery adds an extended, AST-based query on v to the
   113  // analysis. The query, which must be a single Go expression, allows
   114  // destructuring the value.
   115  //
   116  // The query must operate on a variable named 'x', which represents
   117  // the value, and result in a pointer-like object. Only a subset of
   118  // Go expressions are permitted in queries, namely channel receives,
   119  // pointer dereferences, field selectors, array/slice/map/tuple
   120  // indexing and grouping with parentheses. The specific indices when
   121  // indexing arrays, slices and maps have no significance. Indices used
   122  // on tuples must be numeric and within bounds.
   123  //
   124  // All field selectors must be explicit, even ones usually elided
   125  // due to promotion of embedded fields.
   126  //
   127  // The query 'x' is identical to using AddQuery. The query '*x' is
   128  // identical to using AddIndirectQuery.
   129  //
   130  // On success, AddExtendedQuery returns a Pointer to the queried
   131  // value. This Pointer will be initialized during analysis. Using it
   132  // before analysis has finished has undefined behavior.
   133  //
   134  // Example:
   135  //
   136  //	// given v, which represents a function call to 'fn() (int, []*T)', and
   137  //	// 'type T struct { F *int }', the following query will access the field F.
   138  //	c.AddExtendedQuery(v, "x[1][0].F")
   139  func (c *Config) AddExtendedQuery(v ssa.Value, query string) (*Pointer, error) {
   140  	ops, _, err := parseExtendedQuery(v.Type(), query)
   141  	if err != nil {
   142  		return nil, fmt.Errorf("invalid query %q: %s", query, err)
   143  	}
   144  	if c.extendedQueries == nil {
   145  		c.extendedQueries = make(map[ssa.Value][]*extendedQuery)
   146  	}
   147  
   148  	ptr := &Pointer{}
   149  	c.extendedQueries[v] = append(c.extendedQueries[v], &extendedQuery{ops: ops, ptr: ptr})
   150  	return ptr, nil
   151  }
   152  
   153  func (c *Config) prog() *ssa.Program {
   154  	for _, main := range c.Mains {
   155  		return main.Prog
   156  	}
   157  	panic("empty scope")
   158  }
   159  
   160  type Warning struct {
   161  	Pos     token.Pos
   162  	Message string
   163  }
   164  
   165  // A Result contains the results of a pointer analysis.
   166  //
   167  // See Config for how to request the various Result components.
   168  type Result struct {
   169  	CallGraph       *callgraph.Graph      // discovered call graph
   170  	Queries         map[ssa.Value]Pointer // pts(v) for each v in Config.Queries.
   171  	IndirectQueries map[ssa.Value]Pointer // pts(*v) for each v in Config.IndirectQueries.
   172  	Warnings        []Warning             // warnings of unsoundness
   173  }
   174  
   175  // A Pointer is an equivalence class of pointer-like values.
   176  //
   177  // A Pointer doesn't have a unique type because pointers of distinct
   178  // types may alias the same object.
   179  type Pointer struct {
   180  	a *analysis
   181  	n nodeid
   182  }
   183  
   184  // A PointsToSet is a set of labels (locations or allocations).
   185  type PointsToSet struct {
   186  	a   *analysis // may be nil if pts is nil
   187  	pts *nodeset
   188  }
   189  
   190  func (s PointsToSet) String() string {
   191  	var buf bytes.Buffer
   192  	buf.WriteByte('[')
   193  	if s.pts != nil {
   194  		var space [50]int
   195  		for i, l := range s.pts.AppendTo(space[:0]) {
   196  			if i > 0 {
   197  				buf.WriteString(", ")
   198  			}
   199  			buf.WriteString(s.a.labelFor(nodeid(l)).String())
   200  		}
   201  	}
   202  	buf.WriteByte(']')
   203  	return buf.String()
   204  }
   205  
   206  // PointsTo returns the set of labels that this points-to set
   207  // contains.
   208  func (s PointsToSet) Labels() []*Label {
   209  	var labels []*Label
   210  	if s.pts != nil {
   211  		var space [50]int
   212  		for _, l := range s.pts.AppendTo(space[:0]) {
   213  			labels = append(labels, s.a.labelFor(nodeid(l)))
   214  		}
   215  	}
   216  	return labels
   217  }
   218  
   219  // If this PointsToSet came from a Pointer of interface kind
   220  // or a reflect.Value, DynamicTypes returns the set of dynamic
   221  // types that it may contain.  (For an interface, they will
   222  // always be concrete types.)
   223  //
   224  // The result is a mapping whose keys are the dynamic types to which
   225  // it may point.  For each pointer-like key type, the corresponding
   226  // map value is the PointsToSet for pointers of that type.
   227  //
   228  // The result is empty unless CanHaveDynamicTypes(T).
   229  func (s PointsToSet) DynamicTypes() *typeutil.Map {
   230  	var tmap typeutil.Map
   231  	tmap.SetHasher(s.a.hasher)
   232  	if s.pts != nil {
   233  		var space [50]int
   234  		for _, x := range s.pts.AppendTo(space[:0]) {
   235  			ifaceObjID := nodeid(x)
   236  			if !s.a.isTaggedObject(ifaceObjID) {
   237  				continue // !CanHaveDynamicTypes(tDyn)
   238  			}
   239  			tDyn, v, indirect := s.a.taggedValue(ifaceObjID)
   240  			if indirect {
   241  				panic("indirect tagged object") // implement later
   242  			}
   243  			pts, ok := tmap.At(tDyn).(PointsToSet)
   244  			if !ok {
   245  				pts = PointsToSet{s.a, new(nodeset)}
   246  				tmap.Set(tDyn, pts)
   247  			}
   248  			pts.pts.addAll(&s.a.nodes[v].solve.pts)
   249  		}
   250  	}
   251  	return &tmap
   252  }
   253  
   254  // Intersects reports whether this points-to set and the
   255  // argument points-to set contain common members.
   256  func (s PointsToSet) Intersects(y PointsToSet) bool {
   257  	if s.pts == nil || y.pts == nil {
   258  		return false
   259  	}
   260  	// This takes Θ(|x|+|y|) time.
   261  	var z intsets.Sparse
   262  	z.Intersection(&s.pts.Sparse, &y.pts.Sparse)
   263  	return !z.IsEmpty()
   264  }
   265  
   266  func (p Pointer) String() string {
   267  	return fmt.Sprintf("n%d", p.n)
   268  }
   269  
   270  // PointsTo returns the points-to set of this pointer.
   271  func (p Pointer) PointsTo() PointsToSet {
   272  	if p.n == 0 {
   273  		return PointsToSet{}
   274  	}
   275  	return PointsToSet{p.a, &p.a.nodes[p.n].solve.pts}
   276  }
   277  
   278  // MayAlias reports whether the receiver pointer may alias
   279  // the argument pointer.
   280  func (p Pointer) MayAlias(q Pointer) bool {
   281  	return p.PointsTo().Intersects(q.PointsTo())
   282  }
   283  
   284  // DynamicTypes returns p.PointsTo().DynamicTypes().
   285  func (p Pointer) DynamicTypes() *typeutil.Map {
   286  	return p.PointsTo().DynamicTypes()
   287  }
   288  

View as plain text