go/loader: eliminate ImportFromBinary option and PackageCreated hook

The loader package now loads, parses, and type-checks a whole program
from source, and that is all.

Also:
- simplified loader logic
- ssa.Create is gone; use ssautil.CreateProgram.
- ssautil.LoadPackage renamed to BuildPackage.
  It is now independent of go/types' Import hook and the Packages map.
- ssadump: -importbin flag removed.
  The value of this flag was that it caused the tool to print IR
  for only a single package; this is now the normal behaviour.

Fixes #9955

Change-Id: I4571118258ab1a46dccece3241b7dc51401a3acc
Reviewed-on: https://go-review.googlesource.com/8953
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Alan Donovan 2015-04-14 16:56:02 -04:00
parent 1b6275a2ec
commit 9c57c19a58
28 changed files with 155 additions and 251 deletions

View File

@ -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 ------------------------------------------

View File

@ -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 {

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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()

View File

@ -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.

View File

@ -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)

View File

@ -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())

View File

@ -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("<input>", 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("<input>", 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

View File

@ -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.
//

View File

@ -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

View File

@ -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")

View File

@ -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()

View File

@ -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

View File

@ -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")
}
}

View File

@ -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()

View File

@ -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()

View File

@ -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) {

View File

@ -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()

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {