diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-07-16 06:54:42 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-07-16 06:54:42 +0000 |
commit | be47d6eceffd2c5dbbc1566d5eea490527fb2bd4 (patch) | |
tree | 0e8fda573576bb4181dba29d0e88380a8c38fafd /libgo/go/encoding/json | |
parent | efb30cdeb003fd7c585ee0d7657340086abcbd9e (diff) | |
download | gcc-be47d6eceffd2c5dbbc1566d5eea490527fb2bd4.zip gcc-be47d6eceffd2c5dbbc1566d5eea490527fb2bd4.tar.gz gcc-be47d6eceffd2c5dbbc1566d5eea490527fb2bd4.tar.bz2 |
libgo: Update to Go 1.1.1.
From-SVN: r200974
Diffstat (limited to 'libgo/go/encoding/json')
-rw-r--r-- | libgo/go/encoding/json/decode.go | 30 | ||||
-rw-r--r-- | libgo/go/encoding/json/decode_test.go | 86 | ||||
-rw-r--r-- | libgo/go/encoding/json/encode.go | 98 | ||||
-rw-r--r-- | libgo/go/encoding/json/encode_test.go | 104 | ||||
-rw-r--r-- | libgo/go/encoding/json/scanner_test.go | 3 |
5 files changed, 295 insertions, 26 deletions
diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go index 95e9120..62ac294 100644 --- a/libgo/go/encoding/json/decode.go +++ b/libgo/go/encoding/json/decode.go @@ -33,6 +33,10 @@ import ( // the value pointed at by the pointer. If the pointer is nil, Unmarshal // allocates a new value for it to point to. // +// To unmarshal JSON into a struct, Unmarshal matches incoming object +// keys to the keys used by Marshal (either the struct field name or its tag), +// preferring an exact match but also accepting a case-insensitive match. +// // To unmarshal JSON into an interface value, Unmarshal unmarshals // the JSON into the concrete value contained in the interface value. // If the interface value is nil, that is, has no concrete value stored in it, @@ -51,17 +55,22 @@ import ( // If no more serious errors are encountered, Unmarshal returns // an UnmarshalTypeError describing the earliest such error. // +// When unmarshaling quoted strings, invalid UTF-8 or +// invalid UTF-16 surrogate pairs are not treated as an error. +// Instead, they are replaced by the Unicode replacement +// character U+FFFD. +// func Unmarshal(data []byte, v interface{}) error { - d := new(decodeState).init(data) - - // Quick check for well-formedness. + // Check for well-formedness. // Avoids filling out half a data structure // before discovering a JSON syntax error. + var d decodeState err := checkValid(data, &d.scan) if err != nil { return err } + d.init(data) return d.unmarshal(v) } @@ -252,6 +261,16 @@ func (d *decodeState) value(v reflect.Value) { } d.scan.step(&d.scan, '"') d.scan.step(&d.scan, '"') + + n := len(d.scan.parseState) + if n > 0 && d.scan.parseState[n-1] == parseObjectKey { + // d.scan thinks we just read an object key; finish the object + d.scan.step(&d.scan, ':') + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '}') + } + return } @@ -730,6 +749,7 @@ func (d *decodeState) valueInterface() interface{} { switch d.scanWhile(scanSkipSpace) { default: d.error(errPhase) + panic("unreachable") case scanBeginArray: return d.arrayInterface() case scanBeginObject: @@ -737,12 +757,11 @@ func (d *decodeState) valueInterface() interface{} { case scanBeginLiteral: return d.literalInterface() } - panic("unreachable") } // arrayInterface is like array but returns []interface{}. func (d *decodeState) arrayInterface() []interface{} { - var v []interface{} + var v = make([]interface{}, 0) for { // Look ahead for ] - can only happen on first iteration. op := d.scanWhile(scanSkipSpace) @@ -849,7 +868,6 @@ func (d *decodeState) literalInterface() interface{} { } return n } - panic("unreachable") } // getu4 decodes \uXXXX from the beginning of s, returning the hex value, diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go index a91c6da..f845f69 100644 --- a/libgo/go/encoding/json/decode_test.go +++ b/libgo/go/encoding/json/decode_test.go @@ -11,6 +11,7 @@ import ( "reflect" "strings" "testing" + "time" ) type T struct { @@ -29,7 +30,7 @@ type V struct { F3 Number } -// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshalling with and +// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and // without UseNumber var ifaceNumAsFloat64 = map[string]interface{}{ "k1": float64(1), @@ -239,6 +240,12 @@ var unmarshalTests = []unmarshalTest{ {in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}}, {in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}}, + // empty array to interface test + {in: `[]`, ptr: new([]interface{}), out: []interface{}{}}, + {in: `null`, ptr: new([]interface{}), out: []interface{}(nil)}, + {in: `{"T":[]}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}}, + {in: `{"T":null}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": interface{}(nil)}}, + // composite tests {in: allValueIndent, ptr: new(All), out: allValue}, {in: allValueCompact, ptr: new(All), out: allValue}, @@ -323,6 +330,43 @@ var unmarshalTests = []unmarshalTest{ ptr: new(S10), out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}}, }, + + // invalid UTF-8 is coerced to valid UTF-8. + { + in: "\"hello\xffworld\"", + ptr: new(string), + out: "hello\ufffdworld", + }, + { + in: "\"hello\xc2\xc2world\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\xc2\xffworld\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\\ud800world\"", + ptr: new(string), + out: "hello\ufffdworld", + }, + { + in: "\"hello\\ud800\\ud800world\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\\ud800\\ud800world\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"", + ptr: new(string), + out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld", + }, } func TestMarshal(t *testing.T) { @@ -1107,3 +1151,43 @@ func TestUnmarshalUnexported(t *testing.T) { t.Errorf("got %q, want %q", out, want) } } + +// Time3339 is a time.Time which encodes to and from JSON +// as an RFC 3339 time in UTC. +type Time3339 time.Time + +func (t *Time3339) UnmarshalJSON(b []byte) error { + if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { + return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b) + } + tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1])) + if err != nil { + return err + } + *t = Time3339(tm) + return nil +} + +func TestUnmarshalJSONLiteralError(t *testing.T) { + var t3 Time3339 + err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3) + if err == nil { + t.Fatalf("expected error; got time %v", time.Time(t3)) + } + if !strings.Contains(err.Error(), "range") { + t.Errorf("got err = %v; want out of range error", err) + } +} + +// Test that extra object elements in an array do not result in a +// "data changing underfoot" error. +// Issue 3717 +func TestSkipArrayObjects(t *testing.T) { + json := `[{}]` + var dest [0]interface{} + + err := Unmarshal([]byte(json), &dest) + if err != nil { + t.Errorf("got error %q, want nil", err) + } +} diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index fb57f1d..85727ba 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -3,7 +3,8 @@ // license that can be found in the LICENSE file. // Package json implements encoding and decoding of JSON objects as defined in -// RFC 4627. +// RFC 4627. The mapping between JSON objects and Go values is described +// in the documentation for the Marshal and Unmarshal functions. // // See "JSON and Go" for an introduction to this package: // http://golang.org/doc/articles/json_and_go.html @@ -38,8 +39,8 @@ import ( // // Floating point, integer, and Number values encode as JSON numbers. // -// String values encode as JSON strings, with each invalid UTF-8 sequence -// replaced by the encoding of the Unicode replacement character U+FFFD. +// String values encode as JSON strings. InvalidUTF8Error will be returned +// if an invalid UTF-8 sequence is encountered. // The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" // to keep some browsers from misinterpreting JSON output as HTML. // @@ -86,9 +87,21 @@ import ( // underscores and slashes. // // Anonymous struct fields are usually marshaled as if their inner exported fields -// were fields in the outer struct, subject to the usual Go visibility rules. +// were fields in the outer struct, subject to the usual Go visibility rules amended +// as described in the next paragraph. // An anonymous struct field with a name given in its JSON tag is treated as -// having that name instead of as anonymous. +// having that name, rather than being anonymous. +// +// The Go visibility rules for struct fields are amended for JSON when +// deciding which field to marshal or unmarshal. If there are +// multiple fields at the same level, and that level is the least +// nested (and would therefore be the nesting level selected by the +// usual Go rules), the following extra rules apply: +// +// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered, +// even if there are multiple untagged fields that would otherwise conflict. +// 2) If there is exactly one field (tagged or not according to the first rule), that is selected. +// 3) Otherwise there are multiple fields, and all are ignored; no error occurs. // // Handling of anonymous struct fields is new in Go 1.1. // Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of @@ -187,8 +200,10 @@ func (e *UnsupportedValueError) Error() string { return "json: unsupported value: " + e.Str } +// An InvalidUTF8Error is returned by Marshal when attempting +// to encode a string value with invalid UTF-8 sequences. type InvalidUTF8Error struct { - S string + S string // the whole string value that caused the error } func (e *InvalidUTF8Error) Error() string { @@ -654,27 +669,78 @@ func typeFields(t reflect.Type) []field { sort.Sort(byName(fields)) - // Remove fields with annihilating name collisions - // and also fields shadowed by fields with explicit JSON tags. - name := "" + // Delete all fields that are hidden by the Go rules for embedded fields, + // except that fields with JSON tags are promoted. + + // The fields are sorted in primary order of name, secondary order + // of field index length. Loop over names; for each name, delete + // hidden fields by choosing the one dominant field that survives. out := fields[:0] - for _, f := range fields { - if f.name != name { - name = f.name - out = append(out, f) + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.name != name { + break + } + } + if advance == 1 { // Only one field with this name + out = append(out, fi) continue } - if n := len(out); n > 0 && out[n-1].name == name && (!out[n-1].tag || f.tag) { - out = out[:n-1] + dominant, ok := dominantField(fields[i : i+advance]) + if ok { + out = append(out, dominant) } } - fields = out + fields = out sort.Sort(byIndex(fields)) return fields } +// dominantField looks through the fields, all of which are known to +// have the same name, to find the single field that dominates the +// others using Go's embedding rules, modified by the presence of +// JSON tags. If there are multiple top-level fields, the boolean +// will be false: This condition is an error in Go and we skip all +// the fields. +func dominantField(fields []field) (field, bool) { + // The fields are sorted in increasing index-length order. The winner + // must therefore be one with the shortest index length. Drop all + // longer entries, which is easy: just truncate the slice. + length := len(fields[0].index) + tagged := -1 // Index of first tagged field. + for i, f := range fields { + if len(f.index) > length { + fields = fields[:i] + break + } + if f.tag { + if tagged >= 0 { + // Multiple tagged fields at the same level: conflict. + // Return no field. + return field{}, false + } + tagged = i + } + } + if tagged >= 0 { + return fields[tagged], true + } + // All remaining fields have the same length. If there's more than one, + // we have a conflict (two fields named "X" at the same level) and we + // return no field. + if len(fields) > 1 { + return field{}, false + } + return fields[0], true +} + var fieldCache struct { sync.RWMutex m map[reflect.Type][]field diff --git a/libgo/go/encoding/json/encode_test.go b/libgo/go/encoding/json/encode_test.go index be74c99..5be0a99 100644 --- a/libgo/go/encoding/json/encode_test.go +++ b/libgo/go/encoding/json/encode_test.go @@ -206,3 +206,107 @@ func TestAnonymousNonstruct(t *testing.T) { t.Errorf("got %q, want %q", got, want) } } + +type BugA struct { + S string +} + +type BugB struct { + BugA + S string +} + +type BugC struct { + S string +} + +// Legal Go: We never use the repeated embedded field (S). +type BugX struct { + A int + BugA + BugB +} + +// Issue 5245. +func TestEmbeddedBug(t *testing.T) { + v := BugB{ + BugA{"A"}, + "B", + } + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"S":"B"}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } + // Now check that the duplicate field, S, does not appear. + x := BugX{ + A: 23, + } + b, err = Marshal(x) + if err != nil { + t.Fatal("Marshal:", err) + } + want = `{"A":23}` + got = string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } +} + +type BugD struct { // Same as BugA after tagging. + XXX string `json:"S"` +} + +// BugD's tagged S field should dominate BugA's. +type BugY struct { + BugA + BugD +} + +// Test that a field with a tag dominates untagged fields. +func TestTaggedFieldDominates(t *testing.T) { + v := BugY{ + BugA{"BugA"}, + BugD{"BugD"}, + } + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"S":"BugD"}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } +} + +// There are no tags here, so S should not appear. +type BugZ struct { + BugA + BugC + BugY // Contains a tagged S field through BugD; should not dominate. +} + +func TestDuplicatedFieldDisappears(t *testing.T) { + v := BugZ{ + BugA{"BugA"}, + BugC{"BugC"}, + BugY{ + BugA{"nested BugA"}, + BugD{"nested BugD"}, + }, + } + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } +} diff --git a/libgo/go/encoding/json/scanner_test.go b/libgo/go/encoding/json/scanner_test.go index adb3571..77d3455 100644 --- a/libgo/go/encoding/json/scanner_test.go +++ b/libgo/go/encoding/json/scanner_test.go @@ -277,9 +277,6 @@ func genArray(n int) []interface{} { if f > n { f = n } - if n > 0 && f == 0 { - f = 1 - } x := make([]interface{}, f) for i := range x { x[i] = genValue(((i+1)*n)/f - (i*n)/f) |