aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/encoding/json
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/encoding/json')
-rw-r--r--libgo/go/encoding/json/bench_test.go8
-rw-r--r--libgo/go/encoding/json/encode.go31
-rw-r--r--libgo/go/encoding/json/encode_test.go27
-rw-r--r--libgo/go/encoding/json/stream_test.go5
-rw-r--r--libgo/go/encoding/json/tagkey_test.go4
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", "Ελλάδα"},
}