diff options
| author | Filippo Valsorda <filippo@golang.org> | 2018-09-06 13:25:27 -0400 |
|---|---|---|
| committer | Filippo Valsorda <filippo@golang.org> | 2018-09-06 13:25:27 -0400 |
| commit | 4d1aa482b8754c081d8f3f6b39fe61dd2e6504fc (patch) | |
| tree | b12a76ad02035d1206d09a7aa14a6cda0c411c3c /src/encoding/json | |
| parent | 7eb1677c01c3decc510270d532ed69d0bf42bffa (diff) | |
| parent | 3e5b5d69dcdb82494f550049986426d84dd6b8f8 (diff) | |
| download | go-4d1aa482b8754c081d8f3f6b39fe61dd2e6504fc.tar.xz | |
[dev.boringcrypto] all: merge master into dev.boringcrypto
Change-Id: Ia8ddd4e52dcfe87f9daef2edd37c8155fcae7f5a
Diffstat (limited to 'src/encoding/json')
| -rw-r--r-- | src/encoding/json/bench_test.go | 28 | ||||
| -rw-r--r-- | src/encoding/json/decode.go | 57 | ||||
| -rw-r--r-- | src/encoding/json/decode_test.go | 30 | ||||
| -rw-r--r-- | src/encoding/json/encode.go | 164 | ||||
| -rw-r--r-- | src/encoding/json/encode_test.go | 28 | ||||
| -rw-r--r-- | src/encoding/json/stream.go | 24 | ||||
| -rw-r--r-- | src/encoding/json/stream_test.go | 34 |
7 files changed, 235 insertions, 130 deletions
diff --git a/src/encoding/json/bench_test.go b/src/encoding/json/bench_test.go index bd322db2e6..72cb349062 100644 --- a/src/encoding/json/bench_test.go +++ b/src/encoding/json/bench_test.go @@ -114,6 +114,34 @@ func BenchmarkCodeMarshal(b *testing.B) { b.SetBytes(int64(len(codeJSON))) } +func benchMarshalBytes(n int) func(*testing.B) { + sample := []byte("hello world") + // Use a struct pointer, to avoid an allocation when passing it as an + // interface parameter to Marshal. + v := &struct { + Bytes []byte + }{ + bytes.Repeat(sample, (n/len(sample))+1)[:n], + } + return func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := Marshal(v); err != nil { + b.Fatal("Marshal:", err) + } + } + } +} + +func BenchmarkMarshalBytes(b *testing.B) { + // 32 fits within encodeState.scratch. + b.Run("32", benchMarshalBytes(32)) + // 256 doesn't fit in encodeState.scratch, but is small enough to + // allocate and avoid the slower base64.NewEncoder. + b.Run("256", benchMarshalBytes(256)) + // 4096 is large enough that we want to avoid allocating for it. + b.Run("4096", benchMarshalBytes(4096)) +} + func BenchmarkCodeDecoder(b *testing.B) { if codeJSON == nil { b.StopTimer() diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 0b29249218..82dc78083a 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -179,7 +179,7 @@ func (d *decodeState) unmarshal(v interface{}) error { // test must be applied at the top level of the value. err := d.value(rv) if err != nil { - return err + return d.addErrorContext(err) } return d.savedError } @@ -267,7 +267,7 @@ type decodeState struct { opcode int // last read result scan scanner errorContext struct { // provides context for type errors - Struct string + Struct reflect.Type Field string } savedError error @@ -289,7 +289,7 @@ func (d *decodeState) init(data []byte) *decodeState { d.data = data d.off = 0 d.savedError = nil - d.errorContext.Struct = "" + d.errorContext.Struct = nil d.errorContext.Field = "" return d } @@ -304,10 +304,10 @@ func (d *decodeState) saveError(err error) { // addErrorContext returns a new error enhanced with information from d.errorContext func (d *decodeState) addErrorContext(err error) error { - if d.errorContext.Struct != "" || d.errorContext.Field != "" { + if d.errorContext.Struct != nil || d.errorContext.Field != "" { switch err := err.(type) { case *UnmarshalTypeError: - err.Struct = d.errorContext.Struct + err.Struct = d.errorContext.Struct.Name() err.Field = d.errorContext.Field return err } @@ -332,13 +332,12 @@ func (d *decodeState) skip() { // scanNext processes the byte at d.data[d.off]. func (d *decodeState) scanNext() { - s, data, i := &d.scan, d.data, d.off - if i < len(data) { - d.opcode = s.step(s, data[i]) - d.off = i + 1 + if d.off < len(d.data) { + d.opcode = d.scan.step(&d.scan, d.data[d.off]) + d.off++ } else { - d.opcode = s.eof() - d.off = len(data) + 1 // mark processed EOF with len+1 + d.opcode = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 } } @@ -346,7 +345,7 @@ func (d *decodeState) scanNext() { // receives a scan code not equal to op. func (d *decodeState) scanWhile(op int) { s, data, i := &d.scan, d.data, d.off - for i < len(d.data) { + for i < len(data) { newOp := s.step(s, data[i]) i++ if newOp != op { @@ -356,7 +355,7 @@ func (d *decodeState) scanWhile(op int) { } } - d.off = len(d.data) + 1 // mark processed EOF with len+1 + d.off = len(data) + 1 // mark processed EOF with len+1 d.opcode = d.scan.eof() } @@ -413,11 +412,7 @@ func (d *decodeState) valueQuoted() (interface{}, error) { default: return nil, errPhase - case scanBeginArray: - d.skip() - d.scanNext() - - case scanBeginObject: + case scanBeginArray, scanBeginObject: d.skip() d.scanNext() @@ -611,7 +606,7 @@ func (d *decodeState) array(v reflect.Value) error { } var nullLiteral = []byte("null") -var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() +var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() // object consumes an object from d.data[d.off-1:], decoding into v. // The first byte ('{') of the object has been read already. @@ -629,6 +624,7 @@ func (d *decodeState) object(v reflect.Value) error { return nil } v = pv + t := v.Type() // Decoding into nil interface? Switch to non-reflect code. if v.Kind() == reflect.Interface && v.NumMethod() == 0 { @@ -640,6 +636,8 @@ func (d *decodeState) object(v reflect.Value) error { return nil } + var fields []field + // Check type of target: // struct or // map[T1]T2 where T1 is string, an integer type, @@ -648,14 +646,13 @@ func (d *decodeState) object(v reflect.Value) error { case reflect.Map: // Map key must either have string kind, have an integer kind, // or be an encoding.TextUnmarshaler. - t := v.Type() switch t.Key().Kind() { case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: default: if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { - d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)}) d.skip() return nil } @@ -664,14 +661,16 @@ func (d *decodeState) object(v reflect.Value) error { v.Set(reflect.MakeMap(t)) } case reflect.Struct: + fields = cachedTypeFields(t) // ok default: - d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)}) d.skip() return nil } var mapElem reflect.Value + originalErrorContext := d.errorContext for { // Read opening " of string key or closing }. @@ -698,7 +697,7 @@ func (d *decodeState) object(v reflect.Value) error { destring := false // whether the value is wrapped in a string to be decoded first if v.Kind() == reflect.Map { - elemType := v.Type().Elem() + elemType := t.Elem() if !mapElem.IsValid() { mapElem = reflect.New(elemType).Elem() } else { @@ -707,7 +706,6 @@ func (d *decodeState) object(v reflect.Value) error { subv = mapElem } else { var f *field - fields := cachedTypeFields(v.Type()) for i := range fields { ff := &fields[i] if bytes.Equal(ff.nameBytes, key) { @@ -744,7 +742,7 @@ func (d *decodeState) object(v reflect.Value) error { subv = subv.Field(i) } d.errorContext.Field = f.name - d.errorContext.Struct = v.Type().Name() + d.errorContext.Struct = t } else if d.disallowUnknownFields { d.saveError(fmt.Errorf("json: unknown field %q", key)) } @@ -785,13 +783,13 @@ func (d *decodeState) object(v reflect.Value) error { // Write value back to map; // if using struct, subv points into struct already. if v.Kind() == reflect.Map { - kt := v.Type().Key() + kt := t.Key() var kv reflect.Value switch { case kt.Kind() == reflect.String: kv = reflect.ValueOf(key).Convert(kt) case reflect.PtrTo(kt).Implements(textUnmarshalerType): - kv = reflect.New(v.Type().Key()) + kv = reflect.New(kt) if err := d.literalStore(item, kv, true); err != nil { return err } @@ -832,8 +830,7 @@ func (d *decodeState) object(v reflect.Value) error { return errPhase } - d.errorContext.Struct = "" - d.errorContext.Field = "" + d.errorContext = originalErrorContext } return nil } @@ -991,7 +988,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if fromQuoted { return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) } - return &UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())} + d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())}) case reflect.Interface: n, err := d.convertNumber(s) if err != nil { diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go index ab83b81bb3..defa97e40f 100644 --- a/src/encoding/json/decode_test.go +++ b/src/encoding/json/decode_test.go @@ -41,6 +41,16 @@ type VOuter struct { V V } +type W struct { + S SS +} + +type SS string + +func (*SS) UnmarshalJSON(data []byte) error { + return &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(SS(""))} +} + // ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and // without UseNumber var ifaceNumAsFloat64 = map[string]interface{}{ @@ -142,7 +152,7 @@ var ( umstructXY = ustructText{unmarshalerText{"x", "y"}} ummapType = map[unmarshalerText]bool{} - ummapXY = map[unmarshalerText]bool{unmarshalerText{"x", "y"}: true} + ummapXY = map[unmarshalerText]bool{{"x", "y"}: true} ) // Test data structures for anonymous fields. @@ -371,6 +381,10 @@ func (b *intWithPtrMarshalText) UnmarshalText(data []byte) error { return (*intWithMarshalText)(b).UnmarshalText(data) } +type mapStringToStringData struct { + Data map[string]string `json:"data"` +} + type unmarshalTest struct { in string ptr interface{} @@ -401,8 +415,10 @@ var unmarshalTests = []unmarshalTest{ {in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"}, {in: "null", ptr: new(interface{}), out: nil}, {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7, "T", "X"}}, + {in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeOf(""), 8, "T", "X"}}, {in: `{"x": 1}`, ptr: new(tx), out: tx{}}, {in: `{"x": 1}`, ptr: new(tx), out: tx{}}, {in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true}, + {in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeOf(SS("")), 0, "W", "S"}}, {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}}, {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true}, {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64}, @@ -866,6 +882,18 @@ var unmarshalTests = []unmarshalTest{ err: fmt.Errorf("json: unknown field \"extra\""), disallowUnknownFields: true, }, + // issue 26444 + // UnmarshalTypeError without field & struct values + { + in: `{"data":{"test1": "bob", "test2": 123}}`, + ptr: new(mapStringToStringData), + err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), Offset: 37, Struct: "mapStringToStringData", Field: "data"}, + }, + { + in: `{"data":{"test1": 123, "test2": "bob"}}`, + ptr: new(mapStringToStringData), + err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), Offset: 21, Struct: "mapStringToStringData", Field: "data"}, + }, } func TestMarshal(t *testing.T) { diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index 28ca5fe9e0..f10124e67d 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -381,8 +381,8 @@ func typeEncoder(t reflect.Type) encoderFunc { } var ( - marshalerType = reflect.TypeOf(new(Marshaler)).Elem() - textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() + marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() + textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() ) // newTypeEncoder constructs an encoderFunc for a type. @@ -624,40 +624,49 @@ func unsupportedTypeEncoder(e *encodeState, v reflect.Value, _ encOpts) { } type structEncoder struct { - fields []field - fieldEncs []encoderFunc + fields []field } -func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { - e.WriteByte('{') - first := true - for i, f := range se.fields { - fv := fieldByIndex(v, f.index) - if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) { +func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { + next := byte('{') +FieldLoop: + for i := range se.fields { + f := &se.fields[i] + + // Find the nested struct field by following f.index. + fv := v + for _, i := range f.index { + if fv.Kind() == reflect.Ptr { + if fv.IsNil() { + continue FieldLoop + } + fv = fv.Elem() + } + fv = fv.Field(i) + } + + if f.omitEmpty && isEmptyValue(fv) { continue } - if first { - first = false + e.WriteByte(next) + next = ',' + if opts.escapeHTML { + e.WriteString(f.nameEscHTML) } else { - e.WriteByte(',') + e.WriteString(f.nameNonEsc) } - e.string(f.name, opts.escapeHTML) - e.WriteByte(':') opts.quoted = f.quoted - se.fieldEncs[i](e, fv, opts) + f.encoder(e, fv, opts) + } + if next == '{' { + e.WriteString("{}") + } else { + e.WriteByte('}') } - e.WriteByte('}') } func newStructEncoder(t reflect.Type) encoderFunc { - fields := cachedTypeFields(t) - se := &structEncoder{ - fields: fields, - fieldEncs: make([]encoderFunc, len(fields)), - } - for i, f := range fields { - se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index)) - } + se := structEncoder{fields: cachedTypeFields(t)} return se.encode } @@ -665,7 +674,7 @@ type mapEncoder struct { elemEnc encoderFunc } -func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { if v.IsNil() { e.WriteString("null") return @@ -704,7 +713,7 @@ func newMapEncoder(t reflect.Type) encoderFunc { return unsupportedTypeEncoder } } - me := &mapEncoder{typeEncoder(t.Elem())} + me := mapEncoder{typeEncoder(t.Elem())} return me.encode } @@ -715,14 +724,22 @@ func encodeByteSlice(e *encodeState, v reflect.Value, _ encOpts) { } s := v.Bytes() e.WriteByte('"') - if len(s) < 1024 { - // for small buffers, using Encode directly is much faster. - dst := make([]byte, base64.StdEncoding.EncodedLen(len(s))) + encodedLen := base64.StdEncoding.EncodedLen(len(s)) + if encodedLen <= len(e.scratch) { + // If the encoded bytes fit in e.scratch, avoid an extra + // allocation and use the cheaper Encoding.Encode. + dst := e.scratch[:encodedLen] + base64.StdEncoding.Encode(dst, s) + e.Write(dst) + } else if encodedLen <= 1024 { + // The encoded bytes are short enough to allocate for, and + // Encoding.Encode is still cheaper. + dst := make([]byte, encodedLen) base64.StdEncoding.Encode(dst, s) e.Write(dst) } else { - // for large buffers, avoid unnecessary extra temporary - // buffer space. + // The encoded bytes are too long to cheaply allocate, and + // Encoding.Encode is no longer noticeably cheaper. enc := base64.NewEncoder(base64.StdEncoding, e) enc.Write(s) enc.Close() @@ -735,7 +752,7 @@ type sliceEncoder struct { arrayEnc encoderFunc } -func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (se sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { if v.IsNil() { e.WriteString("null") return @@ -751,7 +768,7 @@ func newSliceEncoder(t reflect.Type) encoderFunc { return encodeByteSlice } } - enc := &sliceEncoder{newArrayEncoder(t)} + enc := sliceEncoder{newArrayEncoder(t)} return enc.encode } @@ -759,7 +776,7 @@ type arrayEncoder struct { elemEnc encoderFunc } -func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (ae arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { e.WriteByte('[') n := v.Len() for i := 0; i < n; i++ { @@ -772,7 +789,7 @@ func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { } func newArrayEncoder(t reflect.Type) encoderFunc { - enc := &arrayEncoder{typeEncoder(t.Elem())} + enc := arrayEncoder{typeEncoder(t.Elem())} return enc.encode } @@ -780,7 +797,7 @@ type ptrEncoder struct { elemEnc encoderFunc } -func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (pe ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { if v.IsNil() { e.WriteString("null") return @@ -789,7 +806,7 @@ func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { } func newPtrEncoder(t reflect.Type) encoderFunc { - enc := &ptrEncoder{typeEncoder(t.Elem())} + enc := ptrEncoder{typeEncoder(t.Elem())} return enc.encode } @@ -797,7 +814,7 @@ type condAddrEncoder struct { canAddrEnc, elseEnc encoderFunc } -func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (ce condAddrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { if v.CanAddr() { ce.canAddrEnc(e, v, opts) } else { @@ -808,7 +825,7 @@ func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) // newCondAddrEncoder returns an encoder that checks whether its value // CanAddr and delegates to canAddrEnc if so, else to elseEnc. func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc { - enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc} + enc := condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc} return enc.encode } @@ -822,28 +839,13 @@ func isValidTag(s string) bool { // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. - default: - if !unicode.IsLetter(c) && !unicode.IsDigit(c) { - return false - } + case !unicode.IsLetter(c) && !unicode.IsDigit(c): + return false } } return true } -func fieldByIndex(v reflect.Value, index []int) reflect.Value { - for _, i := range index { - if v.Kind() == reflect.Ptr { - if v.IsNil() { - return reflect.Value{} - } - v = v.Elem() - } - v = v.Field(i) - } - return v -} - func typeByIndex(t reflect.Type, index []int) reflect.Type { for _, i := range index { if t.Kind() == reflect.Ptr { @@ -893,18 +895,15 @@ func (e *encodeState) string(s string, escapeHTML bool) { if start < i { e.WriteString(s[start:i]) } + e.WriteByte('\\') switch b { case '\\', '"': - e.WriteByte('\\') e.WriteByte(b) case '\n': - e.WriteByte('\\') e.WriteByte('n') case '\r': - e.WriteByte('\\') e.WriteByte('r') case '\t': - e.WriteByte('\\') e.WriteByte('t') default: // This encodes bytes < 0x20 except for \t, \n and \r. @@ -912,7 +911,7 @@ func (e *encodeState) string(s string, escapeHTML bool) { // because they can lead to security holes when // user-controlled strings are rendered into JSON // and served to some browsers. - e.WriteString(`\u00`) + e.WriteString(`u00`) e.WriteByte(hex[b>>4]) e.WriteByte(hex[b&0xF]) } @@ -968,18 +967,15 @@ func (e *encodeState) stringBytes(s []byte, escapeHTML bool) { if start < i { e.Write(s[start:i]) } + e.WriteByte('\\') switch b { case '\\', '"': - e.WriteByte('\\') e.WriteByte(b) case '\n': - e.WriteByte('\\') e.WriteByte('n') case '\r': - e.WriteByte('\\') e.WriteByte('r') case '\t': - e.WriteByte('\\') e.WriteByte('t') default: // This encodes bytes < 0x20 except for \t, \n and \r. @@ -987,7 +983,7 @@ func (e *encodeState) stringBytes(s []byte, escapeHTML bool) { // because they can lead to security holes when // user-controlled strings are rendered into JSON // and served to some browsers. - e.WriteString(`\u00`) + e.WriteString(`u00`) e.WriteByte(hex[b>>4]) e.WriteByte(hex[b&0xF]) } @@ -1036,17 +1032,16 @@ type field struct { nameBytes []byte // []byte(name) equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent + nameNonEsc string // `"` + name + `":` + nameEscHTML string // `"` + HTMLEscape(name) + `":` + tag bool index []int typ reflect.Type omitEmpty bool quoted bool -} -func fillField(f field) field { - f.nameBytes = []byte(f.name) - f.equalFold = foldFunc(f.nameBytes) - return f + encoder encoderFunc } // byIndex sorts field by index sequence. @@ -1086,6 +1081,9 @@ func typeFields(t reflect.Type) []field { // Fields found. var fields []field + // Buffer to run HTMLEscape on field names. + var nameEscBuf bytes.Buffer + for len(next) > 0 { current, next = next, current[:0] count, nextCount = nextCount, map[reflect.Type]int{} @@ -1152,14 +1150,26 @@ func typeFields(t reflect.Type) []field { if name == "" { name = sf.Name } - fields = append(fields, fillField(field{ + field := field{ name: name, tag: tagged, index: index, typ: ft, omitEmpty: opts.Contains("omitempty"), quoted: quoted, - })) + } + field.nameBytes = []byte(field.name) + field.equalFold = foldFunc(field.nameBytes) + + // Build nameEscHTML and nameNonEsc ahead of time. + nameEscBuf.Reset() + nameEscBuf.WriteString(`"`) + HTMLEscape(&nameEscBuf, field.nameBytes) + nameEscBuf.WriteString(`":`) + field.nameEscHTML = nameEscBuf.String() + field.nameNonEsc = `"` + field.name + `":` + + fields = append(fields, field) if count[f.typ] > 1 { // If there were multiple instances, add a second, // so that the annihilation code will see a duplicate. @@ -1173,7 +1183,7 @@ func typeFields(t reflect.Type) []field { // Record new anonymous struct to explore in next round. nextCount[ft]++ if nextCount[ft] == 1 { - next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft})) + next = append(next, field{name: ft.Name(), index: index, typ: ft}) } } } @@ -1227,6 +1237,10 @@ func typeFields(t reflect.Type) []field { fields = out sort.Sort(byIndex(fields)) + for i := range fields { + f := &fields[i] + f.encoder = typeEncoder(typeByIndex(t, f.index)) + } return fields } diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go index b90483cf35..cd5eadf3c1 100644 --- a/src/encoding/json/encode_test.go +++ b/src/encoding/json/encode_test.go @@ -405,6 +405,19 @@ func TestAnonymousFields(t *testing.T) { return S{s1{1, 2, s2{3, 4}}, 6} }, want: `{"MyInt1":1,"MyInt2":3}`, + }, { + // If an anonymous struct pointer field is nil, we should ignore + // the embedded fields behind it. Not properly doing so may + // result in the wrong output or reflect panics. + label: "EmbeddedFieldBehindNilPointer", + makeInput: func() interface{} { + type ( + S2 struct{ Field string } + S struct{ *S2 } + ) + return S{} + }, + want: `{}`, }} for _, tt := range tests { @@ -995,3 +1008,18 @@ func TestMarshalPanic(t *testing.T) { Marshal(&marshalPanic{}) t.Error("Marshal should have panicked") } + +func TestMarshalUncommonFieldNames(t *testing.T) { + v := struct { + A0, À, Aβ int + }{} + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"A0":0,"À":0,"Aβ":0}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } +} diff --git a/src/encoding/json/stream.go b/src/encoding/json/stream.go index 75a4270df7..7d5137fbc7 100644 --- a/src/encoding/json/stream.go +++ b/src/encoding/json/stream.go @@ -96,19 +96,19 @@ Input: // Look in the buffer for a new value. for i, c := range dec.buf[scanp:] { dec.scan.bytes++ - v := dec.scan.step(&dec.scan, c) - if v == scanEnd { + switch dec.scan.step(&dec.scan, c) { + case scanEnd: scanp += i break Input - } - // scanEnd is delayed one byte. - // We might block trying to get that byte from src, - // so instead invent a space byte. - if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd { - scanp += i + 1 - break Input - } - if v == scanError { + case scanEndObject, scanEndArray: + // scanEnd is delayed one byte. + // We might block trying to get that byte from src, + // so instead invent a space byte. + if stateEndValue(&dec.scan, ' ') == scanEnd { + scanp += i + 1 + break Input + } + case scanError: dec.err = dec.scan.err return 0, dec.scan.err } @@ -471,7 +471,7 @@ func (dec *Decoder) tokenError(c byte) (Token, error) { case tokenObjectComma: context = " after object key:value pair" } - return nil, &SyntaxError{"invalid character " + quoteChar(c) + " " + context, dec.offset()} + return nil, &SyntaxError{"invalid character " + quoteChar(c) + context, dec.offset()} } // More reports whether there is another element in the diff --git a/src/encoding/json/stream_test.go b/src/encoding/json/stream_test.go index 83c01d170c..aaf32e0a24 100644 --- a/src/encoding/json/stream_test.go +++ b/src/encoding/json/stream_test.go @@ -93,6 +93,10 @@ func TestEncoderIndent(t *testing.T) { func TestEncoderSetEscapeHTML(t *testing.T) { var c C var ct CText + var tagStruct struct { + Valid int `json:"<>&#! "` + Invalid int `json:"\\"` + } for _, tt := range []struct { name string v interface{} @@ -102,6 +106,11 @@ func TestEncoderSetEscapeHTML(t *testing.T) { {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`}, {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`}, {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`}, + { + "tagStruct", tagStruct, + `{"\u003c\u003e\u0026#! ":0,"Invalid":0}`, + `{"<>&#! ":0,"Invalid":0}`, + }, } { var buf bytes.Buffer enc := NewEncoder(&buf) @@ -192,10 +201,9 @@ func nlines(s string, n int) string { } func TestRawMessage(t *testing.T) { - // TODO(rsc): Should not need the * in *RawMessage var data struct { X float64 - Id *RawMessage + Id RawMessage Y float32 } const raw = `["\u0056",null]` @@ -204,8 +212,8 @@ func TestRawMessage(t *testing.T) { if err != nil { t.Fatalf("Unmarshal: %v", err) } - if string([]byte(*data.Id)) != raw { - t.Fatalf("Raw mismatch: have %#q want %#q", []byte(*data.Id), raw) + if string([]byte(data.Id)) != raw { + t.Fatalf("Raw mismatch: have %#q want %#q", []byte(data.Id), raw) } b, err := Marshal(&data) if err != nil { @@ -217,20 +225,22 @@ func TestRawMessage(t *testing.T) { } func TestNullRawMessage(t *testing.T) { - // TODO(rsc): Should not need the * in *RawMessage var data struct { - X float64 - Id *RawMessage - Y float32 + X float64 + Id RawMessage + IdPtr *RawMessage + Y float32 } - data.Id = new(RawMessage) - const msg = `{"X":0.1,"Id":null,"Y":0.2}` + const msg = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}` err := Unmarshal([]byte(msg), &data) if err != nil { t.Fatalf("Unmarshal: %v", err) } - if data.Id != nil { - t.Fatalf("Raw mismatch: have non-nil, want nil") + if want, got := "null", string(data.Id); want != got { + t.Fatalf("Raw mismatch: have %q, want %q", got, want) + } + if data.IdPtr != nil { + t.Fatalf("Raw pointer mismatch: have non-nil, want nil") } b, err := Marshal(&data) if err != nil { |
