Initial commit: Go 1.23 release state
This commit is contained in:
510
src/reflect/abi.go
Normal file
510
src/reflect/abi.go
Normal 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
989
src/reflect/abi_test.go
Normal 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
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
18
src/reflect/arena.go
Normal 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
38
src/reflect/asm_386.s
Normal 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
79
src/reflect/asm_amd64.s
Normal 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
42
src/reflect/asm_arm.s
Normal 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
79
src/reflect/asm_arm64.s
Normal 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
79
src/reflect/asm_loong64.s
Normal 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
42
src/reflect/asm_mips64x.s
Normal 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
42
src/reflect/asm_mipsx.s
Normal 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
83
src/reflect/asm_ppc64x.s
Normal 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
76
src/reflect/asm_riscv64.s
Normal 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
38
src/reflect/asm_s390x.s
Normal 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
52
src/reflect/asm_wasm.s
Normal 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
130
src/reflect/badlinkname.go
Normal 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
|
||||
428
src/reflect/benchmark_test.go
Normal file
428
src/reflect/benchmark_test.go
Normal 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
239
src/reflect/deepequal.go
Normal 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
209
src/reflect/example_test.go
Normal 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
170
src/reflect/export_test.go
Normal 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
|
||||
23
src/reflect/float32reg_generic.go
Normal file
23
src/reflect/float32reg_generic.go
Normal 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)))
|
||||
}
|
||||
30
src/reflect/float32reg_ppc64x.s
Normal file
30
src/reflect/float32reg_ppc64x.s
Normal 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
|
||||
|
||||
27
src/reflect/float32reg_riscv64.s
Normal file
27
src/reflect/float32reg_riscv64.s
Normal 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
|
||||
|
||||
10
src/reflect/internal/example1/example.go
Normal file
10
src/reflect/internal/example1/example.go
Normal 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
|
||||
}
|
||||
10
src/reflect/internal/example2/example.go
Normal file
10
src/reflect/internal/example2/example.go
Normal 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
164
src/reflect/iter.go
Normal 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
304
src/reflect/iter_test.go
Normal 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
176
src/reflect/makefunc.go
Normal 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
38
src/reflect/nih_test.go
Normal 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
227
src/reflect/set_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/reflect/stubs_ppc64x.go
Normal file
10
src/reflect/stubs_ppc64x.go
Normal 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
|
||||
8
src/reflect/stubs_riscv64.go
Normal file
8
src/reflect/stubs_riscv64.go
Normal 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
79
src/reflect/swapper.go
Normal 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)
|
||||
}
|
||||
}
|
||||
95
src/reflect/tostring_test.go
Normal file
95
src/reflect/tostring_test.go
Normal 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
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
169
src/reflect/type_test.go
Normal 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
4030
src/reflect/value.go
Normal file
File diff suppressed because it is too large
Load Diff
105
src/reflect/visiblefields.go
Normal file
105
src/reflect/visiblefields.go
Normal 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)
|
||||
}
|
||||
349
src/reflect/visiblefields_test.go
Normal file
349
src/reflect/visiblefields_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user