mirror of
https://github.com/golang/go
synced 2024-11-22 06:34:40 -07:00
- FieldByName lookup through anonymous fields
- FieldByIndex - changed StructField.Index type from int -> []int - adjustments to reflect clients R=rsc,r DELTA=336 (263 added, 47 deleted, 26 changed) OCL=32731 CL=32802
This commit is contained in:
parent
3355cadf3f
commit
a288095813
@ -410,49 +410,6 @@ func (s *State) error(msg string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// getField searches in val, which must be a struct, for a field
|
|
||||||
// with the given name. It returns the value and the embedded depth
|
|
||||||
// where it was found.
|
|
||||||
//
|
|
||||||
func getField(val reflect.Value, fieldname string) (reflect.Value, int) {
|
|
||||||
// do we have a struct in the first place?
|
|
||||||
sval, ok := val.(*reflect.StructValue);
|
|
||||||
if !ok {
|
|
||||||
return nil, 0;
|
|
||||||
}
|
|
||||||
styp := sval.Type().(*reflect.StructType);
|
|
||||||
|
|
||||||
// look for field at the top level
|
|
||||||
if field, ok := styp.FieldByName(fieldname); ok {
|
|
||||||
return sval.Field(field.Index), 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for field in anonymous fields
|
|
||||||
var field reflect.Value;
|
|
||||||
level := 1000; // infinity (no struct has that many levels)
|
|
||||||
for i := 0; i < styp.NumField(); i++ {
|
|
||||||
f := styp.Field(i);
|
|
||||||
if f.Anonymous {
|
|
||||||
f, l := getField(sval.Field(i), fieldname);
|
|
||||||
// keep the most shallow field
|
|
||||||
if f != nil {
|
|
||||||
switch {
|
|
||||||
case l < level:
|
|
||||||
field, level = f, l;
|
|
||||||
case l == level:
|
|
||||||
// more than one field at the same level,
|
|
||||||
// possibly an error unless there is a more
|
|
||||||
// shallow field found later
|
|
||||||
field = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return field, level + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO At the moment, unnamed types are simply mapped to the default
|
// TODO At the moment, unnamed types are simply mapped to the default
|
||||||
// names below. For instance, all unnamed arrays are mapped to
|
// names below. For instance, all unnamed arrays are mapped to
|
||||||
// 'array' which is not really sufficient. Eventually one may want
|
// 'array' which is not really sufficient. Eventually one may want
|
||||||
@ -613,10 +570,13 @@ func (s *State) eval(fexpr expr, value reflect.Value, index int) bool {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
// value is value of named field
|
// value is value of named field
|
||||||
field, _ := getField(value, t.fieldName);
|
var field reflect.Value;
|
||||||
if field == nil {
|
if sval, ok := value.(*reflect.StructValue); ok {
|
||||||
// TODO consider just returning false in this case
|
field = sval.FieldByName(t.fieldName);
|
||||||
s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type()));
|
if field == nil {
|
||||||
|
// TODO consider just returning false in this case
|
||||||
|
s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
value = field;
|
value = field;
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ func (b *_StructBuilder) Key(k string) Builder {
|
|||||||
if v, ok := reflect.Indirect(b.val).(*reflect.StructValue); ok {
|
if v, ok := reflect.Indirect(b.val).(*reflect.StructValue); ok {
|
||||||
t := v.Type().(*reflect.StructType);
|
t := v.Type().(*reflect.StructType);
|
||||||
if field, ok := t.FieldByName(k); ok {
|
if field, ok := t.FieldByName(k); ok {
|
||||||
return &_StructBuilder{ v.Field(field.Index) }
|
return &_StructBuilder{ v.FieldByIndex(field.Index) }
|
||||||
}
|
}
|
||||||
// Again, case-insensitive.
|
// Again, case-insensitive.
|
||||||
for i := 0; i < t.NumField(); i++ {
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
@ -818,3 +818,147 @@ func TestInterfaceSet(t *testing.T) {
|
|||||||
t.Errorf("Interface Method returned %d; want 250", i);
|
t.Errorf("Interface Method returned %d; want 250", i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type T1 struct { a string; int; }
|
||||||
|
|
||||||
|
func TestAnonymousFields(t *testing.T) {
|
||||||
|
var field StructField;
|
||||||
|
var ok bool;
|
||||||
|
var t1 T1;
|
||||||
|
type1 := Typeof(t1).(*StructType);
|
||||||
|
if field, ok = type1.FieldByName("int"); !ok {
|
||||||
|
t.Error("no field 'int'");
|
||||||
|
}
|
||||||
|
if field.Index[0] != 1 {
|
||||||
|
t.Error("field index should be 1; is", field.Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FTest struct {
|
||||||
|
s interface{};
|
||||||
|
name string;
|
||||||
|
index []int;
|
||||||
|
value int;
|
||||||
|
}
|
||||||
|
|
||||||
|
type S0 struct {
|
||||||
|
a, b, c, d, d int;
|
||||||
|
}
|
||||||
|
|
||||||
|
type S1 struct {
|
||||||
|
b int;
|
||||||
|
S0;
|
||||||
|
}
|
||||||
|
|
||||||
|
type S2 struct {
|
||||||
|
a int;
|
||||||
|
*S1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type S3 struct {
|
||||||
|
S1;
|
||||||
|
S2;
|
||||||
|
d, e int;
|
||||||
|
*S1;
|
||||||
|
}
|
||||||
|
|
||||||
|
type S4 struct {
|
||||||
|
*S4;
|
||||||
|
a int;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fieldTests = []FTest {
|
||||||
|
FTest{ struct{ }{}, "", nil, 0 },
|
||||||
|
FTest{ struct{ }{}, "foo", nil, 0 },
|
||||||
|
FTest{ S0{a: 'a'}, "a", []int{0}, 'a' },
|
||||||
|
FTest{ S0{}, "d", nil, 0 },
|
||||||
|
FTest{ S1{S0: S0{a: 'a'}}, "a", []int{1, 0}, 'a' },
|
||||||
|
FTest{ S1{b: 'b'}, "b", []int{0}, 'b' },
|
||||||
|
FTest{ S1{}, "S0", []int{1}, 0 },
|
||||||
|
FTest{ S1{S0: S0{c: 'c'}}, "c", []int{1, 2}, 'c' },
|
||||||
|
FTest{ S2{a: 'a'}, "a", []int{0}, 'a' },
|
||||||
|
FTest{ S2{}, "S1", []int{1}, 0 },
|
||||||
|
FTest{ S2{S1: &S1{b: 'b'}}, "b", []int{1, 0}, 'b' },
|
||||||
|
FTest{ S2{S1: &S1{S0: S0{c: 'c'}}}, "c", []int{1, 1, 2}, 'c' },
|
||||||
|
FTest{ S2{}, "d", nil, 0 },
|
||||||
|
FTest{ S3{}, "S1", nil, 0 },
|
||||||
|
FTest{ S3{S2: S2{a: 'a'}}, "a", []int{1, 0}, 'a' },
|
||||||
|
FTest{ S3{}, "b", nil, 0 },
|
||||||
|
FTest{ S3{d: 'd'}, "d", []int{2}, 0 },
|
||||||
|
FTest{ S3{e: 'e'}, "e", []int{3}, 'e' },
|
||||||
|
FTest{ S4{a: 'a'}, "a", []int{1}, 'a' },
|
||||||
|
FTest{ S4{}, "b", nil, 0 },
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFieldByIndex(t *testing.T) {
|
||||||
|
for _, test := range fieldTests {
|
||||||
|
s := Typeof(test.s).(*StructType);
|
||||||
|
f := s.FieldByIndex(test.index);
|
||||||
|
if f.Name != "" {
|
||||||
|
if test.index != nil {
|
||||||
|
if f.Name != test.name {
|
||||||
|
t.Errorf("%s.%s found; want %s", s.Name(), f.Name, test.name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s.%s found", s.Name(), f.Name);
|
||||||
|
}
|
||||||
|
} else if len(test.index) > 0 {
|
||||||
|
t.Errorf("%s.%s not found", s.Name(), test.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.value != 0 {
|
||||||
|
v := reflect.NewValue(test.s).(*reflect.StructValue).FieldByIndex(test.index);
|
||||||
|
if v != nil {
|
||||||
|
if x, ok := v.Interface().(int); ok {
|
||||||
|
if x != test.value {
|
||||||
|
t.Errorf("%s%v is %d; want %d", s.Name(), test.index, x, test.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s%v value not an int", s.Name(), test.index);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s%v value not found", s.Name(), test.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFieldByName(t *testing.T) {
|
||||||
|
for _, test := range fieldTests {
|
||||||
|
s := Typeof(test.s).(*StructType);
|
||||||
|
f, found := s.FieldByName(test.name);
|
||||||
|
if found {
|
||||||
|
if test.index != nil {
|
||||||
|
// Verify field depth and index.
|
||||||
|
if len(f.Index) != len(test.index) {
|
||||||
|
t.Errorf("%s.%s depth %d; want %d", s.Name(), test.name, len(f.Index), len(test.index));
|
||||||
|
} else {
|
||||||
|
for i, x := range f.Index {
|
||||||
|
if x != test.index[i] {
|
||||||
|
t.Errorf("%s.%s.Index[%d] is %d; want %d", s.Name(), test.name, i, x, test.index[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s.%s found", s.Name(), f.Name);
|
||||||
|
}
|
||||||
|
} else if len(test.index) > 0 {
|
||||||
|
t.Errorf("%s.%s not found", s.Name(), test.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.value != 0 {
|
||||||
|
v := reflect.NewValue(test.s).(*reflect.StructValue).FieldByName(test.name);
|
||||||
|
if v != nil {
|
||||||
|
if x, ok := v.Interface().(int); ok {
|
||||||
|
if x != test.value {
|
||||||
|
t.Errorf("%s.%s is %d; want %d", s.Name(), test.name, x, test.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s.%s value not an int", s.Name(), test.name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s.%s value not found", s.Name(), test.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -463,7 +463,7 @@ type StructField struct {
|
|||||||
Type Type;
|
Type Type;
|
||||||
Tag string;
|
Tag string;
|
||||||
Offset uintptr;
|
Offset uintptr;
|
||||||
Index int;
|
Index []int;
|
||||||
Anonymous bool;
|
Anonymous bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,24 +491,107 @@ func (t *StructType) Field(i int) (f StructField) {
|
|||||||
f.Tag = *p.tag;
|
f.Tag = *p.tag;
|
||||||
}
|
}
|
||||||
f.Offset = p.offset;
|
f.Offset = p.offset;
|
||||||
f.Index = i;
|
f.Index = []int{i};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FieldByName returns the field with the provided name and a boolean to indicate
|
// TODO(gri): Should there be an error/bool indicator if the index
|
||||||
// that the field was found.
|
// is wrong for FieldByIndex?
|
||||||
func (t *StructType) FieldByName(name string) (f StructField, present bool) {
|
|
||||||
for i, p := range t.fields {
|
// FieldByIndex returns the nested field corresponding to index.
|
||||||
ff := t.Field(i);
|
func (t *StructType) FieldByIndex(index []int) (f StructField) {
|
||||||
if ff.Name == name {
|
for i, x := range index {
|
||||||
f = ff;
|
if i > 0 {
|
||||||
present = true;
|
ft := f.Type;
|
||||||
break;
|
if pt, ok := ft.(*PtrType); ok {
|
||||||
|
ft = pt.Elem();
|
||||||
|
}
|
||||||
|
if st, ok := ft.(*StructType); ok {
|
||||||
|
t = st;
|
||||||
|
} else {
|
||||||
|
var f0 StructField;
|
||||||
|
f = f0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
f = t.Field(x);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inf = 1 << 30; // infinity - no struct has that many nesting levels
|
||||||
|
|
||||||
|
func (t *StructType) fieldByName(name string, mark map[*StructType]bool, depth int) (ff StructField, fd int) {
|
||||||
|
fd = inf; // field depth
|
||||||
|
|
||||||
|
if _, marked := mark[t]; marked {
|
||||||
|
// Struct already seen.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mark[t] = true;
|
||||||
|
|
||||||
|
var fi int; // field index
|
||||||
|
L: for i, _ := range t.fields {
|
||||||
|
f := t.Field(i);
|
||||||
|
d := inf;
|
||||||
|
switch {
|
||||||
|
case f.Name == name:
|
||||||
|
// Matching top-level field.
|
||||||
|
d = depth;
|
||||||
|
case f.Anonymous:
|
||||||
|
ft := f.Type;
|
||||||
|
if pt, ok := ft.(*PtrType); ok {
|
||||||
|
ft = pt.Elem();
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ft.Name() == name:
|
||||||
|
// Matching anonymous top-level field.
|
||||||
|
d = depth;
|
||||||
|
case fd > 0:
|
||||||
|
// No top-level field yet; look inside nested structs.
|
||||||
|
if st, ok := ft.(*StructType); ok {
|
||||||
|
f, d = st.fieldByName(name, mark, depth+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case d < fd:
|
||||||
|
// Found field at shallower depth.
|
||||||
|
ff, fi, fd = f, i, d;
|
||||||
|
case d == fd:
|
||||||
|
// More than one matching field at the same depth (or d, fd == inf).
|
||||||
|
// Same as no field found.
|
||||||
|
fd = inf;
|
||||||
|
if d == depth {
|
||||||
|
// Impossible to find a field at lower depth.
|
||||||
|
break L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fd < inf {
|
||||||
|
// Found matching field.
|
||||||
|
if len(ff.Index) <= depth {
|
||||||
|
ff.Index = make([]int, depth+1);
|
||||||
|
}
|
||||||
|
ff.Index[depth] = fi;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark[t] = false, false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldByName returns the struct field with the given name
|
||||||
|
// and a boolean to indicate if the field was found.
|
||||||
|
func (t *StructType) FieldByName(name string) (f StructField, present bool) {
|
||||||
|
if ff, fd := t.fieldByName(name, make(map[*StructType]bool), 0); fd < inf {
|
||||||
|
ff.Index = ff.Index[0 : fd+1];
|
||||||
|
f, present = ff, true;
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// NumField returns the number of struct fields.
|
// NumField returns the number of struct fields.
|
||||||
func (t *StructType) NumField() int {
|
func (t *StructType) NumField() int {
|
||||||
return len(t.fields);
|
return len(t.fields);
|
||||||
|
@ -1126,6 +1126,35 @@ func (v *StructValue) Field(i int) Value {
|
|||||||
return newValue(f.Type, addr(uintptr(v.addr)+f.Offset), v.canSet && f.PkgPath == "");
|
return newValue(f.Type, addr(uintptr(v.addr)+f.Offset), v.canSet && f.PkgPath == "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FieldByIndex returns the nested field corresponding to index.
|
||||||
|
func (t *StructValue) FieldByIndex(index []int) (v Value) {
|
||||||
|
v = t;
|
||||||
|
for i, x := range index {
|
||||||
|
if i > 0 {
|
||||||
|
if p, ok := v.(*PtrValue); ok {
|
||||||
|
v = p.Elem();
|
||||||
|
}
|
||||||
|
if s, ok := v.(*StructValue); ok {
|
||||||
|
t = s;
|
||||||
|
} else {
|
||||||
|
v = nil;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v = t.Field(x);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldByName returns the struct field with the given name.
|
||||||
|
// The result is nil if no field was found.
|
||||||
|
func (t *StructValue) FieldByName(name string) Value {
|
||||||
|
if f, ok := t.Type().(*StructType).FieldByName(name); ok {
|
||||||
|
return t.FieldByIndex(f.Index);
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
// NumField returns the number of fields in the struct.
|
// NumField returns the number of fields in the struct.
|
||||||
func (v *StructValue) NumField() int {
|
func (v *StructValue) NumField() int {
|
||||||
return v.typ.(*StructType).NumField();
|
return v.typ.(*StructType).NumField();
|
||||||
|
@ -585,7 +585,7 @@ func (st *state) findVar(s string) reflect.Value {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
data = data.(*reflect.StructValue).Field(field.Index);
|
data = data.(*reflect.StructValue).FieldByIndex(field.Index);
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user