github.com/mattn/anko@v0.1.10/vm/vmConvertToX.go (about)

     1  package vm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  )
     8  
     9  // reflectValueSlicetoInterfaceSlice convert from a slice of reflect.Value to a interface slice
    10  // returned in normal reflect.Value form
    11  func reflectValueSlicetoInterfaceSlice(valueSlice []reflect.Value) reflect.Value {
    12  	interfaceSlice := make([]interface{}, 0, len(valueSlice))
    13  	for _, value := range valueSlice {
    14  		if value.Kind() == reflect.Interface && !value.IsNil() {
    15  			value = value.Elem()
    16  		}
    17  		if value.CanInterface() {
    18  			interfaceSlice = append(interfaceSlice, value.Interface())
    19  		} else {
    20  			interfaceSlice = append(interfaceSlice, nil)
    21  		}
    22  	}
    23  	return reflect.ValueOf(interfaceSlice)
    24  }
    25  
    26  // convertReflectValueToType trys to covert the reflect.Value to the reflect.Type
    27  // if it can not, it returns the original rv and an error
    28  func convertReflectValueToType(rv reflect.Value, rt reflect.Type) (reflect.Value, error) {
    29  	if rt == interfaceType || rv.Type() == rt {
    30  		// if reflect.Type is interface or the types match, return the provided reflect.Value
    31  		return rv, nil
    32  	}
    33  	if rv.Type().ConvertibleTo(rt) {
    34  		// if reflect can covert, do that conversion and return
    35  		return rv.Convert(rt), nil
    36  	}
    37  	if (rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array) &&
    38  		(rt.Kind() == reflect.Slice || rt.Kind() == reflect.Array) {
    39  		// covert slice or array
    40  		return convertSliceOrArray(rv, rt)
    41  	}
    42  	if rv.Kind() == rt.Kind() {
    43  		// kind matches
    44  		switch rv.Kind() {
    45  		case reflect.Map:
    46  			// convert map
    47  			return convertMap(rv, rt)
    48  		case reflect.Func:
    49  			// for runVMFunction conversions, call convertVMFunctionToType
    50  			return convertVMFunctionToType(rv, rt)
    51  		case reflect.Ptr:
    52  			// both rv and rt are pointers, convert what they are pointing to
    53  			value, err := convertReflectValueToType(rv.Elem(), rt.Elem())
    54  			if err != nil {
    55  				return rv, err
    56  			}
    57  			// need to make a new value to be able to set it
    58  			ptrV, err := makeValue(rt)
    59  			if err != nil {
    60  				return rv, err
    61  			}
    62  			// set value and return new pointer
    63  			ptrV.Elem().Set(value)
    64  			return ptrV, nil
    65  		}
    66  	}
    67  	if rv.Type() == interfaceType {
    68  		if rv.IsNil() {
    69  			// return nil of correct type
    70  			return reflect.Zero(rt), nil
    71  		}
    72  		// try to convert the element
    73  		return convertReflectValueToType(rv.Elem(), rt)
    74  	}
    75  
    76  	if rv.Type() == stringType {
    77  		if rt == byteType {
    78  			aString := rv.String()
    79  			if len(aString) < 1 {
    80  				return reflect.Zero(rt), nil
    81  			}
    82  			if len(aString) > 1 {
    83  				return rv, errInvalidTypeConversion
    84  			}
    85  			return reflect.ValueOf(aString[0]), nil
    86  		}
    87  		if rt == runeType {
    88  			aString := rv.String()
    89  			if len(aString) < 1 {
    90  				return reflect.Zero(rt), nil
    91  			}
    92  			if len(aString) > 1 {
    93  				return rv, errInvalidTypeConversion
    94  			}
    95  			return reflect.ValueOf(rune(aString[0])), nil
    96  		}
    97  	}
    98  
    99  	// TODO: need to handle the case where either rv or rt are a pointer but not both
   100  
   101  	return rv, errInvalidTypeConversion
   102  }
   103  
   104  // convertSliceOrArray trys to covert the reflect.Value slice or array to the slice or array reflect.Type
   105  func convertSliceOrArray(rv reflect.Value, rt reflect.Type) (reflect.Value, error) {
   106  	rtElemType := rt.Elem()
   107  
   108  	// try to covert elements to new slice/array
   109  	var value reflect.Value
   110  	if rt.Kind() == reflect.Slice {
   111  		// make slice
   112  		value = reflect.MakeSlice(rt, rv.Len(), rv.Len())
   113  	} else {
   114  		// make array
   115  		value = reflect.New(rt).Elem()
   116  	}
   117  
   118  	var err error
   119  	var v reflect.Value
   120  	for i := 0; i < rv.Len(); i++ {
   121  		v, err = convertReflectValueToType(rv.Index(i), rtElemType)
   122  		if err != nil {
   123  			return rv, err
   124  		}
   125  		value.Index(i).Set(v)
   126  	}
   127  
   128  	// return new converted slice or array
   129  	return value, nil
   130  }
   131  
   132  // convertVMFunctionToType is for translating a runVMFunction into the correct type
   133  // so it can be passed to a Go function argument with the correct static types
   134  // it creates a translate function runVMConvertFunction
   135  func convertVMFunctionToType(rv reflect.Value, rt reflect.Type) (reflect.Value, error) {
   136  	// only translates runVMFunction type
   137  	if !checkIfRunVMFunction(rv.Type()) {
   138  		return rv, errInvalidTypeConversion
   139  	}
   140  
   141  	// create runVMConvertFunction to match reflect.Type
   142  	// this function is being called by the Go function
   143  	runVMConvertFunction := func(in []reflect.Value) []reflect.Value {
   144  		// note: this function is being called by another reflect Call
   145  		// only way to pass along any errors is by panic
   146  
   147  		// make the reflect.Value slice of each of the VM reflect.Value
   148  		args := make([]reflect.Value, 0, rt.NumIn()+1)
   149  		// for runVMFunction first arg is always context
   150  		// TOFIX: use normal context
   151  		args = append(args, reflect.ValueOf(context.Background()))
   152  		for i := 0; i < rt.NumIn(); i++ {
   153  			// have to do the double reflect.ValueOf that runVMFunction expects
   154  			args = append(args, reflect.ValueOf(in[i]))
   155  		}
   156  
   157  		// Call runVMFunction
   158  		rvs := rv.Call(args)
   159  
   160  		// call processCallReturnValues to process runVMFunction return values
   161  		// returns normal VM reflect.Value form
   162  		rv, err := processCallReturnValues(rvs, true, false)
   163  		if err != nil {
   164  			panic(err)
   165  		}
   166  
   167  		if rt.NumOut() < 1 {
   168  			// Go function does not want any return values, so give it none
   169  			return []reflect.Value{}
   170  		}
   171  		if rt.NumOut() < 2 {
   172  			// Go function wants one return value
   173  			// will try to covert to reflect.Value correct type and return
   174  			rv, err = convertReflectValueToType(rv, rt.Out(0))
   175  			if err != nil {
   176  				panic("function wants return type " + rt.Out(0).String() + " but received type " + rv.Type().String())
   177  			}
   178  			return []reflect.Value{rv}
   179  		}
   180  
   181  		// Go function wants more than one return value
   182  		// make sure we have a slice/array with enought values
   183  
   184  		if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array {
   185  			panic(fmt.Sprintf("function wants %v return values but received %v", rt.NumOut(), rv.Kind().String()))
   186  		}
   187  		if rv.Len() < rt.NumOut() {
   188  			panic(fmt.Sprintf("function wants %v return values but received %v values", rt.NumOut(), rv.Len()))
   189  		}
   190  
   191  		// try to covert each value in slice to wanted type and put into a reflect.Value slice
   192  		rvs = make([]reflect.Value, rt.NumOut())
   193  		for i := 0; i < rv.Len(); i++ {
   194  			rvs[i], err = convertReflectValueToType(rv.Index(i), rt.Out(i))
   195  			if err != nil {
   196  				panic("function wants return type " + rt.Out(i).String() + " but received type " + rvs[i].Type().String())
   197  			}
   198  		}
   199  
   200  		// return created reflect.Value slice
   201  		return rvs
   202  	}
   203  
   204  	// make the reflect.Value function that calls runVMConvertFunction
   205  	return reflect.MakeFunc(rt, runVMConvertFunction), nil
   206  }