aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/reflect
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2018-01-09 01:23:08 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-01-09 01:23:08 +0000
commit1a2f01efa63036a5104f203a4789e682c0e0915d (patch)
tree373e15778dc8295354584e1f86915ae493b604ff /libgo/go/reflect
parent8799df67f2dab88f9fda11739c501780a85575e2 (diff)
downloadgcc-1a2f01efa63036a5104f203a4789e682c0e0915d.zip
gcc-1a2f01efa63036a5104f203a4789e682c0e0915d.tar.gz
gcc-1a2f01efa63036a5104f203a4789e682c0e0915d.tar.bz2
libgo: update to Go1.10beta1
Update the Go library to the 1.10beta1 release. Requires a few changes to the compiler for modifications to the map runtime code, and to handle some nowritebarrier cases in the runtime. Reviewed-on: https://go-review.googlesource.com/86455 gotools/: * Makefile.am (go_cmd_vet_files): New variable. (go_cmd_buildid_files, go_cmd_test2json_files): New variables. (s-zdefaultcc): Change from constants to functions. (noinst_PROGRAMS): Add vet, buildid, and test2json. (cgo$(EXEEXT)): Link against $(LIBGOTOOL). (vet$(EXEEXT)): New target. (buildid$(EXEEXT)): New target. (test2json$(EXEEXT)): New target. (install-exec-local): Install all $(noinst_PROGRAMS). (uninstall-local): Uninstasll all $(noinst_PROGRAMS). (check-go-tool): Depend on $(noinst_PROGRAMS). Copy down objabi.go. (check-runtime): Depend on $(noinst_PROGRAMS). (check-cgo-test, check-carchive-test): Likewise. (check-vet): New target. (check): Depend on check-vet. Look at cmd_vet-testlog. (.PHONY): Add check-vet. * Makefile.in: Rebuild. From-SVN: r256365
Diffstat (limited to 'libgo/go/reflect')
-rw-r--r--libgo/go/reflect/all_test.go229
-rw-r--r--libgo/go/reflect/export_test.go4
-rw-r--r--libgo/go/reflect/swapper.go4
-rw-r--r--libgo/go/reflect/type.go38
-rw-r--r--libgo/go/reflect/value.go153
5 files changed, 348 insertions, 80 deletions
diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go
index 7364673..6e74859 100644
--- a/libgo/go/reflect/all_test.go
+++ b/libgo/go/reflect/all_test.go
@@ -19,6 +19,7 @@ import (
"strconv"
"strings"
"sync"
+ "sync/atomic"
"testing"
"time"
"unicode"
@@ -321,6 +322,89 @@ func TestSetValue(t *testing.T) {
}
}
+func TestCanSetField(t *testing.T) {
+ type embed struct{ x, X int }
+ type Embed struct{ x, X int }
+ type S1 struct {
+ embed
+ x, X int
+ }
+ type S2 struct {
+ *embed
+ x, X int
+ }
+ type S3 struct {
+ Embed
+ x, X int
+ }
+ type S4 struct {
+ *Embed
+ x, X int
+ }
+
+ type testCase struct {
+ index []int
+ canSet bool
+ }
+ tests := []struct {
+ val Value
+ cases []testCase
+ }{{
+ val: ValueOf(&S1{}),
+ cases: []testCase{
+ {[]int{0}, false},
+ {[]int{0, 0}, false},
+ {[]int{0, 1}, true},
+ {[]int{1}, false},
+ {[]int{2}, true},
+ },
+ }, {
+ val: ValueOf(&S2{embed: &embed{}}),
+ cases: []testCase{
+ {[]int{0}, false},
+ {[]int{0, 0}, false},
+ {[]int{0, 1}, true},
+ {[]int{1}, false},
+ {[]int{2}, true},
+ },
+ }, {
+ val: ValueOf(&S3{}),
+ cases: []testCase{
+ {[]int{0}, true},
+ {[]int{0, 0}, false},
+ {[]int{0, 1}, true},
+ {[]int{1}, false},
+ {[]int{2}, true},
+ },
+ }, {
+ val: ValueOf(&S4{Embed: &Embed{}}),
+ cases: []testCase{
+ {[]int{0}, true},
+ {[]int{0, 0}, false},
+ {[]int{0, 1}, true},
+ {[]int{1}, false},
+ {[]int{2}, true},
+ },
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.val.Type().Name(), func(t *testing.T) {
+ for _, tc := range tt.cases {
+ f := tt.val
+ for _, i := range tc.index {
+ if f.Kind() == Ptr {
+ f = f.Elem()
+ }
+ f = f.Field(i)
+ }
+ if got := f.CanSet(); got != tc.canSet {
+ t.Errorf("CanSet() = %v, want %v", got, tc.canSet)
+ }
+ }
+ })
+ }
+}
+
var _i = 7
var valueToStringTests = []pair{
@@ -584,6 +668,47 @@ func TestCopy(t *testing.T) {
}
}
+func TestCopyString(t *testing.T) {
+ t.Run("Slice", func(t *testing.T) {
+ s := bytes.Repeat([]byte{'_'}, 8)
+ val := ValueOf(s)
+
+ n := Copy(val, ValueOf(""))
+ if expecting := []byte("________"); n != 0 || !bytes.Equal(s, expecting) {
+ t.Errorf("got n = %d, s = %s, expecting n = 0, s = %s", n, s, expecting)
+ }
+
+ n = Copy(val, ValueOf("hello"))
+ if expecting := []byte("hello___"); n != 5 || !bytes.Equal(s, expecting) {
+ t.Errorf("got n = %d, s = %s, expecting n = 5, s = %s", n, s, expecting)
+ }
+
+ n = Copy(val, ValueOf("helloworld"))
+ if expecting := []byte("hellowor"); n != 8 || !bytes.Equal(s, expecting) {
+ t.Errorf("got n = %d, s = %s, expecting n = 8, s = %s", n, s, expecting)
+ }
+ })
+ t.Run("Array", func(t *testing.T) {
+ s := [...]byte{'_', '_', '_', '_', '_', '_', '_', '_'}
+ val := ValueOf(&s).Elem()
+
+ n := Copy(val, ValueOf(""))
+ if expecting := []byte("________"); n != 0 || !bytes.Equal(s[:], expecting) {
+ t.Errorf("got n = %d, s = %s, expecting n = 0, s = %s", n, s[:], expecting)
+ }
+
+ n = Copy(val, ValueOf("hello"))
+ if expecting := []byte("hello___"); n != 5 || !bytes.Equal(s[:], expecting) {
+ t.Errorf("got n = %d, s = %s, expecting n = 5, s = %s", n, s[:], expecting)
+ }
+
+ n = Copy(val, ValueOf("helloworld"))
+ if expecting := []byte("hellowor"); n != 8 || !bytes.Equal(s[:], expecting) {
+ t.Errorf("got n = %d, s = %s, expecting n = 8, s = %s", n, s[:], expecting)
+ }
+ })
+}
+
func TestCopyArray(t *testing.T) {
a := [8]int{1, 2, 3, 4, 10, 9, 8, 7}
b := [11]int{11, 22, 33, 44, 1010, 99, 88, 77, 66, 55, 44}
@@ -1506,6 +1631,15 @@ func TestFunc(t *testing.T) {
}
}
+func TestCallConvert(t *testing.T) {
+ v := ValueOf(new(io.ReadWriter)).Elem()
+ f := ValueOf(func(r io.Reader) io.Reader { return r })
+ out := f.Call([]Value{v})
+ if len(out) != 1 || out[0].Type() != TypeOf(new(io.Reader)).Elem() || !out[0].IsNil() {
+ t.Errorf("expected [nil], got %v", out)
+ }
+}
+
type emptyStruct struct{}
type nonEmptyStruct struct {
@@ -1546,6 +1680,33 @@ func TestCallWithStruct(t *testing.T) {
}
}
+func TestCallReturnsEmpty(t *testing.T) {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("skipping on gccgo: imprecise stack can keep i live")
+ }
+ // Issue 21717: past-the-end pointer write in Call with
+ // nonzero-sized frame and zero-sized return value.
+ runtime.GC()
+ var finalized uint32
+ f := func() (emptyStruct, *int) {
+ i := new(int)
+ runtime.SetFinalizer(i, func(*int) { atomic.StoreUint32(&finalized, 1) })
+ return emptyStruct{}, i
+ }
+ v := ValueOf(f).Call(nil)[0] // out[0] should not alias out[1]'s memory, so the finalizer should run.
+ timeout := time.After(5 * time.Second)
+ for atomic.LoadUint32(&finalized) == 0 {
+ select {
+ case <-timeout:
+ t.Fatal("finalizer did not run")
+ default:
+ }
+ runtime.Gosched()
+ runtime.GC()
+ }
+ runtime.KeepAlive(v)
+}
+
func BenchmarkCall(b *testing.B) {
fv := ValueOf(func(a, b string) {})
b.ReportAllocs()
@@ -2357,10 +2518,13 @@ func TestImportPath(t *testing.T) {
}
func TestFieldPkgPath(t *testing.T) {
+ type x int
typ := TypeOf(struct {
Exported string
unexported string
OtherPkgFields
+ int // issue 21702
+ *x // issue 21122
}{})
type pkgpathTest struct {
@@ -2387,6 +2551,8 @@ func TestFieldPkgPath(t *testing.T) {
{[]int{2}, "", true}, // OtherPkgFields
{[]int{2, 0}, "", false}, // OtherExported
{[]int{2, 1}, "reflect", false}, // otherUnexported
+ {[]int{3}, "reflect_test", true}, // int
+ {[]int{4}, "reflect_test", true}, // *x
})
type localOtherPkgFields OtherPkgFields
@@ -3118,7 +3284,7 @@ func TestCallPanic(t *testing.T) {
i := timp(0)
v := ValueOf(T{i, i, i, i, T2{i, i}, i, i, T2{i, i}})
ok(func() { call(v.Field(0).Method(0)) }) // .t0.W
- ok(func() { call(v.Field(0).Elem().Method(0)) }) // .t0.W
+ bad(func() { call(v.Field(0).Elem().Method(0)) }) // .t0.W
bad(func() { call(v.Field(0).Method(1)) }) // .t0.w
bad(func() { call(v.Field(0).Elem().Method(2)) }) // .t0.w
ok(func() { call(v.Field(1).Method(0)) }) // .T1.Y
@@ -3136,10 +3302,10 @@ func TestCallPanic(t *testing.T) {
bad(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y
bad(func() { call(v.Field(3).Elem().Method(3)) }) // .NamedT1.y
- ok(func() { call(v.Field(4).Field(0).Method(0)) }) // .NamedT2.T1.Y
- ok(func() { call(v.Field(4).Field(0).Elem().Method(0)) }) // .NamedT2.T1.W
- ok(func() { call(v.Field(4).Field(1).Method(0)) }) // .NamedT2.t0.W
- ok(func() { call(v.Field(4).Field(1).Elem().Method(0)) }) // .NamedT2.t0.W
+ ok(func() { call(v.Field(4).Field(0).Method(0)) }) // .NamedT2.T1.Y
+ ok(func() { call(v.Field(4).Field(0).Elem().Method(0)) }) // .NamedT2.T1.W
+ ok(func() { call(v.Field(4).Field(1).Method(0)) }) // .NamedT2.t0.W
+ bad(func() { call(v.Field(4).Field(1).Elem().Method(0)) }) // .NamedT2.t0.W
bad(func() { call(v.Field(5).Method(0)) }) // .namedT0.W
bad(func() { call(v.Field(5).Elem().Method(0)) }) // .namedT0.W
@@ -5538,6 +5704,25 @@ func TestKeepFuncLive(t *testing.T) {
MakeFunc(typ, f).Call([]Value{ValueOf(10)})
}
+type UnExportedFirst int
+
+func (i UnExportedFirst) ΦExported() {}
+func (i UnExportedFirst) unexported() {}
+
+// Issue 21177
+func TestMethodByNameUnExportedFirst(t *testing.T) {
+ defer func() {
+ if recover() != nil {
+ t.Errorf("should not panic")
+ }
+ }()
+ typ := TypeOf(UnExportedFirst(0))
+ m, _ := typ.MethodByName("ΦExported")
+ if m.Name != "ΦExported" {
+ t.Errorf("got %s, expected ΦExported", m.Name)
+ }
+}
+
// Issue 18635 (method version).
type KeepMethodLive struct{}
@@ -6292,3 +6477,37 @@ func TestAliasNames(t *testing.T) {
t.Errorf("Talias2 print:\nhave: %s\nwant: %s", out, want)
}
}
+
+func TestIssue22031(t *testing.T) {
+ type s []struct{ C int }
+
+ type t1 struct{ s }
+ type t2 struct{ f s }
+
+ tests := []Value{
+ ValueOf(t1{s{{}}}).Field(0).Index(0).Field(0),
+ ValueOf(t2{s{{}}}).Field(0).Index(0).Field(0),
+ }
+
+ for i, test := range tests {
+ if test.CanSet() {
+ t.Errorf("%d: CanSet: got true, want false", i)
+ }
+ }
+}
+
+type NonExportedFirst int
+
+func (i NonExportedFirst) ΦExported() {}
+func (i NonExportedFirst) nonexported() int { panic("wrong") }
+
+func TestIssue22073(t *testing.T) {
+ m := ValueOf(NonExportedFirst(0)).Method(0)
+
+ if got := m.Type().NumOut(); got != 0 {
+ t.Errorf("NumOut: got %v, want 0", got)
+ }
+
+ // Shouldn't panic.
+ m.Call(nil)
+}
diff --git a/libgo/go/reflect/export_test.go b/libgo/go/reflect/export_test.go
index 92b1302..203a307 100644
--- a/libgo/go/reflect/export_test.go
+++ b/libgo/go/reflect/export_test.go
@@ -62,7 +62,7 @@ func FirstMethodNameBytes(t Type) *byte {
}
m := ut.methods()[0]
mname := t.(*rtype).nameOff(m.name)
- if *mname.data(0)&(1<<2) == 0 {
+ if *mname.data(0, "name flag field")&(1<<2) == 0 {
panic("method name does not have pkgPath *string")
}
return mname.bytes
@@ -80,7 +80,7 @@ func IsExported(t Type) bool {
/*
func ResolveReflectName(s string) {
- resolveReflectName(newName(s, "", "", false))
+ resolveReflectName(newName(s, "", false))
}
*/
diff --git a/libgo/go/reflect/swapper.go b/libgo/go/reflect/swapper.go
index 5441cb0..bf77b68 100644
--- a/libgo/go/reflect/swapper.go
+++ b/libgo/go/reflect/swapper.go
@@ -65,8 +65,8 @@ func Swapper(slice interface{}) func(i, j int) {
if uint(i) >= uint(s.Len) || uint(j) >= uint(s.Len) {
panic("reflect: slice index out of range")
}
- val1 := arrayAt(s.Data, i, size)
- val2 := arrayAt(s.Data, j, size)
+ val1 := arrayAt(s.Data, i, size, "i < s.Len")
+ val2 := arrayAt(s.Data, j, size, "j < s.Len")
typedmemmove(typ, tmp, val1)
typedmemmove(typ, val1, val2)
typedmemmove(typ, val2, tmp)
diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go
index 664d971..5402a93 100644
--- a/libgo/go/reflect/type.go
+++ b/libgo/go/reflect/type.go
@@ -214,7 +214,7 @@ type Type interface {
// t.FieldByName("x") is not well defined if the struct type t contains
// multiple fields named x (embedded from different packages).
// FieldByName may return one of the fields named x or may report that there are none.
-// See golang.org/issue/4876 for more details.
+// See https://golang.org/issue/4876 for more details.
/*
* These data structures are known to the compiler (../../cmd/internal/gc/reflect.go).
@@ -600,10 +600,15 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) {
if ut == nil {
return Method{}, false
}
- for i := range ut.methods {
- p := &ut.methods[i]
- if p.pkgPath == nil && p.name != nil && *p.name == name {
- return t.Method(i), true
+ utmethods := ut.methods
+ var eidx int
+ for i := 0; i < len(utmethods); i++ {
+ p := utmethods[i]
+ if p.pkgPath == nil {
+ if p.name != nil && *p.name == name {
+ return t.Method(eidx), true
+ }
+ eidx++
}
}
return Method{}, false
@@ -742,6 +747,17 @@ func (t *rtype) Out(i int) Type {
return toType(tt.out[i])
}
+// add returns p+x.
+//
+// The whySafe string is ignored, so that the function still inlines
+// as efficiently as p+x, but all call sites should use the string to
+// record why the addition is safe, which is to say why the addition
+// does not cause x to advance to the very end of p's allocation
+// and therefore point incorrectly at the next block in memory.
+func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
+ return unsafe.Pointer(uintptr(p) + x)
+}
+
func (d ChanDir) String() string {
switch d {
case SendDir:
@@ -2127,7 +2143,7 @@ func StructOf(fields []StructField) Type {
typ.hashfn = func(p unsafe.Pointer, seed uintptr) uintptr {
o := seed
for _, ft := range typ.fields {
- pi := unsafe.Pointer(uintptr(p) + ft.offset())
+ pi := add(p, ft.offset(), "&x.field safe")
o = ft.typ.hashfn(pi, o)
}
return o
@@ -2139,8 +2155,8 @@ func StructOf(fields []StructField) Type {
if comparable {
typ.equalfn = func(p, q unsafe.Pointer) bool {
for _, ft := range typ.fields {
- pi := unsafe.Pointer(uintptr(p) + ft.offset())
- qi := unsafe.Pointer(uintptr(q) + ft.offset())
+ pi := add(p, ft.offset(), "&x.field safe")
+ qi := add(q, ft.offset(), "&x.field safe")
if !ft.typ.equalfn(pi, qi) {
return false
}
@@ -2360,8 +2376,8 @@ func ArrayOf(count int, elem Type) Type {
eequal := typ.equalfn
array.equalfn = func(p, q unsafe.Pointer) bool {
for i := 0; i < count; i++ {
- pi := arrayAt(p, i, esize)
- qi := arrayAt(q, i, esize)
+ pi := arrayAt(p, i, esize, "i < count")
+ qi := arrayAt(q, i, esize, "i < count")
if !eequal(pi, qi) {
return false
}
@@ -2377,7 +2393,7 @@ func ArrayOf(count int, elem Type) Type {
array.hashfn = func(ptr unsafe.Pointer, seed uintptr) uintptr {
o := seed
for i := 0; i < count; i++ {
- o = ehash(arrayAt(ptr, i, esize), o)
+ o = ehash(arrayAt(ptr, i, esize, "i < count"), o)
}
return o
}
diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go
index 792699a..3682b39 100644
--- a/libgo/go/reflect/value.go
+++ b/libgo/go/reflect/value.go
@@ -81,6 +81,13 @@ func (f flag) kind() Kind {
return Kind(f & flagKindMask)
}
+func (f flag) ro() flag {
+ if f&flagRO != 0 {
+ return flagStickyRO
+ }
+ return 0
+}
+
// pointer returns the underlying pointer represented by v.
// v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer
func (v Value) pointer() unsafe.Pointer {
@@ -177,7 +184,7 @@ type emptyInterface struct {
word unsafe.Pointer
}
-// nonEmptyInterface is the header for a interface value with methods.
+// nonEmptyInterface is the header for an interface value with methods.
type nonEmptyInterface struct {
// see ../runtime/iface.go:/Itab
itab *struct {
@@ -235,7 +242,7 @@ func (v Value) Addr() Value {
if v.flag&flagAddr == 0 {
panic("reflect.Value.Addr of unaddressable value")
}
- return Value{v.typ.ptrTo(), v.ptr, (v.flag & flagRO) | flag(Ptr)}
+ return Value{v.typ.ptrTo(), v.ptr, v.flag.ro() | flag(Ptr)}
}
// Bool returns v's underlying value.
@@ -485,11 +492,11 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn
t = m.typ
} else {
rcvrtype = v.typ
- ut := v.typ.uncommon()
- if ut == nil || uint(i) >= uint(len(ut.methods)) {
+ ms := v.typ.exportedMethods()
+ if uint(i) >= uint(len(ms)) {
panic("reflect: internal error: invalid method index")
}
- m := &ut.methods[i]
+ m := ms[i]
if m.pkgPath != nil {
panic("reflect: " + op + " of unexported method")
}
@@ -587,7 +594,7 @@ func (v Value) Elem() Value {
}
x := unpackEface(eface)
if x.flag != 0 {
- x.flag |= v.flag & flagRO
+ x.flag |= v.flag.ro()
}
return x
case Ptr:
@@ -635,8 +642,8 @@ func (v Value) Field(i int) Value {
// or flagIndir is not set and v.ptr is the actual struct data.
// In the former case, we want v.ptr + offset.
// In the latter case, we must have field.offset = 0,
- // so v.ptr + field.offset is still okay.
- ptr := unsafe.Pointer(uintptr(v.ptr) + field.offset())
+ // so v.ptr + field.offset is still the correct address.
+ ptr := add(v.ptr, field.offset(), "same as non-reflect &v.field")
return Value{typ, ptr, fl}
}
@@ -714,9 +721,9 @@ func (v Value) Index(i int) Value {
// or flagIndir is not set and v.ptr is the actual array data.
// In the former case, we want v.ptr + offset.
// In the latter case, we must be doing Index(0), so offset = 0,
- // so v.ptr + offset is still okay.
- val := unsafe.Pointer(uintptr(v.ptr) + offset)
- fl := v.flag&(flagRO|flagIndir|flagAddr) | flag(typ.Kind()) // bits same as overall array
+ // so v.ptr + offset is still the correct address.
+ val := add(v.ptr, offset, "same as &v[i], i < tt.len")
+ fl := v.flag&(flagIndir|flagAddr) | v.flag.ro() | flag(typ.Kind()) // bits same as overall array
return Value{typ, val, fl}
case Slice:
@@ -728,8 +735,8 @@ func (v Value) Index(i int) Value {
}
tt := (*sliceType)(unsafe.Pointer(v.typ))
typ := tt.elem
- val := arrayAt(s.Data, i, typ.size)
- fl := flagAddr | flagIndir | v.flag&flagRO | flag(typ.Kind())
+ val := arrayAt(s.Data, i, typ.size, "i < s.Len")
+ fl := flagAddr | flagIndir | v.flag.ro() | flag(typ.Kind())
return Value{typ, val, fl}
case String:
@@ -737,8 +744,8 @@ func (v Value) Index(i int) Value {
if uint(i) >= uint(s.Len) {
panic("reflect: string index out of range")
}
- p := arrayAt(s.Data, i, 1)
- fl := v.flag&flagRO | flag(Uint8) | flagIndir
+ p := arrayAt(s.Data, i, 1, "i < s.Len")
+ fl := v.flag.ro() | flag(Uint8) | flagIndir
return Value{uint8Type, p, fl}
}
panic(&ValueError{"reflect.Value.Index", v.kind()})
@@ -926,17 +933,16 @@ func (v Value) MapIndex(key Value) Value {
return Value{}
}
typ := tt.elem
- fl := (v.flag | key.flag) & flagRO
+ fl := (v.flag | key.flag).ro()
fl |= flag(typ.Kind())
- if ifaceIndir(typ) {
- // Copy result so future changes to the map
- // won't change the underlying value.
- c := unsafe_New(typ)
- typedmemmove(typ, c, e)
- return Value{typ, c, fl | flagIndir}
- } else {
+ if !ifaceIndir(typ) {
return Value{typ, *(*unsafe.Pointer)(e), fl}
}
+ // Copy result so future changes to the map
+ // won't change the underlying value.
+ c := unsafe_New(typ)
+ typedmemmove(typ, c, e)
+ return Value{typ, c, fl | flagIndir}
}
// MapKeys returns a slice containing all the keys present in the map,
@@ -948,7 +954,7 @@ func (v Value) MapKeys() []Value {
tt := (*mapType)(unsafe.Pointer(v.typ))
keyType := tt.key
- fl := v.flag&flagRO | flag(keyType.Kind())
+ fl := v.flag.ro() | flag(keyType.Kind())
m := v.pointer()
mlen := int(0)
@@ -1420,7 +1426,10 @@ func (v Value) Slice(i, j int) Value {
if i < 0 || j < i || j > s.Len {
panic("reflect.Value.Slice: string slice index out of bounds")
}
- t := stringHeader{arrayAt(s.Data, i, 1), j - i}
+ var t stringHeader
+ if i < s.Len {
+ t = stringHeader{arrayAt(s.Data, i, 1, "i < s.Len"), j - i}
+ }
return Value{v.typ, unsafe.Pointer(&t), v.flag}
}
@@ -1436,13 +1445,13 @@ func (v Value) Slice(i, j int) Value {
s.Len = j - i
s.Cap = cap - i
if cap-i > 0 {
- s.Data = arrayAt(base, i, typ.elem.Size())
+ s.Data = arrayAt(base, i, typ.elem.Size(), "i < cap")
} else {
// do not advance pointer, to avoid pointing beyond end of slice
s.Data = base
}
- fl := v.flag&flagRO | flagIndir | flag(Slice)
+ fl := v.flag.ro() | flagIndir | flag(Slice)
return Value{typ.common(), unsafe.Pointer(&x), fl}
}
@@ -1488,13 +1497,13 @@ func (v Value) Slice3(i, j, k int) Value {
s.Len = j - i
s.Cap = k - i
if k-i > 0 {
- s.Data = arrayAt(base, i, typ.elem.Size())
+ s.Data = arrayAt(base, i, typ.elem.Size(), "i < k <= cap")
} else {
// do not advance pointer, to avoid pointing beyond end of slice
s.Data = base
}
- fl := v.flag&flagRO | flagIndir | flag(Slice)
+ fl := v.flag.ro() | flagIndir | flag(Slice)
return Value{typ.common(), unsafe.Pointer(&x), fl}
}
@@ -1561,11 +1570,11 @@ func (v Value) Type() Type {
return toType(m.typ)
}
// Method on concrete type.
- ut := v.typ.uncommon()
- if ut == nil || uint(i) >= uint(len(ut.methods)) {
+ ms := v.typ.exportedMethods()
+ if uint(i) >= uint(len(ms)) {
panic("reflect: internal error: invalid method index")
}
- m := &ut.methods[i]
+ m := ms[i]
return toType(m.mtyp)
}
@@ -1647,10 +1656,15 @@ func typesMustMatch(what string, t1, t2 Type) {
}
}
-// arrayAt returns the i-th element of p, a C-array whose elements are
-// eltSize wide (in bytes).
-func arrayAt(p unsafe.Pointer, i int, eltSize uintptr) unsafe.Pointer {
- return unsafe.Pointer(uintptr(p) + uintptr(i)*eltSize)
+// arrayAt returns the i-th element of p,
+// an array whose elements are eltSize bytes wide.
+// The array pointed at by p must have at least i+1 elements:
+// it is invalid (but impossible to check here) to pass i >= len,
+// because then the result will point outside the array.
+// whySafe must explain why i < len. (Passing "i < len" is fine;
+// the benefit is to surface this assumption at the call site.)
+func arrayAt(p unsafe.Pointer, i int, eltSize uintptr, whySafe string) unsafe.Pointer {
+ return add(p, uintptr(i)*eltSize, "i < len")
}
// grow grows the slice s so that it can hold extra more values, allocating
@@ -1708,6 +1722,8 @@ func AppendSlice(s, t Value) Value {
// It returns the number of elements copied.
// Dst and src each must have kind Slice or Array, and
// dst and src must have the same element type.
+//
+// As a special case, src can have kind String if the element type of dst is kind Uint8.
func Copy(dst, src Value) int {
dk := dst.kind()
if dk != Array && dk != Slice {
@@ -1719,14 +1735,20 @@ func Copy(dst, src Value) int {
dst.mustBeExported()
sk := src.kind()
+ var stringCopy bool
if sk != Array && sk != Slice {
- panic(&ValueError{"reflect.Copy", sk})
+ stringCopy = sk == String && dst.typ.Elem().Kind() == Uint8
+ if !stringCopy {
+ panic(&ValueError{"reflect.Copy", sk})
+ }
}
src.mustBeExported()
de := dst.typ.Elem()
- se := src.typ.Elem()
- typesMustMatch("reflect.Copy", de, se)
+ if !stringCopy {
+ se := src.typ.Elem()
+ typesMustMatch("reflect.Copy", de, se)
+ }
var ds, ss sliceHeader
if dk == Array {
@@ -1740,8 +1762,13 @@ func Copy(dst, src Value) int {
ss.Data = src.ptr
ss.Len = src.Len()
ss.Cap = ss.Len
- } else {
+ } else if sk == Slice {
ss = *(*sliceHeader)(src.ptr)
+ } else {
+ sh := *(*stringHeader)(src.ptr)
+ ss.Data = sh.Data
+ ss.Len = sh.Len
+ ss.Cap = sh.Len
}
return typedslicecopy(de.common(), ds, ss)
@@ -1929,7 +1956,7 @@ func MakeChan(typ Type, buffer int) Value {
if typ.ChanDir() != BothDir {
panic("reflect.MakeChan: unidirectional channel type")
}
- ch := makechan(typ.(*rtype), uint64(buffer))
+ ch := makechan(typ.(*rtype), buffer)
return Value{typ.common(), unsafe.Pointer(&ch), flag(Chan) | flagIndir}
}
@@ -2021,7 +2048,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value
case directlyAssignable(dst, v.typ):
// Overwrite type so that they match.
// Same memory layout, so no harm done.
- fl := v.flag & (flagRO | flagAddr | flagIndir)
+ fl := v.flag&(flagAddr|flagIndir) | v.flag.ro()
fl |= flag(dst.Kind())
return Value{dst, v.ptr, fl}
@@ -2029,6 +2056,12 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value
if target == nil {
target = unsafe_New(dst)
}
+ if v.Kind() == Interface && v.IsNil() {
+ // A nil ReadWriter passed to nil Reader is OK,
+ // but using ifaceE2I below will panic.
+ // Avoid the panic by returning a nil dst (e.g., Reader) explicitly.
+ return Value{dst, nil, flag(Interface)}
+ }
x := valueInterface(v, false)
if dst.NumMethod() == 0 {
*(*interface{})(target) = x
@@ -2213,72 +2246,72 @@ func makeRunes(f flag, v []rune, t Type) Value {
// convertOp: intXX -> [u]intXX
func cvtInt(v Value, t Type) Value {
- return makeInt(v.flag&flagRO, uint64(v.Int()), t)
+ return makeInt(v.flag.ro(), uint64(v.Int()), t)
}
// convertOp: uintXX -> [u]intXX
func cvtUint(v Value, t Type) Value {
- return makeInt(v.flag&flagRO, v.Uint(), t)
+ return makeInt(v.flag.ro(), v.Uint(), t)
}
// convertOp: floatXX -> intXX
func cvtFloatInt(v Value, t Type) Value {
- return makeInt(v.flag&flagRO, uint64(int64(v.Float())), t)
+ return makeInt(v.flag.ro(), uint64(int64(v.Float())), t)
}
// convertOp: floatXX -> uintXX
func cvtFloatUint(v Value, t Type) Value {
- return makeInt(v.flag&flagRO, uint64(v.Float()), t)
+ return makeInt(v.flag.ro(), uint64(v.Float()), t)
}
// convertOp: intXX -> floatXX
func cvtIntFloat(v Value, t Type) Value {
- return makeFloat(v.flag&flagRO, float64(v.Int()), t)
+ return makeFloat(v.flag.ro(), float64(v.Int()), t)
}
// convertOp: uintXX -> floatXX
func cvtUintFloat(v Value, t Type) Value {
- return makeFloat(v.flag&flagRO, float64(v.Uint()), t)
+ return makeFloat(v.flag.ro(), float64(v.Uint()), t)
}
// convertOp: floatXX -> floatXX
func cvtFloat(v Value, t Type) Value {
- return makeFloat(v.flag&flagRO, v.Float(), t)
+ return makeFloat(v.flag.ro(), v.Float(), t)
}
// convertOp: complexXX -> complexXX
func cvtComplex(v Value, t Type) Value {
- return makeComplex(v.flag&flagRO, v.Complex(), t)
+ return makeComplex(v.flag.ro(), v.Complex(), t)
}
// convertOp: intXX -> string
func cvtIntString(v Value, t Type) Value {
- return makeString(v.flag&flagRO, string(v.Int()), t)
+ return makeString(v.flag.ro(), string(v.Int()), t)
}
// convertOp: uintXX -> string
func cvtUintString(v Value, t Type) Value {
- return makeString(v.flag&flagRO, string(v.Uint()), t)
+ return makeString(v.flag.ro(), string(v.Uint()), t)
}
// convertOp: []byte -> string
func cvtBytesString(v Value, t Type) Value {
- return makeString(v.flag&flagRO, string(v.Bytes()), t)
+ return makeString(v.flag.ro(), string(v.Bytes()), t)
}
// convertOp: string -> []byte
func cvtStringBytes(v Value, t Type) Value {
- return makeBytes(v.flag&flagRO, []byte(v.String()), t)
+ return makeBytes(v.flag.ro(), []byte(v.String()), t)
}
// convertOp: []rune -> string
func cvtRunesString(v Value, t Type) Value {
- return makeString(v.flag&flagRO, string(v.runes()), t)
+ return makeString(v.flag.ro(), string(v.runes()), t)
}
// convertOp: string -> []rune
func cvtStringRunes(v Value, t Type) Value {
- return makeRunes(v.flag&flagRO, []rune(v.String()), t)
+ return makeRunes(v.flag.ro(), []rune(v.String()), t)
}
// convertOp: direct copy
@@ -2293,7 +2326,7 @@ func cvtDirect(v Value, typ Type) Value {
ptr = c
f &^= flagAddr
}
- return Value{t, ptr, v.flag&flagRO | f} // v.flag&flagRO|f == f?
+ return Value{t, ptr, v.flag.ro() | f} // v.flag.ro()|f == f?
}
// convertOp: concrete -> interface
@@ -2305,14 +2338,14 @@ func cvtT2I(v Value, typ Type) Value {
} else {
ifaceE2I(typ.(*rtype), x, target)
}
- return Value{typ.common(), target, v.flag&flagRO | flagIndir | flag(Interface)}
+ return Value{typ.common(), target, v.flag.ro() | flagIndir | flag(Interface)}
}
// convertOp: interface -> interface
func cvtI2I(v Value, typ Type) Value {
if v.IsNil() {
ret := Zero(typ)
- ret.flag |= v.flag & flagRO
+ ret.flag |= v.flag.ro()
return ret
}
return cvtT2I(v.Elem(), typ)
@@ -2337,7 +2370,7 @@ func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, receive
//go:noescape
func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool
-func makechan(typ *rtype, size uint64) (ch unsafe.Pointer)
+func makechan(typ *rtype, size int) (ch unsafe.Pointer)
func makemap(t *rtype, cap int) (m unsafe.Pointer)
//go:noescape