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>
diff --git a/go/loader/loader.go b/go/loader/loader.go
index 41df151..98df7aa 100644
--- a/go/loader/loader.go
+++ b/go/loader/loader.go
@@ -20,13 +20,13 @@
"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 @@
//
// 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 @@
// 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 @@
//
// 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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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) 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 @@
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
}