reflect: add FieldByName(), and FieldByIndex()
Этот коммит содержится в:
родитель
9f02340a26
коммит
fa4f361ca7
3 изменённых файлов: 184 добавлений и 45 удалений
|
@ -575,6 +575,40 @@ func (t *rawType) Field(i int) StructField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rawStructFieldFromPointer(fieldType *rawType, data unsafe.Pointer, flagsByte uint8, name string, offset uintptr) rawStructField {
|
||||||
|
// Read the field tag, if there is one.
|
||||||
|
var tag string
|
||||||
|
if flagsByte&structFieldFlagHasTag != 0 {
|
||||||
|
data = unsafe.Add(data, 1) // C: data+1
|
||||||
|
tagLen := uintptr(*(*byte)(data))
|
||||||
|
data = unsafe.Add(data, 1) // C: data+1
|
||||||
|
tag = *(*string)(unsafe.Pointer(&stringHeader{
|
||||||
|
data: data,
|
||||||
|
len: tagLen,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the PkgPath to some (arbitrary) value if the package path is not
|
||||||
|
// exported.
|
||||||
|
pkgPath := ""
|
||||||
|
if flagsByte&structFieldFlagIsExported == 0 {
|
||||||
|
// This field is unexported.
|
||||||
|
// TODO: list the real package path here. Storing it should not
|
||||||
|
// significantly impact binary size as there is only a limited
|
||||||
|
// number of packages in any program.
|
||||||
|
pkgPath = "<unimplemented>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawStructField{
|
||||||
|
Name: name,
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
Type: fieldType,
|
||||||
|
Tag: StructTag(tag),
|
||||||
|
Anonymous: flagsByte&structFieldFlagAnonymous != 0,
|
||||||
|
Offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// rawField returns nearly the same value as Field but without converting the
|
// rawField returns nearly the same value as Field but without converting the
|
||||||
// Type member to an interface.
|
// Type member to an interface.
|
||||||
//
|
//
|
||||||
|
@ -611,49 +645,56 @@ func (t *rawType) rawField(n int) rawStructField {
|
||||||
flagsByte := *(*byte)(data)
|
flagsByte := *(*byte)(data)
|
||||||
data = unsafe.Add(data, 1)
|
data = unsafe.Add(data, 1)
|
||||||
|
|
||||||
// Read the field name.
|
name := readStringZ(data)
|
||||||
nameStart := data
|
data = unsafe.Add(data, len(name))
|
||||||
var nameLen uintptr
|
|
||||||
for *(*byte)(data) != 0 {
|
|
||||||
nameLen++
|
|
||||||
data = unsafe.Add(data, 1) // C: data++
|
|
||||||
}
|
|
||||||
name := *(*string)(unsafe.Pointer(&stringHeader{
|
|
||||||
data: nameStart,
|
|
||||||
len: nameLen,
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Read the field tag, if there is one.
|
return rawStructFieldFromPointer(field.fieldType, data, flagsByte, name, offset)
|
||||||
var tag string
|
}
|
||||||
if flagsByte&structFieldFlagHasTag != 0 {
|
|
||||||
data = unsafe.Add(data, 1) // C: data+1
|
// rawFieldByName returns nearly the same value as FieldByName but without converting the
|
||||||
tagLen := uintptr(*(*byte)(data))
|
// Type member to an interface.
|
||||||
data = unsafe.Add(data, 1) // C: data+1
|
//
|
||||||
tag = *(*string)(unsafe.Pointer(&stringHeader{
|
// For internal use only.
|
||||||
data: data,
|
func (t *rawType) rawFieldByName(n string) (rawStructField, int, bool) {
|
||||||
len: tagLen,
|
if t.Kind() != Struct {
|
||||||
}))
|
panic(&TypeError{"Field"})
|
||||||
|
}
|
||||||
|
descriptor := (*structType)(unsafe.Pointer(t.underlying()))
|
||||||
|
|
||||||
|
// Iterate over all the fields looking for the matching name
|
||||||
|
// Also calculate field offset.
|
||||||
|
|
||||||
|
field := &descriptor.fields[0]
|
||||||
|
var offset uintptr = 0
|
||||||
|
for i := uint16(0); i < descriptor.numField; i++ {
|
||||||
|
data := field.data
|
||||||
|
|
||||||
|
// Read some flags of this field, like whether the field is an embedded
|
||||||
|
// field. See structFieldFlagAnonymous and similar flags.
|
||||||
|
flagsByte := *(*byte)(data)
|
||||||
|
data = unsafe.Add(data, 1)
|
||||||
|
|
||||||
|
name := readStringZ(data)
|
||||||
|
data = unsafe.Add(data, len(name))
|
||||||
|
if name == n {
|
||||||
|
return rawStructFieldFromPointer(field.fieldType, data, flagsByte, name, offset), int(i), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the PkgPath to some (arbitrary) value if the package path is not
|
// update offset/field pointer if there *is* a next field
|
||||||
// exported.
|
if i < descriptor.numField-1 {
|
||||||
pkgPath := ""
|
offset += field.fieldType.Size()
|
||||||
if flagsByte&structFieldFlagIsExported == 0 {
|
|
||||||
// This field is unexported.
|
// Increment pointer to the next field.
|
||||||
// TODO: list the real package path here. Storing it should not
|
field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{})))
|
||||||
// significantly impact binary size as there is only a limited
|
|
||||||
// number of packages in any program.
|
// Align the offset for the next field.
|
||||||
pkgPath = "<unimplemented>"
|
offset = align(offset, uintptr(field.fieldType.Align()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawStructField{
|
// No match
|
||||||
Name: name,
|
return rawStructField{}, 0, false
|
||||||
PkgPath: pkgPath,
|
|
||||||
Type: field.fieldType,
|
|
||||||
Tag: StructTag(tag),
|
|
||||||
Anonymous: flagsByte&structFieldFlagAnonymous != 0,
|
|
||||||
Offset: offset,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bits returns the number of bits that this type uses. It is only valid for
|
// Bits returns the number of bits that this type uses. It is only valid for
|
||||||
|
@ -959,12 +1000,49 @@ func (t *rawType) PkgPath() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t rawType) FieldByName(name string) (StructField, bool) {
|
func (t *rawType) FieldByName(name string) (StructField, bool) {
|
||||||
panic("unimplemented: (reflect.Type).FieldByName()")
|
if t.Kind() != Struct {
|
||||||
|
panic(TypeError{"FieldByName"})
|
||||||
|
}
|
||||||
|
|
||||||
|
field, index, ok := t.rawFieldByName(name)
|
||||||
|
if !ok {
|
||||||
|
return StructField{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return StructField{
|
||||||
|
Name: field.Name,
|
||||||
|
PkgPath: field.PkgPath,
|
||||||
|
Type: field.Type, // note: converts rawType to Type
|
||||||
|
Tag: field.Tag,
|
||||||
|
Anonymous: field.Anonymous,
|
||||||
|
Offset: field.Offset,
|
||||||
|
Index: []int{index},
|
||||||
|
}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t rawType) FieldByIndex(index []int) StructField {
|
func (t *rawType) FieldByIndex(index []int) StructField {
|
||||||
panic("unimplemented: (reflect.Type).FieldByIndex()")
|
ftype := t
|
||||||
|
var field rawStructField
|
||||||
|
|
||||||
|
for _, n := range index {
|
||||||
|
if ftype.Kind() != Struct {
|
||||||
|
panic(TypeError{"FieldByIndex"})
|
||||||
|
}
|
||||||
|
|
||||||
|
field = ftype.rawField(n)
|
||||||
|
ftype = field.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
return StructField{
|
||||||
|
Name: field.Name,
|
||||||
|
PkgPath: field.PkgPath,
|
||||||
|
Type: field.Type, // note: converts rawType to Type
|
||||||
|
Tag: field.Tag,
|
||||||
|
Anonymous: field.Anonymous,
|
||||||
|
Offset: field.Offset,
|
||||||
|
Index: index,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A StructField describes a single field in a struct.
|
// A StructField describes a single field in a struct.
|
||||||
|
|
|
@ -1400,7 +1400,24 @@ func (v Value) SetMapIndex(key, elem Value) {
|
||||||
|
|
||||||
// FieldByIndex returns the nested field corresponding to index.
|
// FieldByIndex returns the nested field corresponding to index.
|
||||||
func (v Value) FieldByIndex(index []int) Value {
|
func (v Value) FieldByIndex(index []int) Value {
|
||||||
panic("unimplemented: (reflect.Value).FieldByIndex()")
|
if len(index) == 1 {
|
||||||
|
return v.Field(index[0])
|
||||||
|
}
|
||||||
|
if v.Kind() != Struct {
|
||||||
|
panic(&ValueError{"FieldByIndex", v.Kind()})
|
||||||
|
}
|
||||||
|
for i, x := range index {
|
||||||
|
if i > 0 {
|
||||||
|
if v.Kind() == Pointer && v.typecode.elem().Kind() == Struct {
|
||||||
|
if v.IsNil() {
|
||||||
|
panic("reflect: indirection through nil pointer to embedded struct")
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v = v.Field(x)
|
||||||
|
}
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// FieldByIndexErr returns the nested field corresponding to index.
|
// FieldByIndexErr returns the nested field corresponding to index.
|
||||||
|
@ -1409,7 +1426,14 @@ func (v Value) FieldByIndexErr(index []int) (Value, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) FieldByName(name string) Value {
|
func (v Value) FieldByName(name string) Value {
|
||||||
panic("unimplemented: (reflect.Value).FieldByName()")
|
if v.Kind() != Struct {
|
||||||
|
panic(&ValueError{"FieldByName", v.Kind()})
|
||||||
|
}
|
||||||
|
|
||||||
|
if field, ok := v.typecode.FieldByName(name); ok {
|
||||||
|
return v.FieldByIndex(field.Index)
|
||||||
|
}
|
||||||
|
return Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname hashmapMake runtime.hashmapMakeUnsafePointer
|
//go:linkname hashmapMake runtime.hashmapMakeUnsafePointer
|
||||||
|
|
|
@ -266,6 +266,43 @@ func TestNamedTypes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStruct(t *testing.T) {
|
||||||
|
type barStruct struct {
|
||||||
|
QuxString string
|
||||||
|
BazInt int
|
||||||
|
}
|
||||||
|
|
||||||
|
type foobar struct {
|
||||||
|
Foo string `foo:"struct tag"`
|
||||||
|
Bar barStruct
|
||||||
|
}
|
||||||
|
|
||||||
|
var fb foobar
|
||||||
|
fb.Bar.QuxString = "qux"
|
||||||
|
|
||||||
|
reffb := TypeOf(fb)
|
||||||
|
|
||||||
|
q := reffb.FieldByIndex([]int{1, 0})
|
||||||
|
if want := "QuxString"; q.Name != want {
|
||||||
|
t.Errorf("FieldByIndex=%v, want %v", q.Name, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
q, ok = reffb.FieldByName("Foo")
|
||||||
|
if q.Name != "Foo" || !ok {
|
||||||
|
t.Errorf("FieldByName(Foo)=%v,%v, want Foo, true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := q.Tag, `foo:"struct tag"`; string(got) != want {
|
||||||
|
t.Errorf("StrucTag for Foo=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
q, ok = reffb.FieldByName("Snorble")
|
||||||
|
if q.Name != "" || ok {
|
||||||
|
t.Errorf("FieldByName(Snorble)=%v,%v, want ``, false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestZero(t *testing.T) {
|
func TestZero(t *testing.T) {
|
||||||
s := "hello, world"
|
s := "hello, world"
|
||||||
var sptr *string = &s
|
var sptr *string = &s
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче