func (g *cpyGen) genTypeMethods(sym *symbol) { g.decl.Printf("\n/* methods for %s */\n", sym.gofmt()) if sym.isNamed() { typ := sym.GoType().(*types.Named) for imeth := 0; imeth < typ.NumMethods(); imeth++ { m := typ.Method(imeth) if !m.Exported() { continue } mname := types.ObjectString(m, nil) msym := g.pkg.syms.sym(mname) if msym == nil { panic(fmt.Errorf( "gopy: could not find symbol for [%[1]T] (%#[1]v) (%[2]s)", m.Type(), m.Name()+" || "+m.FullName(), )) } g._genFunc(sym, msym) } } g.impl.Printf("\n/* methods for %s */\n", sym.gofmt()) g.impl.Printf("static PyMethodDef %s_methods[] = {\n", sym.cpyname) g.impl.Indent() if sym.isNamed() { typ := sym.GoType().(*types.Named) for imeth := 0; imeth < typ.NumMethods(); imeth++ { m := typ.Method(imeth) if !m.Exported() { continue } mname := types.ObjectString(m, nil) msym := g.pkg.syms.sym(mname) margs := "METH_VARARGS" sig := m.Type().Underlying().(*types.Signature) if sig.Params() == nil || sig.Params().Len() <= 0 { margs = "METH_NOARGS" } g.impl.Printf( "{%[1]q, (PyCFunction)cpy_func_%[2]s, %[3]s, %[4]q},\n", msym.goname, msym.id, margs, msym.doc, ) } } g.impl.Printf("{NULL} /* sentinel */\n") g.impl.Outdent() g.impl.Printf("};\n\n") }
func TestImportedTypes(t *testing.T) { skipSpecialPlatforms(t) // This package only handles gc export data. if runtime.Compiler != "gc" { t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) return } for _, test := range importedObjectTests { s := strings.Split(test.name, ".") if len(s) != 2 { t.Fatal("inconsistent test data") } importPath := s[0] objName := s[1] pkg, err := Import(imports, importPath) if err != nil { t.Error(err) continue } obj := pkg.Scope().Lookup(objName) if obj == nil { t.Errorf("%s: object not found", test.name) continue } got := types.ObjectString(obj, types.RelativeTo(pkg)) if got != test.want { t.Errorf("%s: got %q; want %q", test.name, got, test.want) } } }
func (sym *symtab) addSymbol(obj types.Object) { fn := types.ObjectString(obj, nil) n := obj.Name() pkg := obj.Pkg() id := n if pkg != nil { id = pkg.Name() + "_" + n } switch obj.(type) { case *types.Const: sym.syms[fn] = &symbol{ gopkg: pkg, goobj: obj, kind: skConst, id: id, goname: n, cgoname: "cgo_const_" + id, cpyname: "cpy_const_" + id, } sym.addType(obj, obj.Type()) case *types.Var: sym.syms[fn] = &symbol{ gopkg: pkg, goobj: obj, kind: skVar, id: id, goname: n, cgoname: "cgo_var_" + id, cpyname: "cpy_var_" + id, } sym.addType(obj, obj.Type()) case *types.Func: sym.syms[fn] = &symbol{ gopkg: pkg, goobj: obj, kind: skFunc, id: id, goname: n, cgoname: "cgo_func_" + id, cpyname: "cpy_func_" + id, } sig := obj.Type().Underlying().(*types.Signature) sym.processTuple(sig.Params()) sym.processTuple(sig.Results()) case *types.TypeName: sym.addType(obj, obj.Type()) default: panic(fmt.Errorf("gopy: handled object [%#v]", obj)) } }
func (sym *symtab) addMethod(pkg *types.Package, obj types.Object, t types.Type, kind symkind, id, n string) { fn := types.ObjectString(obj, nil) kind |= skFunc sym.syms[fn] = &symbol{ gopkg: pkg, goobj: obj, gotyp: t, kind: kind, id: id, goname: n, cgoname: "cgo_func_" + id, cpyname: "cpy_func_" + id, } sig := t.Underlying().(*types.Signature) sym.processTuple(sig.Results()) sym.processTuple(sig.Params()) }
func (g *cpyGen) genStructMethods(cpy Struct) { pkgname := cpy.Package().Name() g.decl.Printf("\n/* methods for %s.%s */\n", pkgname, cpy.GoName()) typ := cpy.sym.GoType().(*types.Named) for i := 0; i < typ.NumMethods(); i++ { m := typ.Method(i) if !m.Exported() { continue } mname := types.ObjectString(m, nil) msym := g.pkg.syms.sym(mname) if msym == nil { panic(fmt.Errorf( "gopy: could not find symbol for %q", m.FullName(), )) } g._genFunc(cpy.sym, msym) } g.impl.Printf("\n/* methods for %s.%s */\n", pkgname, cpy.GoName()) g.impl.Printf("static PyMethodDef %s_methods[] = {\n", cpy.sym.cpyname) g.impl.Indent() for _, m := range cpy.meths { margs := "METH_VARARGS" if len(m.Signature().Params()) == 0 { margs = "METH_NOARGS" } g.impl.Printf( "{%[1]q, (PyCFunction)cpy_func_%[2]s, %[3]s, %[4]q},\n", m.GoName(), m.ID(), margs, m.Doc(), ) } g.impl.Printf("{NULL} /* sentinel */\n") g.impl.Outdent() g.impl.Printf("};\n\n") }
// ObjectString prints object obj relative to the query position. func (qpos *queryPos) objectString(obj types.Object) string { return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg)) }
func (r *referrersInitialResult) PrintPlain(printf printfFunc) { printf(r.obj, "references to %s", types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg))) }
func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]InitData, test *importerTest) { pkg, err := imp(make(map[string]*types.Package), test.pkgpath) if err != nil { t.Error(err) return } if test.name != "" { obj := pkg.Scope().Lookup(test.name) if obj == nil { t.Errorf("%s: object not found", test.name) return } got := types.ObjectString(obj, types.RelativeTo(pkg)) if got != test.want { t.Errorf("%s: got %q; want %q", test.name, got, test.want) } if test.wantval != "" { gotval := obj.(*types.Const).Val().String() if gotval != test.wantval { t.Errorf("%s: got val %q; want val %q", test.name, gotval, test.wantval) } } } if len(test.wantinits) > 0 { initdata := initmap[pkg] found := false // Check that the package's own init function has the package's priority for _, pkginit := range initdata.Inits { if pkginit.InitFunc == test.wantinits[0] { if initdata.Priority != pkginit.Priority { t.Errorf("%s: got self priority %d; want %d", test.pkgpath, pkginit.Priority, initdata.Priority) } found = true break } } if !found { t.Errorf("%s: could not find expected function %q", test.pkgpath, test.wantinits[0]) } // Each init function in the list other than the first one is a // dependency of the function immediately before it. Check that // the init functions appear in descending priority order. priority := initdata.Priority for _, wantdepinit := range test.wantinits[1:] { found = false for _, pkginit := range initdata.Inits { if pkginit.InitFunc == wantdepinit { if priority <= pkginit.Priority { t.Errorf("%s: got dep priority %d; want less than %d", test.pkgpath, pkginit.Priority, priority) } found = true priority = pkginit.Priority break } } if !found { t.Errorf("%s: could not find expected function %q", test.pkgpath, wantdepinit) } } } }
func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Named]implementsFacts) { // We must not assume the corresponding SSA packages were // created (i.e. were transitively error-free). // IMPORTS for _, f := range info.Files { // Package decl. fi, offset := a.fileAndOffset(f.Name.Pos()) fi.addLink(aLink{ start: offset, end: offset + len(f.Name.Name), title: "Package docs for " + info.Pkg.Path(), // TODO(adonovan): fix: we're putting the untrusted Path() // into a trusted field. What's the appropriate sanitizer? href: "/pkg/" + info.Pkg.Path(), }) // Import specs. for _, imp := range f.Imports { // Remove quotes. L := int(imp.End()-imp.Path.Pos()) - len(`""`) path, _ := strconv.Unquote(imp.Path.Value) fi, offset := a.fileAndOffset(imp.Path.Pos()) fi.addLink(aLink{ start: offset + 1, end: offset + 1 + L, title: "Package docs for " + path, // TODO(adonovan): fix: we're putting the untrusted path // into a trusted field. What's the appropriate sanitizer? href: "/pkg/" + path, }) } } // RESOLUTION qualifier := types.RelativeTo(info.Pkg) for id, obj := range info.Uses { // Position of the object definition. pos := obj.Pos() Len := len(obj.Name()) // Correct the position for non-renaming import specs. // import "sync/atomic" // ^^^^^^^^^^^ if obj, ok := obj.(*types.PkgName); ok && id.Name == obj.Imported().Name() { // Assume this is a non-renaming import. // NB: not true for degenerate renamings: `import foo "foo"`. pos++ Len = len(obj.Imported().Path()) } if obj.Pkg() == nil { continue // don't mark up built-ins. } fi, offset := a.fileAndOffset(id.NamePos) fi.addLink(aLink{ start: offset, end: offset + len(id.Name), title: types.ObjectString(obj, qualifier), href: a.posURL(pos, Len), }) } // IMPLEMENTS & METHOD SETS for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok { a.namedType(obj, implements) } } }
// ExampleInfo prints various facts recorded by the type checker in a // types.Info struct: definitions of and references to each named object, // and the type, value, and mode of every expression in the package. func ExampleInfo() { // Parse a single source file. const input = ` package fib type S string var a, b, c = len(b), S(c), "hello" func fib(x int) int { if x < 2 { return x } return fib(x-1) - fib(x-2) }` fset := token.NewFileSet() f, err := parser.ParseFile(fset, "fib.go", input, 0) if err != nil { log.Fatal(err) } // Type-check the package. // We create an empty map for each kind of input // we're interested in, and Check populates them. info := types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), } var conf types.Config pkg, err := conf.Check("fib", fset, []*ast.File{f}, &info) if err != nil { log.Fatal(err) } // Print package-level variables in initialization order. fmt.Printf("InitOrder: %v\n\n", info.InitOrder) // For each named object, print the line and // column of its definition and each of its uses. fmt.Println("Defs and Uses of each named object:") usesByObj := make(map[types.Object][]string) for id, obj := range info.Uses { posn := fset.Position(id.Pos()) lineCol := fmt.Sprintf("%d:%d", posn.Line, posn.Column) usesByObj[obj] = append(usesByObj[obj], lineCol) } var items []string for obj, uses := range usesByObj { sort.Strings(uses) item := fmt.Sprintf("%s:\n defined at %s\n used at %s", types.ObjectString(pkg, obj), fset.Position(obj.Pos()), strings.Join(uses, ", ")) items = append(items, item) } sort.Strings(items) // sort by line:col, in effect fmt.Println(strings.Join(items, "\n")) fmt.Println() fmt.Println("Types and Values of each expression:") items = nil for expr, tv := range info.Types { var buf bytes.Buffer posn := fset.Position(expr.Pos()) tvstr := tv.Type.String() if tv.Value != nil { tvstr += " = " + tv.Value.String() } // line:col | expr | mode : type = value fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s", posn.Line, posn.Column, exprString(fset, expr), mode(tv), tvstr) items = append(items, buf.String()) } sort.Strings(items) fmt.Println(strings.Join(items, "\n")) // Output: // InitOrder: [c = "hello" b = S(c) a = len(b)] // // Defs and Uses of each named object: // builtin len: // defined at - // used at 6:15 // func fib(x int) int: // defined at fib.go:8:6 // used at 12:20, 12:9 // type S string: // defined at fib.go:4:6 // used at 6:23 // type int int: // defined at - // used at 8:12, 8:17 // type string string: // defined at - // used at 4:8 // var b S: // defined at fib.go:6:8 // used at 6:19 // var c string: // defined at fib.go:6:11 // used at 6:25 // var x int: // defined at fib.go:8:10 // used at 10:10, 12:13, 12:24, 9:5 // // Types and Values of each expression: // 4: 8 | string | type : string // 6:15 | len | builtin : func(string) int // 6:15 | len(b) | value : int // 6:19 | b | var : fib.S // 6:23 | S | type : fib.S // 6:23 | S(c) | value : fib.S // 6:25 | c | var : string // 6:29 | "hello" | value : string = "hello" // 8:12 | int | type : int // 8:17 | int | type : int // 9: 5 | x | var : int // 9: 5 | x < 2 | value : untyped bool // 9: 9 | 2 | value : int = 2 // 10:10 | x | var : int // 12: 9 | fib | value : func(x int) int // 12: 9 | fib(x - 1) | value : int // 12: 9 | fib(x-1) - fib(x-2) | value : int // 12:13 | x | var : int // 12:13 | x - 1 | value : int // 12:15 | 1 | value : int = 1 // 12:20 | fib | value : func(x int) int // 12:20 | fib(x - 2) | value : int // 12:24 | x | var : int // 12:24 | x - 2 | value : int // 12:26 | 2 | value : int = 2 }
func (g *goGen) genTypeMethods(sym *symbol) { if !sym.isNamed() { return } typ := sym.GoType().(*types.Named) for imeth := 0; imeth < typ.NumMethods(); imeth++ { m := typ.Method(imeth) if !m.Exported() { continue } mname := types.ObjectString(m, nil) msym := g.pkg.syms.sym(mname) if msym == nil { panic(fmt.Errorf( "gopy: could not find symbol for [%[1]T] (%#[1]v) (%[2]s)", m.Type(), m.Name()+" || "+m.FullName(), )) } g.Printf("//export cgo_func_%[1]s\n", msym.id) g.Printf("func cgo_func_%[1]s(self %[2]s", msym.id, sym.cgoname, ) sig := m.Type().(*types.Signature) params := sig.Params() if params != nil { for i := 0; i < params.Len(); i++ { arg := params.At(i) sarg := g.pkg.syms.symtype(arg.Type()) if sarg == nil { panic(fmt.Errorf( "gopy: could not find symbol for [%T]", arg.Type(), )) } g.Printf(", arg%03d %s", i, sarg.cgotypename()) } } g.Printf(") ") res := sig.Results() if res != nil { g.Printf("(") for i := 0; i < res.Len(); i++ { if i > 0 { g.Printf(", ") } ret := res.At(i) sret := g.pkg.syms.symtype(ret.Type()) if sret == nil { panic(fmt.Errorf( "gopy: could not find symbol for [%T]", ret.Type(), )) } g.Printf("%s", sret.cgotypename()) } g.Printf(")") } g.Printf(" {\n") g.Indent() if res != nil { for i := 0; i < res.Len(); i++ { if i > 0 { g.Printf(", ") } g.Printf("res%03d", i) } if res.Len() > 0 { g.Printf(" := ") } } if sym.isBasic() { g.Printf("(*%s)(unsafe.Pointer(&self)).%s(", sym.gofmt(), msym.goname, ) } else { g.Printf("(*%s)(unsafe.Pointer(self)).%s(", sym.gofmt(), msym.goname, ) } if params != nil { for i := 0; i < params.Len(); i++ { if i > 0 { g.Printf(", ") } sarg := g.pkg.syms.symtype(params.At(i).Type()) if needWrapType(sarg.GoType()) { g.Printf("*(*%s)(unsafe.Pointer(arg%03d))", sarg.gofmt(), i, ) } else { g.Printf("arg%03d", i) } } } g.Printf(")\n") if res == nil || res.Len() <= 0 { g.Outdent() g.Printf("}\n\n") continue } g.Printf("return ") for i := 0; i < res.Len(); i++ { if i > 0 { g.Printf(", ") } sret := g.pkg.syms.symtype(res.At(i).Type()) if needWrapType(sret.GoType()) { g.Printf( "%s(unsafe.Pointer(&", sret.cgoname, ) } g.Printf("res%03d", i) if needWrapType(sret.GoType()) { g.Printf("))") } } g.Printf("\n") g.Outdent() g.Printf("}\n\n") } }
// process collects informations about a go package. func (p *Package) process() error { var err error funcs := make(map[string]Func) structs := make(map[string]Struct) scope := p.pkg.Scope() for _, name := range scope.Names() { obj := scope.Lookup(name) if !obj.Exported() { continue } p.n++ p.syms.addSymbol(obj) } for _, name := range scope.Names() { obj := scope.Lookup(name) if !obj.Exported() { continue } switch obj := obj.(type) { case *types.Const: p.addConst(obj) case *types.Var: p.addVar(obj) case *types.Func: funcs[name], err = newFuncFrom(p, "", obj, obj.Type().(*types.Signature)) if err != nil { return err } case *types.TypeName: named := obj.Type().(*types.Named) switch typ := named.Underlying().(type) { case *types.Struct: structs[name], err = newStruct(p, obj) if err != nil { return err } case *types.Basic: // ok. handled by p.syms-types case *types.Array: // ok. handled by p.syms-types case *types.Interface: // ok. handled by p.syms-types case *types.Signature: // ok. handled by p.syms-types case *types.Slice: // ok. handled by p.syms-types default: //TODO(sbinet) panic(fmt.Errorf("not yet supported: %v (%T)", typ, obj)) } default: //TODO(sbinet) panic(fmt.Errorf("not yet supported: %v (%T)", obj, obj)) } } // remove ctors from funcs. // add methods. for sname, s := range structs { for name, fct := range funcs { if fct.Return() == nil { continue } if fct.Return() == s.GoType() { delete(funcs, name) fct.doc = p.getDoc(sname, scope.Lookup(name)) fct.ctor = true s.ctors = append(s.ctors, fct) structs[sname] = s } } ptyp := types.NewPointer(s.GoType()) p.syms.addType(nil, ptyp) mset := types.NewMethodSet(ptyp) for i := 0; i < mset.Len(); i++ { meth := mset.At(i) if !meth.Obj().Exported() { continue } m, err := newFuncFrom(p, sname, meth.Obj(), meth.Type().(*types.Signature)) if err != nil { return err } s.meths = append(s.meths, m) if isStringer(meth.Obj()) { s.prots |= ProtoStringer } } p.addStruct(s) } for _, fct := range funcs { p.addFunc(fct) } // attach docstrings to methods for _, n := range p.syms.names() { sym := p.syms.syms[n] if !sym.isNamed() { continue } switch typ := sym.GoType().(type) { case *types.Named: for i := 0; i < typ.NumMethods(); i++ { m := typ.Method(i) if !m.Exported() { continue } doc := p.getDoc(sym.goname, m) mname := types.ObjectString(m, nil) msym := p.syms.sym(mname) if msym == nil { panic(fmt.Errorf( "gopy: could not retrieve symbol for %q", m.FullName(), )) } msym.doc = doc } } } return err }
func TestStdlib(t *testing.T) { if runtime.GOOS == "android" { t.Skipf("incomplete std lib on %s", runtime.GOOS) } if testing.Short() { t.Skip("skipping in short mode; uses tons of memory (golang.org/issue/14113)") } runtime.GC() t0 := time.Now() var memstats runtime.MemStats runtime.ReadMemStats(&memstats) alloc := memstats.Alloc // Load, parse and type-check the program. ctxt := build.Default // copy ctxt.GOPATH = "" // disable GOPATH conf := loader.Config{Build: &ctxt} for _, path := range buildutil.AllPackages(conf.Build) { conf.ImportWithTests(path) } prog, err := conf.Load() if err != nil { t.Fatalf("Load failed: %v", err) } t1 := time.Now() runtime.GC() runtime.ReadMemStats(&memstats) numPkgs := len(prog.AllPackages) if want := 205; numPkgs < want { t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want) } // Dump package members. if false { for pkg := range prog.AllPackages { fmt.Printf("Package %s:\n", pkg.Path()) scope := pkg.Scope() qualifier := types.RelativeTo(pkg) for _, name := range scope.Names() { if ast.IsExported(name) { fmt.Printf("\t%s\n", types.ObjectString(scope.Lookup(name), qualifier)) } } fmt.Println() } } // Check that Test functions for io/ioutil, regexp and // compress/bzip2 are all simultaneously present. // (The apparent cycle formed when augmenting all three of // these packages by their tests was the original motivation // for reporting b/7114.) // // compress/bzip2.TestBitReader in bzip2_test.go imports io/ioutil // io/ioutil.TestTempFile in tempfile_test.go imports regexp // regexp.TestRE2Search in exec_test.go imports compress/bzip2 for _, test := range []struct{ pkg, fn string }{ {"io/ioutil", "TestTempFile"}, {"regexp", "TestRE2Search"}, {"compress/bzip2", "TestBitReader"}, } { info := prog.Imported[test.pkg] if info == nil { t.Errorf("failed to load package %q", test.pkg) continue } obj, _ := info.Pkg.Scope().Lookup(test.fn).(*types.Func) if obj == nil { t.Errorf("package %q has no func %q", test.pkg, test.fn) continue } } // Dump some statistics. // determine line count var lineCount int prog.Fset.Iterate(func(f *token.File) bool { lineCount += f.LineCount() return true }) t.Log("GOMAXPROCS: ", runtime.GOMAXPROCS(0)) t.Log("#Source lines: ", lineCount) t.Log("Load/parse/typecheck: ", t1.Sub(t0)) t.Log("#MB: ", int64(memstats.Alloc-alloc)/1000000) }