diff options
Diffstat (limited to 'libgo/go/encoding/json/encode.go')
-rw-r--r-- | libgo/go/encoding/json/encode.go | 57 |
1 files changed, 35 insertions, 22 deletions
diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index 8f21ddaed94..0371f0a24dd 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -332,10 +332,7 @@ type encOpts struct { type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts) -var encoderCache struct { - sync.RWMutex - m map[reflect.Type]encoderFunc -} +var encoderCache sync.Map // map[reflect.Type]encoderFunc func valueEncoder(v reflect.Value) encoderFunc { if !v.IsValid() { @@ -345,36 +342,31 @@ func valueEncoder(v reflect.Value) encoderFunc { } func typeEncoder(t reflect.Type) encoderFunc { - encoderCache.RLock() - f := encoderCache.m[t] - encoderCache.RUnlock() - if f != nil { - return f + if fi, ok := encoderCache.Load(t); ok { + return fi.(encoderFunc) } // To deal with recursive types, populate the map with an // indirect func before we build it. This type waits on the // real func (f) to be ready and then calls it. This indirect // func is only used for recursive types. - encoderCache.Lock() - if encoderCache.m == nil { - encoderCache.m = make(map[reflect.Type]encoderFunc) - } - var wg sync.WaitGroup + var ( + wg sync.WaitGroup + f encoderFunc + ) wg.Add(1) - encoderCache.m[t] = func(e *encodeState, v reflect.Value, opts encOpts) { + fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(e *encodeState, v reflect.Value, opts encOpts) { wg.Wait() f(e, v, opts) + })) + if loaded { + return fi.(encoderFunc) } - encoderCache.Unlock() - // Compute fields without lock. - // Might duplicate effort but won't hold other computations back. + // Compute the real encoder and replace the indirect func with it. f = newTypeEncoder(t, true) wg.Done() - encoderCache.Lock() - encoderCache.m[t] = f - encoderCache.Unlock() + encoderCache.Store(t, f) return f } @@ -1101,7 +1093,22 @@ 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 != "" && !sf.Anonymous { // unexported + if sf.Anonymous { + t := sf.Type + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + // If embedded, StructField.PkgPath is not a reliable + // indicator of whether the field is exported. + // See https://golang.org/issue/21122 + if !isExported(t.Name()) && t.Kind() != reflect.Struct { + // Ignore embedded fields of unexported non-struct types. + // Do not ignore embedded fields of unexported struct types + // since they may have exported fields. + continue + } + } else if sf.PkgPath != "" { + // Ignore unexported non-embedded fields. continue } tag := sf.Tag.Get("json") @@ -1219,6 +1226,12 @@ func typeFields(t reflect.Type) []field { return fields } +// isExported reports whether the identifier is exported. +func isExported(id string) bool { + r, _ := utf8.DecodeRuneInString(id) + return unicode.IsUpper(r) +} + // dominantField looks through the fields, all of which are known to // have the same name, to find the single field that dominates the // others using Go's embedding rules, modified by the presence of |