106 lines
3.0 KiB
Go
106 lines
3.0 KiB
Go
// Copyright 2021 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package reflect
|
|
|
|
// VisibleFields returns all the visible fields in t, which must be a
|
|
// struct type. A field is defined as visible if it's accessible
|
|
// directly with a FieldByName call. The returned fields include fields
|
|
// inside anonymous struct members and unexported fields. They follow
|
|
// the same order found in the struct, with anonymous fields followed
|
|
// immediately by their promoted fields.
|
|
//
|
|
// For each element e of the returned slice, the corresponding field
|
|
// can be retrieved from a value v of type t by calling v.FieldByIndex(e.Index).
|
|
func VisibleFields(t Type) []StructField {
|
|
if t == nil {
|
|
panic("reflect: VisibleFields(nil)")
|
|
}
|
|
if t.Kind() != Struct {
|
|
panic("reflect.VisibleFields of non-struct type")
|
|
}
|
|
w := &visibleFieldsWalker{
|
|
byName: make(map[string]int),
|
|
visiting: make(map[Type]bool),
|
|
fields: make([]StructField, 0, t.NumField()),
|
|
index: make([]int, 0, 2),
|
|
}
|
|
w.walk(t)
|
|
// Remove all the fields that have been hidden.
|
|
// Use an in-place removal that avoids copying in
|
|
// the common case that there are no hidden fields.
|
|
j := 0
|
|
for i := range w.fields {
|
|
f := &w.fields[i]
|
|
if f.Name == "" {
|
|
continue
|
|
}
|
|
if i != j {
|
|
// A field has been removed. We need to shuffle
|
|
// all the subsequent elements up.
|
|
w.fields[j] = *f
|
|
}
|
|
j++
|
|
}
|
|
return w.fields[:j]
|
|
}
|
|
|
|
type visibleFieldsWalker struct {
|
|
byName map[string]int
|
|
visiting map[Type]bool
|
|
fields []StructField
|
|
index []int
|
|
}
|
|
|
|
// walk walks all the fields in the struct type t, visiting
|
|
// fields in index preorder and appending them to w.fields
|
|
// (this maintains the required ordering).
|
|
// Fields that have been overridden have their
|
|
// Name field cleared.
|
|
func (w *visibleFieldsWalker) walk(t Type) {
|
|
if w.visiting[t] {
|
|
return
|
|
}
|
|
w.visiting[t] = true
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
w.index = append(w.index, i)
|
|
add := true
|
|
if oldIndex, ok := w.byName[f.Name]; ok {
|
|
old := &w.fields[oldIndex]
|
|
if len(w.index) == len(old.Index) {
|
|
// Fields with the same name at the same depth
|
|
// cancel one another out. Set the field name
|
|
// to empty to signify that has happened, and
|
|
// there's no need to add this field.
|
|
old.Name = ""
|
|
add = false
|
|
} else if len(w.index) < len(old.Index) {
|
|
// The old field loses because it's deeper than the new one.
|
|
old.Name = ""
|
|
} else {
|
|
// The old field wins because it's shallower than the new one.
|
|
add = false
|
|
}
|
|
}
|
|
if add {
|
|
// Copy the index so that it's not overwritten
|
|
// by the other appends.
|
|
f.Index = append([]int(nil), w.index...)
|
|
w.byName[f.Name] = len(w.fields)
|
|
w.fields = append(w.fields, f)
|
|
}
|
|
if f.Anonymous {
|
|
if f.Type.Kind() == Ptr {
|
|
f.Type = f.Type.Elem()
|
|
}
|
|
if f.Type.Kind() == Struct {
|
|
w.walk(f.Type)
|
|
}
|
|
}
|
|
w.index = w.index[:len(w.index)-1]
|
|
}
|
|
delete(w.visiting, t)
|
|
}
|