]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1] encoding/json: fix panic unmarshaling into non-nil interface...
authorRuss Cox <rsc@golang.org>
Wed, 13 Jun 2012 20:24:48 +0000 (16:24 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 13 Jun 2012 20:24:48 +0000 (16:24 -0400)
««« backport bee83c1509a3
encoding/json: fix panic unmarshaling into non-nil interface value

Fixes #3614.

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/6306051

»»»

src/pkg/encoding/json/decode.go
src/pkg/encoding/json/decode_test.go

index dcfdeec3c5a5a07c833d40213c90ae3669bdca8a..d61f8870646cb32c58e430af9eed3e96aa965553 100644 (file)
@@ -273,9 +273,14 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
                        _, isUnmarshaler = v.Interface().(Unmarshaler)
                }
 
+               // Load value from interface, but only if the result will be
+               // usefully addressable.
                if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() {
-                       v = iv.Elem()
-                       continue
+                       e := iv.Elem()
+                       if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
+                               v = e
+                               continue
+                       }
                }
 
                pv := v
index 2a7549cb689a3ebb76af1544aa4701267994070b..6fac22c4a35747fc1ee2ffb5aec4853a726cbcad 100644 (file)
@@ -657,3 +657,49 @@ func TestEmptyString(t *testing.T) {
                t.Fatal("Decode: did not set Number1")
        }
 }
+
+func intp(x int) *int {
+       p := new(int)
+       *p = x
+       return p
+}
+
+func intpp(x *int) **int {
+       pp := new(*int)
+       *pp = x
+       return pp
+}
+
+var interfaceSetTests = []struct {
+       pre  interface{}
+       json string
+       post interface{}
+}{
+       {"foo", `"bar"`, "bar"},
+       {"foo", `2`, 2.0},
+       {"foo", `true`, true},
+       {"foo", `null`, nil},
+
+       {nil, `null`, nil},
+       {new(int), `null`, nil},
+       {(*int)(nil), `null`, nil},
+       {new(*int), `null`, new(*int)},
+       {(**int)(nil), `null`, nil},
+       {intp(1), `null`, nil},
+       {intpp(nil), `null`, intpp(nil)},
+       {intpp(intp(1)), `null`, intpp(nil)},
+}
+
+func TestInterfaceSet(t *testing.T) {
+       for _, tt := range interfaceSetTests {
+               b := struct{ X interface{} }{tt.pre}
+               blob := `{"X":` + tt.json + `}`
+               if err := Unmarshal([]byte(blob), &b); err != nil {
+                       t.Errorf("Unmarshal %#q: %v", blob, err)
+                       continue
+               }
+               if !reflect.DeepEqual(b.X, tt.post) {
+                       t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post)
+               }
+       }
+}