summaryrefslogtreecommitdiff
path: root/libgo/go/encoding/json/encode.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/encoding/json/encode.go')
-rw-r--r--libgo/go/encoding/json/encode.go57
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