aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/encoding
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-07-27 22:27:54 -0700
committerIan Lance Taylor <iant@golang.org>2020-08-01 11:21:40 -0700
commitf75af8c1464e948b5e166cf5ab09ebf0d82fc253 (patch)
tree3ba3299859b504bdeb477727471216bd094a0191 /libgo/go/encoding
parent75a23e59031fe673fc3b2e60fd1fe5f4c70ecb85 (diff)
downloadgcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.zip
gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.gz
gcc-f75af8c1464e948b5e166cf5ab09ebf0d82fc253.tar.bz2
libgo: update to go1.15rc1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/245157
Diffstat (limited to 'libgo/go/encoding')
-rw-r--r--libgo/go/encoding/asn1/asn1.go12
-rw-r--r--libgo/go/encoding/asn1/asn1_test.go12
-rw-r--r--libgo/go/encoding/asn1/marshal.go56
-rw-r--r--libgo/go/encoding/asn1/marshal_test.go57
-rw-r--r--libgo/go/encoding/base64/base64.go26
-rw-r--r--libgo/go/encoding/base64/base64_test.go2
-rw-r--r--libgo/go/encoding/csv/writer.go16
-rw-r--r--libgo/go/encoding/csv/writer_test.go17
-rw-r--r--libgo/go/encoding/hex/hex_test.go1
-rw-r--r--libgo/go/encoding/json/decode_test.go96
-rw-r--r--libgo/go/encoding/json/encode.go2
-rw-r--r--libgo/go/encoding/json/scanner.go31
-rw-r--r--libgo/go/encoding/xml/marshal.go16
-rw-r--r--libgo/go/encoding/xml/marshal_test.go17
-rw-r--r--libgo/go/encoding/xml/read.go16
-rw-r--r--libgo/go/encoding/xml/typeinfo.go16
-rw-r--r--libgo/go/encoding/xml/xml.go4
17 files changed, 352 insertions, 45 deletions
diff --git a/libgo/go/encoding/asn1/asn1.go b/libgo/go/encoding/asn1/asn1.go
index fd4dd68..d809dde 100644
--- a/libgo/go/encoding/asn1/asn1.go
+++ b/libgo/go/encoding/asn1/asn1.go
@@ -313,6 +313,12 @@ func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error)
}
ret64 <<= 7
b := bytes[offset]
+ // integers should be minimally encoded, so the leading octet should
+ // never be 0x80
+ if shifted == 0 && b == 0x80 {
+ err = SyntaxError{"integer is not minimally encoded"}
+ return
+ }
ret64 |= int64(b & 0x7f)
offset++
if b&0x80 == 0 {
@@ -1031,6 +1037,12 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
// Because Unmarshal uses the reflect package, the structs
// being written to must use upper case field names.
//
+// After parsing b, any bytes that were leftover and not used to fill
+// val will be returned in rest. When parsing a SEQUENCE into a struct,
+// any trailing elements of the SEQUENCE that do not have matching
+// fields in val will not be included in rest, as these are considered
+// valid elements of the SEQUENCE and not trailing data.
+//
// An ASN.1 INTEGER can be written to an int, int32, int64,
// or *big.Int (from the math/big package).
// If the encoded value does not fit in the Go type,
diff --git a/libgo/go/encoding/asn1/asn1_test.go b/libgo/go/encoding/asn1/asn1_test.go
index d5649bf..8daae97 100644
--- a/libgo/go/encoding/asn1/asn1_test.go
+++ b/libgo/go/encoding/asn1/asn1_test.go
@@ -1129,3 +1129,15 @@ func TestBMPString(t *testing.T) {
}
}
}
+
+func TestNonMinimalEncodedOID(t *testing.T) {
+ h, err := hex.DecodeString("060a2a80864886f70d01010b")
+ if err != nil {
+ t.Fatalf("failed to decode from hex string: %s", err)
+ }
+ var oid ObjectIdentifier
+ _, err = Unmarshal(h, &oid)
+ if err == nil {
+ t.Fatalf("accepted non-minimally encoded oid")
+ }
+}
diff --git a/libgo/go/encoding/asn1/marshal.go b/libgo/go/encoding/asn1/marshal.go
index c9ae2ca..0d34d5a 100644
--- a/libgo/go/encoding/asn1/marshal.go
+++ b/libgo/go/encoding/asn1/marshal.go
@@ -5,10 +5,12 @@
package asn1
import (
+ "bytes"
"errors"
"fmt"
"math/big"
"reflect"
+ "sort"
"time"
"unicode/utf8"
)
@@ -78,6 +80,48 @@ func (m multiEncoder) Encode(dst []byte) {
}
}
+type setEncoder []encoder
+
+func (s setEncoder) Len() int {
+ var size int
+ for _, e := range s {
+ size += e.Len()
+ }
+ return size
+}
+
+func (s setEncoder) Encode(dst []byte) {
+ // Per X690 Section 11.6: The encodings of the component values of a
+ // set-of value shall appear in ascending order, the encodings being
+ // compared as octet strings with the shorter components being padded
+ // at their trailing end with 0-octets.
+ //
+ // First we encode each element to its TLV encoding and then use
+ // octetSort to get the ordering expected by X690 DER rules before
+ // writing the sorted encodings out to dst.
+ l := make([][]byte, len(s))
+ for i, e := range s {
+ l[i] = make([]byte, e.Len())
+ e.Encode(l[i])
+ }
+
+ sort.Slice(l, func(i, j int) bool {
+ // Since we are using bytes.Compare to compare TLV encodings we
+ // don't need to right pad s[i] and s[j] to the same length as
+ // suggested in X690. If len(s[i]) < len(s[j]) the length octet of
+ // s[i], which is the first determining byte, will inherently be
+ // smaller than the length octet of s[j]. This lets us skip the
+ // padding step.
+ return bytes.Compare(l[i], l[j]) < 0
+ })
+
+ var off int
+ for _, b := range l {
+ copy(dst[off:], b)
+ off += len(b)
+ }
+}
+
type taggedEncoder struct {
// scratch contains temporary space for encoding the tag and length of
// an element in order to avoid extra allocations.
@@ -511,6 +555,9 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error
}
}
+ if params.set {
+ return setEncoder(m), nil
+ }
return multiEncoder(m), nil
}
case reflect.String:
@@ -618,6 +665,15 @@ func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) {
tag = TagSet
}
+ // makeField can be called for a slice that should be treated as a SET
+ // but doesn't have params.set set, for instance when using a slice
+ // with the SET type name suffix. In this case getUniversalType returns
+ // TagSet, but makeBody doesn't know about that so will treat the slice
+ // as a sequence. To work around this we set params.set.
+ if tag == TagSet && !params.set {
+ params.set = true
+ }
+
t := new(taggedEncoder)
t.body, err = makeBody(v, params)
diff --git a/libgo/go/encoding/asn1/marshal_test.go b/libgo/go/encoding/asn1/marshal_test.go
index a77826a..5290522 100644
--- a/libgo/go/encoding/asn1/marshal_test.go
+++ b/libgo/go/encoding/asn1/marshal_test.go
@@ -319,3 +319,60 @@ func BenchmarkMarshal(b *testing.B) {
}
}
}
+
+func TestSetEncoder(t *testing.T) {
+ testStruct := struct {
+ Strings []string `asn1:"set"`
+ }{
+ Strings: []string{"a", "aa", "b", "bb", "c", "cc"},
+ }
+
+ // Expected ordering of the SET should be:
+ // a, b, c, aa, bb, cc
+
+ output, err := Marshal(testStruct)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+
+ expectedOrder := []string{"a", "b", "c", "aa", "bb", "cc"}
+ var resultStruct struct {
+ Strings []string `asn1:"set"`
+ }
+ rest, err := Unmarshal(output, &resultStruct)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+ if len(rest) != 0 {
+ t.Error("Unmarshal returned extra garbage")
+ }
+ if !reflect.DeepEqual(expectedOrder, resultStruct.Strings) {
+ t.Errorf("Unexpected SET content. got: %s, want: %s", resultStruct.Strings, expectedOrder)
+ }
+}
+
+func TestSetEncoderSETSliceSuffix(t *testing.T) {
+ type testSetSET []string
+ testSet := testSetSET{"a", "aa", "b", "bb", "c", "cc"}
+
+ // Expected ordering of the SET should be:
+ // a, b, c, aa, bb, cc
+
+ output, err := Marshal(testSet)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+
+ expectedOrder := testSetSET{"a", "b", "c", "aa", "bb", "cc"}
+ var resultSet testSetSET
+ rest, err := Unmarshal(output, &resultSet)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+ if len(rest) != 0 {
+ t.Error("Unmarshal returned extra garbage")
+ }
+ if !reflect.DeepEqual(expectedOrder, resultSet) {
+ t.Errorf("Unexpected SET content. got: %s, want: %s", resultSet, expectedOrder)
+ }
+}
diff --git a/libgo/go/encoding/base64/base64.go b/libgo/go/encoding/base64/base64.go
index 690d3ce..0c33f8e 100644
--- a/libgo/go/encoding/base64/base64.go
+++ b/libgo/go/encoding/base64/base64.go
@@ -480,15 +480,16 @@ func (enc *Encoding) Decode(dst, src []byte) (n int, err error) {
si := 0
for strconv.IntSize >= 64 && len(src)-si >= 8 && len(dst)-n >= 8 {
+ src2 := src[si : si+8]
if dn, ok := assemble64(
- enc.decodeMap[src[si+0]],
- enc.decodeMap[src[si+1]],
- enc.decodeMap[src[si+2]],
- enc.decodeMap[src[si+3]],
- enc.decodeMap[src[si+4]],
- enc.decodeMap[src[si+5]],
- enc.decodeMap[src[si+6]],
- enc.decodeMap[src[si+7]],
+ enc.decodeMap[src2[0]],
+ enc.decodeMap[src2[1]],
+ enc.decodeMap[src2[2]],
+ enc.decodeMap[src2[3]],
+ enc.decodeMap[src2[4]],
+ enc.decodeMap[src2[5]],
+ enc.decodeMap[src2[6]],
+ enc.decodeMap[src2[7]],
); ok {
binary.BigEndian.PutUint64(dst[n:], dn)
n += 6
@@ -504,11 +505,12 @@ func (enc *Encoding) Decode(dst, src []byte) (n int, err error) {
}
for len(src)-si >= 4 && len(dst)-n >= 4 {
+ src2 := src[si : si+4]
if dn, ok := assemble32(
- enc.decodeMap[src[si+0]],
- enc.decodeMap[src[si+1]],
- enc.decodeMap[src[si+2]],
- enc.decodeMap[src[si+3]],
+ enc.decodeMap[src2[0]],
+ enc.decodeMap[src2[1]],
+ enc.decodeMap[src2[2]],
+ enc.decodeMap[src2[3]],
); ok {
binary.BigEndian.PutUint32(dst[n:], dn)
n += 3
diff --git a/libgo/go/encoding/base64/base64_test.go b/libgo/go/encoding/base64/base64_test.go
index bc67036..c2c9478 100644
--- a/libgo/go/encoding/base64/base64_test.go
+++ b/libgo/go/encoding/base64/base64_test.go
@@ -401,7 +401,7 @@ func TestDecoderIssue3577(t *testing.T) {
source: "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", // twas brillig...
nextc: next,
})
- errc := make(chan error)
+ errc := make(chan error, 1)
go func() {
_, err := ioutil.ReadAll(d)
errc <- err
diff --git a/libgo/go/encoding/csv/writer.go b/libgo/go/encoding/csv/writer.go
index 3f34bc5..ac64b4d 100644
--- a/libgo/go/encoding/csv/writer.go
+++ b/libgo/go/encoding/csv/writer.go
@@ -158,10 +158,24 @@ func (w *Writer) fieldNeedsQuotes(field string) bool {
if field == "" {
return false
}
- if field == `\.` || strings.ContainsRune(field, w.Comma) || strings.ContainsAny(field, "\"\r\n") {
+
+ if field == `\.` {
return true
}
+ if w.Comma < utf8.RuneSelf {
+ for i := 0; i < len(field); i++ {
+ c := field[i]
+ if c == '\n' || c == '\r' || c == '"' || c == byte(w.Comma) {
+ return true
+ }
+ }
+ } else {
+ if strings.ContainsRune(field, w.Comma) || strings.ContainsAny(field, "\"\r\n") {
+ return true
+ }
+ }
+
r1, _ := utf8.DecodeRuneInString(field)
return unicode.IsSpace(r1)
}
diff --git a/libgo/go/encoding/csv/writer_test.go b/libgo/go/encoding/csv/writer_test.go
index 011f01c..ab28b0d7 100644
--- a/libgo/go/encoding/csv/writer_test.go
+++ b/libgo/go/encoding/csv/writer_test.go
@@ -93,3 +93,20 @@ func TestError(t *testing.T) {
t.Error("Error should not be nil")
}
}
+
+var benchmarkWriteData = [][]string{
+ {"abc", "def", "12356", "1234567890987654311234432141542132"},
+ {"abc", "def", "12356", "1234567890987654311234432141542132"},
+ {"abc", "def", "12356", "1234567890987654311234432141542132"},
+}
+
+func BenchmarkWrite(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ w := NewWriter(&bytes.Buffer{})
+ err := w.WriteAll(benchmarkWriteData)
+ if err != nil {
+ b.Fatal(err)
+ }
+ w.Flush()
+ }
+}
diff --git a/libgo/go/encoding/hex/hex_test.go b/libgo/go/encoding/hex/hex_test.go
index dbb00b9..31e3f68 100644
--- a/libgo/go/encoding/hex/hex_test.go
+++ b/libgo/go/encoding/hex/hex_test.go
@@ -267,7 +267,6 @@ func BenchmarkDecode(b *testing.B) {
func BenchmarkDump(b *testing.B) {
for _, size := range []int{256, 1024, 4096, 16384} {
src := bytes.Repeat([]byte{2, 3, 5, 7, 9, 11, 13, 17}, size/8)
- sink = make([]byte, 2*size)
b.Run(fmt.Sprintf("%v", size), func(b *testing.B) {
b.SetBytes(int64(size))
diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go
index 689cc34..219e845 100644
--- a/libgo/go/encoding/json/decode_test.go
+++ b/libgo/go/encoding/json/decode_test.go
@@ -2476,3 +2476,99 @@ func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) {
t.Fatalf("Unexpected roundtrip result:\nwant: %q\ngot: %q", want, got)
}
}
+
+func TestUnmarshalMaxDepth(t *testing.T) {
+ testcases := []struct {
+ name string
+ data string
+ errMaxDepth bool
+ }{
+ {
+ name: "ArrayUnderMaxNestingDepth",
+ data: `{"a":` + strings.Repeat(`[`, 10000-1) + strings.Repeat(`]`, 10000-1) + `}`,
+ errMaxDepth: false,
+ },
+ {
+ name: "ArrayOverMaxNestingDepth",
+ data: `{"a":` + strings.Repeat(`[`, 10000) + strings.Repeat(`]`, 10000) + `}`,
+ errMaxDepth: true,
+ },
+ {
+ name: "ArrayOverStackDepth",
+ data: `{"a":` + strings.Repeat(`[`, 3000000) + strings.Repeat(`]`, 3000000) + `}`,
+ errMaxDepth: true,
+ },
+ {
+ name: "ObjectUnderMaxNestingDepth",
+ data: `{"a":` + strings.Repeat(`{"a":`, 10000-1) + `0` + strings.Repeat(`}`, 10000-1) + `}`,
+ errMaxDepth: false,
+ },
+ {
+ name: "ObjectOverMaxNestingDepth",
+ data: `{"a":` + strings.Repeat(`{"a":`, 10000) + `0` + strings.Repeat(`}`, 10000) + `}`,
+ errMaxDepth: true,
+ },
+ {
+ name: "ObjectOverStackDepth",
+ data: `{"a":` + strings.Repeat(`{"a":`, 3000000) + `0` + strings.Repeat(`}`, 3000000) + `}`,
+ errMaxDepth: true,
+ },
+ }
+
+ targets := []struct {
+ name string
+ newValue func() interface{}
+ }{
+ {
+ name: "unstructured",
+ newValue: func() interface{} {
+ var v interface{}
+ return &v
+ },
+ },
+ {
+ name: "typed named field",
+ newValue: func() interface{} {
+ v := struct {
+ A interface{} `json:"a"`
+ }{}
+ return &v
+ },
+ },
+ {
+ name: "typed missing field",
+ newValue: func() interface{} {
+ v := struct {
+ B interface{} `json:"b"`
+ }{}
+ return &v
+ },
+ },
+ {
+ name: "custom unmarshaler",
+ newValue: func() interface{} {
+ v := unmarshaler{}
+ return &v
+ },
+ },
+ }
+
+ for _, tc := range testcases {
+ for _, target := range targets {
+ t.Run(target.name+"-"+tc.name, func(t *testing.T) {
+ err := Unmarshal([]byte(tc.data), target.newValue())
+ if !tc.errMaxDepth {
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ } else {
+ if err == nil {
+ t.Errorf("expected error containing 'exceeded max depth', got none")
+ } else if !strings.Contains(err.Error(), "exceeded max depth") {
+ t.Errorf("expected error containing 'exceeded max depth', got: %v", err)
+ }
+ }
+ })
+ }
+ }
+}
diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go
index b351cf3..578d551 100644
--- a/libgo/go/encoding/json/encode.go
+++ b/libgo/go/encoding/json/encode.go
@@ -650,7 +650,7 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
func isValidNumber(s string) bool {
// This function implements the JSON numbers grammar.
// See https://tools.ietf.org/html/rfc7159#section-6
- // and https://json.org/number.gif
+ // and https://www.json.org/img/number.png
if s == "" {
return false
diff --git a/libgo/go/encoding/json/scanner.go b/libgo/go/encoding/json/scanner.go
index 552bd70..9dc1903 100644
--- a/libgo/go/encoding/json/scanner.go
+++ b/libgo/go/encoding/json/scanner.go
@@ -139,6 +139,10 @@ const (
parseArrayValue // parsing array value
)
+// This limits the max nesting depth to prevent stack overflow.
+// This is permitted by https://tools.ietf.org/html/rfc7159#section-9
+const maxNestingDepth = 10000
+
// reset prepares the scanner for use.
// It must be called before calling s.step.
func (s *scanner) reset() {
@@ -168,8 +172,13 @@ func (s *scanner) eof() int {
}
// pushParseState pushes a new parse state p onto the parse stack.
-func (s *scanner) pushParseState(p int) {
- s.parseState = append(s.parseState, p)
+// an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned.
+func (s *scanner) pushParseState(c byte, newParseState int, successState int) int {
+ s.parseState = append(s.parseState, newParseState)
+ if len(s.parseState) <= maxNestingDepth {
+ return successState
+ }
+ return s.error(c, "exceeded max depth")
}
// popParseState pops a parse state (already obtained) off the stack
@@ -186,12 +195,12 @@ func (s *scanner) popParseState() {
}
func isSpace(c byte) bool {
- return c == ' ' || c == '\t' || c == '\r' || c == '\n'
+ return c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n')
}
// stateBeginValueOrEmpty is the state after reading `[`.
func stateBeginValueOrEmpty(s *scanner, c byte) int {
- if c <= ' ' && isSpace(c) {
+ if isSpace(c) {
return scanSkipSpace
}
if c == ']' {
@@ -202,18 +211,16 @@ func stateBeginValueOrEmpty(s *scanner, c byte) int {
// stateBeginValue is the state at the beginning of the input.
func stateBeginValue(s *scanner, c byte) int {
- if c <= ' ' && isSpace(c) {
+ if isSpace(c) {
return scanSkipSpace
}
switch c {
case '{':
s.step = stateBeginStringOrEmpty
- s.pushParseState(parseObjectKey)
- return scanBeginObject
+ return s.pushParseState(c, parseObjectKey, scanBeginObject)
case '[':
s.step = stateBeginValueOrEmpty
- s.pushParseState(parseArrayValue)
- return scanBeginArray
+ return s.pushParseState(c, parseArrayValue, scanBeginArray)
case '"':
s.step = stateInString
return scanBeginLiteral
@@ -242,7 +249,7 @@ func stateBeginValue(s *scanner, c byte) int {
// stateBeginStringOrEmpty is the state after reading `{`.
func stateBeginStringOrEmpty(s *scanner, c byte) int {
- if c <= ' ' && isSpace(c) {
+ if isSpace(c) {
return scanSkipSpace
}
if c == '}' {
@@ -255,7 +262,7 @@ func stateBeginStringOrEmpty(s *scanner, c byte) int {
// stateBeginString is the state after reading `{"key": value,`.
func stateBeginString(s *scanner, c byte) int {
- if c <= ' ' && isSpace(c) {
+ if isSpace(c) {
return scanSkipSpace
}
if c == '"' {
@@ -275,7 +282,7 @@ func stateEndValue(s *scanner, c byte) int {
s.endTop = true
return stateEndTop(s, c)
}
- if c <= ' ' && isSpace(c) {
+ if isSpace(c) {
s.step = stateEndValue
return scanSkipSpace
}
diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go
index e325f31..0554b0d 100644
--- a/libgo/go/encoding/xml/marshal.go
+++ b/libgo/go/encoding/xml/marshal.go
@@ -479,8 +479,11 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
xmlname := tinfo.xmlname
if xmlname.name != "" {
start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name
- } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
- start.Name = v
+ } else {
+ fv := xmlname.value(val, dontInitNilPointers)
+ if v, ok := fv.Interface().(Name); ok && v.Local != "" {
+ start.Name = v
+ }
}
}
if start.Name.Local == "" && finfo != nil {
@@ -500,7 +503,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
if finfo.flags&fAttr == 0 {
continue
}
- fv := finfo.value(val)
+ fv := finfo.value(val, dontInitNilPointers)
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
continue
@@ -803,7 +806,12 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
if finfo.flags&fAttr != 0 {
continue
}
- vf := finfo.value(val)
+ vf := finfo.value(val, dontInitNilPointers)
+ if !vf.IsValid() {
+ // The field is behind an anonymous struct field that's
+ // nil. Skip it.
+ continue
+ }
switch finfo.flags & fMode {
case fCDATA, fCharData:
diff --git a/libgo/go/encoding/xml/marshal_test.go b/libgo/go/encoding/xml/marshal_test.go
index 8473158b..31309ef 100644
--- a/libgo/go/encoding/xml/marshal_test.go
+++ b/libgo/go/encoding/xml/marshal_test.go
@@ -309,6 +309,11 @@ type ChardataEmptyTest struct {
Contents *string `xml:",chardata"`
}
+type PointerAnonFields struct {
+ *MyInt
+ *NamedType
+}
+
type MyMarshalerTest struct {
}
@@ -889,6 +894,18 @@ var marshalTests = []struct {
`</EmbedA>`,
},
+ // Anonymous struct pointer field which is nil
+ {
+ Value: &EmbedB{},
+ ExpectXML: `<EmbedB><FieldB></FieldB></EmbedB>`,
+ },
+
+ // Other kinds of nil anonymous fields
+ {
+ Value: &PointerAnonFields{},
+ ExpectXML: `<PointerAnonFields></PointerAnonFields>`,
+ },
+
// Test that name casing matters
{
Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
diff --git a/libgo/go/encoding/xml/read.go b/libgo/go/encoding/xml/read.go
index 10a60ee..ef5df3f 100644
--- a/libgo/go/encoding/xml/read.go
+++ b/libgo/go/encoding/xml/read.go
@@ -435,7 +435,7 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
}
return UnmarshalError(e)
}
- fv := finfo.value(sv)
+ fv := finfo.value(sv, initNilPointers)
if _, ok := fv.Interface().(Name); ok {
fv.Set(reflect.ValueOf(start.Name))
}
@@ -449,7 +449,7 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
finfo := &tinfo.fields[i]
switch finfo.flags & fMode {
case fAttr:
- strv := finfo.value(sv)
+ strv := finfo.value(sv, initNilPointers)
if a.Name.Local == finfo.name && (finfo.xmlns == "" || finfo.xmlns == a.Name.Space) {
if err := d.unmarshalAttr(strv, a); err != nil {
return err
@@ -465,7 +465,7 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
}
if !handled && any >= 0 {
finfo := &tinfo.fields[any]
- strv := finfo.value(sv)
+ strv := finfo.value(sv, initNilPointers)
if err := d.unmarshalAttr(strv, a); err != nil {
return err
}
@@ -478,22 +478,22 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
switch finfo.flags & fMode {
case fCDATA, fCharData:
if !saveData.IsValid() {
- saveData = finfo.value(sv)
+ saveData = finfo.value(sv, initNilPointers)
}
case fComment:
if !saveComment.IsValid() {
- saveComment = finfo.value(sv)
+ saveComment = finfo.value(sv, initNilPointers)
}
case fAny, fAny | fElement:
if !saveAny.IsValid() {
- saveAny = finfo.value(sv)
+ saveAny = finfo.value(sv, initNilPointers)
}
case fInnerXML:
if !saveXML.IsValid() {
- saveXML = finfo.value(sv)
+ saveXML = finfo.value(sv, initNilPointers)
if d.saved == nil {
saveXMLIndex = 0
d.saved = new(bytes.Buffer)
@@ -687,7 +687,7 @@ Loop:
}
if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
// It's a perfect match, unmarshal the field.
- return true, d.unmarshal(finfo.value(sv), start)
+ return true, d.unmarshal(finfo.value(sv, initNilPointers), start)
}
if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local {
// It's a prefix for the field. Break and recurse
diff --git a/libgo/go/encoding/xml/typeinfo.go b/libgo/go/encoding/xml/typeinfo.go
index 639952c..f30fe58 100644
--- a/libgo/go/encoding/xml/typeinfo.go
+++ b/libgo/go/encoding/xml/typeinfo.go
@@ -344,15 +344,25 @@ func (e *TagPathError) Error() string {
return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
}
+const (
+ initNilPointers = true
+ dontInitNilPointers = false
+)
+
// value returns v's field value corresponding to finfo.
-// It's equivalent to v.FieldByIndex(finfo.idx), but initializes
-// and dereferences pointers as necessary.
-func (finfo *fieldInfo) value(v reflect.Value) reflect.Value {
+// It's equivalent to v.FieldByIndex(finfo.idx), but when passed
+// initNilPointers, it initializes and dereferences pointers as necessary.
+// When passed dontInitNilPointers and a nil pointer is reached, the function
+// returns a zero reflect.Value.
+func (finfo *fieldInfo) value(v reflect.Value, shouldInitNilPointers bool) reflect.Value {
for i, x := range finfo.idx {
if i > 0 {
t := v.Type()
if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
if v.IsNil() {
+ if !shouldInitNilPointers {
+ return reflect.Value{}
+ }
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
diff --git a/libgo/go/encoding/xml/xml.go b/libgo/go/encoding/xml/xml.go
index 5e73dcf..adaf4da 100644
--- a/libgo/go/encoding/xml/xml.go
+++ b/libgo/go/encoding/xml/xml.go
@@ -960,7 +960,7 @@ func (d *Decoder) ungetc(b byte) {
d.offset--
}
-var entity = map[string]int{
+var entity = map[string]rune{
"lt": '<',
"gt": '>',
"amp": '&',
@@ -1055,7 +1055,7 @@ Input:
d.buf.WriteByte(';')
n, err := strconv.ParseUint(s, base, 64)
if err == nil && n <= unicode.MaxRune {
- text = string(n)
+ text = string(rune(n))
haveText = true
}
}