forked from razzie/mock
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathicall.go
208 lines (198 loc) · 6.07 KB
/
icall.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package mock
import (
"reflect"
"runtime"
"unsafe"
"github.com/stretchr/testify/mock"
)
type icallBase []*methodInfo
type methodInfo struct {
typ reflect.Type
name string
abiDesc abiDesc
inTypes []reflect.Type
outTypes []reflect.Type
}
func newMethodInfo(typ reflect.Type, method reflect.Method) *methodInfo {
inTypes, outTypes := getMethodInOutTypes(method)
return &methodInfo{
typ: typ,
name: method.Name,
abiDesc: newAbiDesc(method.Type, typ),
inTypes: inTypes,
outTypes: outTypes,
}
}
func icall(index int, recv unsafe.Pointer, intRegs []unsafe.Pointer, floatRegs []float64, frame unsafe.Pointer) (returnIntRegs [intArgRegs]unsafe.Pointer, returnFloatRegs [floatArgRegs]float64) {
receiver := reflect.NewAt(reflect.TypeFor[icallBase](), recv).Elem()
info := receiver.Interface().(icallBase)[index]
receiver = reflect.NewAt(info.typ, recv).Elem()
mock := receiver.Field(1).Interface().(*mock.Mock)
ptr := frame
var in []any
for i, typ := range info.inTypes {
if typ.Size() == 0 {
in = append(in, reflect.Zero(typ).Interface())
continue
}
var v reflect.Value
steps := info.abiDesc.call.stepsForValue(i + 1) // skip receiver
if st := steps[0]; st.kind == abiStepStack {
if toAbiType(typ).ifaceIndir() {
// value cannot be inlined in interface data.
// Must make a copy, because f might keep a reference to it,
// and we cannot let f keep a reference to the stack frame
// after this function returns, not even a read-only reference.
v = reflect.New(typ).Elem()
v_ := reflect.NewAt(typ, unsafe.Add(ptr, st.stkOff)).Elem()
v.Set(v_)
} else {
v = reflect.NewAt(typ, *(*unsafe.Pointer)(unsafe.Add(ptr, st.stkOff))).Elem()
}
} else {
if toAbiType(typ).ifaceIndir() {
// All that's left is values passed in registers that we need to
// create space for the values.
vptr := unsafe_New(typ)
v = reflect.NewAt(typ, vptr).Elem()
for _, st := range steps {
switch st.kind {
case abiStepIntReg:
offset := unsafe.Add(vptr, st.offset)
regPtr := unsafe.Pointer(&intRegs[st.ireg])
if bigEndian {
regPtr = unsafe.Add(regPtr, ptrSize-st.size)
}
memmove(offset, regPtr, st.size)
case abiStepPointer:
s := unsafe.Add(vptr, st.offset)
*((*unsafe.Pointer)(s)) = intRegs[st.ireg]
case abiStepFloatReg:
offset := unsafe.Add(vptr, st.offset)
switch st.size {
case 4:
*(*float32)(offset) = *(*float32)(unsafe.Pointer(&floatRegs[st.freg]))
case 8:
*(*float64)(offset) = floatRegs[st.freg]
default:
panic("bad st.size")
}
case abiStepStack:
panic("register-based return value has stack component")
default:
panic("unknown ABI part kind")
}
}
} else {
// Pointer-valued data gets put directly
// into v.ptr.
if steps[0].kind != abiStepPointer {
print("kind=", steps[0].kind, ", type=", typ.Name(), "\n")
panic("mismatch between ABI description and types")
}
v = reflect.NewAt(typ, unsafe.Pointer(&intRegs[steps[0].ireg])).Elem()
}
}
if v.IsZero() {
in = append(in, nil)
} else {
in = append(in, v.Interface())
}
}
out := mock.MethodCalled(info.name, in...)
if len(out) != len(info.outTypes) {
panic("mock: wrong return count from function: " + info.name)
}
for i, typ := range info.outTypes {
if typ.Size() == 0 {
continue
}
vptr := unsafe_New(typ)
v := reflect.NewAt(typ, vptr).Elem()
if out[i] != nil {
v.Set(reflect.ValueOf(out[i]))
}
steps := info.abiDesc.ret.stepsForValue(i)
if st := steps[0]; st.kind == abiStepStack {
// Copy values to the "stack."
addr := unsafe.Add(ptr, st.stkOff)
// Do not use write barriers. The stack space used
// for this call is not adequately zeroed, and we
// are careful to keep the arguments alive until we
// return to makeFuncStub's caller.
if toAbiType(typ).ifaceIndir() {
memmove(addr, vptr, st.size)
} else {
*(*unsafe.Pointer)(addr) = vptr
}
} else {
for _, st := range steps {
switch st.kind {
case abiStepIntReg, abiStepPointer:
// Copy values to "integer registers."
if toAbiType(typ).ifaceIndir() {
offset := unsafe.Add(vptr, st.offset)
regPtr := unsafe.Pointer(&returnIntRegs[st.ireg])
if bigEndian {
regPtr = unsafe.Add(regPtr, ptrSize-st.size)
}
memmove(regPtr, offset, st.size)
} else {
// Only populate the Ints space on the return path.
// This is safe because out is kept alive until the
// end of this function, and the return path through
// makeFuncStub has no preemption, so these pointers
// are always visible to the GC.
returnIntRegs[st.ireg] = *(*unsafe.Pointer)(vptr)
}
case abiStepFloatReg:
// Copy values to "float registers."
if !toAbiType(typ).ifaceIndir() {
panic("attempted to copy pointer to FP register")
}
offset := unsafe.Add(vptr, st.offset)
switch st.size {
case 4:
*(*float32)(unsafe.Pointer(&returnFloatRegs[st.freg])) = *(*float32)(offset)
case 8:
returnFloatRegs[st.freg] = *(*float64)(offset)
default:
panic("bad st.size")
}
default:
panic("unknown ABI part kind")
}
}
}
}
runtime.KeepAlive(out)
return
}
func getMethodInOutTypes(method reflect.Method) (in, out []reflect.Type) {
typ := method.Type
for i := 0; i < typ.NumIn(); i++ {
in = append(in, typ.In(i))
}
for i := 0; i < typ.NumOut(); i++ {
out = append(out, typ.Out(i))
}
return
}
func memmove(dst, src unsafe.Pointer, size uintptr) {
dstBytes := unsafe.Slice((*byte)(dst), size)
srcBytes := unsafe.Slice((*byte)(src), size)
if uintptr(dst) < uintptr(src) || uintptr(dst) >= uintptr(src)+size {
// No overlap or dst is entirely before src: copy forward
for i := uintptr(0); i < size; i++ {
dstBytes[i] = srcBytes[i]
}
} else {
// Overlapping regions, copy backward
for i := size; i > 0; i-- {
dstBytes[i-1] = srcBytes[i-1]
}
}
}
func unsafe_New(typ reflect.Type) unsafe.Pointer {
return unsafe.Pointer(&make([]byte, typ.Size()+uintptr(typ.Align()))[0])
}