forked from mattn/anko
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vmConvertToX.go
206 lines (185 loc) · 6.26 KB
/
vmConvertToX.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
package vm
import (
"context"
"fmt"
"reflect"
)
// reflectValueSlicetoInterfaceSlice convert from a slice of reflect.Value to a interface slice
// returned in normal reflect.Value form
func reflectValueSlicetoInterfaceSlice(valueSlice []reflect.Value) reflect.Value {
interfaceSlice := make([]interface{}, 0, len(valueSlice))
for _, value := range valueSlice {
if value.Kind() == reflect.Interface && !value.IsNil() {
value = value.Elem()
}
if value.CanInterface() {
interfaceSlice = append(interfaceSlice, value.Interface())
} else {
interfaceSlice = append(interfaceSlice, nil)
}
}
return reflect.ValueOf(interfaceSlice)
}
// convertReflectValueToType trys to covert the reflect.Value to the reflect.Type
// if it can not, it returns the original rv and an error
func convertReflectValueToType(rv reflect.Value, rt reflect.Type) (reflect.Value, error) {
if rt == interfaceType || rv.Type() == rt {
// if reflect.Type is interface or the types match, return the provided reflect.Value
return rv, nil
}
if rv.Type().ConvertibleTo(rt) {
// if reflect can covert, do that conversion and return
return rv.Convert(rt), nil
}
if (rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array) &&
(rt.Kind() == reflect.Slice || rt.Kind() == reflect.Array) {
// covert slice or array
return convertSliceOrArray(rv, rt)
}
if rv.Kind() == rt.Kind() {
// kind matches
switch rv.Kind() {
case reflect.Map:
// convert map
return convertMap(rv, rt)
case reflect.Func:
// for runVMFunction conversions, call convertVMFunctionToType
return convertVMFunctionToType(rv, rt)
case reflect.Ptr:
// both rv and rt are pointers, convert what they are pointing to
value, err := convertReflectValueToType(rv.Elem(), rt.Elem())
if err != nil {
return rv, err
}
// need to make a new value to be able to set it
ptrV, err := makeValue(rt)
if err != nil {
return rv, err
}
// set value and return new pointer
ptrV.Elem().Set(value)
return ptrV, nil
}
}
if rv.Type() == interfaceType {
if rv.IsNil() {
// return nil of correct type
return reflect.Zero(rt), nil
}
// try to convert the element
return convertReflectValueToType(rv.Elem(), rt)
}
if rv.Type() == stringType {
if rt == byteType {
aString := rv.String()
if len(aString) < 1 {
return reflect.Zero(rt), nil
}
if len(aString) > 1 {
return rv, errInvalidTypeConversion
}
return reflect.ValueOf(aString[0]), nil
}
if rt == runeType {
aString := rv.String()
if len(aString) < 1 {
return reflect.Zero(rt), nil
}
if len(aString) > 1 {
return rv, errInvalidTypeConversion
}
return reflect.ValueOf(rune(aString[0])), nil
}
}
// TODO: need to handle the case where either rv or rt are a pointer but not both
return rv, errInvalidTypeConversion
}
// convertSliceOrArray trys to covert the reflect.Value slice or array to the slice or array reflect.Type
func convertSliceOrArray(rv reflect.Value, rt reflect.Type) (reflect.Value, error) {
rtElemType := rt.Elem()
// try to covert elements to new slice/array
var value reflect.Value
if rt.Kind() == reflect.Slice {
// make slice
value = reflect.MakeSlice(rt, rv.Len(), rv.Len())
} else {
// make array
value = reflect.New(rt).Elem()
}
var err error
var v reflect.Value
for i := 0; i < rv.Len(); i++ {
v, err = convertReflectValueToType(rv.Index(i), rtElemType)
if err != nil {
return rv, err
}
value.Index(i).Set(v)
}
// return new converted slice or array
return value, nil
}
// convertVMFunctionToType is for translating a runVMFunction into the correct type
// so it can be passed to a Go function argument with the correct static types
// it creates a translate function runVMConvertFunction
func convertVMFunctionToType(rv reflect.Value, rt reflect.Type) (reflect.Value, error) {
// only translates runVMFunction type
if !checkIfRunVMFunction(rv.Type()) {
return rv, errInvalidTypeConversion
}
// create runVMConvertFunction to match reflect.Type
// this function is being called by the Go function
runVMConvertFunction := func(in []reflect.Value) []reflect.Value {
// note: this function is being called by another reflect Call
// only way to pass along any errors is by panic
// make the reflect.Value slice of each of the VM reflect.Value
args := make([]reflect.Value, 0, rt.NumIn()+1)
// for runVMFunction first arg is always context
// TOFIX: use normal context
args = append(args, reflect.ValueOf(context.Background()))
for i := 0; i < rt.NumIn(); i++ {
// have to do the double reflect.ValueOf that runVMFunction expects
args = append(args, reflect.ValueOf(in[i]))
}
// Call runVMFunction
rvs := rv.Call(args)
// call processCallReturnValues to process runVMFunction return values
// returns normal VM reflect.Value form
rv, err := processCallReturnValues(rvs, true, false)
if err != nil {
panic(err)
}
if rt.NumOut() < 1 {
// Go function does not want any return values, so give it none
return []reflect.Value{}
}
if rt.NumOut() < 2 {
// Go function wants one return value
// will try to covert to reflect.Value correct type and return
rv, err = convertReflectValueToType(rv, rt.Out(0))
if err != nil {
panic("function wants return type " + rt.Out(0).String() + " but received type " + rv.Type().String())
}
return []reflect.Value{rv}
}
// Go function wants more than one return value
// make sure we have a slice/array with enought values
if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array {
panic(fmt.Sprintf("function wants %v return values but received %v", rt.NumOut(), rv.Kind().String()))
}
if rv.Len() < rt.NumOut() {
panic(fmt.Sprintf("function wants %v return values but received %v values", rt.NumOut(), rv.Len()))
}
// try to covert each value in slice to wanted type and put into a reflect.Value slice
rvs = make([]reflect.Value, rt.NumOut())
for i := 0; i < rv.Len(); i++ {
rvs[i], err = convertReflectValueToType(rv.Index(i), rt.Out(i))
if err != nil {
panic("function wants return type " + rt.Out(i).String() + " but received type " + rvs[i].Type().String())
}
}
// return created reflect.Value slice
return rvs
}
// make the reflect.Value function that calls runVMConvertFunction
return reflect.MakeFunc(rt, runVMConvertFunction), nil
}