"sort"
"strconv"
"strings"
+ "sync"
"unicode"
"unicode/utf8"
)
posInfoFormat bool
prevFile string
prevLine int
+ fset *token.FileSet
+ files map[string]*token.File
// debugging support
debugFormat bool
// 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 {
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
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
}
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()
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)
}
// 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()
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
}
func (p *importer) field(parent *types.Package) *types.Var {
- p.pos()
+ pos := p.pos()
pkg, name := p.fieldName(parent)
typ := p.typ(parent)
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) {
}
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) {
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")