-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x/tools/...: fork and tag !1.5 all files that use go/types et al
This change will ensure that the tree continues to work with go1.4.1. All files continue to depend on golang.org/x/tools/go/types, but in a follow-up change, I will switch the primary files to depend on the standard go/types package. Another (smaller) set of files will be forked and tagged, this time !1.6, due to API differences between the two packages. All tests pass using 1.4.1, 1.5, and ~1.6 (tip). Change-Id: Ifd75a6330e120957d646be91693daaba1ce0e8c9 Reviewed-on: https://go-review.googlesource.com/18333 Reviewed-by: Robert Griesemer <gri@golang.org>
- Loading branch information
Showing
147 changed files
with
33,615 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
// 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. | ||
|
||
// +build !go1.5 | ||
|
||
// ssadump: a tool for displaying and interpreting the SSA form of Go programs. | ||
package main // import "golang.org/x/tools/cmd/ssadump" | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"go/build" | ||
"os" | ||
"runtime" | ||
"runtime/pprof" | ||
|
||
"golang.org/x/tools/go/buildutil" | ||
"golang.org/x/tools/go/loader" | ||
"golang.org/x/tools/go/ssa" | ||
"golang.org/x/tools/go/ssa/interp" | ||
"golang.org/x/tools/go/ssa/ssautil" | ||
"golang.org/x/tools/go/types" | ||
) | ||
|
||
var ( | ||
modeFlag = ssa.BuilderModeFlag(flag.CommandLine, "build", 0) | ||
|
||
testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.") | ||
|
||
runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.") | ||
|
||
interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter. | ||
The value is a sequence of zero or more more of these letters: | ||
R disable [R]ecover() from panic; show interpreter crash instead. | ||
T [T]race execution of the program. Best for single-threaded programs! | ||
`) | ||
) | ||
|
||
const usage = `SSA builder and interpreter. | ||
Usage: ssadump [<flag> ...] <args> ... | ||
Use -help flag to display options. | ||
Examples: | ||
% ssadump -build=F hello.go # dump SSA form of a single package | ||
% ssadump -run -interp=T hello.go # interpret a program, with tracing | ||
% ssadump -run -test unicode -- -test.v # interpret the unicode package's tests, verbosely | ||
` + loader.FromArgsUsage + | ||
` | ||
When -run is specified, ssadump will run the program. | ||
The entry point depends on the -test flag: | ||
if clear, it runs the first package named main. | ||
if set, it runs the tests of each package. | ||
` | ||
|
||
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") | ||
|
||
func init() { | ||
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) | ||
|
||
// If $GOMAXPROCS isn't set, use the full capacity of the machine. | ||
// For small machines, use at least 4 threads. | ||
if os.Getenv("GOMAXPROCS") == "" { | ||
n := runtime.NumCPU() | ||
if n < 4 { | ||
n = 4 | ||
} | ||
runtime.GOMAXPROCS(n) | ||
} | ||
} | ||
|
||
func main() { | ||
if err := doMain(); err != nil { | ||
fmt.Fprintf(os.Stderr, "ssadump: %s\n", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func doMain() error { | ||
flag.Parse() | ||
args := flag.Args() | ||
|
||
conf := loader.Config{Build: &build.Default} | ||
|
||
// Choose types.Sizes from conf.Build. | ||
var wordSize int64 = 8 | ||
switch conf.Build.GOARCH { | ||
case "386", "arm": | ||
wordSize = 4 | ||
} | ||
conf.TypeChecker.Sizes = &types.StdSizes{ | ||
MaxAlign: 8, | ||
WordSize: wordSize, | ||
} | ||
|
||
var interpMode interp.Mode | ||
for _, c := range *interpFlag { | ||
switch c { | ||
case 'T': | ||
interpMode |= interp.EnableTracing | ||
case 'R': | ||
interpMode |= interp.DisableRecover | ||
default: | ||
return fmt.Errorf("unknown -interp option: '%c'", c) | ||
} | ||
} | ||
|
||
if len(args) == 0 { | ||
fmt.Fprint(os.Stderr, usage) | ||
os.Exit(1) | ||
} | ||
|
||
// Profiling support. | ||
if *cpuprofile != "" { | ||
f, err := os.Create(*cpuprofile) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
os.Exit(1) | ||
} | ||
pprof.StartCPUProfile(f) | ||
defer pprof.StopCPUProfile() | ||
} | ||
|
||
// Use the initial packages from the command line. | ||
args, err := conf.FromArgs(args, *testFlag) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// The interpreter needs the runtime package. | ||
if *runFlag { | ||
conf.Import("runtime") | ||
} | ||
|
||
// Load, parse and type-check the whole program. | ||
iprog, err := conf.Load() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Create and build SSA-form program representation. | ||
prog := ssautil.CreateProgram(iprog, *modeFlag) | ||
|
||
// Build and display only the initial packages | ||
// (and synthetic wrappers), unless -run is specified. | ||
for _, info := range iprog.InitialPackages() { | ||
prog.Package(info.Pkg).Build() | ||
} | ||
|
||
// Run the interpreter. | ||
if *runFlag { | ||
prog.Build() | ||
|
||
var main *ssa.Package | ||
pkgs := prog.AllPackages() | ||
if *testFlag { | ||
// If -test, run all packages' tests. | ||
if len(pkgs) > 0 { | ||
main = prog.CreateTestMainPackage(pkgs...) | ||
} | ||
if main == nil { | ||
return fmt.Errorf("no tests") | ||
} | ||
} else { | ||
// Otherwise, run main.main. | ||
for _, pkg := range pkgs { | ||
if pkg.Pkg.Name() == "main" { | ||
main = pkg | ||
if main.Func("main") == nil { | ||
return fmt.Errorf("no func main() in main package") | ||
} | ||
break | ||
} | ||
} | ||
if main == nil { | ||
return fmt.Errorf("no main package") | ||
} | ||
} | ||
|
||
if runtime.GOARCH != build.Default.GOARCH { | ||
return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)", | ||
build.Default.GOARCH, runtime.GOARCH) | ||
} | ||
|
||
interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// Copyright 2014 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. | ||
|
||
// +build !go1.5 | ||
|
||
// Package cha computes the call graph of a Go program using the Class | ||
// Hierarchy Analysis (CHA) algorithm. | ||
// | ||
// CHA was first described in "Optimization of Object-Oriented Programs | ||
// Using Static Class Hierarchy Analysis", Jeffrey Dean, David Grove, | ||
// and Craig Chambers, ECOOP'95. | ||
// | ||
// CHA is related to RTA (see go/callgraph/rta); the difference is that | ||
// CHA conservatively computes the entire "implements" relation between | ||
// interfaces and concrete types ahead of time, whereas RTA uses dynamic | ||
// programming to construct it on the fly as it encounters new functions | ||
// reachable from main. CHA may thus include spurious call edges for | ||
// types that haven't been instantiated yet, or types that are never | ||
// instantiated. | ||
// | ||
// Since CHA conservatively assumes that all functions are address-taken | ||
// and all concrete types are put into interfaces, it is sound to run on | ||
// partial programs, such as libraries without a main or test function. | ||
// | ||
package cha // import "golang.org/x/tools/go/callgraph/cha" | ||
|
||
import ( | ||
"golang.org/x/tools/go/callgraph" | ||
"golang.org/x/tools/go/ssa" | ||
"golang.org/x/tools/go/ssa/ssautil" | ||
"golang.org/x/tools/go/types" | ||
"golang.org/x/tools/go/types/typeutil" | ||
) | ||
|
||
// 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 | ||
} |
Oops, something went wrong.