mirror of
https://github.com/golang/go
synced 2024-11-19 01:44:40 -07:00
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"
|
||||||
)
|
)
|
||||||
@ -54,9 +53,10 @@ type Package struct {
|
|||||||
|
|
||||||
// The following fields are set transiently, then cleared
|
// The following fields are set transiently, then cleared
|
||||||
// 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,
|
||||||
|
91
go/ssa/ssautil/load.go
Normal file
91
go/ssa/ssautil/load.go
Normal file
@ -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
|
||||||
|
}
|
60
go/ssa/ssautil/load_test.go
Normal file
60
go/ssa/ssautil/load_test.go
Normal file
@ -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
Block a user