Commit f5eb9a8e by Ian Lance Taylor

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
parent 39f02a1f
10c1d6756ed1dcc814c49921c2a5e27f4677e0e6 012ab5cb2ef1c26e8023ce90d3a2bba174da7b30
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -118,6 +118,7 @@ type Top struct { ...@@ -118,6 +118,7 @@ type Top struct {
Loop Loop
Embed0p // has Point with X, Y, used Embed0p // has Point with X, Y, used
Embed0q // has Point with Z, used Embed0q // has Point with Z, used
embed // contains exported field
} }
type Embed0 struct { type Embed0 struct {
...@@ -148,6 +149,10 @@ type Embed0q struct { ...@@ -148,6 +149,10 @@ type Embed0q struct {
Point Point
} }
type embed struct {
Q int
}
type Loop struct { type Loop struct {
Loop1 int `json:",omitempty"` Loop1 int `json:",omitempty"`
Loop2 int `json:",omitempty"` Loop2 int `json:",omitempty"`
...@@ -331,7 +336,8 @@ var unmarshalTests = []unmarshalTest{ ...@@ -331,7 +336,8 @@ var unmarshalTests = []unmarshalTest{
"Loop2": 14, "Loop2": 14,
"X": 15, "X": 15,
"Y": 16, "Y": 16,
"Z": 17 "Z": 17,
"Q": 18
}`, }`,
ptr: new(Top), ptr: new(Top),
out: Top{ out: Top{
...@@ -361,6 +367,9 @@ var unmarshalTests = []unmarshalTest{ ...@@ -361,6 +367,9 @@ var unmarshalTests = []unmarshalTest{
Embed0q: Embed0q{ Embed0q: Embed0q{
Point: Point{Z: 17}, Point: Point{Z: 17},
}, },
embed: embed{
Q: 18,
},
}, },
}, },
{ {
...@@ -507,12 +516,15 @@ func TestMarshalEmbeds(t *testing.T) { ...@@ -507,12 +516,15 @@ func TestMarshalEmbeds(t *testing.T) {
Embed0q: Embed0q{ Embed0q: Embed0q{
Point: Point{Z: 17}, Point: Point{Z: 17},
}, },
embed: embed{
Q: 18,
},
} }
b, err := Marshal(top) b, err := Marshal(top)
if err != nil { if err != nil {
t.Fatal(err) 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 { if string(b) != want {
t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want) t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
} }
......
...@@ -1022,7 +1022,7 @@ func typeFields(t reflect.Type) []field { ...@@ -1022,7 +1022,7 @@ func typeFields(t reflect.Type) []field {
// Scan f.typ for fields to include. // Scan f.typ for fields to include.
for i := 0; i < f.typ.NumField(); i++ { for i := 0; i < f.typ.NumField(); i++ {
sf := f.typ.Field(i) sf := f.typ.Field(i)
if sf.PkgPath != "" { // unexported if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue continue
} }
tag := sf.Tag.Get("json") tag := sf.Tag.Get("json")
......
...@@ -139,6 +139,7 @@ type EmbedA struct { ...@@ -139,6 +139,7 @@ type EmbedA struct {
EmbedC EmbedC
EmbedB EmbedB EmbedB EmbedB
FieldA string FieldA string
embedD
} }
type EmbedB struct { type EmbedB struct {
...@@ -153,6 +154,11 @@ type EmbedC struct { ...@@ -153,6 +154,11 @@ type EmbedC struct {
FieldC string FieldC string
} }
type embedD struct {
fieldD string
FieldE string // Promoted and visible when embedD is embedded.
}
type NameCasing struct { type NameCasing struct {
XMLName struct{} `xml:"casing"` XMLName struct{} `xml:"casing"`
Xy string Xy string
...@@ -711,6 +717,9 @@ var marshalTests = []struct { ...@@ -711,6 +717,9 @@ var marshalTests = []struct {
}, },
}, },
FieldA: "A.A", FieldA: "A.A",
embedD: embedD{
FieldE: "A.D.E",
},
}, },
ExpectXML: `<EmbedA>` + ExpectXML: `<EmbedA>` +
`<FieldB>A.C.B</FieldB>` + `<FieldB>A.C.B</FieldB>` +
...@@ -724,6 +733,7 @@ var marshalTests = []struct { ...@@ -724,6 +733,7 @@ var marshalTests = []struct {
`<FieldC>A.B.C.C</FieldC>` + `<FieldC>A.B.C.C</FieldC>` +
`</EmbedB>` + `</EmbedB>` +
`<FieldA>A.A</FieldA>` + `<FieldA>A.A</FieldA>` +
`<FieldE>A.D.E</FieldE>` +
`</EmbedA>`, `</EmbedA>`,
}, },
......
...@@ -60,7 +60,7 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) { ...@@ -60,7 +60,7 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
n := typ.NumField() n := typ.NumField()
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
f := typ.Field(i) f := typ.Field(i)
if f.PkgPath != "" || f.Tag.Get("xml") == "-" { if (f.PkgPath != "" && !f.Anonymous) || f.Tag.Get("xml") == "-" {
continue // Private field continue // Private field
} }
......
...@@ -6,13 +6,13 @@ package reflect ...@@ -6,13 +6,13 @@ package reflect
// MakeRO returns a copy of v with the read-only flag set. // MakeRO returns a copy of v with the read-only flag set.
func MakeRO(v Value) Value { func MakeRO(v Value) Value {
v.flag |= flagRO v.flag |= flagStickyRO
return v return v
} }
// IsRO reports whether v's read-only flag is set. // IsRO reports whether v's read-only flag is set.
func IsRO(v Value) bool { func IsRO(v Value) bool {
return v.flag&flagRO != 0 return v.flag&flagStickyRO != 0
} }
var CallGC = &callGC var CallGC = &callGC
......
...@@ -516,7 +516,7 @@ func (t *uncommonType) Method(i int) (m Method) { ...@@ -516,7 +516,7 @@ func (t *uncommonType) Method(i int) (m Method) {
fl := flag(Func) fl := flag(Func)
if p.pkgPath != nil { if p.pkgPath != nil {
m.PkgPath = *p.pkgPath m.PkgPath = *p.pkgPath
fl |= flagRO fl |= flagStickyRO
} }
mt := p.typ mt := p.typ
m.Type = toType(mt) m.Type = toType(mt)
......
...@@ -44,7 +44,8 @@ type Value struct { ...@@ -44,7 +44,8 @@ type Value struct {
// flag holds metadata about the value. // flag holds metadata about the value.
// The lowest bits are flag bits: // 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 // - flagIndir: val holds a pointer to the data
// - flagAddr: v.CanAddr is true (implies flagIndir) // - flagAddr: v.CanAddr is true (implies flagIndir)
// - flagMethod: v is a method value. // - flagMethod: v is a method value.
...@@ -67,12 +68,14 @@ type flag uintptr ...@@ -67,12 +68,14 @@ type flag uintptr
const ( const (
flagKindWidth = 5 // there are 27 kinds flagKindWidth = 5 // there are 27 kinds
flagKindMask flag = 1<<flagKindWidth - 1 flagKindMask flag = 1<<flagKindWidth - 1
flagRO flag = 1 << 5 flagStickyRO flag = 1 << 5
flagIndir flag = 1 << 6 flagEmbedRO flag = 1 << 6
flagAddr flag = 1 << 7 flagIndir flag = 1 << 7
flagMethod flag = 1 << 8 flagAddr flag = 1 << 8
flagMethodFn flag = 1 << 9 // gccgo: first fn parameter is always pointer flagMethod flag = 1 << 9
flagMethodShift = 10 flagMethodFn flag = 1 << 10 // gccgo: first fn parameter is always pointer
flagMethodShift = 11
flagRO flag = flagStickyRO | flagEmbedRO
) )
func (f flag) kind() Kind { func (f flag) kind() Kind {
...@@ -617,11 +620,15 @@ func (v Value) Field(i int) Value { ...@@ -617,11 +620,15 @@ func (v Value) Field(i int) Value {
field := &tt.fields[i] field := &tt.fields[i]
typ := field.typ typ := field.typ
// Inherit permission bits from v. // Inherit permission bits from v, but clear flagEmbedRO.
fl := v.flag&(flagRO|flagIndir|flagAddr) | flag(typ.Kind()) fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
// Using an unexported field forces flagRO. // Using an unexported field forces flagRO.
if field.pkgPath != nil { 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, // Either flagIndir is set and v.ptr points at struct,
// or flagIndir is not set and v.ptr is the actual struct data. // or flagIndir is not set and v.ptr is the actual struct data.
...@@ -986,7 +993,7 @@ func (v Value) Method(i int) Value { ...@@ -986,7 +993,7 @@ func (v Value) Method(i int) Value {
if v.typ.Kind() == Interface && v.IsNil() { if v.typ.Kind() == Interface && v.IsNil() {
panic("reflect: Method on nil interface value") panic("reflect: Method on nil interface value")
} }
fl := v.flag & (flagRO | flagIndir) fl := v.flag & (flagStickyRO | flagIndir) // Clear flagEmbedRO
fl |= flag(Func) fl |= flag(Func)
fl |= flag(i)<<flagMethodShift | flagMethod fl |= flag(i)<<flagMethodShift | flagMethod
return Value{v.typ, v.ptr, fl} return Value{v.typ, v.ptr, fl}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment