Commit ea3c3bb3 authored by Rob Pike's avatar Rob Pike

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
parent 2a0fdf6e
...@@ -426,6 +426,12 @@ func (enc *Encoder) encodeMap(b *bytes.Buffer, mv reflect.Value, keyOp, elemOp e ...@@ -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, // by the concrete value. A nil value gets sent as the empty string for the name,
// followed by no value. // followed by no value.
func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.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 := enc.newEncoderState(b)
state.fieldnum = -1 state.fieldnum = -1
state.sendZero = true state.sendZero = true
...@@ -454,7 +460,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) { ...@@ -454,7 +460,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
enc.pushWriter(b) enc.pushWriter(b)
data := new(bytes.Buffer) data := new(bytes.Buffer)
data.Write(spaceForLength) data.Write(spaceForLength)
enc.encode(data, iv.Elem(), ut) enc.encode(data, elem, ut)
if enc.err != nil { if enc.err != nil {
error_(enc.err) error_(enc.err)
} }
......
...@@ -218,6 +218,12 @@ func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) { ...@@ -218,6 +218,12 @@ func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) {
// EncodeValue transmits the data item represented by the reflection value, // EncodeValue transmits the data item represented by the reflection value,
// guaranteeing that all necessary type information has been transmitted first. // guaranteeing that all necessary type information has been transmitted first.
func (enc *Encoder) EncodeValue(value reflect.Value) error { 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 // Make sure we're single-threaded through here, so multiple
// goroutines can share an encoder. // goroutines can share an encoder.
enc.mutex.Lock() enc.mutex.Lock()
......
...@@ -736,3 +736,47 @@ func TestPtrToMapOfMap(t *testing.T) { ...@@ -736,3 +736,47 @@ func TestPtrToMapOfMap(t *testing.T) {
t.Fatalf("expected %v got %v", data, newData) 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)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment