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:
parent
1b6275a2ec
commit
9c57c19a58
|
@ -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 ------------------------------------------
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue