diff options
author | Ian Lance Taylor <iant@golang.org> | 2018-01-09 01:23:08 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2018-01-09 01:23:08 +0000 |
commit | 1a2f01efa63036a5104f203a4789e682c0e0915d (patch) | |
tree | 373e15778dc8295354584e1f86915ae493b604ff /libgo/go/reflect | |
parent | 8799df67f2dab88f9fda11739c501780a85575e2 (diff) | |
download | gcc-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.go | 229 | ||||
-rw-r--r-- | libgo/go/reflect/export_test.go | 4 | ||||
-rw-r--r-- | libgo/go/reflect/swapper.go | 4 | ||||
-rw-r--r-- | libgo/go/reflect/type.go | 38 | ||||
-rw-r--r-- | libgo/go/reflect/value.go | 153 |
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 |