Skip to content

Commit 8a1c97f

Browse files
authored
fix(util/gconv): cached field indexes append issue caused incorrect field converting (#3790)
1 parent d8e3e9d commit 8a1c97f

File tree

5 files changed

+186
-81
lines changed

5 files changed

+186
-81
lines changed

net/ghttp/ghttp_z_unit_issue_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -568,3 +568,53 @@ func Test_Issue3245(t *testing.T) {
568568
t.Assert(c.GetContent(ctx, "/hello?nickname=oldme"), expect)
569569
})
570570
}
571+
572+
type ItemSecondThird struct {
573+
SecondID uint64 `json:"secondId,string"`
574+
ThirdID uint64 `json:"thirdId,string"`
575+
}
576+
type ItemFirst struct {
577+
ID uint64 `json:"id,string"`
578+
ItemSecondThird
579+
}
580+
type ItemInput struct {
581+
ItemFirst
582+
}
583+
type Issue3789Req struct {
584+
g.Meta `path:"/hello" method:"GET"`
585+
ItemInput
586+
}
587+
type Issue3789Res struct {
588+
ItemInput
589+
}
590+
591+
type Issue3789 struct{}
592+
593+
func (Issue3789) Say(ctx context.Context, req *Issue3789Req) (res *Issue3789Res, err error) {
594+
res = &Issue3789Res{
595+
ItemInput: req.ItemInput,
596+
}
597+
return
598+
}
599+
600+
// https://github.com/gogf/gf/issues/3789
601+
func TestIssue3789(t *testing.T) {
602+
gtest.C(t, func(t *gtest.T) {
603+
s := g.Server()
604+
s.Use(ghttp.MiddlewareHandlerResponse)
605+
s.Group("/", func(group *ghttp.RouterGroup) {
606+
group.Bind(
607+
new(Issue3789),
608+
)
609+
})
610+
s.SetDumpRouterMap(false)
611+
s.Start()
612+
defer s.Shutdown()
613+
time.Sleep(100 * time.Millisecond)
614+
615+
c := g.Client()
616+
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
617+
expect := `{"code":0,"message":"","data":{"id":"0","secondId":"2","thirdId":"3"}}`
618+
t.Assert(c.GetContent(ctx, "/hello?id=&secondId=2&thirdId=3"), expect)
619+
})
620+
}

util/gconv/gconv_struct.go

+78-74
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func doStruct(
8080
paramsInterface interface{} // DO NOT use `params` directly as it might be type `reflect.Value`
8181
pointerReflectValue reflect.Value
8282
pointerReflectKind reflect.Kind
83-
pointerElemReflectValue reflect.Value // The pointed element.
83+
pointerElemReflectValue reflect.Value // The reflection value to struct element.
8484
)
8585
if v, ok := params.(reflect.Value); ok {
8686
paramsReflectValue = v
@@ -183,33 +183,37 @@ func doStruct(
183183
var (
184184
// Indicates that those values have been used and cannot be reused.
185185
usedParamsKeyOrTagNameMap = structcache.GetUsedParamsKeyOrTagNameMapFromPool()
186+
cachedFieldInfo *structcache.CachedFieldInfo
187+
paramsValue interface{}
186188
)
187189
defer structcache.PutUsedParamsKeyOrTagNameMapToPool(usedParamsKeyOrTagNameMap)
188190

189191
// Firstly, search according to custom mapping rules.
190192
// If a possible direct assignment is found, reduce the number of subsequent map searches.
191193
for paramKey, fieldName := range paramKeyToAttrMap {
192-
fieldInfo := cachedStructInfo.GetFieldInfo(fieldName)
193-
if fieldInfo != nil {
194-
if paramsValue, ok := paramsMap[paramKey]; ok {
195-
fieldValue := fieldInfo.GetFieldReflectValue(pointerElemReflectValue)
196-
if err = bindVarToStructField(
197-
fieldValue,
198-
paramsValue,
199-
fieldInfo,
200-
paramKeyToAttrMap,
194+
paramsValue, ok = paramsMap[paramKey]
195+
if !ok {
196+
continue
197+
}
198+
cachedFieldInfo = cachedStructInfo.GetFieldInfo(fieldName)
199+
if cachedFieldInfo != nil {
200+
fieldValue := cachedFieldInfo.GetFieldReflectValueFrom(pointerElemReflectValue)
201+
if err = bindVarToStructField(
202+
fieldValue,
203+
paramsValue,
204+
cachedFieldInfo,
205+
paramKeyToAttrMap,
206+
); err != nil {
207+
return err
208+
}
209+
if len(cachedFieldInfo.OtherSameNameFieldIndex) > 0 {
210+
if err = setOtherSameNameField(
211+
cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap,
201212
); err != nil {
202213
return err
203214
}
204-
if len(fieldInfo.OtherSameNameFieldIndex) > 0 {
205-
if err = setOtherSameNameField(
206-
fieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap,
207-
); err != nil {
208-
return err
209-
}
210-
}
211-
usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
212215
}
216+
usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
213217
}
214218
}
215219
// Already done converting for given `paramsMap`.
@@ -228,15 +232,15 @@ func doStruct(
228232
}
229233

230234
func setOtherSameNameField(
231-
fieldInfo *structcache.CachedFieldInfo,
235+
cachedFieldInfo *structcache.CachedFieldInfo,
232236
srcValue any,
233237
structValue reflect.Value,
234238
paramKeyToAttrMap map[string]string,
235239
) (err error) {
236240
// loop the same field name of all sub attributes.
237-
for i := range fieldInfo.OtherSameNameFieldIndex {
238-
fieldValue := fieldInfo.GetOtherFieldReflectValue(structValue, i)
239-
if err = bindVarToStructField(fieldValue, srcValue, fieldInfo, paramKeyToAttrMap); err != nil {
241+
for i := range cachedFieldInfo.OtherSameNameFieldIndex {
242+
fieldValue := cachedFieldInfo.GetOtherFieldReflectValueFrom(structValue, i)
243+
if err = bindVarToStructField(fieldValue, srcValue, cachedFieldInfo, paramKeyToAttrMap); err != nil {
240244
return err
241245
}
242246
}
@@ -251,36 +255,36 @@ func bindStructWithLoopParamsMap(
251255
cachedStructInfo *structcache.CachedStructInfo,
252256
) (err error) {
253257
var (
254-
fieldName string
255-
fieldInfo *structcache.CachedFieldInfo
256-
fuzzLastKey string
257-
fieldValue reflect.Value
258-
paramKey string
259-
paramValue any
260-
ok bool
258+
fieldName string
259+
cachedFieldInfo *structcache.CachedFieldInfo
260+
fuzzLastKey string
261+
fieldValue reflect.Value
262+
paramKey string
263+
paramValue any
264+
ok bool
261265
)
262266
for paramKey, paramValue = range paramsMap {
263267
if _, ok = usedParamsKeyOrTagNameMap[paramKey]; ok {
264268
continue
265269
}
266-
fieldInfo = cachedStructInfo.GetFieldInfo(paramKey)
267-
if fieldInfo != nil {
268-
fieldName = fieldInfo.FieldName()
270+
cachedFieldInfo = cachedStructInfo.GetFieldInfo(paramKey)
271+
if cachedFieldInfo != nil {
272+
fieldName = cachedFieldInfo.FieldName()
269273
// already converted using its field name?
270274
// the field name has the more priority than tag name.
271275
_, ok = usedParamsKeyOrTagNameMap[fieldName]
272-
if ok && fieldInfo.IsField {
276+
if ok && cachedFieldInfo.IsField {
273277
continue
274278
}
275-
fieldValue = fieldInfo.GetFieldReflectValue(structValue)
279+
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
276280
if err = bindVarToStructField(
277-
fieldValue, paramValue, fieldInfo, paramKeyToAttrMap,
281+
fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
278282
); err != nil {
279283
return err
280284
}
281285
// handle same field name in nested struct.
282-
if len(fieldInfo.OtherSameNameFieldIndex) > 0 {
283-
if err = setOtherSameNameField(fieldInfo, paramValue, structValue, paramKeyToAttrMap); err != nil {
286+
if len(cachedFieldInfo.OtherSameNameFieldIndex) > 0 {
287+
if err = setOtherSameNameField(cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap); err != nil {
284288
return err
285289
}
286290
}
@@ -289,40 +293,40 @@ func bindStructWithLoopParamsMap(
289293
}
290294

291295
// fuzzy matching.
292-
for _, fieldInfo = range cachedStructInfo.FieldConvertInfos {
293-
fieldName = fieldInfo.FieldName()
296+
for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos {
297+
fieldName = cachedFieldInfo.FieldName()
294298
if _, ok = usedParamsKeyOrTagNameMap[fieldName]; ok {
295299
continue
296300
}
297-
fuzzLastKey = fieldInfo.LastFuzzyKey.Load().(string)
301+
fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string)
298302
paramValue, ok = paramsMap[fuzzLastKey]
299303
if !ok {
300304
if strings.EqualFold(
301-
fieldInfo.RemoveSymbolsFieldName, utils.RemoveSymbols(paramKey),
305+
cachedFieldInfo.RemoveSymbolsFieldName, utils.RemoveSymbols(paramKey),
302306
) {
303307
paramValue, ok = paramsMap[paramKey]
304308
// If it is found this time, update it based on what was not found last time.
305-
fieldInfo.LastFuzzyKey.Store(paramKey)
309+
cachedFieldInfo.LastFuzzyKey.Store(paramKey)
306310
}
307311
}
308312
if ok {
309-
fieldValue = fieldInfo.GetFieldReflectValue(structValue)
313+
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
310314
if paramValue != nil {
311315
if err = bindVarToStructField(
312-
fieldValue, paramValue, fieldInfo, paramKeyToAttrMap,
316+
fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
313317
); err != nil {
314318
return err
315319
}
316320
// handle same field name in nested struct.
317-
if len(fieldInfo.OtherSameNameFieldIndex) > 0 {
321+
if len(cachedFieldInfo.OtherSameNameFieldIndex) > 0 {
318322
if err = setOtherSameNameField(
319-
fieldInfo, paramValue, structValue, paramKeyToAttrMap,
323+
cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
320324
); err != nil {
321325
return err
322326
}
323327
}
324328
}
325-
usedParamsKeyOrTagNameMap[fieldInfo.FieldName()] = struct{}{}
329+
usedParamsKeyOrTagNameMap[cachedFieldInfo.FieldName()] = struct{}{}
326330
break
327331
}
328332
}
@@ -338,33 +342,33 @@ func bindStructWithLoopFieldInfos(
338342
cachedStructInfo *structcache.CachedStructInfo,
339343
) (err error) {
340344
var (
341-
fieldInfo *structcache.CachedFieldInfo
342-
fuzzLastKey string
343-
fieldValue reflect.Value
344-
paramKey string
345-
paramValue any
346-
matched bool
347-
ok bool
345+
cachedFieldInfo *structcache.CachedFieldInfo
346+
fuzzLastKey string
347+
fieldValue reflect.Value
348+
paramKey string
349+
paramValue any
350+
matched bool
351+
ok bool
348352
)
349-
for _, fieldInfo = range cachedStructInfo.FieldConvertInfos {
350-
for _, fieldTag := range fieldInfo.PriorityTagAndFieldName {
353+
for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos {
354+
for _, fieldTag := range cachedFieldInfo.PriorityTagAndFieldName {
351355
if paramValue, ok = paramsMap[fieldTag]; !ok {
352356
continue
353357
}
354358
if _, ok = usedParamsKeyOrTagNameMap[fieldTag]; ok {
355359
matched = true
356360
break
357361
}
358-
fieldValue = fieldInfo.GetFieldReflectValue(structValue)
362+
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
359363
if err = bindVarToStructField(
360-
fieldValue, paramValue, fieldInfo, paramKeyToAttrMap,
364+
fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
361365
); err != nil {
362366
return err
363367
}
364368
// handle same field name in nested struct.
365-
if len(fieldInfo.OtherSameNameFieldIndex) > 0 {
369+
if len(cachedFieldInfo.OtherSameNameFieldIndex) > 0 {
366370
if err = setOtherSameNameField(
367-
fieldInfo, paramValue, structValue, paramKeyToAttrMap,
371+
cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
368372
); err != nil {
369373
return err
370374
}
@@ -378,26 +382,26 @@ func bindStructWithLoopFieldInfos(
378382
continue
379383
}
380384

381-
fuzzLastKey = fieldInfo.LastFuzzyKey.Load().(string)
385+
fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string)
382386
if paramValue, ok = paramsMap[fuzzLastKey]; !ok {
383387
paramKey, paramValue = fuzzyMatchingFieldName(
384-
fieldInfo.RemoveSymbolsFieldName, paramsMap, usedParamsKeyOrTagNameMap,
388+
cachedFieldInfo.RemoveSymbolsFieldName, paramsMap, usedParamsKeyOrTagNameMap,
385389
)
386390
ok = paramKey != ""
387-
fieldInfo.LastFuzzyKey.Store(paramKey)
391+
cachedFieldInfo.LastFuzzyKey.Store(paramKey)
388392
}
389393
if ok {
390-
fieldValue = fieldInfo.GetFieldReflectValue(structValue)
394+
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
391395
if paramValue != nil {
392396
if err = bindVarToStructField(
393-
fieldValue, paramValue, fieldInfo, paramKeyToAttrMap,
397+
fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
394398
); err != nil {
395399
return err
396400
}
397401
// handle same field name in nested struct.
398-
if len(fieldInfo.OtherSameNameFieldIndex) > 0 {
402+
if len(cachedFieldInfo.OtherSameNameFieldIndex) > 0 {
399403
if err = setOtherSameNameField(
400-
fieldInfo, paramValue, structValue, paramKeyToAttrMap,
404+
cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
401405
); err != nil {
402406
return err
403407
}
@@ -432,7 +436,7 @@ func fuzzyMatchingFieldName(
432436
func bindVarToStructField(
433437
fieldValue reflect.Value,
434438
srcValue interface{},
435-
fieldInfo *structcache.CachedFieldInfo,
439+
cachedFieldInfo *structcache.CachedFieldInfo,
436440
paramKeyToAttrMap map[string]string,
437441
) (err error) {
438442
if !fieldValue.IsValid() {
@@ -445,7 +449,7 @@ func bindVarToStructField(
445449
defer func() {
446450
if exception := recover(); exception != nil {
447451
if err = bindVarToReflectValue(fieldValue, srcValue, paramKeyToAttrMap); err != nil {
448-
err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, fieldInfo.FieldName())
452+
err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, cachedFieldInfo.FieldName())
449453
}
450454
}
451455
}()
@@ -460,27 +464,27 @@ func bindVarToStructField(
460464
customConverterInput reflect.Value
461465
ok bool
462466
)
463-
if fieldInfo.IsCustomConvert {
467+
if cachedFieldInfo.IsCustomConvert {
464468
if customConverterInput, ok = srcValue.(reflect.Value); !ok {
465469
customConverterInput = reflect.ValueOf(srcValue)
466470
}
467471
if ok, err = callCustomConverter(customConverterInput, fieldValue); ok || err != nil {
468472
return
469473
}
470474
}
471-
if fieldInfo.IsCommonInterface {
475+
if cachedFieldInfo.IsCommonInterface {
472476
if ok, err = bindVarToReflectValueWithInterfaceCheck(fieldValue, srcValue); ok || err != nil {
473477
return
474478
}
475479
}
476480
// Common types use fast assignment logic
477-
if fieldInfo.ConvertFunc != nil {
478-
fieldInfo.ConvertFunc(srcValue, fieldValue)
481+
if cachedFieldInfo.ConvertFunc != nil {
482+
cachedFieldInfo.ConvertFunc(srcValue, fieldValue)
479483
return nil
480484
}
481485
doConvertWithReflectValueSet(fieldValue, doConvertInput{
482486
FromValue: srcValue,
483-
ToTypeName: fieldInfo.StructField.Type.String(),
487+
ToTypeName: cachedFieldInfo.StructField.Type.String(),
484488
ReferValue: fieldValue,
485489
})
486490
return nil

0 commit comments

Comments
 (0)