114 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			114 lines
		
	
	
		
			2.9 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 oracle
 | 
						|
 | 
						|
import (
 | 
						|
	"go/token"
 | 
						|
 | 
						|
	"code.google.com/p/go.tools/oracle/json"
 | 
						|
	"code.google.com/p/go.tools/pointer"
 | 
						|
	"code.google.com/p/go.tools/ssa"
 | 
						|
)
 | 
						|
 | 
						|
// Callstack displays an arbitrary path from a root of the callgraph
 | 
						|
// to the function at the current position.
 | 
						|
//
 | 
						|
// The information may be misleading in a context-insensitive
 | 
						|
// analysis. e.g. the call path X->Y->Z might be infeasible if Y never
 | 
						|
// calls Z when it is called from X.  TODO(adonovan): think about UI.
 | 
						|
//
 | 
						|
// TODO(adonovan): permit user to specify a starting point other than
 | 
						|
// the analysis root.
 | 
						|
//
 | 
						|
func callstack(o *oracle) (queryResult, error) {
 | 
						|
	pkg := o.prog.Package(o.queryPkgInfo.Pkg)
 | 
						|
	if pkg == nil {
 | 
						|
		return nil, o.errorf(o.queryPath[0], "no SSA package")
 | 
						|
	}
 | 
						|
 | 
						|
	if !ssa.HasEnclosingFunction(pkg, o.queryPath) {
 | 
						|
		return nil, o.errorf(o.queryPath[0], "this position is not inside a function")
 | 
						|
	}
 | 
						|
 | 
						|
	buildSSA(o)
 | 
						|
 | 
						|
	target := ssa.EnclosingFunction(pkg, o.queryPath)
 | 
						|
	if target == nil {
 | 
						|
		return nil, o.errorf(o.queryPath[0],
 | 
						|
			"no SSA function built for this location (dead code?)")
 | 
						|
	}
 | 
						|
 | 
						|
	// Run the pointer analysis and build the complete call graph.
 | 
						|
	callgraph := make(pointer.CallGraph)
 | 
						|
	o.config.Call = callgraph.AddEdge
 | 
						|
	root := ptrAnalysis(o)
 | 
						|
 | 
						|
	seen := make(map[pointer.CallGraphNode]bool)
 | 
						|
	var callstack []pointer.CallSite
 | 
						|
 | 
						|
	// Use depth-first search to find an arbitrary path from a
 | 
						|
	// root to the target function.
 | 
						|
	var search func(cgn pointer.CallGraphNode) bool
 | 
						|
	search = func(cgn pointer.CallGraphNode) bool {
 | 
						|
		if !seen[cgn] {
 | 
						|
			seen[cgn] = true
 | 
						|
			if cgn.Func() == target {
 | 
						|
				return true
 | 
						|
			}
 | 
						|
			for callee, site := range callgraph[cgn] {
 | 
						|
				if search(callee) {
 | 
						|
					callstack = append(callstack, site)
 | 
						|
					return true
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	for toplevel := range callgraph[root] {
 | 
						|
		if search(toplevel) {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return &callstackResult{
 | 
						|
		target:    target,
 | 
						|
		callstack: callstack,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
type callstackResult struct {
 | 
						|
	target    *ssa.Function
 | 
						|
	callstack []pointer.CallSite
 | 
						|
}
 | 
						|
 | 
						|
func (r *callstackResult) display(printf printfFunc) {
 | 
						|
	if r.callstack != nil {
 | 
						|
		printf(false, "Found a call path from root to %s", r.target)
 | 
						|
		printf(r.target, "%s", r.target)
 | 
						|
		for _, site := range r.callstack {
 | 
						|
			printf(site, "%s from %s", site.Description(), site.Caller().Func())
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		printf(r.target, "%s is unreachable in this analysis scope", r.target)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *callstackResult) toJSON(res *json.Result, fset *token.FileSet) {
 | 
						|
	var callers []json.Caller
 | 
						|
	for _, site := range r.callstack {
 | 
						|
		callers = append(callers, json.Caller{
 | 
						|
			Pos:    fset.Position(site.Pos()).String(),
 | 
						|
			Caller: site.Caller().Func().String(),
 | 
						|
			Desc:   site.Description(),
 | 
						|
		})
 | 
						|
	}
 | 
						|
	res.Callstack = &json.CallStack{
 | 
						|
		Pos:     fset.Position(r.target.Pos()).String(),
 | 
						|
		Target:  r.target.String(),
 | 
						|
		Callers: callers,
 | 
						|
	}
 | 
						|
}
 |