Skip to content

Commit 1abc718

Browse files
author
Ben Krieger
committed
reflect: add Value.Clear; support anytype->interface{}, Slice->(*)Array in Value.Convert
1 parent b8fe75a commit 1abc718

File tree

3 files changed

+235
-20
lines changed

3 files changed

+235
-20
lines changed

src/reflect/type.go

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

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

441+
type interfaceType struct {
442+
rawType
443+
ptrTo *rawType
444+
// TODO: methods
445+
}
446+
441447
type arrayType struct {
442448
rawType
443449
numMethod uint16
@@ -970,10 +976,18 @@ func (t *rawType) FieldAlign() int {
970976
// AssignableTo returns whether a value of type t can be assigned to a variable
971977
// of type u.
972978
func (t *rawType) AssignableTo(u Type) bool {
973-
if t == u.(*rawType) {
979+
if u := u.(*rawType); t == u || t.underlying() == u || t == u.underlying() {
974980
return true
975981
}
976982

983+
if u.Kind() == Array && t.Kind() == Slice {
984+
return u.Elem().(*rawType) == t.Elem().(*rawType)
985+
}
986+
987+
if (u.Kind() == Pointer && u.Elem().Kind() == Array) && t.Kind() == Slice {
988+
return u.Elem().Elem().(*rawType) == t.Elem().(*rawType)
989+
}
990+
977991
if u.Kind() == Interface && u.NumMethod() == 0 {
978992
return true
979993
}
@@ -1039,6 +1053,9 @@ func (t *rawType) NumMethod() int {
10391053
return int((*ptrType)(unsafe.Pointer(t)).numMethod)
10401054
case Struct:
10411055
return int((*structType)(unsafe.Pointer(t)).numMethod)
1056+
case Interface:
1057+
//FIXME: Use len(methods)
1058+
return (*interfaceType)(unsafe.Pointer(t)).ptrTo.NumMethod()
10421059
}
10431060

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

src/reflect/value.go

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,27 @@ 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+
for i := uintptr(0); i < hdr.len; i++ {
706+
memzero(unsafe.Add(hdr.data, i*elemSize), elemSize)
707+
}
708+
default:
709+
panic(&ValueError{Method: "Clear", Kind: v.Kind()})
710+
}
711+
}
712+
692713
// NumField returns the number of fields of this struct. It panics for other
693714
// value types.
694715
func (v Value) NumField() int {
@@ -888,6 +909,9 @@ func (v Value) Index(i int) Value {
888909
}
889910

890911
func (v Value) NumMethod() int {
912+
if v.typecode == nil {
913+
panic(&ValueError{Method: "reflect.Value.NumMethod", Kind: Invalid})
914+
}
891915
return v.typecode.NumMethod()
892916
}
893917

@@ -1062,7 +1086,7 @@ func (v Value) Set(x Value) {
10621086
v.checkAddressable()
10631087
v.checkRO()
10641088
if !x.typecode.AssignableTo(v.typecode) {
1065-
panic("reflect: cannot set")
1089+
panic("reflect.Value.Set: value of type " + x.typecode.String() + " cannot be assigned to type " + v.typecode.String())
10661090
}
10671091

10681092
if v.typecode.Kind() == Interface && x.typecode.Kind() != Interface {
@@ -1240,7 +1264,9 @@ func (v Value) OverflowUint(x uint64) bool {
12401264
}
12411265

12421266
func (v Value) CanConvert(t Type) bool {
1243-
panic("unimplemented: (reflect.Value).CanConvert()")
1267+
// TODO: Optimize this to not actually perform a conversion
1268+
_, ok := convertOp(v, t)
1269+
return ok
12441270
}
12451271

12461272
func (v Value) Convert(t Type) Value {
@@ -1262,6 +1288,15 @@ func convertOp(src Value, typ Type) (Value, bool) {
12621288
}, true
12631289
}
12641290

1291+
if rtype := typ.(*rawType); rtype.Kind() == Interface && rtype.NumMethod() == 0 {
1292+
iface := composeInterface(unsafe.Pointer(src.typecode), src.value)
1293+
return Value{
1294+
typecode: rtype,
1295+
value: unsafe.Pointer(&iface),
1296+
flags: valueFlagExported,
1297+
}, true
1298+
}
1299+
12651300
switch src.Kind() {
12661301
case Int, Int8, Int16, Int32, Int64:
12671302
switch rtype := typ.(*rawType); rtype.Kind() {
@@ -1302,25 +1337,47 @@ func convertOp(src Value, typ Type) (Value, bool) {
13021337
*/
13031338

13041339
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
1340+
switch rtype := typ.(*rawType); rtype.Kind() {
1341+
case Array:
1342+
if src.typecode.elem() == rtype.elem() && rtype.Len() <= src.Len() {
1343+
return Value{
1344+
typecode: rtype,
1345+
value: (*sliceHeader)(src.value).data,
1346+
flags: src.flags,
1347+
}, true
1348+
}
1349+
case Pointer:
1350+
if rtype.Elem().Kind() != Array {
1351+
break
1352+
}
1353+
if src.typecode.elem() == rtype.elem().elem() && rtype.elem().Len() <= src.Len() {
1354+
return Value{
1355+
typecode: rtype,
1356+
value: (*sliceHeader)(src.value).data,
1357+
flags: src.flags & (valueFlagExported | valueFlagRO),
1358+
}, true
1359+
}
1360+
case String:
1361+
if !src.typecode.elem().isNamed() {
1362+
switch src.Type().Elem().Kind() {
1363+
case Uint8:
1364+
return cvtBytesString(src, rtype), true
1365+
case Int32:
1366+
return cvtRunesString(src, rtype), true
1367+
}
13131368
}
13141369
}
13151370

13161371
case String:
1317-
rtype := typ.(*rawType)
1318-
if typ.Kind() == Slice && !rtype.elem().isNamed() {
1319-
switch typ.Elem().Kind() {
1320-
case Uint8:
1321-
return cvtStringBytes(src, rtype), true
1322-
case Int32:
1323-
return cvtStringRunes(src, rtype), true
1372+
switch rtype := typ.(*rawType); rtype.Kind() {
1373+
case Slice:
1374+
if !rtype.elem().isNamed() {
1375+
switch typ.Elem().Kind() {
1376+
case Uint8:
1377+
return cvtStringBytes(src, rtype), true
1378+
case Int32:
1379+
return cvtStringRunes(src, rtype), true
1380+
}
13241381
}
13251382
}
13261383
}

src/reflect/value_test.go

Lines changed: 141 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,123 @@ 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+
695+
func TestConvertToEmptyInterface(t *testing.T) {
696+
anyType := TypeFor[interface{}]()
697+
698+
v := ValueOf(false).Convert(anyType)
699+
if v.Kind() != Interface || v.NumMethod() > 0 {
700+
t.Error("Convert(bool -> interface{})")
701+
}
702+
_ = v.Interface().(interface{}).(bool)
703+
704+
v = ValueOf(int64(3)).Convert(anyType)
705+
if v.Kind() != Interface || v.NumMethod() > 0 {
706+
t.Error("Convert(int64 -> interface{})")
707+
}
708+
_ = v.Interface().(interface{}).(int64)
709+
710+
v = ValueOf(struct{}{}).Convert(anyType)
711+
if v.Kind() != Interface || v.NumMethod() > 0 {
712+
t.Error("Convert(struct -> interface{})")
713+
}
714+
_ = v.Interface().(interface{}).(struct{})
715+
716+
v = ValueOf([]struct{}{}).Convert(anyType)
717+
if v.Kind() != Interface || v.NumMethod() > 0 {
718+
t.Error("Convert(slice -> interface{})")
719+
}
720+
_ = v.Interface().(interface{}).([]struct{})
721+
722+
v = ValueOf(map[string]string{"A": "B"}).Convert(anyType)
723+
if v.Kind() != Interface || v.NumMethod() > 0 {
724+
t.Error("Convert(map -> interface{})")
725+
}
726+
_ = v.Interface().(interface{}).(map[string]string)
727+
}
728+
729+
func TestClearSlice(t *testing.T) {
730+
type stringSlice []string
731+
for _, test := range []struct {
732+
slice any
733+
expect any
734+
}{
735+
{
736+
slice: []bool{true, false, true},
737+
expect: []bool{false, false, false},
738+
},
739+
{
740+
slice: []byte{0x00, 0x01, 0x02, 0x03},
741+
expect: []byte{0x00, 0x00, 0x00, 0x00},
742+
},
743+
{
744+
slice: [][]int{[]int{2, 1}, []int{3}, []int{}},
745+
expect: [][]int{nil, nil, nil},
746+
},
747+
{
748+
slice: []stringSlice{stringSlice{"hello", "world"}, stringSlice{}, stringSlice{"goodbye"}},
749+
expect: []stringSlice{nil, nil, nil},
750+
},
751+
} {
752+
v := ValueOf(test.slice)
753+
expectLen, expectCap := v.Len(), v.Cap()
754+
755+
v.Clear()
756+
if len := v.Len(); len != expectLen {
757+
t.Errorf("Clear(slice) altered len, got %d, expected %d", len, expectLen)
758+
}
759+
if cap := v.Cap(); cap != expectCap {
760+
t.Errorf("Clear(slice) altered cap, got %d, expected %d", cap, expectCap)
761+
}
762+
if !DeepEqual(test.slice, test.expect) {
763+
t.Errorf("Clear(slice) got %v, expected %v", test.slice, test.expect)
764+
}
765+
}
766+
}
767+
627768
func TestIssue4040(t *testing.T) {
628769
var value interface{} = uint16(0)
629770

0 commit comments

Comments
 (0)