compiler, reflect: properly handle embedded structs
Этот коммит содержится в:
родитель
15109a2924
коммит
93fb897feb
2 изменённых файлов: 98 добавлений и 36 удалений
|
@ -57,6 +57,7 @@ const (
|
||||||
structFieldFlagAnonymous = 1 << iota
|
structFieldFlagAnonymous = 1 << iota
|
||||||
structFieldFlagHasTag
|
structFieldFlagHasTag
|
||||||
structFieldFlagIsExported
|
structFieldFlagIsExported
|
||||||
|
structFieldFlagIsEmbedded
|
||||||
)
|
)
|
||||||
|
|
||||||
// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
|
// createMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction.
|
||||||
|
@ -243,6 +244,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||||
if token.IsExported(field.Name()) {
|
if token.IsExported(field.Name()) {
|
||||||
flags |= structFieldFlagIsExported
|
flags |= structFieldFlagIsExported
|
||||||
}
|
}
|
||||||
|
if field.Embedded() {
|
||||||
|
flags |= structFieldFlagIsEmbedded
|
||||||
|
}
|
||||||
data := string(flags) + field.Name() + "\x00"
|
data := string(flags) + field.Name() + "\x00"
|
||||||
if typ.Tag(i) != "" {
|
if typ.Tag(i) != "" {
|
||||||
if len(typ.Tag(i)) > 0xff {
|
if len(typ.Tag(i)) > 0xff {
|
||||||
|
|
|
@ -69,6 +69,7 @@ const (
|
||||||
structFieldFlagAnonymous = 1 << iota
|
structFieldFlagAnonymous = 1 << iota
|
||||||
structFieldFlagHasTag
|
structFieldFlagHasTag
|
||||||
structFieldFlagIsExported
|
structFieldFlagIsExported
|
||||||
|
structFieldFlagIsEmbedded
|
||||||
)
|
)
|
||||||
|
|
||||||
type Kind uint8
|
type Kind uint8
|
||||||
|
@ -655,46 +656,98 @@ func (t *rawType) rawField(n int) rawStructField {
|
||||||
// Type member to an interface.
|
// Type member to an interface.
|
||||||
//
|
//
|
||||||
// For internal use only.
|
// For internal use only.
|
||||||
func (t *rawType) rawFieldByName(n string) (rawStructField, int, bool) {
|
func (t *rawType) rawFieldByName(n string) (rawStructField, []int, bool) {
|
||||||
if t.Kind() != Struct {
|
if t.Kind() != Struct {
|
||||||
panic(&TypeError{"Field"})
|
panic(&TypeError{"Field"})
|
||||||
}
|
}
|
||||||
descriptor := (*structType)(unsafe.Pointer(t.underlying()))
|
|
||||||
|
|
||||||
// Iterate over all the fields looking for the matching name
|
type fieldWalker struct {
|
||||||
// Also calculate field offset.
|
t *rawType
|
||||||
|
index []int
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// update offset/field pointer if there *is* a next field
|
|
||||||
if i < descriptor.numField-1 {
|
|
||||||
offset += field.fieldType.Size()
|
|
||||||
|
|
||||||
// Increment pointer to the next field.
|
|
||||||
field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{})))
|
|
||||||
|
|
||||||
// Align the offset for the next field.
|
|
||||||
offset = align(offset, uintptr(field.fieldType.Align()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No match
|
queue := make([]fieldWalker, 0, 4)
|
||||||
return rawStructField{}, 0, false
|
queue = append(queue, fieldWalker{t, nil})
|
||||||
|
|
||||||
|
for len(queue) > 0 {
|
||||||
|
type result struct {
|
||||||
|
r rawStructField
|
||||||
|
index []int
|
||||||
|
}
|
||||||
|
|
||||||
|
var found []result
|
||||||
|
var nextlevel []fieldWalker
|
||||||
|
|
||||||
|
// For all the structs at this level..
|
||||||
|
for _, ll := range queue {
|
||||||
|
// Iterate over all the fields looking for the matching name
|
||||||
|
// Also calculate field offset.
|
||||||
|
|
||||||
|
descriptor := (*structType)(unsafe.Pointer(ll.t.underlying()))
|
||||||
|
var offset uintptr
|
||||||
|
field := &descriptor.fields[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 {
|
||||||
|
found = append(found, result{
|
||||||
|
rawStructFieldFromPointer(field.fieldType, data, flagsByte, name, offset),
|
||||||
|
append(ll.index, int(i)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
structOrPtrToStruct := field.fieldType.Kind() == Struct || (field.fieldType.Kind() == Pointer && field.fieldType.elem().Kind() == Struct)
|
||||||
|
if flagsByte&structFieldFlagIsEmbedded == structFieldFlagIsEmbedded && structOrPtrToStruct {
|
||||||
|
embedded := field.fieldType
|
||||||
|
if embedded.Kind() == Pointer {
|
||||||
|
embedded = embedded.elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
nextlevel = append(nextlevel, fieldWalker{
|
||||||
|
t: embedded,
|
||||||
|
index: append(ll.index, int(i)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += field.fieldType.Size()
|
||||||
|
|
||||||
|
// update offset/field pointer if there *is* a next field
|
||||||
|
if i < descriptor.numField-1 {
|
||||||
|
|
||||||
|
// Increment pointer to the next field.
|
||||||
|
field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{})))
|
||||||
|
|
||||||
|
// Align the offset for the next field.
|
||||||
|
offset = align(offset, uintptr(field.fieldType.Align()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// found multiple hits at this level
|
||||||
|
if len(found) > 1 {
|
||||||
|
return rawStructField{}, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// found the field we were looking for
|
||||||
|
if len(found) == 1 {
|
||||||
|
r := found[0]
|
||||||
|
return r.r, r.index, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// else len(found) == 0, move on to the next level
|
||||||
|
queue = append(queue[:0], nextlevel...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// didn't find it
|
||||||
|
return rawStructField{}, nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -1017,7 +1070,7 @@ func (t *rawType) FieldByName(name string) (StructField, bool) {
|
||||||
Tag: field.Tag,
|
Tag: field.Tag,
|
||||||
Anonymous: field.Anonymous,
|
Anonymous: field.Anonymous,
|
||||||
Offset: field.Offset,
|
Offset: field.Offset,
|
||||||
Index: []int{index},
|
Index: index,
|
||||||
}, true
|
}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1026,8 +1079,13 @@ func (t *rawType) FieldByIndex(index []int) StructField {
|
||||||
var field rawStructField
|
var field rawStructField
|
||||||
|
|
||||||
for _, n := range index {
|
for _, n := range index {
|
||||||
if ftype.Kind() != Struct {
|
structOrPtrToStruct := ftype.Kind() == Struct || (ftype.Kind() == Pointer && ftype.elem().Kind() == Struct)
|
||||||
panic(TypeError{"FieldByIndex"})
|
if !structOrPtrToStruct {
|
||||||
|
panic(&TypeError{"FieldByIndex:" + ftype.Kind().String()})
|
||||||
|
}
|
||||||
|
|
||||||
|
if ftype.Kind() == Pointer {
|
||||||
|
ftype = ftype.elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
field = ftype.rawField(n)
|
field = ftype.rawField(n)
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче