diff options
author | Ian Lance Taylor <iant@google.com> | 2011-01-21 18:19:03 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-01-21 18:19:03 +0000 |
commit | ff5f50c52c421d75940ef9392211e3ab24d71332 (patch) | |
tree | 27d8768fb1d25696d3c40b42535eb5e073c278da /libgo/go/xml | |
parent | d6ed1c8903e728f4233122554bab5910853338bd (diff) | |
download | gcc-ff5f50c52c421d75940ef9392211e3ab24d71332.zip gcc-ff5f50c52c421d75940ef9392211e3ab24d71332.tar.gz gcc-ff5f50c52c421d75940ef9392211e3ab24d71332.tar.bz2 |
Remove the types float and complex.
Update to current version of Go library.
Update testsuite for removed types.
* go-lang.c (go_langhook_init): Omit float_type_size when calling
go_create_gogo.
* go-c.h: Update declaration of go_create_gogo.
From-SVN: r169098
Diffstat (limited to 'libgo/go/xml')
-rw-r--r-- | libgo/go/xml/read.go | 139 | ||||
-rw-r--r-- | libgo/go/xml/read_test.go | 97 | ||||
-rw-r--r-- | libgo/go/xml/xml.go | 28 | ||||
-rw-r--r-- | libgo/go/xml/xml_test.go | 45 |
4 files changed, 300 insertions, 9 deletions
diff --git a/libgo/go/xml/read.go b/libgo/go/xml/read.go index bbceda6..9ae3bb8 100644 --- a/libgo/go/xml/read.go +++ b/libgo/go/xml/read.go @@ -6,6 +6,7 @@ package xml import ( "bytes" + "fmt" "io" "os" "reflect" @@ -39,6 +40,7 @@ import ( // Name string // Phone string // Email []Email +// Groups []string "group>value" // } // // result := Result{Name: "name", Phone: "phone", Email: nil} @@ -53,6 +55,10 @@ import ( // <addr>gre@work.com</addr> // </email> // <name>Grace R. Emlin</name> +// <group> +// <value>Friends</value> +// <value>Squash</value> +// </group> // <address>123 Main Street</address> // </result> // @@ -65,10 +71,13 @@ import ( // Email{"home", "gre@example.com"}, // Email{"work", "gre@work.com"}, // }, +// []string{"Friends", "Squash"}, // } // // Note that the field r.Phone has not been modified and -// that the XML <address> element was discarded. +// that the XML <address> element was discarded. Also, the field +// Groups was assigned considering the element path provided in the +// field tag. // // Because Unmarshal uses the reflect package, it can only // assign to upper case fields. Unmarshal uses a case-insensitive @@ -97,6 +106,13 @@ import ( // The struct field may have type []byte or string. // If there is no such field, the character data is discarded. // +// * If the XML element contains a sub-element whose name matches +// the prefix of a struct field tag formatted as "a>b>c", unmarshal +// will descend into the XML structure looking for elements with the +// given names, and will map the innermost elements to that struct field. +// A struct field tag starting with ">" is equivalent to one starting +// with the field name followed by ">". +// // * If the XML element contains a sub-element whose name // matches a struct field whose tag is neither "attr" nor "chardata", // Unmarshal maps the sub-element to that struct field. @@ -104,7 +120,7 @@ import ( // maps the sub-element to that struct field. // // Unmarshal maps an XML element to a string or []byte by saving the -// concatenation of that elements character data in the string or []byte. +// concatenation of that element's character data in the string or []byte. // // Unmarshal maps an XML element to a slice by extending the length // of the slice and mapping the element to the newly created value. @@ -141,6 +157,18 @@ type UnmarshalError string func (e UnmarshalError) String() string { return string(e) } +// A TagPathError represents an error in the unmarshalling process +// caused by the use of field tags with conflicting paths. +type TagPathError struct { + Struct reflect.Type + Field1, Tag1 string + Field2, Tag2 string +} + +func (e *TagPathError) String() 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) +} + // The Parser's Unmarshal method is like xml.Unmarshal // except that it can be passed a pointer to the initial start element, // useful when a client reads some raw XML tokens itself @@ -211,7 +239,9 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { saveXMLData []byte sv *reflect.StructValue styp *reflect.StructType + fieldPaths map[string]pathInfo ) + switch v := val.(type) { default: return os.ErrorString("unknown type " + v.Type().String()) @@ -233,7 +263,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { ncap = 4 } new := reflect.MakeSlice(typ, n, ncap) - reflect.ArrayCopy(new, v) + reflect.Copy(new, v) v.Set(new) } v.SetLen(n + 1) @@ -330,6 +360,24 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { saveXMLIndex = p.savedOffset() } } + + default: + if strings.Contains(f.Tag, ">") { + if fieldPaths == nil { + fieldPaths = make(map[string]pathInfo) + } + path := strings.ToLower(f.Tag) + if strings.HasPrefix(f.Tag, ">") { + path = strings.ToLower(f.Name) + path + } + if strings.HasSuffix(f.Tag, ">") { + path = path[:len(path)-1] + } + err := addFieldPath(sv, fieldPaths, path, f.Index) + if err != nil { + return err + } + } } } } @@ -352,9 +400,19 @@ Loop: // Look up by tag name. if sv != nil { k := fieldName(t.Name.Local) + + if fieldPaths != nil { + if _, found := fieldPaths[k]; found { + if err := p.unmarshalPaths(sv, fieldPaths, k, &t); err != nil { + return err + } + continue Loop + } + } + match := func(s string) bool { // check if the name matches ignoring case - if strings.ToLower(s) != strings.ToLower(k) { + if strings.ToLower(s) != k { return false } // now check that it's public @@ -389,12 +447,12 @@ Loop: case CharData: if saveData != nil { - data = bytes.Add(data, t) + data = append(data, t...) } case Comment: if saveComment != nil { - comment = bytes.Add(comment, t) + comment = append(comment, t...) } } } @@ -470,6 +528,75 @@ Loop: return nil } +type pathInfo struct { + fieldIdx []int + complete bool +} + +// addFieldPath takes an element path such as "a>b>c" and fills the +// paths map with all paths leading to it ("a", "a>b", and "a>b>c"). +// It is okay for paths to share a common, shorter prefix but not ok +// for one path to itself be a prefix of another. +func addFieldPath(sv *reflect.StructValue, paths map[string]pathInfo, path string, fieldIdx []int) os.Error { + if info, found := paths[path]; found { + return tagError(sv, info.fieldIdx, fieldIdx) + } + paths[path] = pathInfo{fieldIdx, true} + for { + i := strings.LastIndex(path, ">") + if i < 0 { + break + } + path = path[:i] + if info, found := paths[path]; found { + if info.complete { + return tagError(sv, info.fieldIdx, fieldIdx) + } + } else { + paths[path] = pathInfo{fieldIdx, false} + } + } + return nil + +} + +func tagError(sv *reflect.StructValue, idx1 []int, idx2 []int) os.Error { + t := sv.Type().(*reflect.StructType) + f1 := t.FieldByIndex(idx1) + f2 := t.FieldByIndex(idx2) + return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag} +} + +// unmarshalPaths walks down an XML structure looking for +// wanted paths, and calls unmarshal on them. +func (p *Parser) unmarshalPaths(sv *reflect.StructValue, paths map[string]pathInfo, path string, start *StartElement) os.Error { + if info, _ := paths[path]; info.complete { + return p.unmarshal(sv.FieldByIndex(info.fieldIdx), start) + } + for { + tok, err := p.Token() + if err != nil { + return err + } + switch t := tok.(type) { + case StartElement: + k := path + ">" + fieldName(t.Name.Local) + if _, found := paths[k]; found { + if err := p.unmarshalPaths(sv, paths, k, &t); err != nil { + return err + } + continue + } + if err := p.Skip(); err != nil { + return err + } + case EndElement: + return nil + } + } + panic("unreachable") +} + // Have already read a start element. // Read tokens until we find the end element. // Token is taking care of making sure the diff --git a/libgo/go/xml/read_test.go b/libgo/go/xml/read_test.go index 9ec1065..71ceddc 100644 --- a/libgo/go/xml/read_test.go +++ b/libgo/go/xml/read_test.go @@ -230,3 +230,100 @@ func TestFieldName(t *testing.T) { } } } + +const pathTestString = ` +<result> + <before>1</before> + <items> + <item1> + <value>A</value> + </item1> + <item2> + <value>B</value> + </item2> + <Item1> + <Value>C</Value> + <Value>D</Value> + </Item1> + </items> + <after>2</after> +</result> +` + +type PathTestItem struct { + Value string +} + +type PathTestA struct { + Items []PathTestItem ">item1" + Before, After string +} + +type PathTestB struct { + Other []PathTestItem "items>Item1" + Before, After string +} + +type PathTestC struct { + Values1 []string "items>item1>value" + Values2 []string "items>item2>value" + Before, After string +} + +type PathTestSet struct { + Item1 []PathTestItem +} + +type PathTestD struct { + Other PathTestSet "items>" + Before, After string +} + +var pathTests = []interface{}{ + &PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"}, + &PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"}, + &PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"}, + &PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"}, +} + +func TestUnmarshalPaths(t *testing.T) { + for _, pt := range pathTests { + p := reflect.MakeZero(reflect.NewValue(pt).Type()).(*reflect.PtrValue) + p.PointTo(reflect.MakeZero(p.Type().(*reflect.PtrType).Elem())) + v := p.Interface() + if err := Unmarshal(StringReader(pathTestString), v); err != nil { + t.Fatalf("Unmarshal: %s", err) + } + if !reflect.DeepEqual(v, pt) { + t.Fatalf("have %#v\nwant %#v", v, pt) + } + } +} + +type BadPathTestA struct { + First string "items>item1" + Other string "items>item2" + Second string "items>" +} + +type BadPathTestB struct { + Other string "items>item2>value" + First string "items>item1" + Second string "items>item1>value" +} + +var badPathTests = []struct { + v, e interface{} +}{ + {&BadPathTestA{}, &TagPathError{reflect.Typeof(BadPathTestA{}), "First", "items>item1", "Second", "items>"}}, + {&BadPathTestB{}, &TagPathError{reflect.Typeof(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}}, +} + +func TestUnmarshalBadPaths(t *testing.T) { + for _, tt := range badPathTests { + err := Unmarshal(StringReader(pathTestString), tt.v) + if !reflect.DeepEqual(err, tt.e) { + t.Fatalf("Unmarshal with %#v didn't fail properly: %#v", tt.v, err) + } + } +} diff --git a/libgo/go/xml/xml.go b/libgo/go/xml/xml.go index eed9355..4d9c672 100644 --- a/libgo/go/xml/xml.go +++ b/libgo/go/xml/xml.go @@ -16,6 +16,7 @@ package xml import ( "bufio" "bytes" + "fmt" "io" "os" "strconv" @@ -871,6 +872,21 @@ Input: data := p.buf.Bytes() data = data[0 : len(data)-trunc] + // Inspect each rune for being a disallowed character. + buf := data + for len(buf) > 0 { + r, size := utf8.DecodeRune(buf) + if r == utf8.RuneError && size == 1 { + p.err = p.syntaxError("invalid UTF-8") + return nil + } + buf = buf[size:] + if !isInCharacterRange(r) { + p.err = p.syntaxError(fmt.Sprintf("illegal character code %U", r)) + return nil + } + } + // Must rewrite \r and \r\n into \n. w := 0 for r := 0; r < len(data); r++ { @@ -887,6 +903,18 @@ Input: return data[0:w] } +// Decide whether the given rune is in the XML Character Range, per +// the Char production of http://www.xml.com/axml/testaxml.htm, +// Section 2.2 Characters. +func isInCharacterRange(rune int) (inrange bool) { + return rune == 0x09 || + rune == 0x0A || + rune == 0x0D || + rune >= 0x20 && rune <= 0xDF77 || + rune >= 0xE000 && rune <= 0xFFFD || + rune >= 0x10000 && rune <= 0x10FFFF +} + // Get name space name: name with a : stuck in the middle. // The part before the : is the name space identifier. func (p *Parser) nsname() (name Name, ok bool) { diff --git a/libgo/go/xml/xml_test.go b/libgo/go/xml/xml_test.go index 0068896..317ecab 100644 --- a/libgo/go/xml/xml_test.go +++ b/libgo/go/xml/xml_test.go @@ -227,7 +227,6 @@ type allScalars struct { Uint32 uint32 Uint64 uint64 Uintptr uintptr - Float float Float32 float32 Float64 float64 String string @@ -249,7 +248,6 @@ var all = allScalars{ Uint32: 9, Uint64: 10, Uintptr: 11, - Float: 12.0, Float32: 13.0, Float64: 14.0, String: "15", @@ -301,7 +299,7 @@ func TestIssue569(t *testing.T) { err := Unmarshal(buf, &i) if err != nil || i.Field_a != "abcd" { - t.Fatalf("Expecting abcd") + t.Fatal("Expecting abcd") } } @@ -398,3 +396,44 @@ func TestEntityInsideCDATA(t *testing.T) { t.Fatalf("p.Token() = _, %v, want _, os.EOF", err) } } + + +// The last three tests (respectively one for characters in attribute +// names and two for character entities) pass not because of code +// changed for issue 1259, but instead pass with the given messages +// from other parts of xml.Parser. I provide these to note the +// current behavior of situations where one might think that character +// range checking would detect the error, but it does not in fact. + +var characterTests = []struct { + in string + err string +}{ + {"\x12<doc/>", "illegal character code U+0012"}, + {"<?xml version=\"1.0\"?>\x0b<doc/>", "illegal character code U+000B"}, + {"\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 &;"}, +} + + +func TestDisallowedCharacters(t *testing.T) { + + for i, tt := range characterTests { + p := NewParser(StringReader(tt.in)) + var err os.Error + + for err == nil { + _, err = p.Token() + } + synerr, ok := err.(*SyntaxError) + if !ok { + t.Fatalf("input %d p.Token() = _, %v, want _, *SyntaxError", i, err) + } + if synerr.Msg != tt.err { + t.Fatalf("input %d synerr.Msg wrong: want '%s', got '%s'", i, tt.err, synerr.Msg) + } + } +} |