diff --git a/cmd/ssadump/main.go b/cmd/ssadump/main.go index 8c4dbd120f..de1254dee6 100644 --- a/cmd/ssadump/main.go +++ b/cmd/ssadump/main.go @@ -15,7 +15,7 @@ import ( "runtime/pprof" "golang.org/x/tools/go/buildutil" - "golang.org/x/tools/go/loader" + "golang.org/x/tools/go/packages" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/interp" "golang.org/x/tools/go/ssa/ssautil" @@ -25,9 +25,9 @@ import ( var ( mode = ssa.BuilderMode(0) - testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.") + testFlag = flag.Bool("test", false, "include implicit test packages and executables") - runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.") + runFlag = flag.Bool("run", false, "interpret the SSA program") interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter. The value is a sequence of zero or more more of these letters: @@ -36,23 +36,25 @@ T [T]race execution of the program. Best for single-threaded programs! `) cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") + + args stringListValue ) func init() { flag.Var(&mode, "build", ssa.BuilderModeDoc) flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) + flag.Var(&args, "arg", "add argument to interpreted program") } const usage = `SSA builder and interpreter. -Usage: ssadump [ ...] ... +Usage: ssadump [-build=[DBCSNFL]] [-test] [-run] [-interp=[TR]] [-arg=...] package... Use -help flag to display options. Examples: % ssadump -build=F hello.go # dump SSA form of a single package % ssadump -build=F -test fmt # dump SSA form of a package and its tests % ssadump -run -interp=T hello.go # interpret a program, with tracing -` + loader.FromArgsUsage + - ` + The -run flag causes ssadump to run the first package named main. Interpretation of the standard "testing" package is no longer supported. @@ -67,17 +69,24 @@ func main() { func doMain() error { flag.Parse() - args := flag.Args() + if len(flag.Args()) == 0 { + fmt.Fprint(os.Stderr, usage) + os.Exit(1) + } - conf := loader.Config{Build: &build.Default} + cfg := &packages.Config{ + Mode: packages.LoadSyntax, + Tests: *testFlag, + } // Choose types.Sizes from conf.Build. + // TODO(adonovan): remove this when go/packages provides a better way. var wordSize int64 = 8 - switch conf.Build.GOARCH { + switch build.Default.GOARCH { case "386", "arm": wordSize = 4 } - conf.TypeChecker.Sizes = &types.StdSizes{ + cfg.TypeChecker.Sizes = &types.StdSizes{ MaxAlign: 8, WordSize: wordSize, } @@ -94,11 +103,6 @@ func doMain() error { } } - if len(args) == 0 { - fmt.Fprint(os.Stderr, usage) - os.Exit(1) - } - // Profiling support. if *cpuprofile != "" { f, err := os.Create(*cpuprofile) @@ -110,58 +114,46 @@ func doMain() error { defer pprof.StopCPUProfile() } - // Use the initial packages from the command line. - args, err := conf.FromArgs(args, *testFlag) - if err != nil { - return err - } - - // The interpreter needs the runtime package. + // Load, parse and type-check the initial packages, + // and, if -run, their dependencies. if *runFlag { - conf.Import("runtime") + cfg.Mode = packages.LoadAllSyntax } - - // Load, parse and type-check the whole program. - lprog, err := conf.Load() + initial, err := packages.Load(cfg, flag.Args()...) if err != nil { return err } + if len(initial) == 0 { + return fmt.Errorf("no packages") + } - // Create and build SSA-form program representation. - prog := ssautil.CreateProgram(lprog, mode) + // Create SSA-form program representation. + prog, pkgs := ssautil.Packages(initial, mode) - // Build and display only the initial packages - // (and synthetic wrappers), unless -run is specified. - var initpkgs []*ssa.Package - for _, info := range lprog.InitialPackages() { - ssapkg := prog.Package(info.Pkg) - ssapkg.Build() - if info.Pkg.Path() != "runtime" { - initpkgs = append(initpkgs, ssapkg) + for i, p := range pkgs { + if p == nil { + return fmt.Errorf("cannot build SSA for package %s", initial[i]) } } - // Run the interpreter. - if *runFlag { + if !*runFlag { + // Build and display only the initial packages + // (and synthetic wrappers). + for _, p := range pkgs { + p.Build() + } + + } else { + // Run the interpreter. + // Build SSA for all packages. prog.Build() - var mains []*ssa.Package - if *testFlag { - // If -test, run the tests. - for _, pkg := range initpkgs { - if main := prog.CreateTestMainPackage(pkg); main != nil { - mains = append(mains, main) - } - } - if mains == nil { - return fmt.Errorf("no tests") - } - } else { - // Otherwise, run the main packages. - mains = ssautil.MainPackages(initpkgs) - if len(mains) == 0 { - return fmt.Errorf("no main package") - } + // The interpreter needs the runtime package. + // It is a limitation of go/packages that + // we cannot add "runtime" to its initial set, + // we can only check that it is present. + if prog.ImportedPackage("runtime") == nil { + return fmt.Errorf("-run: program does not depend on runtime") } if runtime.GOARCH != build.Default.GOARCH { @@ -169,12 +161,27 @@ func doMain() error { build.Default.GOARCH, runtime.GOARCH) } - for _, main := range mains { - if len(mains) > 1 { - fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path()) - } - interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args) + // Run first main package. + for _, main := range ssautil.MainPackages(pkgs) { + fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path()) + os.Exit(interp.Interpret(main, interpMode, cfg.TypeChecker.Sizes, main.Pkg.Path(), args)) } + return fmt.Errorf("no main package") } return nil } + +// stringListValue is a flag.Value that accumulates strings. +// e.g. --flag=one --flag=two would produce []string{"one", "two"}. +type stringListValue []string + +func newStringListValue(val []string, p *[]string) *stringListValue { + *p = val + return (*stringListValue)(p) +} + +func (ss *stringListValue) Get() interface{} { return []string(*ss) } + +func (ss *stringListValue) String() string { return fmt.Sprintf("%q", *ss) } + +func (ss *stringListValue) Set(s string) error { *ss = append(*ss, s); return nil }