From ef72c5bb4e544843badec2e2c63ed10a09c281e6 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Fri, 16 Jun 2023 12:19:53 -0700 Subject: [PATCH] reflect: fix iterating over maps with interface{} keys Fixes #3794 --- src/reflect/value.go | 21 ++++++++++++--------- src/reflect/value_test.go | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index a5014329..5ee7e5fc 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -914,10 +914,11 @@ func (v Value) MapKeys() []Value { e := New(v.typecode.Elem()) 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) { - if isKeyStoredAsInterface { + if shouldUnpackInterface { intf := *(*interface{})(k.value) v := ValueOf(intf) keys = append(keys, v) @@ -992,12 +993,14 @@ func (v Value) MapRange() *MapIter { } 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{ - m: v, - it: hashmapNewIterator(), - keyInterface: isKeyStoredAsInterface, + m: v, + it: hashmapNewIterator(), + unpackKeyInterface: shouldUnpackInterface, } } @@ -1007,8 +1010,8 @@ type MapIter struct { key Value val Value - valid bool - keyInterface bool + valid bool + unpackKeyInterface bool } func (it *MapIter) Key() Value { @@ -1016,7 +1019,7 @@ func (it *MapIter) Key() Value { panic("reflect.MapIter.Key called on invalid iterator") } - if it.keyInterface { + if it.unpackKeyInterface { intf := *(*interface{})(it.key.value) v := ValueOf(intf) return v diff --git a/src/reflect/value_test.go b/src/reflect/value_test.go index ef6eabcf..305cf02e 100644 --- a/src/reflect/value_test.go +++ b/src/reflect/value_test.go @@ -191,6 +191,24 @@ func TestTinyMap(t *testing.T) { if _, ok := utIterKey.Interface().(unmarshalerText); !ok { 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.