Initial commit: Go 1.23 release state

This commit is contained in:
Vorapol Rinsatitnon
2024-09-21 23:49:08 +10:00
commit 17cd57a668
13231 changed files with 3114330 additions and 0 deletions

510
src/reflect/abi.go Normal file
View File

@@ -0,0 +1,510 @@
// Copyright 2021 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 reflect
import (
"internal/abi"
"internal/goarch"
"unsafe"
)
// These variables are used by the register assignment
// algorithm in this file.
//
// They should be modified with care (no other reflect code
// may be executing) and are generally only modified
// when testing this package.
//
// They should never be set higher than their internal/abi
// constant counterparts, because the system relies on a
// structure that is at least large enough to hold the
// registers the system supports.
//
// Currently they're set to zero because using the actual
// constants will break every part of the toolchain that
// uses reflect to call functions (e.g. go test, or anything
// that uses text/template). The values that are currently
// commented out there should be the actual values once
// we're ready to use the register ABI everywhere.
var (
intArgRegs = abi.IntArgRegs
floatArgRegs = abi.FloatArgRegs
floatRegSize = uintptr(abi.EffectiveFloatRegSize)
)
// abiStep represents an ABI "instruction." Each instruction
// describes one part of how to translate between a Go value
// in memory and a call frame.
type abiStep struct {
kind abiStepKind
// offset and size together describe a part of a Go value
// in memory.
offset uintptr
size uintptr // size in bytes of the part
// These fields describe the ABI side of the translation.
stkOff uintptr // stack offset, used if kind == abiStepStack
ireg int // integer register index, used if kind == abiStepIntReg or kind == abiStepPointer
freg int // FP register index, used if kind == abiStepFloatReg
}
// abiStepKind is the "op-code" for an abiStep instruction.
type abiStepKind int
const (
abiStepBad abiStepKind = iota
abiStepStack // copy to/from stack
abiStepIntReg // copy to/from integer register
abiStepPointer // copy pointer to/from integer register
abiStepFloatReg // copy to/from FP register
)
// abiSeq represents a sequence of ABI instructions for copying
// from a series of reflect.Values to a call frame (for call arguments)
// or vice-versa (for call results).
//
// An abiSeq should be populated by calling its addArg method.
type abiSeq struct {
// steps is the set of instructions.
//
// The instructions are grouped together by whole arguments,
// with the starting index for the instructions
// of the i'th Go value available in valueStart.
//
// For instance, if this abiSeq represents 3 arguments
// passed to a function, then the 2nd argument's steps
// begin at steps[valueStart[1]].
//
// Because reflect accepts Go arguments in distinct
// Values and each Value is stored separately, each abiStep
// that begins a new argument will have its offset
// field == 0.
steps []abiStep
valueStart []int
stackBytes uintptr // stack space used
iregs, fregs int // registers used
}
func (a *abiSeq) dump() {
for i, p := range a.steps {
println("part", i, p.kind, p.offset, p.size, p.stkOff, p.ireg, p.freg)
}
print("values ")
for _, i := range a.valueStart {
print(i, " ")
}
println()
println("stack", a.stackBytes)
println("iregs", a.iregs)
println("fregs", a.fregs)
}
// stepsForValue returns the ABI instructions for translating
// the i'th Go argument or return value represented by this
// abiSeq to the Go ABI.
func (a *abiSeq) stepsForValue(i int) []abiStep {
s := a.valueStart[i]
var e int
if i == len(a.valueStart)-1 {
e = len(a.steps)
} else {
e = a.valueStart[i+1]
}
return a.steps[s:e]
}
// addArg extends the abiSeq with a new Go value of type t.
//
// If the value was stack-assigned, returns the single
// abiStep describing that translation, and nil otherwise.
func (a *abiSeq) addArg(t *abi.Type) *abiStep {
// We'll always be adding a new value, so do that first.
pStart := len(a.steps)
a.valueStart = append(a.valueStart, pStart)
if t.Size() == 0 {
// If the size of the argument type is zero, then
// in order to degrade gracefully into ABI0, we need
// to stack-assign this type. The reason is that
// although zero-sized types take up no space on the
// stack, they do cause the next argument to be aligned.
// So just do that here, but don't bother actually
// generating a new ABI step for it (there's nothing to
// actually copy).
//
// We cannot handle this in the recursive case of
// regAssign because zero-sized *fields* of a
// non-zero-sized struct do not cause it to be
// stack-assigned. So we need a special case here
// at the top.
a.stackBytes = align(a.stackBytes, uintptr(t.Align()))
return nil
}
// Hold a copy of "a" so that we can roll back if
// register assignment fails.
aOld := *a
if !a.regAssign(t, 0) {
// Register assignment failed. Roll back any changes
// and stack-assign.
*a = aOld
a.stackAssign(t.Size(), uintptr(t.Align()))
return &a.steps[len(a.steps)-1]
}
return nil
}
// addRcvr extends the abiSeq with a new method call
// receiver according to the interface calling convention.
//
// If the receiver was stack-assigned, returns the single
// abiStep describing that translation, and nil otherwise.
// Returns true if the receiver is a pointer.
func (a *abiSeq) addRcvr(rcvr *abi.Type) (*abiStep, bool) {
// The receiver is always one word.
a.valueStart = append(a.valueStart, len(a.steps))
var ok, ptr bool
if rcvr.IfaceIndir() || rcvr.Pointers() {
ok = a.assignIntN(0, goarch.PtrSize, 1, 0b1)
ptr = true
} else {
// TODO(mknyszek): Is this case even possible?
// The interface data work never contains a non-pointer
// value. This case was copied over from older code
// in the reflect package which only conditionally added
// a pointer bit to the reflect.(Value).Call stack frame's
// GC bitmap.
ok = a.assignIntN(0, goarch.PtrSize, 1, 0b0)
ptr = false
}
if !ok {
a.stackAssign(goarch.PtrSize, goarch.PtrSize)
return &a.steps[len(a.steps)-1], ptr
}
return nil, ptr
}
// regAssign attempts to reserve argument registers for a value of
// type t, stored at some offset.
//
// It returns whether or not the assignment succeeded, but
// leaves any changes it made to a.steps behind, so the caller
// must undo that work by adjusting a.steps if it fails.
//
// This method along with the assign* methods represent the
// complete register-assignment algorithm for the Go ABI.
func (a *abiSeq) regAssign(t *abi.Type, offset uintptr) bool {
switch Kind(t.Kind()) {
case UnsafePointer, Pointer, Chan, Map, Func:
return a.assignIntN(offset, t.Size(), 1, 0b1)
case Bool, Int, Uint, Int8, Uint8, Int16, Uint16, Int32, Uint32, Uintptr:
return a.assignIntN(offset, t.Size(), 1, 0b0)
case Int64, Uint64:
switch goarch.PtrSize {
case 4:
return a.assignIntN(offset, 4, 2, 0b0)
case 8:
return a.assignIntN(offset, 8, 1, 0b0)
}
case Float32, Float64:
return a.assignFloatN(offset, t.Size(), 1)
case Complex64:
return a.assignFloatN(offset, 4, 2)
case Complex128:
return a.assignFloatN(offset, 8, 2)
case String:
return a.assignIntN(offset, goarch.PtrSize, 2, 0b01)
case Interface:
return a.assignIntN(offset, goarch.PtrSize, 2, 0b10)
case Slice:
return a.assignIntN(offset, goarch.PtrSize, 3, 0b001)
case Array:
tt := (*arrayType)(unsafe.Pointer(t))
switch tt.Len {
case 0:
// There's nothing to assign, so don't modify
// a.steps but succeed so the caller doesn't
// try to stack-assign this value.
return true
case 1:
return a.regAssign(tt.Elem, offset)
default:
return false
}
case Struct:
st := (*structType)(unsafe.Pointer(t))
for i := range st.Fields {
f := &st.Fields[i]
if !a.regAssign(f.Typ, offset+f.Offset) {
return false
}
}
return true
default:
print("t.Kind == ", t.Kind(), "\n")
panic("unknown type kind")
}
panic("unhandled register assignment path")
}
// assignIntN assigns n values to registers, each "size" bytes large,
// from the data at [offset, offset+n*size) in memory. Each value at
// [offset+i*size, offset+(i+1)*size) for i < n is assigned to the
// next n integer registers.
//
// Bit i in ptrMap indicates whether the i'th value is a pointer.
// n must be <= 8.
//
// Returns whether assignment succeeded.
func (a *abiSeq) assignIntN(offset, size uintptr, n int, ptrMap uint8) bool {
if n > 8 || n < 0 {
panic("invalid n")
}
if ptrMap != 0 && size != goarch.PtrSize {
panic("non-empty pointer map passed for non-pointer-size values")
}
if a.iregs+n > intArgRegs {
return false
}
for i := 0; i < n; i++ {
kind := abiStepIntReg
if ptrMap&(uint8(1)<<i) != 0 {
kind = abiStepPointer
}
a.steps = append(a.steps, abiStep{
kind: kind,
offset: offset + uintptr(i)*size,
size: size,
ireg: a.iregs,
})
a.iregs++
}
return true
}
// assignFloatN assigns n values to registers, each "size" bytes large,
// from the data at [offset, offset+n*size) in memory. Each value at
// [offset+i*size, offset+(i+1)*size) for i < n is assigned to the
// next n floating-point registers.
//
// Returns whether assignment succeeded.
func (a *abiSeq) assignFloatN(offset, size uintptr, n int) bool {
if n < 0 {
panic("invalid n")
}
if a.fregs+n > floatArgRegs || floatRegSize < size {
return false
}
for i := 0; i < n; i++ {
a.steps = append(a.steps, abiStep{
kind: abiStepFloatReg,
offset: offset + uintptr(i)*size,
size: size,
freg: a.fregs,
})
a.fregs++
}
return true
}
// stackAssign reserves space for one value that is "size" bytes
// large with alignment "alignment" to the stack.
//
// Should not be called directly; use addArg instead.
func (a *abiSeq) stackAssign(size, alignment uintptr) {
a.stackBytes = align(a.stackBytes, alignment)
a.steps = append(a.steps, abiStep{
kind: abiStepStack,
offset: 0, // Only used for whole arguments, so the memory offset is 0.
size: size,
stkOff: a.stackBytes,
})
a.stackBytes += size
}
// abiDesc describes the ABI for a function or method.
type abiDesc struct {
// call and ret represent the translation steps for
// the call and return paths of a Go function.
call, ret abiSeq
// These fields describe the stack space allocated
// for the call. stackCallArgsSize is the amount of space
// reserved for arguments but not return values. retOffset
// is the offset at which return values begin, and
// spill is the size in bytes of additional space reserved
// to spill argument registers into in case of preemption in
// reflectcall's stack frame.
stackCallArgsSize, retOffset, spill uintptr
// stackPtrs is a bitmap that indicates whether
// each word in the ABI stack space (stack-assigned
// args + return values) is a pointer. Used
// as the heap pointer bitmap for stack space
// passed to reflectcall.
stackPtrs *bitVector
// inRegPtrs is a bitmap whose i'th bit indicates
// whether the i'th integer argument register contains
// a pointer. Used by makeFuncStub and methodValueCall
// to make result pointers visible to the GC.
//
// outRegPtrs is the same, but for result values.
// Used by reflectcall to make result pointers visible
// to the GC.
inRegPtrs, outRegPtrs abi.IntArgRegBitmap
}
func (a *abiDesc) dump() {
println("ABI")
println("call")
a.call.dump()
println("ret")
a.ret.dump()
println("stackCallArgsSize", a.stackCallArgsSize)
println("retOffset", a.retOffset)
println("spill", a.spill)
print("inRegPtrs:")
dumpPtrBitMap(a.inRegPtrs)
println()
print("outRegPtrs:")
dumpPtrBitMap(a.outRegPtrs)
println()
}
func dumpPtrBitMap(b abi.IntArgRegBitmap) {
for i := 0; i < intArgRegs; i++ {
x := 0
if b.Get(i) {
x = 1
}
print(" ", x)
}
}
func newAbiDesc(t *funcType, rcvr *abi.Type) abiDesc {
// We need to add space for this argument to
// the frame so that it can spill args into it.
//
// The size of this space is just the sum of the sizes
// of each register-allocated type.
//
// TODO(mknyszek): Remove this when we no longer have
// caller reserved spill space.
spill := uintptr(0)
// Compute gc program & stack bitmap for stack arguments
stackPtrs := new(bitVector)
// Compute the stack frame pointer bitmap and register
// pointer bitmap for arguments.
inRegPtrs := abi.IntArgRegBitmap{}
// Compute abiSeq for input parameters.
var in abiSeq
if rcvr != nil {
stkStep, isPtr := in.addRcvr(rcvr)
if stkStep != nil {
if isPtr {
stackPtrs.append(1)
} else {
stackPtrs.append(0)
}
} else {
spill += goarch.PtrSize
}
}
for i, arg := range t.InSlice() {
stkStep := in.addArg(arg)
if stkStep != nil {
addTypeBits(stackPtrs, stkStep.stkOff, arg)
} else {
spill = align(spill, uintptr(arg.Align()))
spill += arg.Size()
for _, st := range in.stepsForValue(i) {
if st.kind == abiStepPointer {
inRegPtrs.Set(st.ireg)
}
}
}
}
spill = align(spill, goarch.PtrSize)
// From the input parameters alone, we now know
// the stackCallArgsSize and retOffset.
stackCallArgsSize := in.stackBytes
retOffset := align(in.stackBytes, goarch.PtrSize)
// Compute the stack frame pointer bitmap and register
// pointer bitmap for return values.
outRegPtrs := abi.IntArgRegBitmap{}
// Compute abiSeq for output parameters.
var out abiSeq
// Stack-assigned return values do not share
// space with arguments like they do with registers,
// so we need to inject a stack offset here.
// Fake it by artificially extending stackBytes by
// the return offset.
out.stackBytes = retOffset
for i, res := range t.OutSlice() {
stkStep := out.addArg(res)
if stkStep != nil {
addTypeBits(stackPtrs, stkStep.stkOff, res)
} else {
for _, st := range out.stepsForValue(i) {
if st.kind == abiStepPointer {
outRegPtrs.Set(st.ireg)
}
}
}
}
// Undo the faking from earlier so that stackBytes
// is accurate.
out.stackBytes -= retOffset
return abiDesc{in, out, stackCallArgsSize, retOffset, spill, stackPtrs, inRegPtrs, outRegPtrs}
}
// intFromReg loads an argSize sized integer from reg and places it at to.
//
// argSize must be non-zero, fit in a register, and a power-of-two.
func intFromReg(r *abi.RegArgs, reg int, argSize uintptr, to unsafe.Pointer) {
memmove(to, r.IntRegArgAddr(reg, argSize), argSize)
}
// intToReg loads an argSize sized integer and stores it into reg.
//
// argSize must be non-zero, fit in a register, and a power-of-two.
func intToReg(r *abi.RegArgs, reg int, argSize uintptr, from unsafe.Pointer) {
memmove(r.IntRegArgAddr(reg, argSize), from, argSize)
}
// floatFromReg loads a float value from its register representation in r.
//
// argSize must be 4 or 8.
func floatFromReg(r *abi.RegArgs, reg int, argSize uintptr, to unsafe.Pointer) {
switch argSize {
case 4:
*(*float32)(to) = archFloat32FromReg(r.Floats[reg])
case 8:
*(*float64)(to) = *(*float64)(unsafe.Pointer(&r.Floats[reg]))
default:
panic("bad argSize")
}
}
// floatToReg stores a float value in its register representation in r.
//
// argSize must be either 4 or 8.
func floatToReg(r *abi.RegArgs, reg int, argSize uintptr, from unsafe.Pointer) {
switch argSize {
case 4:
r.Floats[reg] = archFloat32ToReg(*(*float32)(from))
case 8:
r.Floats[reg] = *(*uint64)(from)
default:
panic("bad argSize")
}
}

989
src/reflect/abi_test.go Normal file
View File

@@ -0,0 +1,989 @@
// Copyright 2021 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.
//go:build goexperiment.regabiargs
package reflect_test
import (
"internal/abi"
"math"
"math/rand"
"reflect"
"runtime"
"testing"
"testing/quick"
)
// As of early May 2021 this is no longer necessary for amd64,
// but it remains in case this is needed for the next register abi port.
// TODO (1.18) If enabling register ABI on additional architectures turns out not to need this, remove it.
type MagicLastTypeNameForTestingRegisterABI struct{}
func TestMethodValueCallABI(t *testing.T) {
// Enable register-based reflect.Call and ensure we don't
// use potentially incorrect cached versions by clearing
// the cache before we start and after we're done.
defer reflect.SetArgRegs(reflect.SetArgRegs(abi.IntArgRegs, abi.FloatArgRegs, abi.EffectiveFloatRegSize))
// This test is simple. Calling a method value involves
// pretty much just plumbing whatever arguments in whichever
// location through to reflectcall. They're already set up
// for us, so there isn't a whole lot to do. Let's just
// make sure that we can pass register and stack arguments
// through. The exact combination is not super important.
makeMethodValue := func(method string) (*StructWithMethods, any) {
s := new(StructWithMethods)
v := reflect.ValueOf(s).MethodByName(method)
return s, v.Interface()
}
a0 := StructFewRegs{
10, 11, 12, 13,
20.0, 21.0, 22.0, 23.0,
}
a1 := [4]uint64{100, 101, 102, 103}
a2 := StructFillRegs{
1, 2, 3, 4, 5, 6, 7, 8, 9,
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0,
}
s, i := makeMethodValue("AllRegsCall")
f0 := i.(func(StructFewRegs, MagicLastTypeNameForTestingRegisterABI) StructFewRegs)
r0 := f0(a0, MagicLastTypeNameForTestingRegisterABI{})
if r0 != a0 {
t.Errorf("bad method value call: got %#v, want %#v", r0, a0)
}
if s.Value != 1 {
t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 1)
}
s, i = makeMethodValue("RegsAndStackCall")
f1 := i.(func(StructFewRegs, [4]uint64, MagicLastTypeNameForTestingRegisterABI) (StructFewRegs, [4]uint64))
r0, r1 := f1(a0, a1, MagicLastTypeNameForTestingRegisterABI{})
if r0 != a0 {
t.Errorf("bad method value call: got %#v, want %#v", r0, a0)
}
if r1 != a1 {
t.Errorf("bad method value call: got %#v, want %#v", r1, a1)
}
if s.Value != 2 {
t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 2)
}
s, i = makeMethodValue("SpillStructCall")
f2 := i.(func(StructFillRegs, MagicLastTypeNameForTestingRegisterABI) StructFillRegs)
r2 := f2(a2, MagicLastTypeNameForTestingRegisterABI{})
if r2 != a2 {
t.Errorf("bad method value call: got %#v, want %#v", r2, a2)
}
if s.Value != 3 {
t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 3)
}
s, i = makeMethodValue("ValueRegMethodSpillInt")
f3 := i.(func(StructFillRegs, int, MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, int))
r3a, r3b := f3(a2, 42, MagicLastTypeNameForTestingRegisterABI{})
if r3a != a2 {
t.Errorf("bad method value call: got %#v, want %#v", r3a, a2)
}
if r3b != 42 {
t.Errorf("bad method value call: got %#v, want %#v", r3b, 42)
}
if s.Value != 4 {
t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 4)
}
s, i = makeMethodValue("ValueRegMethodSpillPtr")
f4 := i.(func(StructFillRegs, *byte, MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, *byte))
vb := byte(10)
r4a, r4b := f4(a2, &vb, MagicLastTypeNameForTestingRegisterABI{})
if r4a != a2 {
t.Errorf("bad method value call: got %#v, want %#v", r4a, a2)
}
if r4b != &vb {
t.Errorf("bad method value call: got %#v, want %#v", r4b, &vb)
}
if s.Value != 5 {
t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 5)
}
}
type StructWithMethods struct {
Value int
}
type StructFewRegs struct {
a0, a1, a2, a3 int
f0, f1, f2, f3 float64
}
type StructFillRegs struct {
a0, a1, a2, a3, a4, a5, a6, a7, a8 int
f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14 float64
}
func (m *StructWithMethods) AllRegsCall(s StructFewRegs, _ MagicLastTypeNameForTestingRegisterABI) StructFewRegs {
m.Value = 1
return s
}
func (m *StructWithMethods) RegsAndStackCall(s StructFewRegs, a [4]uint64, _ MagicLastTypeNameForTestingRegisterABI) (StructFewRegs, [4]uint64) {
m.Value = 2
return s, a
}
func (m *StructWithMethods) SpillStructCall(s StructFillRegs, _ MagicLastTypeNameForTestingRegisterABI) StructFillRegs {
m.Value = 3
return s
}
// When called as a method value, i is passed on the stack.
// When called as a method, i is passed in a register.
func (m *StructWithMethods) ValueRegMethodSpillInt(s StructFillRegs, i int, _ MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, int) {
m.Value = 4
return s, i
}
// When called as a method value, i is passed on the stack.
// When called as a method, i is passed in a register.
func (m *StructWithMethods) ValueRegMethodSpillPtr(s StructFillRegs, i *byte, _ MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, *byte) {
m.Value = 5
return s, i
}
func TestReflectCallABI(t *testing.T) {
// Enable register-based reflect.Call and ensure we don't
// use potentially incorrect cached versions by clearing
// the cache before we start and after we're done.
defer reflect.SetArgRegs(reflect.SetArgRegs(abi.IntArgRegs, abi.FloatArgRegs, abi.EffectiveFloatRegSize))
// Execute the functions defined below which all have the
// same form and perform the same function: pass all arguments
// to return values. The purpose is to test the call boundary
// and make sure it works.
r := rand.New(rand.NewSource(genValueRandSeed))
for _, fn := range abiCallTestCases {
fn := reflect.ValueOf(fn)
t.Run(runtime.FuncForPC(fn.Pointer()).Name(), func(t *testing.T) {
typ := fn.Type()
if typ.Kind() != reflect.Func {
t.Fatalf("test case is not a function, has type: %s", typ.String())
}
if typ.NumIn() != typ.NumOut() {
t.Fatalf("test case has different number of inputs and outputs: %d in, %d out", typ.NumIn(), typ.NumOut())
}
var args []reflect.Value
for i := 0; i < typ.NumIn(); i++ {
args = append(args, genValue(t, typ.In(i), r))
}
results := fn.Call(args)
for i := range results {
x, y := args[i].Interface(), results[i].Interface()
if reflect.DeepEqual(x, y) {
continue
}
t.Errorf("arg and result %d differ: got %+v, want %+v", i, y, x)
}
})
}
}
func TestReflectMakeFuncCallABI(t *testing.T) {
// Enable register-based reflect.MakeFunc and ensure we don't
// use potentially incorrect cached versions by clearing
// the cache before we start and after we're done.
defer reflect.SetArgRegs(reflect.SetArgRegs(abi.IntArgRegs, abi.FloatArgRegs, abi.EffectiveFloatRegSize))
// Execute the functions defined below which all have the
// same form and perform the same function: pass all arguments
// to return values. The purpose is to test the call boundary
// and make sure it works.
r := rand.New(rand.NewSource(genValueRandSeed))
makeFuncHandler := func(args []reflect.Value) []reflect.Value {
if len(args) == 0 {
return []reflect.Value{}
}
return args[:len(args)-1] // The last Value is an empty magic value.
}
for _, callFn := range abiMakeFuncTestCases {
fnTyp := reflect.TypeOf(callFn).In(0)
fn := reflect.MakeFunc(fnTyp, makeFuncHandler)
callFn := reflect.ValueOf(callFn)
t.Run(runtime.FuncForPC(callFn.Pointer()).Name(), func(t *testing.T) {
args := []reflect.Value{fn}
for i := 0; i < fnTyp.NumIn()-1; /* last one is magic type */ i++ {
args = append(args, genValue(t, fnTyp.In(i), r))
}
results := callFn.Call(args)
for i := range results {
x, y := args[i+1].Interface(), results[i].Interface()
if reflect.DeepEqual(x, y) {
continue
}
t.Errorf("arg and result %d differ: got %+v, want %+v", i, y, x)
}
})
}
t.Run("OnlyPointerInRegisterGC", func(t *testing.T) {
// This test attempts to induce a failure wherein
// the last pointer to an object is passed via registers.
// If makeFuncStub doesn't successfully store the pointer
// to a location visible to the GC, the object should be
// freed and then the next GC should notice that an object
// was inexplicably revived.
var f func(b *uint64, _ MagicLastTypeNameForTestingRegisterABI) *uint64
mkfn := reflect.MakeFunc(reflect.TypeOf(f), func(args []reflect.Value) []reflect.Value {
*(args[0].Interface().(*uint64)) = 5
return args[:1]
})
fn := mkfn.Interface().(func(*uint64, MagicLastTypeNameForTestingRegisterABI) *uint64)
// Call the MakeFunc'd function while trying pass the only pointer
// to a new heap-allocated uint64.
*reflect.CallGC = true
x := fn(new(uint64), MagicLastTypeNameForTestingRegisterABI{})
*reflect.CallGC = false
// Check for bad pointers (which should be x if things went wrong).
runtime.GC()
// Sanity check x.
if *x != 5 {
t.Fatalf("failed to set value in object")
}
})
}
var abiCallTestCases = []any{
passNone,
passInt,
passInt8,
passInt16,
passInt32,
passInt64,
passUint,
passUint8,
passUint16,
passUint32,
passUint64,
passFloat32,
passFloat64,
passComplex64,
passComplex128,
passManyInt,
passManyFloat64,
passArray1,
passArray,
passArray1Mix,
passString,
// TODO(mknyszek): Test passing interface values.
passSlice,
passPointer,
passStruct1,
passStruct2,
passStruct3,
passStruct4,
passStruct5,
passStruct6,
passStruct7,
passStruct8,
passStruct9,
passStruct10,
// TODO(mknyszek): Test passing unsafe.Pointer values.
// TODO(mknyszek): Test passing chan values.
passStruct11,
passStruct12,
passStruct13,
passStruct14,
passStruct15,
pass2Struct1,
passEmptyStruct,
passStruct10AndSmall,
}
// Functions for testing reflect function call functionality.
//go:registerparams
//go:noinline
func passNone() {}
//go:registerparams
//go:noinline
func passInt(a int) int {
return a
}
//go:registerparams
//go:noinline
func passInt8(a int8) int8 {
return a
}
//go:registerparams
//go:noinline
func passInt16(a int16) int16 {
return a
}
//go:registerparams
//go:noinline
func passInt32(a int32) int32 {
return a
}
//go:registerparams
//go:noinline
func passInt64(a int64) int64 {
return a
}
//go:registerparams
//go:noinline
func passUint(a uint) uint {
return a
}
//go:registerparams
//go:noinline
func passUint8(a uint8) uint8 {
return a
}
//go:registerparams
//go:noinline
func passUint16(a uint16) uint16 {
return a
}
//go:registerparams
//go:noinline
func passUint32(a uint32) uint32 {
return a
}
//go:registerparams
//go:noinline
func passUint64(a uint64) uint64 {
return a
}
//go:registerparams
//go:noinline
func passFloat32(a float32) float32 {
return a
}
//go:registerparams
//go:noinline
func passFloat64(a float64) float64 {
return a
}
//go:registerparams
//go:noinline
func passComplex64(a complex64) complex64 {
return a
}
//go:registerparams
//go:noinline
func passComplex128(a complex128) complex128 {
return a
}
//go:registerparams
//go:noinline
func passArray1(a [1]uint32) [1]uint32 {
return a
}
//go:registerparams
//go:noinline
func passArray(a [2]uintptr) [2]uintptr {
return a
}
//go:registerparams
//go:noinline
func passArray1Mix(a int, b [1]uint32, c float64) (int, [1]uint32, float64) {
return a, b, c
}
//go:registerparams
//go:noinline
func passString(a string) string {
return a
}
//go:registerparams
//go:noinline
func passSlice(a []byte) []byte {
return a
}
//go:registerparams
//go:noinline
func passPointer(a *byte) *byte {
return a
}
//go:registerparams
//go:noinline
func passManyInt(a, b, c, d, e, f, g, h, i, j int) (int, int, int, int, int, int, int, int, int, int) {
return a, b, c, d, e, f, g, h, i, j
}
//go:registerparams
//go:noinline
func passManyFloat64(a, b, c, d, e, f, g, h, i, j, l, m, n, o, p, q, r, s, t float64) (float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64) {
return a, b, c, d, e, f, g, h, i, j, l, m, n, o, p, q, r, s, t
}
//go:registerparams
//go:noinline
func passStruct1(a Struct1) Struct1 {
return a
}
//go:registerparams
//go:noinline
func passStruct2(a Struct2) Struct2 {
return a
}
//go:registerparams
//go:noinline
func passStruct3(a Struct3) Struct3 {
return a
}
//go:registerparams
//go:noinline
func passStruct4(a Struct4) Struct4 {
return a
}
//go:registerparams
//go:noinline
func passStruct5(a Struct5) Struct5 {
return a
}
//go:registerparams
//go:noinline
func passStruct6(a Struct6) Struct6 {
return a
}
//go:registerparams
//go:noinline
func passStruct7(a Struct7) Struct7 {
return a
}
//go:registerparams
//go:noinline
func passStruct8(a Struct8) Struct8 {
return a
}
//go:registerparams
//go:noinline
func passStruct9(a Struct9) Struct9 {
return a
}
//go:registerparams
//go:noinline
func passStruct10(a Struct10) Struct10 {
return a
}
//go:registerparams
//go:noinline
func passStruct11(a Struct11) Struct11 {
return a
}
//go:registerparams
//go:noinline
func passStruct12(a Struct12) Struct12 {
return a
}
//go:registerparams
//go:noinline
func passStruct13(a Struct13) Struct13 {
return a
}
//go:registerparams
//go:noinline
func passStruct14(a Struct14) Struct14 {
return a
}
//go:registerparams
//go:noinline
func passStruct15(a Struct15) Struct15 {
return a
}
//go:registerparams
//go:noinline
func pass2Struct1(a, b Struct1) (x, y Struct1) {
return a, b
}
//go:registerparams
//go:noinline
func passEmptyStruct(a int, b struct{}, c float64) (int, struct{}, float64) {
return a, b, c
}
// This test case forces a large argument to the stack followed by more
// in-register arguments.
//
//go:registerparams
//go:noinline
func passStruct10AndSmall(a Struct10, b byte, c uint) (Struct10, byte, uint) {
return a, b, c
}
var abiMakeFuncTestCases = []any{
callArgsNone,
callArgsInt,
callArgsInt8,
callArgsInt16,
callArgsInt32,
callArgsInt64,
callArgsUint,
callArgsUint8,
callArgsUint16,
callArgsUint32,
callArgsUint64,
callArgsFloat32,
callArgsFloat64,
callArgsComplex64,
callArgsComplex128,
callArgsManyInt,
callArgsManyFloat64,
callArgsArray1,
callArgsArray,
callArgsArray1Mix,
callArgsString,
// TODO(mknyszek): Test callArgsing interface values.
callArgsSlice,
callArgsPointer,
callArgsStruct1,
callArgsStruct2,
callArgsStruct3,
callArgsStruct4,
callArgsStruct5,
callArgsStruct6,
callArgsStruct7,
callArgsStruct8,
callArgsStruct9,
callArgsStruct10,
// TODO(mknyszek): Test callArgsing unsafe.Pointer values.
// TODO(mknyszek): Test callArgsing chan values.
callArgsStruct11,
callArgsStruct12,
callArgsStruct13,
callArgsStruct14,
callArgsStruct15,
callArgs2Struct1,
callArgsEmptyStruct,
}
//go:registerparams
//go:noinline
func callArgsNone(f func(MagicLastTypeNameForTestingRegisterABI)) {
f(MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsInt(f func(int, MagicLastTypeNameForTestingRegisterABI) int, a0 int) int {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsInt8(f func(int8, MagicLastTypeNameForTestingRegisterABI) int8, a0 int8) int8 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsInt16(f func(int16, MagicLastTypeNameForTestingRegisterABI) int16, a0 int16) int16 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsInt32(f func(int32, MagicLastTypeNameForTestingRegisterABI) int32, a0 int32) int32 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsInt64(f func(int64, MagicLastTypeNameForTestingRegisterABI) int64, a0 int64) int64 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsUint(f func(uint, MagicLastTypeNameForTestingRegisterABI) uint, a0 uint) uint {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsUint8(f func(uint8, MagicLastTypeNameForTestingRegisterABI) uint8, a0 uint8) uint8 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsUint16(f func(uint16, MagicLastTypeNameForTestingRegisterABI) uint16, a0 uint16) uint16 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsUint32(f func(uint32, MagicLastTypeNameForTestingRegisterABI) uint32, a0 uint32) uint32 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsUint64(f func(uint64, MagicLastTypeNameForTestingRegisterABI) uint64, a0 uint64) uint64 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsFloat32(f func(float32, MagicLastTypeNameForTestingRegisterABI) float32, a0 float32) float32 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsFloat64(f func(float64, MagicLastTypeNameForTestingRegisterABI) float64, a0 float64) float64 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsComplex64(f func(complex64, MagicLastTypeNameForTestingRegisterABI) complex64, a0 complex64) complex64 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsComplex128(f func(complex128, MagicLastTypeNameForTestingRegisterABI) complex128, a0 complex128) complex128 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsArray1(f func([1]uint32, MagicLastTypeNameForTestingRegisterABI) [1]uint32, a0 [1]uint32) [1]uint32 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsArray(f func([2]uintptr, MagicLastTypeNameForTestingRegisterABI) [2]uintptr, a0 [2]uintptr) [2]uintptr {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsArray1Mix(f func(int, [1]uint32, float64, MagicLastTypeNameForTestingRegisterABI) (int, [1]uint32, float64), a0 int, a1 [1]uint32, a2 float64) (int, [1]uint32, float64) {
return f(a0, a1, a2, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsString(f func(string, MagicLastTypeNameForTestingRegisterABI) string, a0 string) string {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsSlice(f func([]byte, MagicLastTypeNameForTestingRegisterABI) []byte, a0 []byte) []byte {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsPointer(f func(*byte, MagicLastTypeNameForTestingRegisterABI) *byte, a0 *byte) *byte {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsManyInt(f func(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 int, x MagicLastTypeNameForTestingRegisterABI) (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 int), a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 int) (int, int, int, int, int, int, int, int, int, int) {
return f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsManyFloat64(f func(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 float64, x MagicLastTypeNameForTestingRegisterABI) (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18 float64), a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 float64) (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18 float64) {
return f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct1(f func(Struct1, MagicLastTypeNameForTestingRegisterABI) Struct1, a0 Struct1) Struct1 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct2(f func(Struct2, MagicLastTypeNameForTestingRegisterABI) Struct2, a0 Struct2) Struct2 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct3(f func(Struct3, MagicLastTypeNameForTestingRegisterABI) Struct3, a0 Struct3) Struct3 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct4(f func(Struct4, MagicLastTypeNameForTestingRegisterABI) Struct4, a0 Struct4) Struct4 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct5(f func(Struct5, MagicLastTypeNameForTestingRegisterABI) Struct5, a0 Struct5) Struct5 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct6(f func(Struct6, MagicLastTypeNameForTestingRegisterABI) Struct6, a0 Struct6) Struct6 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct7(f func(Struct7, MagicLastTypeNameForTestingRegisterABI) Struct7, a0 Struct7) Struct7 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct8(f func(Struct8, MagicLastTypeNameForTestingRegisterABI) Struct8, a0 Struct8) Struct8 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct9(f func(Struct9, MagicLastTypeNameForTestingRegisterABI) Struct9, a0 Struct9) Struct9 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct10(f func(Struct10, MagicLastTypeNameForTestingRegisterABI) Struct10, a0 Struct10) Struct10 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct11(f func(Struct11, MagicLastTypeNameForTestingRegisterABI) Struct11, a0 Struct11) Struct11 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct12(f func(Struct12, MagicLastTypeNameForTestingRegisterABI) Struct12, a0 Struct12) Struct12 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct13(f func(Struct13, MagicLastTypeNameForTestingRegisterABI) Struct13, a0 Struct13) Struct13 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct14(f func(Struct14, MagicLastTypeNameForTestingRegisterABI) Struct14, a0 Struct14) Struct14 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsStruct15(f func(Struct15, MagicLastTypeNameForTestingRegisterABI) Struct15, a0 Struct15) Struct15 {
return f(a0, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgs2Struct1(f func(Struct1, Struct1, MagicLastTypeNameForTestingRegisterABI) (Struct1, Struct1), a0, a1 Struct1) (r0, r1 Struct1) {
return f(a0, a1, MagicLastTypeNameForTestingRegisterABI{})
}
//go:registerparams
//go:noinline
func callArgsEmptyStruct(f func(int, struct{}, float64, MagicLastTypeNameForTestingRegisterABI) (int, struct{}, float64), a0 int, a1 struct{}, a2 float64) (int, struct{}, float64) {
return f(a0, a1, a2, MagicLastTypeNameForTestingRegisterABI{})
}
// Struct1 is a simple integer-only aggregate struct.
type Struct1 struct {
A, B, C uint
}
// Struct2 is Struct1 but with an array-typed field that will
// force it to get passed on the stack.
type Struct2 struct {
A, B, C uint
D [2]uint32
}
// Struct3 is Struct2 but with an anonymous array-typed field.
// This should act identically to Struct2.
type Struct3 struct {
A, B, C uint
D [2]uint32
}
// Struct4 has byte-length fields that should
// each use up a whole registers.
type Struct4 struct {
A, B int8
C, D uint8
E bool
}
// Struct5 is a relatively large struct
// with both integer and floating point values.
type Struct5 struct {
A uint16
B int16
C, D uint32
E int32
F, G, H, I, J float32
}
// Struct6 has a nested struct.
type Struct6 struct {
Struct1
}
// Struct7 is a struct with a nested array-typed field
// that cannot be passed in registers as a result.
type Struct7 struct {
Struct1
Struct2
}
// Struct8 is large aggregate struct type that may be
// passed in registers.
type Struct8 struct {
Struct5
Struct1
}
// Struct9 is a type that has an array type nested
// 2 layers deep, and as a result needs to be passed
// on the stack.
type Struct9 struct {
Struct1
Struct7
}
// Struct10 is a struct type that is too large to be
// passed in registers.
type Struct10 struct {
Struct5
Struct8
}
// Struct11 is a struct type that has several reference
// types in it.
type Struct11 struct {
X map[string]int
}
// Struct12 has Struct11 embedded into it to test more
// paths.
type Struct12 struct {
A int
Struct11
}
// Struct13 tests an empty field.
type Struct13 struct {
A int
X struct{}
B int
}
// Struct14 tests a non-zero-sized (and otherwise register-assignable)
// struct with a field that is a non-zero length array with zero-sized members.
type Struct14 struct {
A uintptr
X [3]struct{}
B float64
}
// Struct15 tests a non-zero-sized (and otherwise register-assignable)
// struct with a struct field that is zero-sized but contains a
// non-zero length array with zero-sized members.
type Struct15 struct {
A uintptr
X struct {
Y [3]struct{}
}
B float64
}
const genValueRandSeed = 0
// genValue generates a pseudorandom reflect.Value with type t.
// The reflect.Value produced by this function is always the same
// for the same type.
func genValue(t *testing.T, typ reflect.Type, r *rand.Rand) reflect.Value {
// Re-seed and reset the PRNG because we want each value with the
// same type to be the same random value.
r.Seed(genValueRandSeed)
v, ok := quick.Value(typ, r)
if !ok {
t.Fatal("failed to generate value")
}
return v
}
func TestSignalingNaNArgument(t *testing.T) {
v := reflect.ValueOf(func(x float32) {
// make sure x is a signaling NaN.
u := math.Float32bits(x)
if u != snan {
t.Fatalf("signaling NaN not correct: %x\n", u)
}
})
v.Call([]reflect.Value{reflect.ValueOf(math.Float32frombits(snan))})
}
func TestSignalingNaNReturn(t *testing.T) {
v := reflect.ValueOf(func() float32 {
return math.Float32frombits(snan)
})
var x float32
reflect.ValueOf(&x).Elem().Set(v.Call(nil)[0])
// make sure x is a signaling NaN.
u := math.Float32bits(x)
if u != snan {
t.Fatalf("signaling NaN not correct: %x\n", u)
}
}

8605
src/reflect/all_test.go Normal file

File diff suppressed because it is too large Load Diff

18
src/reflect/arena.go Normal file
View File

@@ -0,0 +1,18 @@
// Copyright 2022 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.
//go:build goexperiment.arenas
package reflect
import "arena"
// ArenaNew returns a [Value] representing a pointer to a new zero value for the
// specified type, allocating storage for it in the provided arena. That is,
// the returned Value's Type is [PointerTo](typ).
func ArenaNew(a *arena.Arena, typ Type) Value {
return ValueOf(arena_New(a, PointerTo(typ)))
}
func arena_New(a *arena.Arena, typ any) any

38
src/reflect/asm_386.s Normal file
View File

@@ -0,0 +1,38 @@
// Copyright 2012 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.
#include "textflag.h"
#include "funcdata.h"
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$20
NO_LOCAL_POINTERS
MOVL DX, 0(SP)
LEAL argframe+0(FP), CX
MOVL CX, 4(SP)
MOVB $0, 16(SP)
LEAL 16(SP), AX
MOVL AX, 8(SP)
MOVL $0, 12(SP)
CALL ·callReflect(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$20
NO_LOCAL_POINTERS
MOVL DX, 0(SP)
LEAL argframe+0(FP), CX
MOVL CX, 4(SP)
MOVB $0, 16(SP)
LEAL 16(SP), AX
MOVL AX, 8(SP)
MOVL $0, 12(SP)
CALL ·callMethod(SB)
RET

79
src/reflect/asm_amd64.s Normal file
View File

@@ -0,0 +1,79 @@
// Copyright 2012 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.
#include "textflag.h"
#include "funcdata.h"
#include "go_asm.h"
// The frames of each of the two functions below contain two locals, at offsets
// that are known to the runtime.
//
// The first local is a bool called retValid with a whole pointer-word reserved
// for it on the stack. The purpose of this word is so that the runtime knows
// whether the stack-allocated return space contains valid values for stack
// scanning.
//
// The second local is an abi.RegArgs value whose offset is also known to the
// runtime, so that a stack map for it can be constructed, since it contains
// pointers visible to the GC.
#define LOCAL_RETVALID 32
#define LOCAL_REGARGS 40
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
// This frame contains two locals. See the comment above LOCAL_RETVALID.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$312
NO_LOCAL_POINTERS
// NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this
// frame is specially handled in the runtime. See the comment above LOCAL_RETVALID.
LEAQ LOCAL_REGARGS(SP), R12
CALL runtime·spillArgs(SB)
MOVQ DX, 24(SP) // outside of moveMakeFuncArgPtrs's arg area
MOVQ DX, 0(SP)
MOVQ R12, 8(SP)
CALL ·moveMakeFuncArgPtrs(SB)
MOVQ 24(SP), DX
MOVQ DX, 0(SP)
LEAQ argframe+0(FP), CX
MOVQ CX, 8(SP)
MOVB $0, LOCAL_RETVALID(SP)
LEAQ LOCAL_RETVALID(SP), AX
MOVQ AX, 16(SP)
LEAQ LOCAL_REGARGS(SP), AX
MOVQ AX, 24(SP)
CALL ·callReflect(SB)
LEAQ LOCAL_REGARGS(SP), R12
CALL runtime·unspillArgs(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
// This frame contains two locals. See the comment above LOCAL_RETVALID.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$312
NO_LOCAL_POINTERS
// NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this
// frame is specially handled in the runtime. See the comment above LOCAL_RETVALID.
LEAQ LOCAL_REGARGS(SP), R12
CALL runtime·spillArgs(SB)
MOVQ DX, 24(SP) // outside of moveMakeFuncArgPtrs's arg area
MOVQ DX, 0(SP)
MOVQ R12, 8(SP)
CALL ·moveMakeFuncArgPtrs(SB)
MOVQ 24(SP), DX
MOVQ DX, 0(SP)
LEAQ argframe+0(FP), CX
MOVQ CX, 8(SP)
MOVB $0, LOCAL_RETVALID(SP)
LEAQ LOCAL_RETVALID(SP), AX
MOVQ AX, 16(SP)
LEAQ LOCAL_REGARGS(SP), AX
MOVQ AX, 24(SP)
CALL ·callMethod(SB)
LEAQ LOCAL_REGARGS(SP), R12
CALL runtime·unspillArgs(SB)
RET

42
src/reflect/asm_arm.s Normal file
View File

@@ -0,0 +1,42 @@
// Copyright 2012 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.
#include "textflag.h"
#include "funcdata.h"
// makeFuncStub is jumped to by the code generated by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$20
NO_LOCAL_POINTERS
MOVW R7, 4(R13)
MOVW $argframe+0(FP), R1
MOVW R1, 8(R13)
MOVW $0, R1
MOVB R1, 20(R13)
ADD $20, R13, R1
MOVW R1, 12(R13)
MOVW $0, R1
MOVW R1, 16(R13)
BL ·callReflect(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$20
NO_LOCAL_POINTERS
MOVW R7, 4(R13)
MOVW $argframe+0(FP), R1
MOVW R1, 8(R13)
MOVW $0, R1
MOVB R1, 20(R13)
ADD $20, R13, R1
MOVW R1, 12(R13)
MOVW $0, R1
MOVW R1, 16(R13)
BL ·callMethod(SB)
RET

79
src/reflect/asm_arm64.s Normal file
View File

@@ -0,0 +1,79 @@
// Copyright 2012 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.
#include "textflag.h"
#include "funcdata.h"
// The frames of each of the two functions below contain two locals, at offsets
// that are known to the runtime.
//
// The first local is a bool called retValid with a whole pointer-word reserved
// for it on the stack. The purpose of this word is so that the runtime knows
// whether the stack-allocated return space contains valid values for stack
// scanning.
//
// The second local is an abi.RegArgs value whose offset is also known to the
// runtime, so that a stack map for it can be constructed, since it contains
// pointers visible to the GC.
#define LOCAL_RETVALID 40
#define LOCAL_REGARGS 48
// The frame size of the functions below is
// 32 (args of callReflect) + 8 (bool + padding) + 392 (abi.RegArgs) = 432.
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No arg size here, runtime pulls arg map out of the func value.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$432
NO_LOCAL_POINTERS
// NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this
// frame is specially handled in the runtime. See the comment above LOCAL_RETVALID.
ADD $LOCAL_REGARGS, RSP, R20
CALL runtime·spillArgs(SB)
MOVD R26, 32(RSP) // outside of moveMakeFuncArgPtrs's arg area
MOVD R26, R0
MOVD R20, R1
CALL ·moveMakeFuncArgPtrs<ABIInternal>(SB)
MOVD 32(RSP), R26
MOVD R26, 8(RSP)
MOVD $argframe+0(FP), R3
MOVD R3, 16(RSP)
MOVB $0, LOCAL_RETVALID(RSP)
ADD $LOCAL_RETVALID, RSP, R3
MOVD R3, 24(RSP)
ADD $LOCAL_REGARGS, RSP, R3
MOVD R3, 32(RSP)
CALL ·callReflect(SB)
ADD $LOCAL_REGARGS, RSP, R20
CALL runtime·unspillArgs(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$432
NO_LOCAL_POINTERS
// NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this
// frame is specially handled in the runtime. See the comment above LOCAL_RETVALID.
ADD $LOCAL_REGARGS, RSP, R20
CALL runtime·spillArgs(SB)
MOVD R26, 32(RSP) // outside of moveMakeFuncArgPtrs's arg area
MOVD R26, R0
MOVD R20, R1
CALL ·moveMakeFuncArgPtrs<ABIInternal>(SB)
MOVD 32(RSP), R26
MOVD R26, 8(RSP)
MOVD $argframe+0(FP), R3
MOVD R3, 16(RSP)
MOVB $0, LOCAL_RETVALID(RSP)
ADD $LOCAL_RETVALID, RSP, R3
MOVD R3, 24(RSP)
ADD $LOCAL_REGARGS, RSP, R3
MOVD R3, 32(RSP)
CALL ·callMethod(SB)
ADD $LOCAL_REGARGS, RSP, R20
CALL runtime·unspillArgs(SB)
RET

79
src/reflect/asm_loong64.s Normal file
View File

@@ -0,0 +1,79 @@
// Copyright 2022 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.
#include "textflag.h"
#include "funcdata.h"
#define REGCTXT R29
// The frames of each of the two functions below contain two locals, at offsets
// that are known to the runtime.
//
// The first local is a bool called retValid with a whole pointer-word reserved
// for it on the stack. The purpose of this word is so that the runtime knows
// whether the stack-allocated return space contains valid values for stack
// scanning.
//
// The second local is an abi.RegArgs value whose offset is also known to the
// runtime, so that a stack map for it can be constructed, since it contains
// pointers visible to the GC.
#define LOCAL_RETVALID 40
#define LOCAL_REGARGS 48
// The frame size of the functions below is
// 32 (args of callReflect) + 8 (bool + padding) + 392 (abi.RegArgs) = 432.
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No arg size here, runtime pulls arg map out of the func value.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$432
NO_LOCAL_POINTERS
ADDV $LOCAL_REGARGS, R3, R25 // spillArgs using R25
JAL runtime·spillArgs(SB)
MOVV REGCTXT, 32(R3) // save REGCTXT > args of moveMakeFuncArgPtrs < LOCAL_REGARGS
MOVV REGCTXT, R4
MOVV R25, R5
JAL ·moveMakeFuncArgPtrs<ABIInternal>(SB)
MOVV 32(R3), REGCTXT // restore REGCTXT
MOVV REGCTXT, 8(R3)
MOVV $argframe+0(FP), R20
MOVV R20, 16(R3)
MOVV R0, LOCAL_RETVALID(R3)
ADDV $LOCAL_RETVALID, R3, R20
MOVV R20, 24(R3)
ADDV $LOCAL_REGARGS, R3, R20
MOVV R20, 32(R3)
JAL ·callReflect(SB)
ADDV $LOCAL_REGARGS, R3, R25 //unspillArgs using R25
JAL runtime·unspillArgs(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$432
NO_LOCAL_POINTERS
ADDV $LOCAL_REGARGS, R3, R25 // spillArgs using R25
JAL runtime·spillArgs(SB)
MOVV REGCTXT, 32(R3) // save REGCTXT > args of moveMakeFuncArgPtrs < LOCAL_REGARGS
MOVV REGCTXT, R4
MOVV R25, R5
JAL ·moveMakeFuncArgPtrs<ABIInternal>(SB)
MOVV 32(R3), REGCTXT // restore REGCTXT
MOVV REGCTXT, 8(R3)
MOVV $argframe+0(FP), R20
MOVV R20, 16(R3)
MOVB R0, LOCAL_RETVALID(R3)
ADDV $LOCAL_RETVALID, R3, R20
MOVV R20, 24(R3)
ADDV $LOCAL_REGARGS, R3, R20
MOVV R20, 32(R3) // frame size to 32+SP as callreflect args)
JAL ·callMethod(SB)
ADDV $LOCAL_REGARGS, R3, R25 // unspillArgs using R25
JAL runtime·unspillArgs(SB)
RET

42
src/reflect/asm_mips64x.s Normal file
View File

@@ -0,0 +1,42 @@
// Copyright 2015 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.
//go:build mips64 || mips64le
#include "textflag.h"
#include "funcdata.h"
#define REGCTXT R22
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No arg size here, runtime pulls arg map out of the func value.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40
NO_LOCAL_POINTERS
MOVV REGCTXT, 8(R29)
MOVV $argframe+0(FP), R1
MOVV R1, 16(R29)
MOVB R0, 40(R29)
ADDV $40, R29, R1
MOVV R1, 24(R29)
MOVV R0, 32(R29)
JAL ·callReflect(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40
NO_LOCAL_POINTERS
MOVV REGCTXT, 8(R29)
MOVV $argframe+0(FP), R1
MOVV R1, 16(R29)
MOVB R0, 40(R29)
ADDV $40, R29, R1
MOVV R1, 24(R29)
MOVV R0, 32(R29)
JAL ·callMethod(SB)
RET

42
src/reflect/asm_mipsx.s Normal file
View File

@@ -0,0 +1,42 @@
// Copyright 2016 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.
//go:build mips || mipsle
#include "textflag.h"
#include "funcdata.h"
#define REGCTXT R22
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No arg size here, runtime pulls arg map out of the func value.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$20
NO_LOCAL_POINTERS
MOVW REGCTXT, 4(R29)
MOVW $argframe+0(FP), R1
MOVW R1, 8(R29)
MOVB R0, 20(R29)
ADD $20, R29, R1
MOVW R1, 12(R29)
MOVW R0, 16(R29)
JAL ·callReflect(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$20
NO_LOCAL_POINTERS
MOVW REGCTXT, 4(R29)
MOVW $argframe+0(FP), R1
MOVW R1, 8(R29)
MOVB R0, 20(R29)
ADD $20, R29, R1
MOVW R1, 12(R29)
MOVW R0, 16(R29)
JAL ·callMethod(SB)
RET

83
src/reflect/asm_ppc64x.s Normal file
View File

@@ -0,0 +1,83 @@
// Copyright 2012 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.
//go:build ppc64 || ppc64le
#include "textflag.h"
#include "funcdata.h"
#include "asm_ppc64x.h"
// The frames of each of the two functions below contain two locals, at offsets
// that are known to the runtime.
//
// The first local is a bool called retValid with a whole pointer-word reserved
// for it on the stack. The purpose of this word is so that the runtime knows
// whether the stack-allocated return space contains valid values for stack
// scanning.
//
// The second local is an abi.RegArgs value whose offset is also known to the
// runtime, so that a stack map for it can be constructed, since it contains
// pointers visible to the GC.
#define LOCAL_RETVALID 32+FIXED_FRAME
#define LOCAL_REGARGS 40+FIXED_FRAME
// The frame size of the functions below is
// 32 (args of callReflect) + 8 (bool + padding) + 296 (abi.RegArgs) = 336.
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No arg size here, runtime pulls arg map out of the func value.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$336
NO_LOCAL_POINTERS
// NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this
// frame is specially handled in the runtime. See the comment above LOCAL_RETVALID.
ADD $LOCAL_REGARGS, R1, R20
CALL runtime·spillArgs(SB)
MOVD R11, FIXED_FRAME+32(R1) // save R11
MOVD R11, FIXED_FRAME+0(R1) // arg for moveMakeFuncArgPtrs
MOVD R20, FIXED_FRAME+8(R1) // arg for local args
CALL ·moveMakeFuncArgPtrs(SB)
MOVD FIXED_FRAME+32(R1), R11 // restore R11 ctxt
MOVD R11, FIXED_FRAME+0(R1) // ctxt (arg0)
MOVD $argframe+0(FP), R3 // save arg to callArg
MOVD R3, FIXED_FRAME+8(R1) // frame (arg1)
ADD $LOCAL_RETVALID, R1, R3 // addr of return flag
MOVB R0, (R3) // clear flag
MOVD R3, FIXED_FRAME+16(R1) // addr retvalid (arg2)
ADD $LOCAL_REGARGS, R1, R3
MOVD R3, FIXED_FRAME+24(R1) // abiregargs (arg3)
BL ·callReflect(SB)
ADD $LOCAL_REGARGS, R1, R20 // set address of spill area
CALL runtime·unspillArgs(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$336
NO_LOCAL_POINTERS
// NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this
// frame is specially handled in the runtime. See the comment above LOCAL_RETVALID.
ADD $LOCAL_REGARGS, R1, R20
CALL runtime·spillArgs(SB)
MOVD R11, FIXED_FRAME+0(R1) // arg0 ctxt
MOVD R11, FIXED_FRAME+32(R1) // save for later
MOVD R20, FIXED_FRAME+8(R1) // arg1 abiregargs
CALL ·moveMakeFuncArgPtrs(SB)
MOVD FIXED_FRAME+32(R1), R11 // restore ctxt
MOVD R11, FIXED_FRAME+0(R1) // set as arg0
MOVD $argframe+0(FP), R3 // frame pointer
MOVD R3, FIXED_FRAME+8(R1) // set as arg1
ADD $LOCAL_RETVALID, R1, R3
MOVB $0, (R3) // clear ret flag
MOVD R3, FIXED_FRAME+16(R1) // addr of return flag
ADD $LOCAL_REGARGS, R1, R3 // addr of abiregargs
MOVD R3, FIXED_FRAME+24(R1) // set as arg3
BL ·callMethod(SB)
ADD $LOCAL_REGARGS, R1, R20
CALL runtime·unspillArgs(SB)
RET

76
src/reflect/asm_riscv64.s Normal file
View File

@@ -0,0 +1,76 @@
// Copyright 2019 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.
#include "textflag.h"
#include "funcdata.h"
// The frames of each of the two functions below contain two locals, at offsets
// that are known to the runtime.
//
// The first local is a bool called retValid with a whole pointer-word reserved
// for it on the stack. The purpose of this word is so that the runtime knows
// whether the stack-allocated return space contains valid values for stack
// scanning.
//
// The second local is an abi.RegArgs value whose offset is also known to the
// runtime, so that a stack map for it can be constructed, since it contains
// pointers visible to the GC.
#define LOCAL_RETVALID 40
#define LOCAL_REGARGS 48
// The frame size of the functions below is
// 32 (args of callReflect/callMethod) + (8 bool with padding) + 392 (abi.RegArgs) = 432.
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No arg size here, runtime pulls arg map out of the func value.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$432
NO_LOCAL_POINTERS
ADD $LOCAL_REGARGS, SP, X25 // spillArgs using X25
CALL runtime·spillArgs(SB)
MOV CTXT, 32(SP) // save CTXT > args of moveMakeFuncArgPtrs < LOCAL_REGARGS
MOV CTXT, 8(SP)
MOV X25, 16(SP)
CALL ·moveMakeFuncArgPtrs(SB)
MOV 32(SP), CTXT // restore CTXT
MOV CTXT, 8(SP)
MOV $argframe+0(FP), T0
MOV T0, 16(SP)
MOV ZERO, LOCAL_RETVALID(SP)
ADD $LOCAL_RETVALID, SP, T1
MOV T1, 24(SP)
ADD $LOCAL_REGARGS, SP, T1
MOV T1, 32(SP)
CALL ·callReflect(SB)
ADD $LOCAL_REGARGS, SP, X25 // unspillArgs using X25
CALL runtime·unspillArgs(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$432
NO_LOCAL_POINTERS
ADD $LOCAL_REGARGS, SP, X25 // spillArgs using X25
CALL runtime·spillArgs(SB)
MOV CTXT, 32(SP) // save CTXT
MOV CTXT, 8(SP)
MOV X25, 16(SP)
CALL ·moveMakeFuncArgPtrs(SB)
MOV 32(SP), CTXT // restore CTXT
MOV CTXT, 8(SP)
MOV $argframe+0(FP), T0
MOV T0, 16(SP)
MOV ZERO, LOCAL_RETVALID(SP)
ADD $LOCAL_RETVALID, SP, T1
MOV T1, 24(SP)
ADD $LOCAL_REGARGS, SP, T1
MOV T1, 32(SP) // frame size to 32+SP as callreflect args
CALL ·callMethod(SB)
ADD $LOCAL_REGARGS, SP, X25 // unspillArgs using X25
CALL runtime·unspillArgs(SB)
RET

38
src/reflect/asm_s390x.s Normal file
View File

@@ -0,0 +1,38 @@
// Copyright 2016 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.
#include "textflag.h"
#include "funcdata.h"
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No arg size here, runtime pulls arg map out of the func value.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40
NO_LOCAL_POINTERS
MOVD R12, 8(R15)
MOVD $argframe+0(FP), R3
MOVD R3, 16(R15)
MOVB $0, 40(R15)
ADD $40, R15, R3
MOVD R3, 24(R15)
MOVD $0, 32(R15)
BL ·callReflect(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40
NO_LOCAL_POINTERS
MOVD R12, 8(R15)
MOVD $argframe+0(FP), R3
MOVD R3, 16(R15)
MOVB $0, 40(R15)
ADD $40, R15, R3
MOVD R3, 24(R15)
MOVD $0, 32(R15)
BL ·callMethod(SB)
RET

52
src/reflect/asm_wasm.s Normal file
View File

@@ -0,0 +1,52 @@
// Copyright 2018 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.
#include "textflag.h"
#include "funcdata.h"
// makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40
NO_LOCAL_POINTERS
MOVD CTXT, 0(SP)
Get SP
Get SP
I64ExtendI32U
I64Const $argframe+0(FP)
I64Add
I64Store $8
MOVB $0, 32(SP)
MOVD $32(SP), 16(SP)
MOVD $0, 24(SP)
CALL ·callReflect(SB)
RET
// methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No arg size here; runtime pulls arg map out of the func value.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40
NO_LOCAL_POINTERS
MOVD CTXT, 0(SP)
Get SP
Get SP
I64ExtendI32U
I64Const $argframe+0(FP)
I64Add
I64Store $8
MOVB $0, 32(SP)
MOVD $32(SP), 16(SP)
MOVD $0, 24(SP)
CALL ·callMethod(SB)
RET

130
src/reflect/badlinkname.go Normal file
View File

@@ -0,0 +1,130 @@
// Copyright 2024 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 reflect
import (
"internal/abi"
"unsafe"
_ "unsafe"
)
// Widely used packages access these symbols using linkname,
// most notably:
// - github.com/goccy/go-json
// - github.com/goccy/go-reflect
// - github.com/sohaha/zlsgo
// - github.com/undefinedlabs/go-mpatch
//
// Do not remove or change the type signature.
// See go.dev/issue/67401
// and go.dev/issue/67279.
// ifaceIndir reports whether t is stored indirectly in an interface value.
// It is no longer used by this package and is here entirely for the
// linkname uses.
//
//go:linkname unusedIfaceIndir reflect.ifaceIndir
func unusedIfaceIndir(t *abi.Type) bool {
return t.Kind_&abi.KindDirectIface == 0
}
//go:linkname valueInterface
// The compiler doesn't allow linknames on methods, for good reasons.
// We use this trick to push linknames of the methods.
// Do not call them in this package.
//go:linkname badlinkname_rtype_Align reflect.(*rtype).Align
func badlinkname_rtype_Align(*rtype) int
//go:linkname badlinkname_rtype_AssignableTo reflect.(*rtype).AssignableTo
func badlinkname_rtype_AssignableTo(*rtype, Type) bool
//go:linkname badlinkname_rtype_Bits reflect.(*rtype).Bits
func badlinkname_rtype_Bits(*rtype) int
//go:linkname badlinkname_rtype_ChanDir reflect.(*rtype).ChanDir
func badlinkname_rtype_ChanDir(*rtype) ChanDir
//go:linkname badlinkname_rtype_Comparable reflect.(*rtype).Comparable
func badlinkname_rtype_Comparable(*rtype) bool
//go:linkname badlinkname_rtype_ConvertibleTo reflect.(*rtype).ConvertibleTo
func badlinkname_rtype_ConvertibleTo(*rtype, Type) bool
//go:linkname badlinkname_rtype_Elem reflect.(*rtype).Elem
func badlinkname_rtype_Elem(*rtype) Type
//go:linkname badlinkname_rtype_Field reflect.(*rtype).Field
func badlinkname_rtype_Field(*rtype, int) StructField
//go:linkname badlinkname_rtype_FieldAlign reflect.(*rtype).FieldAlign
func badlinkname_rtype_FieldAlign(*rtype) int
//go:linkname badlinkname_rtype_FieldByIndex reflect.(*rtype).FieldByIndex
func badlinkname_rtype_FieldByIndex(*rtype, []int) StructField
//go:linkname badlinkname_rtype_FieldByName reflect.(*rtype).FieldByName
func badlinkname_rtype_FieldByName(*rtype, string) (StructField, bool)
//go:linkname badlinkname_rtype_FieldByNameFunc reflect.(*rtype).FieldByNameFunc
func badlinkname_rtype_FieldByNameFunc(*rtype, func(string) bool) (StructField, bool)
//go:linkname badlinkname_rtype_Implements reflect.(*rtype).Implements
func badlinkname_rtype_Implements(*rtype, Type) bool
//go:linkname badlinkname_rtype_In reflect.(*rtype).In
func badlinkname_rtype_In(*rtype, int) Type
//go:linkname badlinkname_rtype_IsVariadic reflect.(*rtype).IsVariadic
func badlinkname_rtype_IsVariadic(*rtype) bool
//go:linkname badlinkname_rtype_Key reflect.(*rtype).Key
func badlinkname_rtype_Key(*rtype) Type
//go:linkname badlinkname_rtype_Kind reflect.(*rtype).Kind
func badlinkname_rtype_Kind(*rtype) Kind
//go:linkname badlinkname_rtype_Len reflect.(*rtype).Len
func badlinkname_rtype_Len(*rtype) int
//go:linkname badlinkname_rtype_Method reflect.(*rtype).Method
func badlinkname_rtype_Method(*rtype, int) Method
//go:linkname badlinkname_rtype_MethodByName reflect.(*rtype).MethodByName
func badlinkname_rtype_MethodByName(*rtype, string) (Method, bool)
//go:linkname badlinkname_rtype_Name reflect.(*rtype).Name
func badlinkname_rtype_Name(*rtype) string
//go:linkname badlinkname_rtype_NumField reflect.(*rtype).NumField
func badlinkname_rtype_NumField(*rtype) int
//go:linkname badlinkname_rtype_NumIn reflect.(*rtype).NumIn
func badlinkname_rtype_NumIn(*rtype) int
//go:linkname badlinkname_rtype_NumMethod reflect.(*rtype).NumMethod
func badlinkname_rtype_NumMethod(*rtype) int
//go:linkname badlinkname_rtype_NumOut reflect.(*rtype).NumOut
func badlinkname_rtype_NumOut(*rtype) int
//go:linkname badlinkname_rtype_Out reflect.(*rtype).Out
func badlinkname_rtype_Out(*rtype, int) Type
//go:linkname badlinkname_rtype_PkgPath reflect.(*rtype).PkgPath
func badlinkname_rtype_PkgPath(*rtype) string
//go:linkname badlinkname_rtype_Size reflect.(*rtype).Size
func badlinkname_rtype_Size(*rtype) uintptr
//go:linkname badlinkname_rtype_String reflect.(*rtype).String
func badlinkname_rtype_String(*rtype) string
//go:linkname badlinkname_rtype_ptrTo reflect.(*rtype).ptrTo
func badlinkname_rtype_ptrTo(*rtype) *abi.Type
//go:linkname badlinkname_Value_pointer reflect.(*Value).pointer
func badlinkname_Value_pointer(Value) unsafe.Pointer

View File

@@ -0,0 +1,428 @@
// Copyright 2022 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 reflect_test
import (
"fmt"
. "reflect"
"strconv"
"testing"
)
var sourceAll = struct {
Bool Value
String Value
Bytes Value
NamedBytes Value
BytesArray Value
SliceAny Value
MapStringAny Value
}{
Bool: ValueOf(new(bool)).Elem(),
String: ValueOf(new(string)).Elem(),
Bytes: ValueOf(new([]byte)).Elem(),
NamedBytes: ValueOf(new(namedBytes)).Elem(),
BytesArray: ValueOf(new([32]byte)).Elem(),
SliceAny: ValueOf(new([]any)).Elem(),
MapStringAny: ValueOf(new(map[string]any)).Elem(),
}
var sinkAll struct {
RawBool bool
RawString string
RawBytes []byte
RawInt int
}
func BenchmarkBool(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawBool = sourceAll.Bool.Bool()
}
}
func BenchmarkString(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawString = sourceAll.String.String()
}
}
func BenchmarkBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawBytes = sourceAll.Bytes.Bytes()
}
}
func BenchmarkNamedBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawBytes = sourceAll.NamedBytes.Bytes()
}
}
func BenchmarkBytesArray(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawBytes = sourceAll.BytesArray.Bytes()
}
}
func BenchmarkSliceLen(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawInt = sourceAll.SliceAny.Len()
}
}
func BenchmarkMapLen(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawInt = sourceAll.MapStringAny.Len()
}
}
func BenchmarkStringLen(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawInt = sourceAll.String.Len()
}
}
func BenchmarkArrayLen(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawInt = sourceAll.BytesArray.Len()
}
}
func BenchmarkSliceCap(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkAll.RawInt = sourceAll.SliceAny.Cap()
}
}
func BenchmarkDeepEqual(b *testing.B) {
for _, bb := range deepEqualPerfTests {
b.Run(ValueOf(bb.x).Type().String(), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
sink = DeepEqual(bb.x, bb.y)
}
})
}
}
func BenchmarkMapsDeepEqual(b *testing.B) {
m1 := map[int]int{
1: 1, 2: 2,
}
m2 := map[int]int{
1: 1, 2: 2,
}
for i := 0; i < b.N; i++ {
DeepEqual(m1, m2)
}
}
func BenchmarkIsZero(b *testing.B) {
type Int4 struct {
a, b, c, d int
}
type Int1024 struct {
a [1024]int
}
type Int512 struct {
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16 [16]S
}
s := struct {
ArrayComparable [4]T
ArrayIncomparable [4]_Complex
StructComparable T
StructIncomparable _Complex
ArrayInt_4 [4]int
ArrayInt_1024 [1024]int
ArrayInt_1024_NoZero [1024]int
Struct4Int Int4
ArrayStruct4Int_1024 [256]Int4
ArrayChanInt_1024 [1024]chan int
StructInt_512 Int512
}{}
s.ArrayInt_1024_NoZero[512] = 1
source := ValueOf(s)
for i := 0; i < source.NumField(); i++ {
name := source.Type().Field(i).Name
value := source.Field(i)
b.Run(name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
sink = value.IsZero()
}
})
}
}
func BenchmarkSetZero(b *testing.B) {
source := ValueOf(new(struct {
Bool bool
Int int64
Uint uint64
Float float64
Complex complex128
Array [4]Value
Chan chan Value
Func func() Value
Interface interface{ String() string }
Map map[string]Value
Pointer *Value
Slice []Value
String string
Struct Value
})).Elem()
for i := 0; i < source.NumField(); i++ {
name := source.Type().Field(i).Name
value := source.Field(i)
zero := Zero(value.Type())
b.Run(name+"/Direct", func(b *testing.B) {
for i := 0; i < b.N; i++ {
value.SetZero()
}
})
b.Run(name+"/CachedZero", func(b *testing.B) {
for i := 0; i < b.N; i++ {
value.Set(zero)
}
})
b.Run(name+"/NewZero", func(b *testing.B) {
for i := 0; i < b.N; i++ {
value.Set(Zero(value.Type()))
}
})
}
}
func BenchmarkSelect(b *testing.B) {
channel := make(chan int)
close(channel)
var cases []SelectCase
for i := 0; i < 8; i++ {
cases = append(cases, SelectCase{
Dir: SelectRecv,
Chan: ValueOf(channel),
})
}
for _, numCases := range []int{1, 4, 8} {
b.Run(strconv.Itoa(numCases), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _, _ = Select(cases[:numCases])
}
})
}
}
func BenchmarkCall(b *testing.B) {
fv := ValueOf(func(a, b string) {})
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
args := []Value{ValueOf("a"), ValueOf("b")}
for pb.Next() {
fv.Call(args)
}
})
}
type myint int64
func (i *myint) inc() {
*i = *i + 1
}
func BenchmarkCallMethod(b *testing.B) {
b.ReportAllocs()
z := new(myint)
v := ValueOf(z.inc)
for i := 0; i < b.N; i++ {
v.Call(nil)
}
}
func BenchmarkCallArgCopy(b *testing.B) {
byteArray := func(n int) Value {
return Zero(ArrayOf(n, TypeOf(byte(0))))
}
sizes := [...]struct {
fv Value
arg Value
}{
{ValueOf(func(a [128]byte) {}), byteArray(128)},
{ValueOf(func(a [256]byte) {}), byteArray(256)},
{ValueOf(func(a [1024]byte) {}), byteArray(1024)},
{ValueOf(func(a [4096]byte) {}), byteArray(4096)},
{ValueOf(func(a [65536]byte) {}), byteArray(65536)},
}
for _, size := range sizes {
bench := func(b *testing.B) {
args := []Value{size.arg}
b.SetBytes(int64(size.arg.Len()))
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
size.fv.Call(args)
}
})
}
name := fmt.Sprintf("size=%v", size.arg.Len())
b.Run(name, bench)
}
}
func BenchmarkPtrTo(b *testing.B) {
// Construct a type with a zero ptrToThis.
type T struct{ int }
t := SliceOf(TypeOf(T{}))
ptrToThis := ValueOf(t).Elem().FieldByName("PtrToThis")
if !ptrToThis.IsValid() {
b.Skipf("%v has no ptrToThis field; was it removed from rtype?", t) // TODO fix this at top of refactoring
// b.Fatalf("%v has no ptrToThis field; was it removed from rtype?", t)
}
if ptrToThis.Int() != 0 {
b.Fatalf("%v.ptrToThis unexpectedly nonzero", t)
}
b.ResetTimer()
// Now benchmark calling PointerTo on it: we'll have to hit the ptrMap cache on
// every call.
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
PointerTo(t)
}
})
}
type B1 struct {
X int
Y int
Z int
}
func BenchmarkFieldByName1(b *testing.B) {
t := TypeOf(B1{})
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
t.FieldByName("Z")
}
})
}
func BenchmarkFieldByName2(b *testing.B) {
t := TypeOf(S3{})
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
t.FieldByName("B")
}
})
}
func BenchmarkFieldByName3(b *testing.B) {
t := TypeOf(R0{})
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
t.FieldByName("X")
}
})
}
type S struct {
i1 int64
i2 int64
}
func BenchmarkInterfaceBig(b *testing.B) {
v := ValueOf(S{})
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
v.Interface()
}
})
b.StopTimer()
}
func BenchmarkInterfaceSmall(b *testing.B) {
v := ValueOf(int64(0))
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
v.Interface()
}
})
}
func BenchmarkNew(b *testing.B) {
v := TypeOf(XM{})
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
New(v)
}
})
}
func BenchmarkMap(b *testing.B) {
type V *int
type S string
value := ValueOf((V)(nil))
stringKeys := []string{}
mapOfStrings := map[string]V{}
uint64Keys := []uint64{}
mapOfUint64s := map[uint64]V{}
userStringKeys := []S{}
mapOfUserStrings := map[S]V{}
for i := 0; i < 100; i++ {
stringKey := fmt.Sprintf("key%d", i)
stringKeys = append(stringKeys, stringKey)
mapOfStrings[stringKey] = nil
uint64Key := uint64(i)
uint64Keys = append(uint64Keys, uint64Key)
mapOfUint64s[uint64Key] = nil
userStringKey := S(fmt.Sprintf("key%d", i))
userStringKeys = append(userStringKeys, userStringKey)
mapOfUserStrings[userStringKey] = nil
}
tests := []struct {
label string
m, keys, value Value
}{
{"StringKeys", ValueOf(mapOfStrings), ValueOf(stringKeys), value},
{"Uint64Keys", ValueOf(mapOfUint64s), ValueOf(uint64Keys), value},
{"UserStringKeys", ValueOf(mapOfUserStrings), ValueOf(userStringKeys), value},
}
for _, tt := range tests {
b.Run(tt.label, func(b *testing.B) {
b.Run("MapIndex", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := tt.keys.Len() - 1; j >= 0; j-- {
tt.m.MapIndex(tt.keys.Index(j))
}
}
})
b.Run("SetMapIndex", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := tt.keys.Len() - 1; j >= 0; j-- {
tt.m.SetMapIndex(tt.keys.Index(j), tt.value)
}
}
})
})
}
}
func BenchmarkMapIterNext(b *testing.B) {
m := ValueOf(map[string]int{"a": 0, "b": 1, "c": 2, "d": 3})
it := m.MapRange()
for i := 0; i < b.N; i++ {
for it.Next() {
}
it.Reset(m)
}
}

239
src/reflect/deepequal.go Normal file
View File

@@ -0,0 +1,239 @@
// 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.
// Deep equality test via reflection
package reflect
import (
"internal/bytealg"
"unsafe"
)
// During deepValueEqual, must keep track of checks that are
// in progress. The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited comparisons are stored in a map indexed by visit.
type visit struct {
a1 unsafe.Pointer
a2 unsafe.Pointer
typ Type
}
// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
if !v1.IsValid() || !v2.IsValid() {
return v1.IsValid() == v2.IsValid()
}
if v1.Type() != v2.Type() {
return false
}
// We want to avoid putting more in the visited map than we need to.
// For any possible reference cycle that might be encountered,
// hard(v1, v2) needs to return true for at least one of the types in the cycle,
// and it's safe and valid to get Value's internal pointer.
hard := func(v1, v2 Value) bool {
switch v1.Kind() {
case Pointer:
if !v1.typ().Pointers() {
// not-in-heap pointers can't be cyclic.
// At least, all of our current uses of runtime/internal/sys.NotInHeap
// have that property. The runtime ones aren't cyclic (and we don't use
// DeepEqual on them anyway), and the cgo-generated ones are
// all empty structs.
return false
}
fallthrough
case Map, Slice, Interface:
// Nil pointers cannot be cyclic. Avoid putting them in the visited map.
return !v1.IsNil() && !v2.IsNil()
}
return false
}
if hard(v1, v2) {
// For a Pointer or Map value, we need to check flagIndir,
// which we do by calling the pointer method.
// For Slice or Interface, flagIndir is always set,
// and using v.ptr suffices.
ptrval := func(v Value) unsafe.Pointer {
switch v.Kind() {
case Pointer, Map:
return v.pointer()
default:
return v.ptr
}
}
addr1 := ptrval(v1)
addr2 := ptrval(v2)
if uintptr(addr1) > uintptr(addr2) {
// Canonicalize order to reduce number of entries in visited.
// Assumes non-moving garbage collector.
addr1, addr2 = addr2, addr1
}
// Short circuit if references are already seen.
typ := v1.Type()
v := visit{addr1, addr2, typ}
if visited[v] {
return true
}
// Remember for later.
visited[v] = true
}
switch v1.Kind() {
case Array:
for i := 0; i < v1.Len(); i++ {
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
return false
}
}
return true
case Slice:
if v1.IsNil() != v2.IsNil() {
return false
}
if v1.Len() != v2.Len() {
return false
}
if v1.UnsafePointer() == v2.UnsafePointer() {
return true
}
// Special case for []byte, which is common.
if v1.Type().Elem().Kind() == Uint8 {
return bytealg.Equal(v1.Bytes(), v2.Bytes())
}
for i := 0; i < v1.Len(); i++ {
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
return false
}
}
return true
case Interface:
if v1.IsNil() || v2.IsNil() {
return v1.IsNil() == v2.IsNil()
}
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
case Pointer:
if v1.UnsafePointer() == v2.UnsafePointer() {
return true
}
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
case Struct:
for i, n := 0, v1.NumField(); i < n; i++ {
if !deepValueEqual(v1.Field(i), v2.Field(i), visited) {
return false
}
}
return true
case Map:
if v1.IsNil() != v2.IsNil() {
return false
}
if v1.Len() != v2.Len() {
return false
}
if v1.UnsafePointer() == v2.UnsafePointer() {
return true
}
iter := v1.MapRange()
for iter.Next() {
val1 := iter.Value()
val2 := v2.MapIndex(iter.Key())
if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) {
return false
}
}
return true
case Func:
if v1.IsNil() && v2.IsNil() {
return true
}
// Can't do better than this:
return false
case Int, Int8, Int16, Int32, Int64:
return v1.Int() == v2.Int()
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return v1.Uint() == v2.Uint()
case String:
return v1.String() == v2.String()
case Bool:
return v1.Bool() == v2.Bool()
case Float32, Float64:
return v1.Float() == v2.Float()
case Complex64, Complex128:
return v1.Complex() == v2.Complex()
default:
// Normal equality suffices
return valueInterface(v1, false) == valueInterface(v2, false)
}
}
// DeepEqual reports whether x and y are “deeply equal,” defined as follows.
// Two values of identical type are deeply equal if one of the following cases applies.
// Values of distinct types are never deeply equal.
//
// Array values are deeply equal when their corresponding elements are deeply equal.
//
// Struct values are deeply equal if their corresponding fields,
// both exported and unexported, are deeply equal.
//
// Func values are deeply equal if both are nil; otherwise they are not deeply equal.
//
// Interface values are deeply equal if they hold deeply equal concrete values.
//
// Map values are deeply equal when all of the following are true:
// they are both nil or both non-nil, they have the same length,
// and either they are the same map object or their corresponding keys
// (matched using Go equality) map to deeply equal values.
//
// Pointer values are deeply equal if they are equal using Go's == operator
// or if they point to deeply equal values.
//
// Slice values are deeply equal when all of the following are true:
// they are both nil or both non-nil, they have the same length,
// and either they point to the same initial entry of the same underlying array
// (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal.
// Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil))
// are not deeply equal.
//
// Other values - numbers, bools, strings, and channels - are deeply equal
// if they are equal using Go's == operator.
//
// In general DeepEqual is a recursive relaxation of Go's == operator.
// However, this idea is impossible to implement without some inconsistency.
// Specifically, it is possible for a value to be unequal to itself,
// either because it is of func type (uncomparable in general)
// or because it is a floating-point NaN value (not equal to itself in floating-point comparison),
// or because it is an array, struct, or interface containing
// such a value.
// On the other hand, pointer values are always equal to themselves,
// even if they point at or contain such problematic values,
// because they compare equal using Go's == operator, and that
// is a sufficient condition to be deeply equal, regardless of content.
// DeepEqual has been defined so that the same short-cut applies
// to slices and maps: if x and y are the same slice or the same map,
// they are deeply equal regardless of content.
//
// As DeepEqual traverses the data values it may find a cycle. The
// second and subsequent times that DeepEqual compares two pointer
// values that have been compared before, it treats the values as
// equal rather than examining the values to which they point.
// This ensures that DeepEqual terminates.
func DeepEqual(x, y any) bool {
if x == nil || y == nil {
return x == y
}
v1 := ValueOf(x)
v2 := ValueOf(y)
if v1.Type() != v2.Type() {
return false
}
return deepValueEqual(v1, v2, make(map[visit]bool))
}

209
src/reflect/example_test.go Normal file
View File

@@ -0,0 +1,209 @@
// Copyright 2012 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 reflect_test
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"reflect"
)
func ExampleKind() {
for _, v := range []any{"hi", 42, func() {}} {
switch v := reflect.ValueOf(v); v.Kind() {
case reflect.String:
fmt.Println(v.String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fmt.Println(v.Int())
default:
fmt.Printf("unhandled kind %s", v.Kind())
}
}
// Output:
// hi
// 42
// unhandled kind func
}
func ExampleMakeFunc() {
// swap is the implementation passed to MakeFunc.
// It must work in terms of reflect.Values so that it is possible
// to write code without knowing beforehand what the types
// will be.
swap := func(in []reflect.Value) []reflect.Value {
return []reflect.Value{in[1], in[0]}
}
// makeSwap expects fptr to be a pointer to a nil function.
// It sets that pointer to a new function created with MakeFunc.
// When the function is invoked, reflect turns the arguments
// into Values, calls swap, and then turns swap's result slice
// into the values returned by the new function.
makeSwap := func(fptr any) {
// fptr is a pointer to a function.
// Obtain the function value itself (likely nil) as a reflect.Value
// so that we can query its type and then set the value.
fn := reflect.ValueOf(fptr).Elem()
// Make a function of the right type.
v := reflect.MakeFunc(fn.Type(), swap)
// Assign it to the value fn represents.
fn.Set(v)
}
// Make and call a swap function for ints.
var intSwap func(int, int) (int, int)
makeSwap(&intSwap)
fmt.Println(intSwap(0, 1))
// Make and call a swap function for float64s.
var floatSwap func(float64, float64) (float64, float64)
makeSwap(&floatSwap)
fmt.Println(floatSwap(2.72, 3.14))
// Output:
// 1 0
// 3.14 2.72
}
func ExampleStructTag() {
type S struct {
F string `species:"gopher" color:"blue"`
}
s := S{}
st := reflect.TypeOf(s)
field := st.Field(0)
fmt.Println(field.Tag.Get("color"), field.Tag.Get("species"))
// Output:
// blue gopher
}
func ExampleStructTag_Lookup() {
type S struct {
F0 string `alias:"field_0"`
F1 string `alias:""`
F2 string
}
s := S{}
st := reflect.TypeOf(s)
for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
if alias, ok := field.Tag.Lookup("alias"); ok {
if alias == "" {
fmt.Println("(blank)")
} else {
fmt.Println(alias)
}
} else {
fmt.Println("(not specified)")
}
}
// Output:
// field_0
// (blank)
// (not specified)
}
func ExampleTypeOf() {
// As interface types are only used for static typing, a
// common idiom to find the reflection Type for an interface
// type Foo is to use a *Foo value.
writerType := reflect.TypeOf((*io.Writer)(nil)).Elem()
fileType := reflect.TypeOf((*os.File)(nil))
fmt.Println(fileType.Implements(writerType))
// Output:
// true
}
func ExampleStructOf() {
typ := reflect.StructOf([]reflect.StructField{
{
Name: "Height",
Type: reflect.TypeOf(float64(0)),
Tag: `json:"height"`,
},
{
Name: "Age",
Type: reflect.TypeOf(int(0)),
Tag: `json:"age"`,
},
})
v := reflect.New(typ).Elem()
v.Field(0).SetFloat(0.4)
v.Field(1).SetInt(2)
s := v.Addr().Interface()
w := new(bytes.Buffer)
if err := json.NewEncoder(w).Encode(s); err != nil {
panic(err)
}
fmt.Printf("value: %+v\n", s)
fmt.Printf("json: %s", w.Bytes())
r := bytes.NewReader([]byte(`{"height":1.5,"age":10}`))
if err := json.NewDecoder(r).Decode(s); err != nil {
panic(err)
}
fmt.Printf("value: %+v\n", s)
// Output:
// value: &{Height:0.4 Age:2}
// json: {"height":0.4,"age":2}
// value: &{Height:1.5 Age:10}
}
func ExampleValue_FieldByIndex() {
// This example shows a case in which the name of a promoted field
// is hidden by another field: FieldByName will not work, so
// FieldByIndex must be used instead.
type user struct {
firstName string
lastName string
}
type data struct {
user
firstName string
lastName string
}
u := data{
user: user{"Embedded John", "Embedded Doe"},
firstName: "John",
lastName: "Doe",
}
s := reflect.ValueOf(u).FieldByIndex([]int{0, 1})
fmt.Println("embedded last name:", s)
// Output:
// embedded last name: Embedded Doe
}
func ExampleValue_FieldByName() {
type user struct {
firstName string
lastName string
}
u := user{firstName: "John", lastName: "Doe"}
s := reflect.ValueOf(u)
fmt.Println("Name:", s.FieldByName("firstName"))
// Output:
// Name: John
}

170
src/reflect/export_test.go Normal file
View File

@@ -0,0 +1,170 @@
// Copyright 2012 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 reflect
import (
"internal/abi"
"internal/goarch"
"sync"
"unsafe"
)
// MakeRO returns a copy of v with the read-only flag set.
func MakeRO(v Value) Value {
v.flag |= flagStickyRO
return v
}
// IsRO reports whether v's read-only flag is set.
func IsRO(v Value) bool {
return v.flag&flagStickyRO != 0
}
var CallGC = &callGC
// FuncLayout calls funcLayout and returns a subset of the results for testing.
//
// Bitmaps like stack, gc, inReg, and outReg are expanded such that each bit
// takes up one byte, so that writing out test cases is a little clearer.
// If ptrs is false, gc will be nil.
func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, stack, gc, inReg, outReg []byte, ptrs bool) {
var ft *abi.Type
var abid abiDesc
if rcvr != nil {
ft, _, abid = funcLayout((*funcType)(unsafe.Pointer(t.common())), rcvr.common())
} else {
ft, _, abid = funcLayout((*funcType)(unsafe.Pointer(t.(*rtype))), nil)
}
// Extract size information.
argSize = abid.stackCallArgsSize
retOffset = abid.retOffset
frametype = toType(ft)
// Expand stack pointer bitmap into byte-map.
for i := uint32(0); i < abid.stackPtrs.n; i++ {
stack = append(stack, abid.stackPtrs.data[i/8]>>(i%8)&1)
}
// Expand register pointer bitmaps into byte-maps.
bool2byte := func(b bool) byte {
if b {
return 1
}
return 0
}
for i := 0; i < intArgRegs; i++ {
inReg = append(inReg, bool2byte(abid.inRegPtrs.Get(i)))
outReg = append(outReg, bool2byte(abid.outRegPtrs.Get(i)))
}
if ft.Kind_&abi.KindGCProg != 0 {
panic("can't handle gc programs")
}
// Expand frame type's GC bitmap into byte-map.
ptrs = ft.Pointers()
if ptrs {
nptrs := ft.PtrBytes / goarch.PtrSize
gcdata := ft.GcSlice(0, (nptrs+7)/8)
for i := uintptr(0); i < nptrs; i++ {
gc = append(gc, gcdata[i/8]>>(i%8)&1)
}
}
return
}
func TypeLinks() []string {
var r []string
sections, offset := typelinks()
for i, offs := range offset {
rodata := sections[i]
for _, off := range offs {
typ := (*rtype)(resolveTypeOff(rodata, off))
r = append(r, typ.String())
}
}
return r
}
var GCBits = gcbits
func gcbits(any) []byte // provided by runtime
func MapBucketOf(x, y Type) Type {
return toType(bucketOf(x.common(), y.common()))
}
func CachedBucketOf(m Type) Type {
t := m.(*rtype)
if Kind(t.t.Kind_&abi.KindMask) != Map {
panic("not map")
}
tt := (*mapType)(unsafe.Pointer(t))
return toType(tt.Bucket)
}
type EmbedWithUnexpMeth struct{}
func (EmbedWithUnexpMeth) f() {}
type pinUnexpMeth interface {
f()
}
var pinUnexpMethI = pinUnexpMeth(EmbedWithUnexpMeth{})
func FirstMethodNameBytes(t Type) *byte {
_ = pinUnexpMethI
ut := t.uncommon()
if ut == nil {
panic("type has no methods")
}
m := ut.Methods()[0]
mname := t.(*rtype).nameOff(m.Name)
if *mname.DataChecked(0, "name flag field")&(1<<2) == 0 {
panic("method name does not have pkgPath *string")
}
return mname.Bytes
}
type OtherPkgFields struct {
OtherExported int
otherUnexported int
}
func IsExported(t Type) bool {
typ := t.(*rtype)
n := typ.nameOff(typ.t.Str)
return n.IsExported()
}
func ResolveReflectName(s string) {
resolveReflectName(newName(s, "", false, false))
}
type Buffer struct {
buf []byte
}
func clearLayoutCache() {
layoutCache = sync.Map{}
}
func SetArgRegs(ints, floats int, floatSize uintptr) (oldInts, oldFloats int, oldFloatSize uintptr) {
oldInts = intArgRegs
oldFloats = floatArgRegs
oldFloatSize = floatRegSize
intArgRegs = ints
floatArgRegs = floats
floatRegSize = floatSize
clearLayoutCache()
return
}
var MethodValueCallCodePtr = methodValueCallCodePtr
var InternalIsZero = isZero
var IsRegularMemory = isRegularMemory

View File

@@ -0,0 +1,23 @@
// Copyright 2021 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.
//go:build !ppc64 && !ppc64le && !riscv64
package reflect
import "unsafe"
// This file implements a straightforward conversion of a float32
// value into its representation in a register. This conversion
// applies for amd64 and arm64. It is also chosen for the case of
// zero argument registers, but is not used.
func archFloat32FromReg(reg uint64) float32 {
i := uint32(reg)
return *(*float32)(unsafe.Pointer(&i))
}
func archFloat32ToReg(val float32) uint64 {
return uint64(*(*uint32)(unsafe.Pointer(&val)))
}

View File

@@ -0,0 +1,30 @@
// Copyright 2021 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.
//go:build ppc64 || ppc64le
#include "textflag.h"
// On PPC64, the float32 becomes a float64
// when loaded in a register, different from
// other platforms. These functions are
// needed to ensure correct conversions on PPC64.
// Convert float32->uint64
TEXT ·archFloat32ToReg(SB),NOSPLIT,$0-16
FMOVS val+0(FP), F1
FMOVD F1, ret+8(FP)
RET
// Convert uint64->float32
TEXT ·archFloat32FromReg(SB),NOSPLIT,$0-12
FMOVD reg+0(FP), F1
// Normally a float64->float32 conversion
// would need rounding, but that is not needed
// here since the uint64 was originally converted
// from float32, and should be avoided to
// preserve SNaN values.
FMOVS F1, ret+8(FP)
RET

View File

@@ -0,0 +1,27 @@
// Copyright 2022 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.
#include "textflag.h"
// riscv64 allows 32-bit floats to live in the bottom
// part of the register, it expects them to be NaN-boxed.
// These functions are needed to ensure correct conversions
// on riscv64.
// Convert float32->uint64
TEXT ·archFloat32ToReg(SB),NOSPLIT,$0-16
MOVF val+0(FP), F1
MOVD F1, ret+8(FP)
RET
// Convert uint64->float32
TEXT ·archFloat32FromReg(SB),NOSPLIT,$0-12
// Normally a float64->float32 conversion
// would need rounding, but riscv64 store valid
// float32 in the lower 32 bits, thus we only need to
// unboxed the NaN-box by store a float32.
MOVD reg+0(FP), F1
MOVF F1, ret+8(FP)
RET

View File

@@ -0,0 +1,10 @@
// Copyright 2021 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 example1
type MyStruct struct {
MyStructs []MyStruct
MyStruct *MyStruct
}

View File

@@ -0,0 +1,10 @@
// Copyright 2021 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 example2
type MyStruct struct {
MyStructs []MyStruct
MyStruct *MyStruct
}

164
src/reflect/iter.go Normal file
View File

@@ -0,0 +1,164 @@
// Copyright 2024 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 reflect
import "iter"
func rangeNum[T int8 | int16 | int32 | int64 | int |
uint8 | uint16 | uint32 | uint64 | uint |
uintptr, N int64 | uint64](v N) iter.Seq[Value] {
return func(yield func(v Value) bool) {
// cannot use range T(v) because no core type.
for i := T(0); i < T(v); i++ {
if !yield(ValueOf(i)) {
return
}
}
}
}
// Seq returns an iter.Seq[Value] that loops over the elements of v.
// If v's kind is Func, it must be a function that has no results and
// that takes a single argument of type func(T) bool for some type T.
// If v's kind is Pointer, the pointer element type must have kind Array.
// Otherwise v's kind must be Int, Int8, Int16, Int32, Int64,
// Uint, Uint8, Uint16, Uint32, Uint64, Uintptr,
// Array, Chan, Map, Slice, or String.
func (v Value) Seq() iter.Seq[Value] {
if canRangeFunc(v.typ()) {
return func(yield func(Value) bool) {
rf := MakeFunc(v.Type().In(0), func(in []Value) []Value {
return []Value{ValueOf(yield(in[0]))}
})
v.Call([]Value{rf})
}
}
switch v.Kind() {
case Int:
return rangeNum[int](v.Int())
case Int8:
return rangeNum[int8](v.Int())
case Int16:
return rangeNum[int16](v.Int())
case Int32:
return rangeNum[int32](v.Int())
case Int64:
return rangeNum[int64](v.Int())
case Uint:
return rangeNum[uint](v.Uint())
case Uint8:
return rangeNum[uint8](v.Uint())
case Uint16:
return rangeNum[uint16](v.Uint())
case Uint32:
return rangeNum[uint32](v.Uint())
case Uint64:
return rangeNum[uint64](v.Uint())
case Uintptr:
return rangeNum[uintptr](v.Uint())
case Pointer:
if v.Elem().kind() != Array {
break
}
return func(yield func(Value) bool) {
v = v.Elem()
for i := range v.Len() {
if !yield(ValueOf(i)) {
return
}
}
}
case Array, Slice:
return func(yield func(Value) bool) {
for i := range v.Len() {
if !yield(ValueOf(i)) {
return
}
}
}
case String:
return func(yield func(Value) bool) {
for i := range v.String() {
if !yield(ValueOf(i)) {
return
}
}
}
case Map:
return func(yield func(Value) bool) {
i := v.MapRange()
for i.Next() {
if !yield(i.Key()) {
return
}
}
}
case Chan:
return func(yield func(Value) bool) {
for value, ok := v.Recv(); ok; value, ok = v.Recv() {
if !yield(value) {
return
}
}
}
}
panic("reflect: " + v.Type().String() + " cannot produce iter.Seq[Value]")
}
// Seq2 returns an iter.Seq2[Value, Value] that loops over the elements of v.
// If v's kind is Func, it must be a function that has no results and
// that takes a single argument of type func(K, V) bool for some type K, V.
// If v's kind is Pointer, the pointer element type must have kind Array.
// Otherwise v's kind must be Array, Map, Slice, or String.
func (v Value) Seq2() iter.Seq2[Value, Value] {
if canRangeFunc2(v.typ()) {
return func(yield func(Value, Value) bool) {
rf := MakeFunc(v.Type().In(0), func(in []Value) []Value {
return []Value{ValueOf(yield(in[0], in[1]))}
})
v.Call([]Value{rf})
}
}
switch v.Kind() {
case Pointer:
if v.Elem().kind() != Array {
break
}
return func(yield func(Value, Value) bool) {
v = v.Elem()
for i := range v.Len() {
if !yield(ValueOf(i), v.Index(i)) {
return
}
}
}
case Array, Slice:
return func(yield func(Value, Value) bool) {
for i := range v.Len() {
if !yield(ValueOf(i), v.Index(i)) {
return
}
}
}
case String:
return func(yield func(Value, Value) bool) {
for i, v := range v.String() {
if !yield(ValueOf(i), ValueOf(v)) {
return
}
}
}
case Map:
return func(yield func(Value, Value) bool) {
i := v.MapRange()
for i.Next() {
if !yield(i.Key(), i.Value()) {
return
}
}
}
}
panic("reflect: " + v.Type().String() + " cannot produce iter.Seq2[Value, Value]")
}

304
src/reflect/iter_test.go Normal file
View File

@@ -0,0 +1,304 @@
// Copyright 2024 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 reflect_test
import (
"iter"
"maps"
. "reflect"
"testing"
)
func TestValueSeq(t *testing.T) {
m := map[string]int{
"1": 1,
"2": 2,
"3": 3,
"4": 4,
}
c := make(chan int, 3)
for i := range 3 {
c <- i
}
close(c)
tests := []struct {
name string
val Value
check func(*testing.T, iter.Seq[Value])
}{
{"int", ValueOf(4), func(t *testing.T, s iter.Seq[Value]) {
i := int64(0)
for v := range s {
if v.Int() != i {
t.Fatalf("got %d, want %d", v.Int(), i)
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"int8", ValueOf(int8(4)), func(t *testing.T, s iter.Seq[Value]) {
i := int8(0)
for v := range s {
if v.Interface().(int8) != i {
t.Fatalf("got %d, want %d", v.Int(), i)
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"uint", ValueOf(uint64(4)), func(t *testing.T, s iter.Seq[Value]) {
i := uint64(0)
for v := range s {
if v.Uint() != i {
t.Fatalf("got %d, want %d", v.Uint(), i)
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"uint8", ValueOf(uint8(4)), func(t *testing.T, s iter.Seq[Value]) {
i := uint8(0)
for v := range s {
if v.Interface().(uint8) != i {
t.Fatalf("got %d, want %d", v.Int(), i)
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"*[4]int", ValueOf(&[4]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq[Value]) {
i := int64(0)
for v := range s {
if v.Int() != i {
t.Fatalf("got %d, want %d", v.Int(), i)
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"[4]int", ValueOf([4]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq[Value]) {
i := int64(0)
for v := range s {
if v.Int() != i {
t.Fatalf("got %d, want %d", v.Int(), i)
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"[]int", ValueOf([]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq[Value]) {
i := int64(0)
for v := range s {
if v.Int() != i {
t.Fatalf("got %d, want %d", v.Int(), i)
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"string", ValueOf("12语言"), func(t *testing.T, s iter.Seq[Value]) {
i := int64(0)
indexs := []int64{0, 1, 2, 5}
for v := range s {
if v.Int() != indexs[i] {
t.Fatalf("got %d, want %d", v.Int(), indexs[i])
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"map[string]int", ValueOf(m), func(t *testing.T, s iter.Seq[Value]) {
i := int64(0)
copy := maps.Clone(m)
for v := range s {
if _, ok := copy[v.String()]; !ok {
t.Fatalf("unexpected %v", v.Interface())
}
delete(copy, v.String())
i++
}
if len(copy) != 0 {
t.Fatalf("should loop four times")
}
}},
{"chan int", ValueOf(c), func(t *testing.T, s iter.Seq[Value]) {
i := 0
m := map[int64]bool{
0: false,
1: false,
2: false,
}
for v := range s {
if b, ok := m[v.Int()]; !ok || b {
t.Fatalf("unexpected %v", v.Interface())
}
m[v.Int()] = true
i++
}
if i != 3 {
t.Fatalf("should loop three times")
}
}},
{"func", ValueOf(func(yield func(int) bool) {
for i := range 4 {
if !yield(i) {
return
}
}
}), func(t *testing.T, s iter.Seq[Value]) {
i := int64(0)
for v := range s {
if v.Int() != i {
t.Fatalf("got %d, want %d", v.Int(), i)
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
}
for _, tc := range tests {
seq := tc.val.Seq()
tc.check(t, seq)
}
}
func TestValueSeq2(t *testing.T) {
m := map[string]int{
"1": 1,
"2": 2,
"3": 3,
"4": 4,
}
tests := []struct {
name string
val Value
check func(*testing.T, iter.Seq2[Value, Value])
}{
{"*[4]int", ValueOf(&[4]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq2[Value, Value]) {
i := int64(0)
for v1, v2 := range s {
if v1.Int() != i {
t.Fatalf("got %d, want %d", v1.Int(), i)
}
i++
if v2.Int() != i {
t.Fatalf("got %d, want %d", v2.Int(), i)
}
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"[4]int", ValueOf([4]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq2[Value, Value]) {
i := int64(0)
for v1, v2 := range s {
if v1.Int() != i {
t.Fatalf("got %d, want %d", v1.Int(), i)
}
i++
if v2.Int() != i {
t.Fatalf("got %d, want %d", v2.Int(), i)
}
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"[]int", ValueOf([]int{1, 2, 3, 4}), func(t *testing.T, s iter.Seq2[Value, Value]) {
i := int64(0)
for v1, v2 := range s {
if v1.Int() != i {
t.Fatalf("got %d, want %d", v1.Int(), i)
}
i++
if v2.Int() != i {
t.Fatalf("got %d, want %d", v2.Int(), i)
}
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"string", ValueOf("12语言"), func(t *testing.T, s iter.Seq2[Value, Value]) {
i := int64(0)
str := "12语言"
next, stop := iter.Pull2(s)
defer stop()
for j, s := range str {
v1, v2, ok := next()
if !ok {
t.Fatalf("should loop four times")
}
if v1.Int() != int64(j) {
t.Fatalf("got %d, want %d", v1.Int(), j)
}
if v2.Interface() != s {
t.Fatalf("got %v, want %v", v2.Interface(), s)
}
i++
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
{"map[string]int", ValueOf(m), func(t *testing.T, s iter.Seq2[Value, Value]) {
copy := maps.Clone(m)
for v1, v2 := range s {
v, ok := copy[v1.String()]
if !ok {
t.Fatalf("unexpected %v", v1.String())
}
if v != v2.Interface() {
t.Fatalf("got %v, want %d", v2.Interface(), v)
}
delete(copy, v1.String())
}
if len(copy) != 0 {
t.Fatalf("should loop four times")
}
}},
{"func", ValueOf(func(f func(int, int) bool) {
for i := range 4 {
f(i, i+1)
}
}), func(t *testing.T, s iter.Seq2[Value, Value]) {
i := int64(0)
for v1, v2 := range s {
if v1.Int() != i {
t.Fatalf("got %d, want %d", v1.Int(), i)
}
i++
if v2.Int() != i {
t.Fatalf("got %d, want %d", v2.Int(), i)
}
}
if i != 4 {
t.Fatalf("should loop four times")
}
}},
}
for _, tc := range tests {
seq := tc.val.Seq2()
tc.check(t, seq)
}
}

176
src/reflect/makefunc.go Normal file
View File

@@ -0,0 +1,176 @@
// Copyright 2012 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.
// MakeFunc implementation.
package reflect
import (
"internal/abi"
"unsafe"
)
// makeFuncImpl is the closure value implementing the function
// returned by MakeFunc.
// The first three words of this type must be kept in sync with
// methodValue and runtime.reflectMethodValue.
// Any changes should be reflected in all three.
type makeFuncImpl struct {
makeFuncCtxt
ftyp *funcType
fn func([]Value) []Value
}
// MakeFunc returns a new function of the given [Type]
// that wraps the function fn. When called, that new function
// does the following:
//
// - converts its arguments to a slice of Values.
// - runs results := fn(args).
// - returns the results as a slice of Values, one per formal result.
//
// The implementation fn can assume that the argument [Value] slice
// has the number and type of arguments given by typ.
// If typ describes a variadic function, the final Value is itself
// a slice representing the variadic arguments, as in the
// body of a variadic function. The result Value slice returned by fn
// must have the number and type of results given by typ.
//
// The [Value.Call] method allows the caller to invoke a typed function
// in terms of Values; in contrast, MakeFunc allows the caller to implement
// a typed function in terms of Values.
//
// The Examples section of the documentation includes an illustration
// of how to use MakeFunc to build a swap function for different types.
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
if typ.Kind() != Func {
panic("reflect: call of MakeFunc with non-Func type")
}
t := typ.common()
ftyp := (*funcType)(unsafe.Pointer(t))
code := abi.FuncPCABI0(makeFuncStub)
// makeFuncImpl contains a stack map for use by the runtime
_, _, abid := funcLayout(ftyp, nil)
impl := &makeFuncImpl{
makeFuncCtxt: makeFuncCtxt{
fn: code,
stack: abid.stackPtrs,
argLen: abid.stackCallArgsSize,
regPtrs: abid.inRegPtrs,
},
ftyp: ftyp,
fn: fn,
}
return Value{t, unsafe.Pointer(impl), flag(Func)}
}
// makeFuncStub is an assembly function that is the code half of
// the function returned from MakeFunc. It expects a *callReflectFunc
// as its context register, and its job is to invoke callReflect(ctxt, frame)
// where ctxt is the context register and frame is a pointer to the first
// word in the passed-in argument frame.
func makeFuncStub()
// The first 3 words of this type must be kept in sync with
// makeFuncImpl and runtime.reflectMethodValue.
// Any changes should be reflected in all three.
type methodValue struct {
makeFuncCtxt
method int
rcvr Value
}
// makeMethodValue converts v from the rcvr+method index representation
// of a method value to an actual method func value, which is
// basically the receiver value with a special bit set, into a true
// func value - a value holding an actual func. The output is
// semantically equivalent to the input as far as the user of package
// reflect can tell, but the true func representation can be handled
// by code like Convert and Interface and Assign.
func makeMethodValue(op string, v Value) Value {
if v.flag&flagMethod == 0 {
panic("reflect: internal error: invalid use of makeMethodValue")
}
// Ignoring the flagMethod bit, v describes the receiver, not the method type.
fl := v.flag & (flagRO | flagAddr | flagIndir)
fl |= flag(v.typ().Kind())
rcvr := Value{v.typ(), v.ptr, fl}
// v.Type returns the actual type of the method value.
ftyp := (*funcType)(unsafe.Pointer(v.Type().(*rtype)))
code := methodValueCallCodePtr()
// methodValue contains a stack map for use by the runtime
_, _, abid := funcLayout(ftyp, nil)
fv := &methodValue{
makeFuncCtxt: makeFuncCtxt{
fn: code,
stack: abid.stackPtrs,
argLen: abid.stackCallArgsSize,
regPtrs: abid.inRegPtrs,
},
method: int(v.flag) >> flagMethodShift,
rcvr: rcvr,
}
// Cause panic if method is not appropriate.
// The panic would still happen during the call if we omit this,
// but we want Interface() and other operations to fail early.
methodReceiver(op, fv.rcvr, fv.method)
return Value{ftyp.Common(), unsafe.Pointer(fv), v.flag&flagRO | flag(Func)}
}
func methodValueCallCodePtr() uintptr {
return abi.FuncPCABI0(methodValueCall)
}
// methodValueCall is an assembly function that is the code half of
// the function returned from makeMethodValue. It expects a *methodValue
// as its context register, and its job is to invoke callMethod(ctxt, frame)
// where ctxt is the context register and frame is a pointer to the first
// word in the passed-in argument frame.
func methodValueCall()
// This structure must be kept in sync with runtime.reflectMethodValue.
// Any changes should be reflected in all both.
type makeFuncCtxt struct {
fn uintptr
stack *bitVector // ptrmap for both stack args and results
argLen uintptr // just args
regPtrs abi.IntArgRegBitmap
}
// moveMakeFuncArgPtrs uses ctxt.regPtrs to copy integer pointer arguments
// in args.Ints to args.Ptrs where the GC can see them.
//
// This is similar to what reflectcallmove does in the runtime, except
// that happens on the return path, whereas this happens on the call path.
//
// nosplit because pointers are being held in uintptr slots in args, so
// having our stack scanned now could lead to accidentally freeing
// memory.
//
//go:nosplit
func moveMakeFuncArgPtrs(ctxt *makeFuncCtxt, args *abi.RegArgs) {
for i, arg := range args.Ints {
// Avoid write barriers! Because our write barrier enqueues what
// was there before, we might enqueue garbage.
if ctxt.regPtrs.Get(i) {
*(*uintptr)(unsafe.Pointer(&args.Ptrs[i])) = arg
} else {
// We *must* zero this space ourselves because it's defined in
// assembly code and the GC will scan these pointers. Otherwise,
// there will be garbage here.
*(*uintptr)(unsafe.Pointer(&args.Ptrs[i])) = 0
}
}
}

38
src/reflect/nih_test.go Normal file
View File

@@ -0,0 +1,38 @@
// 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.
//go:build cgo
package reflect_test
import (
. "reflect"
"runtime/cgo"
"testing"
"unsafe"
)
type nih struct {
_ cgo.Incomplete
x int
}
var global_nih = nih{x: 7}
func TestNotInHeapDeref(t *testing.T) {
// See issue 48399.
v := ValueOf((*nih)(nil))
v.Elem()
shouldPanic("reflect: call of reflect.Value.Field on zero Value", func() { v.Elem().Field(0) })
v = ValueOf(&global_nih)
if got := v.Elem().Field(1).Int(); got != 7 {
t.Fatalf("got %d, want 7", got)
}
v = ValueOf((*nih)(unsafe.Pointer(new(int))))
shouldPanic("reflect: reflect.Value.Elem on an invalid notinheap pointer", func() { v.Elem() })
shouldPanic("reflect: reflect.Value.Pointer on an invalid notinheap pointer", func() { v.Pointer() })
shouldPanic("reflect: reflect.Value.UnsafePointer on an invalid notinheap pointer", func() { v.UnsafePointer() })
}

227
src/reflect/set_test.go Normal file
View File

@@ -0,0 +1,227 @@
// Copyright 2011 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 reflect_test
import (
"bytes"
"go/ast"
"go/token"
"io"
. "reflect"
"strings"
"testing"
"unsafe"
)
func TestImplicitMapConversion(t *testing.T) {
// Test implicit conversions in MapIndex and SetMapIndex.
{
// direct
m := make(map[int]int)
mv := ValueOf(m)
mv.SetMapIndex(ValueOf(1), ValueOf(2))
x, ok := m[1]
if x != 2 {
t.Errorf("#1 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
}
if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
t.Errorf("#1 MapIndex(1) = %d", n)
}
}
{
// convert interface key
m := make(map[any]int)
mv := ValueOf(m)
mv.SetMapIndex(ValueOf(1), ValueOf(2))
x, ok := m[1]
if x != 2 {
t.Errorf("#2 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
}
if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
t.Errorf("#2 MapIndex(1) = %d", n)
}
}
{
// convert interface value
m := make(map[int]any)
mv := ValueOf(m)
mv.SetMapIndex(ValueOf(1), ValueOf(2))
x, ok := m[1]
if x != 2 {
t.Errorf("#3 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
}
if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
t.Errorf("#3 MapIndex(1) = %d", n)
}
}
{
// convert both interface key and interface value
m := make(map[any]any)
mv := ValueOf(m)
mv.SetMapIndex(ValueOf(1), ValueOf(2))
x, ok := m[1]
if x != 2 {
t.Errorf("#4 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
}
if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
t.Errorf("#4 MapIndex(1) = %d", n)
}
}
{
// convert both, with non-empty interfaces
m := make(map[io.Reader]io.Writer)
mv := ValueOf(m)
b1 := new(bytes.Buffer)
b2 := new(bytes.Buffer)
mv.SetMapIndex(ValueOf(b1), ValueOf(b2))
x, ok := m[b1]
if x != b2 {
t.Errorf("#5 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
}
if p := mv.MapIndex(ValueOf(b1)).Elem().UnsafePointer(); p != unsafe.Pointer(b2) {
t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2)
}
}
{
// convert channel direction
m := make(map[<-chan int]chan int)
mv := ValueOf(m)
c1 := make(chan int)
c2 := make(chan int)
mv.SetMapIndex(ValueOf(c1), ValueOf(c2))
x, ok := m[c1]
if x != c2 {
t.Errorf("#6 after SetMapIndex(c1, c2): %p (!= %p), %t (map=%v)", x, c2, ok, m)
}
if p := mv.MapIndex(ValueOf(c1)).UnsafePointer(); p != ValueOf(c2).UnsafePointer() {
t.Errorf("#6 MapIndex(c1) = %#x want %p", p, c2)
}
}
{
// convert identical underlying types
type MyBuffer bytes.Buffer
m := make(map[*MyBuffer]*bytes.Buffer)
mv := ValueOf(m)
b1 := new(MyBuffer)
b2 := new(bytes.Buffer)
mv.SetMapIndex(ValueOf(b1), ValueOf(b2))
x, ok := m[b1]
if x != b2 {
t.Errorf("#7 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
}
if p := mv.MapIndex(ValueOf(b1)).UnsafePointer(); p != unsafe.Pointer(b2) {
t.Errorf("#7 MapIndex(b1) = %#x want %p", p, b2)
}
}
}
func TestImplicitSetConversion(t *testing.T) {
// Assume TestImplicitMapConversion covered the basics.
// Just make sure conversions are being applied at all.
var r io.Reader
b := new(bytes.Buffer)
rv := ValueOf(&r).Elem()
rv.Set(ValueOf(b))
if r != b {
t.Errorf("after Set: r=%T(%v)", r, r)
}
}
func TestImplicitSendConversion(t *testing.T) {
c := make(chan io.Reader, 10)
b := new(bytes.Buffer)
ValueOf(c).Send(ValueOf(b))
if bb := <-c; bb != b {
t.Errorf("Received %p != %p", bb, b)
}
}
func TestImplicitCallConversion(t *testing.T) {
// Arguments must be assignable to parameter types.
fv := ValueOf(io.WriteString)
b := new(strings.Builder)
fv.Call([]Value{ValueOf(b), ValueOf("hello world")})
if b.String() != "hello world" {
t.Errorf("After call: string=%q want %q", b.String(), "hello world")
}
}
func TestImplicitAppendConversion(t *testing.T) {
// Arguments must be assignable to the slice's element type.
s := []io.Reader{}
sv := ValueOf(&s).Elem()
b := new(bytes.Buffer)
sv.Set(Append(sv, ValueOf(b)))
if len(s) != 1 || s[0] != b {
t.Errorf("after append: s=%v want [%p]", s, b)
}
}
var implementsTests = []struct {
x any
t any
b bool
}{
{new(*bytes.Buffer), new(io.Reader), true},
{new(bytes.Buffer), new(io.Reader), false},
{new(*bytes.Buffer), new(io.ReaderAt), false},
{new(*ast.Ident), new(ast.Expr), true},
{new(*notAnExpr), new(ast.Expr), false},
{new(*ast.Ident), new(notASTExpr), false},
{new(notASTExpr), new(ast.Expr), false},
{new(ast.Expr), new(notASTExpr), false},
{new(*notAnExpr), new(notASTExpr), true},
}
type notAnExpr struct{}
func (notAnExpr) Pos() token.Pos { return token.NoPos }
func (notAnExpr) End() token.Pos { return token.NoPos }
func (notAnExpr) exprNode() {}
type notASTExpr interface {
Pos() token.Pos
End() token.Pos
exprNode()
}
func TestImplements(t *testing.T) {
for _, tt := range implementsTests {
xv := TypeOf(tt.x).Elem()
xt := TypeOf(tt.t).Elem()
if b := xv.Implements(xt); b != tt.b {
t.Errorf("(%s).Implements(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
}
}
}
var assignableTests = []struct {
x any
t any
b bool
}{
{new(chan int), new(<-chan int), true},
{new(<-chan int), new(chan int), false},
{new(*int), new(IntPtr), true},
{new(IntPtr), new(*int), true},
{new(IntPtr), new(IntPtr1), false},
{new(Ch), new(<-chan any), true},
// test runs implementsTests too
}
type IntPtr *int
type IntPtr1 *int
type Ch <-chan any
func TestAssignableTo(t *testing.T) {
for _, tt := range append(assignableTests, implementsTests...) {
xv := TypeOf(tt.x).Elem()
xt := TypeOf(tt.t).Elem()
if b := xv.AssignableTo(xt); b != tt.b {
t.Errorf("(%s).AssignableTo(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
}
}
}

View File

@@ -0,0 +1,10 @@
// Copyright 2021 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.
//go:build ppc64le || ppc64
package reflect
func archFloat32FromReg(reg uint64) float32
func archFloat32ToReg(val float32) uint64

View File

@@ -0,0 +1,8 @@
// Copyright 2022 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 reflect
func archFloat32FromReg(reg uint64) float32
func archFloat32ToReg(val float32) uint64

79
src/reflect/swapper.go Normal file
View File

@@ -0,0 +1,79 @@
// Copyright 2016 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 reflect
import (
"internal/abi"
"internal/goarch"
"internal/unsafeheader"
"unsafe"
)
// Swapper returns a function that swaps the elements in the provided
// slice.
//
// Swapper panics if the provided interface is not a slice.
func Swapper(slice any) func(i, j int) {
v := ValueOf(slice)
if v.Kind() != Slice {
panic(&ValueError{Method: "Swapper", Kind: v.Kind()})
}
// Fast path for slices of size 0 and 1. Nothing to swap.
switch v.Len() {
case 0:
return func(i, j int) { panic("reflect: slice index out of range") }
case 1:
return func(i, j int) {
if i != 0 || j != 0 {
panic("reflect: slice index out of range")
}
}
}
typ := v.Type().Elem().common()
size := typ.Size()
hasPtr := typ.Pointers()
// Some common & small cases, without using memmove:
if hasPtr {
if size == goarch.PtrSize {
ps := *(*[]unsafe.Pointer)(v.ptr)
return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
}
if typ.Kind() == abi.String {
ss := *(*[]string)(v.ptr)
return func(i, j int) { ss[i], ss[j] = ss[j], ss[i] }
}
} else {
switch size {
case 8:
is := *(*[]int64)(v.ptr)
return func(i, j int) { is[i], is[j] = is[j], is[i] }
case 4:
is := *(*[]int32)(v.ptr)
return func(i, j int) { is[i], is[j] = is[j], is[i] }
case 2:
is := *(*[]int16)(v.ptr)
return func(i, j int) { is[i], is[j] = is[j], is[i] }
case 1:
is := *(*[]int8)(v.ptr)
return func(i, j int) { is[i], is[j] = is[j], is[i] }
}
}
s := (*unsafeheader.Slice)(v.ptr)
tmp := unsafe_New(typ) // swap scratch space
return func(i, j int) {
if uint(i) >= uint(s.Len) || uint(j) >= uint(s.Len) {
panic("reflect: slice index out of range")
}
val1 := arrayAt(s.Data, i, size, "i < s.Len")
val2 := arrayAt(s.Data, j, size, "j < s.Len")
typedmemmove(typ, tmp, val1)
typedmemmove(typ, val1, val2)
typedmemmove(typ, val2, tmp)
}
}

View File

@@ -0,0 +1,95 @@
// 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.
// Formatting of reflection types and values for debugging.
// Not defined as methods so they do not need to be linked into most binaries;
// the functions are not used by the library itself, only in tests.
package reflect_test
import (
. "reflect"
"strconv"
)
// valueToString returns a textual representation of the reflection value val.
// For debugging only.
func valueToString(val Value) string {
var str string
if !val.IsValid() {
return "<zero Value>"
}
typ := val.Type()
switch val.Kind() {
case Int, Int8, Int16, Int32, Int64:
return strconv.FormatInt(val.Int(), 10)
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return strconv.FormatUint(val.Uint(), 10)
case Float32, Float64:
return strconv.FormatFloat(val.Float(), 'g', -1, 64)
case Complex64, Complex128:
c := val.Complex()
return strconv.FormatFloat(real(c), 'g', -1, 64) + "+" + strconv.FormatFloat(imag(c), 'g', -1, 64) + "i"
case String:
return val.String()
case Bool:
if val.Bool() {
return "true"
} else {
return "false"
}
case Pointer:
v := val
str = typ.String() + "("
if v.IsNil() {
str += "0"
} else {
str += "&" + valueToString(v.Elem())
}
str += ")"
return str
case Array, Slice:
v := val
str += typ.String()
str += "{"
for i := 0; i < v.Len(); i++ {
if i > 0 {
str += ", "
}
str += valueToString(v.Index(i))
}
str += "}"
return str
case Map:
t := typ
str = t.String()
str += "{"
str += "<can't iterate on maps>"
str += "}"
return str
case Chan:
str = typ.String()
return str
case Struct:
t := typ
v := val
str += t.String()
str += "{"
for i, n := 0, v.NumField(); i < n; i++ {
if i > 0 {
str += ", "
}
str += valueToString(v.Field(i))
}
str += "}"
return str
case Interface:
return typ.String() + "(" + valueToString(val.Elem()) + ")"
case Func:
v := val
return typ.String() + "(" + strconv.FormatUint(uint64(v.Pointer()), 10) + ")"
default:
panic("valueToString: can't print type " + typ.String())
}
}

3086
src/reflect/type.go Normal file

File diff suppressed because it is too large Load Diff

169
src/reflect/type_test.go Normal file
View File

@@ -0,0 +1,169 @@
// Copyright 2023 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 reflect_test
import (
"reflect"
"testing"
)
func TestTypeFor(t *testing.T) {
type (
mystring string
myiface interface{}
)
testcases := []struct {
wantFrom any
got reflect.Type
}{
{new(int), reflect.TypeFor[int]()},
{new(int64), reflect.TypeFor[int64]()},
{new(string), reflect.TypeFor[string]()},
{new(mystring), reflect.TypeFor[mystring]()},
{new(any), reflect.TypeFor[any]()},
{new(myiface), reflect.TypeFor[myiface]()},
}
for _, tc := range testcases {
want := reflect.ValueOf(tc.wantFrom).Elem().Type()
if want != tc.got {
t.Errorf("unexpected reflect.Type: got %v; want %v", tc.got, want)
}
}
}
func TestStructOfEmbeddedIfaceMethodCall(t *testing.T) {
type Named interface {
Name() string
}
typ := reflect.StructOf([]reflect.StructField{
{
Anonymous: true,
Name: "Named",
Type: reflect.TypeFor[Named](),
},
})
v := reflect.New(typ).Elem()
v.Field(0).Set(
reflect.ValueOf(reflect.TypeFor[string]()),
)
x := v.Interface().(Named)
shouldPanic("StructOf does not support methods of embedded interfaces", func() {
_ = x.Name()
})
}
func TestIsRegularMemory(t *testing.T) {
type args struct {
t reflect.Type
}
type S struct {
int
}
tests := []struct {
name string
args args
want bool
}{
{"struct{i int}", args{reflect.TypeOf(struct{ i int }{})}, true},
{"struct{}", args{reflect.TypeOf(struct{}{})}, true},
{"struct{i int; s S}", args{reflect.TypeOf(struct {
i int
s S
}{})}, true},
{"map[int][int]", args{reflect.TypeOf(map[int]int{})}, false},
{"[4]chan int", args{reflect.TypeOf([4]chan int{})}, true},
{"[0]struct{_ S}", args{reflect.TypeOf([0]struct {
_ S
}{})}, true},
{"struct{i int; _ S}", args{reflect.TypeOf(struct {
i int
_ S
}{})}, false},
{"struct{a int16; b int32}", args{reflect.TypeOf(struct {
a int16
b int32
}{})}, false},
{"struct {x int32; y int16}", args{reflect.TypeOf(struct {
x int32
y int16
}{})}, false},
{"struct {_ int32 }", args{reflect.TypeOf(struct{ _ int32 }{})}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := reflect.IsRegularMemory(tt.args.t); got != tt.want {
t.Errorf("isRegularMemory() = %v, want %v", got, tt.want)
}
})
}
}
var sinkType reflect.Type
func BenchmarkTypeForString(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkType = reflect.TypeFor[string]()
}
}
func BenchmarkTypeForError(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkType = reflect.TypeFor[error]()
}
}
func TestType_CanSeq(t *testing.T) {
tests := []struct {
name string
tr reflect.Type
want bool
}{
{"func(func(int) bool)", reflect.TypeOf(func(func(int) bool) {}), true},
{"func(func(int))", reflect.TypeOf(func(func(int)) {}), false},
{"int64", reflect.TypeOf(int64(1)), true},
{"uint64", reflect.TypeOf(uint64(1)), true},
{"*[4]int", reflect.TypeOf(&[4]int{}), true},
{"chan int64", reflect.TypeOf(make(chan int64)), true},
{"map[int]int", reflect.TypeOf(make(map[int]int)), true},
{"string", reflect.TypeOf(""), true},
{"[]int", reflect.TypeOf([]int{}), true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.tr.CanSeq(); got != tt.want {
t.Errorf("Type.CanSeq() = %v, want %v", got, tt.want)
}
})
}
}
func TestType_CanSeq2(t *testing.T) {
tests := []struct {
name string
tr reflect.Type
want bool
}{
{"func(func(int, int) bool)", reflect.TypeOf(func(func(int, int) bool) {}), true},
{"func(func(int, int))", reflect.TypeOf(func(func(int, int)) {}), false},
{"int64", reflect.TypeOf(int64(1)), false},
{"uint64", reflect.TypeOf(uint64(1)), false},
{"*[4]int", reflect.TypeOf(&[4]int{}), true},
{"chan int64", reflect.TypeOf(make(chan int64)), false},
{"map[int]int", reflect.TypeOf(make(map[int]int)), true},
{"string", reflect.TypeOf(""), true},
{"[]int", reflect.TypeOf([]int{}), true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.tr.CanSeq2(); got != tt.want {
t.Errorf("Type.CanSeq2() = %v, want %v", got, tt.want)
}
})
}
}

4030
src/reflect/value.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,105 @@
// Copyright 2021 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 reflect
// VisibleFields returns all the visible fields in t, which must be a
// struct type. A field is defined as visible if it's accessible
// directly with a FieldByName call. The returned fields include fields
// inside anonymous struct members and unexported fields. They follow
// the same order found in the struct, with anonymous fields followed
// immediately by their promoted fields.
//
// For each element e of the returned slice, the corresponding field
// can be retrieved from a value v of type t by calling v.FieldByIndex(e.Index).
func VisibleFields(t Type) []StructField {
if t == nil {
panic("reflect: VisibleFields(nil)")
}
if t.Kind() != Struct {
panic("reflect.VisibleFields of non-struct type")
}
w := &visibleFieldsWalker{
byName: make(map[string]int),
visiting: make(map[Type]bool),
fields: make([]StructField, 0, t.NumField()),
index: make([]int, 0, 2),
}
w.walk(t)
// Remove all the fields that have been hidden.
// Use an in-place removal that avoids copying in
// the common case that there are no hidden fields.
j := 0
for i := range w.fields {
f := &w.fields[i]
if f.Name == "" {
continue
}
if i != j {
// A field has been removed. We need to shuffle
// all the subsequent elements up.
w.fields[j] = *f
}
j++
}
return w.fields[:j]
}
type visibleFieldsWalker struct {
byName map[string]int
visiting map[Type]bool
fields []StructField
index []int
}
// walk walks all the fields in the struct type t, visiting
// fields in index preorder and appending them to w.fields
// (this maintains the required ordering).
// Fields that have been overridden have their
// Name field cleared.
func (w *visibleFieldsWalker) walk(t Type) {
if w.visiting[t] {
return
}
w.visiting[t] = true
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
w.index = append(w.index, i)
add := true
if oldIndex, ok := w.byName[f.Name]; ok {
old := &w.fields[oldIndex]
if len(w.index) == len(old.Index) {
// Fields with the same name at the same depth
// cancel one another out. Set the field name
// to empty to signify that has happened, and
// there's no need to add this field.
old.Name = ""
add = false
} else if len(w.index) < len(old.Index) {
// The old field loses because it's deeper than the new one.
old.Name = ""
} else {
// The old field wins because it's shallower than the new one.
add = false
}
}
if add {
// Copy the index so that it's not overwritten
// by the other appends.
f.Index = append([]int(nil), w.index...)
w.byName[f.Name] = len(w.fields)
w.fields = append(w.fields, f)
}
if f.Anonymous {
if f.Type.Kind() == Pointer {
f.Type = f.Type.Elem()
}
if f.Type.Kind() == Struct {
w.walk(f.Type)
}
}
w.index = w.index[:len(w.index)-1]
}
delete(w.visiting, t)
}

View File

@@ -0,0 +1,349 @@
// Copyright 2021 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 reflect_test
import (
. "reflect"
"strings"
"testing"
)
type structField struct {
name string
index []int
}
var fieldsTests = []struct {
testName string
val any
expect []structField
}{{
testName: "SimpleStruct",
val: struct {
A int
B string
C bool
}{},
expect: []structField{{
name: "A",
index: []int{0},
}, {
name: "B",
index: []int{1},
}, {
name: "C",
index: []int{2},
}},
}, {
testName: "NonEmbeddedStructMember",
val: struct {
A struct {
X int
}
}{},
expect: []structField{{
name: "A",
index: []int{0},
}},
}, {
testName: "EmbeddedExportedStruct",
val: struct {
SFG
}{},
expect: []structField{{
name: "SFG",
index: []int{0},
}, {
name: "F",
index: []int{0, 0},
}, {
name: "G",
index: []int{0, 1},
}},
}, {
testName: "EmbeddedUnexportedStruct",
val: struct {
sFG
}{},
expect: []structField{{
name: "sFG",
index: []int{0},
}, {
name: "F",
index: []int{0, 0},
}, {
name: "G",
index: []int{0, 1},
}},
}, {
testName: "TwoEmbeddedStructsWithCancelingMembers",
val: struct {
SFG
SF
}{},
expect: []structField{{
name: "SFG",
index: []int{0},
}, {
name: "G",
index: []int{0, 1},
}, {
name: "SF",
index: []int{1},
}},
}, {
testName: "EmbeddedStructsWithSameFieldsAtDifferentDepths",
val: struct {
SFGH3
SG1
SFG2
SF2
L int
}{},
expect: []structField{{
name: "SFGH3",
index: []int{0},
}, {
name: "SFGH2",
index: []int{0, 0},
}, {
name: "SFGH1",
index: []int{0, 0, 0},
}, {
name: "SFGH",
index: []int{0, 0, 0, 0},
}, {
name: "H",
index: []int{0, 0, 0, 0, 2},
}, {
name: "SG1",
index: []int{1},
}, {
name: "SG",
index: []int{1, 0},
}, {
name: "G",
index: []int{1, 0, 0},
}, {
name: "SFG2",
index: []int{2},
}, {
name: "SFG1",
index: []int{2, 0},
}, {
name: "SFG",
index: []int{2, 0, 0},
}, {
name: "SF2",
index: []int{3},
}, {
name: "SF1",
index: []int{3, 0},
}, {
name: "SF",
index: []int{3, 0, 0},
}, {
name: "L",
index: []int{4},
}},
}, {
testName: "EmbeddedPointerStruct",
val: struct {
*SF
}{},
expect: []structField{{
name: "SF",
index: []int{0},
}, {
name: "F",
index: []int{0, 0},
}},
}, {
testName: "EmbeddedNotAPointer",
val: struct {
M
}{},
expect: []structField{{
name: "M",
index: []int{0},
}},
}, {
testName: "RecursiveEmbedding",
val: Rec1{},
expect: []structField{{
name: "Rec2",
index: []int{0},
}, {
name: "F",
index: []int{0, 0},
}, {
name: "Rec1",
index: []int{0, 1},
}},
}, {
testName: "RecursiveEmbedding2",
val: Rec2{},
expect: []structField{{
name: "F",
index: []int{0},
}, {
name: "Rec1",
index: []int{1},
}, {
name: "Rec2",
index: []int{1, 0},
}},
}, {
testName: "RecursiveEmbedding3",
val: RS3{},
expect: []structField{{
name: "RS2",
index: []int{0},
}, {
name: "RS1",
index: []int{1},
}, {
name: "i",
index: []int{1, 0},
}},
}}
type SFG struct {
F int
G int
}
type SFG1 struct {
SFG
}
type SFG2 struct {
SFG1
}
type SFGH struct {
F int
G int
H int
}
type SFGH1 struct {
SFGH
}
type SFGH2 struct {
SFGH1
}
type SFGH3 struct {
SFGH2
}
type SF struct {
F int
}
type SF1 struct {
SF
}
type SF2 struct {
SF1
}
type SG struct {
G int
}
type SG1 struct {
SG
}
type sFG struct {
F int
G int
}
type RS1 struct {
i int
}
type RS2 struct {
RS1
}
type RS3 struct {
RS2
RS1
}
type M map[string]any
type Rec1 struct {
*Rec2
}
type Rec2 struct {
F string
*Rec1
}
func TestFields(t *testing.T) {
for _, test := range fieldsTests {
test := test
t.Run(test.testName, func(t *testing.T) {
typ := TypeOf(test.val)
fields := VisibleFields(typ)
if got, want := len(fields), len(test.expect); got != want {
t.Fatalf("unexpected field count; got %d want %d", got, want)
}
for j, field := range fields {
expect := test.expect[j]
t.Logf("field %d: %s", j, expect.name)
gotField := typ.FieldByIndex(field.Index)
// Unfortunately, FieldByIndex does not return
// a field with the same index that we passed in,
// so we set it to the expected value so that
// it can be compared later with the result of FieldByName.
gotField.Index = field.Index
expectField := typ.FieldByIndex(expect.index)
// ditto.
expectField.Index = expect.index
if !DeepEqual(gotField, expectField) {
t.Fatalf("unexpected field result\ngot %#v\nwant %#v", gotField, expectField)
}
// Sanity check that we can actually access the field by the
// expected name.
gotField1, ok := typ.FieldByName(expect.name)
if !ok {
t.Fatalf("field %q not accessible by name", expect.name)
}
if !DeepEqual(gotField1, expectField) {
t.Fatalf("unexpected FieldByName result; got %#v want %#v", gotField1, expectField)
}
}
})
}
}
// Must not panic with nil embedded pointer.
func TestFieldByIndexErr(t *testing.T) {
type A struct {
S string
}
type B struct {
*A
}
v := ValueOf(B{})
_, err := v.FieldByIndexErr([]int{0, 0})
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "embedded struct field A") {
t.Fatal(err)
}
}