func (imp *importer) newPackageInfo(path string) *PackageInfo { pkg := types.NewPackage(path, "") info := &PackageInfo{ Pkg: pkg, 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), }, errorFunc: imp.conf.TypeChecker.Error, } // Copy the types.Config so we can vary it across PackageInfos. tc := imp.conf.TypeChecker tc.IgnoreFuncBodies = false if f := imp.conf.TypeCheckFuncBodies; f != nil { tc.IgnoreFuncBodies = !f(path) } tc.Import = imp.doImport // doImport wraps the user's importfn, effectively tc.Error = info.appendError // appendError wraps the user's Error function info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info) imp.prog.AllPackages[pkg] = info return info }
// Name = identifier | "?" | QualifiedName . // // If materializePkg is set, the returned package is guaranteed to be set. // For fully qualified names, the returned package may be a fake package // (without name, scope, and not in the p.imports map), created for the // sole purpose of providing a package path. Fake packages are created // when the package id is not found in the p.imports map; in that case // we cannot create a real package because we don't have a package name. // For non-qualified names, the returned package is the imported package. // func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) { switch p.tok { case scanner.Ident: pkg = p.sharedPkgs[p.id] name = p.lit p.next() case '?': // anonymous pkg = p.sharedPkgs[p.id] p.next() case '@': // exported name prefixed with package path var id string id, name = p.parseQualifiedName() if materializePkg { // we don't have a package name - if the package // doesn't exist yet, create a fake package instead pkg = p.getPkg(id, "") if pkg == nil { pkg = types.NewPackage(id, "") } } default: p.error("name expected") } return }
func (p *importer) pkg() *types.Package { // if the package was seen before, i is its index (>= 0) i := p.int() if i >= 0 { return p.pkgList[i] } // otherwise, i is the package tag (< 0) if i != packageTag { panic(fmt.Sprintf("unexpected package tag %d", i)) } // read package data name := p.string() path := p.string() // if the package was imported before, use that one; otherwise create a new one pkg := p.imports[path] if pkg == nil { pkg = types.NewPackage(path, name) p.imports[path] = pkg } p.pkgList = append(p.pkgList, pkg) return pkg }
func TestDemo(ot *testing.T) { importPath := "github.com/bronze1man/kmg/kmgGoSource" kmgCmd.MustRun("kmg go test -i " + importPath) pkgDir := kmgConfig.DefaultEnv().MustGetPathFromImportPath(importPath) fset := token.NewFileSet() astPkgMap, err := parser.ParseDir(fset, pkgDir, nil, 0) if err != nil { panic(err) } astPkg := astPkgMap["kmgGoSource_test"] astFileList := []*ast.File{} for _, file := range astPkg.Files { astFileList = append(astFileList, file) } //os.Chdir(kmgConfig.DefaultEnv().ProjectPath) pkg, err := types.Check(pkgDir, fset, astFileList) if err != nil { panic(err) } funcA := pkg.Scope().Lookup("FuncA") recvPkg := types.NewPackage("github.com/bronze1man/kmg/kmgGoSource", "kmgGoSource") kmgDebug.Println(types.TypeString(recvPkg, funcA.Type())) funTypParams := funcA.Type().(*types.Signature).Params() for i := 0; i < funTypParams.Len(); i++ { kmgDebug.Println(funTypParams.At(i).Name()) kmgDebug.Println(funTypParams.At(i).Type().String()) } //for _,p:=range funcA.Type().(*types.Signature).Params(). //kmgDebug.Println(funcA.Type().(*types.Signature).Params().String()) }
// getPkg returns the package for a given path. If the package is // not found but we have a package name, create the package and // add it to the p.imports map. // func (p *parser) getPkg(pkgpath, name string) *types.Package { // package unsafe is not in the imports map - handle explicitly if pkgpath == "unsafe" { return types.Unsafe } pkg := p.imports[pkgpath] if pkg == nil && name != "" { pkg = types.NewPackage(pkgpath, name) p.imports[pkgpath] = pkg } return pkg }
func TestBuildPackage_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.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0) if err == nil || ssapkg != nil { t.Fatal("BuildPackage succeeded unexpectedly") } }
func (r *Rule) evaluate(d float64) (bool, error) { pkg := types.NewPackage("evaluateme", "evaluateme") v := exact.MakeFloat64(d) pkg.Scope().Insert(types.NewConst(0, pkg, "x", types.Typ[types.Float64], v)) tv, err := types.Eval(token.NewFileSet(), pkg, token.NoPos, r.Condition) if err != nil { return false, fmt.Errorf("Failed to evaluate condition %q: %s", r.Condition, err) } if tv.Type.String() != "untyped bool" { return false, fmt.Errorf("Rule \"%v\" does not return boolean type.", r.Condition) } return exact.BoolVal(tv.Value), nil }
func (s *Session) dumpSSA() (string, error) { pkg := types.NewPackage("goresession", "") if err := s.doQuickFix(); err != nil { debugf("quickfix SSA: %v", err) return "", nil } files := []*ast.File{s.File} ssapkg, _, err := ssautil.BuildPackage(s.Types, s.Fset, pkg, files, ssa.SanityCheckFunctions) if err != nil { fmt.Print(err) // type error in some package return "", err } var b bytes.Buffer ssa.WriteFunction(&b, ssapkg.Func("main")) debugf("ssa :: %s", b.String()) return b.String(), nil }
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 { t.Fatal(err) } pkg := types.NewPackage("hello", "") ssapkg, _, err := ssautil.BuildPackage(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") } }
// getPkg returns the package for a given id. If the package is // not found but we have a package name, create the package and // add it to the p.localPkgs and p.sharedPkgs maps. // // id identifies a package, usually by a canonical package path like // "encoding/json" but possibly by a non-canonical import path like // "./json". // func (p *parser) getPkg(id, name string) *types.Package { // package unsafe is not in the packages maps - handle explicitly if id == "unsafe" { return types.Unsafe } pkg := p.localPkgs[id] if pkg == nil && name != "" { // first import of id from this package pkg = p.sharedPkgs[id] if pkg == nil { // first import of id by this importer pkg = types.NewPackage(id, name) p.sharedPkgs[id] = pkg } if p.localPkgs == nil { p.localPkgs = make(map[string]*types.Package) } p.localPkgs[id] = pkg } return pkg }
// CreateTestMainPackage creates and returns a synthetic "main" // package that runs all the tests of the supplied packages, similar // to the one that would be created by the 'go test' tool. // // It returns nil if the program contains no tests. // func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { pkgs, tests, benchmarks, examples := FindTests(pkgs) if len(pkgs) == 0 { return nil } testmain := &Package{ Prog: prog, Members: make(map[string]Member), values: make(map[types.Object]Value), Object: types.NewPackage("test$main", "main"), } // Build package's init function. init := &Function{ name: "init", Signature: new(types.Signature), Synthetic: "package initializer", Pkg: testmain, Prog: prog, } init.startBody() if testMainStartBodyHook != nil { testMainStartBodyHook(init) } // Initialize packages to test. var pkgpaths []string for _, pkg := range pkgs { var v Call v.Call.Value = pkg.init v.setType(types.NewTuple()) init.emit(&v) pkgpaths = append(pkgpaths, pkg.Object.Path()) } sort.Strings(pkgpaths) init.emit(new(Return)) init.finishBody() testmain.init = init testmain.Object.MarkComplete() testmain.Members[init.name] = init // For debugging convenience, define an unexported const // that enumerates the packages. packagesConst := types.NewConst(token.NoPos, testmain.Object, "packages", tString, exact.MakeString(strings.Join(pkgpaths, " "))) memberFromObject(testmain, packagesConst, nil) // Create main *types.Func and *ssa.Function mainFunc := types.NewFunc(token.NoPos, testmain.Object, "main", new(types.Signature)) memberFromObject(testmain, mainFunc, nil) main := testmain.Func("main") main.Synthetic = "test main function" main.startBody() if testMainStartBodyHook != nil { testMainStartBodyHook(main) } if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { testingMain := testingPkg.Func("Main") testingMainParams := testingMain.Signature.Params() // The generated code is as if compiled from this: // // func main() { // match := func(_, _ string) (bool, error) { return true, nil } // tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...} // benchmarks := []testing.InternalBenchmark{...} // examples := []testing.InternalExample{...} // testing.Main(match, tests, benchmarks, examples) // } matcher := &Function{ name: "matcher", Signature: testingMainParams.At(0).Type().(*types.Signature), Synthetic: "test matcher predicate", parent: main, Pkg: testmain, Prog: prog, } main.AnonFuncs = append(main.AnonFuncs, matcher) matcher.startBody() matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}}) matcher.finishBody() // Emit call: testing.Main(matcher, tests, benchmarks, examples). var c Call c.Call.Value = testingMain c.Call.Args = []Value{ matcher, testMainSlice(main, tests, testingMainParams.At(1).Type()), testMainSlice(main, benchmarks, testingMainParams.At(2).Type()), testMainSlice(main, examples, testingMainParams.At(3).Type()), } emitTailCall(main, &c) } else { // The program does not import "testing", but FindTests // returned non-nil, which must mean there were Examples // but no Tests or Benchmarks. // We'll simply call them from testmain.main; this will // ensure they don't panic, but will not check any // "Output:" comments. for _, eg := range examples { var c Call c.Call.Value = eg c.setType(types.NewTuple()) main.emit(&c) } main.emit(&Return{}) main.currentBlock = nil } main.finishBody() testmain.Members["main"] = main if prog.mode&PrintPackages != 0 { printMu.Lock() testmain.WriteTo(os.Stdout) printMu.Unlock() } if prog.mode&SanityCheckFunctions != 0 { sanityCheckPackage(testmain) } prog.packages[testmain.Object] = testmain return testmain }
// This program demonstrates how to run the SSA builder on a single // 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 ExampleBuildPackage() { // Parse the source files. fset := token.NewFileSet() f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments) if err != nil { fmt.Print(err) // parse error return } files := []*ast.File{f} // Create the type-checker's package. pkg := types.NewPackage("hello", "") // Type-check the package, load dependencies. // Create and build the SSA program. hello, _, err := ssautil.BuildPackage( new(types.Config), fset, pkg, files, ssa.SanityCheckFunctions) if err != nil { fmt.Print(err) // type error in some package return } // Print out the package. hello.WriteTo(os.Stdout) // Print out the package-level functions. hello.Func("init").WriteTo(os.Stdout) hello.Func("main").WriteTo(os.Stdout) // Output: // // package hello: // func init func() // var init$guard bool // func main func() // const message message = "Hello, World!":untyped string // // # Name: hello.init // # Package: hello // # Synthetic: package initializer // func init(): // 0: entry P:0 S:2 // t0 = *init$guard bool // if t0 goto 2 else 1 // 1: init.start P:1 S:1 // *init$guard = true:bool // t1 = fmt.init() () // jump 2 // 2: init.done P:2 S:0 // return // // # Name: hello.main // # Package: hello // # Location: hello.go:8:6 // func main(): // 0: entry P:0 S:0 // t0 = new [1]interface{} (varargs) *[1]interface{} // t1 = &t0[0:int] *interface{} // t2 = make interface{} <- string ("Hello, World!":string) interface{} // *t1 = t2 // t3 = slice t0[:] []interface{} // t4 = fmt.Println(t3...) (n int, err error) // return }
"reflect" "unsafe" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/types" ) type opaqueType struct { types.Type name string } func (t *opaqueType) String() string { return t.name } // A bogus "reflect" type-checker package. Shared across interpreters. var reflectTypesPackage = types.NewPackage("reflect", "reflect") // rtype is the concrete type the interpreter uses to implement the // reflect.Type interface. // // type rtype <opaque> var rtypeType = makeNamedType("rtype", &opaqueType{nil, "rtype"}) // error is an (interpreted) named type whose underlying type is string. // The interpreter uses it for all implementations of the built-in error // interface that it creates. // We put it in the "reflect" package for expedience. // // type error string var errorType = makeNamedType("error", &opaqueType{nil, "error"})
// Tests that programs partially loaded from gc object files contain // functions with no code for the external portions, but are otherwise ok. func TestBuildPackage(t *testing.T) { input := ` package main import ( "bytes" "io" "testing" ) func main() { var t testing.T t.Parallel() // static call to external declared method t.Fail() // static call to promoted external declared method testing.Short() // static call to external package-level function var w io.Writer = new(bytes.Buffer) w.Write(nil) // interface invoke of external declared method } ` // Parse the file. fset := token.NewFileSet() f, err := parser.ParseFile(fset, "input.go", input, 0) if err != nil { t.Error(err) return } // 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{ // directly imported dependencies: "bytes", "io", "testing", // indirect dependencies (partial list): "errors", "fmt", "os", "runtime", } prog := mainPkg.Prog all := prog.AllPackages() if len(all) <= len(deps) { t.Errorf("unexpected set of loaded packages: %q", all) } for _, path := range deps { pkg := prog.ImportedPackage(path) if pkg == nil { t.Errorf("package not loaded: %q", path) continue } // External packages should have no function bodies (except for wrappers). isExt := pkg != mainPkg // init() if isExt && !isEmpty(pkg.Func("init")) { t.Errorf("external package %s has non-empty init", pkg) } else if !isExt && isEmpty(pkg.Func("init")) { t.Errorf("main package %s has empty init", pkg) } for _, mem := range pkg.Members { switch mem := mem.(type) { case *ssa.Function: // Functions at package level. if isExt && !isEmpty(mem) { t.Errorf("external function %s is non-empty", mem) } else if !isExt && isEmpty(mem) { t.Errorf("function %s is empty", mem) } case *ssa.Type: // Methods of named types T. // (In this test, all exported methods belong to *T not T.) if !isExt { t.Fatalf("unexpected name type in main package: %s", mem) } mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type())) for i, n := 0, mset.Len(); i < n; i++ { m := prog.Method(mset.At(i)) // For external types, only synthetic wrappers have code. expExt := !strings.Contains(m.Synthetic, "wrapper") if expExt && !isEmpty(m) { t.Errorf("external method %s is non-empty: %s", m, m.Synthetic) } else if !expExt && isEmpty(m) { t.Errorf("method function %s is empty: %s", m, m.Synthetic) } } } } } expectedCallee := []string{ "(*testing.T).Parallel", "(*testing.common).Fail", "testing.Short", "N/A", } callNum := 0 for _, b := range mainPkg.Func("main").Blocks { for _, instr := range b.Instrs { switch instr := instr.(type) { case ssa.CallInstruction: call := instr.Common() if want := expectedCallee[callNum]; want != "N/A" { got := call.StaticCallee().String() if want != got { t.Errorf("call #%d from main.main: got callee %s, want %s", callNum, got, want) } } callNum++ } } } if callNum != 4 { t.Errorf("in main.main: got %d calls, want %d", callNum, 4) } }
// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types. func TestRuntimeTypes(t *testing.T) { tests := []struct { input string want []string }{ // An exported package-level type is needed. {`package A; type T struct{}; func (T) f() {}`, []string{"*p.T", "p.T"}, }, // An unexported package-level type is not needed. {`package B; type t struct{}; func (t) f() {}`, nil, }, // Subcomponents of type of exported package-level var are needed. {`package C; import "bytes"; var V struct {*bytes.Buffer}`, []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"}, }, // Subcomponents of type of unexported package-level var are not needed. {`package D; import "bytes"; var v struct {*bytes.Buffer}`, nil, }, // Subcomponents of type of exported package-level function are needed. {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`, []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, }, // Subcomponents of type of unexported package-level function are not needed. {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`, nil, }, // Subcomponents of type of exported method of uninstantiated unexported type are not needed. {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`, nil, }, // ...unless used by MakeInterface. {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`, []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"}, }, // Subcomponents of type of unexported method are not needed. {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`, []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"}, }, // Local types aren't needed. {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`, nil, }, // ...unless used by MakeInterface. {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`, []string{"*bytes.Buffer", "*p.T", "p.T"}, }, // Types used as operand of MakeInterface are needed. {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`, []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, }, // MakeInterface is optimized away when storing to a blank. {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`, nil, }, } for _, test := range tests { // 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 } // 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 %q: %s", test.input[:15], err) continue } var typstrs []string for _, T := range ssapkg.Prog.RuntimeTypes() { typstrs = append(typstrs, T.String()) } sort.Strings(typstrs) if !reflect.DeepEqual(typstrs, test.want) { t.Errorf("test 'package %s': got %q, want %q", f.Name.Name, typstrs, test.want) } } }