diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-10-31 00:59:47 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-10-31 00:59:47 +0000 |
commit | 4a3da3a8a45d5496118798146de1fa4e5798ed5a (patch) | |
tree | 13beeaed3698c61903fe93fb1ce70bd9b18d4e7f /libgo/go/encoding/xml | |
parent | cd529f4d86a17a3e8959f2cb5ac7132a841ab6f1 (diff) | |
download | gcc-4a3da3a8a45d5496118798146de1fa4e5798ed5a.tar.gz |
runtime: Remove now unnecessary pad field from ParFor.
It is not needed due to the removal of the ctx field.
Reviewed-on: https://go-review.googlesource.com/16525
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@229616 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/encoding/xml')
-rw-r--r-- | libgo/go/encoding/xml/marshal.go | 59 | ||||
-rw-r--r-- | libgo/go/encoding/xml/marshal_test.go | 552 | ||||
-rw-r--r-- | libgo/go/encoding/xml/read_test.go | 2 | ||||
-rw-r--r-- | libgo/go/encoding/xml/xml.go | 44 | ||||
-rw-r--r-- | libgo/go/encoding/xml/xml_test.go | 21 |
5 files changed, 626 insertions, 52 deletions
diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go index 8c6342013d3..86d1422a5bd 100644 --- a/libgo/go/encoding/xml/marshal.go +++ b/libgo/go/encoding/xml/marshal.go @@ -173,6 +173,7 @@ func (enc *Encoder) EncodeElement(v interface{}, start StartElement) error { } var ( + begComment = []byte("<!--") endComment = []byte("-->") endProcInst = []byte("?>") endDirective = []byte(">") @@ -191,6 +192,7 @@ var ( // EncodeToken allows writing a ProcInst with Target set to "xml" only as the first token // in the stream. func (enc *Encoder) EncodeToken(t Token) error { + p := &enc.p switch t := t.(type) { case StartElement: @@ -202,7 +204,7 @@ func (enc *Encoder) EncodeToken(t Token) error { return err } case CharData: - EscapeText(p, t) + escapeText(p, t, false) case Comment: if bytes.Contains(t, endComment) { return fmt.Errorf("xml: EncodeToken of Comment containing --> marker") @@ -231,16 +233,59 @@ func (enc *Encoder) EncodeToken(t Token) error { } p.WriteString("?>") case Directive: - if bytes.Contains(t, endDirective) { - return fmt.Errorf("xml: EncodeToken of Directive containing > marker") + if !isValidDirective(t) { + return fmt.Errorf("xml: EncodeToken of Directive containing wrong < or > markers") } p.WriteString("<!") p.Write(t) p.WriteString(">") + default: + return fmt.Errorf("xml: EncodeToken of invalid token type") + } return p.cachedWriteError() } +// isValidDirective reports whether dir is a valid directive text, +// meaning angle brackets are matched, ignoring comments and strings. +func isValidDirective(dir Directive) bool { + var ( + depth int + inquote uint8 + incomment bool + ) + for i, c := range dir { + switch { + case incomment: + if c == '>' { + if n := 1 + i - len(endComment); n >= 0 && bytes.Equal(dir[n:i+1], endComment) { + incomment = false + } + } + // Just ignore anything in comment + case inquote != 0: + if c == inquote { + inquote = 0 + } + // Just ignore anything within quotes + case c == '\'' || c == '"': + inquote = c + case c == '<': + if i+len(begComment) < len(dir) && bytes.Equal(dir[i:i+len(begComment)], begComment) { + incomment = true + } else { + depth++ + } + case c == '>': + if depth == 0 { + return false + } + depth-- + } + } + return depth == 0 && inquote == 0 && !incomment +} + // Flush flushes any buffered XML to the underlying writer. // See the EncodeToken documentation for details about when it is necessary. func (enc *Encoder) Flush() error { @@ -724,6 +769,9 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { switch finfo.flags & fMode { case fCharData: + if err := s.trim(finfo.parents); err != nil { + return err + } if vf.CanInterface() && vf.Type().Implements(textMarshalerType) { data, err := vf.Interface().(encoding.TextMarshaler).MarshalText() if err != nil { @@ -767,6 +815,9 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { continue case fComment: + if err := s.trim(finfo.parents); err != nil { + return err + } k := vf.Kind() if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) { return fmt.Errorf("xml: bad type for comment field of %s", val.Type()) @@ -894,7 +945,7 @@ func (s *parentStack) trim(parents []string) error { return err } } - s.stack = parents[:split] + s.stack = s.stack[:split] return nil } diff --git a/libgo/go/encoding/xml/marshal_test.go b/libgo/go/encoding/xml/marshal_test.go index 14f73a75d5f..66675d7abc4 100644 --- a/libgo/go/encoding/xml/marshal_test.go +++ b/libgo/go/encoding/xml/marshal_test.go @@ -12,6 +12,7 @@ import ( "reflect" "strconv" "strings" + "sync" "testing" "time" ) @@ -339,6 +340,16 @@ type OuterOuterStruct struct { OuterStruct } +type NestedAndChardata struct { + AB []string `xml:"A>B"` + Chardata string `xml:",chardata"` +} + +type NestedAndComment struct { + AB []string `xml:"A>B"` + Comment string `xml:",comment"` +} + func ifaceptr(x interface{}) interface{} { return &x } @@ -617,6 +628,69 @@ var marshalTests = []struct { `</service>`, MarshalOnly: true, }, + { + Value: &struct { + XMLName struct{} `xml:"space top"` + A string `xml:"x>a"` + B string `xml:"x>b"` + C string `xml:"space x>c"` + C1 string `xml:"space1 x>c"` + D1 string `xml:"space1 x>d"` + }{ + A: "a", + B: "b", + C: "c", + C1: "c1", + D1: "d1", + }, + ExpectXML: `<top xmlns="space">` + + `<x><a>a</a><b>b</b><c xmlns="space">c</c>` + + `<c xmlns="space1">c1</c>` + + `<d xmlns="space1">d1</d>` + + `</x>` + + `</top>`, + }, + { + Value: &struct { + XMLName Name + A string `xml:"x>a"` + B string `xml:"x>b"` + C string `xml:"space x>c"` + C1 string `xml:"space1 x>c"` + D1 string `xml:"space1 x>d"` + }{ + XMLName: Name{ + Space: "space0", + Local: "top", + }, + A: "a", + B: "b", + C: "c", + C1: "c1", + D1: "d1", + }, + ExpectXML: `<top xmlns="space0">` + + `<x><a>a</a><b>b</b>` + + `<c xmlns="space">c</c>` + + `<c xmlns="space1">c1</c>` + + `<d xmlns="space1">d1</d>` + + `</x>` + + `</top>`, + }, + { + Value: &struct { + XMLName struct{} `xml:"top"` + B string `xml:"space x>b"` + B1 string `xml:"space1 x>b"` + }{ + B: "b", + B1: "b1", + }, + ExpectXML: `<top>` + + `<x><b xmlns="space">b</b>` + + `<b xmlns="space1">b1</b></x>` + + `</top>`, + }, // Test struct embedding { @@ -924,6 +998,14 @@ var marshalTests = []struct { ExpectXML: `<outer xmlns="testns" int="10"></outer>`, Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}}, }, + { + ExpectXML: `<NestedAndChardata><A><B></B><B></B></A>test</NestedAndChardata>`, + Value: &NestedAndChardata{AB: make([]string, 2), Chardata: "test"}, + }, + { + ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`, + Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"}, + }, } func TestMarshal(t *testing.T) { @@ -933,7 +1015,7 @@ func TestMarshal(t *testing.T) { } data, err := Marshal(test.Value) if err != nil { - t.Errorf("#%d: Error: %s", idx, err) + t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err) continue } if got, want := string(data), test.ExpectXML; got != want { @@ -1037,6 +1119,14 @@ func TestUnmarshal(t *testing.T) { if _, ok := test.Value.(*Plain); ok { continue } + if test.ExpectXML == `<top>`+ + `<x><b xmlns="space">b</b>`+ + `<b xmlns="space1">b1</b></x>`+ + `</top>` { + // TODO(rogpeppe): re-enable this test in + // https://go-review.googlesource.com/#/c/5910/ + continue + } vt := reflect.TypeOf(test.Value) dest := reflect.New(vt.Elem()).Interface() @@ -1148,12 +1238,14 @@ func TestMarshalFlush(t *testing.T) { } func BenchmarkMarshal(b *testing.B) { + b.ReportAllocs() for i := 0; i < b.N; i++ { Marshal(atomValue) } } func BenchmarkUnmarshal(b *testing.B) { + b.ReportAllocs() xml := []byte(atomXml) for i := 0; i < b.N; i++ { Unmarshal(xml, &Feed{}) @@ -1192,41 +1284,369 @@ func TestStructPointerMarshal(t *testing.T) { } var encodeTokenTests = []struct { - tok Token + desc string + toks []Token want string - ok bool -}{ - {StartElement{Name{"space", "local"}, nil}, "<local xmlns=\"space\">", true}, - {StartElement{Name{"space", ""}, nil}, "", false}, - {EndElement{Name{"space", ""}}, "", false}, - {CharData("foo"), "foo", true}, - {Comment("foo"), "<!--foo-->", true}, - {Comment("foo-->"), "", false}, - {ProcInst{"Target", []byte("Instruction")}, "<?Target Instruction?>", true}, - {ProcInst{"", []byte("Instruction")}, "", false}, - {ProcInst{"Target", []byte("Instruction?>")}, "", false}, - {Directive("foo"), "<!foo>", true}, - {Directive("foo>"), "", false}, -} + err string +}{{ + desc: "start element with name space", + toks: []Token{ + StartElement{Name{"space", "local"}, nil}, + }, + want: `<local xmlns="space">`, +}, { + desc: "start element with no name", + toks: []Token{ + StartElement{Name{"space", ""}, nil}, + }, + err: "xml: start tag with no name", +}, { + desc: "end element with no name", + toks: []Token{ + EndElement{Name{"space", ""}}, + }, + err: "xml: end tag with no name", +}, { + desc: "char data", + toks: []Token{ + CharData("foo"), + }, + want: `foo`, +}, { + desc: "char data with escaped chars", + toks: []Token{ + CharData(" \t\n"), + }, + want: " 	\n", +}, { + desc: "comment", + toks: []Token{ + Comment("foo"), + }, + want: `<!--foo-->`, +}, { + desc: "comment with invalid content", + toks: []Token{ + Comment("foo-->"), + }, + err: "xml: EncodeToken of Comment containing --> marker", +}, { + desc: "proc instruction", + toks: []Token{ + ProcInst{"Target", []byte("Instruction")}, + }, + want: `<?Target Instruction?>`, +}, { + desc: "proc instruction with empty target", + toks: []Token{ + ProcInst{"", []byte("Instruction")}, + }, + err: "xml: EncodeToken of ProcInst with invalid Target", +}, { + desc: "proc instruction with bad content", + toks: []Token{ + ProcInst{"", []byte("Instruction?>")}, + }, + err: "xml: EncodeToken of ProcInst with invalid Target", +}, { + desc: "directive", + toks: []Token{ + Directive("foo"), + }, + want: `<!foo>`, +}, { + desc: "more complex directive", + toks: []Token{ + Directive("DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]"), + }, + want: `<!DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]>`, +}, { + desc: "directive instruction with bad name", + toks: []Token{ + Directive("foo>"), + }, + err: "xml: EncodeToken of Directive containing wrong < or > markers", +}, { + desc: "end tag without start tag", + toks: []Token{ + EndElement{Name{"foo", "bar"}}, + }, + err: "xml: end tag </bar> without start tag", +}, { + desc: "mismatching end tag local name", + toks: []Token{ + StartElement{Name{"", "foo"}, nil}, + EndElement{Name{"", "bar"}}, + }, + err: "xml: end tag </bar> does not match start tag <foo>", + want: `<foo>`, +}, { + desc: "mismatching end tag namespace", + toks: []Token{ + StartElement{Name{"space", "foo"}, nil}, + EndElement{Name{"another", "foo"}}, + }, + err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space", + want: `<foo xmlns="space">`, +}, { + desc: "start element with explicit namespace", + toks: []Token{ + StartElement{Name{"space", "local"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + {Name{"space", "foo"}, "value"}, + }}, + }, + want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value">`, +}, { + desc: "start element with explicit namespace and colliding prefix", + toks: []Token{ + StartElement{Name{"space", "local"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + {Name{"space", "foo"}, "value"}, + {Name{"x", "bar"}, "other"}, + }}, + }, + want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value" xmlns:x="x" x:bar="other">`, +}, { + desc: "start element using previously defined namespace", + toks: []Token{ + StartElement{Name{"", "local"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"space", "x"}, "y"}, + }}, + }, + want: `<local xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns:space="space" space:x="y">`, +}, { + desc: "nested name space with same prefix", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "x"}, "space1"}, + }}, + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "x"}, "space2"}, + }}, + StartElement{Name{"", "foo"}, []Attr{ + {Name{"space1", "a"}, "space1 value"}, + {Name{"space2", "b"}, "space2 value"}, + }}, + EndElement{Name{"", "foo"}}, + EndElement{Name{"", "foo"}}, + StartElement{Name{"", "foo"}, []Attr{ + {Name{"space1", "a"}, "space1 value"}, + {Name{"space2", "b"}, "space2 value"}, + }}, + }, + want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space1"><foo _xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value"></foo></foo><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value">`, +}, { + desc: "start element defining several prefixes for the same name space", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"xmlns", "a"}, "space"}, + {Name{"xmlns", "b"}, "space"}, + {Name{"space", "x"}, "value"}, + }}, + }, + want: `<foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:a="space" _xmlns:b="space" xmlns:space="space" space:x="value">`, +}, { + desc: "nested element redefines name space", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"xmlns", "y"}, "space"}, + {Name{"space", "a"}, "value"}, + }}, + }, + want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" _xmlns:y="space" xmlns:space="space" space:a="value">`, +}, { + desc: "nested element creates alias for default name space", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"xmlns", "y"}, "space"}, + {Name{"space", "a"}, "value"}, + }}, + }, + want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:y="space" xmlns:space="space" space:a="value">`, +}, { + desc: "nested element defines default name space with existing prefix", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + {Name{"space", "a"}, "value"}, + }}, + }, + want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns="space" xmlns:space="space" space:a="value">`, +}, { + desc: "nested element uses empty attribute name space when default ns defined", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "attr"}, "value"}, + }}, + }, + want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" attr="value">`, +}, { + desc: "redefine xmlns", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"foo", "xmlns"}, "space"}, + }}, + }, + want: `<foo xmlns:foo="foo" foo:xmlns="space">`, +}, { + desc: "xmlns with explicit name space #1", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"xml", "xmlns"}, "space"}, + }}, + }, + want: `<foo xmlns="space" xmlns:_xml="xml" _xml:xmlns="space">`, +}, { + desc: "xmlns with explicit name space #2", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{xmlURL, "xmlns"}, "space"}, + }}, + }, + want: `<foo xmlns="space" xml:xmlns="space">`, +}, { + desc: "empty name space declaration is ignored", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "foo"}, ""}, + }}, + }, + want: `<foo xmlns:_xmlns="xmlns" _xmlns:foo="">`, +}, { + desc: "attribute with no name is ignored", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"", ""}, "value"}, + }}, + }, + want: `<foo>`, +}, { + desc: "namespace URL with non-valid name", + toks: []Token{ + StartElement{Name{"/34", "foo"}, []Attr{ + {Name{"/34", "x"}, "value"}, + }}, + }, + want: `<foo xmlns="/34" xmlns:_="/34" _:x="value">`, +}, { + desc: "nested element resets default namespace to empty", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"", "foo"}, []Attr{ + {Name{"", "xmlns"}, ""}, + {Name{"", "x"}, "value"}, + {Name{"space", "x"}, "value"}, + }}, + }, + want: `<foo xmlns="space" xmlns="space"><foo xmlns="" x="value" xmlns:space="space" space:x="value">`, +}, { + desc: "nested element requires empty default name space", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"", "foo"}, nil}, + }, + want: `<foo xmlns="space" xmlns="space"><foo>`, +}, { + desc: "attribute uses name space from xmlns", + toks: []Token{ + StartElement{Name{"some/space", "foo"}, []Attr{ + {Name{"", "attr"}, "value"}, + {Name{"some/space", "other"}, "other value"}, + }}, + }, + want: `<foo xmlns="some/space" attr="value" xmlns:space="some/space" space:other="other value">`, +}, { + desc: "default name space should not be used by attributes", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + {Name{"xmlns", "bar"}, "space"}, + {Name{"space", "baz"}, "foo"}, + }}, + StartElement{Name{"space", "baz"}, nil}, + EndElement{Name{"space", "baz"}}, + EndElement{Name{"space", "foo"}}, + }, + want: `<foo xmlns="space" xmlns="space" xmlns:_xmlns="xmlns" _xmlns:bar="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`, +}, { + desc: "default name space not used by attributes, not explicitly defined", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + {Name{"space", "baz"}, "foo"}, + }}, + StartElement{Name{"space", "baz"}, nil}, + EndElement{Name{"space", "baz"}}, + EndElement{Name{"space", "foo"}}, + }, + want: `<foo xmlns="space" xmlns="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`, +}, { + desc: "impossible xmlns declaration", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"space", "bar"}, []Attr{ + {Name{"space", "attr"}, "value"}, + }}, + }, + want: `<foo xmlns="space"><bar xmlns="space" xmlns:space="space" space:attr="value">`, +}} func TestEncodeToken(t *testing.T) { - for _, tt := range encodeTokenTests { +loop: + for i, tt := range encodeTokenTests { var buf bytes.Buffer enc := NewEncoder(&buf) - err := enc.EncodeToken(tt.tok) + var err error + for j, tok := range tt.toks { + err = enc.EncodeToken(tok) + if err != nil && j < len(tt.toks)-1 { + t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err) + continue loop + } + } + errorf := func(f string, a ...interface{}) { + t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...)) + } switch { - case !tt.ok && err == nil: - t.Errorf("enc.EncodeToken(%#v): expected error; got none", tt.tok) - case tt.ok && err != nil: - t.Fatalf("enc.EncodeToken: %v", err) - case !tt.ok && err != nil: - // expected error, got one + case tt.err != "" && err == nil: + errorf(" expected error; got none") + continue + case tt.err == "" && err != nil: + errorf(" got error: %v", err) + continue + case tt.err != "" && err != nil && tt.err != err.Error(): + errorf(" error mismatch; got %v, want %v", err, tt.err) + continue } if err := enc.Flush(); err != nil { - t.Fatalf("enc.EncodeToken: %v", err) + errorf(" %v", err) + continue } if got := buf.String(); got != tt.want { - t.Errorf("enc.EncodeToken = %s; want: %s", got, tt.want) + errorf("\ngot %v\nwant %v", got, tt.want) + continue } } } @@ -1264,3 +1684,83 @@ func TestDecodeEncode(t *testing.T) { } } } + +// Issue 9796. Used to fail with GORACE="halt_on_error=1" -race. +func TestRace9796(t *testing.T) { + type A struct{} + type B struct { + C []A `xml:"X>Y"` + } + var wg sync.WaitGroup + for i := 0; i < 2; i++ { + wg.Add(1) + go func() { + Marshal(B{[]A{A{}}}) + wg.Done() + }() + } + wg.Wait() +} + +func TestIsValidDirective(t *testing.T) { + testOK := []string{ + "<>", + "< < > >", + "<!DOCTYPE '<' '>' '>' <!--nothing-->>", + "<!DOCTYPE doc [ <!ELEMENT doc ANY> <!ELEMENT doc ANY> ]>", + "<!DOCTYPE doc [ <!ELEMENT doc \"ANY> '<' <!E\" LEMENT '>' doc ANY> ]>", + "<!DOCTYPE doc <!-- just>>>> a < comment --> [ <!ITEM anything> ] >", + } + testKO := []string{ + "<", + ">", + "<!--", + "-->", + "< > > < < >", + "<!dummy <!-- > -->", + "<!DOCTYPE doc '>", + "<!DOCTYPE doc '>'", + "<!DOCTYPE doc <!--comment>", + } + for _, s := range testOK { + if !isValidDirective(Directive(s)) { + t.Errorf("Directive %q is expected to be valid", s) + } + } + for _, s := range testKO { + if isValidDirective(Directive(s)) { + t.Errorf("Directive %q is expected to be invalid", s) + } + } +} + +// Issue 11719. EncodeToken used to silently eat tokens with an invalid type. +func TestSimpleUseOfEncodeToken(t *testing.T) { + var buf bytes.Buffer + enc := NewEncoder(&buf) + if err := enc.EncodeToken(&StartElement{Name: Name{"", "object1"}}); err == nil { + t.Errorf("enc.EncodeToken: pointer type should be rejected") + } + if err := enc.EncodeToken(&EndElement{Name: Name{"", "object1"}}); err == nil { + t.Errorf("enc.EncodeToken: pointer type should be rejected") + } + if err := enc.EncodeToken(StartElement{Name: Name{"", "object2"}}); err != nil { + t.Errorf("enc.EncodeToken: StartElement %s", err) + } + if err := enc.EncodeToken(EndElement{Name: Name{"", "object2"}}); err != nil { + t.Errorf("enc.EncodeToken: EndElement %s", err) + } + if err := enc.EncodeToken(Universe{}); err == nil { + t.Errorf("enc.EncodeToken: invalid type not caught") + } + if err := enc.Flush(); err != nil { + t.Errorf("enc.Flush: %s", err) + } + if buf.Len() == 0 { + t.Errorf("enc.EncodeToken: empty buffer") + } + want := "<object2></object2>" + if buf.String() != want { + t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String()) + } +} diff --git a/libgo/go/encoding/xml/read_test.go b/libgo/go/encoding/xml/read_test.go index 01f55d0dd00..7d004dc488c 100644 --- a/libgo/go/encoding/xml/read_test.go +++ b/libgo/go/encoding/xml/read_test.go @@ -694,7 +694,7 @@ type Pod struct { Pea interface{} `xml:"Pea"` } -// https://code.google.com/p/go/issues/detail?id=6836 +// https://golang.org/issue/6836 func TestUnmarshalIntoInterface(t *testing.T) { pod := new(Pod) pod.Pea = new(Pea) diff --git a/libgo/go/encoding/xml/xml.go b/libgo/go/encoding/xml/xml.go index 8c15b98c3a9..0a21c930531 100644 --- a/libgo/go/encoding/xml/xml.go +++ b/libgo/go/encoding/xml/xml.go @@ -549,7 +549,6 @@ func (d *Decoder) rawToken() (Token, error) { case '?': // <?: Processing instruction. - // TODO(rsc): Should parse the <?xml declaration to make sure the version is 1.0. var target string if target, ok = d.name(); !ok { if d.err == nil { @@ -574,7 +573,13 @@ func (d *Decoder) rawToken() (Token, error) { data = data[0 : len(data)-2] // chop ?> if target == "xml" { - enc := procInstEncoding(string(data)) + content := string(data) + ver := procInst("version", content) + if ver != "" && ver != "1.0" { + d.err = fmt.Errorf("xml: unsupported version %q; only version 1.0 is supported", ver) + return nil, d.err + } + enc := procInst("encoding", content) if enc != "" && enc != "utf-8" && enc != "UTF-8" { if d.CharsetReader == nil { d.err = fmt.Errorf("xml: encoding %q declared but Decoder.CharsetReader is nil", enc) @@ -723,7 +728,7 @@ func (d *Decoder) rawToken() (Token, error) { return nil, d.err } - attr = make([]Attr, 0, 4) + attr = []Attr{} for { d.space() if b, ok = d.mustgetc(); !ok { @@ -747,7 +752,11 @@ func (d *Decoder) rawToken() (Token, error) { n := len(attr) if n >= cap(attr) { - nattr := make([]Attr, n, 2*cap(attr)) + nCap := 2 * cap(attr) + if nCap == 0 { + nCap = 4 + } + nattr := make([]Attr, n, nCap) copy(nattr, attr) attr = nattr } @@ -1119,12 +1128,12 @@ func (d *Decoder) name() (s string, ok bool) { } // Now we check the characters. - s = d.buf.String() - if !isName([]byte(s)) { - d.err = d.syntaxError("invalid XML name: " + s) + b := d.buf.Bytes() + if !isName(b) { + d.err = d.syntaxError("invalid XML name: " + string(b)) return "", false } - return s, true + return string(b), true } // Read a name and append its bytes to d.buf. @@ -1832,6 +1841,13 @@ var ( // EscapeText writes to w the properly escaped XML equivalent // of the plain text data s. func EscapeText(w io.Writer, s []byte) error { + return escapeText(w, s, true) +} + +// escapeText writes to w the properly escaped XML equivalent +// of the plain text data s. If escapeNewline is true, newline +// characters will be escaped. +func escapeText(w io.Writer, s []byte, escapeNewline bool) error { var esc []byte last := 0 for i := 0; i < len(s); { @@ -1851,6 +1867,9 @@ func EscapeText(w io.Writer, s []byte) error { case '\t': esc = esc_tab case '\n': + if !escapeNewline { + continue + } esc = esc_nl case '\r': esc = esc_cr @@ -1921,16 +1940,17 @@ func Escape(w io.Writer, s []byte) { EscapeText(w, s) } -// procInstEncoding parses the `encoding="..."` or `encoding='...'` +// procInst parses the `param="..."` or `param='...'` // value out of the provided string, returning "" if not found. -func procInstEncoding(s string) string { +func procInst(param, s string) string { // TODO: this parsing is somewhat lame and not exact. // It works for all actual cases, though. - idx := strings.Index(s, "encoding=") + param = param + "=" + idx := strings.Index(s, param) if idx == -1 { return "" } - v := s[idx+len("encoding="):] + v := s[idx+len(param):] if v == "" { return "" } diff --git a/libgo/go/encoding/xml/xml_test.go b/libgo/go/encoding/xml/xml_test.go index be995c0d52c..312a7c98a5c 100644 --- a/libgo/go/encoding/xml/xml_test.go +++ b/libgo/go/encoding/xml/xml_test.go @@ -657,20 +657,23 @@ type procInstEncodingTest struct { } var procInstTests = []struct { - input, expect string + input string + expect [2]string }{ - {`version="1.0" encoding="utf-8"`, "utf-8"}, - {`version="1.0" encoding='utf-8'`, "utf-8"}, - {`version="1.0" encoding='utf-8' `, "utf-8"}, - {`version="1.0" encoding=utf-8`, ""}, - {`encoding="FOO" `, "FOO"}, + {`version="1.0" encoding="utf-8"`, [2]string{"1.0", "utf-8"}}, + {`version="1.0" encoding='utf-8'`, [2]string{"1.0", "utf-8"}}, + {`version="1.0" encoding='utf-8' `, [2]string{"1.0", "utf-8"}}, + {`version="1.0" encoding=utf-8`, [2]string{"1.0", ""}}, + {`encoding="FOO" `, [2]string{"", "FOO"}}, } func TestProcInstEncoding(t *testing.T) { for _, test := range procInstTests { - got := procInstEncoding(test.input) - if got != test.expect { - t.Errorf("procInstEncoding(%q) = %q; want %q", test.input, got, test.expect) + if got := procInst("version", test.input); got != test.expect[0] { + t.Errorf("procInst(version, %q) = %q; want %q", test.input, got, test.expect[0]) + } + if got := procInst("encoding", test.input); got != test.expect[1] { + t.Errorf("procInst(encoding, %q) = %q; want %q", test.input, got, test.expect[1]) } } } |