1
0
mirror of https://github.com/golang/go synced 2024-11-19 00:44:40 -07:00

go.tools/go/importer: fine-tuning of export format, test against std lib

(TBR adonovan)

- provide version string rather than version number (more flexible)
- match naming of tag constants with documentation (forth-coming)
- more formats for constant values (1-byte booleans, short 64bit
  values, support for floating point numbers +123p+45)
- more regular format for signatures: always print with type tag;
  this permits (future) sharing of identical signatures and possibly
  large export data size reduction
- test export and import of all type-checked std library packages
- compare import of all std library packages against corresponding
  import of gc-generated export data and verify that the packages
  match 100% (except for floating point values which are exact when
  coming from go/types, and possibly inexact when coming from gc)

On average, export data generated by the importer is ~28% of the size
of the gc-generated export data for corresponding packages of the std
library:

        package 		size	gc size

        archive/tar		2945	12030	24%
        archive/zip		4122	15796	26%
        bufio			1776	6171	28%
        bytes			2540	8006	31%
        compress/bzip2		146	419	34%
        compress/flate		2305	6356	36%
        compress/gzip		4262	17917	23%
        compress/lzw		336	832	40%
        compress/zlib		2503	9814	25%
        container/heap		255	527	48%
        container/list		653	2971	21%
        container/ring		246	707	34%
        crypto			368	1015	36%
        crypto/aes		219	499	43%
        crypto/cipher		809	1875	43%
        crypto/des		254	636	39%
        crypto/dsa		3324	15609	21%
        crypto/ecdsa		3833	18567	20%
        crypto/elliptic		3801	17417	21%
        crypto/hmac		205	459	44%
        crypto/md5		206	413	49%
        crypto/rand		3070	15047	20%
        crypto/rc4		180	443	40%
        crypto/rsa		4205	17941	23%
        crypto/sha1		208	414	50%
        crypto/sha256		272	555	49%
        crypto/sha512		274	555	49%
        crypto/subtle		247	1157	21%
        crypto/tls		13769	48330	28%
        crypto/x509		10716	40556	26%
        crypto/x509/pkix	5670	24616	23%
        database/sql		4227	15957	26%
        database/sql/driver	1239	2806	44%
        debug/dwarf		5165	12066	42%
        debug/elf		14205	36401	39%
        debug/goobj		992	2495	39%
        debug/gosym		1808	4798	37%
        debug/macho		4560	13173	34%
        debug/pe		3694	10530	35%
        encoding		261	387	67%
        encoding/ascii85	368	1541	23%
        encoding/asn1		479	1661	28%
        encoding/base32		608	2636	23%
        encoding/base64		610	2636	23%
        encoding/binary		1006	3591	28%
        encoding/csv		2287	8405	27%
        encoding/gob		9161	30565	29%
        encoding/hex		384	1331	28%
        encoding/json		6564	23334	28%
        encoding/pem		208	529	39%
        encoding/xml		7964	27220	29%
        errors			52	277	18%
        expvar			955	2978	32%
        flag			2475	18028	13%
        fmt			1141	3141	36%
        go/ast			11595	41019	28%
        go/build		3736	13920	26%
        go/doc			2647	9523	27%
        go/format		1310	5010	26%
        go/parser		4346	18525	23%
        go/printer		1737	6442	26%
        go/scanner		2360	8598	27%
        go/token		2290	7599	30%
        hash			215	595	36%
        hash/adler32		217	391	55%
        hash/crc32		399	1475	27%
        hash/crc64		324	1327	24%
        hash/fnv		338	3964	8%
        html			80	253	31%
        html/template		7981	37471	21%
        image			4270	18894	22%
        image/color		845	3154	26%
        image/color/palette	121	319	37%
        image/draw		947	4807	19%
        image/gif		1468	6611	22%
        image/jpeg		971	4534	21%
        image/png		880	4432	19%
        index/suffixarray	3702	13960	26%
        io			2049	6117	33%
        io/ioutil		2845	12060	23%
        log			2416	10241	23%
        log/syslog		3102	12208	25%
        math			2361	6008	39%
        math/big		3648	12250	29%
        math/cmplx		473	1542	30%
        math/rand		786	2002	39%
        mime			180	595	30%
        mime/multipart		2761	9338	29%
        net			10593	34352	30%
        net/http		24542	84065	29%
        net/http/cgi		12859	50970	25%
        net/http/cookiejar	2845	12429	22%
        net/http/fcgi		12331	49111	25%
        net/http/httptest	18953	75243	25%
        net/http/httputil	13994	54386	25%
        net/http/pprof		12153	49039	24%
        net/mail		1803	8599	20%
        net/rpc			18503	69044	26%
        net/rpc/jsonrpc		1063	2737	38%
        net/smtp		11775	46821	25%
        net/textproto		2684	8678	30%
        net/url			767	2886	26%
        os			5712	20293	28%
        os/exec			4705	18350	25%
        os/signal		125	330	37%
        os/user			234	720	32%
        path			231	770	30%
        path/filepath		2236	9721	23%
        reflect			4518	13467	33%
        regexp			3608	11167	32%
        regexp/syntax		2184	5157	42%
        runtime			1563	3407	45%
        runtime/cgo		36	66	54%
        runtime/debug		1714	8010	21%
        runtime/pprof		485	1358	35%
        runtime/race		38	43	88%
        sort			748	2658	28%
        strconv			949	2573	36%
        strings			1846	6220	29%
        sync			712	1970	36%
        sync/atomic		1089	2506	43%
        syscall			38975	79396	49%
        testing			3734	13626	27%
        testing/iotest		332	2378	13%
        testing/quick		4411	16434	26%
        text/scanner		1821	5349	34%
        text/tabwriter		1593	4937	32%
        text/template		6882	33329	20%
        text/template/parse	3946	12760	30%
        time			3031	9703	31%
        unicode			3148	8676	36%
        unicode/utf16		163	760	21%
        unicode/utf8		475	1122	42%

        total			463641	1644936	28%

R=adonovan
TBR=adonovan
CC=golang-codereviews
https://golang.org/cl/37250044
This commit is contained in:
Robert Griesemer 2013-12-20 16:43:31 -08:00
parent e405e03b16
commit 30645c4bc6
3 changed files with 358 additions and 128 deletions

View File

@ -23,30 +23,40 @@ const (
const (
magic = "\n$$ exports $$\n"
version = 0
version = "v0"
)
// Object and type tags. Must be < 0.
const (
// Packages
packageTag = -(iota + 1)
// Objects
_Package = -(iota + 1)
_Const
_Type
_Var
_Func
constTag
typeTag
varTag
funcTag
// Types
_Basic
_Array
_Slice
_Struct
_Pointer
_Tuple
_Signature
_Interface
_Map
_Chan
_Named
basicTag
arrayTag
sliceTag
structTag
pointerTag
signatureTag
interfaceTag
mapTag
chanTag
namedTag
// Values
intTag
floatTag
fractionTag
complexTag
stringTag
falseTag
trueTag
)
// ExportData serializes the interface (exported package objects)
@ -72,7 +82,7 @@ func ExportData(pkg *types.Package) []byte {
defer p.tracef("\n")
}
p.int(version)
p.string(version)
p.pkg(pkg)
@ -117,7 +127,7 @@ func (p *exporter) pkg(pkg *types.Package) {
p.pkgIndex[pkg] = len(p.pkgIndex)
// otherwise, write the package tag (< 0) and package data
p.int(_Package)
p.int(packageTag)
p.string(pkg.Name())
p.string(pkg.Path())
}
@ -130,67 +140,98 @@ func (p *exporter) obj(obj types.Object) {
switch obj := obj.(type) {
case *types.Const:
p.int(_Const)
p.int(constTag)
p.string(obj.Name())
p.typ(obj.Type())
p.val(obj.Val())
p.value(obj.Val())
case *types.TypeName:
p.int(_Type)
p.int(typeTag)
// name is written by corresponding named type
p.typ(obj.Type().(*types.Named))
case *types.Var:
p.int(_Var)
p.int(varTag)
p.string(obj.Name())
p.typ(obj.Type())
case *types.Func:
p.int(_Func)
p.int(funcTag)
p.string(obj.Name())
p.signature(obj.Type().(*types.Signature))
p.typ(obj.Type())
default:
panic(fmt.Sprintf("unexpected object type %T", obj))
}
}
func (p *exporter) val(x exact.Value) {
func (p *exporter) value(x exact.Value) {
if trace {
p.tracef("value { ")
defer p.tracef("} ")
}
kind := x.Kind()
p.int(int(kind))
switch kind {
switch kind := x.Kind(); kind {
case exact.Bool:
p.bool(exact.BoolVal(x))
tag := falseTag
if exact.BoolVal(x) {
tag = trueTag
}
p.int(tag)
case exact.String:
p.int(stringTag)
p.string(exact.StringVal(x))
case exact.Int:
p.intVal(x)
if i, ok := exact.Int64Val(x); ok {
p.int(intTag)
p.int64(i)
return
}
p.int(floatTag)
p.float(x)
case exact.Float:
p.floatVal(x)
p.int(fractionTag)
p.fraction(x)
case exact.Complex:
p.floatVal(exact.Real(x))
p.floatVal(exact.Imag(x))
p.int(complexTag)
p.fraction(exact.Real(x))
p.fraction(exact.Imag(x))
default:
panic(fmt.Sprintf("unexpected value kind %d", kind))
}
}
func (p *exporter) intVal(x exact.Value) {
func (p *exporter) float(x exact.Value) {
sign := exact.Sign(x)
p.int(sign)
if sign != 0 {
p.bytes(exact.Bytes(x))
if sign == 0 {
return
}
p.ufloat(x)
}
func (p *exporter) floatVal(x exact.Value) {
p.intVal(exact.Num(x))
if exact.Sign(x) != 0 {
// TODO(gri): For gc-generated constants, the denominator is
// often a large power of two. Use a more compact representation.
p.bytes(exact.Bytes(exact.Denom(x)))
func (p *exporter) fraction(x exact.Value) {
sign := exact.Sign(x)
p.int(sign)
if sign == 0 {
return
}
p.ufloat(exact.Num(x))
p.ufloat(exact.Denom(x))
}
func (p *exporter) ufloat(x exact.Value) {
mant := exact.Bytes(x)
exp8 := -1
for i, b := range mant {
if b != 0 {
exp8 = i
break
}
}
if exp8 < 0 {
panic(fmt.Sprintf("%s has no mantissa", x))
}
p.int(exp8 * 8)
p.bytes(mant[exp8:])
}
func (p *exporter) typ(typ types.Type) {
@ -215,20 +256,20 @@ func (p *exporter) typ(typ types.Type) {
// This permits faithful reconstruction of the alias type (i.e.,
// keeping the name). If we decide to eliminate the distinction
// between the alias types, this code can go.
p.int(_Basic)
p.int(basicTag)
p.string(t.Name())
case *types.Array:
p.int(_Array)
p.typ(t.Elem())
p.int(arrayTag)
p.int64(t.Len())
p.typ(t.Elem())
case *types.Slice:
p.int(_Slice)
p.int(sliceTag)
p.typ(t.Elem())
case *types.Struct:
p.int(_Struct)
p.int(structTag)
n := t.NumFields()
p.int(n)
for i := 0; i < n; i++ {
@ -237,35 +278,44 @@ func (p *exporter) typ(typ types.Type) {
}
case *types.Pointer:
p.int(_Pointer)
p.int(pointerTag)
p.typ(t.Elem())
case *types.Signature:
p.int(_Signature)
p.int(signatureTag)
p.signature(t)
case *types.Interface:
p.int(_Interface)
n := t.NumMethods()
p.int(interfaceTag)
// write methods
n := t.NumExplicitMethods()
p.int(n)
for i := 0; i < n; i++ {
m := t.Method(i)
m := t.ExplicitMethod(i)
p.qualifiedName(m.Pkg(), m.Name())
p.signature(m.Type().(*types.Signature))
p.typ(m.Type())
}
// write embedded interfaces
m := t.NumEmbeddeds()
p.int(m)
for i := 0; i < m; i++ {
p.typ(t.Embedded(i))
}
case *types.Map:
p.int(_Map)
p.int(mapTag)
p.typ(t.Key())
p.typ(t.Elem())
case *types.Chan:
p.int(_Chan)
p.int(chanTag)
p.int(int(t.Dir()))
p.typ(t.Elem())
case *types.Named:
p.int(_Named)
p.int(namedTag)
// write type object
obj := t.Obj()
@ -281,7 +331,7 @@ func (p *exporter) typ(typ types.Type) {
for i := 0; i < n; i++ {
m := t.Method(i)
p.string(m.Name())
p.signature(m.Type().(*types.Signature))
p.typ(m.Type())
}
default:
@ -302,7 +352,7 @@ func (p *exporter) field(f *types.Var) {
func (p *exporter) qualifiedName(pkg *types.Package, name string) {
p.string(name)
// exported names don't write package
// exported names don't need package
if !exported(name) {
if pkg == nil {
panic(fmt.Sprintf("nil package for unexported qualified name %s", name))

View File

@ -42,7 +42,7 @@ func ImportData(imports map[string]*types.Package, data []byte) (*types.Package,
}
p.typList = append(p.typList, types.Universe.Lookup("error").Type())
if v := p.int(); v != version {
if v := p.string(); v != version {
return nil, fmt.Errorf("unknown version: got %d; want %d", v, version)
}
@ -85,7 +85,7 @@ func (p *importer) pkg() *types.Package {
}
// otherwise, i is the package tag (< 0)
if i != _Package {
if i != packageTag {
panic(fmt.Sprintf("unexpected package tag %d", i))
}
@ -107,16 +107,16 @@ func (p *importer) pkg() *types.Package {
func (p *importer) obj(pkg *types.Package) {
var obj types.Object
switch tag := p.int(); tag {
case _Const:
obj = types.NewConst(token.NoPos, pkg, p.string(), p.typ(), p.val())
case _Type:
case constTag:
obj = types.NewConst(token.NoPos, pkg, p.string(), p.typ(), p.value())
case typeTag:
// type object is added to scope via respective named type
_ = p.typ().(*types.Named)
return
case _Var:
case varTag:
obj = types.NewVar(token.NoPos, pkg, p.string(), p.typ())
case _Func:
obj = types.NewFunc(token.NoPos, pkg, p.string(), p.signature())
case funcTag:
obj = types.NewFunc(token.NoPos, pkg, p.string(), p.typ().(*types.Signature))
default:
panic(fmt.Sprintf("unexpected object tag %d", tag))
}
@ -126,43 +126,64 @@ func (p *importer) obj(pkg *types.Package) {
}
}
func (p *importer) val() exact.Value {
func (p *importer) value() exact.Value {
switch kind := exact.Kind(p.int()); kind {
case exact.Bool:
return exact.MakeBool(p.bool())
case exact.String:
case falseTag:
return exact.MakeBool(false)
case trueTag:
return exact.MakeBool(true)
case stringTag:
return exact.MakeString(p.string())
case exact.Int:
return p.intVal()
case exact.Float:
return p.floatVal()
case exact.Complex:
re := p.floatVal()
im := p.floatVal()
case intTag:
return exact.MakeInt64(p.int64())
case floatTag:
return p.float()
case fractionTag:
return p.fraction()
case complexTag:
re := p.fraction()
im := p.fraction()
return exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
default:
panic(fmt.Sprintf("unexpected value kind %d", kind))
}
}
func (p *importer) intVal() exact.Value {
func (p *importer) float() exact.Value {
sign := p.int()
var bytes []byte
if sign != 0 {
bytes = p.bytes()
if sign == 0 {
return exact.MakeInt64(0)
}
x := exact.MakeFromBytes(bytes)
x := p.ufloat()
if sign < 0 {
x = exact.UnaryOp(token.SUB, x, 0)
}
return x
}
func (p *importer) floatVal() exact.Value {
x := p.intVal()
if exact.Sign(x) != 0 {
y := exact.MakeFromBytes(p.bytes())
x = exact.BinaryOp(x, token.QUO, y)
func (p *importer) fraction() exact.Value {
sign := p.int()
if sign == 0 {
return exact.MakeInt64(0)
}
x := exact.BinaryOp(p.ufloat(), token.QUO, p.ufloat())
if sign < 0 {
x = exact.UnaryOp(token.SUB, x, 0)
}
return x
}
func (p *importer) ufloat() exact.Value {
exp := p.int()
x := exact.MakeFromBytes(p.bytes())
switch {
case exp < 0:
d := exact.Shift(exact.MakeInt64(1), token.SHL, uint(-exp))
x = exact.BinaryOp(x, token.QUO, d)
case exp > 0:
x = exact.Shift(x, token.SHL, uint(exp))
}
return x
}
@ -180,26 +201,27 @@ func (p *importer) typ() types.Type {
// otherwise, i is the type tag (< 0)
switch i {
case _Basic:
case basicTag:
t := types.Universe.Lookup(p.string()).(*types.TypeName).Type().(*types.Basic)
p.record(t)
return t
case _Array:
case arrayTag:
t := new(types.Array)
p.record(t)
*t = *types.NewArray(p.typ(), p.int64())
n := p.int64()
*t = *types.NewArray(p.typ(), n)
return t
case _Slice:
case sliceTag:
t := new(types.Slice)
p.record(t)
*t = *types.NewSlice(p.typ())
return t
case _Struct:
case structTag:
t := new(types.Struct)
p.record(t)
@ -213,47 +235,52 @@ func (p *importer) typ() types.Type {
*t = *types.NewStruct(fields, tags)
return t
case _Pointer:
case pointerTag:
t := new(types.Pointer)
p.record(t)
*t = *types.NewPointer(p.typ())
return t
case _Signature:
case signatureTag:
t := new(types.Signature)
p.record(t)
*t = *p.signature()
return t
case _Interface:
case interfaceTag:
t := new(types.Interface)
p.record(t)
methods := make([]*types.Func, p.int())
for i := range methods {
pkg, name := p.qualifiedName()
methods[i] = types.NewFunc(token.NoPos, pkg, name, p.signature())
methods[i] = types.NewFunc(token.NoPos, pkg, name, p.typ().(*types.Signature))
}
*t = *types.NewInterface(methods, nil)
embeddeds := make([]*types.Named, p.int())
for i := range embeddeds {
embeddeds[i] = p.typ().(*types.Named)
}
*t = *types.NewInterface(methods, embeddeds)
return t
case _Map:
case mapTag:
t := new(types.Map)
p.record(t)
*t = *types.NewMap(p.typ(), p.typ())
return t
case _Chan:
case chanTag:
t := new(types.Chan)
p.record(t)
*t = *types.NewChan(types.ChanDir(p.int()), p.typ())
return t
case _Named:
case namedTag:
// import type object
name := p.string()
pkg := p.pkg()
@ -277,7 +304,7 @@ func (p *importer) typ() types.Type {
// read associated methods
n := p.int()
for i := 0; i < n; i++ {
t.AddMethod(types.NewFunc(token.NoPos, pkg, p.string(), p.signature()))
t.AddMethod(types.NewFunc(token.NoPos, pkg, p.string(), p.typ().(*types.Signature)))
}
return t

View File

@ -5,19 +5,27 @@
package importer
import (
"bufio"
"bytes"
"flag"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"os"
"path/filepath"
"runtime"
"sort"
"testing"
"time"
_ "code.google.com/p/go.tools/go/gcimporter"
"code.google.com/p/go.tools/go/gcimporter"
"code.google.com/p/go.tools/go/types"
)
var verbose = flag.Bool("importer.v", false, "verbose mode")
var tests = []string{
`package p`,
@ -84,34 +92,60 @@ func TestImportSrc(t *testing.T) {
t.Errorf("typecheck failed: %s", err)
continue
}
testExportImport(t, pkg)
testExportImport(t, pkg, "")
}
}
// TODO(gri) expand to std library
var libs = []string{
"../exact",
"../gcimporter",
"../importer",
"../types",
"../types/typemap",
}
func TestImportStdLib(t *testing.T) {
start := time.Now()
func TestImportLib(t *testing.T) {
// TODO(gri) Enable once we can run these tests on builders.
return
for _, lib := range libs {
pkg, err := pkgFor(lib)
libs, err := stdLibs()
if err != nil {
t.Fatalf("could not compute list of std libraries: %s", err)
}
// make sure printed go/types types and gc-imported types
// can be compared reasonably well
types.GcCompatibilityMode = true
var totSize, totGcsize, n int
for _, lib := range libs {
// limit run time for short tests
if testing.Short() && time.Since(start) >= 750*time.Millisecond {
return
}
pkg, err := pkgForPath(lib)
switch err := err.(type) {
case nil:
// ok
case *build.NoGoError:
// no Go files - ignore
continue
default:
t.Errorf("typecheck failed: %s", err)
continue
}
testExportImport(t, pkg)
size, gcsize := testExportImport(t, pkg, lib)
if *verbose {
fmt.Printf("%s\t%d\t%d\t%d%%\n", lib, size, gcsize, int(float64(size)*100/float64(gcsize)))
}
totSize += size
totGcsize += gcsize
n++
}
if *verbose {
fmt.Printf("\n%d\t%d\t%d%%\n", totSize, totGcsize, int(float64(totSize)*100/float64(totGcsize)))
}
types.GcCompatibilityMode = false
}
func testExportImport(t *testing.T, pkg0 *types.Package) {
func testExportImport(t *testing.T, pkg0 *types.Package, path string) (size, gcsize int) {
data := ExportData(pkg0)
size = len(data)
imports := make(map[string]*types.Package)
pkg1, err := ImportData(imports, data)
@ -123,8 +157,30 @@ func testExportImport(t *testing.T, pkg0 *types.Package) {
s0 := pkgString(pkg0)
s1 := pkgString(pkg1)
if s1 != s0 {
t.Errorf("package %s: \ngot:\n%s\nwant:\n%s\n", pkg0.Name(), s1, s0)
t.Errorf("package %s: \nimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s1, s0)
}
// If we have a standard library, compare also against the gcimported package.
if path == "" {
return // not std library
}
gcdata, err := gcExportData(path)
gcsize = len(gcdata)
imports = make(map[string]*types.Package)
pkg2, err := gcImportData(imports, gcdata, path)
if err != nil {
t.Errorf("package %s: gcimport failed: %s", pkg0.Name(), err)
return
}
s2 := pkgString(pkg2)
if s2 != s0 {
t.Errorf("package %s: \ngcimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s2, s0)
}
return
}
func pkgForSource(src string) (*types.Package, error) {
@ -139,11 +195,11 @@ func pkgForSource(src string) (*types.Package, error) {
return types.Check("import-test", fset, []*ast.File{f})
}
func pkgFor(path string) (*types.Package, error) {
func pkgForPath(path string) (*types.Package, error) {
// collect filenames
ctxt := build.Default
pkginfo, err := ctxt.ImportDir(path, 0)
if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
pkginfo, err := ctxt.Import(path, "", 0)
if err != nil {
return nil, err
}
filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
@ -153,16 +209,19 @@ func pkgFor(path string) (*types.Package, error) {
files := make([]*ast.File, len(filenames))
for i, filename := range filenames {
var err error
files[i], err = parser.ParseFile(fset, filepath.Join(path, filename), nil, 0)
files[i], err = parser.ParseFile(fset, filepath.Join(pkginfo.Dir, filename), nil, 0)
if err != nil {
return nil, err
}
}
// typecheck files
return types.Check(path, fset, files)
// (we only care about exports and thus can ignore function bodies)
conf := types.Config{IgnoreFuncBodies: true, FakeImportC: true}
return conf.Check(path, fset, files, nil)
}
// pkgString returns a string representation of a package's exported interface.
func pkgString(pkg *types.Package) string {
var buf bytes.Buffer
@ -176,16 +235,35 @@ func pkgString(pkg *types.Package) string {
switch obj := obj.(type) {
case *types.Const:
// For now only print constant values if they are not float
// or complex. This permits comparing go/types results with
// gc-generated gcimported package interfaces.
info := obj.Type().Underlying().(*types.Basic).Info()
if info&types.IsFloat == 0 && info&types.IsComplex == 0 {
fmt.Fprintf(&buf, " = %s", obj.Val())
}
case *types.TypeName:
// Print associated methods.
// Basic types (e.g., unsafe.Pointer) have *types.Basic
// type rather than *types.Named; so we need to check.
if typ, _ := obj.Type().(*types.Named); typ != nil {
if n := typ.NumMethods(); n > 0 {
buf.WriteString("\nmethods (\n")
// Sort methods by name so that we get the
// same order independent of whether the
// methods got imported or coming directly
// for the source.
// TODO(gri) This should probably be done
// in go/types.
list := make([]*types.Func, n)
for i := 0; i < n; i++ {
fmt.Fprintf(&buf, "\t%s\n", typ.Method(i))
list[i] = typ.Method(i)
}
sort.Sort(byName(list))
buf.WriteString("\nmethods (\n")
for _, m := range list {
fmt.Fprintf(&buf, "\t%s\n", m)
}
buf.WriteString(")")
}
@ -197,3 +275,78 @@ func pkgString(pkg *types.Package) string {
return buf.String()
}
var stdLibRoot = filepath.Join(runtime.GOROOT(), "src", "pkg") + string(filepath.Separator)
// The following std libraries are excluded from the stdLibs list.
var excluded = map[string]bool{
"builtin": true, // contains type declarations with cycles
"unsafe": true, // contains fake declarations
}
// stdLibs returns the list if standard library package paths.
func stdLibs() (list []string, err error) {
err = filepath.Walk(stdLibRoot, func(path string, info os.FileInfo, err error) error {
if err == nil && info.IsDir() {
if info.Name() == "testdata" {
return filepath.SkipDir
}
pkgPath := path[len(stdLibRoot):] // remove stdLibRoot
if len(pkgPath) > 0 && !excluded[pkgPath] {
list = append(list, pkgPath)
}
}
return nil
})
return
}
type byName []*types.Func
func (a byName) Len() int { return len(a) }
func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byName) Less(i, j int) bool { return a[i].Name() < a[j].Name() }
// gcExportData returns the gc-generated export data for the given path.
// It is based on a trimmed-down version of gcimporter.Import which does
// not do the actual import, does not handle package unsafe, and assumes
// that path is a correct standard library package path (no canonicalization,
// or handling of local import paths).
func gcExportData(path string) ([]byte, error) {
filename, id := gcimporter.FindPkg(path, "")
if filename == "" {
return nil, fmt.Errorf("can't find import: %s", path)
}
if id != path {
panic("path should be canonicalized")
}
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
buf := bufio.NewReader(f)
if err = gcimporter.FindExportData(buf); err != nil {
return nil, err
}
var data []byte
for {
line, err := buf.ReadBytes('\n')
if err != nil {
return nil, err
}
data = append(data, line...)
// export data ends in "$$\n"
if len(line) == 3 && line[0] == '$' && line[1] == '$' {
return data, nil
}
}
}
func gcImportData(imports map[string]*types.Package, data []byte, path string) (*types.Package, error) {
filename := fmt.Sprintf("<filename for %s>", path) // so we have a decent error message if necessary
return gcimporter.ImportData(imports, filename, path, bufio.NewReader(bytes.NewBuffer(data)))
}