diff --git a/cmd/callgraph/main.go b/cmd/callgraph/main.go
index 04113953..8979b5b4 100644
--- a/cmd/callgraph/main.go
+++ b/cmd/callgraph/main.go
@@ -37,6 +37,7 @@ import (
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
)
var algoFlag = flag.String("algo", "rta",
@@ -173,7 +174,7 @@ func doCallgraph(ctxt *build.Context, algo, format string, tests bool, args []st
}
// Create and build SSA-form program representation.
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(iprog, 0)
prog.BuildAll()
// -- call graph construction ------------------------------------------
diff --git a/cmd/ssadump/main.go b/cmd/ssadump/main.go
index a7b2ea7e..588b8240 100644
--- a/cmd/ssadump/main.go
+++ b/cmd/ssadump/main.go
@@ -16,14 +16,11 @@ import (
"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 (
- importbinFlag = flag.Bool("importbin", false,
- "Import binary export data from gc's object files, not source. "+
- "Imported functions will have no bodies.")
-
modeFlag = ssa.BuilderModeFlag(flag.CommandLine, "build", 0)
testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.")
@@ -78,10 +75,7 @@ func doMain() error {
flag.Parse()
args := flag.Args()
- conf := loader.Config{
- Build: &build.Default,
- ImportFromBinary: *importbinFlag,
- }
+ conf := loader.Config{Build: &build.Default}
// TODO(adonovan): make go/types choose its default Sizes from
// build.Default or a specified *build.Context.
var wordSize int64 = 8
@@ -140,11 +134,18 @@ func doMain() error {
}
// Create and build SSA-form program representation.
- prog := ssa.Create(iprog, *modeFlag)
- prog.BuildAll()
+ 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.BuildAll()
+
var main *ssa.Package
pkgs := prog.AllPackages()
if *testFlag {
diff --git a/go/callgraph/cha/cha_test.go b/go/callgraph/cha/cha_test.go
index 56c7c1f3..e8ddda47 100644
--- a/go/callgraph/cha/cha_test.go
+++ b/go/callgraph/cha/cha_test.go
@@ -18,7 +18,7 @@ import (
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
)
@@ -72,7 +72,7 @@ func TestCHA(t *testing.T) {
continue
}
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(iprog, 0)
mainPkg := prog.Package(iprog.Created[0].Pkg)
prog.BuildAll()
diff --git a/go/callgraph/rta/rta_test.go b/go/callgraph/rta/rta_test.go
index 11fa1a57..aef788df 100644
--- a/go/callgraph/rta/rta_test.go
+++ b/go/callgraph/rta/rta_test.go
@@ -19,6 +19,7 @@ import (
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
)
@@ -76,7 +77,7 @@ func TestRTA(t *testing.T) {
continue
}
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(iprog, 0)
mainPkg := prog.Package(iprog.Created[0].Pkg)
prog.BuildAll()
diff --git a/go/callgraph/static/static_test.go b/go/callgraph/static/static_test.go
index 5a74ca1a..62297f79 100644
--- a/go/callgraph/static/static_test.go
+++ b/go/callgraph/static/static_test.go
@@ -14,7 +14,7 @@ import (
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/static"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
)
const input = `package P
@@ -62,7 +62,7 @@ func TestStatic(t *testing.T) {
P := iprog.Created[0].Pkg
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(iprog, 0)
prog.BuildAll()
cg := static.CallGraph(prog)
diff --git a/go/loader/doc.go b/go/loader/doc.go
index c6dcac24..1ff4b15d 100644
--- a/go/loader/doc.go
+++ b/go/loader/doc.go
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package loader loads, parses and type-checks packages of Go code
-// plus their transitive closure, and retains both the ASTs and the
-// derived facts.
+// Package loader loads a complete Go program from source code, parsing
+// and type-checking the initial packages plus their transitive closure
+// of dependencies. The ASTs and the derived facts are retained for
+// later use.
//
// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.
//
@@ -25,11 +26,11 @@
// // See FromArgsUsage for help.
// rest, err := conf.FromArgs(os.Args[1:], wantTests)
//
-// // Parse the specified files and create an ad-hoc package with path "foo".
+// // Parse the specified files and create an ad hoc package with path "foo".
// // All files must have the same 'package' declaration.
// conf.CreateFromFilenames("foo", "foo.go", "bar.go")
//
-// // Create an ad-hoc package with path "foo" from
+// // Create an ad hoc package with path "foo" from
// // the specified already-parsed files.
// // All ASTs must have the same 'package' declaration.
// conf.CreateFromFiles("foo", parsedFiles)
@@ -49,7 +50,7 @@
//
// CONCEPTS AND TERMINOLOGY
//
-// An AD-HOC package is one specified as a set of source files on the
+// An AD HOC package is one specified as a set of source files on the
// command line. In the simplest case, it may consist of a single file
// such as $GOROOT/src/net/http/triv.go.
//
@@ -61,10 +62,10 @@
// spec. The Path() of each importable package is unique within a
// Program.
//
-// Ad-hoc packages and external test packages are NON-IMPORTABLE. The
-// Path() of an ad-hoc package is inferred from the package
+// ad hoc packages and external test packages are NON-IMPORTABLE. The
+// Path() of an ad hoc package is inferred from the package
// declarations of its files and is therefore not a unique package key.
-// For example, Config.CreatePkgs may specify two initial ad-hoc
+// For example, Config.CreatePkgs may specify two initial ad hoc
// packages both called "main".
//
// An AUGMENTED package is an importable package P plus all the
@@ -125,7 +126,7 @@ package loader
// list of files passed to (Checker).Files at once. Many of these lists
// are the production code of an importable Go package, so those nodes
// are labelled by the package's import path. The remaining nodes are
-// ad-hoc packages and lists of in-package *_test.go files that augment
+// ad hoc packages and lists of in-package *_test.go files that augment
// an importable package; those nodes have no label.
//
// The edges of the graph represent import statements appearing within a
diff --git a/go/loader/loader.go b/go/loader/loader.go
index 41df1512..98df7aad 100644
--- a/go/loader/loader.go
+++ b/go/loader/loader.go
@@ -20,13 +20,13 @@ import (
"time"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/gcimporter"
"golang.org/x/tools/go/types"
)
const trace = false // show timing info for type-checking
-// Config specifies the configuration for a program to load.
+// Config specifies the configuration for loading a whole program from
+// Go source code.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
// Fset is the file set for the parser to use when loading the
@@ -42,8 +42,7 @@ type Config struct {
//
// The supplied IgnoreFuncBodies is not used; the effective
// value comes from the TypeCheckFuncBodies func below.
- //
- // TypeChecker.Packages is lazily initialized during Load.
+ // The supplied Import function is not used either.
TypeChecker types.Config
// TypeCheckFuncBodies is a predicate over package import
@@ -54,33 +53,6 @@ type Config struct {
// checked.
TypeCheckFuncBodies func(string) bool
- // ImportFromBinary determines whether to satisfy dependencies by
- // loading gc export data instead of Go source code.
- //
- // If false, the entire program---the initial packages and their
- // transitive closure of dependencies---will be loaded from
- // source, parsed, and type-checked. This is required for
- // whole-program analyses such as pointer analysis.
- //
- // If true, the go/gcimporter mechanism is used instead to read
- // the binary export-data files written by the gc toolchain.
- // They supply only the types of package-level declarations and
- // values of constants, but no code, this option will not yield
- // a whole program. It is intended for analyses that perform
- // modular analysis of a single package, e.g. traditional
- // compilation.
- //
- // No check is made that the export data files are up-to-date.
- //
- // The initial packages (CreatePkgs and ImportPkgs) are always
- // loaded from Go source, regardless of this flag's setting.
- //
- // NB: there is a bug when loading multiple initial packages with
- // this flag enabled: https://github.com/golang/go/issues/9955.
- //
- // THIS FEATURE IS DEPRECATED and will be removed shortly (Apr 2015).
- ImportFromBinary bool
-
// If Build is non-nil, it is used to locate source packages.
// Otherwise &build.Default is used.
//
@@ -131,21 +103,6 @@ type Config struct {
//
// It must be safe to call concurrently from multiple goroutines.
FindPackage func(ctxt *build.Context, importPath string) (*build.Package, error)
-
- // PackageCreated is a hook called when a types.Package
- // is created but before it has been populated.
- //
- // The package's import Path() and Scope() are defined,
- // but not its Name() since no package declaration has
- // been seen yet.
- //
- // Clients may use this to insert synthetic items into
- // the package scope, for example.
- //
- // It must be safe to call concurrently from multiple goroutines.
- //
- // THIS FEATURE IS DEPRECATED and will be removed shortly (Apr 2015).
- PackageCreated func(*types.Package)
}
// A PkgSpec specifies a non-importable package to be created by Load.
@@ -158,8 +115,7 @@ type PkgSpec struct {
Filenames []string // names of files to be parsed
}
-// A Program is a Go program loaded from source or binary
-// as specified by a Config.
+// A Program is a Go program loaded from source as specified by a Config.
type Program struct {
Fset *token.FileSet // the file set for this program
@@ -179,9 +135,8 @@ type Program struct {
AllPackages map[*types.Package]*PackageInfo
// importMap is the canonical mapping of import paths to
- // packages used by the type-checker.
- // It contains all Imported initial packages, but not Created
- // ones, and all imported dependencies.
+ // packages. It contains all Imported initial packages, but not
+ // Created ones, and all imported dependencies.
importMap map[string]*types.Package
}
@@ -284,7 +239,7 @@ func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) {
if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
// Assume args is a list of a *.go files
- // denoting a single ad-hoc package.
+ // denoting a single ad hoc package.
for _, arg := range args {
if !strings.HasSuffix(arg, ".go") {
return nil, fmt.Errorf("named files must be .go files: %s", arg)
@@ -400,18 +355,12 @@ func (prog *Program) Package(path string) *PackageInfo {
// importer holds the working state of the algorithm.
type importer struct {
conf *Config // the client configuration
- prog *Program // resulting program
start time.Time // for logging
- // This mutex serializes access to prog.ImportMap (aka
- // TypeChecker.Packages); we also use it for AllPackages.
- //
- // The TypeChecker.Packages map is not really used by this
- // package, but may be used by the client's Import function,
- // and by clients of the returned Program.
- typecheckerMu sync.Mutex
+ progMu sync.Mutex // guards prog
+ prog *Program // the resulting program
- importedMu sync.Mutex
+ importedMu sync.Mutex // guards imported
imported map[string]*importInfo // all imported packages (incl. failures) by import path
// import dependency graph: graph[x][y] => x imports y
@@ -472,12 +421,6 @@ func (ii *importInfo) Complete(info *PackageInfo, err error) {
// It is an error if no packages were loaded.
//
func (conf *Config) Load() (*Program, error) {
- // Initialize by setting the conf's copy, so all copies of
- // TypeChecker agree on the identity of the map.
- if conf.TypeChecker.Packages == nil {
- conf.TypeChecker.Packages = make(map[string]*types.Package)
- }
-
// Create a simple default error handler for parse/type errors.
if conf.TypeChecker.Error == nil {
conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
@@ -508,7 +451,7 @@ func (conf *Config) Load() (*Program, error) {
prog := &Program{
Fset: conf.fset(),
Imported: make(map[string]*PackageInfo),
- importMap: conf.TypeChecker.Packages,
+ importMap: make(map[string]*types.Package),
AllPackages: make(map[*types.Package]*PackageInfo),
}
@@ -577,7 +520,7 @@ func (conf *Config) Load() (*Program, error) {
info.appendError(err)
}
- // Ad-hoc packages are non-importable,
+ // Ad hoc packages are non-importable,
// so no cycle check is needed.
// addFiles loads dependencies in parallel.
imp.addFiles(info, files, false)
@@ -879,57 +822,19 @@ func (imp *importer) startLoad(path string) *importInfo {
ii = &importInfo{path: path}
ii.complete.L = &ii.mu
imp.imported[path] = ii
-
- go imp.load(path, ii)
+ go func() {
+ ii.Complete(imp.load(path))
+ }()
}
imp.importedMu.Unlock()
return ii
}
-func (imp *importer) load(path string, ii *importInfo) {
- var info *PackageInfo
- var err error
- // Find and create the actual package.
- if _, ok := imp.conf.ImportPkgs[path]; ok || !imp.conf.ImportFromBinary {
- info, err = imp.loadFromSource(path)
- } else {
- info, err = imp.importFromBinary(path)
- }
- ii.Complete(info, err)
-}
-
-// importFromBinary implements package loading from the client-supplied
-// external source, e.g. object files from the gc compiler.
-//
-func (imp *importer) importFromBinary(path string) (*PackageInfo, error) {
- // Determine the caller's effective Import function.
- importfn := imp.conf.TypeChecker.Import
- if importfn == nil {
- importfn = gcimporter.Import
- }
- imp.typecheckerMu.Lock()
- pkg, err := importfn(imp.conf.TypeChecker.Packages, path)
- if pkg != nil {
- imp.conf.TypeChecker.Packages[path] = pkg
- }
- imp.typecheckerMu.Unlock()
- if err != nil {
- return nil, err
- }
- info := &PackageInfo{Pkg: pkg}
- info.Importable = true
- imp.typecheckerMu.Lock()
- imp.prog.AllPackages[pkg] = info
- imp.typecheckerMu.Unlock()
- return info, nil
-}
-
-// loadFromSource implements package loading by parsing Go source files
+// load implements package loading by parsing Go source files
// located by go/build.
-// The returned PackageInfo's typeCheck function must be called.
//
-func (imp *importer) loadFromSource(path string) (*PackageInfo, error) {
+func (imp *importer) load(path string) (*PackageInfo, error) {
bp, err := imp.conf.FindPackage(imp.conf.build(), path)
if err != nil {
return nil, err // package not found
@@ -943,9 +848,9 @@ func (imp *importer) loadFromSource(path string) (*PackageInfo, error) {
imp.addFiles(info, files, true)
- imp.typecheckerMu.Lock()
- imp.conf.TypeChecker.Packages[path] = info.Pkg
- imp.typecheckerMu.Unlock()
+ imp.progMu.Lock()
+ imp.prog.importMap[path] = info.Pkg
+ imp.progMu.Unlock()
return info, nil
}
@@ -985,9 +890,6 @@ func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck b
func (imp *importer) newPackageInfo(path string) *PackageInfo {
pkg := types.NewPackage(path, "")
- if imp.conf.PackageCreated != nil {
- imp.conf.PackageCreated(pkg)
- }
info := &PackageInfo{
Pkg: pkg,
Info: types.Info{
@@ -1013,8 +915,8 @@ func (imp *importer) newPackageInfo(path string) *PackageInfo {
tc.Error = info.appendError // appendError wraps the user's Error function
info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
- imp.typecheckerMu.Lock()
+ imp.progMu.Lock()
imp.prog.AllPackages[pkg] = info
- imp.typecheckerMu.Unlock()
+ imp.progMu.Unlock()
return info
}
diff --git a/go/loader/source_test.go b/go/loader/source_test.go
index f2e06be5..9f6839a7 100644
--- a/go/loader/source_test.go
+++ b/go/loader/source_test.go
@@ -16,6 +16,7 @@ import (
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
)
// findInterval parses input and returns the [start, end) positions of
@@ -100,7 +101,7 @@ func TestEnclosingFunction(t *testing.T) {
t.Error(err)
continue
}
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(iprog, 0)
pkg := prog.Package(iprog.Created[0].Pkg)
pkg.Build()
diff --git a/go/pointer/example_test.go b/go/pointer/example_test.go
index 5f2e9402..ba70557b 100644
--- a/go/pointer/example_test.go
+++ b/go/pointer/example_test.go
@@ -12,6 +12,7 @@ import (
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
)
// This program demonstrates how to use the pointer analysis to
@@ -61,7 +62,7 @@ func main() {
}
// Create SSA-form program representation.
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(iprog, 0)
mainPkg := prog.Package(iprog.Created[0].Pkg)
// Build SSA code for bodies of all functions in the whole program.
diff --git a/go/pointer/pointer_test.go b/go/pointer/pointer_test.go
index 1daf9c3d..2ea7ef6c 100644
--- a/go/pointer/pointer_test.go
+++ b/go/pointer/pointer_test.go
@@ -172,7 +172,7 @@ func doOneInput(input, filename string) bool {
mainPkgInfo := iprog.Created[0].Pkg
// SSA creation + building.
- prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
+ prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
prog.BuildAll()
mainpkg := prog.Package(mainPkgInfo)
diff --git a/go/pointer/stdlib_test.go b/go/pointer/stdlib_test.go
index 63652794..21afdf27 100644
--- a/go/pointer/stdlib_test.go
+++ b/go/pointer/stdlib_test.go
@@ -47,7 +47,7 @@ func TestStdlib(t *testing.T) {
}
// Create SSA packages.
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(iprog, 0)
prog.BuildAll()
numPkgs := len(prog.AllPackages())
diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go
index cb30ef6f..e7ac8381 100644
--- a/go/ssa/builder_test.go
+++ b/go/ssa/builder_test.go
@@ -6,6 +6,9 @@ package ssa_test
import (
"bytes"
+ "go/ast"
+ "go/parser"
+ "go/token"
"reflect"
"sort"
"strings"
@@ -15,14 +18,16 @@ import (
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
+
+ _ "golang.org/x/tools/go/gcimporter"
)
func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
// Tests that programs partially loaded from gc object files contain
// functions with no code for the external portions, but are otherwise ok.
-func TestImportFromBinary(t *testing.T) {
- test := `
+func TestBuildPackage(t *testing.T) {
+ input := `
package main
import (
@@ -42,24 +47,22 @@ func main() {
}
`
- // Create a single-file main package.
- conf := loader.Config{ImportFromBinary: true}
- f, err := conf.ParseFile("", test)
- if err != nil {
- t.Error(err)
- return
- }
- conf.CreateFromFiles("main", f)
-
- iprog, err := conf.Load()
+ // Parse the file.
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "input.go", input, 0)
if err != nil {
t.Error(err)
return
}
- prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
- mainPkg := prog.Package(iprog.Created[0].Pkg)
- mainPkg.Build()
+ // Build an SSA program from the parsed file.
+ // Load its dependencies from gc binary export data.
+ mainPkg, _, err := ssautil.BuildPackage(new(types.Config), fset,
+ types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
+ if err != nil {
+ t.Error(err)
+ return
+ }
// The main package, its direct and indirect dependencies are loaded.
deps := []string{
@@ -69,6 +72,7 @@ func main() {
"errors", "fmt", "os", "runtime",
}
+ prog := mainPkg.Prog
all := prog.AllPackages()
if len(all) <= len(deps) {
t.Errorf("unexpected set of loaded packages: %q", all)
@@ -211,25 +215,25 @@ func TestRuntimeTypes(t *testing.T) {
},
}
for _, test := range tests {
- // Create a single-file main package.
- conf := loader.Config{ImportFromBinary: true}
- f, err := conf.ParseFile("", test.input)
+ // Parse the file.
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "input.go", test.input, 0)
if err != nil {
t.Errorf("test %q: %s", test.input[:15], err)
continue
}
- conf.CreateFromFiles("p", f)
- iprog, err := conf.Load()
+ // Create a single-file main package.
+ // Load dependencies from gc binary export data.
+ ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset,
+ types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
if err != nil {
- t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
+ t.Errorf("test %q: %s", test.input[:15], err)
continue
}
- prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
- prog.BuildAll()
var typstrs []string
- for _, T := range prog.RuntimeTypes() {
+ for _, T := range ssapkg.Prog.RuntimeTypes() {
typstrs = append(typstrs, T.String())
}
sort.Strings(typstrs)
@@ -241,7 +245,7 @@ func TestRuntimeTypes(t *testing.T) {
}
}
-// Tests that synthesized init functions are correctly formed.
+// TestInit tests that synthesized init functions are correctly formed.
// Bare init functions omit calls to dependent init functions and the use of
// an init guard. They are useful in cases where the client uses a different
// calling convention for init functions, or cases where it is easier for a
@@ -292,13 +296,13 @@ func init():
}
conf.CreateFromFiles(f.Name.Name, f)
- iprog, err := conf.Load()
+ lprog, err := conf.Load()
if err != nil {
t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
continue
}
- prog := ssa.Create(iprog, test.mode)
- mainPkg := prog.Package(iprog.Created[0].Pkg)
+ prog := ssautil.CreateProgram(lprog, test.mode)
+ mainPkg := prog.Package(lprog.Created[0].Pkg)
prog.BuildAll()
initFunc := mainPkg.Func("init")
if initFunc == nil {
@@ -363,13 +367,13 @@ var (
conf.CreateFromFiles(f.Name.Name, f)
// Load
- iprog, err := conf.Load()
+ lprog, err := conf.Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
// Create and build SSA
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(lprog, 0)
prog.BuildAll()
// Enumerate reachable synthetic functions
diff --git a/go/ssa/create.go b/go/ssa/create.go
index c13facee..88226aeb 100644
--- a/go/ssa/create.go
+++ b/go/ssa/create.go
@@ -14,7 +14,6 @@ import (
"os"
"sync"
- "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
@@ -40,29 +39,6 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
return prog
}
-// Create returns a new SSA Program. An SSA Package is created for
-// each transitively error-free package of lprog.
-//
-// Code for bodies of functions is not built until BuildAll() is called
-// on the result.
-//
-// mode controls diagnostics and checking during SSA construction.
-//
-// TODO(adonovan): move this to ssautil and breaking the go/ssa ->
-// go/loader dependency.
-//
-func Create(lprog *loader.Program, mode BuilderMode) *Program {
- prog := NewProgram(lprog.Fset, mode)
-
- for _, info := range lprog.AllPackages {
- if info.TransitivelyErrorFree {
- prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
- }
- }
-
- return prog
-}
-
// memberFromObject populates package pkg with a member for the
// typechecker object obj.
//
diff --git a/go/ssa/example_test.go b/go/ssa/example_test.go
index 9e6bcfda..3e095b87 100644
--- a/go/ssa/example_test.go
+++ b/go/ssa/example_test.go
@@ -48,7 +48,7 @@ func main() {
// with similar functionality. It is located at
// golang.org/x/tools/cmd/ssadump.
//
-func ExampleLoadPackage() {
+func ExampleBuildPackage() {
// Parse the source files.
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments)
@@ -63,7 +63,7 @@ func ExampleLoadPackage() {
// Type-check the package, load dependencies.
// Create and build the SSA program.
- hello, _, err := ssautil.LoadPackage(
+ hello, _, err := ssautil.BuildPackage(
new(types.Config), fset, pkg, files, ssa.SanityCheckFunctions)
if err != nil {
fmt.Print(err) // type error in some package
diff --git a/go/ssa/interp/interp_test.go b/go/ssa/interp/interp_test.go
index 569d9eaa..93e1097d 100644
--- a/go/ssa/interp/interp_test.go
+++ b/go/ssa/interp/interp_test.go
@@ -19,6 +19,7 @@ import (
"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"
)
@@ -217,7 +218,7 @@ func run(t *testing.T, dir, input string, success successPredicate) bool {
return false
}
- prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
+ prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
prog.BuildAll()
var mainPkg *ssa.Package
@@ -345,7 +346,7 @@ func TestNullTestmainPackage(t *testing.T) {
if err != nil {
t.Fatalf("CreatePackages failed: %s", err)
}
- prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
+ prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
mainPkg := prog.Package(iprog.Created[0].Pkg)
if mainPkg.Func("main") != nil {
t.Fatalf("unexpected main function")
diff --git a/go/ssa/source_test.go b/go/ssa/source_test.go
index 68b5401c..36925c47 100644
--- a/go/ssa/source_test.go
+++ b/go/ssa/source_test.go
@@ -20,6 +20,7 @@ import (
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
)
@@ -54,7 +55,7 @@ func TestObjValueLookup(t *testing.T) {
return
}
- prog := ssa.Create(iprog, 0 /*|ssa.PrintFunctions*/)
+ prog := ssautil.CreateProgram(iprog, 0 /*|ssa.PrintFunctions*/)
mainInfo := iprog.Created[0]
mainPkg := prog.Package(mainInfo.Pkg)
mainPkg.SetDebugMode(true)
@@ -204,7 +205,7 @@ func TestValueForExpr(t *testing.T) {
mainInfo := iprog.Created[0]
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(iprog, 0)
mainPkg := prog.Package(mainInfo.Pkg)
mainPkg.SetDebugMode(true)
mainPkg.Build()
diff --git a/go/ssa/ssautil/load.go b/go/ssa/ssautil/load.go
index f3090075..c2b8ce13 100644
--- a/go/ssa/ssautil/load.go
+++ b/go/ssa/ssautil/load.go
@@ -25,16 +25,23 @@ import (
// mode controls diagnostics and checking during SSA construction.
//
func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
- // TODO(adonovan): inline and delete ssa.Create.
- return ssa.Create(lprog, mode)
+ prog := ssa.NewProgram(lprog.Fset, mode)
+
+ for _, info := range lprog.AllPackages {
+ if info.TransitivelyErrorFree {
+ prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
+ }
+ }
+
+ return prog
}
-// LoadPackage builds SSA code for a single package.
+// BuildPackage builds an SSA program with IR for a single package.
//
// It populates pkg by type-checking the specified file ASTs. All
// dependencies are loaded using the importer specified by tc, which
// typically loads compiler export data; SSA code cannot be built for
-// those packages. LoadPackage then constructs an ssa.Program with all
+// those packages. BuildPackage then constructs an ssa.Program with all
// dependency packages created, and builds and returns the SSA package
// corresponding to pkg.
//
@@ -42,30 +49,14 @@ func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
//
// The operation fails if there were any type-checking or import errors.
//
-// LoadPackage modifies the tc.Import field.
+// See ../ssa/example_test.go for an example.
//
-func LoadPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
+func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
if fset == nil {
panic("no token.FileSet")
}
if pkg.Path() == "" {
- panic("no package path")
- }
-
- // client's effective import function
- clientImport := tc.Import
- if clientImport == nil {
- clientImport = types.DefaultImport
- }
-
- deps := make(map[*types.Package]bool)
-
- tc.Import = func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
- pkg, err = clientImport(packages, path)
- if pkg != nil {
- deps[pkg] = true
- }
- return
+ panic("package has no import path")
}
info := &types.Info{
@@ -80,11 +71,24 @@ func LoadPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, file
return nil, nil, err
}
- // Create the SSA program and its packages.
prog := ssa.NewProgram(fset, mode)
- for dep := range deps {
- prog.CreatePackage(dep, nil, nil, true)
+
+ // Create SSA packages for all imports.
+ // Order is not significant.
+ created := make(map[*types.Package]bool)
+ var createAll func(pkgs []*types.Package)
+ createAll = func(pkgs []*types.Package) {
+ for _, p := range pkgs {
+ if !created[p] {
+ created[p] = true
+ prog.CreatePackage(p, nil, nil, true)
+ createAll(p.Imports())
+ }
+ }
}
+ createAll(pkg.Imports())
+
+ // Create and build the primary package.
ssapkg := prog.CreatePackage(pkg, files, info, false)
ssapkg.Build()
return ssapkg, info, nil
diff --git a/go/ssa/ssautil/load_test.go b/go/ssa/ssautil/load_test.go
index 3e6a5092..458d2dc3 100644
--- a/go/ssa/ssautil/load_test.go
+++ b/go/ssa/ssautil/load_test.go
@@ -13,6 +13,8 @@ import (
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
+
+ _ "golang.org/x/tools/go/gcimporter"
)
const hello = `package main
@@ -24,7 +26,10 @@ func main() {
}
`
-func TestLoadPackage(t *testing.T) {
+func TestBuildPackage(t *testing.T) {
+ // There is a more substantial test of BuildPackage and the
+ // SSA program it builds in ../ssa/builder_test.go.
+
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "hello.go", hello, 0)
if err != nil {
@@ -32,7 +37,7 @@ func TestLoadPackage(t *testing.T) {
}
pkg := types.NewPackage("hello", "")
- ssapkg, _, err := ssautil.LoadPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
+ ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
if err != nil {
t.Fatal(err)
}
@@ -45,7 +50,7 @@ func TestLoadPackage(t *testing.T) {
}
}
-func TestLoadPackage_MissingImport(t *testing.T) {
+func TestBuildPackage_MissingImport(t *testing.T) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "bad.go", `package bad; import "missing"`, 0)
if err != nil {
@@ -53,8 +58,8 @@ func TestLoadPackage_MissingImport(t *testing.T) {
}
pkg := types.NewPackage("bad", "")
- ssapkg, _, err := ssautil.LoadPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
+ ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
if err == nil || ssapkg != nil {
- t.Fatal("LoadPackage succeeded unexpectedly")
+ t.Fatal("BuildPackage succeeded unexpectedly")
}
}
diff --git a/go/ssa/ssautil/switch_test.go b/go/ssa/ssautil/switch_test.go
index ae110584..2acbd7e1 100644
--- a/go/ssa/ssautil/switch_test.go
+++ b/go/ssa/ssautil/switch_test.go
@@ -29,7 +29,7 @@ func TestSwitches(t *testing.T) {
return
}
- prog := ssa.Create(iprog, 0)
+ prog := ssautil.CreateProgram(iprog, 0)
mainPkg := prog.Package(iprog.Created[0].Pkg)
mainPkg.Build()
diff --git a/go/ssa/stdlib_test.go b/go/ssa/stdlib_test.go
index 63d031eb..f64a93ac 100644
--- a/go/ssa/stdlib_test.go
+++ b/go/ssa/stdlib_test.go
@@ -57,7 +57,7 @@ func TestStdlib(t *testing.T) {
// Comment out these lines during benchmarking. Approx SSA build costs are noted.
mode |= ssa.SanityCheckFunctions // + 2% space, + 4% time
mode |= ssa.GlobalDebug // +30% space, +18% time
- prog := ssa.Create(iprog, mode)
+ prog := ssautil.CreateProgram(iprog, mode)
t2 := time.Now()
diff --git a/go/ssa/testmain_test.go b/go/ssa/testmain_test.go
index 04a41fc3..56cb6040 100644
--- a/go/ssa/testmain_test.go
+++ b/go/ssa/testmain_test.go
@@ -14,6 +14,7 @@ import (
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
)
func create(t *testing.T, content string) []*ssa.Package {
@@ -30,7 +31,7 @@ func create(t *testing.T, content string) []*ssa.Package {
}
// We needn't call Build.
- return ssa.Create(iprog, ssa.SanityCheckFunctions).AllPackages()
+ return ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions).AllPackages()
}
func TestFindTests(t *testing.T) {
diff --git a/godoc/analysis/analysis.go b/godoc/analysis/analysis.go
index 1f59a4a8..c11ecbda 100644
--- a/godoc/analysis/analysis.go
+++ b/godoc/analysis/analysis.go
@@ -393,7 +393,7 @@ func Run(pta bool, result *Result) {
// Create SSA-form program representation.
// Only the transitively error-free packages are used.
- prog := ssa.Create(iprog, ssa.GlobalDebug)
+ prog := ssautil.CreateProgram(iprog, ssa.GlobalDebug)
// Compute the set of main packages, including testmain.
allPackages := prog.AllPackages()
diff --git a/oracle/callees.go b/oracle/callees.go
index 4bad1f0a..f05c8b28 100644
--- a/oracle/callees.go
+++ b/oracle/callees.go
@@ -13,6 +13,7 @@ import (
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
)
@@ -38,7 +39,7 @@ func callees(q *Query) error {
return err
}
- prog := ssa.Create(lprog, 0)
+ prog := ssautil.CreateProgram(lprog, 0)
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
if err != nil {
diff --git a/oracle/callers.go b/oracle/callers.go
index dcf34c39..159a403a 100644
--- a/oracle/callers.go
+++ b/oracle/callers.go
@@ -11,6 +11,7 @@ import (
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/oracle/serial"
)
@@ -36,7 +37,7 @@ func callers(q *Query) error {
return err
}
- prog := ssa.Create(lprog, 0)
+ prog := ssautil.CreateProgram(lprog, 0)
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
if err != nil {
diff --git a/oracle/callstack.go b/oracle/callstack.go
index 05b6ae99..6f04b603 100644
--- a/oracle/callstack.go
+++ b/oracle/callstack.go
@@ -11,6 +11,7 @@ import (
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/oracle/serial"
)
@@ -43,7 +44,7 @@ func callstack(q *Query) error {
return err
}
- prog := ssa.Create(lprog, 0)
+ prog := ssautil.CreateProgram(lprog, 0)
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
if err != nil {
diff --git a/oracle/peers.go b/oracle/peers.go
index a6c9ece8..9c2a4971 100644
--- a/oracle/peers.go
+++ b/oracle/peers.go
@@ -42,7 +42,7 @@ func peers(q *Query) error {
return err
}
- prog := ssa.Create(lprog, ssa.GlobalDebug)
+ prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
if err != nil {
diff --git a/oracle/pointsto.go b/oracle/pointsto.go
index eacd810c..d366dd3c 100644
--- a/oracle/pointsto.go
+++ b/oracle/pointsto.go
@@ -14,6 +14,7 @@ import (
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial"
)
@@ -44,7 +45,7 @@ func pointsto(q *Query) error {
return err
}
- prog := ssa.Create(lprog, ssa.GlobalDebug)
+ prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
if err != nil {
diff --git a/oracle/whicherrs.go b/oracle/whicherrs.go
index 3ca3fc9f..aaa60682 100644
--- a/oracle/whicherrs.go
+++ b/oracle/whicherrs.go
@@ -47,7 +47,7 @@ func whicherrs(q *Query) error {
return err
}
- prog := ssa.Create(lprog, ssa.GlobalDebug)
+ prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
if err != nil {