// 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 ( "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 // Print is invoked during the analysis for each discovered // call to the built-in print(x), providing a convenient way // to identify arbitrary expressions of interest in the tests. // // Pointer p may be saved until the analysis is complete, at // which point its methods provide access to the analysis // (The result of callings its methods within the Print // callback is undefined.) p is nil if x is non-pointerlike. // Print func(site *ssa.CallCommon, p Pointer) // The client populates Queries[v] for each ssa.Value v of // interest. // // The boolean (Indirect) indicates whether to compute the // points-to set for v (false) or *v (true): the latter is // typically wanted for Values corresponding to source-level // lvalues, e.g. an *ssa.Global. // // The pointer analysis will populate the corresponding // Results.Queries value 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 Results.Queries slice // may have multiple Pointers, one per distinct context. Use // PointsToCombined to merge them. // Queries map[ssa.Value]Indirect // If Log is non-nil, log messages are written to it. // Logging is extremely verbose. Log io.Writer } type Indirect bool // map[ssa.Value]Indirect is not a set 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 // points-to sets for queried ssa.Values Warnings []Warning // warnings of unsoundness } // A Pointer is an equivalence class of pointerlike values. type Pointer interface { // PointsTo returns the points-to set of this pointer. PointsTo() PointsToSet // MayAlias reports whether the receiver pointer may alias // the argument pointer. MayAlias(Pointer) bool // Context returns the context of this pointer, // if it corresponds to a local variable. Context() call.GraphNode String() string } // A PointsToSet is a set of labels (locations or allocations). // type PointsToSet interface { // PointsTo returns the set of labels that this points-to set // contains. Labels() []*Label // Intersects reports whether this points-to set and the // argument points-to set contain common members. Intersects(PointsToSet) bool // 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). // DynamicTypes() *typemap.M } // Union returns the set containing all the elements of each set in sets. func Union(sets ...PointsToSet) PointsToSet { var union ptset for _, set := range sets { set := set.(ptset) 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...) } // ---- PointsToSet public interface type ptset struct { a *analysis // may be nil if pts is nil pts nodeset } func (s ptset) Labels() []*Label { var labels []*Label for l := range s.pts { labels = append(labels, s.a.labelFor(l)) } return labels } func (s ptset) DynamicTypes() *typemap.M { var tmap typemap.M tmap.SetHasher(s.a.hasher) for ifaceObjId := range s.pts { tDyn, v, indirect := s.a.taggedValue(ifaceObjId) if tDyn == nil { continue // !CanHaveDynamicTypes(tDyn) } if indirect { panic("indirect tagged object") // implement later } prev, _ := tmap.At(tDyn).([]Pointer) tmap.Set(tDyn, append(prev, ptr{s.a, nil, v})) } return &tmap } func (x ptset) Intersects(y_ PointsToSet) bool { y := y_.(ptset) for l := range x.pts { if _, ok := y.pts[l]; ok { return true } } return false } // ---- Pointer public interface // ptr adapts a node to the Pointer interface. type ptr struct { a *analysis cgn *cgnode n nodeid // non-zero } func (p ptr) String() string { return fmt.Sprintf("n%d", p.n) } func (p ptr) Context() call.GraphNode { return p.cgn } func (p ptr) PointsTo() PointsToSet { return ptset{p.a, p.a.nodes[p.n].pts} } func (p ptr) MayAlias(q Pointer) bool { return p.PointsTo().Intersects(q.PointsTo()) } func (p ptr) DynamicTypes() *typemap.M { return p.PointsTo().DynamicTypes() }