Skip to content

Commit e98fea0

Browse files
ben-kriegerdgryski
authored andcommitted
reflect: add Value.Clear; support anytype->interface{}, Slice->(*)Array in Value.Convert
1 parent c728e03 commit e98fea0

File tree

3 files changed

+257
-12
lines changed

3 files changed

+257
-12
lines changed

src/reflect/type.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,8 +424,8 @@ type rawType struct {
424424
meta uint8 // metadata byte, contains kind and flags (see constants above)
425425
}
426426

427-
// All types that have an element type: named, chan, slice, array, map (but not
428-
// pointer because it doesn't have ptrTo).
427+
// All types that have an element type: named, chan, slice, array, map, interface
428+
// (but not pointer because it doesn't have ptrTo).
429429
type elemType struct {
430430
rawType
431431
numMethod uint16
@@ -439,6 +439,12 @@ type ptrType struct {
439439
elem *rawType
440440
}
441441

442+
type interfaceType struct {
443+
rawType
444+
ptrTo *rawType
445+
// TODO: methods
446+
}
447+
442448
type arrayType struct {
443449
rawType
444450
numMethod uint16
@@ -995,6 +1001,10 @@ func (t *rawType) AssignableTo(u Type) bool {
9951001
return true
9961002
}
9971003

1004+
if t.underlying() == u.(*rawType).underlying() && (!t.isNamed() || !u.(*rawType).isNamed()) {
1005+
return true
1006+
}
1007+
9981008
if u.Kind() == Interface && u.NumMethod() == 0 {
9991009
return true
10001010
}
@@ -1060,6 +1070,9 @@ func (t *rawType) NumMethod() int {
10601070
return int((*ptrType)(unsafe.Pointer(t)).numMethod)
10611071
case Struct:
10621072
return int((*structType)(unsafe.Pointer(t)).numMethod)
1073+
case Interface:
1074+
//FIXME: Use len(methods)
1075+
return (*interfaceType)(unsafe.Pointer(t)).ptrTo.NumMethod()
10631076
}
10641077

10651078
// Other types have no methods attached. Note we don't panic here.

src/reflect/value.go

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,25 @@ func (v Value) Cap() int {
689689
}
690690
}
691691

692+
//go:linkname mapclear runtime.hashmapClear
693+
func mapclear(p unsafe.Pointer)
694+
695+
// Clear clears the contents of a map or zeros the contents of a slice
696+
//
697+
// It panics if v's Kind is not Map or Slice.
698+
func (v Value) Clear() {
699+
switch v.typecode.Kind() {
700+
case Map:
701+
mapclear(v.pointer())
702+
case Slice:
703+
hdr := (*sliceHeader)(v.value)
704+
elemSize := v.typecode.underlying().elem().Size()
705+
memzero(hdr.data, elemSize*hdr.len)
706+
default:
707+
panic(&ValueError{Method: "Clear", Kind: v.Kind()})
708+
}
709+
}
710+
692711
// NumField returns the number of fields of this struct. It panics for other
693712
// value types.
694713
func (v Value) NumField() int {
@@ -888,6 +907,9 @@ func (v Value) Index(i int) Value {
888907
}
889908

890909
func (v Value) NumMethod() int {
910+
if v.typecode == nil {
911+
panic(&ValueError{Method: "reflect.Value.NumMethod", Kind: Invalid})
912+
}
891913
return v.typecode.NumMethod()
892914
}
893915

@@ -1062,7 +1084,7 @@ func (v Value) Set(x Value) {
10621084
v.checkAddressable()
10631085
v.checkRO()
10641086
if !x.typecode.AssignableTo(v.typecode) {
1065-
panic("reflect: cannot set")
1087+
panic("reflect.Value.Set: value of type " + x.typecode.String() + " cannot be assigned to type " + v.typecode.String())
10661088
}
10671089

10681090
if v.typecode.Kind() == Interface && x.typecode.Kind() != Interface {
@@ -1240,7 +1262,9 @@ func (v Value) OverflowUint(x uint64) bool {
12401262
}
12411263

12421264
func (v Value) CanConvert(t Type) bool {
1243-
panic("unimplemented: (reflect.Value).CanConvert()")
1265+
// TODO: Optimize this to not actually perform a conversion
1266+
_, ok := convertOp(v, t)
1267+
return ok
12441268
}
12451269

12461270
func (v Value) Convert(t Type) Value {
@@ -1262,6 +1286,15 @@ func convertOp(src Value, typ Type) (Value, bool) {
12621286
}, true
12631287
}
12641288

1289+
if rtype := typ.(*rawType); rtype.Kind() == Interface && rtype.NumMethod() == 0 {
1290+
iface := composeInterface(unsafe.Pointer(src.typecode), src.value)
1291+
return Value{
1292+
typecode: rtype,
1293+
value: unsafe.Pointer(&iface),
1294+
flags: valueFlagExported,
1295+
}, true
1296+
}
1297+
12651298
switch src.Kind() {
12661299
case Int, Int8, Int16, Int32, Int64:
12671300
switch rtype := typ.(*rawType); rtype.Kind() {
@@ -1302,14 +1335,33 @@ func convertOp(src Value, typ Type) (Value, bool) {
13021335
*/
13031336

13041337
case Slice:
1305-
if typ.Kind() == String && !src.typecode.elem().isNamed() {
1306-
rtype := typ.(*rawType)
1307-
1308-
switch src.Type().Elem().Kind() {
1309-
case Uint8:
1310-
return cvtBytesString(src, rtype), true
1311-
case Int32:
1312-
return cvtRunesString(src, rtype), true
1338+
switch rtype := typ.(*rawType); rtype.Kind() {
1339+
case Array:
1340+
if src.typecode.elem() == rtype.elem() && rtype.Len() <= src.Len() {
1341+
return Value{
1342+
typecode: rtype,
1343+
value: (*sliceHeader)(src.value).data,
1344+
flags: src.flags | valueFlagIndirect,
1345+
}, true
1346+
}
1347+
case Pointer:
1348+
if rtype.Elem().Kind() == Array {
1349+
if src.typecode.elem() == rtype.elem().elem() && rtype.elem().Len() <= src.Len() {
1350+
return Value{
1351+
typecode: rtype,
1352+
value: (*sliceHeader)(src.value).data,
1353+
flags: src.flags & (valueFlagExported | valueFlagRO),
1354+
}, true
1355+
}
1356+
}
1357+
case String:
1358+
if !src.typecode.elem().isNamed() {
1359+
switch src.Type().Elem().Kind() {
1360+
case Uint8:
1361+
return cvtBytesString(src, rtype), true
1362+
case Int32:
1363+
return cvtRunesString(src, rtype), true
1364+
}
13131365
}
13141366
}
13151367

src/reflect/value_test.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package reflect_test
22

33
import (
4+
"bytes"
45
"encoding/base64"
56
. "reflect"
67
"sort"
@@ -599,6 +600,29 @@ func TestAssignableTo(t *testing.T) {
599600
if got, want := refa.Interface().(int), 4; got != want {
600601
t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
601602
}
603+
604+
b := []byte{0x01, 0x02}
605+
refb := ValueOf(&b).Elem()
606+
refb.Set(ValueOf([]byte{0x02, 0x03}))
607+
if got, want := refb.Interface().([]byte), []byte{0x02, 0x03}; !bytes.Equal(got, want) {
608+
t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
609+
}
610+
611+
type bstr []byte
612+
613+
c := bstr{0x01, 0x02}
614+
refc := ValueOf(&c).Elem()
615+
refc.Set(ValueOf([]byte{0x02, 0x03}))
616+
if got, want := refb.Interface().([]byte), []byte{0x02, 0x03}; !bytes.Equal(got, want) {
617+
t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
618+
}
619+
620+
d := []byte{0x01, 0x02}
621+
refd := ValueOf(&d).Elem()
622+
refd.Set(ValueOf(bstr{0x02, 0x03}))
623+
if got, want := refb.Interface().([]byte), []byte{0x02, 0x03}; !bytes.Equal(got, want) {
624+
t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
625+
}
602626
}
603627

604628
func TestConvert(t *testing.T) {
@@ -624,6 +648,162 @@ func TestConvert(t *testing.T) {
624648
}
625649
}
626650

651+
func TestConvertSliceToArrayOrArrayPointer(t *testing.T) {
652+
s := make([]byte, 2, 4)
653+
// a0 := [0]byte(s)
654+
// a1 := [1]byte(s[1:]) // a1[0] == s[1]
655+
// a2 := [2]byte(s) // a2[0] == s[0]
656+
// a4 := [4]byte(s) // panics: len([4]byte) > len(s)
657+
658+
v := ValueOf(s).Convert(TypeFor[[0]byte]())
659+
if v.Kind() != Array || v.Type().Len() != 0 {
660+
t.Error("Convert([]byte -> [0]byte)")
661+
}
662+
v = ValueOf(s[1:]).Convert(TypeFor[[1]byte]())
663+
if v.Kind() != Array || v.Type().Len() != 1 {
664+
t.Error("Convert([]byte -> [1]byte)")
665+
}
666+
v = ValueOf(s).Convert(TypeFor[[2]byte]())
667+
if v.Kind() != Array || v.Type().Len() != 2 {
668+
t.Error("Convert([]byte -> [2]byte)")
669+
}
670+
if ValueOf(s).CanConvert(TypeFor[[4]byte]()) {
671+
t.Error("Converting a slice with len smaller than array to array should fail")
672+
}
673+
674+
// s0 := (*[0]byte)(s) // s0 != nil
675+
// s1 := (*[1]byte)(s[1:]) // &s1[0] == &s[1]
676+
// s2 := (*[2]byte)(s) // &s2[0] == &s[0]
677+
// s4 := (*[4]byte)(s) // panics: len([4]byte) > len(s)
678+
v = ValueOf(s).Convert(TypeFor[*[0]byte]())
679+
if v.Kind() != Pointer || v.Elem().Kind() != Array || v.Elem().Type().Len() != 0 {
680+
t.Error("Convert([]byte -> *[0]byte)")
681+
}
682+
v = ValueOf(s[1:]).Convert(TypeFor[*[1]byte]())
683+
if v.Kind() != Pointer || v.Elem().Kind() != Array || v.Elem().Type().Len() != 1 {
684+
t.Error("Convert([]byte -> *[1]byte)")
685+
}
686+
v = ValueOf(s).Convert(TypeFor[*[2]byte]())
687+
if v.Kind() != Pointer || v.Elem().Kind() != Array || v.Elem().Type().Len() != 2 {
688+
t.Error("Convert([]byte -> *[2]byte)")
689+
}
690+
if ValueOf(s).CanConvert(TypeFor[*[4]byte]()) {
691+
t.Error("Converting a slice with len smaller than array to array pointer should fail")
692+
}
693+
694+
// Test converting slices with backing arrays <= and >64bits
695+
slice64 := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
696+
array64 := ValueOf(slice64).Convert(TypeFor[[8]byte]()).Interface().([8]byte)
697+
if !bytes.Equal(slice64, array64[:]) {
698+
t.Errorf("converted array %x does not match backing array of slice %x", array64, slice64)
699+
}
700+
701+
slice72 := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}
702+
array72 := ValueOf(slice72).Convert(TypeFor[[9]byte]()).Interface().([9]byte)
703+
if !bytes.Equal(slice72, array72[:]) {
704+
t.Errorf("converted array %x does not match backing array of slice %x", array72, slice72)
705+
}
706+
}
707+
708+
func TestConvertToEmptyInterface(t *testing.T) {
709+
anyType := TypeFor[interface{}]()
710+
711+
v := ValueOf(false).Convert(anyType)
712+
if v.Kind() != Interface || v.NumMethod() > 0 {
713+
t.Error("Convert(bool -> interface{})")
714+
}
715+
_ = v.Interface().(interface{}).(bool)
716+
717+
v = ValueOf(int64(3)).Convert(anyType)
718+
if v.Kind() != Interface || v.NumMethod() > 0 {
719+
t.Error("Convert(int64 -> interface{})")
720+
}
721+
_ = v.Interface().(interface{}).(int64)
722+
723+
v = ValueOf(struct{}{}).Convert(anyType)
724+
if v.Kind() != Interface || v.NumMethod() > 0 {
725+
t.Error("Convert(struct -> interface{})")
726+
}
727+
_ = v.Interface().(interface{}).(struct{})
728+
729+
v = ValueOf([]struct{}{}).Convert(anyType)
730+
if v.Kind() != Interface || v.NumMethod() > 0 {
731+
t.Error("Convert(slice -> interface{})")
732+
}
733+
_ = v.Interface().(interface{}).([]struct{})
734+
735+
v = ValueOf(map[string]string{"A": "B"}).Convert(anyType)
736+
if v.Kind() != Interface || v.NumMethod() > 0 {
737+
t.Error("Convert(map -> interface{})")
738+
}
739+
_ = v.Interface().(interface{}).(map[string]string)
740+
}
741+
742+
func TestClearSlice(t *testing.T) {
743+
type stringSlice []string
744+
for _, test := range []struct {
745+
slice any
746+
expect any
747+
}{
748+
{
749+
slice: []bool{true, false, true},
750+
expect: []bool{false, false, false},
751+
},
752+
{
753+
slice: []byte{0x00, 0x01, 0x02, 0x03},
754+
expect: []byte{0x00, 0x00, 0x00, 0x00},
755+
},
756+
{
757+
slice: [][]int{[]int{2, 1}, []int{3}, []int{}},
758+
expect: [][]int{nil, nil, nil},
759+
},
760+
{
761+
slice: []stringSlice{stringSlice{"hello", "world"}, stringSlice{}, stringSlice{"goodbye"}},
762+
expect: []stringSlice{nil, nil, nil},
763+
},
764+
} {
765+
v := ValueOf(test.slice)
766+
expectLen, expectCap := v.Len(), v.Cap()
767+
768+
v.Clear()
769+
if len := v.Len(); len != expectLen {
770+
t.Errorf("Clear(slice) altered len, got %d, expected %d", len, expectLen)
771+
}
772+
if cap := v.Cap(); cap != expectCap {
773+
t.Errorf("Clear(slice) altered cap, got %d, expected %d", cap, expectCap)
774+
}
775+
if !DeepEqual(test.slice, test.expect) {
776+
t.Errorf("Clear(slice) got %v, expected %v", test.slice, test.expect)
777+
}
778+
}
779+
}
780+
781+
func TestClearMap(t *testing.T) {
782+
for _, test := range []struct {
783+
m any
784+
expect any
785+
}{
786+
{
787+
m: map[int]bool{1: true, 2: false, 3: true},
788+
expect: map[int]bool{},
789+
},
790+
{
791+
m: map[string][]byte{"hello": []byte("world")},
792+
expect: map[string][]byte{},
793+
},
794+
} {
795+
v := ValueOf(test.m)
796+
797+
v.Clear()
798+
if len := v.Len(); len != 0 {
799+
t.Errorf("Clear(map) should set len to 0, got %d", len)
800+
}
801+
if !DeepEqual(test.m, test.expect) {
802+
t.Errorf("Clear(map) got %v, expected %v", test.m, test.expect)
803+
}
804+
}
805+
}
806+
627807
func TestIssue4040(t *testing.T) {
628808
var value interface{} = uint16(0)
629809

0 commit comments

Comments
 (0)