2013-08-27 16:49:13 -06:00
|
|
|
// 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.
|
|
|
|
|
2013-08-22 10:27:55 -06:00
|
|
|
package pointer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"go/token"
|
|
|
|
|
|
|
|
"code.google.com/p/go.tools/ssa"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TODO(adonovan): move the CallGraph, CallGraphNode, CallSite types
|
|
|
|
// into a separate package 'callgraph', and make them pure interfaces
|
|
|
|
// capable of supporting several implementations (context-sensitive
|
|
|
|
// and insensitive PTA, RTA, etc).
|
|
|
|
|
|
|
|
// ---------- CallGraphNode ----------
|
|
|
|
|
|
|
|
// A CallGraphNode is a context-sensitive representation of a node in
|
|
|
|
// the callgraph. In other words, there may be multiple nodes
|
|
|
|
// representing a single *Function, depending on the contexts in which
|
|
|
|
// it is called. The identity of the node is therefore important.
|
|
|
|
//
|
|
|
|
type CallGraphNode interface {
|
|
|
|
Func() *ssa.Function // the function this node represents
|
|
|
|
String() string // diagnostic description of this callgraph node
|
|
|
|
}
|
|
|
|
|
|
|
|
type cgnode struct {
|
|
|
|
fn *ssa.Function
|
|
|
|
obj nodeid // start of this contour's object block
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *cgnode) Func() *ssa.Function {
|
|
|
|
return n.fn
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *cgnode) String() string {
|
|
|
|
return fmt.Sprintf("cg%d:%s", n.obj, n.fn)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------- CallSite ----------
|
|
|
|
|
|
|
|
// A CallSite is a context-sensitive representation of a function call
|
|
|
|
// site in the program.
|
|
|
|
//
|
|
|
|
type CallSite interface {
|
|
|
|
Caller() CallGraphNode // the enclosing context of this call
|
|
|
|
Pos() token.Pos // source position; token.NoPos for synthetic calls
|
|
|
|
Description() string // UI description of call kind; see (*ssa.CallCommon).Description
|
|
|
|
String() string // diagnostic description of this callsite
|
|
|
|
}
|
|
|
|
|
|
|
|
// A callsite represents a single function or method callsite within a
|
|
|
|
// function. callsites never represent calls to built-ins; they are
|
|
|
|
// handled as intrinsics.
|
|
|
|
//
|
|
|
|
type callsite struct {
|
|
|
|
caller *cgnode // the origin of the call
|
|
|
|
targets nodeid // pts(targets) contains identities of all called functions.
|
|
|
|
instr ssa.CallInstruction // optional call instruction; provides IsInvoke, position, etc.
|
|
|
|
pos token.Pos // position, if instr == nil, i.e. synthetic callsites.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Caller returns the node in the callgraph from which this call originated.
|
|
|
|
func (c *callsite) Caller() CallGraphNode {
|
|
|
|
return c.caller
|
|
|
|
}
|
|
|
|
|
|
|
|
// Description returns a description of this kind of call, in the
|
|
|
|
// manner of ssa.CallCommon.Description().
|
|
|
|
//
|
|
|
|
func (c *callsite) Description() string {
|
|
|
|
if c.instr != nil {
|
|
|
|
return c.instr.Common().Description()
|
|
|
|
}
|
|
|
|
return "synthetic function call"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pos returns the source position of this callsite, or token.NoPos if implicit.
|
|
|
|
func (c *callsite) Pos() token.Pos {
|
|
|
|
if c.instr != nil {
|
|
|
|
return c.instr.Pos()
|
|
|
|
}
|
|
|
|
return c.pos
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *callsite) String() string {
|
|
|
|
// TODO(adonovan): provide more info, e.g. target of static
|
|
|
|
// call, arguments, location.
|
|
|
|
return c.Description()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------- CallGraph ----------
|
|
|
|
|
|
|
|
// CallGraph is a forward directed graph of functions labelled by an
|
|
|
|
// arbitrary site within the caller.
|
|
|
|
//
|
|
|
|
// CallGraph.AddEdge may be used as the Context.Call callback for
|
|
|
|
// clients that wish to construct a call graph.
|
|
|
|
//
|
|
|
|
// TODO(adonovan): this is just a starting point. Add options to
|
|
|
|
// control whether we record no callsite, an arbitrary callsite, or
|
|
|
|
// all callsites for a given graph edge. Also, this could live in
|
|
|
|
// another package since it's just a client utility.
|
|
|
|
//
|
|
|
|
type CallGraph map[CallGraphNode]map[CallGraphNode]CallSite
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (cg CallGraph) AddEdge(site CallSite, callee CallGraphNode) {
|
|
|
|
caller := site.Caller()
|
2013-08-22 10:27:55 -06:00
|
|
|
callees := cg[caller]
|
|
|
|
if callees == nil {
|
|
|
|
callees = make(map[CallGraphNode]CallSite)
|
|
|
|
cg[caller] = callees
|
|
|
|
}
|
|
|
|
callees[callee] = site // save an arbitrary site
|
|
|
|
}
|