diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-01-29 20:52:43 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2013-01-29 20:52:43 +0000 |
commit | d6f2922e91928b5191a5c5f1b3a6b320712b5ce3 (patch) | |
tree | 4f2fad1f4b778519bdd5941185c7e1d032af055b /libgo/go/encoding/json | |
parent | 91bfca59095b1cca9d4364996866848eaaf76c26 (diff) | |
download | gcc-d6f2922e91928b5191a5c5f1b3a6b320712b5ce3.zip gcc-d6f2922e91928b5191a5c5f1b3a6b320712b5ce3.tar.gz gcc-d6f2922e91928b5191a5c5f1b3a6b320712b5ce3.tar.bz2 |
libgo: Update Go library to master revision 15489/921e53d4863c.
From-SVN: r195560
Diffstat (limited to 'libgo/go/encoding/json')
-rw-r--r-- | libgo/go/encoding/json/decode.go | 71 | ||||
-rw-r--r-- | libgo/go/encoding/json/decode_test.go | 92 | ||||
-rw-r--r-- | libgo/go/encoding/json/encode.go | 23 | ||||
-rw-r--r-- | libgo/go/encoding/json/encode_test.go | 20 | ||||
-rw-r--r-- | libgo/go/encoding/json/scanner_test.go | 6 | ||||
-rw-r--r-- | libgo/go/encoding/json/tagkey_test.go | 10 |
6 files changed, 184 insertions, 38 deletions
diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go index b46dac9..6e6815f 100644 --- a/libgo/go/encoding/json/decode.go +++ b/libgo/go/encoding/json/decode.go @@ -52,6 +52,25 @@ import ( // an UnmarshalTypeError describing the earliest such error. // func Unmarshal(data []byte, v interface{}) error { + + // skip heavy processing for primitive values + var first byte + var i int + for i, first = range data { + if !isSpace(rune(first)) { + break + } + } + if first != '{' && first != '[' { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr || rv.IsNil() { + return &InvalidUnmarshalError{reflect.TypeOf(v)} + } + var d decodeState + d.literalStore(data[i:], rv.Elem(), false) + return d.savedError + } + d := new(decodeState).init(data) // Quick check for well-formedness. @@ -87,6 +106,7 @@ func (e *UnmarshalTypeError) Error() string { // An UnmarshalFieldError describes a JSON object key that // led to an unexported (and therefore unwritable) struct field. +// (No longer used; kept for compatibility.) type UnmarshalFieldError struct { Key string Type reflect.Type @@ -328,15 +348,19 @@ func (d *decodeState) array(v reflect.Value) { // Check type of target. switch v.Kind() { + case reflect.Interface: + if v.NumMethod() == 0 { + // Decoding into nil interface? Switch to non-reflect code. + v.Set(reflect.ValueOf(d.arrayInterface())) + return + } + // Otherwise it's invalid. + fallthrough default: d.saveError(&UnmarshalTypeError{"array", v.Type()}) d.off-- d.next() return - case reflect.Interface: - // Decoding into nil interface? Switch to non-reflect code. - v.Set(reflect.ValueOf(d.arrayInterface())) - return case reflect.Array: case reflect.Slice: break @@ -422,7 +446,7 @@ func (d *decodeState) object(v reflect.Value) { v = pv // Decoding into nil interface? Switch to non-reflect code. - if v.Kind() == reflect.Interface { + if v.Kind() == reflect.Interface && v.NumMethod() == 0 { v.Set(reflect.ValueOf(d.objectInterface())) return } @@ -430,9 +454,9 @@ func (d *decodeState) object(v reflect.Value) { // Check type of target: struct or map[string]T switch v.Kind() { case reflect.Map: - // map must have string type + // map must have string kind t := v.Type() - if t.Key() != reflect.TypeOf("") { + if t.Key().Kind() != reflect.String { d.saveError(&UnmarshalTypeError{"object", v.Type()}) break } @@ -440,11 +464,9 @@ func (d *decodeState) object(v reflect.Value) { v.Set(reflect.MakeMap(t)) } case reflect.Struct: + default: d.saveError(&UnmarshalTypeError{"object", v.Type()}) - } - - if !v.IsValid() { d.off-- d.next() // skip over { } in input return @@ -509,15 +531,6 @@ func (d *decodeState) object(v reflect.Value) { } subv = subv.Field(i) } - } else { - // To give a good error, a quick scan for unexported fields in top level. - st := v.Type() - for i := 0; i < st.NumField(); i++ { - f := st.Field(i) - if f.PkgPath != "" && strings.EqualFold(f.Name, key) { - d.saveError(&UnmarshalFieldError{key, st, f}) - } - } } } @@ -536,10 +549,12 @@ func (d *decodeState) object(v reflect.Value) { } else { d.value(subv) } + // Write value back to map; // if using struct, subv points into struct already. if v.Kind() == reflect.Map { - v.SetMapIndex(reflect.ValueOf(key), subv) + kv := reflect.ValueOf(key).Convert(v.Type().Key()) + v.SetMapIndex(kv, subv) } // Next token must be , or }. @@ -625,7 +640,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.Bool: v.SetBool(value) case reflect.Interface: - v.Set(reflect.ValueOf(value)) + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(value)) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type()}) + } } case '"': // string @@ -655,7 +674,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.String: v.SetString(string(s)) case reflect.Interface: - v.Set(reflect.ValueOf(string(s))) + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(string(s))) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + } } default: // number @@ -684,6 +707,10 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool d.saveError(err) break } + if v.NumMethod() != 0 { + d.saveError(&UnmarshalTypeError{"number", v.Type()}) + break + } v.Set(reflect.ValueOf(n)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go index b9fad05..97f2a41 100644 --- a/libgo/go/encoding/json/decode_test.go +++ b/libgo/go/encoding/json/decode_test.go @@ -199,12 +199,19 @@ 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("")}}, - {in: `{"x": 1}`, ptr: new(tx), out: tx{}, err: &UnmarshalFieldError{"x", txType, txType.Field(0)}}, + {in: `{"x": 1}`, ptr: new(tx), out: tx{}}, {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}, {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true}, + // raw values with whitespace + {in: "\n true ", ptr: new(bool), out: true}, + {in: "\t 1 ", ptr: new(int), out: 1}, + {in: "\r 1.2 ", ptr: new(float64), out: 1.2}, + {in: "\t -5 \n", ptr: new(int16), out: int16(-5)}, + {in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"}, + // Z has a "-" tag. {in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}}, @@ -217,6 +224,16 @@ var unmarshalTests = []unmarshalTest{ {in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}}, {in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true}, + // raw value errors + {in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}}, + {in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}}, + {in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}}, + {in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}}, + // array tests {in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}}, {in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}}, @@ -422,7 +439,7 @@ func TestUnmarshalMarshal(t *testing.T) { if err != nil { t.Fatalf("Marshal: %v", err) } - if bytes.Compare(jsonBig, b) != 0 { + if !bytes.Equal(jsonBig, b) { t.Errorf("Marshal jsonBig") diff(t, b, jsonBig) return @@ -474,7 +491,7 @@ func TestLargeByteSlice(t *testing.T) { if err := Unmarshal(b, &s1); err != nil { t.Fatalf("Unmarshal: %v", err) } - if bytes.Compare(s0, s1) != 0 { + if !bytes.Equal(s0, s1) { t.Errorf("Marshal large byte slice") diff(t, s0, s1) } @@ -1000,3 +1017,72 @@ func TestUnmarshalNulls(t *testing.T) { t.Errorf("Unmarshal of null values affected primitives") } } + +func TestStringKind(t *testing.T) { + type stringKind string + type aMap map[stringKind]int + + var m1, m2 map[stringKind]int + m1 = map[stringKind]int{ + "foo": 42, + } + + data, err := Marshal(m1) + if err != nil { + t.Errorf("Unexpected error marshalling: %v", err) + } + + err = Unmarshal(data, &m2) + if err != nil { + t.Errorf("Unexpected error unmarshalling: %v", err) + } + + if !reflect.DeepEqual(m1, m2) { + t.Error("Items should be equal after encoding and then decoding") + } + +} + +var decodeTypeErrorTests = []struct { + dest interface{} + src string +}{ + {new(string), `{"user": "name"}`}, // issue 4628. + {new(error), `{}`}, // issue 4222 + {new(error), `[]`}, + {new(error), `""`}, + {new(error), `123`}, + {new(error), `true`}, +} + +func TestUnmarshalTypeError(t *testing.T) { + for _, item := range decodeTypeErrorTests { + err := Unmarshal([]byte(item.src), item.dest) + if _, ok := err.(*UnmarshalTypeError); !ok { + t.Errorf("expected type error for Unmarshal(%q, type %T): got %v instead", + item.src, item.dest, err) + } + } +} + +// Test handling of unexported fields that should be ignored. +// Issue 4660 +type unexportedFields struct { + Name string + m map[string]interface{} `json:"-"` + m2 map[string]interface{} `json:"abcd"` +} + +func TestUnmarshalUnexported(t *testing.T) { + input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}}` + want := &unexportedFields{Name: "Bob"} + + out := &unexportedFields{} + err := Unmarshal([]byte(input), out) + if err != nil { + t.Errorf("got error %v, expected nil", err) + } + if !reflect.DeepEqual(out, want) { + t.Errorf("got %q, want %q", out, want) + } +} diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index a5803b4..fb57f1d 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -75,8 +75,9 @@ import ( // Field int `json:",omitempty"` // // The "string" option signals that a field is stored as JSON inside a -// JSON-encoded string. This extra level of encoding is sometimes -// used when communicating with JavaScript programs: +// JSON-encoded string. It applies only to fields of string, floating point, +// or integer types. This extra level of encoding is sometimes used when +// communicating with JavaScript programs: // // Int64String int64 `json:",string"` // @@ -437,7 +438,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. @@ -617,13 +618,20 @@ func typeFields(t reflect.Type) []field { index := make([]int, len(f.index)+1) copy(index, f.index) index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + // Record found field and index sequence. - if name != "" || !sf.Anonymous { + if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { tagged := name != "" if name == "" { name = sf.Name } - fields = append(fields, field{name, tagged, index, sf.Type, + fields = append(fields, field{name, tagged, index, ft, opts.Contains("omitempty"), opts.Contains("string")}) if count[f.typ] > 1 { // If there were multiple instances, add a second, @@ -636,11 +644,6 @@ func typeFields(t reflect.Type) []field { } // Record new anonymous struct to explore in next round. - ft := sf.Type - if ft.Name() == "" { - // Must be pointer. - ft = ft.Elem() - } nextCount[ft]++ if nextCount[ft] == 1 { next = append(next, field{name: ft.Name(), index: index, typ: ft}) diff --git a/libgo/go/encoding/json/encode_test.go b/libgo/go/encoding/json/encode_test.go index cb1c77e..be74c99 100644 --- a/libgo/go/encoding/json/encode_test.go +++ b/libgo/go/encoding/json/encode_test.go @@ -186,3 +186,23 @@ func TestMarshalerEscaping(t *testing.T) { t.Errorf("got %q, want %q", got, want) } } + +type IntType int + +type MyStruct struct { + IntType +} + +func TestAnonymousNonstruct(t *testing.T) { + var i IntType = 11 + a := MyStruct{i} + const want = `{"IntType":11}` + + b, err := Marshal(a) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if got := string(b); got != want { + t.Errorf("got %q, want %q", got, want) + } +} diff --git a/libgo/go/encoding/json/scanner_test.go b/libgo/go/encoding/json/scanner_test.go index 14d8508..adb3571 100644 --- a/libgo/go/encoding/json/scanner_test.go +++ b/libgo/go/encoding/json/scanner_test.go @@ -92,7 +92,7 @@ func TestCompactBig(t *testing.T) { t.Fatalf("Compact: %v", err) } b := buf.Bytes() - if bytes.Compare(b, jsonBig) != 0 { + if !bytes.Equal(b, jsonBig) { t.Error("Compact(jsonBig) != jsonBig") diff(t, b, jsonBig) return @@ -118,7 +118,7 @@ func TestIndentBig(t *testing.T) { t.Fatalf("Indent2: %v", err) } b1 := buf1.Bytes() - if bytes.Compare(b1, b) != 0 { + if !bytes.Equal(b1, b) { t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)") diff(t, b1, b) return @@ -130,7 +130,7 @@ func TestIndentBig(t *testing.T) { t.Fatalf("Compact: %v", err) } b1 = buf1.Bytes() - if bytes.Compare(b1, jsonBig) != 0 { + if !bytes.Equal(b1, jsonBig) { t.Error("Compact(Indent(jsonBig)) != jsonBig") diff(t, b1, jsonBig) return diff --git a/libgo/go/encoding/json/tagkey_test.go b/libgo/go/encoding/json/tagkey_test.go index da8b12b..23e71c7 100644 --- a/libgo/go/encoding/json/tagkey_test.go +++ b/libgo/go/encoding/json/tagkey_test.go @@ -60,6 +60,14 @@ type badCodeTag struct { Z string `json:" !\"#&'()*+,."` } +type spaceTag struct { + Q string `json:"With space"` +} + +type unicodeTag struct { + W string `json:"Ελλάδα"` +} + var structTagObjectKeyTests = []struct { raw interface{} value string @@ -78,6 +86,8 @@ var structTagObjectKeyTests = []struct { {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"}, {percentSlashTag{"brut"}, "brut", "text/html%"}, {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"}, + {spaceTag{"Perreddu"}, "Perreddu", "With space"}, + {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}, } func TestStructTagObjectKey(t *testing.T) { |