diff options
Diffstat (limited to 'libgo/go/encoding/json')
-rw-r--r-- | libgo/go/encoding/json/bench_test.go | 8 | ||||
-rw-r--r-- | libgo/go/encoding/json/encode.go | 31 | ||||
-rw-r--r-- | libgo/go/encoding/json/encode_test.go | 27 | ||||
-rw-r--r-- | libgo/go/encoding/json/stream_test.go | 5 | ||||
-rw-r--r-- | libgo/go/encoding/json/tagkey_test.go | 4 |
5 files changed, 64 insertions, 11 deletions
diff --git a/libgo/go/encoding/json/bench_test.go b/libgo/go/encoding/json/bench_test.go index 4a5fe7e..73c7b09 100644 --- a/libgo/go/encoding/json/bench_test.go +++ b/libgo/go/encoding/json/bench_test.go @@ -15,7 +15,7 @@ import ( "compress/gzip" "fmt" "internal/testenv" - "io/ioutil" + "io" "os" "reflect" "runtime" @@ -52,7 +52,7 @@ func codeInit() { if err != nil { panic(err) } - data, err := ioutil.ReadAll(gz) + data, err := io.ReadAll(gz) if err != nil { panic(err) } @@ -89,7 +89,7 @@ func BenchmarkCodeEncoder(b *testing.B) { b.StartTimer() } b.RunParallel(func(pb *testing.PB) { - enc := NewEncoder(ioutil.Discard) + enc := NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&codeStruct); err != nil { b.Fatal("Encode:", err) @@ -399,7 +399,7 @@ func BenchmarkEncodeMarshaler(b *testing.B) { }{} b.RunParallel(func(pb *testing.PB) { - enc := NewEncoder(ioutil.Discard) + enc := NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&m); err != nil { diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index 578d551..483b9d8 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -236,6 +236,8 @@ func (e *UnsupportedTypeError) Error() string { return "json: unsupported type: " + e.Type.String() } +// An UnsupportedValueError is returned by Marshal when attempting +// to encode an unsupported value. type UnsupportedValueError struct { Value reflect.Value Str string @@ -779,6 +781,16 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { e.WriteString("null") return } + if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter { + // We're a large number of nested ptrEncoder.encode calls deep; + // start checking if we've run into a pointer cycle. + ptr := v.Pointer() + if _, ok := e.ptrSeen[ptr]; ok { + e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())}) + } + e.ptrSeen[ptr] = struct{}{} + defer delete(e.ptrSeen, ptr) + } e.WriteByte('{') // Extract and sort the keys. @@ -801,6 +813,7 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { me.elemEnc(e, v.MapIndex(kv.v), opts) } e.WriteByte('}') + e.ptrLevel-- } func newMapEncoder(t reflect.Type) encoderFunc { @@ -857,7 +870,23 @@ func (se sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { e.WriteString("null") return } + if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter { + // We're a large number of nested ptrEncoder.encode calls deep; + // start checking if we've run into a pointer cycle. + // Here we use a struct to memorize the pointer to the first element of the slice + // and its length. + ptr := struct { + ptr uintptr + len int + }{v.Pointer(), v.Len()} + if _, ok := e.ptrSeen[ptr]; ok { + e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())}) + } + e.ptrSeen[ptr] = struct{}{} + defer delete(e.ptrSeen, ptr) + } se.arrayEnc(e, v, opts) + e.ptrLevel-- } func newSliceEncoder(t reflect.Type) encoderFunc { @@ -946,7 +975,7 @@ func isValidTag(s string) bool { } for _, c := range s { switch { - case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): + case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c): // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. diff --git a/libgo/go/encoding/json/encode_test.go b/libgo/go/encoding/json/encode_test.go index 7290eca..42bb09d 100644 --- a/libgo/go/encoding/json/encode_test.go +++ b/libgo/go/encoding/json/encode_test.go @@ -183,7 +183,15 @@ type PointerCycleIndirect struct { Ptrs []interface{} } -var pointerCycleIndirect = &PointerCycleIndirect{} +type RecursiveSlice []RecursiveSlice + +var ( + pointerCycleIndirect = &PointerCycleIndirect{} + mapCycle = make(map[string]interface{}) + sliceCycle = []interface{}{nil} + sliceNoCycle = []interface{}{nil, nil} + recursiveSliceCycle = []RecursiveSlice{nil} +) func init() { ptr := &SamePointerNoCycle{} @@ -192,6 +200,14 @@ func init() { pointerCycle.Ptr = pointerCycle pointerCycleIndirect.Ptrs = []interface{}{pointerCycleIndirect} + + mapCycle["x"] = mapCycle + sliceCycle[0] = sliceCycle + sliceNoCycle[1] = sliceNoCycle[:1] + for i := startDetectingCyclesAfter; i > 0; i-- { + sliceNoCycle = []interface{}{sliceNoCycle} + } + recursiveSliceCycle[0] = recursiveSliceCycle } func TestSamePointerNoCycle(t *testing.T) { @@ -200,12 +216,21 @@ func TestSamePointerNoCycle(t *testing.T) { } } +func TestSliceNoCycle(t *testing.T) { + if _, err := Marshal(sliceNoCycle); err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + var unsupportedValues = []interface{}{ math.NaN(), math.Inf(-1), math.Inf(1), pointerCycle, pointerCycleIndirect, + mapCycle, + sliceCycle, + recursiveSliceCycle, } func TestUnsupportedValues(t *testing.T) { diff --git a/libgo/go/encoding/json/stream_test.go b/libgo/go/encoding/json/stream_test.go index c9e5334..c284f2d 100644 --- a/libgo/go/encoding/json/stream_test.go +++ b/libgo/go/encoding/json/stream_test.go @@ -7,7 +7,6 @@ package json import ( "bytes" "io" - "io/ioutil" "log" "net" "net/http" @@ -215,7 +214,7 @@ func TestDecoderBuffered(t *testing.T) { if m.Name != "Gopher" { t.Errorf("Name = %q; want Gopher", m.Name) } - rest, err := ioutil.ReadAll(d.Buffered()) + rest, err := io.ReadAll(d.Buffered()) if err != nil { t.Fatal(err) } @@ -318,7 +317,7 @@ func BenchmarkEncoderEncode(b *testing.B) { v := &T{"foo", "bar"} b.RunParallel(func(pb *testing.PB) { for pb.Next() { - if err := NewEncoder(ioutil.Discard).Encode(v); err != nil { + if err := NewEncoder(io.Discard).Encode(v); err != nil { b.Fatal(err) } } diff --git a/libgo/go/encoding/json/tagkey_test.go b/libgo/go/encoding/json/tagkey_test.go index f77c49c..bbb4e6a 100644 --- a/libgo/go/encoding/json/tagkey_test.go +++ b/libgo/go/encoding/json/tagkey_test.go @@ -41,7 +41,7 @@ type percentSlashTag struct { } type punctuationTag struct { - V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // https://golang.org/issue/3546 + V string `json:"!#$%&()*+-./:;<=>?@[]^_{|}~ "` // https://golang.org/issue/3546 } type dashTag struct { @@ -90,7 +90,7 @@ var structTagObjectKeyTests = []struct { {badFormatTag{"Orfevre"}, "Orfevre", "Y"}, {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"}, {percentSlashTag{"brut"}, "brut", "text/html%"}, - {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"}, + {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:;<=>?@[]^_{|}~ "}, {spaceTag{"Perreddu"}, "Perreddu", "With space"}, {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}, } |