mirror of
https://github.com/golang/go
synced 2024-11-18 14:14:46 -07:00
reflect: implement ArrayOf
This change exposes reflect.ArrayOf to create new reflect.Type array types at runtime, when given a reflect.Type element. - reflect: implement ArrayOf - reflect: tests for ArrayOf - runtime: document that typeAlg is used by reflect and must be kept in synchronized Fixes #5996. Change-Id: I5d07213364ca915c25612deea390507c19461758 Reviewed-on: https://go-review.googlesource.com/4111 Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
c0fa9e3f6f
commit
918fdae348
@ -15,6 +15,7 @@ import (
|
|||||||
. "reflect"
|
. "reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@ -3388,26 +3389,242 @@ func checkSameType(t *testing.T, x, y interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestArrayOf(t *testing.T) {
|
func TestArrayOf(t *testing.T) {
|
||||||
// TODO(rsc): Finish ArrayOf and enable-test.
|
|
||||||
t.Skip("ArrayOf is not finished (and not exported)")
|
|
||||||
|
|
||||||
// check construction and use of type not in binary
|
// check construction and use of type not in binary
|
||||||
type T int
|
for _, table := range []struct {
|
||||||
at := ArrayOf(10, TypeOf(T(1)))
|
n int
|
||||||
v := New(at).Elem()
|
value func(i int) interface{}
|
||||||
for i := 0; i < v.Len(); i++ {
|
comparable bool
|
||||||
v.Index(i).Set(ValueOf(T(i)))
|
want string
|
||||||
}
|
}{
|
||||||
s := fmt.Sprint(v.Interface())
|
{
|
||||||
want := "[0 1 2 3 4 5 6 7 8 9]"
|
n: 0,
|
||||||
if s != want {
|
value: func(i int) interface{} { type Tint int; return Tint(i) },
|
||||||
t.Errorf("constructed array = %s, want %s", s, want)
|
comparable: true,
|
||||||
|
want: "[]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} { type Tint int; return Tint(i) },
|
||||||
|
comparable: true,
|
||||||
|
want: "[0 1 2 3 4 5 6 7 8 9]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} { type Tfloat float64; return Tfloat(i) },
|
||||||
|
comparable: true,
|
||||||
|
want: "[0 1 2 3 4 5 6 7 8 9]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} { type Tstring string; return Tstring(strconv.Itoa(i)) },
|
||||||
|
comparable: true,
|
||||||
|
want: "[0 1 2 3 4 5 6 7 8 9]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} { type Tstruct struct{ V int }; return Tstruct{i} },
|
||||||
|
comparable: true,
|
||||||
|
want: "[{0} {1} {2} {3} {4} {5} {6} {7} {8} {9}]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} { type Tint int; return []Tint{Tint(i)} },
|
||||||
|
comparable: false,
|
||||||
|
want: "[[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} { type Tint int; return [1]Tint{Tint(i)} },
|
||||||
|
comparable: true,
|
||||||
|
want: "[[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} { type Tstruct struct{ V [1]int }; return Tstruct{[1]int{i}} },
|
||||||
|
comparable: true,
|
||||||
|
want: "[{[0]} {[1]} {[2]} {[3]} {[4]} {[5]} {[6]} {[7]} {[8]} {[9]}]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} { type Tstruct struct{ V []int }; return Tstruct{[]int{i}} },
|
||||||
|
comparable: false,
|
||||||
|
want: "[{[0]} {[1]} {[2]} {[3]} {[4]} {[5]} {[6]} {[7]} {[8]} {[9]}]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} { type TstructUV struct{ U, V int }; return TstructUV{i, i} },
|
||||||
|
comparable: true,
|
||||||
|
want: "[{0 0} {1 1} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} {9 9}]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
n: 10,
|
||||||
|
value: func(i int) interface{} {
|
||||||
|
type TstructUV struct {
|
||||||
|
U int
|
||||||
|
V float64
|
||||||
|
}
|
||||||
|
return TstructUV{i, float64(i)}
|
||||||
|
},
|
||||||
|
comparable: true,
|
||||||
|
want: "[{0 0} {1 1} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} {9 9}]",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
at := ArrayOf(table.n, TypeOf(table.value(0)))
|
||||||
|
v := New(at).Elem()
|
||||||
|
vok := New(at).Elem()
|
||||||
|
vnot := New(at).Elem()
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
v.Index(i).Set(ValueOf(table.value(i)))
|
||||||
|
vok.Index(i).Set(ValueOf(table.value(i)))
|
||||||
|
j := i
|
||||||
|
if i+1 == v.Len() {
|
||||||
|
j = i + 1
|
||||||
|
}
|
||||||
|
vnot.Index(i).Set(ValueOf(table.value(j))) // make it differ only by last element
|
||||||
|
}
|
||||||
|
s := fmt.Sprint(v.Interface())
|
||||||
|
if s != table.want {
|
||||||
|
t.Errorf("constructed array = %s, want %s", s, table.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
if table.comparable != at.Comparable() {
|
||||||
|
t.Errorf("constructed array (%#v) is comparable=%v, want=%v", v.Interface(), at.Comparable(), table.comparable)
|
||||||
|
}
|
||||||
|
if table.comparable {
|
||||||
|
if table.n > 0 {
|
||||||
|
if DeepEqual(vnot.Interface(), v.Interface()) {
|
||||||
|
t.Errorf(
|
||||||
|
"arrays (%#v) compare ok (but should not)",
|
||||||
|
v.Interface(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !DeepEqual(vok.Interface(), v.Interface()) {
|
||||||
|
t.Errorf(
|
||||||
|
"arrays (%#v) compare NOT-ok (but should)",
|
||||||
|
v.Interface(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that type already in binary is found
|
// check that type already in binary is found
|
||||||
|
type T int
|
||||||
checkSameType(t, Zero(ArrayOf(5, TypeOf(T(1)))).Interface(), [5]T{})
|
checkSameType(t, Zero(ArrayOf(5, TypeOf(T(1)))).Interface(), [5]T{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestArrayOfGC(t *testing.T) {
|
||||||
|
type T *uintptr
|
||||||
|
tt := TypeOf(T(nil))
|
||||||
|
const n = 100
|
||||||
|
var x []interface{}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
v := New(ArrayOf(n, tt)).Elem()
|
||||||
|
for j := 0; j < v.Len(); j++ {
|
||||||
|
p := new(uintptr)
|
||||||
|
*p = uintptr(i*n + j)
|
||||||
|
v.Index(j).Set(ValueOf(p).Convert(tt))
|
||||||
|
}
|
||||||
|
x = append(x, v.Interface())
|
||||||
|
}
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
|
for i, xi := range x {
|
||||||
|
v := ValueOf(xi)
|
||||||
|
for j := 0; j < v.Len(); j++ {
|
||||||
|
k := v.Index(j).Elem().Interface()
|
||||||
|
if k != uintptr(i*n+j) {
|
||||||
|
t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArrayOfAlg(t *testing.T) {
|
||||||
|
at := ArrayOf(6, TypeOf(byte(0)))
|
||||||
|
v1 := New(at).Elem()
|
||||||
|
v2 := New(at).Elem()
|
||||||
|
if v1.Interface() != v1.Interface() {
|
||||||
|
t.Errorf("constructed array %v not equal to itself", v1.Interface())
|
||||||
|
}
|
||||||
|
v1.Index(5).Set(ValueOf(byte(1)))
|
||||||
|
if i1, i2 := v1.Interface(), v2.Interface(); i1 == i2 {
|
||||||
|
t.Errorf("constructed arrays %v and %v should not be equal", i1, i2)
|
||||||
|
}
|
||||||
|
|
||||||
|
at = ArrayOf(6, TypeOf([]int(nil)))
|
||||||
|
v1 = New(at).Elem()
|
||||||
|
shouldPanic(func() { _ = v1.Interface() == v1.Interface() })
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArrayOfGenericAlg(t *testing.T) {
|
||||||
|
at1 := ArrayOf(5, TypeOf(string("")))
|
||||||
|
at := ArrayOf(6, at1)
|
||||||
|
v1 := New(at).Elem()
|
||||||
|
v2 := New(at).Elem()
|
||||||
|
if v1.Interface() != v1.Interface() {
|
||||||
|
t.Errorf("constructed array %v not equal to itself", v1.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
v1.Index(0).Index(0).Set(ValueOf("abc"))
|
||||||
|
v2.Index(0).Index(0).Set(ValueOf("efg"))
|
||||||
|
if i1, i2 := v1.Interface(), v2.Interface(); i1 == i2 {
|
||||||
|
t.Errorf("constructed arrays %v and %v should not be equal", i1, i2)
|
||||||
|
}
|
||||||
|
|
||||||
|
v1.Index(0).Index(0).Set(ValueOf("abc"))
|
||||||
|
v2.Index(0).Index(0).Set(ValueOf((v1.Index(0).Index(0).String() + " ")[:3]))
|
||||||
|
if i1, i2 := v1.Interface(), v2.Interface(); i1 != i2 {
|
||||||
|
t.Errorf("constructed arrays %v and %v should be equal", i1, i2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test hash
|
||||||
|
m := MakeMap(MapOf(at, TypeOf(int(0))))
|
||||||
|
m.SetMapIndex(v1, ValueOf(1))
|
||||||
|
if i1, i2 := v1.Interface(), v2.Interface(); !m.MapIndex(v2).IsValid() {
|
||||||
|
t.Errorf("constructed arrays %v and %v have different hashes", i1, i2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArrayOfDirectIface(t *testing.T) {
|
||||||
|
{
|
||||||
|
type T [1]*byte
|
||||||
|
i1 := Zero(TypeOf(T{})).Interface()
|
||||||
|
v1 := ValueOf(&i1).Elem()
|
||||||
|
p1 := v1.InterfaceData()[1]
|
||||||
|
|
||||||
|
i2 := Zero(ArrayOf(1, PtrTo(TypeOf(int8(0))))).Interface()
|
||||||
|
v2 := ValueOf(&i2).Elem()
|
||||||
|
p2 := v2.InterfaceData()[1]
|
||||||
|
|
||||||
|
if p1 != 0 {
|
||||||
|
t.Errorf("got p1=%v. want=%v", p1, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p2 != 0 {
|
||||||
|
t.Errorf("got p2=%v. want=%v", p2, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
type T [0]*byte
|
||||||
|
i1 := Zero(TypeOf(T{})).Interface()
|
||||||
|
v1 := ValueOf(&i1).Elem()
|
||||||
|
p1 := v1.InterfaceData()[1]
|
||||||
|
|
||||||
|
i2 := Zero(ArrayOf(0, PtrTo(TypeOf(int8(0))))).Interface()
|
||||||
|
v2 := ValueOf(&i2).Elem()
|
||||||
|
p2 := v2.InterfaceData()[1]
|
||||||
|
|
||||||
|
if p1 == 0 {
|
||||||
|
t.Errorf("got p1=%v. want=not-%v", p1, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p2 == 0 {
|
||||||
|
t.Errorf("got p2=%v. want=not-%v", p2, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSliceOf(t *testing.T) {
|
func TestSliceOf(t *testing.T) {
|
||||||
// check construction and use of type not in binary
|
// check construction and use of type not in binary
|
||||||
type T int
|
type T int
|
||||||
|
@ -15,7 +15,6 @@ func IsRO(v Value) bool {
|
|||||||
return v.flag&flagRO != 0
|
return v.flag&flagRO != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var ArrayOf = arrayOf
|
|
||||||
var CallGC = &callGC
|
var CallGC = &callGC
|
||||||
|
|
||||||
const PtrSize = ptrSize
|
const PtrSize = ptrSize
|
||||||
|
@ -262,11 +262,11 @@ type rtype struct {
|
|||||||
// a copy of runtime.typeAlg
|
// a copy of runtime.typeAlg
|
||||||
type typeAlg struct {
|
type typeAlg struct {
|
||||||
// function for hashing objects of this type
|
// function for hashing objects of this type
|
||||||
// (ptr to object, size, seed) -> hash
|
// (ptr to object, seed) -> hash
|
||||||
hash func(unsafe.Pointer, uintptr, uintptr) uintptr
|
hash func(unsafe.Pointer, uintptr) uintptr
|
||||||
// function for comparing objects of this type
|
// function for comparing objects of this type
|
||||||
// (ptr to object A, ptr to object B, size) -> ==?
|
// (ptr to object A, ptr to object B) -> ==?
|
||||||
equal func(unsafe.Pointer, unsafe.Pointer, uintptr) bool
|
equal func(unsafe.Pointer, unsafe.Pointer) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method on non-interface type
|
// Method on non-interface type
|
||||||
@ -1878,26 +1878,24 @@ func SliceOf(t Type) Type {
|
|||||||
//
|
//
|
||||||
// If the resulting type would be larger than the available address space,
|
// If the resulting type would be larger than the available address space,
|
||||||
// ArrayOf panics.
|
// ArrayOf panics.
|
||||||
//
|
func ArrayOf(count int, elem Type) Type {
|
||||||
// TODO(rsc): Unexported for now. Export once the alg field is set correctly
|
|
||||||
// for the type. This may require significant work.
|
|
||||||
//
|
|
||||||
// TODO(rsc): TestArrayOf is also disabled. Re-enable.
|
|
||||||
func arrayOf(count int, elem Type) Type {
|
|
||||||
typ := elem.(*rtype)
|
typ := elem.(*rtype)
|
||||||
|
// call SliceOf here as it calls cacheGet/cachePut.
|
||||||
|
// ArrayOf also calls cacheGet/cachePut and thus may modify the state of
|
||||||
|
// the lookupCache mutex.
|
||||||
slice := SliceOf(elem)
|
slice := SliceOf(elem)
|
||||||
|
|
||||||
// Look in cache.
|
// Look in cache.
|
||||||
ckey := cacheKey{Array, typ, nil, uintptr(count)}
|
ckey := cacheKey{Array, typ, nil, uintptr(count)}
|
||||||
if slice := cacheGet(ckey); slice != nil {
|
if array := cacheGet(ckey); array != nil {
|
||||||
return slice
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look in known types.
|
// Look in known types.
|
||||||
s := "[" + strconv.Itoa(count) + "]" + *typ.string
|
s := "[" + strconv.Itoa(count) + "]" + *typ.string
|
||||||
for _, tt := range typesByString(s) {
|
for _, tt := range typesByString(s) {
|
||||||
slice := (*sliceType)(unsafe.Pointer(tt))
|
array := (*arrayType)(unsafe.Pointer(tt))
|
||||||
if slice.elem == typ {
|
if array.elem == typ {
|
||||||
return cachePut(ckey, tt)
|
return cachePut(ckey, tt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1907,7 +1905,6 @@ func arrayOf(count int, elem Type) Type {
|
|||||||
prototype := *(**arrayType)(unsafe.Pointer(&iarray))
|
prototype := *(**arrayType)(unsafe.Pointer(&iarray))
|
||||||
array := new(arrayType)
|
array := new(arrayType)
|
||||||
*array = *prototype
|
*array = *prototype
|
||||||
// TODO: Set extra kind bits correctly.
|
|
||||||
array.string = &s
|
array.string = &s
|
||||||
array.hash = fnv1(typ.hash, '[')
|
array.hash = fnv1(typ.hash, '[')
|
||||||
for n := uint32(count); n > 0; n >>= 8 {
|
for n := uint32(count); n > 0; n >>= 8 {
|
||||||
@ -1922,15 +1919,68 @@ func arrayOf(count int, elem Type) Type {
|
|||||||
array.size = typ.size * uintptr(count)
|
array.size = typ.size * uintptr(count)
|
||||||
array.align = typ.align
|
array.align = typ.align
|
||||||
array.fieldAlign = typ.fieldAlign
|
array.fieldAlign = typ.fieldAlign
|
||||||
// TODO: array.alg
|
|
||||||
// TODO: array.gc
|
|
||||||
// TODO:
|
|
||||||
array.uncommonType = nil
|
array.uncommonType = nil
|
||||||
array.ptrToThis = nil
|
array.ptrToThis = nil
|
||||||
array.zero = unsafe.Pointer(&make([]byte, array.size)[0])
|
if array.size > 0 {
|
||||||
|
zero := make([]byte, array.size)
|
||||||
|
array.zero = unsafe.Pointer(&zero[0])
|
||||||
|
}
|
||||||
array.len = uintptr(count)
|
array.len = uintptr(count)
|
||||||
array.slice = slice.(*rtype)
|
array.slice = slice.(*rtype)
|
||||||
|
|
||||||
|
var gc gcProg
|
||||||
|
// TODO(sbinet): count could be possibly very large.
|
||||||
|
// use insArray directives from ../runtime/mbitmap.go.
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
gc.appendProg(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasPtr bool
|
||||||
|
array.gc[0], hasPtr = gc.finalize()
|
||||||
|
if !hasPtr {
|
||||||
|
array.kind |= kindNoPointers
|
||||||
|
} else {
|
||||||
|
array.kind &^= kindNoPointers
|
||||||
|
}
|
||||||
|
|
||||||
|
etyp := typ.common()
|
||||||
|
esize := etyp.Size()
|
||||||
|
ealg := etyp.alg
|
||||||
|
|
||||||
|
array.alg = new(typeAlg)
|
||||||
|
if ealg.equal != nil {
|
||||||
|
eequal := ealg.equal
|
||||||
|
array.alg.equal = func(p, q unsafe.Pointer) bool {
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
pi := arrayAt(p, i, esize)
|
||||||
|
qi := arrayAt(q, i, esize)
|
||||||
|
if !eequal(pi, qi) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ealg.hash != nil {
|
||||||
|
ehash := ealg.hash
|
||||||
|
array.alg.hash = func(ptr unsafe.Pointer, seed uintptr) uintptr {
|
||||||
|
o := seed
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
o = ehash(arrayAt(ptr, i, esize), o)
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case count == 1 && !ifaceIndir(typ):
|
||||||
|
// array of 1 direct iface type can be direct
|
||||||
|
array.kind |= kindDirectIface
|
||||||
|
default:
|
||||||
|
array.kind &^= kindDirectIface
|
||||||
|
}
|
||||||
|
|
||||||
return cachePut(ckey, &array.rtype)
|
return cachePut(ckey, &array.rtype)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1767,6 +1767,12 @@ 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)
|
||||||
|
}
|
||||||
|
|
||||||
// grow grows the slice s so that it can hold extra more values, allocating
|
// grow grows the slice s so that it can hold extra more values, allocating
|
||||||
// more capacity if needed. It also returns the old and new slice lengths.
|
// more capacity if needed. It also returns the old and new slice lengths.
|
||||||
func grow(s Value, extra int) (Value, int, int) {
|
func grow(s Value, extra int) (Value, int, int) {
|
||||||
|
@ -38,6 +38,8 @@ const (
|
|||||||
alg_max
|
alg_max
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// typeAlg is also copied/used in reflect/type.go.
|
||||||
|
// keep them in sync.
|
||||||
type typeAlg struct {
|
type typeAlg struct {
|
||||||
// function for hashing objects of this type
|
// function for hashing objects of this type
|
||||||
// (ptr to object, seed) -> hash
|
// (ptr to object, seed) -> hash
|
||||||
|
Loading…
Reference in New Issue
Block a user