diff --git a/go/gcimporter15/bexport.go b/go/gcimporter15/bexport.go index 7dec3005..936459bb 100644 --- a/go/gcimporter15/bexport.go +++ b/go/gcimporter15/bexport.go @@ -40,10 +40,9 @@ const debugFormat = false // default: false // If trace is set, debugging output is printed to std out. const trace = false // default: false -// This version doesn't write the nointerface flag for exported methods. -// The corresponding importer handles both "v0" and "v1". -// See also issues #16243, #16244. -const exportVersion = "v0" +// Current export format version. +// Must not start with 'c' or 'd' (initials of prior format). +const exportVersion = "version 1" // trackAllTypes enables cycle tracking for all types, not just named // types. The existing compiler invariants assume that unnamed types @@ -86,39 +85,18 @@ func BExportData(fset *token.FileSet, pkg *types.Package) []byte { posInfoFormat: true, // TODO(gri) might become a flag, eventually } - // first byte indicates low-level encoding format - var format byte = 'c' // compact + // write version info + p.rawStringln(exportVersion) + var debug string if debugFormat { - format = 'd' + debug = "debug" } - p.rawByte(format) - - format = 'n' // track named types only - if trackAllTypes { - format = 'a' - } - p.rawByte(format) - - // posInfo exported or not? + p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly + p.bool(trackAllTypes) p.bool(p.posInfoFormat) // --- generic export data --- - if trace { - p.tracef("\n--- generic export data ---\n") - if p.indent != 0 { - log.Fatalf("gcimporter: incorrect indentation %d", p.indent) - } - } - - if trace { - p.tracef("version = ") - } - p.string(exportVersion) - if trace { - p.tracef("\n") - } - // populate type map with predeclared "known" types for index, typ := range predeclared { p.typIndex[typ] = index @@ -401,6 +379,7 @@ func (p *exporter) assocMethods(named *types.Named) { p.paramList(types.NewTuple(sig.Recv()), false) p.paramList(sig.Params(), sig.Variadic()) p.paramList(sig.Results(), false) + p.int(0) // dummy value for go:nointerface pragma - ignored by importer } if trace && methods != nil { @@ -719,7 +698,7 @@ func (p *exporter) marker(m byte) { p.rawInt64(int64(p.written)) } -// rawInt64 should only be used by low-level encoders +// rawInt64 should only be used by low-level encoders. func (p *exporter) rawInt64(x int64) { var tmp [binary.MaxVarintLen64]byte n := binary.PutVarint(tmp[:], x) @@ -728,6 +707,14 @@ func (p *exporter) rawInt64(x int64) { } } +// rawStringln should only be used to emit the initial version string. +func (p *exporter) rawStringln(s string) { + for i := 0; i < len(s); i++ { + p.rawByte(s[i]) + } + p.rawByte('\n') +} + // rawByte is the bottleneck interface to write to p.out. // rawByte escapes b as follows (any encoding does that // hides '$'): diff --git a/go/gcimporter15/bimport.go b/go/gcimporter15/bimport.go index b2af4b19..d172d76c 100644 --- a/go/gcimporter15/bimport.go +++ b/go/gcimporter15/bimport.go @@ -26,7 +26,6 @@ type importer struct { data []byte path string buf []byte // for reading strings - version string // object lists strList []string // in order of appearance @@ -60,27 +59,36 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] files: make(map[string]*token.File), } - // read low-level encoding format - switch format := p.rawByte(); format { - case 'c': - // compact format - nothing to do - case 'd': - p.debugFormat = true - default: - return p.read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format) + // read version info + if b := p.rawByte(); b == 'c' || b == 'd' { + // Go1.7 encoding; first byte encodes low-level + // encoding format (compact vs debug). + // For backward-compatibility only (avoid problems with + // old installed packages). Newly compiled packages use + // the extensible format string. + // TODO(gri) Remove this support eventually; after Go1.8. + if b == 'd' { + p.debugFormat = true + } + p.trackAllTypes = p.rawByte() == 'a' + p.posInfoFormat = p.int() != 0 + const go17version = "v1" + if s := p.string(); s != go17version { + return p.read, nil, fmt.Errorf("importer: unknown export data format: %s (imported package compiled with old compiler?)", s) + } + } else { + // Go1.8 extensible encoding + const exportVersion = "version 1" + if s := p.rawStringln(b); s != exportVersion { + return p.read, nil, fmt.Errorf("importer: unknown export data format: %s (imported package compiled with old compiler?)", s) + } + p.debugFormat = p.rawStringln(p.rawByte()) == "debug" + p.trackAllTypes = p.int() != 0 + p.posInfoFormat = p.int() != 0 } - p.trackAllTypes = p.rawByte() == 'a' - - p.posInfoFormat = p.int() != 0 - // --- generic export data --- - p.version = p.string() - if p.version != "v0" && p.version != "v1" { - return p.read, nil, fmt.Errorf("unknown export data version: %s", p.version) - } - // populate typList with predeclared "known" types p.typList = append(p.typList, predeclared...) @@ -345,10 +353,7 @@ func (p *importer) typ(parent *types.Package) types.Type { recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? params, isddd := p.paramList() result, _ := p.paramList() - - if p.version == "v1" { - p.int() // nointerface flag - discarded - } + p.int() // go:nointerface pragma - discarded sig := types.NewSignature(recv.At(0), params, result, isddd) t0.AddMethod(types.NewFunc(pos, parent, name, sig)) @@ -729,7 +734,7 @@ func (p *importer) marker(want byte) { } } -// rawInt64 should only be used by low-level decoders +// rawInt64 should only be used by low-level decoders. func (p *importer) rawInt64() int64 { i, err := binary.ReadVarint(p) if err != nil { @@ -738,6 +743,16 @@ func (p *importer) rawInt64() int64 { return i } +// rawStringln should only be used to read the initial version string. +func (p *importer) rawStringln(b byte) string { + p.buf = p.buf[:0] + for b != '\n' { + p.buf = append(p.buf, b) + b = p.rawByte() + } + return string(p.buf) +} + // needed for binary.ReadVarint in rawInt64 func (p *importer) ReadByte() (byte, error) { return p.rawByte(), nil