re PR go/66138 (json decoder Decode function fails for some structure return values)

PR go/66138
    reflect, encoding/json, encoding/xml: fix unexported embedded structs
    
    Bring in three changes from the master Go repository.  These changes
    will be in Go 1.6, but they are appropriate for gccgo now because they
    resolve a long-standing discrepancy between how gc and gccgo handle the
    PkgPath field for embedded unexported struct fields.  The core issue is
    described at https://golang.org/cl/7247.  This has been reported against
    gccgo as https://gcc.gnu.org/PR66138.
    
    The three changes being brought over are:
    
    https://golang.org/cl/14010
    
    reflect: adjust access to unexported embedded structs
    
    This CL changes reflect to allow access to exported fields and
    methods in unexported embedded structs for gccgo and after gc
    has been adjusted to disallow access to embedded unexported structs.
    
    Adresses #12367, #7363, #11007, and #7247.
    
    https://golang.org/cl/14011
    
    encoding/json: check for exported fields in embedded structs
    
    Addresses issue #12367.
    
    https://golang.org/cl/14012
    
    encoding/xml: check for exported fields in embedded structs
    
    Addresses issue #12367.
    
    Reviewed-on: https://go-review.googlesource.com/16723

From-SVN: r229907
This commit is contained in:
Ian Lance Taylor 2015-11-07 01:24:57 +00:00
parent 39f02a1f52
commit f5eb9a8ec6
8 changed files with 48 additions and 19 deletions

View file

@ -1,4 +1,4 @@
10c1d6756ed1dcc814c49921c2a5e27f4677e0e6
012ab5cb2ef1c26e8023ce90d3a2bba174da7b30
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View file

@ -118,6 +118,7 @@ type Top struct {
Loop
Embed0p // has Point with X, Y, used
Embed0q // has Point with Z, used
embed // contains exported field
}
type Embed0 struct {
@ -148,6 +149,10 @@ type Embed0q struct {
Point
}
type embed struct {
Q int
}
type Loop struct {
Loop1 int `json:",omitempty"`
Loop2 int `json:",omitempty"`
@ -331,7 +336,8 @@ var unmarshalTests = []unmarshalTest{
"Loop2": 14,
"X": 15,
"Y": 16,
"Z": 17
"Z": 17,
"Q": 18
}`,
ptr: new(Top),
out: Top{
@ -361,6 +367,9 @@ var unmarshalTests = []unmarshalTest{
Embed0q: Embed0q{
Point: Point{Z: 17},
},
embed: embed{
Q: 18,
},
},
},
{
@ -507,12 +516,15 @@ func TestMarshalEmbeds(t *testing.T) {
Embed0q: Embed0q{
Point: Point{Z: 17},
},
embed: embed{
Q: 18,
},
}
b, err := Marshal(top)
if err != nil {
t.Fatal(err)
}
want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17}"
want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17,\"Q\":18}"
if string(b) != want {
t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
}

View file

@ -1022,7 +1022,7 @@ func typeFields(t reflect.Type) []field {
// Scan f.typ for fields to include.
for i := 0; i < f.typ.NumField(); i++ {
sf := f.typ.Field(i)
if sf.PkgPath != "" { // unexported
if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue
}
tag := sf.Tag.Get("json")

View file

@ -139,6 +139,7 @@ type EmbedA struct {
EmbedC
EmbedB EmbedB
FieldA string
embedD
}
type EmbedB struct {
@ -153,6 +154,11 @@ type EmbedC struct {
FieldC string
}
type embedD struct {
fieldD string
FieldE string // Promoted and visible when embedD is embedded.
}
type NameCasing struct {
XMLName struct{} `xml:"casing"`
Xy string
@ -711,6 +717,9 @@ var marshalTests = []struct {
},
},
FieldA: "A.A",
embedD: embedD{
FieldE: "A.D.E",
},
},
ExpectXML: `<EmbedA>` +
`<FieldB>A.C.B</FieldB>` +
@ -724,6 +733,7 @@ var marshalTests = []struct {
`<FieldC>A.B.C.C</FieldC>` +
`</EmbedB>` +
`<FieldA>A.A</FieldA>` +
`<FieldE>A.D.E</FieldE>` +
`</EmbedA>`,
},

View file

@ -60,7 +60,7 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
n := typ.NumField()
for i := 0; i < n; i++ {
f := typ.Field(i)
if f.PkgPath != "" || f.Tag.Get("xml") == "-" {
if (f.PkgPath != "" && !f.Anonymous) || f.Tag.Get("xml") == "-" {
continue // Private field
}

View file

@ -6,13 +6,13 @@ package reflect
// MakeRO returns a copy of v with the read-only flag set.
func MakeRO(v Value) Value {
v.flag |= flagRO
v.flag |= flagStickyRO
return v
}
// IsRO reports whether v's read-only flag is set.
func IsRO(v Value) bool {
return v.flag&flagRO != 0
return v.flag&flagStickyRO != 0
}
var CallGC = &callGC

View file

@ -516,7 +516,7 @@ func (t *uncommonType) Method(i int) (m Method) {
fl := flag(Func)
if p.pkgPath != nil {
m.PkgPath = *p.pkgPath
fl |= flagRO
fl |= flagStickyRO
}
mt := p.typ
m.Type = toType(mt)

View file

@ -44,7 +44,8 @@ type Value struct {
// flag holds metadata about the value.
// The lowest bits are flag bits:
// - flagRO: obtained via unexported field, so read-only
// - flagStickyRO: obtained via unexported not embedded field, so read-only
// - flagEmbedRO: obtained via unexported embedded field, so read-only
// - flagIndir: val holds a pointer to the data
// - flagAddr: v.CanAddr is true (implies flagIndir)
// - flagMethod: v is a method value.
@ -67,12 +68,14 @@ type flag uintptr
const (
flagKindWidth = 5 // there are 27 kinds
flagKindMask flag = 1<<flagKindWidth - 1
flagRO flag = 1 << 5
flagIndir flag = 1 << 6
flagAddr flag = 1 << 7
flagMethod flag = 1 << 8
flagMethodFn flag = 1 << 9 // gccgo: first fn parameter is always pointer
flagMethodShift = 10
flagStickyRO flag = 1 << 5
flagEmbedRO flag = 1 << 6
flagIndir flag = 1 << 7
flagAddr flag = 1 << 8
flagMethod flag = 1 << 9
flagMethodFn flag = 1 << 10 // gccgo: first fn parameter is always pointer
flagMethodShift = 11
flagRO flag = flagStickyRO | flagEmbedRO
)
func (f flag) kind() Kind {
@ -617,11 +620,15 @@ func (v Value) Field(i int) Value {
field := &tt.fields[i]
typ := field.typ
// Inherit permission bits from v.
fl := v.flag&(flagRO|flagIndir|flagAddr) | flag(typ.Kind())
// Inherit permission bits from v, but clear flagEmbedRO.
fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
// Using an unexported field forces flagRO.
if field.pkgPath != nil {
fl |= flagRO
if field.name == nil {
fl |= flagEmbedRO
} else {
fl |= flagStickyRO
}
}
// Either flagIndir is set and v.ptr points at struct,
// or flagIndir is not set and v.ptr is the actual struct data.
@ -986,7 +993,7 @@ func (v Value) Method(i int) Value {
if v.typ.Kind() == Interface && v.IsNil() {
panic("reflect: Method on nil interface value")
}
fl := v.flag & (flagRO | flagIndir)
fl := v.flag & (flagStickyRO | flagIndir) // Clear flagEmbedRO
fl |= flag(Func)
fl |= flag(i)<<flagMethodShift | flagMethod
return Value{v.typ, v.ptr, fl}