Skip to content

Commit 59be226

Browse files
committed
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]>
1 parent 4f6b9ed commit 59be226

File tree

9 files changed

+62
-22
lines changed

9 files changed

+62
-22
lines changed

src/go/internal/gcimporter/bimport.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,24 +50,24 @@ type importer struct {
5050
// compromised, an error is returned.
5151
func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
5252
// catch panics and return them as errors
53+
const currentVersion = 6
54+
version := -1 // unknown version
5355
defer func() {
5456
if e := recover(); e != nil {
55-
// The package (filename) causing the problem is added to this
56-
// error by a wrapper in the caller (Import in gcimporter.go).
5757
// Return a (possibly nil or incomplete) package unchanged (see #16088).
58-
err = fmt.Errorf("cannot import, possibly version skew (%v) - reinstall package", e)
58+
if version > currentVersion {
59+
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
60+
} else {
61+
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
62+
}
5963
}
6064
}()
6165

62-
if len(data) > 0 && data[0] == 'i' {
63-
return iImportData(fset, imports, data[1:], path)
64-
}
65-
6666
p := importer{
6767
imports: imports,
6868
data: data,
6969
importpath: path,
70-
version: -1, // unknown version
70+
version: version,
7171
strList: []string{""}, // empty string is mapped to 0
7272
pathList: []string{""}, // empty string is mapped to 0
7373
fake: fakeFileSet{
@@ -92,32 +92,33 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []
9292
p.posInfoFormat = p.int() != 0
9393
versionstr = p.string()
9494
if versionstr == "v1" {
95-
p.version = 0
95+
version = 0
9696
}
9797
} else {
9898
// Go1.8 extensible encoding
9999
// read version string and extract version number (ignore anything after the version number)
100100
versionstr = p.rawStringln(b)
101101
if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" {
102102
if v, err := strconv.Atoi(s[1]); err == nil && v > 0 {
103-
p.version = v
103+
version = v
104104
}
105105
}
106106
}
107+
p.version = version
107108

108109
// read version specific flags - extend as necessary
109110
switch p.version {
110-
// case 7:
111+
// case currentVersion:
111112
// ...
112113
// fallthrough
113-
case 6, 5, 4, 3, 2, 1:
114+
case currentVersion, 5, 4, 3, 2, 1:
114115
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
115116
p.trackAllTypes = p.int() != 0
116117
p.posInfoFormat = p.int() != 0
117118
case 0:
118119
// Go1.7 encoding format - nothing to do here
119120
default:
120-
errorf("unknown export format version %d (%q)", p.version, versionstr)
121+
errorf("unknown bexport format version %d (%q)", p.version, versionstr)
121122
}
122123

123124
// --- generic export data ---

src/go/internal/gcimporter/gcimporter.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,27 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
144144
switch hdr {
145145
case "$$\n":
146146
err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path)
147+
147148
case "$$B\n":
148149
var data []byte
149150
data, err = ioutil.ReadAll(buf)
150-
if err == nil {
151-
// TODO(gri): allow clients of go/importer to provide a FileSet.
152-
// Or, define a new standard go/types/gcexportdata package.
153-
fset := token.NewFileSet()
151+
if err != nil {
152+
break
153+
}
154+
155+
// TODO(gri): allow clients of go/importer to provide a FileSet.
156+
// Or, define a new standard go/types/gcexportdata package.
157+
fset := token.NewFileSet()
158+
159+
// The indexed export format starts with an 'i'; the older
160+
// binary export format starts with a 'c', 'd', or 'v'
161+
// (from "version"). Select appropriate importer.
162+
if len(data) > 0 && data[0] == 'i' {
163+
_, pkg, err = iImportData(fset, packages, data[1:], id)
164+
} else {
154165
_, pkg, err = BImportData(fset, packages, data, id)
155-
return
156166
}
167+
157168
default:
158169
err = fmt.Errorf("unknown export data header: %q", hdr)
159170
}

src/go/internal/gcimporter/gcimporter_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,21 @@ func TestVersionHandling(t *testing.T) {
141141
}
142142
pkgpath := "./" + name[:len(name)-2]
143143

144+
if testing.Verbose() {
145+
t.Logf("importing %s", name)
146+
}
147+
144148
// test that export data can be imported
145149
_, err := Import(make(map[string]*types.Package), pkgpath, dir, nil)
146150
if err != nil {
151+
// ok to fail if it fails with a newer version error for select files
152+
if strings.Contains(err.Error(), "newer version") {
153+
switch name {
154+
case "test_go1.11_999b.a", "test_go1.11_999i.a":
155+
continue
156+
}
157+
// fall through
158+
}
147159
t.Errorf("import %q failed: %v", pkgpath, err)
148160
continue
149161
}

src/go/internal/gcimporter/iimport.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ package gcimporter
1010
import (
1111
"bytes"
1212
"encoding/binary"
13+
"fmt"
1314
"go/constant"
1415
"go/token"
1516
"go/types"
@@ -60,13 +61,25 @@ const (
6061
// If the export data version is not recognized or the format is otherwise
6162
// compromised, an error is returned.
6263
func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
64+
const currentVersion = 0
65+
version := -1
66+
defer func() {
67+
if e := recover(); e != nil {
68+
if version > currentVersion {
69+
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
70+
} else {
71+
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
72+
}
73+
}
74+
}()
75+
6376
r := &intReader{bytes.NewReader(data), path}
6477

65-
version := r.uint64()
78+
version = int(r.uint64())
6679
switch version {
67-
case 0:
80+
case currentVersion:
6881
default:
69-
errorf("cannot import %q: unknown iexport format version %d", path, version)
82+
errorf("unknown iexport format version %d", version)
7083
}
7184

7285
sLen := int64(r.uint64())

src/go/internal/gcimporter/testdata/versions/test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
//
1212
// go build -o test_go1.$X_$Y.a test.go
1313
//
14-
// with $X = Go version and $Y = export format version.
14+
// with $X = Go version and $Y = export format version
15+
// (add 'b' or 'i' to distinguish between binary and
16+
// indexed format starting with 1.11 as long as both
17+
// formats are supported).
1518
//
1619
// Make sure this source is extended such that it exercises
1720
// whatever export format change has taken place.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)