Example #1
0
// 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
}
Example #2
0
// CallGraph computes the call graph of the specified program using the
// Class Hierarchy Analysis algorithm.
//
func CallGraph(prog *ssa.Program) *callgraph.Graph {
	cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph

	allFuncs := ssautil.AllFunctions(prog)

	// funcsBySig contains all functions, keyed by signature.  It is
	// the effective set of address-taken functions used to resolve
	// a dynamic call of a particular signature.
	var funcsBySig typeutil.Map // value is []*ssa.Function

	// methodsByName contains all methods,
	// grouped by name for efficient lookup.
	methodsByName := make(map[string][]*ssa.Function)

	// methodsMemo records, for every abstract method call call I.f on
	// interface type I, the set of concrete methods C.f of all
	// types C that satisfy interface I.
	methodsMemo := make(map[*types.Func][]*ssa.Function)
	lookupMethods := func(m *types.Func) []*ssa.Function {
		methods, ok := methodsMemo[m]
		if !ok {
			I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface)
			for _, f := range methodsByName[m.Name()] {
				C := f.Signature.Recv().Type() // named or *named
				if types.Implements(C, I) {
					methods = append(methods, f)
				}
			}
			methodsMemo[m] = methods
		}
		return methods
	}

	for f := range allFuncs {
		if f.Signature.Recv() == nil {
			// Package initializers can never be address-taken.
			if f.Name() == "init" && f.Synthetic == "package initializer" {
				continue
			}
			funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function)
			funcs = append(funcs, f)
			funcsBySig.Set(f.Signature, funcs)
		} else {
			methodsByName[f.Name()] = append(methodsByName[f.Name()], f)
		}
	}

	addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) {
		gnode := cg.CreateNode(g)
		callgraph.AddEdge(fnode, site, gnode)
	}

	addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) {
		// Because every call to a highly polymorphic and
		// frequently used abstract method such as
		// (io.Writer).Write is assumed to call every concrete
		// Write method in the program, the call graph can
		// contain a lot of duplication.
		//
		// TODO(adonovan): opt: consider factoring the callgraph
		// API so that the Callers component of each edge is a
		// slice of nodes, not a singleton.
		for _, g := range callees {
			addEdge(fnode, site, g)
		}
	}

	for f := range allFuncs {
		fnode := cg.CreateNode(f)
		for _, b := range f.Blocks {
			for _, instr := range b.Instrs {
				if site, ok := instr.(ssa.CallInstruction); ok {
					call := site.Common()
					if call.IsInvoke() {
						addEdges(fnode, site, lookupMethods(call.Method))
					} else if g := call.StaticCallee(); g != nil {
						addEdge(fnode, site, g)
					} else if _, ok := call.Value.(*ssa.Builtin); !ok {
						callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function)
						addEdges(fnode, site, callees)
					}
				}
			}
		}
	}

	return cg
}