1
0
mirror of https://github.com/golang/go synced 2024-10-03 19:31:21 -06:00

encoding/gob: better handling of nil pointers

- better message for top-level nil
- nil inside interface yields error, not panic

Fixes #3704.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/6304064
This commit is contained in:
Rob Pike 2012-06-12 00:36:39 -04:00
parent 2a0fdf6ea0
commit ea3c3bb3a8
3 changed files with 57 additions and 1 deletions

View File

@ -426,6 +426,12 @@ func (enc *Encoder) encodeMap(b *bytes.Buffer, mv reflect.Value, keyOp, elemOp e
// by the concrete value. A nil value gets sent as the empty string for the name,
// followed by no value.
func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
// Gobs can encode nil interface values but not typed interface
// values holding nil pointers, since nil pointers point to no value.
elem := iv.Elem()
if elem.Kind() == reflect.Ptr && elem.IsNil() {
errorf("gob: cannot encode nil pointer of type %s inside interface", iv.Elem().Type())
}
state := enc.newEncoderState(b)
state.fieldnum = -1
state.sendZero = true
@ -454,7 +460,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
enc.pushWriter(b)
data := new(bytes.Buffer)
data.Write(spaceForLength)
enc.encode(data, iv.Elem(), ut)
enc.encode(data, elem, ut)
if enc.err != nil {
error_(enc.err)
}

View File

@ -218,6 +218,12 @@ func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) {
// EncodeValue transmits the data item represented by the reflection value,
// guaranteeing that all necessary type information has been transmitted first.
func (enc *Encoder) EncodeValue(value reflect.Value) error {
// Gobs contain values. They cannot represent nil pointers, which
// have no value to encode.
if value.Kind() == reflect.Ptr && value.IsNil() {
panic("gob: cannot encode nil pointer of type " + value.Type().String())
}
// Make sure we're single-threaded through here, so multiple
// goroutines can share an encoder.
enc.mutex.Lock()

View File

@ -736,3 +736,47 @@ func TestPtrToMapOfMap(t *testing.T) {
t.Fatalf("expected %v got %v", data, newData)
}
}
// A top-level nil pointer generates a panic with a helpful string-valued message.
func TestTopLevelNilPointer(t *testing.T) {
errMsg := topLevelNilPanic(t)
if errMsg == "" {
t.Fatal("top-level nil pointer did not panic")
}
if !strings.Contains(errMsg, "nil pointer") {
t.Fatal("expected nil pointer error, got:", errMsg)
}
}
func topLevelNilPanic(t *testing.T) (panicErr string) {
defer func() {
e := recover()
if err, ok := e.(string); ok {
panicErr = err
}
}()
var ip *int
buf := new(bytes.Buffer)
if err := NewEncoder(buf).Encode(ip); err != nil {
t.Fatal("error in encode:", err)
}
return
}
func TestNilPointerInsideInterface(t *testing.T) {
var ip *int
si := struct {
I interface{}
}{
I: ip,
}
buf := new(bytes.Buffer)
err := NewEncoder(buf).Encode(si)
if err == nil {
t.Fatal("expected error, got none")
}
errMsg := err.Error()
if !strings.Contains(errMsg, "nil pointer") || !strings.Contains(errMsg, "interface") {
t.Fatal("expected error about nil pointer and interface, got:", errMsg)
}
}