Skip to content

Commit

Permalink
reflect: implement ArrayOf
Browse files Browse the repository at this point in the history
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 golang#5996.

Change-Id: I5d07213364ca915c25612deea390507c19461758
Reviewed-on: https://go-review.googlesource.com/4111
Reviewed-by: Keith Randall <[email protected]>
  • Loading branch information
sbinet authored and randall77 committed Apr 21, 2015
1 parent c0fa9e3 commit 918fdae
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 33 deletions.
243 changes: 230 additions & 13 deletions src/reflect/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
. "reflect"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -3388,26 +3389,242 @@ func checkSameType(t *testing.T, x, y interface{}) {
}

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
type T int
at := ArrayOf(10, TypeOf(T(1)))
v := New(at).Elem()
for i := 0; i < v.Len(); i++ {
v.Index(i).Set(ValueOf(T(i)))
}
s := fmt.Sprint(v.Interface())
want := "[0 1 2 3 4 5 6 7 8 9]"
if s != want {
t.Errorf("constructed array = %s, want %s", s, want)
for _, table := range []struct {
n int
value func(i int) interface{}
comparable bool
want string
}{
{
n: 0,
value: func(i int) interface{} { type Tint int; return Tint(i) },
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
type T int
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) {
// check construction and use of type not in binary
type T int
Expand Down
1 change: 0 additions & 1 deletion src/reflect/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ func IsRO(v Value) bool {
return v.flag&flagRO != 0
}

var ArrayOf = arrayOf
var CallGC = &callGC

const PtrSize = ptrSize
Expand Down
Loading

0 comments on commit 918fdae

Please sign in to comment.