diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go index 2a8b98b66f..ad28ef7735 100644 --- a/src/go/internal/gcimporter/bimport.go +++ b/src/go/internal/gcimporter/bimport.go @@ -13,6 +13,7 @@ import ( "sort" "strconv" "strings" + "sync" "unicode" "unicode/utf8" ) @@ -34,6 +35,8 @@ type importer struct { posInfoFormat bool prevFile string prevLine int + fset *token.FileSet + files map[string]*token.File // debugging support debugFormat bool @@ -44,7 +47,7 @@ type importer struct { // and returns the number of bytes consumed and a reference to the package. // If the export data version is not recognized or the format is otherwise // compromised, an error is returned. -func BImportData(imports map[string]*types.Package, data []byte, path string) (_ int, _ *types.Package, err error) { +func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, _ *types.Package, err error) { // catch panics and return them as errors defer func() { if e := recover(); e != nil { @@ -60,6 +63,8 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (_ path: path, version: -1, // unknown version strList: []string{""}, // empty string is mapped to 0 + fset: fset, + files: make(map[string]*token.File), } // read version info @@ -219,37 +224,37 @@ func (p *importer) declare(obj types.Object) { func (p *importer) obj(tag int) { switch tag { case constTag: - p.pos() + pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) val := p.value() - p.declare(types.NewConst(token.NoPos, pkg, name, typ, val)) + p.declare(types.NewConst(pos, pkg, name, typ, val)) case typeTag: _ = p.typ(nil) case varTag: - p.pos() + pos := p.pos() pkg, name := p.qualifiedName() typ := p.typ(nil) - p.declare(types.NewVar(token.NoPos, pkg, name, typ)) + p.declare(types.NewVar(pos, pkg, name, typ)) case funcTag: - p.pos() + pos := p.pos() pkg, name := p.qualifiedName() params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) - p.declare(types.NewFunc(token.NoPos, pkg, name, sig)) + p.declare(types.NewFunc(pos, pkg, name, sig)) default: errorf("unexpected object tag %d", tag) } } -func (p *importer) pos() { +func (p *importer) pos() token.Pos { if !p.posInfoFormat { - return + return token.NoPos } file := p.prevFile @@ -265,9 +270,40 @@ func (p *importer) pos() { } p.prevLine = line - // TODO(gri) register new position + // Synthesize a token.Pos + + // Since we don't know the set of needed file positions, we + // reserve maxlines positions per file. + const maxlines = 64 * 1024 + f := p.files[file] + if f == nil { + f = p.fset.AddFile(file, -1, maxlines) + p.files[file] = f + // Allocate the fake linebreak indices on first use. + // TODO(adonovan): opt: save ~512KB using a more complex scheme? + fakeLinesOnce.Do(func() { + fakeLines = make([]int, maxlines) + for i := range fakeLines { + fakeLines[i] = i + } + }) + f.SetLines(fakeLines) + } + + if line > maxlines { + line = 1 + } + + // Treat the file as if it contained only newlines + // and column=1: use the line number as the offset. + return f.Pos(line - 1) } +var ( + fakeLines []int + fakeLinesOnce sync.Once +) + func (p *importer) qualifiedName() (pkg *types.Package, name string) { name = p.string() pkg = p.pkg() @@ -303,14 +339,14 @@ func (p *importer) typ(parent *types.Package) types.Type { switch i { case namedTag: // read type object - p.pos() + pos := p.pos() parent, name := p.qualifiedName() scope := parent.Scope() obj := scope.Lookup(name) // if the object doesn't exist yet, create and insert it if obj == nil { - obj = types.NewTypeName(token.NoPos, parent, name, nil) + obj = types.NewTypeName(pos, parent, name, nil) scope.Insert(obj) } @@ -336,7 +372,7 @@ func (p *importer) typ(parent *types.Package) types.Type { // read associated methods for i := p.int(); i > 0; i-- { // TODO(gri) replace this with something closer to fieldName - p.pos() + pos := p.pos() name := p.string() if !exported(name) { p.pkg() @@ -348,7 +384,7 @@ func (p *importer) typ(parent *types.Package) types.Type { p.int() // go:nointerface pragma - discarded sig := types.NewSignature(recv.At(0), params, result, isddd) - t0.AddMethod(types.NewFunc(token.NoPos, parent, name, sig)) + t0.AddMethod(types.NewFunc(pos, parent, name, sig)) } return t @@ -483,7 +519,7 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [ } func (p *importer) field(parent *types.Package) *types.Var { - p.pos() + pos := p.pos() pkg, name := p.fieldName(parent) typ := p.typ(parent) @@ -502,7 +538,7 @@ func (p *importer) field(parent *types.Package) *types.Var { anonymous = true } - return types.NewField(token.NoPos, pkg, name, typ, anonymous) + return types.NewField(pos, pkg, name, typ, anonymous) } func (p *importer) methodList(parent *types.Package) (methods []*types.Func) { @@ -516,12 +552,12 @@ func (p *importer) methodList(parent *types.Package) (methods []*types.Func) { } func (p *importer) method(parent *types.Package) *types.Func { - p.pos() + pos := p.pos() pkg, name := p.fieldName(parent) params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) - return types.NewFunc(token.NoPos, pkg, name, sig) + return types.NewFunc(pos, pkg, name, sig) } func (p *importer) fieldName(parent *types.Package) (*types.Package, string) { @@ -613,6 +649,8 @@ func (p *importer) value() constant.Value { return constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) case stringTag: return constant.MakeString(p.string()) + case unknownTag: + return constant.MakeUnknown() default: errorf("unexpected value tag %d", tag) panic("unreachable") diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go index 1e1102a451..f99f0f8bef 100644 --- a/src/go/internal/gcimporter/gcimporter.go +++ b/src/go/internal/gcimporter/gcimporter.go @@ -9,6 +9,7 @@ import ( "bufio" "fmt" "go/build" + "go/token" "go/types" "io/ioutil" "os" @@ -123,7 +124,10 @@ func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types var data []byte data, err = ioutil.ReadAll(buf) if err == nil { - _, pkg, err = BImportData(packages, data, id) + // TODO(gri): allow clients of go/importer to provide a FileSet. + // Or, define a new standard go/types/gcexportdata package. + fset := token.NewFileSet() + _, pkg, err = BImportData(fset, packages, data, id) return } default: