mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Since CL 391014, cmd/compile now requires the -p flag to be set the build system. This CL changes it to initialize LocalPkg.Path to the provided path, rather than relying on writing out `"".` into object files and expecting cmd/link to substitute them. However, this actually involved a rather long tail of fixes. Many have already been submitted, but a few notable ones that have to land simultaneously with changing LocalPkg: 1. When compiling package runtime, there are really two "runtime" packages: types.LocalPkg (the source package itself) and ir.Pkgs.Runtime (the compiler's internal representation, for synthetic references). Previously, these ended up creating separate link symbols (`"".xxx` and `runtime.xxx`, respectively), but now they both end up as `runtime.xxx`, which causes lsym collisions (notably inittask and funcsyms). 2. test/codegen tests need to be updated to expect symbols to be named `command-line-arguments.xxx` rather than `"".foo`. 3. The issue20014 test case is sensitive to the sort order of field tracking symbols. In particular, the local package now sorts to its natural place in the list, rather than to the front. Thanks to David Chase for helping track down all of the fixes needed for this CL. Updates #51734. Change-Id: Iba3041cf7ad967d18c6e17922fa06ba11798b565 Reviewed-on: https://go-review.googlesource.com/c/go/+/393715 Reviewed-by: David Chase <drchase@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
1038 lines
24 KiB
Go
1038 lines
24 KiB
Go
// Copyright 2009 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 reflectlite_test
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
. "internal/reflectlite"
|
|
"math"
|
|
"reflect"
|
|
"runtime"
|
|
"testing"
|
|
"unsafe"
|
|
)
|
|
|
|
func ToValue(v Value) reflect.Value {
|
|
return reflect.ValueOf(ToInterface(v))
|
|
}
|
|
|
|
func TypeString(t Type) string {
|
|
return fmt.Sprintf("%T", ToInterface(Zero(t)))
|
|
}
|
|
|
|
type integer int
|
|
type T struct {
|
|
a int
|
|
b float64
|
|
c string
|
|
d *int
|
|
}
|
|
|
|
type pair struct {
|
|
i any
|
|
s string
|
|
}
|
|
|
|
func assert(t *testing.T, s, want string) {
|
|
t.Helper()
|
|
if s != want {
|
|
t.Errorf("have %#q want %#q", s, want)
|
|
}
|
|
}
|
|
|
|
var typeTests = []pair{
|
|
{struct{ x int }{}, "int"},
|
|
{struct{ x int8 }{}, "int8"},
|
|
{struct{ x int16 }{}, "int16"},
|
|
{struct{ x int32 }{}, "int32"},
|
|
{struct{ x int64 }{}, "int64"},
|
|
{struct{ x uint }{}, "uint"},
|
|
{struct{ x uint8 }{}, "uint8"},
|
|
{struct{ x uint16 }{}, "uint16"},
|
|
{struct{ x uint32 }{}, "uint32"},
|
|
{struct{ x uint64 }{}, "uint64"},
|
|
{struct{ x float32 }{}, "float32"},
|
|
{struct{ x float64 }{}, "float64"},
|
|
{struct{ x int8 }{}, "int8"},
|
|
{struct{ x (**int8) }{}, "**int8"},
|
|
{struct{ x (**integer) }{}, "**reflectlite_test.integer"},
|
|
{struct{ x ([32]int32) }{}, "[32]int32"},
|
|
{struct{ x ([]int8) }{}, "[]int8"},
|
|
{struct{ x (map[string]int32) }{}, "map[string]int32"},
|
|
{struct{ x (chan<- string) }{}, "chan<- string"},
|
|
{struct {
|
|
x struct {
|
|
c chan *int32
|
|
d float32
|
|
}
|
|
}{},
|
|
"struct { c chan *int32; d float32 }",
|
|
},
|
|
{struct{ x (func(a int8, b int32)) }{}, "func(int8, int32)"},
|
|
{struct {
|
|
x struct {
|
|
c func(chan *integer, *int8)
|
|
}
|
|
}{},
|
|
"struct { c func(chan *reflectlite_test.integer, *int8) }",
|
|
},
|
|
{struct {
|
|
x struct {
|
|
a int8
|
|
b int32
|
|
}
|
|
}{},
|
|
"struct { a int8; b int32 }",
|
|
},
|
|
{struct {
|
|
x struct {
|
|
a int8
|
|
b int8
|
|
c int32
|
|
}
|
|
}{},
|
|
"struct { a int8; b int8; c int32 }",
|
|
},
|
|
{struct {
|
|
x struct {
|
|
a int8
|
|
b int8
|
|
c int8
|
|
d int32
|
|
}
|
|
}{},
|
|
"struct { a int8; b int8; c int8; d int32 }",
|
|
},
|
|
{struct {
|
|
x struct {
|
|
a int8
|
|
b int8
|
|
c int8
|
|
d int8
|
|
e int32
|
|
}
|
|
}{},
|
|
"struct { a int8; b int8; c int8; d int8; e int32 }",
|
|
},
|
|
{struct {
|
|
x struct {
|
|
a int8
|
|
b int8
|
|
c int8
|
|
d int8
|
|
e int8
|
|
f int32
|
|
}
|
|
}{},
|
|
"struct { a int8; b int8; c int8; d int8; e int8; f int32 }",
|
|
},
|
|
{struct {
|
|
x struct {
|
|
a int8 `reflect:"hi there"`
|
|
}
|
|
}{},
|
|
`struct { a int8 "reflect:\"hi there\"" }`,
|
|
},
|
|
{struct {
|
|
x struct {
|
|
a int8 `reflect:"hi \x00there\t\n\"\\"`
|
|
}
|
|
}{},
|
|
`struct { a int8 "reflect:\"hi \\x00there\\t\\n\\\"\\\\\"" }`,
|
|
},
|
|
{struct {
|
|
x struct {
|
|
f func(args ...int)
|
|
}
|
|
}{},
|
|
"struct { f func(...int) }",
|
|
},
|
|
// {struct {
|
|
// x (interface {
|
|
// a(func(func(int) int) func(func(int)) int)
|
|
// b()
|
|
// })
|
|
// }{},
|
|
// "interface { reflectlite_test.a(func(func(int) int) func(func(int)) int); reflectlite_test.b() }",
|
|
// },
|
|
{struct {
|
|
x struct {
|
|
int32
|
|
int64
|
|
}
|
|
}{},
|
|
"struct { int32; int64 }",
|
|
},
|
|
}
|
|
|
|
var valueTests = []pair{
|
|
{new(int), "132"},
|
|
{new(int8), "8"},
|
|
{new(int16), "16"},
|
|
{new(int32), "32"},
|
|
{new(int64), "64"},
|
|
{new(uint), "132"},
|
|
{new(uint8), "8"},
|
|
{new(uint16), "16"},
|
|
{new(uint32), "32"},
|
|
{new(uint64), "64"},
|
|
{new(float32), "256.25"},
|
|
{new(float64), "512.125"},
|
|
{new(complex64), "532.125+10i"},
|
|
{new(complex128), "564.25+1i"},
|
|
{new(string), "stringy cheese"},
|
|
{new(bool), "true"},
|
|
{new(*int8), "*int8(0)"},
|
|
{new(**int8), "**int8(0)"},
|
|
{new([5]int32), "[5]int32{0, 0, 0, 0, 0}"},
|
|
{new(**integer), "**reflectlite_test.integer(0)"},
|
|
{new(map[string]int32), "map[string]int32{<can't iterate on maps>}"},
|
|
{new(chan<- string), "chan<- string"},
|
|
{new(func(a int8, b int32)), "func(int8, int32)(arg)"},
|
|
{new(struct {
|
|
c chan *int32
|
|
d float32
|
|
}),
|
|
"struct { c chan *int32; d float32 }{chan *int32, 0}",
|
|
},
|
|
{new(struct{ c func(chan *integer, *int8) }),
|
|
"struct { c func(chan *reflectlite_test.integer, *int8) }{func(chan *reflectlite_test.integer, *int8)(arg)}",
|
|
},
|
|
{new(struct {
|
|
a int8
|
|
b int32
|
|
}),
|
|
"struct { a int8; b int32 }{0, 0}",
|
|
},
|
|
{new(struct {
|
|
a int8
|
|
b int8
|
|
c int32
|
|
}),
|
|
"struct { a int8; b int8; c int32 }{0, 0, 0}",
|
|
},
|
|
}
|
|
|
|
func testType(t *testing.T, i int, typ Type, want string) {
|
|
s := TypeString(typ)
|
|
if s != want {
|
|
t.Errorf("#%d: have %#q, want %#q", i, s, want)
|
|
}
|
|
}
|
|
|
|
func testReflectType(t *testing.T, i int, typ Type, want string) {
|
|
s := TypeString(typ)
|
|
if s != want {
|
|
t.Errorf("#%d: have %#q, want %#q", i, s, want)
|
|
}
|
|
}
|
|
|
|
func TestTypes(t *testing.T) {
|
|
for i, tt := range typeTests {
|
|
testReflectType(t, i, Field(ValueOf(tt.i), 0).Type(), tt.s)
|
|
}
|
|
}
|
|
|
|
func TestSetValue(t *testing.T) {
|
|
for i, tt := range valueTests {
|
|
v := ValueOf(tt.i).Elem()
|
|
switch v.Kind() {
|
|
case Int:
|
|
v.Set(ValueOf(int(132)))
|
|
case Int8:
|
|
v.Set(ValueOf(int8(8)))
|
|
case Int16:
|
|
v.Set(ValueOf(int16(16)))
|
|
case Int32:
|
|
v.Set(ValueOf(int32(32)))
|
|
case Int64:
|
|
v.Set(ValueOf(int64(64)))
|
|
case Uint:
|
|
v.Set(ValueOf(uint(132)))
|
|
case Uint8:
|
|
v.Set(ValueOf(uint8(8)))
|
|
case Uint16:
|
|
v.Set(ValueOf(uint16(16)))
|
|
case Uint32:
|
|
v.Set(ValueOf(uint32(32)))
|
|
case Uint64:
|
|
v.Set(ValueOf(uint64(64)))
|
|
case Float32:
|
|
v.Set(ValueOf(float32(256.25)))
|
|
case Float64:
|
|
v.Set(ValueOf(512.125))
|
|
case Complex64:
|
|
v.Set(ValueOf(complex64(532.125 + 10i)))
|
|
case Complex128:
|
|
v.Set(ValueOf(complex128(564.25 + 1i)))
|
|
case String:
|
|
v.Set(ValueOf("stringy cheese"))
|
|
case Bool:
|
|
v.Set(ValueOf(true))
|
|
}
|
|
s := valueToString(v)
|
|
if s != tt.s {
|
|
t.Errorf("#%d: have %#q, want %#q", i, s, tt.s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCanSetField(t *testing.T) {
|
|
type embed struct{ x, X int }
|
|
type Embed struct{ x, X int }
|
|
type S1 struct {
|
|
embed
|
|
x, X int
|
|
}
|
|
type S2 struct {
|
|
*embed
|
|
x, X int
|
|
}
|
|
type S3 struct {
|
|
Embed
|
|
x, X int
|
|
}
|
|
type S4 struct {
|
|
*Embed
|
|
x, X int
|
|
}
|
|
|
|
type testCase struct {
|
|
index []int
|
|
canSet bool
|
|
}
|
|
tests := []struct {
|
|
val Value
|
|
cases []testCase
|
|
}{{
|
|
val: ValueOf(&S1{}),
|
|
cases: []testCase{
|
|
{[]int{0}, false},
|
|
{[]int{0, 0}, false},
|
|
{[]int{0, 1}, true},
|
|
{[]int{1}, false},
|
|
{[]int{2}, true},
|
|
},
|
|
}, {
|
|
val: ValueOf(&S2{embed: &embed{}}),
|
|
cases: []testCase{
|
|
{[]int{0}, false},
|
|
{[]int{0, 0}, false},
|
|
{[]int{0, 1}, true},
|
|
{[]int{1}, false},
|
|
{[]int{2}, true},
|
|
},
|
|
}, {
|
|
val: ValueOf(&S3{}),
|
|
cases: []testCase{
|
|
{[]int{0}, true},
|
|
{[]int{0, 0}, false},
|
|
{[]int{0, 1}, true},
|
|
{[]int{1}, false},
|
|
{[]int{2}, true},
|
|
},
|
|
}, {
|
|
val: ValueOf(&S4{Embed: &Embed{}}),
|
|
cases: []testCase{
|
|
{[]int{0}, true},
|
|
{[]int{0, 0}, false},
|
|
{[]int{0, 1}, true},
|
|
{[]int{1}, false},
|
|
{[]int{2}, true},
|
|
},
|
|
}}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.val.Type().Name(), func(t *testing.T) {
|
|
for _, tc := range tt.cases {
|
|
f := tt.val
|
|
for _, i := range tc.index {
|
|
if f.Kind() == Ptr {
|
|
f = f.Elem()
|
|
}
|
|
f = Field(f, i)
|
|
}
|
|
if got := f.CanSet(); got != tc.canSet {
|
|
t.Errorf("CanSet() = %v, want %v", got, tc.canSet)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
var _i = 7
|
|
|
|
var valueToStringTests = []pair{
|
|
{123, "123"},
|
|
{123.5, "123.5"},
|
|
{byte(123), "123"},
|
|
{"abc", "abc"},
|
|
{T{123, 456.75, "hello", &_i}, "reflectlite_test.T{123, 456.75, hello, *int(&7)}"},
|
|
{new(chan *T), "*chan *reflectlite_test.T(&chan *reflectlite_test.T)"},
|
|
{[10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "[10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"},
|
|
{&[10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "*[10]int(&[10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})"},
|
|
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"},
|
|
{&[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "*[]int(&[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})"},
|
|
}
|
|
|
|
func TestValueToString(t *testing.T) {
|
|
for i, test := range valueToStringTests {
|
|
s := valueToString(ValueOf(test.i))
|
|
if s != test.s {
|
|
t.Errorf("#%d: have %#q, want %#q", i, s, test.s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPtrSetNil(t *testing.T) {
|
|
var i int32 = 1234
|
|
ip := &i
|
|
vip := ValueOf(&ip)
|
|
vip.Elem().Set(Zero(vip.Elem().Type()))
|
|
if ip != nil {
|
|
t.Errorf("got non-nil (%d), want nil", *ip)
|
|
}
|
|
}
|
|
|
|
func TestMapSetNil(t *testing.T) {
|
|
m := make(map[string]int)
|
|
vm := ValueOf(&m)
|
|
vm.Elem().Set(Zero(vm.Elem().Type()))
|
|
if m != nil {
|
|
t.Errorf("got non-nil (%p), want nil", m)
|
|
}
|
|
}
|
|
|
|
func TestAll(t *testing.T) {
|
|
testType(t, 1, TypeOf((int8)(0)), "int8")
|
|
testType(t, 2, TypeOf((*int8)(nil)).Elem(), "int8")
|
|
|
|
typ := TypeOf((*struct {
|
|
c chan *int32
|
|
d float32
|
|
})(nil))
|
|
testType(t, 3, typ, "*struct { c chan *int32; d float32 }")
|
|
etyp := typ.Elem()
|
|
testType(t, 4, etyp, "struct { c chan *int32; d float32 }")
|
|
}
|
|
|
|
func TestInterfaceValue(t *testing.T) {
|
|
var inter struct {
|
|
E any
|
|
}
|
|
inter.E = 123.456
|
|
v1 := ValueOf(&inter)
|
|
v2 := Field(v1.Elem(), 0)
|
|
// assert(t, TypeString(v2.Type()), "interface {}")
|
|
v3 := v2.Elem()
|
|
assert(t, TypeString(v3.Type()), "float64")
|
|
|
|
i3 := ToInterface(v2)
|
|
if _, ok := i3.(float64); !ok {
|
|
t.Error("v2.Interface() did not return float64, got ", TypeOf(i3))
|
|
}
|
|
}
|
|
|
|
func TestFunctionValue(t *testing.T) {
|
|
var x any = func() {}
|
|
v := ValueOf(x)
|
|
if fmt.Sprint(ToInterface(v)) != fmt.Sprint(x) {
|
|
t.Fatalf("TestFunction returned wrong pointer")
|
|
}
|
|
assert(t, TypeString(v.Type()), "func()")
|
|
}
|
|
|
|
var appendTests = []struct {
|
|
orig, extra []int
|
|
}{
|
|
{make([]int, 2, 4), []int{22}},
|
|
{make([]int, 2, 4), []int{22, 33, 44}},
|
|
}
|
|
|
|
func sameInts(x, y []int) bool {
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
for i, xx := range x {
|
|
if xx != y[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func TestBigUnnamedStruct(t *testing.T) {
|
|
b := struct{ a, b, c, d int64 }{1, 2, 3, 4}
|
|
v := ValueOf(b)
|
|
b1 := ToInterface(v).(struct {
|
|
a, b, c, d int64
|
|
})
|
|
if b1.a != b.a || b1.b != b.b || b1.c != b.c || b1.d != b.d {
|
|
t.Errorf("ValueOf(%v).Interface().(*Big) = %v", b, b1)
|
|
}
|
|
}
|
|
|
|
type big struct {
|
|
a, b, c, d, e int64
|
|
}
|
|
|
|
func TestBigStruct(t *testing.T) {
|
|
b := big{1, 2, 3, 4, 5}
|
|
v := ValueOf(b)
|
|
b1 := ToInterface(v).(big)
|
|
if b1.a != b.a || b1.b != b.b || b1.c != b.c || b1.d != b.d || b1.e != b.e {
|
|
t.Errorf("ValueOf(%v).Interface().(big) = %v", b, b1)
|
|
}
|
|
}
|
|
|
|
type Basic struct {
|
|
x int
|
|
y float32
|
|
}
|
|
|
|
type NotBasic Basic
|
|
|
|
type DeepEqualTest struct {
|
|
a, b any
|
|
eq bool
|
|
}
|
|
|
|
// Simple functions for DeepEqual tests.
|
|
var (
|
|
fn1 func() // nil.
|
|
fn2 func() // nil.
|
|
fn3 = func() { fn1() } // Not nil.
|
|
)
|
|
|
|
type self struct{}
|
|
|
|
type Loop *Loop
|
|
type Loopy any
|
|
|
|
var loop1, loop2 Loop
|
|
var loopy1, loopy2 Loopy
|
|
|
|
func init() {
|
|
loop1 = &loop2
|
|
loop2 = &loop1
|
|
|
|
loopy1 = &loopy2
|
|
loopy2 = &loopy1
|
|
}
|
|
|
|
var typeOfTests = []DeepEqualTest{
|
|
// Equalities
|
|
{nil, nil, true},
|
|
{1, 1, true},
|
|
{int32(1), int32(1), true},
|
|
{0.5, 0.5, true},
|
|
{float32(0.5), float32(0.5), true},
|
|
{"hello", "hello", true},
|
|
{make([]int, 10), make([]int, 10), true},
|
|
{&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true},
|
|
{Basic{1, 0.5}, Basic{1, 0.5}, true},
|
|
{error(nil), error(nil), true},
|
|
{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true},
|
|
{fn1, fn2, true},
|
|
|
|
// Inequalities
|
|
{1, 2, false},
|
|
{int32(1), int32(2), false},
|
|
{0.5, 0.6, false},
|
|
{float32(0.5), float32(0.6), false},
|
|
{"hello", "hey", false},
|
|
{make([]int, 10), make([]int, 11), false},
|
|
{&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false},
|
|
{Basic{1, 0.5}, Basic{1, 0.6}, false},
|
|
{Basic{1, 0}, Basic{2, 0}, false},
|
|
{map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false},
|
|
{map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false},
|
|
{map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false},
|
|
{map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false},
|
|
{nil, 1, false},
|
|
{1, nil, false},
|
|
{fn1, fn3, false},
|
|
{fn3, fn3, false},
|
|
{[][]int{{1}}, [][]int{{2}}, false},
|
|
{math.NaN(), math.NaN(), false},
|
|
{&[1]float64{math.NaN()}, &[1]float64{math.NaN()}, false},
|
|
{&[1]float64{math.NaN()}, self{}, true},
|
|
{[]float64{math.NaN()}, []float64{math.NaN()}, false},
|
|
{[]float64{math.NaN()}, self{}, true},
|
|
{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
|
|
{map[float64]float64{math.NaN(): 1}, self{}, true},
|
|
|
|
// Nil vs empty: not the same.
|
|
{[]int{}, []int(nil), false},
|
|
{[]int{}, []int{}, true},
|
|
{[]int(nil), []int(nil), true},
|
|
{map[int]int{}, map[int]int(nil), false},
|
|
{map[int]int{}, map[int]int{}, true},
|
|
{map[int]int(nil), map[int]int(nil), true},
|
|
|
|
// Mismatched types
|
|
{1, 1.0, false},
|
|
{int32(1), int64(1), false},
|
|
{0.5, "hello", false},
|
|
{[]int{1, 2, 3}, [3]int{1, 2, 3}, false},
|
|
{&[3]any{1, 2, 4}, &[3]any{1, 2, "s"}, false},
|
|
{Basic{1, 0.5}, NotBasic{1, 0.5}, false},
|
|
{map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false},
|
|
|
|
// Possible loops.
|
|
{&loop1, &loop1, true},
|
|
{&loop1, &loop2, true},
|
|
{&loopy1, &loopy1, true},
|
|
{&loopy1, &loopy2, true},
|
|
}
|
|
|
|
func TestTypeOf(t *testing.T) {
|
|
// Special case for nil
|
|
if typ := TypeOf(nil); typ != nil {
|
|
t.Errorf("expected nil type for nil value; got %v", typ)
|
|
}
|
|
for _, test := range typeOfTests {
|
|
v := ValueOf(test.a)
|
|
if !v.IsValid() {
|
|
continue
|
|
}
|
|
typ := TypeOf(test.a)
|
|
if typ != v.Type() {
|
|
t.Errorf("TypeOf(%v) = %v, but ValueOf(%v).Type() = %v", test.a, typ, test.a, v.Type())
|
|
}
|
|
}
|
|
}
|
|
|
|
func Nil(a any, t *testing.T) {
|
|
n := Field(ValueOf(a), 0)
|
|
if !n.IsNil() {
|
|
t.Errorf("%v should be nil", a)
|
|
}
|
|
}
|
|
|
|
func NotNil(a any, t *testing.T) {
|
|
n := Field(ValueOf(a), 0)
|
|
if n.IsNil() {
|
|
t.Errorf("value of type %v should not be nil", TypeString(ValueOf(a).Type()))
|
|
}
|
|
}
|
|
|
|
func TestIsNil(t *testing.T) {
|
|
// These implement IsNil.
|
|
// Wrap in extra struct to hide interface type.
|
|
doNil := []any{
|
|
struct{ x *int }{},
|
|
struct{ x any }{},
|
|
struct{ x map[string]int }{},
|
|
struct{ x func() bool }{},
|
|
struct{ x chan int }{},
|
|
struct{ x []string }{},
|
|
struct{ x unsafe.Pointer }{},
|
|
}
|
|
for _, ts := range doNil {
|
|
ty := TField(TypeOf(ts), 0)
|
|
v := Zero(ty)
|
|
v.IsNil() // panics if not okay to call
|
|
}
|
|
|
|
// Check the implementations
|
|
var pi struct {
|
|
x *int
|
|
}
|
|
Nil(pi, t)
|
|
pi.x = new(int)
|
|
NotNil(pi, t)
|
|
|
|
var si struct {
|
|
x []int
|
|
}
|
|
Nil(si, t)
|
|
si.x = make([]int, 10)
|
|
NotNil(si, t)
|
|
|
|
var ci struct {
|
|
x chan int
|
|
}
|
|
Nil(ci, t)
|
|
ci.x = make(chan int)
|
|
NotNil(ci, t)
|
|
|
|
var mi struct {
|
|
x map[int]int
|
|
}
|
|
Nil(mi, t)
|
|
mi.x = make(map[int]int)
|
|
NotNil(mi, t)
|
|
|
|
var ii struct {
|
|
x any
|
|
}
|
|
Nil(ii, t)
|
|
ii.x = 2
|
|
NotNil(ii, t)
|
|
|
|
var fi struct {
|
|
x func(t *testing.T)
|
|
}
|
|
Nil(fi, t)
|
|
fi.x = TestIsNil
|
|
NotNil(fi, t)
|
|
}
|
|
|
|
// Indirect returns the value that v points to.
|
|
// If v is a nil pointer, Indirect returns a zero Value.
|
|
// If v is not a pointer, Indirect returns v.
|
|
func Indirect(v Value) Value {
|
|
if v.Kind() != Ptr {
|
|
return v
|
|
}
|
|
return v.Elem()
|
|
}
|
|
|
|
func TestNilPtrValueSub(t *testing.T) {
|
|
var pi *int
|
|
if pv := ValueOf(pi); pv.Elem().IsValid() {
|
|
t.Error("ValueOf((*int)(nil)).Elem().IsValid()")
|
|
}
|
|
}
|
|
|
|
type Point struct {
|
|
x, y int
|
|
}
|
|
|
|
// This will be index 0.
|
|
func (p Point) AnotherMethod(scale int) int {
|
|
return -1
|
|
}
|
|
|
|
// This will be index 1.
|
|
func (p Point) Dist(scale int) int {
|
|
//println("Point.Dist", p.x, p.y, scale)
|
|
return p.x*p.x*scale + p.y*p.y*scale
|
|
}
|
|
|
|
// This will be index 2.
|
|
func (p Point) GCMethod(k int) int {
|
|
runtime.GC()
|
|
return k + p.x
|
|
}
|
|
|
|
// This will be index 3.
|
|
func (p Point) NoArgs() {
|
|
// Exercise no-argument/no-result paths.
|
|
}
|
|
|
|
// This will be index 4.
|
|
func (p Point) TotalDist(points ...Point) int {
|
|
tot := 0
|
|
for _, q := range points {
|
|
dx := q.x - p.x
|
|
dy := q.y - p.y
|
|
tot += dx*dx + dy*dy // Should call Sqrt, but it's just a test.
|
|
|
|
}
|
|
return tot
|
|
}
|
|
|
|
type D1 struct {
|
|
d int
|
|
}
|
|
type D2 struct {
|
|
d int
|
|
}
|
|
|
|
func TestImportPath(t *testing.T) {
|
|
tests := []struct {
|
|
t Type
|
|
path string
|
|
}{
|
|
{TypeOf(&base64.Encoding{}).Elem(), "encoding/base64"},
|
|
{TypeOf(int(0)), ""},
|
|
{TypeOf(int8(0)), ""},
|
|
{TypeOf(int16(0)), ""},
|
|
{TypeOf(int32(0)), ""},
|
|
{TypeOf(int64(0)), ""},
|
|
{TypeOf(uint(0)), ""},
|
|
{TypeOf(uint8(0)), ""},
|
|
{TypeOf(uint16(0)), ""},
|
|
{TypeOf(uint32(0)), ""},
|
|
{TypeOf(uint64(0)), ""},
|
|
{TypeOf(uintptr(0)), ""},
|
|
{TypeOf(float32(0)), ""},
|
|
{TypeOf(float64(0)), ""},
|
|
{TypeOf(complex64(0)), ""},
|
|
{TypeOf(complex128(0)), ""},
|
|
{TypeOf(byte(0)), ""},
|
|
{TypeOf(rune(0)), ""},
|
|
{TypeOf([]byte(nil)), ""},
|
|
{TypeOf([]rune(nil)), ""},
|
|
{TypeOf(string("")), ""},
|
|
{TypeOf((*any)(nil)).Elem(), ""},
|
|
{TypeOf((*byte)(nil)), ""},
|
|
{TypeOf((*rune)(nil)), ""},
|
|
{TypeOf((*int64)(nil)), ""},
|
|
{TypeOf(map[string]int{}), ""},
|
|
{TypeOf((*error)(nil)).Elem(), ""},
|
|
{TypeOf((*Point)(nil)), ""},
|
|
{TypeOf((*Point)(nil)).Elem(), "internal/reflectlite_test"},
|
|
}
|
|
for _, test := range tests {
|
|
if path := test.t.PkgPath(); path != test.path {
|
|
t.Errorf("%v.PkgPath() = %q, want %q", test.t, path, test.path)
|
|
}
|
|
}
|
|
}
|
|
|
|
func noAlloc(t *testing.T, n int, f func(int)) {
|
|
if testing.Short() {
|
|
t.Skip("skipping malloc count in short mode")
|
|
}
|
|
if runtime.GOMAXPROCS(0) > 1 {
|
|
t.Skip("skipping; GOMAXPROCS>1")
|
|
}
|
|
i := -1
|
|
allocs := testing.AllocsPerRun(n, func() {
|
|
f(i)
|
|
i++
|
|
})
|
|
if allocs > 0 {
|
|
t.Errorf("%d iterations: got %v mallocs, want 0", n, allocs)
|
|
}
|
|
}
|
|
|
|
func TestAllocations(t *testing.T) {
|
|
noAlloc(t, 100, func(j int) {
|
|
var i any
|
|
var v Value
|
|
|
|
// We can uncomment this when compiler escape analysis
|
|
// is good enough to see that the integer assigned to i
|
|
// does not escape and therefore need not be allocated.
|
|
//
|
|
// i = 42 + j
|
|
// v = ValueOf(i)
|
|
// if int(v.Int()) != 42+j {
|
|
// panic("wrong int")
|
|
// }
|
|
|
|
i = func(j int) int { return j }
|
|
v = ValueOf(i)
|
|
if ToInterface(v).(func(int) int)(j) != j {
|
|
panic("wrong result")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestSetPanic(t *testing.T) {
|
|
ok := func(f func()) { f() }
|
|
bad := shouldPanic
|
|
clear := func(v Value) { v.Set(Zero(v.Type())) }
|
|
|
|
type t0 struct {
|
|
W int
|
|
}
|
|
|
|
type t1 struct {
|
|
Y int
|
|
t0
|
|
}
|
|
|
|
type T2 struct {
|
|
Z int
|
|
namedT0 t0
|
|
}
|
|
|
|
type T struct {
|
|
X int
|
|
t1
|
|
T2
|
|
NamedT1 t1
|
|
NamedT2 T2
|
|
namedT1 t1
|
|
namedT2 T2
|
|
}
|
|
|
|
// not addressable
|
|
v := ValueOf(T{})
|
|
bad(func() { clear(Field(v, 0)) }) // .X
|
|
bad(func() { clear(Field(v, 1)) }) // .t1
|
|
bad(func() { clear(Field(Field(v, 1), 0)) }) // .t1.Y
|
|
bad(func() { clear(Field(Field(v, 1), 1)) }) // .t1.t0
|
|
bad(func() { clear(Field(Field(Field(v, 1), 1), 0)) }) // .t1.t0.W
|
|
bad(func() { clear(Field(v, 2)) }) // .T2
|
|
bad(func() { clear(Field(Field(v, 2), 0)) }) // .T2.Z
|
|
bad(func() { clear(Field(Field(v, 2), 1)) }) // .T2.namedT0
|
|
bad(func() { clear(Field(Field(Field(v, 2), 1), 0)) }) // .T2.namedT0.W
|
|
bad(func() { clear(Field(v, 3)) }) // .NamedT1
|
|
bad(func() { clear(Field(Field(v, 3), 0)) }) // .NamedT1.Y
|
|
bad(func() { clear(Field(Field(v, 3), 1)) }) // .NamedT1.t0
|
|
bad(func() { clear(Field(Field(Field(v, 3), 1), 0)) }) // .NamedT1.t0.W
|
|
bad(func() { clear(Field(v, 4)) }) // .NamedT2
|
|
bad(func() { clear(Field(Field(v, 4), 0)) }) // .NamedT2.Z
|
|
bad(func() { clear(Field(Field(v, 4), 1)) }) // .NamedT2.namedT0
|
|
bad(func() { clear(Field(Field(Field(v, 4), 1), 0)) }) // .NamedT2.namedT0.W
|
|
bad(func() { clear(Field(v, 5)) }) // .namedT1
|
|
bad(func() { clear(Field(Field(v, 5), 0)) }) // .namedT1.Y
|
|
bad(func() { clear(Field(Field(v, 5), 1)) }) // .namedT1.t0
|
|
bad(func() { clear(Field(Field(Field(v, 5), 1), 0)) }) // .namedT1.t0.W
|
|
bad(func() { clear(Field(v, 6)) }) // .namedT2
|
|
bad(func() { clear(Field(Field(v, 6), 0)) }) // .namedT2.Z
|
|
bad(func() { clear(Field(Field(v, 6), 1)) }) // .namedT2.namedT0
|
|
bad(func() { clear(Field(Field(Field(v, 6), 1), 0)) }) // .namedT2.namedT0.W
|
|
|
|
// addressable
|
|
v = ValueOf(&T{}).Elem()
|
|
ok(func() { clear(Field(v, 0)) }) // .X
|
|
bad(func() { clear(Field(v, 1)) }) // .t1
|
|
ok(func() { clear(Field(Field(v, 1), 0)) }) // .t1.Y
|
|
bad(func() { clear(Field(Field(v, 1), 1)) }) // .t1.t0
|
|
ok(func() { clear(Field(Field(Field(v, 1), 1), 0)) }) // .t1.t0.W
|
|
ok(func() { clear(Field(v, 2)) }) // .T2
|
|
ok(func() { clear(Field(Field(v, 2), 0)) }) // .T2.Z
|
|
bad(func() { clear(Field(Field(v, 2), 1)) }) // .T2.namedT0
|
|
bad(func() { clear(Field(Field(Field(v, 2), 1), 0)) }) // .T2.namedT0.W
|
|
ok(func() { clear(Field(v, 3)) }) // .NamedT1
|
|
ok(func() { clear(Field(Field(v, 3), 0)) }) // .NamedT1.Y
|
|
bad(func() { clear(Field(Field(v, 3), 1)) }) // .NamedT1.t0
|
|
ok(func() { clear(Field(Field(Field(v, 3), 1), 0)) }) // .NamedT1.t0.W
|
|
ok(func() { clear(Field(v, 4)) }) // .NamedT2
|
|
ok(func() { clear(Field(Field(v, 4), 0)) }) // .NamedT2.Z
|
|
bad(func() { clear(Field(Field(v, 4), 1)) }) // .NamedT2.namedT0
|
|
bad(func() { clear(Field(Field(Field(v, 4), 1), 0)) }) // .NamedT2.namedT0.W
|
|
bad(func() { clear(Field(v, 5)) }) // .namedT1
|
|
bad(func() { clear(Field(Field(v, 5), 0)) }) // .namedT1.Y
|
|
bad(func() { clear(Field(Field(v, 5), 1)) }) // .namedT1.t0
|
|
bad(func() { clear(Field(Field(Field(v, 5), 1), 0)) }) // .namedT1.t0.W
|
|
bad(func() { clear(Field(v, 6)) }) // .namedT2
|
|
bad(func() { clear(Field(Field(v, 6), 0)) }) // .namedT2.Z
|
|
bad(func() { clear(Field(Field(v, 6), 1)) }) // .namedT2.namedT0
|
|
bad(func() { clear(Field(Field(Field(v, 6), 1), 0)) }) // .namedT2.namedT0.W
|
|
}
|
|
|
|
func shouldPanic(f func()) {
|
|
defer func() {
|
|
if recover() == nil {
|
|
panic("did not panic")
|
|
}
|
|
}()
|
|
f()
|
|
}
|
|
|
|
type S struct {
|
|
i1 int64
|
|
i2 int64
|
|
}
|
|
|
|
func TestBigZero(t *testing.T) {
|
|
const size = 1 << 10
|
|
var v [size]byte
|
|
z := ToInterface(Zero(ValueOf(v).Type())).([size]byte)
|
|
for i := 0; i < size; i++ {
|
|
if z[i] != 0 {
|
|
t.Fatalf("Zero object not all zero, index %d", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInvalid(t *testing.T) {
|
|
// Used to have inconsistency between IsValid() and Kind() != Invalid.
|
|
type T struct{ v any }
|
|
|
|
v := Field(ValueOf(T{}), 0)
|
|
if v.IsValid() != true || v.Kind() != Interface {
|
|
t.Errorf("field: IsValid=%v, Kind=%v, want true, Interface", v.IsValid(), v.Kind())
|
|
}
|
|
v = v.Elem()
|
|
if v.IsValid() != false || v.Kind() != Invalid {
|
|
t.Errorf("field elem: IsValid=%v, Kind=%v, want false, Invalid", v.IsValid(), v.Kind())
|
|
}
|
|
}
|
|
|
|
type TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678 int
|
|
|
|
type nameTest struct {
|
|
v any
|
|
want string
|
|
}
|
|
|
|
type A struct{}
|
|
type B[T any] struct{}
|
|
|
|
var nameTests = []nameTest{
|
|
{(*int32)(nil), "int32"},
|
|
{(*D1)(nil), "D1"},
|
|
{(*[]D1)(nil), ""},
|
|
{(*chan D1)(nil), ""},
|
|
{(*func() D1)(nil), ""},
|
|
{(*<-chan D1)(nil), ""},
|
|
{(*chan<- D1)(nil), ""},
|
|
{(*any)(nil), ""},
|
|
{(*interface {
|
|
F()
|
|
})(nil), ""},
|
|
{(*TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678)(nil), "TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678"},
|
|
{(*B[A])(nil), "B[internal/reflectlite_test.A]"},
|
|
{(*B[B[A]])(nil), "B[internal/reflectlite_test.B[internal/reflectlite_test.A]]"},
|
|
}
|
|
|
|
func TestNames(t *testing.T) {
|
|
for _, test := range nameTests {
|
|
typ := TypeOf(test.v).Elem()
|
|
if got := typ.Name(); got != test.want {
|
|
t.Errorf("%v Name()=%q, want %q", typ, got, test.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestUnaddressableField tests that the reflect package will not allow
|
|
// a type from another package to be used as a named type with an
|
|
// unexported field.
|
|
//
|
|
// This ensures that unexported fields cannot be modified by other packages.
|
|
func TestUnaddressableField(t *testing.T) {
|
|
var b Buffer // type defined in reflect, a different package
|
|
var localBuffer struct {
|
|
buf []byte
|
|
}
|
|
lv := ValueOf(&localBuffer).Elem()
|
|
rv := ValueOf(b)
|
|
shouldPanic(func() {
|
|
lv.Set(rv)
|
|
})
|
|
}
|
|
|
|
type Tint int
|
|
|
|
type Tint2 = Tint
|
|
|
|
type Talias1 struct {
|
|
byte
|
|
uint8
|
|
int
|
|
int32
|
|
rune
|
|
}
|
|
|
|
type Talias2 struct {
|
|
Tint
|
|
Tint2
|
|
}
|
|
|
|
func TestAliasNames(t *testing.T) {
|
|
t1 := Talias1{byte: 1, uint8: 2, int: 3, int32: 4, rune: 5}
|
|
out := fmt.Sprintf("%#v", t1)
|
|
want := "reflectlite_test.Talias1{byte:0x1, uint8:0x2, int:3, int32:4, rune:5}"
|
|
if out != want {
|
|
t.Errorf("Talias1 print:\nhave: %s\nwant: %s", out, want)
|
|
}
|
|
|
|
t2 := Talias2{Tint: 1, Tint2: 2}
|
|
out = fmt.Sprintf("%#v", t2)
|
|
want = "reflectlite_test.Talias2{Tint:1, Tint2:2}"
|
|
if out != want {
|
|
t.Errorf("Talias2 print:\nhave: %s\nwant: %s", out, want)
|
|
}
|
|
}
|