aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorDamian Gryski <[email protected]>2023-03-06 11:49:20 -0800
committerAyke <[email protected]>2023-03-15 21:53:57 +0100
commit93fb897feb725cf6aaf32cfe0acd1201e4630d8f (patch)
tree77c03beeca58596766e36f4c406f82b019a6c1a5 /src
parent15109a2924bfb23be629ec667f5c3efd69a38f75 (diff)
downloadtinygo-93fb897feb725cf6aaf32cfe0acd1201e4630d8f.tar.gz
tinygo-93fb897feb725cf6aaf32cfe0acd1201e4630d8f.zip
compiler, reflect: properly handle embedded structs
Diffstat (limited to 'src')
-rw-r--r--src/reflect/type.go120
1 files changed, 89 insertions, 31 deletions
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 5b1e3d825..ff58175b4 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -69,6 +69,7 @@ const (
structFieldFlagAnonymous = 1 << iota
structFieldFlagHasTag
structFieldFlagIsExported
+ structFieldFlagIsEmbedded
)
type Kind uint8
@@ -655,46 +656,98 @@ func (t *rawType) rawField(n int) rawStructField {
// Type member to an interface.
//
// 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 {
panic(&TypeError{"Field"})
}
- descriptor := (*structType)(unsafe.Pointer(t.underlying()))
- // Iterate over all the fields looking for the matching name
- // Also calculate field offset.
+ type fieldWalker struct {
+ 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
+ queue := make([]fieldWalker, 0, 4)
+ queue = append(queue, fieldWalker{t, nil})
+
+ for len(queue) > 0 {
+ type result struct {
+ r rawStructField
+ index []int
}
- // update offset/field pointer if there *is* a next field
- if i < descriptor.numField-1 {
- offset += field.fieldType.Size()
+ 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()))
+ }
+ }
+ }
- // Increment pointer to the next field.
- field = (*structField)(unsafe.Add(unsafe.Pointer(field), unsafe.Sizeof(structField{})))
+ // found multiple hits at this level
+ if len(found) > 1 {
+ return rawStructField{}, nil, false
+ }
- // Align the offset for the next field.
- offset = align(offset, uintptr(field.fieldType.Align()))
+ // found the field we were looking for
+ if len(found) == 1 {
+ r := found[0]
+ return r.r, r.index, true
}
- }
- // No match
- return rawStructField{}, 0, false
+ // 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
@@ -1017,7 +1070,7 @@ func (t *rawType) FieldByName(name string) (StructField, bool) {
Tag: field.Tag,
Anonymous: field.Anonymous,
Offset: field.Offset,
- Index: []int{index},
+ Index: index,
}, true
}
@@ -1026,8 +1079,13 @@ func (t *rawType) FieldByIndex(index []int) StructField {
var field rawStructField
for _, n := range index {
- if ftype.Kind() != Struct {
- panic(TypeError{"FieldByIndex"})
+ structOrPtrToStruct := ftype.Kind() == Struct || (ftype.Kind() == Pointer && ftype.elem().Kind() == Struct)
+ if !structOrPtrToStruct {
+ panic(&TypeError{"FieldByIndex:" + ftype.Kind().String()})
+ }
+
+ if ftype.Kind() == Pointer {
+ ftype = ftype.elem()
}
field = ftype.rawField(n)