1
0
mirror of https://github.com/golang/go synced 2024-11-18 23:34:45 -07:00
go/pointer/api.go
Alan Donovan 5d70784aca go.tools/pointer: replace Config.Print (callback) with a Result.PrintCalls (map).
This avoids leaking nodeids into client code before the
analysis has had a chance to run the (forthcoming) constraint
optimizer, which renumbers them.

R=crawshaw
CC=golang-dev
https://golang.org/cl/39410043
2013-12-10 09:15:39 -05:00

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()
}