diff options
author | Ian Lance Taylor <iant@golang.org> | 2021-07-30 14:28:58 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2021-08-12 20:23:07 -0700 |
commit | c5b21c3f4c17b0649155035d2f9aa97b2da8a813 (patch) | |
tree | c6d3a68b503ba5b16182acbb958e3e5dbc95a43b /libgo/go/reflect | |
parent | 72be20e20299ec57b4bc9ba03d5b7d6bf10e97cc (diff) | |
download | gcc-c5b21c3f4c17b0649155035d2f9aa97b2da8a813.zip gcc-c5b21c3f4c17b0649155035d2f9aa97b2da8a813.tar.gz gcc-c5b21c3f4c17b0649155035d2f9aa97b2da8a813.tar.bz2 |
libgo: update to Go1.17rc2
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/341629
Diffstat (limited to 'libgo/go/reflect')
-rw-r--r-- | libgo/go/reflect/all_test.go | 410 | ||||
-rw-r--r-- | libgo/go/reflect/internal/example1/example.go | 6 | ||||
-rw-r--r-- | libgo/go/reflect/internal/example2/example.go | 6 | ||||
-rw-r--r-- | libgo/go/reflect/type.go | 74 | ||||
-rw-r--r-- | libgo/go/reflect/value.go | 66 | ||||
-rw-r--r-- | libgo/go/reflect/visiblefields.go | 105 | ||||
-rw-r--r-- | libgo/go/reflect/visiblefields_test.go | 330 |
7 files changed, 816 insertions, 181 deletions
diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index c73ef47..7fa815f 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -15,6 +15,8 @@ import ( "math/rand" "os" . "reflect" + "reflect/internal/example1" + "reflect/internal/example2" "runtime" "sort" "strconv" @@ -1945,6 +1947,22 @@ func BenchmarkCall(b *testing.B) { }) } +type myint int64 + +func (i *myint) inc() { + *i = *i + 1 +} + +func BenchmarkCallMethod(b *testing.B) { + b.ReportAllocs() + z := new(myint) + + v := ValueOf(z.inc) + for i := 0; i < b.N; i++ { + v.Call(nil) + } +} + func BenchmarkCallArgCopy(b *testing.B) { byteArray := func(n int) Value { return Zero(ArrayOf(n, TypeOf(byte(0)))) @@ -2903,6 +2921,7 @@ func TestFieldPkgPath(t *testing.T) { index []int pkgPath string embedded bool + exported bool } checkPkgPath := func(name string, s []pkgpathTest) { @@ -2914,27 +2933,63 @@ func TestFieldPkgPath(t *testing.T) { if got, want := f.Anonymous, test.embedded; got != want { t.Errorf("%s: Field(%d).Anonymous = %v, want %v", name, test.index, got, want) } + if got, want := f.IsExported(), test.exported; got != want { + t.Errorf("%s: Field(%d).IsExported = %v, want %v", name, test.index, got, want) + } } } checkPkgPath("testStruct", []pkgpathTest{ - {[]int{0}, "", false}, // Exported - {[]int{1}, "reflect_test", false}, // unexported - {[]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 + {[]int{0}, "", false, true}, // Exported + {[]int{1}, "reflect_test", false, false}, // unexported + {[]int{2}, "", true, true}, // OtherPkgFields + {[]int{2, 0}, "", false, true}, // OtherExported + {[]int{2, 1}, "reflect", false, false}, // otherUnexported + {[]int{3}, "reflect_test", true, false}, // int + {[]int{4}, "reflect_test", true, false}, // *x }) type localOtherPkgFields OtherPkgFields typ = TypeOf(localOtherPkgFields{}) checkPkgPath("localOtherPkgFields", []pkgpathTest{ - {[]int{0}, "", false}, // OtherExported - {[]int{1}, "reflect", false}, // otherUnexported + {[]int{0}, "", false, true}, // OtherExported + {[]int{1}, "reflect", false, false}, // otherUnexported }) } +func TestMethodPkgPath(t *testing.T) { + type I interface { + x() + X() + } + typ := TypeOf((*interface { + I + y() + Y() + })(nil)).Elem() + + tests := []struct { + name string + pkgPath string + exported bool + }{ + {"X", "", true}, + {"Y", "", true}, + {"x", "reflect_test", false}, + {"y", "reflect_test", false}, + } + + for _, test := range tests { + m, _ := typ.MethodByName(test.name) + if got, want := m.PkgPath, test.pkgPath; got != want { + t.Errorf("MethodByName(%q).PkgPath = %q, want %q", test.name, got, want) + } + if got, want := m.IsExported(), test.exported; got != want { + t.Errorf("MethodByName(%q).IsExported = %v, want %v", test.name, got, want) + } + } +} + func TestVariadicType(t *testing.T) { // Test example from Type documentation. var f func(x int, y ...float64) @@ -3765,8 +3820,22 @@ type Empty struct{} type MyStruct struct { x int `some:"tag"` } +type MyStruct1 struct { + x struct { + int `some:"bar"` + } +} +type MyStruct2 struct { + x struct { + int `some:"foo"` + } +} type MyString string type MyBytes []byte +type MyBytesArrayPtr0 *[0]byte +type MyBytesArrayPtr *[4]byte +type MyBytesArray0 [0]byte +type MyBytesArray [4]byte type MyRunes []int32 type MyFunc func() type MyByte byte @@ -4073,6 +4142,30 @@ var convertTests = []struct { {V(MyString("runes♝")), V(MyRunes("runes♝"))}, {V(MyRunes("runes♕")), V(MyString("runes♕"))}, + // slice to array pointer + {V([]byte(nil)), V((*[0]byte)(nil))}, + {V([]byte{}), V(new([0]byte))}, + {V([]byte{7}), V(&[1]byte{7})}, + {V(MyBytes([]byte(nil))), V((*[0]byte)(nil))}, + {V(MyBytes([]byte{})), V(new([0]byte))}, + {V(MyBytes([]byte{9})), V(&[1]byte{9})}, + {V([]byte(nil)), V(MyBytesArrayPtr0(nil))}, + {V([]byte{}), V(MyBytesArrayPtr0(new([0]byte)))}, + {V([]byte{1, 2, 3, 4}), V(MyBytesArrayPtr(&[4]byte{1, 2, 3, 4}))}, + {V(MyBytes([]byte{})), V(MyBytesArrayPtr0(new([0]byte)))}, + {V(MyBytes([]byte{5, 6, 7, 8})), V(MyBytesArrayPtr(&[4]byte{5, 6, 7, 8}))}, + + {V([]byte(nil)), V((*MyBytesArray0)(nil))}, + {V([]byte{}), V((*MyBytesArray0)(new([0]byte)))}, + {V([]byte{1, 2, 3, 4}), V(&MyBytesArray{1, 2, 3, 4})}, + {V(MyBytes([]byte(nil))), V((*MyBytesArray0)(nil))}, + {V(MyBytes([]byte{})), V((*MyBytesArray0)(new([0]byte)))}, + {V(MyBytes([]byte{5, 6, 7, 8})), V(&MyBytesArray{5, 6, 7, 8})}, + {V(new([0]byte)), V(new(MyBytesArray0))}, + {V(new(MyBytesArray0)), V(new([0]byte))}, + {V(MyBytesArrayPtr0(nil)), V((*[0]byte)(nil))}, + {V((*[0]byte)(nil)), V(MyBytesArrayPtr0(nil))}, + // named types and equal underlying types {V(new(int)), V(new(integer))}, {V(new(integer)), V(new(int))}, @@ -4115,6 +4208,9 @@ var convertTests = []struct { x int `some:"bar"` }{}), V(MyStruct{})}, + {V(MyStruct1{}), V(MyStruct2{})}, + {V(MyStruct2{}), V(MyStruct1{})}, + // can convert *byte and *MyByte {V((*byte)(nil)), V((*MyByte)(nil))}, {V((*MyByte)(nil)), V((*byte)(nil))}, @@ -4218,6 +4314,9 @@ func TestConvert(t *testing.T) { // vout1 represents the in value converted to the in type. v1 := tt.in + if !v1.CanConvert(t1) { + t.Errorf("ValueOf(%T(%[1]v)).CanConvert(%s) = false, want true", tt.in.Interface(), t1) + } vout1 := v1.Convert(t1) out1 := vout1.Interface() if vout1.Type() != tt.in.Type() || !DeepEqual(out1, tt.in.Interface()) { @@ -4225,11 +4324,17 @@ func TestConvert(t *testing.T) { } // vout2 represents the in value converted to the out type. + if !v1.CanConvert(t2) { + t.Errorf("ValueOf(%T(%[1]v)).CanConvert(%s) = false, want true", tt.in.Interface(), t2) + } vout2 := v1.Convert(t2) out2 := vout2.Interface() if vout2.Type() != tt.out.Type() || !DeepEqual(out2, tt.out.Interface()) { t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t2, out2, tt.out.Interface()) } + if got, want := vout2.Kind(), vout2.Type().Kind(); got != want { + t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) has internal kind %v want %v", tt.in.Interface(), t1, got, want) + } // vout3 represents a new value of the out type, set to vout2. This makes // sure the converted value vout2 is really usable as a regular value. @@ -4274,6 +4379,22 @@ func TestConvert(t *testing.T) { } } +func TestConvertPanic(t *testing.T) { + s := make([]byte, 4) + p := new([8]byte) + v := ValueOf(s) + pt := TypeOf(p) + if !v.Type().ConvertibleTo(pt) { + t.Errorf("[]byte should be convertible to *[8]byte") + } + if v.CanConvert(pt) { + t.Errorf("slice with length 4 should not be convertible to *[8]byte") + } + shouldPanic("reflect: cannot convert slice with length 4 to pointer to array with length 8", func() { + _ = v.Convert(pt) + }) +} + var gFloat32 float32 func TestConvertNaNs(t *testing.T) { @@ -4612,6 +4733,14 @@ func TestArrayOfDirectIface(t *testing.T) { } } +// Ensure passing in negative lengths panics. +// See https://golang.org/issue/43603 +func TestArrayOfPanicOnNegativeLength(t *testing.T) { + shouldPanic("reflect: negative length passed to ArrayOf", func() { + ArrayOf(-1, TypeOf(byte(0))) + }) +} + func TestSliceOf(t *testing.T) { // check construction and use of type not in binary type T int @@ -6357,148 +6486,142 @@ func clobber() { runtime.GC() } -type funcLayoutTest struct { - rcvr, t Type - size, argsize, retOffset uintptr - stack []byte // pointer bitmap: 1 is pointer, 0 is scalar - gc []byte -} - -var funcLayoutTests []funcLayoutTest - -func init() { - var argAlign uintptr = PtrSize - roundup := func(x uintptr, a uintptr) uintptr { - return (x + a - 1) / a * a - } - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a, b string) string { return "" }).Type(), - 6 * PtrSize, - 4 * PtrSize, - 4 * PtrSize, - []byte{1, 0, 1, 0, 1}, - []byte{1, 0, 1, 0, 1}, - }) +/* +func TestFuncLayout(t *testing.T) { + align := func(x uintptr) uintptr { + return (x + PtrSize - 1) &^ (PtrSize - 1) + } var r []byte if PtrSize == 4 { r = []byte{0, 0, 0, 1} } else { r = []byte{0, 0, 1} } - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(), - roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign), - roundup(3*4, PtrSize) + PtrSize + 2, - roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign), - r, - r, - }) - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(), - 4 * PtrSize, - 4 * PtrSize, - 4 * PtrSize, - []byte{1, 0, 1, 1}, - []byte{1, 0, 1, 1}, - }) type S struct { a, b uintptr c, d *byte } - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a S) {}).Type(), - 4 * PtrSize, - 4 * PtrSize, - 4 * PtrSize, - []byte{0, 0, 1, 1}, - []byte{0, 0, 1, 1}, - }) - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - ValueOf((*byte)(nil)).Type(), - ValueOf(func(a uintptr, b *int) {}).Type(), - roundup(3*PtrSize, argAlign), - 3 * PtrSize, - roundup(3*PtrSize, argAlign), - []byte{1, 0, 1}, - []byte{1, 0, 1}, - }) - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a uintptr) {}).Type(), - roundup(PtrSize, argAlign), - PtrSize, - roundup(PtrSize, argAlign), - []byte{}, - []byte{}, - }) - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func() uintptr { return 0 }).Type(), - PtrSize, - 0, - 0, - []byte{}, - []byte{}, - }) - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - ValueOf(uintptr(0)).Type(), - ValueOf(func(a uintptr) {}).Type(), - 2 * PtrSize, - 2 * PtrSize, - 2 * PtrSize, - []byte{1}, - []byte{1}, + type test struct { + rcvr, typ Type + size, argsize, retOffset uintptr + stack, gc, inRegs, outRegs []byte // pointer bitmap: 1 is pointer, 0 is scalar + intRegs, floatRegs int + floatRegSize uintptr + } + tests := []test{ + { + typ: ValueOf(func(a, b string) string { return "" }).Type(), + size: 6 * PtrSize, + argsize: 4 * PtrSize, + retOffset: 4 * PtrSize, + stack: []byte{1, 0, 1, 0, 1}, + gc: []byte{1, 0, 1, 0, 1}, + }, + { + typ: ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(), + size: align(align(3*4) + PtrSize + 2), + argsize: align(3*4) + PtrSize + 2, + retOffset: align(align(3*4) + PtrSize + 2), + stack: r, + gc: r, + }, + { + typ: ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(), + size: 4 * PtrSize, + argsize: 4 * PtrSize, + retOffset: 4 * PtrSize, + stack: []byte{1, 0, 1, 1}, + gc: []byte{1, 0, 1, 1}, + }, + { + typ: ValueOf(func(a S) {}).Type(), + size: 4 * PtrSize, + argsize: 4 * PtrSize, + retOffset: 4 * PtrSize, + stack: []byte{0, 0, 1, 1}, + gc: []byte{0, 0, 1, 1}, + }, + { + rcvr: ValueOf((*byte)(nil)).Type(), + typ: ValueOf(func(a uintptr, b *int) {}).Type(), + size: 3 * PtrSize, + argsize: 3 * PtrSize, + retOffset: 3 * PtrSize, + stack: []byte{1, 0, 1}, + gc: []byte{1, 0, 1}, + }, + { + typ: ValueOf(func(a uintptr) {}).Type(), + size: PtrSize, + argsize: PtrSize, + retOffset: PtrSize, + stack: []byte{}, + gc: []byte{}, + }, + { + typ: ValueOf(func() uintptr { return 0 }).Type(), + size: PtrSize, + argsize: 0, + retOffset: 0, + stack: []byte{}, + gc: []byte{}, + }, + { + rcvr: ValueOf(uintptr(0)).Type(), + typ: ValueOf(func(a uintptr) {}).Type(), + size: 2 * PtrSize, + argsize: 2 * PtrSize, + retOffset: 2 * PtrSize, + stack: []byte{1}, + gc: []byte{1}, // Note: this one is tricky, as the receiver is not a pointer. But we // pass the receiver by reference to the autogenerated pointer-receiver // version of the function. - }) -} - -func TestFuncLayout(t *testing.T) { - t.Skip("gccgo does not use funcLayout") - for _, lt := range funcLayoutTests { - typ, argsize, retOffset, stack, gc, ptrs := FuncLayout(lt.t, lt.rcvr) - if typ.Size() != lt.size { - t.Errorf("funcLayout(%v, %v).size=%d, want %d", lt.t, lt.rcvr, typ.Size(), lt.size) - } - if argsize != lt.argsize { - t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.t, lt.rcvr, argsize, lt.argsize) - } - if retOffset != lt.retOffset { - t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.t, lt.rcvr, retOffset, lt.retOffset) - } - if !bytes.Equal(stack, lt.stack) { - t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.t, lt.rcvr, stack, lt.stack) - } - if !bytes.Equal(gc, lt.gc) { - t.Errorf("funcLayout(%v, %v).gc=%v, want %v", lt.t, lt.rcvr, gc, lt.gc) - } - if ptrs && len(stack) == 0 || !ptrs && len(stack) > 0 { - t.Errorf("funcLayout(%v, %v) pointers flag=%v, want %v", lt.t, lt.rcvr, ptrs, !ptrs) + }, + // TODO(mknyszek): Add tests for non-zero register count. + } + for _, lt := range tests { + name := lt.typ.String() + if lt.rcvr != nil { + name = lt.rcvr.String() + "." + name } + t.Run(name, func(t *testing.T) { + defer SetArgRegs(SetArgRegs(lt.intRegs, lt.floatRegs, lt.floatRegSize)) + + typ, argsize, retOffset, stack, gc, inRegs, outRegs, ptrs := FuncLayout(lt.typ, lt.rcvr) + if typ.Size() != lt.size { + t.Errorf("funcLayout(%v, %v).size=%d, want %d", lt.typ, lt.rcvr, typ.Size(), lt.size) + } + if argsize != lt.argsize { + t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.typ, lt.rcvr, argsize, lt.argsize) + } + if retOffset != lt.retOffset { + t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.typ, lt.rcvr, retOffset, lt.retOffset) + } + if !bytes.Equal(stack, lt.stack) { + t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.typ, lt.rcvr, stack, lt.stack) + } + if !bytes.Equal(gc, lt.gc) { + t.Errorf("funcLayout(%v, %v).gc=%v, want %v", lt.typ, lt.rcvr, gc, lt.gc) + } + if !bytes.Equal(inRegs, lt.inRegs) { + t.Errorf("funcLayout(%v, %v).inRegs=%v, want %v", lt.typ, lt.rcvr, inRegs, lt.inRegs) + } + if !bytes.Equal(outRegs, lt.outRegs) { + t.Errorf("funcLayout(%v, %v).outRegs=%v, want %v", lt.typ, lt.rcvr, outRegs, lt.outRegs) + } + if ptrs && len(stack) == 0 || !ptrs && len(stack) > 0 { + t.Errorf("funcLayout(%v, %v) pointers flag=%v, want %v", lt.typ, lt.rcvr, ptrs, !ptrs) + } + }) } } +*/ + func verifyGCBits(t *testing.T, typ Type, bits []byte) { heapBits := GCBits(New(typ).Interface()) if !bytes.Equal(heapBits, bits) { @@ -6861,21 +6984,6 @@ func TestExported(t *testing.T) { } */ -type embed struct { - EmbedWithUnexpMeth -} - -/* -func TestNameBytesAreAligned(t *testing.T) { - typ := TypeOf(embed{}) - b := FirstMethodNameBytes(typ) - v := uintptr(unsafe.Pointer(b)) - if v%unsafe.Alignof((*byte)(nil)) != 0 { - t.Errorf("reflect.name.bytes pointer is not aligned: %x", v) - } -} -*/ - func TestTypeStrings(t *testing.T) { type stringTest struct { typ Type @@ -7215,3 +7323,13 @@ func iterateToString(it *MapIter) string { sort.Strings(got) return "[" + strings.Join(got, ", ") + "]" } + +func TestConvertibleTo(t *testing.T) { + t1 := ValueOf(example1.MyStruct{}).Type() + t2 := ValueOf(example2.MyStruct{}).Type() + + // Shouldn't raise stack overflow + if t1.ConvertibleTo(t2) { + t.Fatalf("(%s).ConvertibleTo(%s) = true, want false", t1, t2) + } +} diff --git a/libgo/go/reflect/internal/example1/example.go b/libgo/go/reflect/internal/example1/example.go new file mode 100644 index 0000000..0f829a8 --- /dev/null +++ b/libgo/go/reflect/internal/example1/example.go @@ -0,0 +1,6 @@ +package example1 + +type MyStruct struct { + MyStructs []MyStruct + MyStruct *MyStruct +} diff --git a/libgo/go/reflect/internal/example2/example.go b/libgo/go/reflect/internal/example2/example.go new file mode 100644 index 0000000..df64ba1 --- /dev/null +++ b/libgo/go/reflect/internal/example2/example.go @@ -0,0 +1,6 @@ +package example2 + +type MyStruct struct { + MyStructs []MyStruct + MyStruct *MyStruct +} diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index c05ec68..0c394a2 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -108,9 +108,15 @@ type Type interface { AssignableTo(u Type) bool // ConvertibleTo reports whether a value of the type is convertible to type u. + // Even if ConvertibleTo returns true, the conversion may still panic. + // For example, a slice of type []T is convertible to *[N]T, + // but the conversion will panic if its length is less than N. ConvertibleTo(u Type) bool // Comparable reports whether values of this type are comparable. + // Even if Comparable returns true, the comparison may still panic. + // For example, values of interface type are comparable, + // but the comparison will panic if their dynamic type is not comparable. Comparable() bool // Methods applicable only to some types, depending on Kind. @@ -224,7 +230,7 @@ type Type interface { // See https://golang.org/issue/4876 for more details. /* - * These data structures are known to the compiler (../../cmd/internal/gc/reflect.go). + * These data structures are known to the compiler (../../cmd/internal/reflectdata/reflect.go). * A few are known to ../runtime/type.go to convey to debuggers. * They are also known to ../runtime/type.go. */ @@ -419,12 +425,13 @@ type structType struct { // Method represents a single method. type Method struct { // Name is the method name. + Name string + // PkgPath is the package path that qualifies a lower case (unexported) // method name. It is empty for upper case (exported) method names. // The combination of PkgPath and Name uniquely identifies a method // in a method set. // See https://golang.org/ref/spec#Uniqueness_of_identifiers - Name string PkgPath string Type Type // method type @@ -432,6 +439,11 @@ type Method struct { Index int // index for Type.Method } +// IsExported reports whether the method is exported. +func (m Method) IsExported() bool { + return m.PkgPath == "" +} + const ( kindDirectIface = 1 << 5 kindGCProg = 1 << 6 // Type.gc points to GC program @@ -828,6 +840,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { type StructField struct { // Name is the field name. Name string + // PkgPath is the package path that qualifies a lower case (unexported) // field name. It is empty for upper case (exported) field names. // See https://golang.org/ref/spec#Uniqueness_of_identifiers @@ -840,6 +853,11 @@ type StructField struct { Anonymous bool // is an embedded field } +// IsExported reports whether the field is exported. +func (f StructField) IsExported() bool { + return f.PkgPath == "" +} + // A StructTag is the tag string in a struct field. // // By convention, tag strings are a concatenation of @@ -1300,7 +1318,7 @@ func haveIdenticalType(T, V Type, cmpTags bool) bool { return T == V } - if T.Name() != V.Name() || T.Kind() != V.Kind() { + if T.Name() != V.Name() || T.Kind() != V.Kind() || T.PkgPath() != V.PkgPath() { return false } @@ -1529,7 +1547,7 @@ func MapOf(key, elem Type) Type { // Make a map type. // Note: flag values must match those used in the TMAP case - // in ../cmd/compile/internal/gc/reflect.go:dtypesym. + // in ../cmd/compile/internal/reflectdata/reflect.go:writeType. var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil) mt := **(**mapType)(unsafe.Pointer(&imap)) mt.string = &s @@ -2257,8 +2275,7 @@ func runtimeStructField(field StructField) (structField, string) { panic("reflect.StructOf: field \"" + field.Name + "\" is anonymous but has PkgPath set") } - exported := field.PkgPath == "" - if exported { + if field.IsExported() { // Best-effort check for misuse. // Since this field will be treated as exported, not much harm done if Unicode lowercase slips through. c := field.Name[0] @@ -2298,7 +2315,7 @@ func runtimeStructField(field StructField) (structField, string) { // typeptrdata returns the length in bytes of the prefix of t // containing pointer data. Anything after this offset is scalar data. -// keep in sync with ../cmd/compile/internal/gc/reflect.go +// keep in sync with ../cmd/compile/internal/reflectdata/reflect.go func typeptrdata(t *rtype) uintptr { switch t.Kind() { case Struct: @@ -2322,25 +2339,29 @@ func typeptrdata(t *rtype) uintptr { } } -// See cmd/compile/internal/gc/reflect.go for derivation of constant. +// See cmd/compile/internal/reflectdata/reflect.go for derivation of constant. const maxPtrmaskBytes = 2048 -// ArrayOf returns the array type with the given count and element type. +// ArrayOf returns the array type with the given length and element type. // For example, if t represents int, ArrayOf(5, t) represents [5]int. // // If the resulting type would be larger than the available address space, // ArrayOf panics. -func ArrayOf(count int, elem Type) Type { +func ArrayOf(length int, elem Type) Type { + if length < 0 { + panic("reflect: negative length passed to ArrayOf") + } + typ := elem.(*rtype) // Look in cache. - ckey := cacheKey{Array, typ, nil, uintptr(count)} + ckey := cacheKey{Array, typ, nil, uintptr(length)} if array, ok := lookupCache.Load(ckey); ok { return array.(Type) } // Look in known types. - s := "[" + strconv.Itoa(count) + "]" + *typ.string + s := "[" + strconv.Itoa(length) + "]" + *typ.string if tt := lookupType(s); tt != nil { array := (*arrayType)(unsafe.Pointer(toType(tt).(*rtype))) if array.elem == typ { @@ -2358,28 +2379,27 @@ func ArrayOf(count int, elem Type) Type { // gccgo uses a different hash. // array.hash = fnv1(typ.hash, '[') - // for n := uint32(count); n > 0; n >>= 8 { + // for n := uint32(length); n > 0; n >>= 8 { // array.hash = fnv1(array.hash, byte(n)) // } // array.hash = fnv1(array.hash, ']') array.hash = typ.hash + 1 + 13 - array.elem = typ array.ptrToThis = nil if typ.size > 0 { max := ^uintptr(0) / typ.size - if uintptr(count) > max { + if uintptr(length) > max { panic("reflect.ArrayOf: array size would exceed virtual address space") } } - array.size = typ.size * uintptr(count) - if count > 0 && typ.ptrdata != 0 { - array.ptrdata = typ.size*uintptr(count-1) + typ.ptrdata + array.size = typ.size * uintptr(length) + if length > 0 && typ.ptrdata != 0 { + array.ptrdata = typ.size*uintptr(length-1) + typ.ptrdata } array.align = typ.align array.fieldAlign = typ.fieldAlign array.uncommonType = nil - array.len = uintptr(count) + array.len = uintptr(length) array.slice = SliceOf(elem).(*rtype) switch { @@ -2388,7 +2408,7 @@ func ArrayOf(count int, elem Type) Type { array.gcdata = nil array.ptrdata = 0 - case count == 1: + case length == 1: // In memory, 1-element array looks just like the element. array.kind |= typ.kind & kindGCProg array.gcdata = typ.gcdata @@ -2397,7 +2417,7 @@ func ArrayOf(count int, elem Type) Type { case typ.kind&kindGCProg == 0 && array.size <= maxPtrmaskBytes*8*ptrSize: // Element is small with pointer mask; array is still small. // Create direct pointer mask by turning each 1 bit in elem - // into count 1 bits in larger mask. + // into length 1 bits in larger mask. mask := make([]byte, (array.ptrdata/ptrSize+7)/8) emitGCMask(mask, 0, typ, array.len) array.gcdata = &mask[0] @@ -2418,14 +2438,14 @@ func ArrayOf(count int, elem Type) Type { prog = appendVarint(prog, elemWords-elemPtrs-1) } } - // Repeat count-1 times. + // Repeat length-1 times. if elemWords < 0x80 { prog = append(prog, byte(elemWords|0x80)) } else { prog = append(prog, 0x80) prog = appendVarint(prog, elemWords) } - prog = appendVarint(prog, uintptr(count)-1) + prog = appendVarint(prog, uintptr(length)-1) prog = append(prog, 0) *(*uint32)(unsafe.Pointer(&prog[0])) = uint32(len(prog) - 4) array.kind |= kindGCProg @@ -2439,9 +2459,9 @@ func ArrayOf(count int, elem Type) Type { array.equal = nil if eequal := etyp.equal; eequal != nil { array.equal = func(p, q unsafe.Pointer) bool { - for i := 0; i < count; i++ { - pi := arrayAt(p, i, esize, "i < count") - qi := arrayAt(q, i, esize, "i < count") + for i := 0; i < length; i++ { + pi := arrayAt(p, i, esize, "i < length") + qi := arrayAt(q, i, esize, "i < length") if !eequal(pi, qi) { return false } @@ -2451,7 +2471,7 @@ func ArrayOf(count int, elem Type) Type { } switch { - case count == 1 && !ifaceIndir(typ): + case length == 1 && !ifaceIndir(typ): // array of 1 direct iface type can be direct array.kind |= kindDirectIface default: diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index 0b4f094..300ef1a 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -5,6 +5,7 @@ package reflect import ( + "internal/itoa" "internal/unsafeheader" "math" "runtime" @@ -350,6 +351,8 @@ func (v Value) CallSlice(in []Value) []Value { var callGC bool // for testing; see TestCallMethodJump +const debugReflectCall = false + func (v Value) call(op string, in []Value) []Value { // Get function pointer, type. t := (*funcType)(unsafe.Pointer(v.typ)) @@ -372,8 +375,9 @@ func (v Value) call(op string, in []Value) []Value { isSlice := op == "CallSlice" n := t.NumIn() + isVariadic := t.IsVariadic() if isSlice { - if !t.IsVariadic() { + if !isVariadic { panic("reflect: CallSlice of non-variadic function") } if len(in) < n { @@ -383,13 +387,13 @@ func (v Value) call(op string, in []Value) []Value { panic("reflect: CallSlice with too many input arguments") } } else { - if t.IsVariadic() { + if isVariadic { n-- } if len(in) < n { panic("reflect: Call with too few input arguments") } - if !t.IsVariadic() && len(in) > n { + if !isVariadic && len(in) > n { panic("reflect: Call with too many input arguments") } } @@ -403,7 +407,7 @@ func (v Value) call(op string, in []Value) []Value { panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String()) } } - if !isSlice && t.IsVariadic() { + if !isSlice && isVariadic { // prepare slice for remaining values m := len(in) - n slice := MakeSlice(t.In(n), m, m) @@ -857,10 +861,16 @@ func valueInterface(v Value, safe bool) interface{} { return packEface(v) } -// InterfaceData returns the interface v's value as a uintptr pair. +// InterfaceData returns a pair of unspecified uintptr values. // It panics if v's Kind is not Interface. +// +// In earlier versions of Go, this function returned the interface's +// value as a uintptr pair. As of Go 1.4, the implementation of +// interface values precludes any defined use of InterfaceData. +// +// Deprecated: The memory representation of interface values is not +// compatible with InterfaceData. func (v Value) InterfaceData() [2]uintptr { - // TODO: deprecate this v.mustBe(Interface) // We treat this as a read operation, so we allow // it even for unexported data, because the caller @@ -2200,9 +2210,14 @@ func New(typ Type) Value { panic("reflect: New(nil)") } t := typ.(*rtype) + pt := t.ptrTo() + if ifaceIndir(pt) { + // This is a pointer to a go:notinheap type. + panic("reflect: New of type that may not be allocated in heap (possibly undefined cgo C type)") + } ptr := unsafe_New(t) fl := flag(Ptr) - return Value{t.ptrTo(), ptr, fl} + return Value{pt, ptr, fl} } // NewAt returns a Value representing a pointer to a value of the @@ -2255,7 +2270,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value // Convert returns the value v converted to type t. // If the usual Go conversion rules do not allow conversion -// of the value v to type t, Convert panics. +// of the value v to type t, or if converting v to type t panics, Convert panics. func (v Value) Convert(t Type) Value { if v.flag&flagMethod != 0 { v = makeMethodValue("Convert", v) @@ -2267,6 +2282,26 @@ func (v Value) Convert(t Type) Value { return op(v, t) } +// CanConvert reports whether the value v can be converted to type t. +// If v.CanConvert(t) returns true then v.Convert(t) will not panic. +func (v Value) CanConvert(t Type) bool { + vt := v.Type() + if !vt.ConvertibleTo(t) { + return false + } + // Currently the only conversion that is OK in terms of type + // but that can panic depending on the value is converting + // from slice to pointer-to-array. + if vt.Kind() == Slice && t.Kind() == Ptr && t.Elem().Kind() == Array { + n := t.Elem().Len() + h := (*unsafeheader.Slice)(v.ptr) + if n > h.Len { + return false + } + } + return true +} + // convertOp returns the function to convert a value of type src // to a value of type dst. If the conversion is illegal, convertOp returns nil. func convertOp(dst, src *rtype) func(Value, Type) Value { @@ -2326,6 +2361,11 @@ func convertOp(dst, src *rtype) func(Value, Type) Value { return cvtRunesString } } + // "x is a slice, T is a pointer-to-array type, + // and the slice and array types have identical element types." + if dst.Kind() == Ptr && dst.Elem().Kind() == Array && src.Elem() == dst.Elem().Elem() { + return cvtSliceArrayPtr + } case Chan: if dst.Kind() == Chan && specialChannelAssignability(dst, src) { @@ -2519,6 +2559,16 @@ func cvtStringRunes(v Value, t Type) Value { return makeRunes(v.flag.ro(), []rune(v.String()), t) } +// convertOp: []T -> *[N]T +func cvtSliceArrayPtr(v Value, t Type) Value { + n := t.Elem().Len() + h := (*unsafeheader.Slice)(v.ptr) + if n > h.Len { + panic("reflect: cannot convert slice with length " + itoa.Itoa(h.Len) + " to pointer to array with length " + itoa.Itoa(n)) + } + return Value{t.common(), h.Data, v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Ptr)} +} + // convertOp: direct copy func cvtDirect(v Value, typ Type) Value { f := v.flag diff --git a/libgo/go/reflect/visiblefields.go b/libgo/go/reflect/visiblefields.go new file mode 100644 index 0000000..1a2b535 --- /dev/null +++ b/libgo/go/reflect/visiblefields.go @@ -0,0 +1,105 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflect + +// VisibleFields returns all the visible fields in t, which must be a +// struct type. A field is defined as visible if it's accessible +// directly with a FieldByName call. The returned fields include fields +// inside anonymous struct members and unexported fields. They follow +// the same order found in the struct, with anonymous fields followed +// immediately by their promoted fields. +// +// For each element e of the returned slice, the corresponding field +// can be retrieved from a value v of type t by calling v.FieldByIndex(e.Index). +func VisibleFields(t Type) []StructField { + if t == nil { + panic("reflect: VisibleFields(nil)") + } + if t.Kind() != Struct { + panic("reflect.VisibleFields of non-struct type") + } + w := &visibleFieldsWalker{ + byName: make(map[string]int), + visiting: make(map[Type]bool), + fields: make([]StructField, 0, t.NumField()), + index: make([]int, 0, 2), + } + w.walk(t) + // Remove all the fields that have been hidden. + // Use an in-place removal that avoids copying in + // the common case that there are no hidden fields. + j := 0 + for i := range w.fields { + f := &w.fields[i] + if f.Name == "" { + continue + } + if i != j { + // A field has been removed. We need to shuffle + // all the subsequent elements up. + w.fields[j] = *f + } + j++ + } + return w.fields[:j] +} + +type visibleFieldsWalker struct { + byName map[string]int + visiting map[Type]bool + fields []StructField + index []int +} + +// walk walks all the fields in the struct type t, visiting +// fields in index preorder and appending them to w.fields +// (this maintains the required ordering). +// Fields that have been overridden have their +// Name field cleared. +func (w *visibleFieldsWalker) walk(t Type) { + if w.visiting[t] { + return + } + w.visiting[t] = true + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + w.index = append(w.index, i) + add := true + if oldIndex, ok := w.byName[f.Name]; ok { + old := &w.fields[oldIndex] + if len(w.index) == len(old.Index) { + // Fields with the same name at the same depth + // cancel one another out. Set the field name + // to empty to signify that has happened, and + // there's no need to add this field. + old.Name = "" + add = false + } else if len(w.index) < len(old.Index) { + // The old field loses because it's deeper than the new one. + old.Name = "" + } else { + // The old field wins because it's shallower than the new one. + add = false + } + } + if add { + // Copy the index so that it's not overwritten + // by the other appends. + f.Index = append([]int(nil), w.index...) + w.byName[f.Name] = len(w.fields) + w.fields = append(w.fields, f) + } + if f.Anonymous { + if f.Type.Kind() == Ptr { + f.Type = f.Type.Elem() + } + if f.Type.Kind() == Struct { + w.walk(f.Type) + } + } + w.index = w.index[:len(w.index)-1] + } + delete(w.visiting, t) +} diff --git a/libgo/go/reflect/visiblefields_test.go b/libgo/go/reflect/visiblefields_test.go new file mode 100644 index 0000000..915bbee --- /dev/null +++ b/libgo/go/reflect/visiblefields_test.go @@ -0,0 +1,330 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflect_test + +import ( + . "reflect" + "testing" +) + +type structField struct { + name string + index []int +} + +var fieldsTests = []struct { + testName string + val interface{} + expect []structField +}{{ + testName: "SimpleStruct", + val: struct { + A int + B string + C bool + }{}, + expect: []structField{{ + name: "A", + index: []int{0}, + }, { + name: "B", + index: []int{1}, + }, { + name: "C", + index: []int{2}, + }}, +}, { + testName: "NonEmbeddedStructMember", + val: struct { + A struct { + X int + } + }{}, + expect: []structField{{ + name: "A", + index: []int{0}, + }}, +}, { + testName: "EmbeddedExportedStruct", + val: struct { + SFG + }{}, + expect: []structField{{ + name: "SFG", + index: []int{0}, + }, { + name: "F", + index: []int{0, 0}, + }, { + name: "G", + index: []int{0, 1}, + }}, +}, { + testName: "EmbeddedUnexportedStruct", + val: struct { + sFG + }{}, + expect: []structField{{ + name: "sFG", + index: []int{0}, + }, { + name: "F", + index: []int{0, 0}, + }, { + name: "G", + index: []int{0, 1}, + }}, +}, { + testName: "TwoEmbeddedStructsWithCancellingMembers", + val: struct { + SFG + SF + }{}, + expect: []structField{{ + name: "SFG", + index: []int{0}, + }, { + name: "G", + index: []int{0, 1}, + }, { + name: "SF", + index: []int{1}, + }}, +}, { + testName: "EmbeddedStructsWithSameFieldsAtDifferentDepths", + val: struct { + SFGH3 + SG1 + SFG2 + SF2 + L int + }{}, + expect: []structField{{ + name: "SFGH3", + index: []int{0}, + }, { + name: "SFGH2", + index: []int{0, 0}, + }, { + name: "SFGH1", + index: []int{0, 0, 0}, + }, { + name: "SFGH", + index: []int{0, 0, 0, 0}, + }, { + name: "H", + index: []int{0, 0, 0, 0, 2}, + }, { + name: "SG1", + index: []int{1}, + }, { + name: "SG", + index: []int{1, 0}, + }, { + name: "G", + index: []int{1, 0, 0}, + }, { + name: "SFG2", + index: []int{2}, + }, { + name: "SFG1", + index: []int{2, 0}, + }, { + name: "SFG", + index: []int{2, 0, 0}, + }, { + name: "SF2", + index: []int{3}, + }, { + name: "SF1", + index: []int{3, 0}, + }, { + name: "SF", + index: []int{3, 0, 0}, + }, { + name: "L", + index: []int{4}, + }}, +}, { + testName: "EmbeddedPointerStruct", + val: struct { + *SF + }{}, + expect: []structField{{ + name: "SF", + index: []int{0}, + }, { + name: "F", + index: []int{0, 0}, + }}, +}, { + testName: "EmbeddedNotAPointer", + val: struct { + M + }{}, + expect: []structField{{ + name: "M", + index: []int{0}, + }}, +}, { + testName: "RecursiveEmbedding", + val: Rec1{}, + expect: []structField{{ + name: "Rec2", + index: []int{0}, + }, { + name: "F", + index: []int{0, 0}, + }, { + name: "Rec1", + index: []int{0, 1}, + }}, +}, { + testName: "RecursiveEmbedding2", + val: Rec2{}, + expect: []structField{{ + name: "F", + index: []int{0}, + }, { + name: "Rec1", + index: []int{1}, + }, { + name: "Rec2", + index: []int{1, 0}, + }}, +}, { + testName: "RecursiveEmbedding3", + val: RS3{}, + expect: []structField{{ + name: "RS2", + index: []int{0}, + }, { + name: "RS1", + index: []int{1}, + }, { + name: "i", + index: []int{1, 0}, + }}, +}} + +type SFG struct { + F int + G int +} + +type SFG1 struct { + SFG +} + +type SFG2 struct { + SFG1 +} + +type SFGH struct { + F int + G int + H int +} + +type SFGH1 struct { + SFGH +} + +type SFGH2 struct { + SFGH1 +} + +type SFGH3 struct { + SFGH2 +} + +type SF struct { + F int +} + +type SF1 struct { + SF +} + +type SF2 struct { + SF1 +} + +type SG struct { + G int +} + +type SG1 struct { + SG +} + +type sFG struct { + F int + G int +} + +type RS1 struct { + i int +} + +type RS2 struct { + RS1 +} + +type RS3 struct { + RS2 + RS1 +} + +type M map[string]interface{} + +type Rec1 struct { + *Rec2 +} + +type Rec2 struct { + F string + *Rec1 +} + +func TestFields(t *testing.T) { + for _, test := range fieldsTests { + test := test + t.Run(test.testName, func(t *testing.T) { + typ := TypeOf(test.val) + fields := VisibleFields(typ) + if got, want := len(fields), len(test.expect); got != want { + t.Fatalf("unexpected field count; got %d want %d", got, want) + } + + for j, field := range fields { + expect := test.expect[j] + t.Logf("field %d: %s", j, expect.name) + gotField := typ.FieldByIndex(field.Index) + // Unfortunately, FieldByIndex does not return + // a field with the same index that we passed in, + // so we set it to the expected value so that + // it can be compared later with the result of FieldByName. + gotField.Index = field.Index + expectField := typ.FieldByIndex(expect.index) + // ditto. + expectField.Index = expect.index + if !DeepEqual(gotField, expectField) { + t.Fatalf("unexpected field result\ngot %#v\nwant %#v", gotField, expectField) + } + + // Sanity check that we can actually access the field by the + // expected name. + gotField1, ok := typ.FieldByName(expect.name) + if !ok { + t.Fatalf("field %q not accessible by name", expect.name) + } + if !DeepEqual(gotField1, expectField) { + t.Fatalf("unexpected FieldByName result; got %#v want %#v", gotField1, expectField) + } + } + }) + } +} |