1
0
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:
Sebastien Binet 2015-01-27 10:04:11 +01:00 committed by Keith Randall
parent c0fa9e3f6f
commit 918fdae348
5 changed files with 307 additions and 33 deletions

View File

@ -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

View File

@ -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

View File

@ -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)
} }

View File

@ -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) {

View File

@ -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