Skip to content

Commit a3c5eb5

Browse files
committed
reflect: Implement FieldByNameFunc in terms of VisibleFields
This simplifies things quite a bit, and ensures that the output of FieldByNameFunc() always agrees with the output of VisibleFields(). To simplify it even further, define FieldByName() in terms of FieldByNameFunc(), passing it a closure. Signed-off-by: L. Pereira <[email protected]>
1 parent 8dc37b4 commit a3c5eb5

File tree

1 file changed

+20
-124
lines changed

1 file changed

+20
-124
lines changed

src/reflect/type.go

Lines changed: 20 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -724,100 +724,6 @@ func (t *rawType) rawField(n int) rawStructField {
724724
return rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset)
725725
}
726726

727-
// rawFieldByNameFunc returns nearly the same value as FieldByNameFunc but without converting the
728-
// Type member to an interface.
729-
//
730-
// For internal use only.
731-
func (t *rawType) rawFieldByNameFunc(match func(string) bool) (rawStructField, []int, bool) {
732-
if t.Kind() != Struct {
733-
panic(errTypeField)
734-
}
735-
736-
type fieldWalker struct {
737-
t *rawType
738-
index []int
739-
}
740-
741-
queue := make([]fieldWalker, 0, 4)
742-
queue = append(queue, fieldWalker{t, nil})
743-
744-
for len(queue) > 0 {
745-
type result struct {
746-
r rawStructField
747-
index []int
748-
}
749-
750-
var found []result
751-
var nextlevel []fieldWalker
752-
753-
// For all the structs at this level..
754-
for _, ll := range queue {
755-
// Iterate over all the fields looking for the matching name
756-
// Also calculate field offset.
757-
758-
descriptor := (*structType)(unsafe.Pointer(ll.t.underlying()))
759-
field := &descriptor.fields[0]
760-
761-
for i := uint16(0); i < descriptor.numField; i++ {
762-
data := field.data
763-
764-
// Read some flags of this field, like whether the field is an embedded
765-
// field. See structFieldFlagAnonymous and similar flags.
766-
flagsByte := *(*byte)(data)
767-
data = unsafe.Add(data, 1)
768-
769-
offset, lenOffs := uvarint32(unsafe.Slice((*byte)(data), maxVarintLen32))
770-
data = unsafe.Add(data, lenOffs)
771-
772-
name := readStringZ(data)
773-
data = unsafe.Add(data, len(name))
774-
if match(name) {
775-
found = append(found, result{
776-
rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset),
777-
append(ll.index, int(i)),
778-
})
779-
}
780-
781-
structOrPtrToStruct := field.fieldType.Kind() == Struct || (field.fieldType.Kind() == Pointer && field.fieldType.elem().Kind() == Struct)
782-
if flagsByte&structFieldFlagIsEmbedded == structFieldFlagIsEmbedded && structOrPtrToStruct {
783-
embedded := field.fieldType
784-
if embedded.Kind() == Pointer {
785-
embedded = embedded.elem()
786-
}
787-
788-
nextlevel = append(nextlevel, fieldWalker{
789-
t: embedded,
790-
index: append(ll.index, int(i)),
791-
})
792-
}
793-
794-
// update offset/field pointer if there *is* a next field
795-
if i < descriptor.numField-1 {
796-
// Increment pointer to the next field.
797-
field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{})))
798-
}
799-
}
800-
}
801-
802-
// found multiple hits at this level
803-
if len(found) > 1 {
804-
return rawStructField{}, nil, false
805-
}
806-
807-
// found the field we were looking for
808-
if len(found) == 1 {
809-
r := found[0]
810-
return r.r, r.index, true
811-
}
812-
813-
// else len(found) == 0, move on to the next level
814-
queue = append(queue[:0], nextlevel...)
815-
}
816-
817-
// didn't find it
818-
return rawStructField{}, nil, false
819-
}
820-
821727
// Bits returns the number of bits that this type uses. It is only valid for
822728
// arithmetic types (integers, floats, and complex numbers). For other types, it
823729
// will panic.
@@ -1096,46 +1002,36 @@ func (t *rawType) PkgPath() string {
10961002
return ""
10971003
}
10981004

1005+
//go:inline
10991006
func (t *rawType) FieldByName(name string) (StructField, bool) {
1100-
if t.Kind() != Struct {
1101-
panic(errTypeFieldByName)
1102-
}
1103-
1104-
field, index, ok := t.rawFieldByNameFunc(func(n string) bool { return n == name })
1105-
if !ok {
1106-
return StructField{}, false
1107-
}
1108-
1109-
return StructField{
1110-
Name: field.Name,
1111-
PkgPath: field.PkgPath,
1112-
Type: field.Type, // note: converts rawType to Type
1113-
Tag: field.Tag,
1114-
Anonymous: field.Anonymous,
1115-
Offset: field.Offset,
1116-
Index: index,
1117-
}, true
1007+
return t.FieldByNameFunc(func(n string) bool { return n == name })
11181008
}
11191009

11201010
func (t *rawType) FieldByNameFunc(match func(string) bool) (StructField, bool) {
11211011
if t.Kind() != Struct {
11221012
panic(TypeError{"FieldByNameFunc"})
11231013
}
11241014

1125-
field, index, ok := t.rawFieldByNameFunc(match)
1126-
if !ok {
1127-
return StructField{}, false
1015+
var matchedField *StructField
1016+
for _, field := range VisibleFields(Type(t)) {
1017+
if !match(field.Name) {
1018+
continue
1019+
}
1020+
1021+
if matchedField == nil || len(field.Index) < len(matchedField.Index) {
1022+
// First hit, or hit is in a shallower level
1023+
matchedField = &field
1024+
} else if len(field.Index) == len(matchedField.Index) {
1025+
// Multiple hits at this level
1026+
break
1027+
}
11281028
}
11291029

1130-
return StructField{
1131-
Name: field.Name,
1132-
PkgPath: field.PkgPath,
1133-
Type: field.Type, // note: converts rawType to Type
1134-
Tag: field.Tag,
1135-
Anonymous: field.Anonymous,
1136-
Offset: field.Offset,
1137-
Index: index,
1138-
}, true
1030+
if matchedField != nil {
1031+
return *matchedField, true
1032+
}
1033+
1034+
return StructField{}, false
11391035
}
11401036

11411037
func (t *rawType) FieldByIndex(index []int) StructField {

0 commit comments

Comments
 (0)