Skip to content

Commit 2fd7b37

Browse files
committed
reflect: implement StructOf
This change exposes a facility to create new struct types from a slice of reflect.StructFields. - reflect: implement StructOf - reflect: tests for StructOf - runtime: generate typelinks for structs Fixes golang#5748. Change-Id: I3b8fd4fadd6ce3b1b922e284f0ae72a3a8e3ce44
1 parent ca9128f commit 2fd7b37

File tree

3 files changed

+444
-2
lines changed

3 files changed

+444
-2
lines changed

src/cmd/internal/gc/reflect.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,7 @@ ok:
12261226
break
12271227
}
12281228
fallthrough
1229-
case TARRAY, TCHAN, TFUNC, TMAP:
1229+
case TARRAY, TCHAN, TFUNC, TMAP, TSTRUCT:
12301230
slink := typelinksym(t)
12311231
dsymptr(slink, 0, s, 0)
12321232
ggloblsym(slink, int32(Widthptr), int8(dupok|obj.RODATA))

src/reflect/all_test.go

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3693,6 +3693,270 @@ func TestSliceOfGC(t *testing.T) {
36933693
}
36943694
}
36953695

3696+
func TestStructOf(t *testing.T) {
3697+
// check construction and use of type not in binary
3698+
fields := []StructField{
3699+
StructField{
3700+
Name: "S",
3701+
Tag: "s",
3702+
Type: TypeOf(""),
3703+
},
3704+
StructField{
3705+
Name: "X",
3706+
Tag: "x",
3707+
Type: TypeOf(byte(0)),
3708+
},
3709+
StructField{
3710+
Name: "Y",
3711+
Type: TypeOf(uint64(0)),
3712+
},
3713+
StructField{
3714+
Name: "Z",
3715+
Type: TypeOf([3]uint16{}),
3716+
},
3717+
}
3718+
3719+
st := StructOf(fields)
3720+
v := New(st).Elem()
3721+
runtime.GC()
3722+
v.FieldByName("X").Set(ValueOf(byte(2)))
3723+
v.FieldByIndex([]int{1}).Set(ValueOf(byte(1)))
3724+
runtime.GC()
3725+
3726+
s := fmt.Sprint(v.Interface())
3727+
want := `{ 1 0 [0 0 0]}`
3728+
if s != want {
3729+
t.Errorf("constructed struct = %s, want %s", s, want)
3730+
}
3731+
3732+
// check the size, alignment and field offsets
3733+
stt := TypeOf(struct {
3734+
String string
3735+
X byte
3736+
Y uint64
3737+
Z [3]uint16
3738+
}{})
3739+
if st.Size() != stt.Size() {
3740+
t.Errorf("constructed struct size = %v, want %v", st.Size(), stt.Size())
3741+
}
3742+
if st.Align() != stt.Align() {
3743+
t.Errorf("constructed struct align = %v, want %v", st.Align(), stt.Align())
3744+
}
3745+
if st.FieldAlign() != stt.FieldAlign() {
3746+
t.Errorf("constructed struct field align = %v, want %v", st.FieldAlign(), stt.FieldAlign())
3747+
}
3748+
for i := 0; i < st.NumField(); i++ {
3749+
o1 := st.Field(i).Offset
3750+
o2 := stt.Field(i).Offset
3751+
if o1 != o2 {
3752+
t.Errorf("constructed struct field %v offset = %v, want %v", i, o1, o2)
3753+
}
3754+
}
3755+
3756+
// check duplicate names
3757+
shouldPanic(func() {
3758+
StructOf([]StructField{
3759+
StructField{Name: "string", Type: TypeOf("")},
3760+
StructField{Name: "string", Type: TypeOf("")},
3761+
})
3762+
})
3763+
shouldPanic(func() {
3764+
StructOf([]StructField{
3765+
StructField{Type: TypeOf("")},
3766+
StructField{Name: "string", Type: TypeOf("")},
3767+
})
3768+
})
3769+
shouldPanic(func() {
3770+
StructOf([]StructField{
3771+
StructField{Type: TypeOf("")},
3772+
StructField{Type: TypeOf("")},
3773+
})
3774+
})
3775+
// check that type already in binary is found
3776+
checkSameType(t, Zero(StructOf(fields[2:3])).Interface(), struct{ Y uint64 }{})
3777+
}
3778+
3779+
func TestStructOfGC(t *testing.T) {
3780+
type T *uintptr
3781+
tt := TypeOf(T(nil))
3782+
fields := []StructField{
3783+
{Name: "X", Type: TypeOf(T(nil))},
3784+
{Name: "Y", Type: TypeOf(T(nil))},
3785+
}
3786+
st := StructOf(fields)
3787+
3788+
const n = 10000
3789+
var x []interface{}
3790+
for i := 0; i < n; i++ {
3791+
v := New(st).Elem()
3792+
for j := 0; j < v.NumField(); j += 1 {
3793+
p := new(uintptr)
3794+
*p = uintptr(i*n + j)
3795+
v.Field(j).Set(ValueOf(p).Convert(tt))
3796+
}
3797+
x = append(x, v.Interface())
3798+
}
3799+
runtime.GC()
3800+
3801+
for i, xi := range x {
3802+
v := ValueOf(xi)
3803+
for j := 0; j < v.NumField(); j += 1 {
3804+
k := v.Field(j).Elem().Interface()
3805+
if k != uintptr(i*n+j) {
3806+
t.Errorf("lost x[%d]%c = %d, want %d", i, "XY"[j], k, i*n+j)
3807+
}
3808+
}
3809+
}
3810+
}
3811+
3812+
func TestStructOfAlg(t *testing.T) {
3813+
st := StructOf([]StructField{{Name: "X", Tag: "x", Type: TypeOf(int(0))}})
3814+
v1 := New(st).Elem()
3815+
v2 := New(st).Elem()
3816+
if !DeepEqual(v1.Interface(), v1.Interface()) {
3817+
t.Errorf("constructed struct %v not equal to itself", v1.Interface())
3818+
}
3819+
v1.FieldByName("X").Set(ValueOf(int(1)))
3820+
if i1, i2 := v1.Interface(), v2.Interface(); DeepEqual(i1, i2) {
3821+
t.Errorf("constructed structs %v and %v should not be equal", i1, i2)
3822+
}
3823+
3824+
st = StructOf([]StructField{{Name: "X", Tag: "x", Type: TypeOf([]int(nil))}})
3825+
v1 = New(st).Elem()
3826+
shouldPanic(func() { _ = v1.Interface() == v1.Interface() })
3827+
}
3828+
3829+
func TestStructOfGenericAlg(t *testing.T) {
3830+
st1 := StructOf([]StructField{
3831+
{Name: "X", Tag: "x", Type: TypeOf(int64(0))},
3832+
{Name: "Y", Type: TypeOf(string(""))},
3833+
})
3834+
st := StructOf([]StructField{
3835+
{Name: "S0", Type: st1},
3836+
{Name: "S1", Type: st1},
3837+
})
3838+
3839+
for _, table := range []struct {
3840+
rt Type
3841+
idx []int
3842+
}{
3843+
{
3844+
rt: st,
3845+
idx: []int{0, 1},
3846+
},
3847+
{
3848+
rt: st1,
3849+
idx: []int{1},
3850+
},
3851+
{
3852+
rt: StructOf(
3853+
[]StructField{
3854+
{Name: "XX", Type: TypeOf([0]int{})},
3855+
{Name: "YY", Type: TypeOf("")},
3856+
},
3857+
),
3858+
idx: []int{1},
3859+
},
3860+
{
3861+
rt: StructOf(
3862+
[]StructField{
3863+
{Name: "XX", Type: TypeOf([0]int{})},
3864+
{Name: "YY", Type: TypeOf("")},
3865+
{Name: "ZZ", Type: TypeOf([2]int{})},
3866+
},
3867+
),
3868+
idx: []int{1},
3869+
},
3870+
{
3871+
rt: StructOf(
3872+
[]StructField{
3873+
{Name: "XX", Type: TypeOf([1]int{})},
3874+
{Name: "YY", Type: TypeOf("")},
3875+
},
3876+
),
3877+
idx: []int{1},
3878+
},
3879+
{
3880+
rt: StructOf(
3881+
[]StructField{
3882+
{Name: "XX", Type: TypeOf([1]int{})},
3883+
{Name: "YY", Type: TypeOf("")},
3884+
{Name: "ZZ", Type: TypeOf([1]int{})},
3885+
},
3886+
),
3887+
idx: []int{1},
3888+
},
3889+
{
3890+
rt: StructOf(
3891+
[]StructField{
3892+
{Name: "XX", Type: TypeOf([2]int{})},
3893+
{Name: "YY", Type: TypeOf("")},
3894+
{Name: "ZZ", Type: TypeOf([2]int{})},
3895+
},
3896+
),
3897+
idx: []int{1},
3898+
},
3899+
{
3900+
rt: StructOf(
3901+
[]StructField{
3902+
{Name: "XX", Type: TypeOf(int64(0))},
3903+
{Name: "YY", Type: TypeOf(byte(0))},
3904+
{Name: "ZZ", Type: TypeOf("")},
3905+
},
3906+
),
3907+
idx: []int{2},
3908+
},
3909+
{
3910+
rt: StructOf(
3911+
[]StructField{
3912+
{Name: "XX", Type: TypeOf(int64(0))},
3913+
{Name: "YY", Type: TypeOf(int64(0))},
3914+
{Name: "ZZ", Type: TypeOf("")},
3915+
{Name: "AA", Type: TypeOf([1]int64{})},
3916+
},
3917+
),
3918+
idx: []int{2},
3919+
},
3920+
} {
3921+
v1 := New(table.rt).Elem()
3922+
v2 := New(table.rt).Elem()
3923+
3924+
if !DeepEqual(v1.Interface(), v1.Interface()) {
3925+
t.Errorf("constructed struct %v not equal to itself", v1.Interface())
3926+
}
3927+
3928+
v1.FieldByIndex(table.idx).Set(ValueOf("abc"))
3929+
v2.FieldByIndex(table.idx).Set(ValueOf("def"))
3930+
if i1, i2 := v1.Interface(), v2.Interface(); DeepEqual(i1, i2) {
3931+
t.Errorf("constructed structs %v and %v should not be equal", i1, i2)
3932+
}
3933+
3934+
abc := "abc"
3935+
v1.FieldByIndex(table.idx).Set(ValueOf(abc))
3936+
val := "+" + abc + "-"
3937+
v2.FieldByIndex(table.idx).Set(ValueOf(val[1:4]))
3938+
if i1, i2 := v1.Interface(), v2.Interface(); !DeepEqual(i1, i2) {
3939+
t.Errorf("constructed structs %v and %v should be equal", i1, i2)
3940+
}
3941+
3942+
// Test hash
3943+
m := MakeMap(MapOf(table.rt, TypeOf(int(0))))
3944+
m.SetMapIndex(v1, ValueOf(1))
3945+
if i1, i2 := v1.Interface(), v2.Interface(); !m.MapIndex(v2).IsValid() {
3946+
t.Errorf("constructed structs %#v and %#v have different hashes", i1, i2)
3947+
}
3948+
3949+
v2.FieldByIndex(table.idx).Set(ValueOf("a" + "b" + "c"))
3950+
if i1, i2 := v1.Interface(), v2.Interface(); !DeepEqual(i1, i2) {
3951+
t.Errorf("constructed structs %v and %v should be equal", i1, i2)
3952+
}
3953+
3954+
if i1, i2 := v1.Interface(), v2.Interface(); !m.MapIndex(v2).IsValid() {
3955+
t.Errorf("constructed structs %v and %v have different hashes", i1, i2)
3956+
}
3957+
}
3958+
}
3959+
36963960
func TestChanOf(t *testing.T) {
36973961
// check construction and use of type not in binary
36983962
type T string

0 commit comments

Comments
 (0)