Repository: vmware/govmomi
Pull request #2100 (2020-08-29 15:28:28)

vim25/xml: sync with Go 1.15 encoding/xml
Applied via:

% cd ~/go/src/github.com/golang/go
% git diff go1.13.6..go1.15 src/encoding/xml > encoding-xml.patch
% cd ~/go/src/github.com/vmware/govmomi/vim25/xml
% patch -p4 < ~/go/src/github.com/golang/go/encoding-xml.patch

Diff after sync: https://gist.github.com/dougm/a941f77324a6172712430c3ff9ff8c63


Patch:
diff --git a/vim25/xml/marshal.go b/vim25/xml/marshal.go
index 92c63717..c0c0a588 100644
--- a/vim25/xml/marshal.go
+++ b/vim25/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 {
@@ -505,7 +508,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
@@ -814,7 +817,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:
@@ -925,7 +933,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
 			p.WriteString("-->")
 			continue
 
-		case fInnerXml:
+		case fInnerXML:
 			vf = indirect(vf)
 			iface := vf.Interface()
 			switch raw := iface.(type) {
diff --git a/vim25/xml/read.go b/vim25/xml/read.go
index 60f71582..ea61352f 100644
--- a/vim25/xml/read.go
+++ b/vim25/xml/read.go
@@ -491,7 +491,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))
 			}
@@ -505,7 +505,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) {
 						needTypeAttr := (finfo.flags & fTypeAttr) != 0
 						// HACK: avoid using xsi:type value for a "type" attribute, such as ManagedObjectReference.Type for example.
@@ -525,7 +525,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
 				}
@@ -538,22 +538,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:
+			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)
@@ -769,7 +769,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/vim25/xml/typeinfo.go b/vim25/xml/typeinfo.go
index 0d240b1e..5eb0b4e5 100644
--- a/vim25/xml/typeinfo.go
+++ b/vim25/xml/typeinfo.go
@@ -33,14 +33,14 @@ const (
 	fAttr
 	fCDATA
 	fCharData
-	fInnerXml
+	fInnerXML
 	fComment
 	fAny
 
 	fOmitEmpty
 	fTypeAttr
 
-	fMode = fElement | fAttr | fCDATA | fCharData | fInnerXml | fComment | fAny
+	fMode = fElement | fAttr | fCDATA | fCharData | fInnerXML | fComment | fAny
 
 	xmlName = "XMLName"
 )
@@ -135,7 +135,7 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro
 			case "chardata":
 				finfo.flags |= fCharData
 			case "innerxml":
-				finfo.flags |= fInnerXml
+				finfo.flags |= fInnerXML
 			case "comment":
 				finfo.flags |= fComment
 			case "any":
@@ -152,7 +152,7 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro
 		switch mode := finfo.flags & fMode; mode {
 		case 0:
 			finfo.flags |= fElement
-		case fAttr, fCDATA, fCharData, fInnerXml, fComment, fAny, fAny | fAttr:
+		case fAttr, fCDATA, fCharData, fInnerXML, fComment, fAny, fAny | fAttr:
 			if f.Name == xmlName || tag != "" && mode != fAttr {
 				valid = false
 			}
@@ -347,15 +347,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/vim25/xml/xml.go b/vim25/xml/xml.go
index 50117a00..28631bdf 100644
--- a/vim25/xml/xml.go
+++ b/vim25/xml/xml.go
@@ -290,7 +290,10 @@ func (d *Decoder) Token() (Token, error) {
 		t = d.nextToken
 		d.nextToken = nil
 	} else if t, err = d.rawToken(); err != nil {
-		if err == io.EOF && d.stk != nil && d.stk.kind != stkEOF {
+		switch {
+		case err == io.EOF && d.t != nil:
+			err = nil
+		case err == io.EOF && d.stk != nil && d.stk.kind != stkEOF:
 			err = d.syntaxError("unexpected EOF")
 		}
 		return t, err
@@ -961,7 +964,7 @@ func (d *Decoder) ungetc(b byte) {
 	d.offset--
 }
 
-var entity = map[string]int{
+var entity = map[string]rune{
 	"lt":   '<',
 	"gt":   '>',
 	"amp":  '&',
@@ -1056,7 +1059,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
 					}
 				}


Test patch:
diff --git a/vim25/xml/marshal_test.go b/vim25/xml/marshal_test.go
index a0ccf440..31309ef2 100644
--- a/vim25/xml/marshal_test.go
+++ b/vim25/xml/marshal_test.go
@@ -309,6 +309,11 @@ type ChardataEmptyTest struct {
 	Contents *string `xml:",chardata"`
 }
 
+type PointerAnonFields struct {
+	*MyInt
+	*NamedType
+}
+
 type MyMarshalerTest struct {
 }
 
@@ -392,7 +397,6 @@ func stringptr(x string) *string {
 
 type T1 struct{}
 type T2 struct{}
-type T3 struct{}
 
 type IndirComment struct {
 	T1      T1
@@ -890,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/vim25/xml/xml_test.go b/vim25/xml/xml_test.go
index ee4ffa24..efddca43 100644
--- a/vim25/xml/xml_test.go
+++ b/vim25/xml/xml_test.go
@@ -14,6 +14,51 @@ import (
 	"unicode/utf8"
 )
 
+type toks struct {
+	earlyEOF bool
+	t        []Token
+}
+
+func (t *toks) Token() (Token, error) {
+	if len(t.t) == 0 {
+		return nil, io.EOF
+	}
+	var tok Token
+	tok, t.t = t.t[0], t.t[1:]
+	if t.earlyEOF && len(t.t) == 0 {
+		return tok, io.EOF
+	}
+	return tok, nil
+}
+
+func TestDecodeEOF(t *testing.T) {
+	start := StartElement{Name: Name{Local: "test"}}
+	t.Run("EarlyEOF", func(t *testing.T) {
+		d := NewTokenDecoder(&toks{earlyEOF: true, t: []Token{
+			start,
+			start.End(),
+		}})
+		err := d.Decode(&struct {
+			XMLName Name `xml:"test"`
+		}{})
+		if err != nil {
+			t.Error(err)
+		}
+	})
+	t.Run("LateEOF", func(t *testing.T) {
+		d := NewTokenDecoder(&toks{t: []Token{
+			start,
+			start.End(),
+		}})
+		err := d.Decode(&struct {
+			XMLName Name `xml:"test"`
+		}{})
+		if err != nil {
+			t.Error(err)
+		}
+	})
+}
+
 const testInput = `
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"