summaryrefslogtreecommitdiffstats
path: root/libgo/go/encoding/xml
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-10-23 04:31:11 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-10-23 04:31:11 +0000
commitfb08d0057f91d420b6f48c112264fc87dc91b532 (patch)
tree46bb86f514fbf6bad82da48e69a18fb09d878834 /libgo/go/encoding/xml
parentf507227a181bb31fa87d23a082485f99f3ef9183 (diff)
downloadppe42-gcc-fb08d0057f91d420b6f48c112264fc87dc91b532.tar.gz
ppe42-gcc-fb08d0057f91d420b6f48c112264fc87dc91b532.zip
libgo: Update to current sources.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@192704 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/encoding/xml')
-rw-r--r--libgo/go/encoding/xml/marshal.go36
-rw-r--r--libgo/go/encoding/xml/marshal_test.go56
-rw-r--r--libgo/go/encoding/xml/read.go18
-rw-r--r--libgo/go/encoding/xml/typeinfo.go27
-rw-r--r--libgo/go/encoding/xml/xml.go51
-rw-r--r--libgo/go/encoding/xml/xml_test.go84
6 files changed, 238 insertions, 34 deletions
diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go
index 6c3170bdda3..8592a0c15cb 100644
--- a/libgo/go/encoding/xml/marshal.go
+++ b/libgo/go/encoding/xml/marshal.go
@@ -57,8 +57,8 @@ const (
// if the field value is empty. The empty values are false, 0, any
// nil pointer or interface value, and any array, slice, map, or
// string of length zero.
-// - a non-pointer anonymous struct field is handled as if the
-// fields of its value were part of the outer struct.
+// - an anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
//
// If a field uses a tag "a>b>c", then the element c will be nested inside
// parent elements a and b. Fields that appear next to each other that name
@@ -83,9 +83,7 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
enc := NewEncoder(&b)
enc.prefix = prefix
enc.indent = indent
- err := enc.marshalValue(reflect.ValueOf(v), nil)
- enc.Flush()
- if err != nil {
+ if err := enc.Encode(v); err != nil {
return nil, err
}
return b.Bytes(), nil
@@ -107,8 +105,10 @@ func NewEncoder(w io.Writer) *Encoder {
// of Go values to XML.
func (enc *Encoder) Encode(v interface{}) error {
err := enc.marshalValue(reflect.ValueOf(v), nil)
- enc.Flush()
- return err
+ if err != nil {
+ return err
+ }
+ return enc.Flush()
}
type printer struct {
@@ -164,7 +164,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
xmlname := tinfo.xmlname
if xmlname.name != "" {
xmlns, name = xmlname.xmlns, xmlname.name
- } else if v, ok := val.FieldByIndex(xmlname.idx).Interface().(Name); ok && v.Local != "" {
+ } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
xmlns, name = v.Space, v.Local
}
}
@@ -195,7 +195,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
if finfo.flags&fAttr == 0 {
continue
}
- fv := val.FieldByIndex(finfo.idx)
+ fv := finfo.value(val)
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
continue
}
@@ -224,7 +224,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
p.WriteString(name)
p.WriteByte('>')
- return nil
+ return p.cachedWriteError()
}
var timeType = reflect.TypeOf(time.Time{})
@@ -260,15 +260,15 @@ func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error {
default:
return &UnsupportedTypeError{typ}
}
- return nil
+ return p.cachedWriteError()
}
var ddBytes = []byte("--")
func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
if val.Type() == timeType {
- p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
- return nil
+ _, err := p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
+ return err
}
s := parentStack{printer: p}
for i := range tinfo.fields {
@@ -276,7 +276,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
if finfo.flags&(fAttr|fAny) != 0 {
continue
}
- vf := val.FieldByIndex(finfo.idx)
+ vf := finfo.value(val)
switch finfo.flags & fMode {
case fCharData:
switch vf.Kind() {
@@ -353,7 +353,13 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
}
}
s.trim(nil)
- return nil
+ return p.cachedWriteError()
+}
+
+// return the bufio Writer's cached write error
+func (p *printer) cachedWriteError() error {
+ _, err := p.Write(nil)
+ return err
}
func (p *printer) writeIndent(depthDelta int) {
diff --git a/libgo/go/encoding/xml/marshal_test.go b/libgo/go/encoding/xml/marshal_test.go
index b6978a1e65b..e729a247af4 100644
--- a/libgo/go/encoding/xml/marshal_test.go
+++ b/libgo/go/encoding/xml/marshal_test.go
@@ -5,6 +5,9 @@
package xml
import (
+ "bytes"
+ "errors"
+ "io"
"reflect"
"strconv"
"strings"
@@ -108,7 +111,7 @@ type EmbedA struct {
type EmbedB struct {
FieldB string
- EmbedC
+ *EmbedC
}
type EmbedC struct {
@@ -493,7 +496,7 @@ var marshalTests = []struct {
},
EmbedB: EmbedB{
FieldB: "A.B.B",
- EmbedC: EmbedC{
+ EmbedC: &EmbedC{
FieldA1: "A.B.C.A1",
FieldA2: "A.B.C.A2",
FieldB: "", // Shadowed by A.B.B
@@ -779,6 +782,55 @@ func TestUnmarshal(t *testing.T) {
}
}
+type limitedBytesWriter struct {
+ w io.Writer
+ remain int // until writes fail
+}
+
+func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
+ if lw.remain <= 0 {
+ println("error")
+ return 0, errors.New("write limit hit")
+ }
+ if len(p) > lw.remain {
+ p = p[:lw.remain]
+ n, _ = lw.w.Write(p)
+ lw.remain = 0
+ return n, errors.New("write limit hit")
+ }
+ n, err = lw.w.Write(p)
+ lw.remain -= n
+ return n, err
+}
+
+func TestMarshalWriteErrors(t *testing.T) {
+ var buf bytes.Buffer
+ const writeCap = 1024
+ w := &limitedBytesWriter{&buf, writeCap}
+ enc := NewEncoder(w)
+ var err error
+ var i int
+ const n = 4000
+ for i = 1; i <= n; i++ {
+ err = enc.Encode(&Passenger{
+ Name: []string{"Alice", "Bob"},
+ Weight: 5,
+ })
+ if err != nil {
+ break
+ }
+ }
+ if err == nil {
+ t.Error("expected an error")
+ }
+ if i == n {
+ t.Errorf("expected to fail before the end")
+ }
+ if buf.Len() != writeCap {
+ t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
+ }
+}
+
func BenchmarkMarshal(b *testing.B) {
for i := 0; i < b.N; i++ {
Marshal(atomValue)
diff --git a/libgo/go/encoding/xml/read.go b/libgo/go/encoding/xml/read.go
index c2168242091..0e6761d66ad 100644
--- a/libgo/go/encoding/xml/read.go
+++ b/libgo/go/encoding/xml/read.go
@@ -81,8 +81,8 @@ import (
// of the above rules and the struct has a field with tag ",any",
// unmarshal maps the sub-element to that struct field.
//
-// * A non-pointer anonymous struct field is handled as if the
-// fields of its value were part of the outer struct.
+// * An anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
//
// * A struct field with tag "-" is never unmarshalled into.
//
@@ -248,7 +248,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
}
return UnmarshalError(e)
}
- fv := sv.FieldByIndex(finfo.idx)
+ fv := finfo.value(sv)
if _, ok := fv.Interface().(Name); ok {
fv.Set(reflect.ValueOf(start.Name))
}
@@ -260,7 +260,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
finfo := &tinfo.fields[i]
switch finfo.flags & fMode {
case fAttr:
- strv := sv.FieldByIndex(finfo.idx)
+ strv := finfo.value(sv)
// Look for attribute.
for _, a := range start.Attr {
if a.Name.Local == finfo.name {
@@ -271,22 +271,22 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
case fCharData:
if !saveData.IsValid() {
- saveData = sv.FieldByIndex(finfo.idx)
+ saveData = finfo.value(sv)
}
case fComment:
if !saveComment.IsValid() {
- saveComment = sv.FieldByIndex(finfo.idx)
+ saveComment = finfo.value(sv)
}
case fAny:
if !saveAny.IsValid() {
- saveAny = sv.FieldByIndex(finfo.idx)
+ saveAny = finfo.value(sv)
}
case fInnerXml:
if !saveXML.IsValid() {
- saveXML = sv.FieldByIndex(finfo.idx)
+ saveXML = finfo.value(sv)
if p.saved == nil {
saveXMLIndex = 0
p.saved = new(bytes.Buffer)
@@ -461,7 +461,7 @@ Loop:
}
if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
// It's a perfect match, unmarshal the field.
- return true, p.unmarshal(sv.FieldByIndex(finfo.idx), start)
+ return true, p.unmarshal(finfo.value(sv), 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 8e2e4508b10..970d1701932 100644
--- a/libgo/go/encoding/xml/typeinfo.go
+++ b/libgo/go/encoding/xml/typeinfo.go
@@ -66,10 +66,14 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
// For embedded structs, embed its fields.
if f.Anonymous {
- if f.Type.Kind() != reflect.Struct {
+ t := f.Type
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ if t.Kind() != reflect.Struct {
continue
}
- inner, err := getTypeInfo(f.Type)
+ inner, err := getTypeInfo(t)
if err != nil {
return nil, err
}
@@ -327,3 +331,22 @@ type TagPathError struct {
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)
}
+
+// 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 {
+ 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() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ v = v.Elem()
+ }
+ }
+ v = v.Field(x)
+ }
+ return v
+}
diff --git a/libgo/go/encoding/xml/xml.go b/libgo/go/encoding/xml/xml.go
index 5066f5c0106..fbd2208e334 100644
--- a/libgo/go/encoding/xml/xml.go
+++ b/libgo/go/encoding/xml/xml.go
@@ -584,6 +584,7 @@ func (d *Decoder) RawToken() (Token, error) {
if inquote == 0 && b == '>' && depth == 0 {
break
}
+ HandleB:
d.buf.WriteByte(b)
switch {
case b == inquote:
@@ -599,7 +600,35 @@ func (d *Decoder) RawToken() (Token, error) {
depth--
case b == '<' && inquote == 0:
- depth++
+ // Look for <!-- to begin comment.
+ s := "!--"
+ for i := 0; i < len(s); i++ {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b != s[i] {
+ for j := 0; j < i; j++ {
+ d.buf.WriteByte(s[j])
+ }
+ depth++
+ goto HandleB
+ }
+ }
+
+ // Remove < that was written above.
+ d.buf.Truncate(d.buf.Len() - 1)
+
+ // Look for terminator.
+ var b0, b1 byte
+ for {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b0 == '-' && b1 == '-' && b == '>' {
+ break
+ }
+ b0, b1 = b1, b
+ }
}
}
return Directive(d.buf.Bytes()), nil
@@ -850,6 +879,8 @@ Input:
// Parsers are required to recognize lt, gt, amp, apos, and quot
// even if they have not been declared. That's all we allow.
var i int
+ var semicolon bool
+ var valid bool
for i = 0; i < len(d.tmp); i++ {
var ok bool
d.tmp[i], ok = d.getc()
@@ -861,6 +892,8 @@ Input:
}
c := d.tmp[i]
if c == ';' {
+ semicolon = true
+ valid = i > 0
break
}
if 'a' <= c && c <= 'z' ||
@@ -873,14 +906,25 @@ Input:
break
}
s := string(d.tmp[0:i])
- if i >= len(d.tmp) {
+ if !valid {
if !d.Strict {
b0, b1 = 0, 0
d.buf.WriteByte('&')
d.buf.Write(d.tmp[0:i])
+ if semicolon {
+ d.buf.WriteByte(';')
+ }
continue Input
}
- d.err = d.syntaxError("character entity expression &" + s + "... too long")
+ semi := ";"
+ if !semicolon {
+ semi = " (no semicolon)"
+ }
+ if i < len(d.tmp) {
+ d.err = d.syntaxError("invalid character entity &" + s + semi)
+ } else {
+ d.err = d.syntaxError("invalid character entity &" + s + "... too long")
+ }
return nil
}
var haveText bool
@@ -910,6 +954,7 @@ Input:
b0, b1 = 0, 0
d.buf.WriteByte('&')
d.buf.Write(d.tmp[0:i])
+ d.buf.WriteByte(';')
continue Input
}
d.err = d.syntaxError("invalid character entity &" + s + ";")
diff --git a/libgo/go/encoding/xml/xml_test.go b/libgo/go/encoding/xml/xml_test.go
index 1d0696ce087..2ad4d4af5df 100644
--- a/libgo/go/encoding/xml/xml_test.go
+++ b/libgo/go/encoding/xml/xml_test.go
@@ -5,6 +5,7 @@
package xml
import (
+ "fmt"
"io"
"reflect"
"strings"
@@ -158,6 +159,39 @@ func TestRawToken(t *testing.T) {
testRawToken(t, d, rawTokens)
}
+const nonStrictInput = `
+<tag>non&entity</tag>
+<tag>&unknown;entity</tag>
+<tag>&#123</tag>
+<tag>&#zzz;</tag>
+`
+
+var nonStrictTokens = []Token{
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("non&entity"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&unknown;entity"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&#123"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&#zzz;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+}
+
+func TestNonStrictRawToken(t *testing.T) {
+ d := NewDecoder(strings.NewReader(nonStrictInput))
+ d.Strict = false
+ testRawToken(t, d, nonStrictTokens)
+}
+
type downCaser struct {
t *testing.T
r io.ByteReader
@@ -219,7 +253,18 @@ func testRawToken(t *testing.T, d *Decoder, rawTokens []Token) {
t.Fatalf("token %d: unexpected error: %s", i, err)
}
if !reflect.DeepEqual(have, want) {
- t.Errorf("token %d = %#v want %#v", i, have, want)
+ var shave, swant string
+ if _, ok := have.(CharData); ok {
+ shave = fmt.Sprintf("CharData(%q)", have)
+ } else {
+ shave = fmt.Sprintf("%#v", have)
+ }
+ if _, ok := want.(CharData); ok {
+ swant = fmt.Sprintf("CharData(%q)", want)
+ } else {
+ swant = fmt.Sprintf("%#v", want)
+ }
+ t.Errorf("token %d = %s, want %s", i, shave, swant)
}
}
}
@@ -531,8 +576,8 @@ var characterTests = []struct {
{"\xef\xbf\xbe<doc/>", "illegal character code U+FFFE"},
{"<?xml version=\"1.0\"?><doc>\r\n<hiya/>\x07<toots/></doc>", "illegal character code U+0007"},
{"<?xml version=\"1.0\"?><doc \x12='value'>what's up</doc>", "expected attribute name in element"},
- {"<doc>&\x01;</doc>", "invalid character entity &;"},
- {"<doc>&\xef\xbf\xbe;</doc>", "invalid character entity &;"},
+ {"<doc>&\x01;</doc>", "invalid character entity & (no semicolon)"},
+ {"<doc>&\xef\xbf\xbe;</doc>", "invalid character entity & (no semicolon)"},
}
func TestDisallowedCharacters(t *testing.T) {
@@ -576,3 +621,36 @@ func TestProcInstEncoding(t *testing.T) {
}
}
}
+
+// Ensure that directives with comments include the complete
+// text of any nested directives.
+
+var directivesWithCommentsInput = `
+<!DOCTYPE [<!-- a comment --><!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]>
+<!DOCTYPE [<!ENTITY go "Golang"><!-- a comment-->]>
+<!DOCTYPE <!-> <!> <!----> <!-->--> <!--->--> [<!ENTITY go "Golang"><!-- a comment-->]>
+`
+
+var directivesWithCommentsTokens = []Token{
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY go "Golang">]`),
+ CharData("\n"),
+ Directive(`DOCTYPE <!-> <!> [<!ENTITY go "Golang">]`),
+ CharData("\n"),
+}
+
+func TestDirectivesWithComments(t *testing.T) {
+ d := NewDecoder(strings.NewReader(directivesWithCommentsInput))
+
+ for i, want := range directivesWithCommentsTokens {
+ have, err := d.Token()
+ if err != nil {
+ t.Fatalf("token %d: unexpected error: %s", i, err)
+ }
+ if !reflect.DeepEqual(have, want) {
+ t.Errorf("token %d = %#v want %#v", i, have, want)
+ }
+ }
+}
OpenPOWER on IntegriCloud