internal/pkgbits: copy over changes for V2
Copy over changes from GOROOT's internal/pkgbits for unified IR
version V2.
Updates golang/go#68778
Change-Id: I7813767e8c11a0d0227e2284af07ce0d86291476
Reviewed-on: https://go-review.googlesource.com/c/tools/+/609316
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/internal/pkgbits/decoder.go b/internal/pkgbits/decoder.go
index c299f03..f6cb37c 100644
--- a/internal/pkgbits/decoder.go
+++ b/internal/pkgbits/decoder.go
@@ -21,7 +21,7 @@
// export data.
type PkgDecoder struct {
// version is the file format version.
- version uint32
+ version Version
// sync indicates whether the file uses sync markers.
sync bool
@@ -78,14 +78,15 @@
r := strings.NewReader(input)
- assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil)
+ var ver uint32
+ assert(binary.Read(r, binary.LittleEndian, &ver) == nil)
+ pr.version = Version(ver)
- switch pr.version {
- default:
- panic(fmt.Errorf("cannot import %q, export data is newer version (%d) - update tool", pkgPath, pr.version))
- case 0:
- // no flags
- case 1:
+ if pr.version >= numVersions {
+ panic(fmt.Errorf("cannot decode %q, export data version %d is greater than maximum supported version %d", pkgPath, pr.version, numVersions-1))
+ }
+
+ if pr.version.Has(Flags) {
var flags uint32
assert(binary.Read(r, binary.LittleEndian, &flags) == nil)
pr.sync = flags&flagSyncMarkers != 0
@@ -100,7 +101,9 @@
assert(err == nil)
pr.elemData = input[pos:]
- assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1]))
+
+ const fingerprintSize = 8
+ assert(len(pr.elemData)-fingerprintSize == int(pr.elemEnds[len(pr.elemEnds)-1]))
return pr
}
@@ -134,7 +137,7 @@
absIdx += int(pr.elemEndsEnds[k-1])
}
if absIdx >= int(pr.elemEndsEnds[k]) {
- errorf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds)
+ panicf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds)
}
return absIdx
}
@@ -191,9 +194,7 @@
Idx: idx,
}
- // TODO(mdempsky) r.data.Reset(...) after #44505 is resolved.
- r.Data = *strings.NewReader(pr.DataIdx(k, idx))
-
+ r.Data.Reset(pr.DataIdx(k, idx))
r.Sync(SyncRelocs)
r.Relocs = make([]RelocEnt, r.Len())
for i := range r.Relocs {
@@ -242,7 +243,7 @@
func (r *Decoder) checkErr(err error) {
if err != nil {
- errorf("unexpected decoding error: %w", err)
+ panicf("unexpected decoding error: %w", err)
}
}
@@ -513,3 +514,6 @@
return path, name, tag
}
+
+// Version reports the version of the bitstream.
+func (w *Decoder) Version() Version { return w.common.version }
diff --git a/internal/pkgbits/encoder.go b/internal/pkgbits/encoder.go
index 6482617..c17a123 100644
--- a/internal/pkgbits/encoder.go
+++ b/internal/pkgbits/encoder.go
@@ -12,18 +12,15 @@
"io"
"math/big"
"runtime"
+ "strings"
)
-// currentVersion is the current version number.
-//
-// - v0: initial prototype
-//
-// - v1: adds the flags uint32 word
-const currentVersion uint32 = 1
-
// A PkgEncoder provides methods for encoding a package's Unified IR
// export data.
type PkgEncoder struct {
+ // version of the bitstream.
+ version Version
+
// elems holds the bitstream for previously encoded elements.
elems [numRelocs][]string
@@ -47,8 +44,9 @@
// export data files, but can help diagnosing desync errors in
// higher-level Unified IR reader/writer code. If syncFrames is
// negative, then sync markers are omitted entirely.
-func NewPkgEncoder(syncFrames int) PkgEncoder {
+func NewPkgEncoder(version Version, syncFrames int) PkgEncoder {
return PkgEncoder{
+ version: version,
stringsIdx: make(map[string]Index),
syncFrames: syncFrames,
}
@@ -64,13 +62,15 @@
assert(binary.Write(out, binary.LittleEndian, x) == nil)
}
- writeUint32(currentVersion)
+ writeUint32(uint32(pw.version))
- var flags uint32
- if pw.SyncMarkers() {
- flags |= flagSyncMarkers
+ if pw.version.Has(Flags) {
+ var flags uint32
+ if pw.SyncMarkers() {
+ flags |= flagSyncMarkers
+ }
+ writeUint32(flags)
}
- writeUint32(flags)
// Write elemEndsEnds.
var sum uint32
@@ -159,7 +159,7 @@
// Flush finalizes the element's bitstream and returns its Index.
func (w *Encoder) Flush() Index {
- var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved
+ var sb strings.Builder
// Backup the data so we write the relocations at the front.
var tmp bytes.Buffer
@@ -189,7 +189,7 @@
func (w *Encoder) checkErr(err error) {
if err != nil {
- errorf("unexpected encoding error: %v", err)
+ panicf("unexpected encoding error: %v", err)
}
}
@@ -320,8 +320,14 @@
// section (if not already present), and then writing a relocation
// into the element bitstream.
func (w *Encoder) String(s string) {
+ w.StringRef(w.p.StringIdx(s))
+}
+
+// StringRef writes a reference to the given index, which must be a
+// previously encoded string value.
+func (w *Encoder) StringRef(idx Index) {
w.Sync(SyncString)
- w.Reloc(RelocString, w.p.StringIdx(s))
+ w.Reloc(RelocString, idx)
}
// Strings encodes and writes a variable-length slice of strings into
@@ -348,7 +354,7 @@
func (w *Encoder) scalar(val constant.Value) {
switch v := constant.Val(val).(type) {
default:
- errorf("unhandled %v (%v)", val, val.Kind())
+ panicf("unhandled %v (%v)", val, val.Kind())
case bool:
w.Code(ValBool)
w.Bool(v)
@@ -381,3 +387,6 @@
b := v.Append(nil, 'p', -1)
w.String(string(b)) // TODO: More efficient encoding.
}
+
+// Version reports the version of the bitstream.
+func (w *Encoder) Version() Version { return w.p.version }
diff --git a/internal/pkgbits/pkgbits_test.go b/internal/pkgbits/pkgbits_test.go
new file mode 100644
index 0000000..b8f946a
--- /dev/null
+++ b/internal/pkgbits/pkgbits_test.go
@@ -0,0 +1,77 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkgbits_test
+
+import (
+ "strings"
+ "testing"
+
+ "golang.org/x/tools/internal/pkgbits"
+)
+
+func TestRoundTrip(t *testing.T) {
+ for _, version := range []pkgbits.Version{
+ pkgbits.V0,
+ pkgbits.V1,
+ pkgbits.V2,
+ } {
+ pw := pkgbits.NewPkgEncoder(version, -1)
+ w := pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPublic)
+ w.Flush()
+
+ var b strings.Builder
+ _ = pw.DumpTo(&b)
+ input := b.String()
+
+ pr := pkgbits.NewPkgDecoder("package_id", input)
+ r := pr.NewDecoder(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
+
+ if r.Version() != w.Version() {
+ t.Errorf("Expected reader version %q to be the writer version %q", r.Version(), w.Version())
+ }
+ }
+}
+
+// Type checker to enforce that know V* have the constant values they must have.
+var _ [0]bool = [pkgbits.V0]bool{}
+var _ [1]bool = [pkgbits.V1]bool{}
+
+func TestVersions(t *testing.T) {
+ type vfpair struct {
+ v pkgbits.Version
+ f pkgbits.Field
+ }
+
+ // has field tests
+ for _, c := range []vfpair{
+ {pkgbits.V1, pkgbits.Flags},
+ {pkgbits.V2, pkgbits.Flags},
+ {pkgbits.V0, pkgbits.HasInit},
+ {pkgbits.V1, pkgbits.HasInit},
+ {pkgbits.V0, pkgbits.DerivedFuncInstance},
+ {pkgbits.V1, pkgbits.DerivedFuncInstance},
+ {pkgbits.V0, pkgbits.DerivedInfoNeeded},
+ {pkgbits.V1, pkgbits.DerivedInfoNeeded},
+ {pkgbits.V2, pkgbits.AliasTypeParamNames},
+ } {
+ if !c.v.Has(c.f) {
+ t.Errorf("Expected version %v to have field %v", c.v, c.f)
+ }
+ }
+
+ // does not have field tests
+ for _, c := range []vfpair{
+ {pkgbits.V0, pkgbits.Flags},
+ {pkgbits.V2, pkgbits.HasInit},
+ {pkgbits.V2, pkgbits.DerivedFuncInstance},
+ {pkgbits.V2, pkgbits.DerivedInfoNeeded},
+ {pkgbits.V0, pkgbits.AliasTypeParamNames},
+ {pkgbits.V1, pkgbits.AliasTypeParamNames},
+ } {
+ if c.v.Has(c.f) {
+ t.Errorf("Expected version %v to not have field %v", c.v, c.f)
+ }
+ }
+}
diff --git a/internal/pkgbits/support.go b/internal/pkgbits/support.go
index ad26d3b..50534a2 100644
--- a/internal/pkgbits/support.go
+++ b/internal/pkgbits/support.go
@@ -12,6 +12,6 @@
}
}
-func errorf(format string, args ...interface{}) {
+func panicf(format string, args ...any) {
panic(fmt.Errorf(format, args...))
}
diff --git a/internal/pkgbits/sync.go b/internal/pkgbits/sync.go
index 5bd51ef..a17a008 100644
--- a/internal/pkgbits/sync.go
+++ b/internal/pkgbits/sync.go
@@ -110,4 +110,8 @@
SyncStmtsEnd
SyncLabel
SyncOptLabel
+
+ SyncMultiExpr
+ SyncRType
+ SyncConvRTTI
)
diff --git a/internal/pkgbits/syncmarker_string.go b/internal/pkgbits/syncmarker_string.go
index 4a5b0ca..582ad56 100644
--- a/internal/pkgbits/syncmarker_string.go
+++ b/internal/pkgbits/syncmarker_string.go
@@ -74,11 +74,14 @@
_ = x[SyncStmtsEnd-64]
_ = x[SyncLabel-65]
_ = x[SyncOptLabel-66]
+ _ = x[SyncMultiExpr-67]
+ _ = x[SyncRType-68]
+ _ = x[SyncConvRTTI-69]
}
-const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel"
+const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabelMultiExprRTypeConvRTTI"
-var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458}
+var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458, 467, 472, 480}
func (i SyncMarker) String() string {
i -= 1
diff --git a/internal/pkgbits/version.go b/internal/pkgbits/version.go
new file mode 100644
index 0000000..53af9df
--- /dev/null
+++ b/internal/pkgbits/version.go
@@ -0,0 +1,85 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkgbits
+
+// Version indicates a version of a unified IR bitstream.
+// Each Version indicates the addition, removal, or change of
+// new data in the bitstream.
+//
+// These are serialized to disk and the interpretation remains fixed.
+type Version uint32
+
+const (
+ // V0: initial prototype.
+ //
+ // All data that is not assigned a Field is in version V0
+ // and has not been deprecated.
+ V0 Version = iota
+
+ // V1: adds the Flags uint32 word
+ V1
+
+ // V2: removes unused legacy fields and supports type parameters for aliases.
+ // - remove the legacy "has init" bool from the public root
+ // - remove obj's "derived func instance" bool
+ // - add a TypeParamNames field to ObjAlias
+ // - remove derived info "needed" bool
+ V2
+
+ numVersions = iota
+)
+
+// Field denotes a unit of data in the serialized unified IR bitstream.
+// It is conceptually a like field in a structure.
+//
+// We only really need Fields when the data may or may not be present
+// in a stream based on the Version of the bitstream.
+//
+// Unlike much of pkgbits, Fields are not serialized and
+// can change values as needed.
+type Field int
+
+const (
+ // Flags in a uint32 in the header of a bitstream
+ // that is used to indicate whether optional features are enabled.
+ Flags Field = iota
+
+ // Deprecated: HasInit was a bool indicating whether a package
+ // has any init functions.
+ HasInit
+
+ // Deprecated: DerivedFuncInstance was a bool indicating
+ // whether an object was a function instance.
+ DerivedFuncInstance
+
+ // ObjAlias has a list of TypeParamNames.
+ AliasTypeParamNames
+
+ // Deprecated: DerivedInfoNeeded was a bool indicating
+ // whether a type was a derived type.
+ DerivedInfoNeeded
+
+ numFields = iota
+)
+
+// introduced is the version a field was added.
+var introduced = [numFields]Version{
+ Flags: V1,
+ AliasTypeParamNames: V2,
+}
+
+// removed is the version a field was removed in or 0 for fields
+// that have not yet been deprecated.
+// (So removed[f]-1 is the last version it is included in.)
+var removed = [numFields]Version{
+ HasInit: V2,
+ DerivedFuncInstance: V2,
+ DerivedInfoNeeded: V2,
+}
+
+// Has reports whether field f is present in a bitstream at version v.
+func (v Version) Has(f Field) bool {
+ return introduced[f] <= v && (v < removed[f] || removed[f] == V0)
+}