]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/gob: better handling of nil pointers
authorRob Pike <r@golang.org>
Tue, 12 Jun 2012 04:36:39 +0000 (00:36 -0400)
committerRob Pike <r@golang.org>
Tue, 12 Jun 2012 04:36:39 +0000 (00:36 -0400)
- 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

src/pkg/encoding/gob/encode.go
src/pkg/encoding/gob/encoder.go
src/pkg/encoding/gob/encoder_test.go

index 168e08b137a435dac52186cd37968d7bf90d2197..764351db6d427d0d085c4238d3850ef584642150 100644 (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)
        }
index a15b5a1f9a1ee20abbb6e3d8b7eb2d05e453250d..51444bb5269f2dc177702a138f89dd1bd1011242 100644 (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()
index c4947cbb8d312da0bc98cfc39144d1725d59967a..c3c87dc4f1a6c5fcfb847641915c543c5ac0d5e7 100644 (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)
+       }
+}