reflect: fix iterating over maps with interface{} keys

Fixes #3794
Этот коммит содержится в:
Damian Gryski 2023-06-16 12:19:53 -07:00 коммит произвёл Ron Evans
родитель 93cc03b15a
коммит ef72c5bb4e
2 изменённых файлов: 30 добавлений и 9 удалений

Просмотреть файл

@ -914,10 +914,11 @@ func (v Value) MapKeys() []Value {
e := New(v.typecode.Elem()) e := New(v.typecode.Elem())
keyType := v.typecode.key() keyType := v.typecode.key()
isKeyStoredAsInterface := keyType.Kind() != String && !keyType.isBinary() keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()
for hashmapNext(v.pointer(), it, k.value, e.value) { for hashmapNext(v.pointer(), it, k.value, e.value) {
if isKeyStoredAsInterface { if shouldUnpackInterface {
intf := *(*interface{})(k.value) intf := *(*interface{})(k.value)
v := ValueOf(intf) v := ValueOf(intf)
keys = append(keys, v) keys = append(keys, v)
@ -992,12 +993,14 @@ func (v Value) MapRange() *MapIter {
} }
keyType := v.typecode.key() keyType := v.typecode.key()
isKeyStoredAsInterface := keyType.Kind() != String && !keyType.isBinary()
keyTypeIsEmptyInterface := keyType.Kind() == Interface && keyType.NumMethod() == 0
shouldUnpackInterface := !keyTypeIsEmptyInterface && keyType.Kind() != String && !keyType.isBinary()
return &MapIter{ return &MapIter{
m: v, m: v,
it: hashmapNewIterator(), it: hashmapNewIterator(),
keyInterface: isKeyStoredAsInterface, unpackKeyInterface: shouldUnpackInterface,
} }
} }
@ -1007,8 +1010,8 @@ type MapIter struct {
key Value key Value
val Value val Value
valid bool valid bool
keyInterface bool unpackKeyInterface bool
} }
func (it *MapIter) Key() Value { func (it *MapIter) Key() Value {
@ -1016,7 +1019,7 @@ func (it *MapIter) Key() Value {
panic("reflect.MapIter.Key called on invalid iterator") panic("reflect.MapIter.Key called on invalid iterator")
} }
if it.keyInterface { if it.unpackKeyInterface {
intf := *(*interface{})(it.key.value) intf := *(*interface{})(it.key.value)
v := ValueOf(intf) v := ValueOf(intf)
return v return v

Просмотреть файл

@ -191,6 +191,24 @@ func TestTinyMap(t *testing.T) {
if _, ok := utIterKey.Interface().(unmarshalerText); !ok { if _, ok := utIterKey.Interface().(unmarshalerText); !ok {
t.Errorf("Map keys via MapIter() have wrong type: %v", utIterKey.Type().String()) t.Errorf("Map keys via MapIter() have wrong type: %v", utIterKey.Type().String())
} }
{
m := map[any]any{1: 2}
rv := ValueOf(m)
iter := rv.MapRange()
iter.Next()
k := iter.Key()
if k.Kind().String() != "interface" {
t.Errorf("map[any]any MapRange has wrong key type: %v", k.Kind().String())
}
keys := rv.MapKeys()
if k := keys[0]; k.Kind().String() != "interface" {
t.Errorf("map[any]any MapRange has wrong key type: %v", k.Kind().String())
}
}
} }
// For an interface type, it returns the number of exported and unexported methods. // For an interface type, it returns the number of exported and unexported methods.