aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/json/encode.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/encoding/json/encode.go')
-rw-r--r--src/encoding/json/encode.go31
1 files changed, 30 insertions, 1 deletions
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index 578d551102..483b9d8f2d 100644
--- a/src/encoding/json/encode.go
+++ b/src/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.