Skip to content

Commit d42c08a

Browse files
committed
reflect,runtime: add Value.Clear
Fixes #55002 Change-Id: I7d0f14cc54f67f2769b51d2efafc4ae3714f0e3d Reviewed-on: https://go-review.googlesource.com/c/go/+/457895 Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Ian Lance Taylor <iant@google.com> Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Keith Randall <khr@google.com> Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com> TryBot-Result: Gopher Robot <gobot@golang.org>
1 parent f2a2600 commit d42c08a

File tree

5 files changed

+84
-0
lines changed

5 files changed

+84
-0
lines changed

api/next/55002.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pkg reflect, method (Value) Clear() #55002

src/reflect/all_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8363,3 +8363,48 @@ func TestInitFuncTypes(t *testing.T) {
83638363
}
83648364
wg.Wait()
83658365
}
8366+
8367+
func TestClear(t *testing.T) {
8368+
m := make(map[string]any, len(valueTests))
8369+
for _, tt := range valueTests {
8370+
m[tt.s] = tt.i
8371+
}
8372+
mapTestFn := func(v Value) bool { v.Clear(); return v.Len() == 0 }
8373+
8374+
s := make([]*pair, len(valueTests))
8375+
for i := range s {
8376+
s[i] = &valueTests[i]
8377+
}
8378+
sliceTestFn := func(v Value) bool {
8379+
v.Clear()
8380+
for i := 0; i < v.Len(); i++ {
8381+
if !v.Index(i).IsZero() {
8382+
return false
8383+
}
8384+
}
8385+
return true
8386+
}
8387+
8388+
panicTestFn := func(v Value) bool { shouldPanic("reflect.Value.Clear", func() { v.Clear() }); return true }
8389+
8390+
tests := []struct {
8391+
name string
8392+
value Value
8393+
testFunc func(v Value) bool
8394+
}{
8395+
{"map", ValueOf(m), mapTestFn},
8396+
{"slice no pointer", ValueOf([]int{1, 2, 3, 4, 5}), sliceTestFn},
8397+
{"slice has pointer", ValueOf(s), sliceTestFn},
8398+
{"non-map/slice", ValueOf(1), panicTestFn},
8399+
}
8400+
8401+
for _, tc := range tests {
8402+
tc := tc
8403+
t.Run(tc.name, func(t *testing.T) {
8404+
t.Parallel()
8405+
if !tc.testFunc(tc.value) {
8406+
t.Errorf("unexpected result for value.Clear(): %value", tc.value)
8407+
}
8408+
})
8409+
}
8410+
}

src/reflect/value.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2827,6 +2827,22 @@ func (v Value) extendSlice(n int) Value {
28272827
return v
28282828
}
28292829

2830+
// Clear clears the contents of a map or zeros the contents of a slice.
2831+
//
2832+
// It panics if v's Kind is not Map or Slice.
2833+
func (v Value) Clear() {
2834+
switch v.Kind() {
2835+
case Slice:
2836+
sh := *(*unsafeheader.Slice)(v.ptr)
2837+
st := (*sliceType)(unsafe.Pointer(v.typ))
2838+
typedarrayclear(st.elem, sh.Data, sh.Len)
2839+
case Map:
2840+
mapclear(v.typ, v.pointer())
2841+
default:
2842+
panic(&ValueError{"reflect.Value.Clear", v.Kind()})
2843+
}
2844+
}
2845+
28302846
// Append appends the values x to a slice s and returns the resulting slice.
28312847
// As in Go, each x's value must be assignable to the slice's element type.
28322848
func Append(s Value, x ...Value) Value {
@@ -3774,6 +3790,8 @@ func mapiternext(it *hiter)
37743790
//go:noescape
37753791
func maplen(m unsafe.Pointer) int
37763792

3793+
func mapclear(t *rtype, m unsafe.Pointer)
3794+
37773795
// call calls fn with "stackArgsSize" bytes of stack arguments laid out
37783796
// at stackArgs and register arguments laid out in regArgs. frameSize is
37793797
// the total amount of stack space that will be reserved by call, so this
@@ -3837,6 +3855,12 @@ func typedmemclrpartial(t *rtype, ptr unsafe.Pointer, off, size uintptr)
38373855
//go:noescape
38383856
func typedslicecopy(elemType *rtype, dst, src unsafeheader.Slice) int
38393857

3858+
// typedarrayclear zeroes the value at ptr of an array of elemType,
3859+
// only clears len elem.
3860+
//
3861+
//go:noescape
3862+
func typedarrayclear(elemType *rtype, ptr unsafe.Pointer, len int)
3863+
38403864
//go:noescape
38413865
func typehash(t *rtype, p unsafe.Pointer, h uintptr) uintptr
38423866

src/runtime/map.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,11 @@ func reflect_maplen(h *hmap) int {
14031403
return h.count
14041404
}
14051405

1406+
//go:linkname reflect_mapclear reflect.mapclear
1407+
func reflect_mapclear(t *maptype, h *hmap) {
1408+
mapclear(t, h)
1409+
}
1410+
14061411
//go:linkname reflectlite_maplen internal/reflectlite.maplen
14071412
func reflectlite_maplen(h *hmap) int {
14081413
if h == nil {

src/runtime/mbarrier.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,15 @@ func reflect_typedmemclrpartial(typ *_type, ptr unsafe.Pointer, off, size uintpt
334334
memclrNoHeapPointers(ptr, size)
335335
}
336336

337+
//go:linkname reflect_typedarrayclear reflect.typedarrayclear
338+
func reflect_typedarrayclear(typ *_type, ptr unsafe.Pointer, len int) {
339+
size := typ.size * uintptr(len)
340+
if writeBarrier.needed && typ.ptrdata != 0 {
341+
bulkBarrierPreWrite(uintptr(ptr), 0, size)
342+
}
343+
memclrNoHeapPointers(ptr, size)
344+
}
345+
337346
// memclrHasPointers clears n bytes of typed memory starting at ptr.
338347
// The caller must ensure that the type of the object at ptr has
339348
// pointers, usually by checking typ.ptrdata. However, ptr

0 commit comments

Comments
 (0)