+++ /dev/null
-// 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"
-
- "golang.org/x/tools/container/intsets"
- "golang.org/x/tools/go/callgraph"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types/typeutil"
-)
-
-// A Config formulates a pointer analysis problem for Analyze. It is
-// only usable for a single invocation of Analyze and must not be
-// reused.
-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.
- //
- // Non-main packages in the ssa.Program that are not
- // dependencies of any main package may still affect the
- // analysis result, because they contribute runtime types and
- // thus methods.
- // TODO(adonovan): investigate whether this is desirable.
- 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
-
- // 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.
- //
- // TODO(adonovan): this API doesn't scale well for batch tools
- // that want to dump the entire solution. Perhaps optionally
- // populate a map[*ssa.DebugRef]Pointer in the Result, one
- // entry per source expression.
- //
- Queries map[ssa.Value]struct{}
- IndirectQueries map[ssa.Value]struct{}
- extendedQueries map[ssa.Value][]*extendedQuery
-
- // If Log is non-nil, log messages are written to it.
- // Logging is extremely verbose.
- Log io.Writer
-}
-
-type track uint32
-
-const (
- trackChan track = 1 << iota // track 'chan' references
- trackMap // track 'map' references
- trackPtr // track regular pointers
- trackSlice // track slice references
-
- trackAll = ^track(0)
-)
-
-// AddQuery adds v to Config.Queries.
-// Precondition: CanPoint(v.Type()).
-func (c *Config) AddQuery(v ssa.Value) {
- if !CanPoint(v.Type()) {
- panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type()))
- }
- if c.Queries == nil {
- c.Queries = make(map[ssa.Value]struct{})
- }
- c.Queries[v] = struct{}{}
-}
-
-// AddQuery adds v to Config.IndirectQueries.
-// Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()).
-func (c *Config) AddIndirectQuery(v ssa.Value) {
- if c.IndirectQueries == nil {
- c.IndirectQueries = make(map[ssa.Value]struct{})
- }
- if !CanPoint(mustDeref(v.Type())) {
- panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type()))
- }
- c.IndirectQueries[v] = struct{}{}
-}
-
-// AddExtendedQuery adds an extended, AST-based query on v to the
-// analysis. The query, which must be a single Go expression, allows
-// destructuring the value.
-//
-// The query must operate on a variable named 'x', which represents
-// the value, and result in a pointer-like object. Only a subset of
-// Go expressions are permitted in queries, namely channel receives,
-// pointer dereferences, field selectors, array/slice/map/tuple
-// indexing and grouping with parentheses. The specific indices when
-// indexing arrays, slices and maps have no significance. Indices used
-// on tuples must be numeric and within bounds.
-//
-// All field selectors must be explicit, even ones usually elided
-// due to promotion of embedded fields.
-//
-// The query 'x' is identical to using AddQuery. The query '*x' is
-// identical to using AddIndirectQuery.
-//
-// On success, AddExtendedQuery returns a Pointer to the queried
-// value. This Pointer will be initialized during analysis. Using it
-// before analysis has finished has undefined behavior.
-//
-// Example:
-// // given v, which represents a function call to 'fn() (int, []*T)', and
-// // 'type T struct { F *int }', the following query will access the field F.
-// c.AddExtendedQuery(v, "x[1][0].F")
-func (c *Config) AddExtendedQuery(v ssa.Value, query string) (*Pointer, error) {
- ops, _, err := parseExtendedQuery(v.Type(), query)
- if err != nil {
- return nil, fmt.Errorf("invalid query %q: %s", query, err)
- }
- if c.extendedQueries == nil {
- c.extendedQueries = make(map[ssa.Value][]*extendedQuery)
- }
-
- ptr := &Pointer{}
- c.extendedQueries[v] = append(c.extendedQueries[v], &extendedQuery{ops: ops, ptr: ptr})
- return ptr, nil
-}
-
-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 *callgraph.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
-}
-
-// A Pointer is an equivalence class of pointer-like values.
-//
-// A Pointer doesn't have a unique type because pointers of distinct
-// types may alias the same object.
-//
-type Pointer struct {
- a *analysis
- n nodeid
-}
-
-// A PointsToSet is a set of labels (locations or allocations).
-type PointsToSet struct {
- a *analysis // may be nil if pts is nil
- pts *nodeset
-}
-
-func (s PointsToSet) String() string {
- var buf bytes.Buffer
- buf.WriteByte('[')
- if s.pts != nil {
- var space [50]int
- for i, l := range s.pts.AppendTo(space[:0]) {
- if i > 0 {
- buf.WriteString(", ")
- }
- buf.WriteString(s.a.labelFor(nodeid(l)).String())
- }
- }
- buf.WriteByte(']')
- return buf.String()
-}
-
-// PointsTo returns the set of labels that this points-to set
-// contains.
-func (s PointsToSet) Labels() []*Label {
- var labels []*Label
- if s.pts != nil {
- var space [50]int
- for _, l := range s.pts.AppendTo(space[:0]) {
- labels = append(labels, s.a.labelFor(nodeid(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 the PointsToSet for pointers of that type.
-//
-// The result is empty unless CanHaveDynamicTypes(T).
-//
-func (s PointsToSet) DynamicTypes() *typeutil.Map {
- var tmap typeutil.Map
- tmap.SetHasher(s.a.hasher)
- if s.pts != nil {
- var space [50]int
- for _, x := range s.pts.AppendTo(space[:0]) {
- ifaceObjID := nodeid(x)
- if !s.a.isTaggedObject(ifaceObjID) {
- continue // !CanHaveDynamicTypes(tDyn)
- }
- tDyn, v, indirect := s.a.taggedValue(ifaceObjID)
- if indirect {
- panic("indirect tagged object") // implement later
- }
- pts, ok := tmap.At(tDyn).(PointsToSet)
- if !ok {
- pts = PointsToSet{s.a, new(nodeset)}
- tmap.Set(tDyn, pts)
- }
- pts.pts.addAll(&s.a.nodes[v].solve.pts)
- }
- }
- return &tmap
-}
-
-// Intersects reports whether this points-to set and the
-// argument points-to set contain common members.
-func (s PointsToSet) Intersects(y PointsToSet) bool {
- if s.pts == nil || y.pts == nil {
- return false
- }
- // This takes Θ(|x|+|y|) time.
- var z intsets.Sparse
- z.Intersection(&s.pts.Sparse, &y.pts.Sparse)
- return !z.IsEmpty()
-}
-
-func (p Pointer) String() string {
- return fmt.Sprintf("n%d", p.n)
-}
-
-// PointsTo returns the points-to set of this pointer.
-func (p Pointer) PointsTo() PointsToSet {
- if p.n == 0 {
- return PointsToSet{}
- }
- return PointsToSet{p.a, &p.a.nodes[p.n].solve.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() *typeutil.Map {
- return p.PointsTo().DynamicTypes()
-}