From f5eb9a8ec6cff02f52d7a29d96ec6641d2a06de1 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sat, 7 Nov 2015 01:24:57 +0000 Subject: [PATCH] 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 --- gcc/go/gofrontend/MERGE | 2 +- libgo/go/encoding/json/decode_test.go | 16 +++++++++++++-- libgo/go/encoding/json/encode.go | 2 +- libgo/go/encoding/xml/marshal_test.go | 10 +++++++++ libgo/go/encoding/xml/typeinfo.go | 2 +- libgo/go/reflect/export_test.go | 4 ++-- libgo/go/reflect/type.go | 2 +- libgo/go/reflect/value.go | 29 +++++++++++++++++---------- 8 files changed, 48 insertions(+), 19 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index b17ca93b24ca..f325bb33ecbf 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -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. diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go index 8aa158f08cda..51b15ef997cb 100644 --- a/libgo/go/encoding/json/decode_test.go +++ b/libgo/go/encoding/json/decode_test.go @@ -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) } diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index 90782deb70be..e829a930768b 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -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") diff --git a/libgo/go/encoding/xml/marshal_test.go b/libgo/go/encoding/xml/marshal_test.go index 66675d7abc40..ef6c20e949b3 100644 --- a/libgo/go/encoding/xml/marshal_test.go +++ b/libgo/go/encoding/xml/marshal_test.go @@ -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: `` + `A.C.B` + @@ -724,6 +733,7 @@ var marshalTests = []struct { `A.B.C.C` + `` + `A.A` + + `A.D.E` + ``, }, diff --git a/libgo/go/encoding/xml/typeinfo.go b/libgo/go/encoding/xml/typeinfo.go index 22248d20a6d6..6766b88f09a1 100644 --- a/libgo/go/encoding/xml/typeinfo.go +++ b/libgo/go/encoding/xml/typeinfo.go @@ -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 } diff --git a/libgo/go/reflect/export_test.go b/libgo/go/reflect/export_test.go index 89473d352a72..bdbd60074acb 100644 --- a/libgo/go/reflect/export_test.go +++ b/libgo/go/reflect/export_test.go @@ -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 diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index e488938a13f1..180a364de35e 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -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) diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index a924d8639a22..8374370cfa66 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -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<