@@ -57,6 +57,9 @@ type FieldCacheEntry struct {
57
57
// a field in a reflect.Value struct. The field indices in the list form a path used
58
58
// to traverse through intermediary 'inline' fields.
59
59
fieldPath [][]int
60
+
61
+ fieldType reflect.Type
62
+ TypeEntry * TypeReflectCacheEntry
60
63
}
61
64
62
65
// GetFrom returns the field identified by this FieldCacheEntry from the provided struct.
@@ -74,25 +77,51 @@ var unstructuredConvertableType = reflect.TypeOf(new(UnstructuredConverter)).Ele
74
77
var defaultReflectCache = newReflectCache ()
75
78
76
79
// TypeReflectEntryOf returns the TypeReflectCacheEntry of the provided reflect.Type.
77
- func TypeReflectEntryOf (t reflect.Type ) TypeReflectCacheEntry {
78
- if record , ok := defaultReflectCache .get (t ); ok {
80
+ func TypeReflectEntryOf (t reflect.Type ) * TypeReflectCacheEntry {
81
+ cm := defaultReflectCache .get ()
82
+ if record , ok := cm [t ]; ok {
83
+ return record
84
+ }
85
+ updates := reflectCacheMap {}
86
+ result := typeReflectEntryOf (cm , t , updates )
87
+ if len (updates ) > 0 {
88
+ defaultReflectCache .update (updates )
89
+ }
90
+ return result
91
+ }
92
+
93
+ // TypeReflectEntryOf returns all updates needed to add provided reflect.Type, and the types its fields transitively
94
+ // depend on, to the cache.
95
+ func typeReflectEntryOf (cm reflectCacheMap , t reflect.Type , updates reflectCacheMap ) * TypeReflectCacheEntry {
96
+ if record , ok := cm [t ]; ok {
97
+ return record
98
+ }
99
+ if record , ok := updates [t ]; ok {
79
100
return record
80
101
}
81
- record := TypeReflectCacheEntry {
102
+ typeEntry := & TypeReflectCacheEntry {
82
103
isJsonMarshaler : t .Implements (marshalerType ),
83
104
ptrIsJsonMarshaler : reflect .PtrTo (t ).Implements (marshalerType ),
84
105
isJsonUnmarshaler : reflect .PtrTo (t ).Implements (unmarshalerType ),
85
106
isStringConvertable : t .Implements (unstructuredConvertableType ),
86
107
ptrIsStringConvertable : reflect .PtrTo (t ).Implements (unstructuredConvertableType ),
87
108
}
88
109
if t .Kind () == reflect .Struct {
89
- hints := map [string ]* FieldCacheEntry {}
90
- buildStructCacheEntry (t , hints , nil )
91
- record .structFields = hints
110
+ fieldEntries := map [string ]* FieldCacheEntry {}
111
+ buildStructCacheEntry (t , fieldEntries , nil )
112
+ typeEntry .structFields = fieldEntries
92
113
}
93
114
94
- defaultReflectCache .update (t , record )
95
- return record
115
+ // cyclic type references are allowed, so we must add the typeEntry to the updates map before resolving
116
+ // the field.typeEntry references, or creating them if they are not already in the cache
117
+ updates [t ] = typeEntry
118
+
119
+ for _ , field := range typeEntry .structFields {
120
+ if field .TypeEntry == nil {
121
+ field .TypeEntry = typeReflectEntryOf (cm , field .fieldType , updates )
122
+ }
123
+ }
124
+ return typeEntry
96
125
}
97
126
98
127
func buildStructCacheEntry (t reflect.Type , infos map [string ]* FieldCacheEntry , fieldPath [][]int ) {
@@ -106,7 +135,7 @@ func buildStructCacheEntry(t reflect.Type, infos map[string]*FieldCacheEntry, fi
106
135
buildStructCacheEntry (field .Type , infos , append (fieldPath , field .Index ))
107
136
continue
108
137
}
109
- info := & FieldCacheEntry {isOmitEmpty : isOmitempty , fieldPath : append (fieldPath , field .Index )}
138
+ info := & FieldCacheEntry {isOmitEmpty : isOmitempty , fieldPath : append (fieldPath , field .Index ), fieldType : t }
110
139
infos [jsonName ] = info
111
140
}
112
141
}
@@ -275,22 +304,29 @@ func newReflectCache() *typeReflectCache {
275
304
return cache
276
305
}
277
306
278
- type reflectCacheMap map [reflect.Type ]TypeReflectCacheEntry
307
+ type reflectCacheMap map [reflect.Type ]* TypeReflectCacheEntry
279
308
280
- // get returns true and TypeReflectCacheEntry for the given type if the type is in the cache. Otherwise get returns false.
281
- func (c * typeReflectCache ) get (t reflect.Type ) (TypeReflectCacheEntry , bool ) {
282
- entry , ok := c .value .Load ().(reflectCacheMap )[t ]
283
- return entry , ok
309
+ // get returns the reflectCacheMap.
310
+ func (c * typeReflectCache ) get () reflectCacheMap {
311
+ return c .value .Load ().(reflectCacheMap )
284
312
}
285
313
286
- // update sets the TypeReflectCacheEntry for the given type via a copy-on-write update to the struct cache.
287
- func (c * typeReflectCache ) update (t reflect. Type , m TypeReflectCacheEntry ) {
314
+ // update merges the provided updates into the cache.
315
+ func (c * typeReflectCache ) update (updates reflectCacheMap ) {
288
316
c .mu .Lock ()
289
317
defer c .mu .Unlock ()
290
318
291
319
currentCacheMap := c .value .Load ().(reflectCacheMap )
292
- if _ , ok := currentCacheMap [t ]; ok {
293
- // Bail if the entry has been set while waiting for lock acquisition.
320
+
321
+ hasNewEntries := false
322
+ for t := range updates {
323
+ if _ , ok := currentCacheMap [t ]; ! ok {
324
+ hasNewEntries = true
325
+ break
326
+ }
327
+ }
328
+ if ! hasNewEntries {
329
+ // Bail if the updates have been set while waiting for lock acquisition.
294
330
// This is safe since setting entries is idempotent.
295
331
return
296
332
}
@@ -299,6 +335,8 @@ func (c *typeReflectCache) update(t reflect.Type, m TypeReflectCacheEntry) {
299
335
for k , v := range currentCacheMap {
300
336
newCacheMap [k ] = v
301
337
}
302
- newCacheMap [t ] = m
338
+ for t , update := range updates {
339
+ newCacheMap [t ] = update
340
+ }
303
341
c .value .Store (newCacheMap )
304
342
}
0 commit comments