242 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			242 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright 2013 The Go Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package pointer
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"go/token"
 | 
						|
	"io"
 | 
						|
 | 
						|
	"code.google.com/p/go.tools/call"
 | 
						|
	"code.google.com/p/go.tools/go/types/typemap"
 | 
						|
	"code.google.com/p/go.tools/ssa"
 | 
						|
)
 | 
						|
 | 
						|
// A Config formulates a pointer analysis problem for Analyze().
 | 
						|
type Config struct {
 | 
						|
	// Mains contains the set of 'main' packages to analyze
 | 
						|
	// Clients must provide the analysis with at least one
 | 
						|
	// package defining a main() function.
 | 
						|
	Mains []*ssa.Package
 | 
						|
 | 
						|
	// Reflection determines whether to handle reflection
 | 
						|
	// operators soundly, which is currently rather slow since it
 | 
						|
	// causes constraint to be generated during solving
 | 
						|
	// proportional to the number of constraint variables, which
 | 
						|
	// has not yet been reduced by presolver optimisation.
 | 
						|
	Reflection bool
 | 
						|
 | 
						|
	// BuildCallGraph determines whether to construct a callgraph.
 | 
						|
	// If enabled, the graph will be available in Result.CallGraph.
 | 
						|
	BuildCallGraph bool
 | 
						|
 | 
						|
	// QueryPrintCalls causes the analysis to record (in
 | 
						|
	// Result.PrintCalls) the points-to set of the first operand
 | 
						|
	// of each discovered call to the built-in print(x), providing
 | 
						|
	// a convenient way to identify arbitrary expressions of
 | 
						|
	// interest in the tests.
 | 
						|
	//
 | 
						|
	QueryPrintCalls bool
 | 
						|
 | 
						|
	// The client populates Queries[v] or IndirectQueries[v]
 | 
						|
	// for each ssa.Value v of interest, to request that the
 | 
						|
	// points-to sets pts(v) or pts(*v) be computed.  If the
 | 
						|
	// client needs both points-to sets, v may appear in both
 | 
						|
	// maps.
 | 
						|
	//
 | 
						|
	// (IndirectQueries is typically used for Values corresponding
 | 
						|
	// to source-level lvalues, e.g. an *ssa.Global.)
 | 
						|
	//
 | 
						|
	// The analysis populates the corresponding
 | 
						|
	// Result.{Indirect,}Queries map when it creates the pointer
 | 
						|
	// variable for v or *v.  Upon completion the client can
 | 
						|
	// inspect that map for the results.
 | 
						|
	//
 | 
						|
	// If a Value belongs to a function that the analysis treats
 | 
						|
	// context-sensitively, the corresponding Result.{Indirect,}Queries
 | 
						|
	// slice may have multiple Pointers, one per distinct context.
 | 
						|
	// Use PointsToCombined to merge them.
 | 
						|
	//
 | 
						|
	// TODO(adonovan): this API doesn't scale well for batch tools
 | 
						|
	// that want to dump the entire solution.
 | 
						|
	//
 | 
						|
	// TODO(adonovan): need we distinguish contexts?  Current
 | 
						|
	// clients always combine them.
 | 
						|
	//
 | 
						|
	Queries         map[ssa.Value]struct{}
 | 
						|
	IndirectQueries map[ssa.Value]struct{}
 | 
						|
 | 
						|
	// If Log is non-nil, log messages are written to it.
 | 
						|
	// Logging is extremely verbose.
 | 
						|
	Log io.Writer
 | 
						|
}
 | 
						|
 | 
						|
// AddQuery adds v to Config.Queries.
 | 
						|
func (c *Config) AddQuery(v ssa.Value) {
 | 
						|
	if c.Queries == nil {
 | 
						|
		c.Queries = make(map[ssa.Value]struct{})
 | 
						|
	}
 | 
						|
	c.Queries[v] = struct{}{}
 | 
						|
}
 | 
						|
 | 
						|
// AddQuery adds v to Config.IndirectQueries.
 | 
						|
func (c *Config) AddIndirectQuery(v ssa.Value) {
 | 
						|
	if c.IndirectQueries == nil {
 | 
						|
		c.IndirectQueries = make(map[ssa.Value]struct{})
 | 
						|
	}
 | 
						|
	c.IndirectQueries[v] = struct{}{}
 | 
						|
}
 | 
						|
 | 
						|
func (c *Config) prog() *ssa.Program {
 | 
						|
	for _, main := range c.Mains {
 | 
						|
		return main.Prog
 | 
						|
	}
 | 
						|
	panic("empty scope")
 | 
						|
}
 | 
						|
 | 
						|
type Warning struct {
 | 
						|
	Pos     token.Pos
 | 
						|
	Message string
 | 
						|
}
 | 
						|
 | 
						|
// A Result contains the results of a pointer analysis.
 | 
						|
//
 | 
						|
// See Config for how to request the various Result components.
 | 
						|
//
 | 
						|
type Result struct {
 | 
						|
	CallGraph       call.Graph                  // discovered call graph
 | 
						|
	Queries         map[ssa.Value][]Pointer     // pts(v) for each v in Config.Queries.
 | 
						|
	IndirectQueries map[ssa.Value][]Pointer     // pts(*v) for each v in Config.IndirectQueries.
 | 
						|
	Warnings        []Warning                   // warnings of unsoundness
 | 
						|
	PrintCalls      map[*ssa.CallCommon]Pointer // pts(x) for each call to print(x)
 | 
						|
}
 | 
						|
 | 
						|
// A Pointer is an equivalence class of pointerlike values.
 | 
						|
//
 | 
						|
// A pointer doesn't have a unique type because pointers of distinct
 | 
						|
// types may alias the same object.
 | 
						|
//
 | 
						|
type Pointer struct {
 | 
						|
	a   *analysis
 | 
						|
	cgn *cgnode
 | 
						|
	n   nodeid // non-zero
 | 
						|
}
 | 
						|
 | 
						|
// A PointsToSet is a set of labels (locations or allocations).
 | 
						|
type PointsToSet struct {
 | 
						|
	a   *analysis // may be nil if pts is nil
 | 
						|
	pts nodeset
 | 
						|
}
 | 
						|
 | 
						|
// Union returns the set containing all the elements of each set in sets.
 | 
						|
func Union(sets ...PointsToSet) PointsToSet {
 | 
						|
	var union PointsToSet
 | 
						|
	for _, set := range sets {
 | 
						|
		union.a = set.a
 | 
						|
		union.pts.addAll(set.pts)
 | 
						|
	}
 | 
						|
	return union
 | 
						|
}
 | 
						|
 | 
						|
// PointsToCombined returns the combined points-to set of all the
 | 
						|
// specified pointers.
 | 
						|
func PointsToCombined(ptrs []Pointer) PointsToSet {
 | 
						|
	var ptsets []PointsToSet
 | 
						|
	for _, ptr := range ptrs {
 | 
						|
		ptsets = append(ptsets, ptr.PointsTo())
 | 
						|
	}
 | 
						|
	return Union(ptsets...)
 | 
						|
}
 | 
						|
 | 
						|
func (s PointsToSet) String() string {
 | 
						|
	var buf bytes.Buffer
 | 
						|
	fmt.Fprintf(&buf, "[")
 | 
						|
	sep := ""
 | 
						|
	for l := range s.pts {
 | 
						|
		fmt.Fprintf(&buf, "%s%s", sep, s.a.labelFor(l))
 | 
						|
		sep = ", "
 | 
						|
	}
 | 
						|
	fmt.Fprintf(&buf, "]")
 | 
						|
	return buf.String()
 | 
						|
}
 | 
						|
 | 
						|
// PointsTo returns the set of labels that this points-to set
 | 
						|
// contains.
 | 
						|
func (s PointsToSet) Labels() []*Label {
 | 
						|
	var labels []*Label
 | 
						|
	for l := range s.pts {
 | 
						|
		labels = append(labels, s.a.labelFor(l))
 | 
						|
	}
 | 
						|
	return labels
 | 
						|
}
 | 
						|
 | 
						|
// If this PointsToSet came from a Pointer of interface kind
 | 
						|
// or a reflect.Value, DynamicTypes returns the set of dynamic
 | 
						|
// types that it may contain.  (For an interface, they will
 | 
						|
// always be concrete types.)
 | 
						|
//
 | 
						|
// The result is a mapping whose keys are the dynamic types to
 | 
						|
// which it may point.  For each pointer-like key type, the
 | 
						|
// corresponding map value is a set of pointer abstractions of
 | 
						|
// that dynamic type, represented as a []Pointer slice.  Use
 | 
						|
// PointsToCombined to merge them.
 | 
						|
//
 | 
						|
// The result is empty unless CanHaveDynamicTypes(T).
 | 
						|
//
 | 
						|
func (s PointsToSet) DynamicTypes() *typemap.M {
 | 
						|
	var tmap typemap.M
 | 
						|
	tmap.SetHasher(s.a.hasher)
 | 
						|
	for ifaceObjId := range s.pts {
 | 
						|
		if !s.a.isTaggedObject(ifaceObjId) {
 | 
						|
			continue // !CanHaveDynamicTypes(tDyn)
 | 
						|
		}
 | 
						|
		tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
 | 
						|
		if indirect {
 | 
						|
			panic("indirect tagged object") // implement later
 | 
						|
		}
 | 
						|
		prev, _ := tmap.At(tDyn).([]Pointer)
 | 
						|
		tmap.Set(tDyn, append(prev, Pointer{s.a, nil, v}))
 | 
						|
	}
 | 
						|
	return &tmap
 | 
						|
}
 | 
						|
 | 
						|
// Intersects reports whether this points-to set and the
 | 
						|
// argument points-to set contain common members.
 | 
						|
func (x PointsToSet) Intersects(y PointsToSet) bool {
 | 
						|
	for l := range x.pts {
 | 
						|
		if _, ok := y.pts[l]; ok {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (p Pointer) String() string {
 | 
						|
	return fmt.Sprintf("n%d", p.n)
 | 
						|
}
 | 
						|
 | 
						|
// Context returns the context of this pointer,
 | 
						|
// if it corresponds to a local variable.
 | 
						|
func (p Pointer) Context() call.GraphNode {
 | 
						|
	return p.cgn
 | 
						|
}
 | 
						|
 | 
						|
// PointsTo returns the points-to set of this pointer.
 | 
						|
func (p Pointer) PointsTo() PointsToSet {
 | 
						|
	return PointsToSet{p.a, p.a.nodes[p.n].pts}
 | 
						|
}
 | 
						|
 | 
						|
// MayAlias reports whether the receiver pointer may alias
 | 
						|
// the argument pointer.
 | 
						|
func (p Pointer) MayAlias(q Pointer) bool {
 | 
						|
	return p.PointsTo().Intersects(q.PointsTo())
 | 
						|
}
 | 
						|
 | 
						|
// DynamicTypes returns p.PointsTo().DynamicTypes().
 | 
						|
func (p Pointer) DynamicTypes() *typemap.M {
 | 
						|
	return p.PointsTo().DynamicTypes()
 | 
						|
}
 |