github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gotools/go/pointer/api.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  package pointer
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"go/token"
    11  	"io"
    12  
    13  	"llvm.org/llgo/third_party/gotools/container/intsets"
    14  	"llvm.org/llgo/third_party/gotools/go/callgraph"
    15  	"llvm.org/llgo/third_party/gotools/go/ssa"
    16  	"llvm.org/llgo/third_party/gotools/go/types/typeutil"
    17  )
    18  
    19  // A Config formulates a pointer analysis problem for Analyze().
    20  type Config struct {
    21  	// Mains contains the set of 'main' packages to analyze
    22  	// Clients must provide the analysis with at least one
    23  	// package defining a main() function.
    24  	//
    25  	// Non-main packages in the ssa.Program that are not
    26  	// dependencies of any main package may still affect the
    27  	// analysis result, because they contribute runtime types and
    28  	// thus methods.
    29  	// TODO(adonovan): investigate whether this is desirable.
    30  	Mains []*ssa.Package
    31  
    32  	// Reflection determines whether to handle reflection
    33  	// operators soundly, which is currently rather slow since it
    34  	// causes constraint to be generated during solving
    35  	// proportional to the number of constraint variables, which
    36  	// has not yet been reduced by presolver optimisation.
    37  	Reflection bool
    38  
    39  	// BuildCallGraph determines whether to construct a callgraph.
    40  	// If enabled, the graph will be available in Result.CallGraph.
    41  	BuildCallGraph bool
    42  
    43  	// The client populates Queries[v] or IndirectQueries[v]
    44  	// for each ssa.Value v of interest, to request that the
    45  	// points-to sets pts(v) or pts(*v) be computed.  If the
    46  	// client needs both points-to sets, v may appear in both
    47  	// maps.
    48  	//
    49  	// (IndirectQueries is typically used for Values corresponding
    50  	// to source-level lvalues, e.g. an *ssa.Global.)
    51  	//
    52  	// The analysis populates the corresponding
    53  	// Result.{Indirect,}Queries map when it creates the pointer
    54  	// variable for v or *v.  Upon completion the client can
    55  	// inspect that map for the results.
    56  	//
    57  	// TODO(adonovan): this API doesn't scale well for batch tools
    58  	// that want to dump the entire solution.  Perhaps optionally
    59  	// populate a map[*ssa.DebugRef]Pointer in the Result, one
    60  	// entry per source expression.
    61  	//
    62  	Queries         map[ssa.Value]struct{}
    63  	IndirectQueries map[ssa.Value]struct{}
    64  
    65  	// If Log is non-nil, log messages are written to it.
    66  	// Logging is extremely verbose.
    67  	Log io.Writer
    68  }
    69  
    70  type track uint32
    71  
    72  const (
    73  	trackChan  track = 1 << iota // track 'chan' references
    74  	trackMap                     // track 'map' references
    75  	trackPtr                     // track regular pointers
    76  	trackSlice                   // track slice references
    77  
    78  	trackAll = ^track(0)
    79  )
    80  
    81  // AddQuery adds v to Config.Queries.
    82  // Precondition: CanPoint(v.Type()).
    83  // TODO(adonovan): consider returning a new Pointer for this query,
    84  // which will be initialized during analysis.  That avoids the needs
    85  // for the corresponding ssa.Value-keyed maps in Config and Result.
    86  func (c *Config) AddQuery(v ssa.Value) {
    87  	if !CanPoint(v.Type()) {
    88  		panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type()))
    89  	}
    90  	if c.Queries == nil {
    91  		c.Queries = make(map[ssa.Value]struct{})
    92  	}
    93  	c.Queries[v] = struct{}{}
    94  }
    95  
    96  // AddQuery adds v to Config.IndirectQueries.
    97  // Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()).
    98  func (c *Config) AddIndirectQuery(v ssa.Value) {
    99  	if c.IndirectQueries == nil {
   100  		c.IndirectQueries = make(map[ssa.Value]struct{})
   101  	}
   102  	if !CanPoint(mustDeref(v.Type())) {
   103  		panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type()))
   104  	}
   105  	c.IndirectQueries[v] = struct{}{}
   106  }
   107  
   108  func (c *Config) prog() *ssa.Program {
   109  	for _, main := range c.Mains {
   110  		return main.Prog
   111  	}
   112  	panic("empty scope")
   113  }
   114  
   115  type Warning struct {
   116  	Pos     token.Pos
   117  	Message string
   118  }
   119  
   120  // A Result contains the results of a pointer analysis.
   121  //
   122  // See Config for how to request the various Result components.
   123  //
   124  type Result struct {
   125  	CallGraph       *callgraph.Graph      // discovered call graph
   126  	Queries         map[ssa.Value]Pointer // pts(v) for each v in Config.Queries.
   127  	IndirectQueries map[ssa.Value]Pointer // pts(*v) for each v in Config.IndirectQueries.
   128  	Warnings        []Warning             // warnings of unsoundness
   129  }
   130  
   131  // A Pointer is an equivalence class of pointer-like values.
   132  //
   133  // A Pointer doesn't have a unique type because pointers of distinct
   134  // types may alias the same object.
   135  //
   136  type Pointer struct {
   137  	a *analysis
   138  	n nodeid
   139  }
   140  
   141  // A PointsToSet is a set of labels (locations or allocations).
   142  type PointsToSet struct {
   143  	a   *analysis // may be nil if pts is nil
   144  	pts *nodeset
   145  }
   146  
   147  func (s PointsToSet) String() string {
   148  	var buf bytes.Buffer
   149  	buf.WriteByte('[')
   150  	if s.pts != nil {
   151  		var space [50]int
   152  		for i, l := range s.pts.AppendTo(space[:0]) {
   153  			if i > 0 {
   154  				buf.WriteString(", ")
   155  			}
   156  			buf.WriteString(s.a.labelFor(nodeid(l)).String())
   157  		}
   158  	}
   159  	buf.WriteByte(']')
   160  	return buf.String()
   161  }
   162  
   163  // PointsTo returns the set of labels that this points-to set
   164  // contains.
   165  func (s PointsToSet) Labels() []*Label {
   166  	var labels []*Label
   167  	if s.pts != nil {
   168  		var space [50]int
   169  		for _, l := range s.pts.AppendTo(space[:0]) {
   170  			labels = append(labels, s.a.labelFor(nodeid(l)))
   171  		}
   172  	}
   173  	return labels
   174  }
   175  
   176  // If this PointsToSet came from a Pointer of interface kind
   177  // or a reflect.Value, DynamicTypes returns the set of dynamic
   178  // types that it may contain.  (For an interface, they will
   179  // always be concrete types.)
   180  //
   181  // The result is a mapping whose keys are the dynamic types to which
   182  // it may point.  For each pointer-like key type, the corresponding
   183  // map value is the PointsToSet for pointers of that type.
   184  //
   185  // The result is empty unless CanHaveDynamicTypes(T).
   186  //
   187  func (s PointsToSet) DynamicTypes() *typeutil.Map {
   188  	var tmap typeutil.Map
   189  	tmap.SetHasher(s.a.hasher)
   190  	if s.pts != nil {
   191  		var space [50]int
   192  		for _, x := range s.pts.AppendTo(space[:0]) {
   193  			ifaceObjId := nodeid(x)
   194  			if !s.a.isTaggedObject(ifaceObjId) {
   195  				continue // !CanHaveDynamicTypes(tDyn)
   196  			}
   197  			tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
   198  			if indirect {
   199  				panic("indirect tagged object") // implement later
   200  			}
   201  			pts, ok := tmap.At(tDyn).(PointsToSet)
   202  			if !ok {
   203  				pts = PointsToSet{s.a, new(nodeset)}
   204  				tmap.Set(tDyn, pts)
   205  			}
   206  			pts.pts.addAll(&s.a.nodes[v].solve.pts)
   207  		}
   208  	}
   209  	return &tmap
   210  }
   211  
   212  // Intersects reports whether this points-to set and the
   213  // argument points-to set contain common members.
   214  func (x PointsToSet) Intersects(y PointsToSet) bool {
   215  	if x.pts == nil || y.pts == nil {
   216  		return false
   217  	}
   218  	// This takes Θ(|x|+|y|) time.
   219  	var z intsets.Sparse
   220  	z.Intersection(&x.pts.Sparse, &y.pts.Sparse)
   221  	return !z.IsEmpty()
   222  }
   223  
   224  func (p Pointer) String() string {
   225  	return fmt.Sprintf("n%d", p.n)
   226  }
   227  
   228  // PointsTo returns the points-to set of this pointer.
   229  func (p Pointer) PointsTo() PointsToSet {
   230  	if p.n == 0 {
   231  		return PointsToSet{}
   232  	}
   233  	return PointsToSet{p.a, &p.a.nodes[p.n].solve.pts}
   234  }
   235  
   236  // MayAlias reports whether the receiver pointer may alias
   237  // the argument pointer.
   238  func (p Pointer) MayAlias(q Pointer) bool {
   239  	return p.PointsTo().Intersects(q.PointsTo())
   240  }
   241  
   242  // DynamicTypes returns p.PointsTo().DynamicTypes().
   243  func (p Pointer) DynamicTypes() *typeutil.Map {
   244  	return p.PointsTo().DynamicTypes()
   245  }