Commit 59be2261 authored by Robert Griesemer's avatar Robert Griesemer

go/importer: better error message when importer is out of date

Separated out panic handling for bimporter and importer so that
the handler can consider the current version and report a better
error.

Added new export data test for export data version 999 (created
by changing the compiler temporarily) and verifying expected
error message.

Fixes #25856.

Change-Id: Iaafec07b79499154ef7c007341783fa07c57f24d
Reviewed-on: https://go-review.googlesource.com/118496
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarMatthew Dempsky <mdempsky@google.com>
parent 4f6b9ed5
...@@ -50,24 +50,24 @@ type importer struct { ...@@ -50,24 +50,24 @@ type importer struct {
// compromised, an error is returned. // compromised, an error is returned.
func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
// catch panics and return them as errors // catch panics and return them as errors
const currentVersion = 6
version := -1 // unknown version
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
// The package (filename) causing the problem is added to this
// error by a wrapper in the caller (Import in gcimporter.go).
// Return a (possibly nil or incomplete) package unchanged (see #16088). // Return a (possibly nil or incomplete) package unchanged (see #16088).
err = fmt.Errorf("cannot import, possibly version skew (%v) - reinstall package", e) if version > currentVersion {
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
} else {
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
} }
}()
if len(data) > 0 && data[0] == 'i' {
return iImportData(fset, imports, data[1:], path)
} }
}()
p := importer{ p := importer{
imports: imports, imports: imports,
data: data, data: data,
importpath: path, importpath: path,
version: -1, // unknown version version: version,
strList: []string{""}, // empty string is mapped to 0 strList: []string{""}, // empty string is mapped to 0
pathList: []string{""}, // empty string is mapped to 0 pathList: []string{""}, // empty string is mapped to 0
fake: fakeFileSet{ fake: fakeFileSet{
...@@ -92,7 +92,7 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] ...@@ -92,7 +92,7 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []
p.posInfoFormat = p.int() != 0 p.posInfoFormat = p.int() != 0
versionstr = p.string() versionstr = p.string()
if versionstr == "v1" { if versionstr == "v1" {
p.version = 0 version = 0
} }
} else { } else {
// Go1.8 extensible encoding // Go1.8 extensible encoding
...@@ -100,24 +100,25 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] ...@@ -100,24 +100,25 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []
versionstr = p.rawStringln(b) versionstr = p.rawStringln(b)
if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" { if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" {
if v, err := strconv.Atoi(s[1]); err == nil && v > 0 { if v, err := strconv.Atoi(s[1]); err == nil && v > 0 {
p.version = v version = v
} }
} }
} }
p.version = version
// read version specific flags - extend as necessary // read version specific flags - extend as necessary
switch p.version { switch p.version {
// case 7: // case currentVersion:
// ... // ...
// fallthrough // fallthrough
case 6, 5, 4, 3, 2, 1: case currentVersion, 5, 4, 3, 2, 1:
p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
p.trackAllTypes = p.int() != 0 p.trackAllTypes = p.int() != 0
p.posInfoFormat = p.int() != 0 p.posInfoFormat = p.int() != 0
case 0: case 0:
// Go1.7 encoding format - nothing to do here // Go1.7 encoding format - nothing to do here
default: default:
errorf("unknown export format version %d (%q)", p.version, versionstr) errorf("unknown bexport format version %d (%q)", p.version, versionstr)
} }
// --- generic export data --- // --- generic export data ---
......
...@@ -144,16 +144,27 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func ...@@ -144,16 +144,27 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
switch hdr { switch hdr {
case "$$\n": case "$$\n":
err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path) err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path)
case "$$B\n": case "$$B\n":
var data []byte var data []byte
data, err = ioutil.ReadAll(buf) data, err = ioutil.ReadAll(buf)
if err == nil { if err != nil {
break
}
// TODO(gri): allow clients of go/importer to provide a FileSet. // TODO(gri): allow clients of go/importer to provide a FileSet.
// Or, define a new standard go/types/gcexportdata package. // Or, define a new standard go/types/gcexportdata package.
fset := token.NewFileSet() fset := token.NewFileSet()
// The indexed export format starts with an 'i'; the older
// binary export format starts with a 'c', 'd', or 'v'
// (from "version"). Select appropriate importer.
if len(data) > 0 && data[0] == 'i' {
_, pkg, err = iImportData(fset, packages, data[1:], id)
} else {
_, pkg, err = BImportData(fset, packages, data, id) _, pkg, err = BImportData(fset, packages, data, id)
return
} }
default: default:
err = fmt.Errorf("unknown export data header: %q", hdr) err = fmt.Errorf("unknown export data header: %q", hdr)
} }
......
...@@ -141,9 +141,21 @@ func TestVersionHandling(t *testing.T) { ...@@ -141,9 +141,21 @@ func TestVersionHandling(t *testing.T) {
} }
pkgpath := "./" + name[:len(name)-2] pkgpath := "./" + name[:len(name)-2]
if testing.Verbose() {
t.Logf("importing %s", name)
}
// test that export data can be imported // test that export data can be imported
_, err := Import(make(map[string]*types.Package), pkgpath, dir, nil) _, err := Import(make(map[string]*types.Package), pkgpath, dir, nil)
if err != nil { if err != nil {
// ok to fail if it fails with a newer version error for select files
if strings.Contains(err.Error(), "newer version") {
switch name {
case "test_go1.11_999b.a", "test_go1.11_999i.a":
continue
}
// fall through
}
t.Errorf("import %q failed: %v", pkgpath, err) t.Errorf("import %q failed: %v", pkgpath, err)
continue continue
} }
......
...@@ -10,6 +10,7 @@ package gcimporter ...@@ -10,6 +10,7 @@ package gcimporter
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt"
"go/constant" "go/constant"
"go/token" "go/token"
"go/types" "go/types"
...@@ -60,13 +61,25 @@ const ( ...@@ -60,13 +61,25 @@ const (
// If the export data version is not recognized or the format is otherwise // If the export data version is not recognized or the format is otherwise
// compromised, an error is returned. // compromised, an error is returned.
func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
const currentVersion = 0
version := -1
defer func() {
if e := recover(); e != nil {
if version > currentVersion {
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
} else {
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
}
}
}()
r := &intReader{bytes.NewReader(data), path} r := &intReader{bytes.NewReader(data), path}
version := r.uint64() version = int(r.uint64())
switch version { switch version {
case 0: case currentVersion:
default: default:
errorf("cannot import %q: unknown iexport format version %d", path, version) errorf("unknown iexport format version %d", version)
} }
sLen := int64(r.uint64()) sLen := int64(r.uint64())
......
...@@ -11,7 +11,10 @@ ...@@ -11,7 +11,10 @@
// //
// go build -o test_go1.$X_$Y.a test.go // go build -o test_go1.$X_$Y.a test.go
// //
// with $X = Go version and $Y = export format version. // with $X = Go version and $Y = export format version
// (add 'b' or 'i' to distinguish between binary and
// indexed format starting with 1.11 as long as both
// formats are supported).
// //
// Make sure this source is extended such that it exercises // Make sure this source is extended such that it exercises
// whatever export format change has taken place. // whatever export format change has taken place.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment