mirror of
https://github.com/golang/go.git
synced 2024-09-28 22:06:49 +00:00
encoding/gob: ignore chan and func fields of structures
Previously, fields of type chan or func caused an error. Now we just treat them like unexported fields and ignore them. This makes it easier to guarantee long-term compatibilty since a substructure from another package cannot break gob encoding by adding a func or chan field. Fixes #6071 R=golang-dev, rsc CC=golang-dev https://golang.org/cl/13693043
This commit is contained in:
parent
6d68fc8eea
commit
1fba73de45
@ -1009,24 +1009,6 @@ func TestBadRecursiveType(t *testing.T) {
|
||||
// Can't test decode easily because we can't encode one, so we can't pass one to a Decoder.
|
||||
}
|
||||
|
||||
type Bad0 struct {
|
||||
CH chan int
|
||||
C float64
|
||||
}
|
||||
|
||||
func TestInvalidField(t *testing.T) {
|
||||
var bad0 Bad0
|
||||
bad0.CH = make(chan int)
|
||||
b := new(bytes.Buffer)
|
||||
dummyEncoder := new(Encoder) // sufficient for this purpose.
|
||||
dummyEncoder.encode(b, reflect.ValueOf(&bad0), userType(reflect.TypeOf(&bad0)))
|
||||
if err := dummyEncoder.err; err == nil {
|
||||
t.Error("expected error; got none")
|
||||
} else if strings.Index(err.Error(), "type") < 0 {
|
||||
t.Error("expected type error; got", err)
|
||||
}
|
||||
}
|
||||
|
||||
type Indirect struct {
|
||||
A ***[3]int
|
||||
S ***[]int
|
||||
|
@ -74,8 +74,9 @@ slice has capacity the slice will be extended in place; if not, a new array is
|
||||
allocated. Regardless, the length of the resulting slice reports the number of
|
||||
elements decoded.
|
||||
|
||||
Functions and channels cannot be sent in a gob. Attempting to encode a value
|
||||
that contains one will fail.
|
||||
Functions and channels will not be sent in a gob. Attempting to encode such a value
|
||||
at top the level will fail. A struct field of chan or func type is treated exactly
|
||||
like an unexported field and is ignored.
|
||||
|
||||
Gob can encode a value of any type implementing the GobEncoder,
|
||||
encoding.BinaryMarshaler, or encoding.TextMarshaler interfaces by calling the
|
||||
|
@ -686,11 +686,10 @@ func (enc *Encoder) compileEnc(ut *userTypeInfo) *encEngine {
|
||||
if ut.externalEnc != 0 {
|
||||
rt = ut.user
|
||||
}
|
||||
if ut.externalEnc == 0 &&
|
||||
srt.Kind() == reflect.Struct {
|
||||
if ut.externalEnc == 0 && srt.Kind() == reflect.Struct {
|
||||
for fieldNum, wireFieldNum := 0, 0; fieldNum < srt.NumField(); fieldNum++ {
|
||||
f := srt.Field(fieldNum)
|
||||
if !isExported(f.Name) {
|
||||
if !isSent(&f) {
|
||||
continue
|
||||
}
|
||||
op, indir := enc.encOpFor(f.Type, seen)
|
||||
|
@ -6,7 +6,6 @@ package gob
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"sync"
|
||||
@ -54,10 +53,6 @@ func (enc *Encoder) popWriter() {
|
||||
enc.w = enc.w[0 : len(enc.w)-1]
|
||||
}
|
||||
|
||||
func (enc *Encoder) badType(rt reflect.Type) {
|
||||
enc.setError(errors.New("gob: can't encode type " + rt.String()))
|
||||
}
|
||||
|
||||
func (enc *Encoder) setError(err error) {
|
||||
if enc.err == nil { // remember the first.
|
||||
enc.err = err
|
||||
@ -163,8 +158,7 @@ func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Typ
|
||||
// structs must be sent so we know their fields.
|
||||
break
|
||||
case reflect.Chan, reflect.Func:
|
||||
// Probably a bad field in a struct.
|
||||
enc.badType(rt)
|
||||
// If we get here, it's a field of a struct; ignore it.
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ func TestBadData(t *testing.T) {
|
||||
corruptDataCheck("\x03now is the time for all good men", errBadType, t)
|
||||
}
|
||||
|
||||
// Types not supported by the Encoder.
|
||||
// Types not supported at top level by the Encoder.
|
||||
var unsupportedValues = []interface{}{
|
||||
make(chan int),
|
||||
func(a int) bool { return true },
|
||||
@ -662,19 +662,35 @@ func TestSequentialDecoder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Should be able to have unrepresentable fields (chan, func) as long as they
|
||||
// are unexported.
|
||||
// Should be able to have unrepresentable fields (chan, func, *chan etc.); we just ignore them.
|
||||
type Bug2 struct {
|
||||
A int
|
||||
b chan int
|
||||
A int
|
||||
C chan int
|
||||
CP *chan int
|
||||
F func()
|
||||
FPP **func()
|
||||
}
|
||||
|
||||
func TestUnexportedChan(t *testing.T) {
|
||||
b := Bug2{23, make(chan int)}
|
||||
var stream bytes.Buffer
|
||||
enc := NewEncoder(&stream)
|
||||
if err := enc.Encode(b); err != nil {
|
||||
t.Fatalf("error encoding unexported channel: %s", err)
|
||||
func TestChanFuncIgnored(t *testing.T) {
|
||||
c := make(chan int)
|
||||
f := func() {}
|
||||
fp := &f
|
||||
b0 := Bug2{23, c, &c, f, &fp}
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
if err := enc.Encode(b0); err != nil {
|
||||
t.Fatal("error encoding:", err)
|
||||
}
|
||||
var b1 Bug2
|
||||
err := NewDecoder(&buf).Decode(&b1)
|
||||
if err != nil {
|
||||
t.Fatal("decode:", err)
|
||||
}
|
||||
if b1.A != b0.A {
|
||||
t.Fatal("got %d want %d", b1.A, b0.A)
|
||||
}
|
||||
if b1.C != nil || b1.CP != nil || b1.F != nil || b1.FPP != nil {
|
||||
t.Fatal("unexpected value for chan or func")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,7 +526,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, err
|
||||
idToType[st.id()] = st
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
if !isExported(f.Name) {
|
||||
if !isSent(&f) {
|
||||
continue
|
||||
}
|
||||
typ := userType(f.Type).base
|
||||
@ -561,6 +561,25 @@ func isExported(name string) bool {
|
||||
return unicode.IsUpper(rune)
|
||||
}
|
||||
|
||||
// isSent reports whether this struct field is to be transmitted.
|
||||
// It will be transmitted only if it is exported and not a chan or func field
|
||||
// or pointer to chan or func.
|
||||
func isSent(field *reflect.StructField) bool {
|
||||
if !isExported(field.Name) {
|
||||
return false
|
||||
}
|
||||
// If the field is a chan or func or pointer thereto, don't send it.
|
||||
// That is, treat it like an unexported field.
|
||||
typ := field.Type
|
||||
for typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
if typ.Kind() == reflect.Chan || typ.Kind() == reflect.Func {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// getBaseType returns the Gob type describing the given reflect.Type's base type.
|
||||
// typeLock must be held.
|
||||
func getBaseType(name string, rt reflect.Type) (gobType, error) {
|
||||
|
Loading…
Reference in New Issue
Block a user