gopls/internal/lsp/frob: make API generic
Change-Id: Ia3f2c68cec6588fa23f35d1a125d9f602767f375
Reviewed-on: https://go-review.googlesource.com/c/tools/+/510378
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/analysis.go
index c5f6f72..1cfa84d 100644
--- a/gopls/internal/lsp/cache/analysis.go
+++ b/gopls/internal/lsp/cache/analysis.go
@@ -1403,14 +1403,12 @@
return buf.Bytes()
}
-// var analyzeSummaryCodec = frob.For[*analyzeSummary]()
-var analyzeSummaryCodec = frob.CodecFor117(new(*analyzeSummary))
+var analyzeSummaryCodec = frob.CodecFor[*analyzeSummary]()
// -- data types for serialization of analysis.Diagnostic and source.Diagnostic --
// (The name says gob but we use frob.)
-// var diagnosticsCodec = frob.For[[]gobDiagnostic]()
-var diagnosticsCodec = frob.CodecFor117(new([]gobDiagnostic))
+var diagnosticsCodec = frob.CodecFor[[]gobDiagnostic]()
type gobDiagnostic struct {
Location protocol.Location
diff --git a/gopls/internal/lsp/frob/frob.go b/gopls/internal/lsp/frob/frob.go
index 5582ebe..e3abcfe 100644
--- a/gopls/internal/lsp/frob/frob.go
+++ b/gopls/internal/lsp/frob/frob.go
@@ -3,11 +3,18 @@
// license that can be found in the LICENSE file.
// Package frob is a fast restricted object encoder/decoder in the
-// spirit of gob. Restrictions include:
+// spirit of encoding/gob.
//
-// - Interface values are not supported. This avoids the need for
+// As with gob, types that recursively contain functions,
+// channels, and unsafe.Pointers cannot encoded, but frob has these
+// additional restrictions:
+//
+// - Interface values are not supported; this avoids the need for
// the encoding to describe types.
//
+// - Types that recursively contain private struct fields are not
+// permitted.
+//
// - The encoding is unspecified and subject to change, so the encoder
// and decoder must exactly agree on their implementation and on the
// definitions of the target types.
@@ -33,38 +40,20 @@
"sync"
)
-// Use CodecFor117(new(T)) to create a codec for values of type T.
-// Then call Encode(T) and Decode(data, *T).
-// This is a placeholder for the forthcoming generic API -- see below.
-// CodecFor117 panics if type T is unsuitable.
-func CodecFor117(x any) Codec {
- frobsMu.Lock()
- defer frobsMu.Unlock()
- return Codec{frobFor(reflect.TypeOf(x).Elem())}
-}
-
-type any = interface{}
-
-// A Codec is an immutable encoder and decoder for values of a particular type.
-type Codec struct{ *frob }
-
-// TODO(adonovan): after go1.18, enable this generic interface.
-/*
-
-// CodecFor[T] returns a codec for values of type T.
-//
-// For panics if the type recursively contains members of unsupported
-// types: functions, channels, interfaces, unsafe.Pointer.
-func CodecFor[T any]() Codec[T] { return For117((*T)(nil)) }
-
// A Codec[T] is an immutable encoder and decoder for values of type T.
type Codec[T any] struct{ frob *frob }
+// CodecFor[T] returns a codec for values of type T.
+// It panics if type T is unsuitable.
+func CodecFor[T any]() Codec[T] {
+ frobsMu.Lock()
+ defer frobsMu.Unlock()
+ return Codec[T]{frobFor(reflect.TypeOf((*T)(nil)).Elem())}
+}
+
func (codec Codec[T]) Encode(v T) []byte { return codec.frob.Encode(v) }
func (codec Codec[T]) Decode(data []byte, ptr *T) { codec.frob.Decode(data, ptr) }
-*/
-
var (
frobsMu sync.Mutex
frobs = make(map[reflect.Type]*frob)
@@ -106,7 +95,7 @@
case reflect.Array,
reflect.Slice,
- reflect.Ptr: // TODO(adonovan): after go1.18, use Pointer
+ reflect.Pointer:
fr.addElem(fr.t.Elem())
case reflect.Map:
@@ -224,7 +213,7 @@
}
}
- case reflect.Ptr: // TODO(adonovan): after go1.18, use Pointer
+ case reflect.Pointer:
if v.IsNil() {
out.uint8(0)
} else {
@@ -337,7 +326,7 @@
kzero := reflect.Zero(kfrob.t)
vzero := reflect.Zero(vfrob.t)
for i := 0; i < len; i++ {
- // TODO(adonovan): after go1.18, use SetZero.
+ // TODO(adonovan): use SetZero from go1.20.
// k.SetZero()
// v.SetZero()
k.Set(kzero)
@@ -348,7 +337,7 @@
}
}
- case reflect.Ptr: // TODO(adonovan): after go1.18, use Pointer
+ case reflect.Pointer:
isNil := in.uint8() == 0
if !isNil {
ptr := reflect.New(fr.elems[0].t)
@@ -409,38 +398,7 @@
type writer struct{ data []byte }
func (w *writer) uint8(v uint8) { w.data = append(w.data, v) }
-func (w *writer) uint16(v uint16) { w.data = appendUint16(w.data, v) }
-func (w *writer) uint32(v uint32) { w.data = appendUint32(w.data, v) }
-func (w *writer) uint64(v uint64) { w.data = appendUint64(w.data, v) }
+func (w *writer) uint16(v uint16) { w.data = le.AppendUint16(w.data, v) }
+func (w *writer) uint32(v uint32) { w.data = le.AppendUint32(w.data, v) }
+func (w *writer) uint64(v uint64) { w.data = le.AppendUint64(w.data, v) }
func (w *writer) bytes(v []byte) { w.data = append(w.data, v...) }
-
-// TODO(adonovan): delete these as in go1.18 they are methods on LittleEndian:
-
-func appendUint16(b []byte, v uint16) []byte {
- return append(b,
- byte(v),
- byte(v>>8),
- )
-}
-
-func appendUint32(b []byte, v uint32) []byte {
- return append(b,
- byte(v),
- byte(v>>8),
- byte(v>>16),
- byte(v>>24),
- )
-}
-
-func appendUint64(b []byte, v uint64) []byte {
- return append(b,
- byte(v),
- byte(v>>8),
- byte(v>>16),
- byte(v>>24),
- byte(v>>32),
- byte(v>>40),
- byte(v>>48),
- byte(v>>56),
- )
-}
diff --git a/gopls/internal/lsp/frob/frob_test.go b/gopls/internal/lsp/frob/frob_test.go
index d2a9f2a..892f18b 100644
--- a/gopls/internal/lsp/frob/frob_test.go
+++ b/gopls/internal/lsp/frob/frob_test.go
@@ -19,7 +19,7 @@
C *Basics
D map[string]int
}
- codec := frob.CodecFor117(new(Basics))
+ codec := frob.CodecFor[Basics]()
s1, s2 := "hello", "world"
x := Basics{
@@ -55,7 +55,7 @@
C64 complex64
C128 complex128
}
- codec := frob.CodecFor117(new(Ints))
+ codec := frob.CodecFor[Ints]()
// maxima
max1 := Ints{
diff --git a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/lsp/source/methodsets/methodsets.go
index 1ade740..d934c3c 100644
--- a/gopls/internal/lsp/source/methodsets/methodsets.go
+++ b/gopls/internal/lsp/source/methodsets/methodsets.go
@@ -455,8 +455,7 @@
// -- serial format of index --
// (The name says gob but in fact we use frob.)
-// var packageCodec = frob.For[gobPackage]()
-var packageCodec = frob.CodecFor117(new(gobPackage))
+var packageCodec = frob.CodecFor[gobPackage]()
// A gobPackage records the method set of each package-level type for a single package.
type gobPackage struct {
diff --git a/gopls/internal/lsp/source/typerefs/refs.go b/gopls/internal/lsp/source/typerefs/refs.go
index 2f6b1d9..9adbb88 100644
--- a/gopls/internal/lsp/source/typerefs/refs.go
+++ b/gopls/internal/lsp/source/typerefs/refs.go
@@ -738,8 +738,7 @@
// -- serialization --
// (The name says gob but in fact we use frob.)
-// var classesCodec = frob.For[gobClasses]()
-var classesCodec = frob.CodecFor117(new(gobClasses))
+var classesCodec = frob.CodecFor[gobClasses]()
type gobClasses struct {
Strings []string // table of strings (PackageIDs and names)
diff --git a/gopls/internal/lsp/source/xrefs/xrefs.go b/gopls/internal/lsp/source/xrefs/xrefs.go
index 0a8d574..88f76b1 100644
--- a/gopls/internal/lsp/source/xrefs/xrefs.go
+++ b/gopls/internal/lsp/source/xrefs/xrefs.go
@@ -172,8 +172,7 @@
// The gobRef.Range field is the obvious place to begin.
// (The name says gob but in fact we use frob.)
-// var packageCodec = frob.For[[]*gobPackage]()
-var packageCodec = frob.CodecFor117(new([]*gobPackage))
+var packageCodec = frob.CodecFor[[]*gobPackage]()
// A gobPackage records the set of outgoing references from the index
// package to symbols defined in a dependency package.