go/ssa/ssautil: break ssa->loader dependency
Remove all dependencies from non-test code in go/ssa to go/loader, except the deprecated Create function which will be eliminated in favor of ssautil.CreateProgram in a mechnanical followup. Add Examples of two main use cases of SSA construction: loading a complete program from source; and building a single package, loading its dependencies from import data. Add tests to ssautil of the two load functions. Suggestions welcome for better names. Planned follow-ups: - replace all references to ssa.Create with ssautil.CreateProgram and eliminate it. - eliminate support in go/loader for the ImportBinary flag, and the PackageCreated hook which is no longer needed since clients can create the package themselves (see Example). Step 1 to fixing issue 9955. Change-Id: I4e64df67fcd5b7f0c0388047e06cea247fddfec5 Reviewed-on: https://go-review.googlesource.com/8669 Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
		
							parent
							
								
									14c815ee28
								
							
						
					
					
						commit
						20186168d5
					
				| 
						 | 
					@ -257,6 +257,8 @@ type Config struct {
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// NB: there is a bug when loading multiple initial packages with
 | 
						// NB: there is a bug when loading multiple initial packages with
 | 
				
			||||||
	// this flag enabled: https://github.com/golang/go/issues/9955.
 | 
						// this flag enabled: https://github.com/golang/go/issues/9955.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// THIS FEATURE IS DEPRECATED and will be removed shortly (Apr 2015).
 | 
				
			||||||
	ImportFromBinary bool
 | 
						ImportFromBinary bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If Build is non-nil, it is used to locate source packages.
 | 
						// If Build is non-nil, it is used to locate source packages.
 | 
				
			||||||
| 
						 | 
					@ -321,6 +323,8 @@ type Config struct {
 | 
				
			||||||
	// the package scope, for example.
 | 
						// the package scope, for example.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// It must be safe to call concurrently from multiple goroutines.
 | 
						// 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)
 | 
						PackageCreated func(*types.Package)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2212,8 +2212,13 @@ func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
 | 
				
			||||||
// BuildAll calls Package.Build() for each package in prog.
 | 
					// BuildAll calls Package.Build() for each package in prog.
 | 
				
			||||||
// Building occurs in parallel unless the BuildSerially mode flag was set.
 | 
					// Building occurs in parallel unless the BuildSerially mode flag was set.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					// BuildAll is intended for whole-program analysis; a typical compiler
 | 
				
			||||||
 | 
					// need only build a single package.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
// BuildAll is idempotent and thread-safe.
 | 
					// BuildAll is idempotent and thread-safe.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
 | 
					// TODO(adonovan): rename to Build.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
func (prog *Program) BuildAll() {
 | 
					func (prog *Program) BuildAll() {
 | 
				
			||||||
	var wg sync.WaitGroup
 | 
						var wg sync.WaitGroup
 | 
				
			||||||
	for _, p := range prog.packages {
 | 
						for _, p := range prog.packages {
 | 
				
			||||||
| 
						 | 
					@ -2245,7 +2250,7 @@ func (p *Package) Build() {
 | 
				
			||||||
	if p.info == nil {
 | 
						if p.info == nil {
 | 
				
			||||||
		return // synthetic package, e.g. "testmain"
 | 
							return // synthetic package, e.g. "testmain"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(p.info.Files) == 0 {
 | 
						if p.files == nil {
 | 
				
			||||||
		p.info = nil
 | 
							p.info = nil
 | 
				
			||||||
		return // package loaded from export data
 | 
							return // package loaded from export data
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -2276,7 +2281,7 @@ func (p *Package) Build() {
 | 
				
			||||||
		emitStore(init, initguard, vTrue, token.NoPos)
 | 
							emitStore(init, initguard, vTrue, token.NoPos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Call the init() function of each package we import.
 | 
							// Call the init() function of each package we import.
 | 
				
			||||||
		for _, pkg := range p.info.Pkg.Imports() {
 | 
							for _, pkg := range p.Object.Imports() {
 | 
				
			||||||
			prereq := p.Prog.packages[pkg]
 | 
								prereq := p.Prog.packages[pkg]
 | 
				
			||||||
			if prereq == nil {
 | 
								if prereq == nil {
 | 
				
			||||||
				panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Object.Path(), pkg.Path()))
 | 
									panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Object.Path(), pkg.Path()))
 | 
				
			||||||
| 
						 | 
					@ -2321,7 +2326,7 @@ func (p *Package) Build() {
 | 
				
			||||||
	// Build all package-level functions, init functions
 | 
						// Build all package-level functions, init functions
 | 
				
			||||||
	// and methods, including unreachable/blank ones.
 | 
						// and methods, including unreachable/blank ones.
 | 
				
			||||||
	// We build them in source order, but it's not significant.
 | 
						// We build them in source order, but it's not significant.
 | 
				
			||||||
	for _, file := range p.info.Files {
 | 
						for _, file := range p.files {
 | 
				
			||||||
		for _, decl := range file.Decls {
 | 
							for _, decl := range file.Decls {
 | 
				
			||||||
			if decl, ok := decl.(*ast.FuncDecl); ok {
 | 
								if decl, ok := decl.(*ast.FuncDecl); ok {
 | 
				
			||||||
				b.buildFuncDecl(p, decl)
 | 
									b.buildFuncDecl(p, decl)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,17 +19,13 @@ import (
 | 
				
			||||||
	"golang.org/x/tools/go/types/typeutil"
 | 
						"golang.org/x/tools/go/types/typeutil"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create returns a new SSA Program.  An SSA Package is created for
 | 
					// NewProgram returns a new SSA Program.
 | 
				
			||||||
// each transitively error-free package of iprog.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Code for bodies of functions is not built until Build() is called
 | 
					 | 
				
			||||||
// on the result.
 | 
					 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// mode controls diagnostics and checking during SSA construction.
 | 
					// mode controls diagnostics and checking during SSA construction.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func Create(iprog *loader.Program, mode BuilderMode) *Program {
 | 
					func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
 | 
				
			||||||
	prog := &Program{
 | 
						prog := &Program{
 | 
				
			||||||
		Fset:     iprog.Fset,
 | 
							Fset:     fset,
 | 
				
			||||||
		imported: make(map[string]*Package),
 | 
							imported: make(map[string]*Package),
 | 
				
			||||||
		packages: make(map[*types.Package]*Package),
 | 
							packages: make(map[*types.Package]*Package),
 | 
				
			||||||
		thunks:   make(map[selectionKey]*Function),
 | 
							thunks:   make(map[selectionKey]*Function),
 | 
				
			||||||
| 
						 | 
					@ -41,11 +37,26 @@ func Create(iprog *loader.Program, mode BuilderMode) *Program {
 | 
				
			||||||
	prog.methodSets.SetHasher(h)
 | 
						prog.methodSets.SetHasher(h)
 | 
				
			||||||
	prog.canon.SetHasher(h)
 | 
						prog.canon.SetHasher(h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, info := range iprog.AllPackages {
 | 
						return prog
 | 
				
			||||||
		// TODO(adonovan): relax this constraint if the
 | 
					}
 | 
				
			||||||
		// program contains only "soft" errors.
 | 
					
 | 
				
			||||||
 | 
					// 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 {
 | 
							if info.TransitivelyErrorFree {
 | 
				
			||||||
			prog.CreatePackage(info)
 | 
								prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,26 +171,24 @@ func membersFromDecl(pkg *Package, decl ast.Decl) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CreatePackage constructs and returns an SSA Package from an
 | 
					// CreatePackage constructs and returns an SSA Package from the
 | 
				
			||||||
// error-free package described by info, and populates its Members
 | 
					// specified type-checked, error-free file ASTs, and populates its
 | 
				
			||||||
// mapping.
 | 
					// Members mapping.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Repeated calls with the same info return the same Package.
 | 
					// importable determines whether this package should be returned by a
 | 
				
			||||||
 | 
					// subsequent call to ImportedPackage(pkg.Path()).
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// The real work of building SSA form for each function is not done
 | 
					// The real work of building SSA form for each function is not done
 | 
				
			||||||
// until a subsequent call to Package.Build().
 | 
					// until a subsequent call to Package.Build().
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func (prog *Program) CreatePackage(info *loader.PackageInfo) *Package {
 | 
					func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
 | 
				
			||||||
	if p := prog.packages[info.Pkg]; p != nil {
 | 
					 | 
				
			||||||
		return p // already loaded
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p := &Package{
 | 
						p := &Package{
 | 
				
			||||||
		Prog:    prog,
 | 
							Prog:    prog,
 | 
				
			||||||
		Members: make(map[string]Member),
 | 
							Members: make(map[string]Member),
 | 
				
			||||||
		values:  make(map[types.Object]Value),
 | 
							values:  make(map[types.Object]Value),
 | 
				
			||||||
		Object:  info.Pkg,
 | 
							Object:  pkg,
 | 
				
			||||||
		info:    info,  // transient (CREATE and BUILD phases)
 | 
							info:    info,  // transient (CREATE and BUILD phases)
 | 
				
			||||||
 | 
							files:   files, // transient (CREATE and BUILD phases)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Add init() function.
 | 
						// Add init() function.
 | 
				
			||||||
| 
						 | 
					@ -194,9 +203,9 @@ func (prog *Program) CreatePackage(info *loader.PackageInfo) *Package {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// CREATE phase.
 | 
						// CREATE phase.
 | 
				
			||||||
	// Allocate all package members: vars, funcs, consts and types.
 | 
						// Allocate all package members: vars, funcs, consts and types.
 | 
				
			||||||
	if len(info.Files) > 0 {
 | 
						if len(files) > 0 {
 | 
				
			||||||
		// Go source package.
 | 
							// Go source package.
 | 
				
			||||||
		for _, file := range info.Files {
 | 
							for _, file := range files {
 | 
				
			||||||
			for _, decl := range file.Decls {
 | 
								for _, decl := range file.Decls {
 | 
				
			||||||
				membersFromDecl(p, decl)
 | 
									membersFromDecl(p, decl)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -238,8 +247,8 @@ func (prog *Program) CreatePackage(info *loader.PackageInfo) *Package {
 | 
				
			||||||
		printMu.Unlock()
 | 
							printMu.Unlock()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if info.Importable {
 | 
						if importable {
 | 
				
			||||||
		prog.imported[info.Pkg.Path()] = p
 | 
							prog.imported[p.Object.Path()] = p
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	prog.packages[p.Object] = p
 | 
						prog.packages[p.Object] = p
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,28 +8,17 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go/ast"
 | 
				
			||||||
 | 
						"go/parser"
 | 
				
			||||||
 | 
						"go/token"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/tools/go/loader"
 | 
						"golang.org/x/tools/go/loader"
 | 
				
			||||||
	"golang.org/x/tools/go/ssa"
 | 
						"golang.org/x/tools/go/ssa"
 | 
				
			||||||
 | 
						"golang.org/x/tools/go/ssa/ssautil"
 | 
				
			||||||
 | 
						"golang.org/x/tools/go/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This program demonstrates how to run the SSA builder on a "Hello,
 | 
					const hello = `
 | 
				
			||||||
// World!" program and shows the printed representation of packages,
 | 
					 | 
				
			||||||
// functions and instructions.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Within the function listing, the name of each BasicBlock such as
 | 
					 | 
				
			||||||
// ".0.entry" is printed left-aligned, followed by the block's
 | 
					 | 
				
			||||||
// Instructions.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// For each instruction that defines an SSA virtual register
 | 
					 | 
				
			||||||
// (i.e. implements Value), the type of that value is shown in the
 | 
					 | 
				
			||||||
// right column.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Build and run the ssadump.go program if you want a standalone tool
 | 
					 | 
				
			||||||
// with similar functionality. It is located at
 | 
					 | 
				
			||||||
// golang.org/x/tools/cmd/ssadump.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
func Example() {
 | 
					 | 
				
			||||||
	const hello = `
 | 
					 | 
				
			||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "fmt"
 | 
					import "fmt"
 | 
				
			||||||
| 
						 | 
					@ -40,49 +29,64 @@ func main() {
 | 
				
			||||||
	fmt.Println(message)
 | 
						fmt.Println(message)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
	var conf loader.Config
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Parse the input file.
 | 
					// This program demonstrates how to run the SSA builder on a single
 | 
				
			||||||
	file, err := conf.ParseFile("hello.go", hello)
 | 
					// package of one or more already-parsed files.  Its dependencies are
 | 
				
			||||||
 | 
					// loaded from compiler export data.  This is what you'd typically use
 | 
				
			||||||
 | 
					// for a compiler; it does not depend on golang.org/x/tools/go/loader.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// It shows the printed representation of packages, functions, and
 | 
				
			||||||
 | 
					// instructions.  Within the function listing, the name of each
 | 
				
			||||||
 | 
					// BasicBlock such as ".0.entry" is printed left-aligned, followed by
 | 
				
			||||||
 | 
					// the block's Instructions.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// For each instruction that defines an SSA virtual register
 | 
				
			||||||
 | 
					// (i.e. implements Value), the type of that value is shown in the
 | 
				
			||||||
 | 
					// right column.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Build and run the ssadump.go program if you want a standalone tool
 | 
				
			||||||
 | 
					// with similar functionality. It is located at
 | 
				
			||||||
 | 
					// golang.org/x/tools/cmd/ssadump.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					func ExampleLoadPackage() {
 | 
				
			||||||
 | 
						// Parse the source files.
 | 
				
			||||||
 | 
						fset := token.NewFileSet()
 | 
				
			||||||
 | 
						f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Print(err) // parse error
 | 
							fmt.Print(err) // parse error
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						files := []*ast.File{f}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create single-file main package.
 | 
						// Create the type-checker's package.
 | 
				
			||||||
	conf.CreateFromFiles("main", file)
 | 
						pkg := types.NewPackage("hello", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Load the main package and its dependencies.
 | 
						// Type-check the package, load dependencies.
 | 
				
			||||||
	iprog, err := conf.Load()
 | 
						// Create and build the SSA program.
 | 
				
			||||||
 | 
						hello, _, err := ssautil.LoadPackage(
 | 
				
			||||||
 | 
							new(types.Config), fset, pkg, files, ssa.SanityCheckFunctions)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Print(err) // type error in some package
 | 
							fmt.Print(err) // type error in some package
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create SSA-form program representation.
 | 
					 | 
				
			||||||
	prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
 | 
					 | 
				
			||||||
	mainPkg := prog.Package(iprog.Created[0].Pkg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Print out the package.
 | 
						// Print out the package.
 | 
				
			||||||
	mainPkg.WriteTo(os.Stdout)
 | 
						hello.WriteTo(os.Stdout)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Build SSA code for bodies of functions in mainPkg.
 | 
					 | 
				
			||||||
	mainPkg.Build()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Print out the package-level functions.
 | 
						// Print out the package-level functions.
 | 
				
			||||||
	mainPkg.Func("init").WriteTo(os.Stdout)
 | 
						hello.Func("init").WriteTo(os.Stdout)
 | 
				
			||||||
	mainPkg.Func("main").WriteTo(os.Stdout)
 | 
						hello.Func("main").WriteTo(os.Stdout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Output:
 | 
						// Output:
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// package main:
 | 
						// package hello:
 | 
				
			||||||
	//   func  init       func()
 | 
						//   func  init       func()
 | 
				
			||||||
	//   var   init$guard bool
 | 
						//   var   init$guard bool
 | 
				
			||||||
	//   func  main       func()
 | 
						//   func  main       func()
 | 
				
			||||||
	//   const message    message = "Hello, World!":untyped string
 | 
						//   const message    message = "Hello, World!":untyped string
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// # Name: main.init
 | 
						// # Name: hello.init
 | 
				
			||||||
	// # Package: main
 | 
						// # Package: hello
 | 
				
			||||||
	// # Synthetic: package initializer
 | 
						// # Synthetic: package initializer
 | 
				
			||||||
	// func init():
 | 
						// func init():
 | 
				
			||||||
	// 0:                                                                entry P:0 S:2
 | 
						// 0:                                                                entry P:0 S:2
 | 
				
			||||||
| 
						 | 
					@ -95,8 +99,8 @@ func main() {
 | 
				
			||||||
	// 2:                                                            init.done P:2 S:0
 | 
						// 2:                                                            init.done P:2 S:0
 | 
				
			||||||
	// 	return
 | 
						// 	return
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// # Name: main.main
 | 
						// # Name: hello.main
 | 
				
			||||||
	// # Package: main
 | 
						// # Package: hello
 | 
				
			||||||
	// # Location: hello.go:8:6
 | 
						// # Location: hello.go:8:6
 | 
				
			||||||
	// func main():
 | 
						// func main():
 | 
				
			||||||
	// 0:                                                                entry P:0 S:0
 | 
						// 0:                                                                entry P:0 S:0
 | 
				
			||||||
| 
						 | 
					@ -108,3 +112,27 @@ func main() {
 | 
				
			||||||
	// 	t4 = fmt.Println(t3...)                              (n int, err error)
 | 
						// 	t4 = fmt.Println(t3...)                              (n int, err error)
 | 
				
			||||||
	// 	return
 | 
						// 	return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This program shows how to load a main package (cmd/nm) and all its
 | 
				
			||||||
 | 
					// dependencies from source, using the loader, and then build SSA code
 | 
				
			||||||
 | 
					// for the entire program.  This is what you'd typically use for a
 | 
				
			||||||
 | 
					// whole-program analysis.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					func ExampleLoadProgram() {
 | 
				
			||||||
 | 
						// Load cmd/nm and its dependencies.
 | 
				
			||||||
 | 
						var conf loader.Config
 | 
				
			||||||
 | 
						conf.Import("cmd/nm")
 | 
				
			||||||
 | 
						lprog, err := conf.Load()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Print(err) // type error in some package
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create SSA-form program representation.
 | 
				
			||||||
 | 
						prog := ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Build SSA code for the entire cmd/nm program.
 | 
				
			||||||
 | 
						prog.BuildAll()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Output:
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -140,7 +140,7 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
 | 
				
			||||||
//    - f was not built with debug information; or
 | 
					//    - f was not built with debug information; or
 | 
				
			||||||
//    - e is a constant expression.  (For efficiency, no debug
 | 
					//    - e is a constant expression.  (For efficiency, no debug
 | 
				
			||||||
//      information is stored for constants. Use
 | 
					//      information is stored for constants. Use
 | 
				
			||||||
//      loader.PackageInfo.ValueOf(e) instead.)
 | 
					//      go/types.Info.Types[e].Value instead.)
 | 
				
			||||||
//    - e is a reference to nil or a built-in function.
 | 
					//    - e is a reference to nil or a built-in function.
 | 
				
			||||||
//    - the value was optimised away.
 | 
					//    - the value was optimised away.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,6 @@ import (
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/tools/go/exact"
 | 
						"golang.org/x/tools/go/exact"
 | 
				
			||||||
	"golang.org/x/tools/go/loader"
 | 
					 | 
				
			||||||
	"golang.org/x/tools/go/types"
 | 
						"golang.org/x/tools/go/types"
 | 
				
			||||||
	"golang.org/x/tools/go/types/typeutil"
 | 
						"golang.org/x/tools/go/types/typeutil"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -56,7 +55,8 @@ type Package struct {
 | 
				
			||||||
	// after building.
 | 
						// after building.
 | 
				
			||||||
	started int32       // atomically tested and set at start of build phase
 | 
						started int32       // atomically tested and set at start of build phase
 | 
				
			||||||
	ninit   int32       // number of init functions
 | 
						ninit   int32       // number of init functions
 | 
				
			||||||
	info    *loader.PackageInfo // package ASTs and type information
 | 
						info    *types.Info // package type information
 | 
				
			||||||
 | 
						files   []*ast.File // package ASTs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A Member is a member of a Go package, implemented by *NamedConst,
 | 
					// A Member is a member of a Go package, implemented by *NamedConst,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,91 @@
 | 
				
			||||||
 | 
					// Copyright 2015 The Go Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package ssautil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This file defines utility functions for constructing programs in SSA form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"go/ast"
 | 
				
			||||||
 | 
						"go/token"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/tools/go/loader"
 | 
				
			||||||
 | 
						"golang.org/x/tools/go/ssa"
 | 
				
			||||||
 | 
						"golang.org/x/tools/go/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CreateProgram returns a new program in SSA form, given a program
 | 
				
			||||||
 | 
					// loaded from source.  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.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
 | 
				
			||||||
 | 
						// TODO(adonovan): inline and delete ssa.Create.
 | 
				
			||||||
 | 
						return ssa.Create(lprog, mode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoadPackage builds SSA code 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
 | 
				
			||||||
 | 
					// dependency packages created, and builds and returns the SSA package
 | 
				
			||||||
 | 
					// corresponding to pkg.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The caller must have set pkg.Path() to the import path.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The operation fails if there were any type-checking or import errors.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// LoadPackage modifies the tc.Import field.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					func LoadPackage(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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						info := &types.Info{
 | 
				
			||||||
 | 
							Types:      make(map[ast.Expr]types.TypeAndValue),
 | 
				
			||||||
 | 
							Defs:       make(map[*ast.Ident]types.Object),
 | 
				
			||||||
 | 
							Uses:       make(map[*ast.Ident]types.Object),
 | 
				
			||||||
 | 
							Implicits:  make(map[ast.Node]types.Object),
 | 
				
			||||||
 | 
							Scopes:     make(map[ast.Node]*types.Scope),
 | 
				
			||||||
 | 
							Selections: make(map[*ast.SelectorExpr]*types.Selection),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ssapkg := prog.CreatePackage(pkg, files, info, false)
 | 
				
			||||||
 | 
						ssapkg.Build()
 | 
				
			||||||
 | 
						return ssapkg, info, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					// Copyright 2015 The Go Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a BSD-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package ssautil_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"go/ast"
 | 
				
			||||||
 | 
						"go/parser"
 | 
				
			||||||
 | 
						"go/token"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/tools/go/ssa/ssautil"
 | 
				
			||||||
 | 
						"golang.org/x/tools/go/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const hello = `package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						fmt.Println("Hello, world")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestLoadPackage(t *testing.T) {
 | 
				
			||||||
 | 
						fset := token.NewFileSet()
 | 
				
			||||||
 | 
						f, err := parser.ParseFile(fset, "hello.go", hello, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkg := types.NewPackage("hello", "")
 | 
				
			||||||
 | 
						ssapkg, _, err := ssautil.LoadPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if pkg.Name() != "main" {
 | 
				
			||||||
 | 
							t.Errorf("pkg.Name() = %s, want main", pkg.Name())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ssapkg.Func("main") == nil {
 | 
				
			||||||
 | 
							ssapkg.WriteTo(os.Stderr)
 | 
				
			||||||
 | 
							t.Errorf("ssapkg has no main function")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestLoadPackage_MissingImport(t *testing.T) {
 | 
				
			||||||
 | 
						fset := token.NewFileSet()
 | 
				
			||||||
 | 
						f, err := parser.ParseFile(fset, "bad.go", `package bad; import "missing"`, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkg := types.NewPackage("bad", "")
 | 
				
			||||||
 | 
						ssapkg, _, err := ssautil.LoadPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
 | 
				
			||||||
 | 
						if err == nil || ssapkg != nil {
 | 
				
			||||||
 | 
							t.Fatal("LoadPackage succeeded unexpectedly")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue