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

     1  package vm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  
     8  	"github.com/mattn/anko/ast"
     9  )
    10  
    11  // funcExpr creates a function that reflect Call can use.
    12  // When called, it will run runVMFunction, to run the function statements
    13  func (runInfo *runInfoStruct) funcExpr() {
    14  	funcExpr := runInfo.expr.(*ast.FuncExpr)
    15  
    16  	// create the inTypes needed by reflect.FuncOf
    17  	inTypes := make([]reflect.Type, len(funcExpr.Params)+1)
    18  	// for runVMFunction first arg is always context
    19  	inTypes[0] = contextType
    20  	for i := 1; i < len(inTypes); i++ {
    21  		inTypes[i] = reflectValueType
    22  	}
    23  	if funcExpr.VarArg {
    24  		inTypes[len(inTypes)-1] = interfaceSliceType
    25  	}
    26  	// create funcType, output is always slice of reflect.Type with two values
    27  	funcType := reflect.FuncOf(inTypes, []reflect.Type{reflectValueType, reflectValueType}, funcExpr.VarArg)
    28  
    29  	// for adding env into saved function
    30  	envFunc := runInfo.env
    31  
    32  	// create a function that can be used by reflect.MakeFunc
    33  	// this function is a translator that converts a function call into a vm run
    34  	// returns slice of reflect.Type with two values:
    35  	// return value of the function and error value of the run
    36  	runVMFunction := func(in []reflect.Value) []reflect.Value {
    37  		runInfo := runInfoStruct{ctx: in[0].Interface().(context.Context), options: runInfo.options, env: envFunc.NewEnv(), stmt: funcExpr.Stmt, rv: nilValue}
    38  
    39  		// add Params to newEnv, except last Params
    40  		for i := 0; i < len(funcExpr.Params)-1; i++ {
    41  			runInfo.rv = in[i+1].Interface().(reflect.Value)
    42  			runInfo.env.DefineValue(funcExpr.Params[i], runInfo.rv)
    43  		}
    44  		// add last Params to newEnv
    45  		if len(funcExpr.Params) > 0 {
    46  			if funcExpr.VarArg {
    47  				// function is variadic, add last Params to newEnv without convert to Interface and then reflect.Value
    48  				runInfo.rv = in[len(funcExpr.Params)]
    49  				runInfo.env.DefineValue(funcExpr.Params[len(funcExpr.Params)-1], runInfo.rv)
    50  			} else {
    51  				// function is not variadic, add last Params to newEnv
    52  				runInfo.rv = in[len(funcExpr.Params)].Interface().(reflect.Value)
    53  				runInfo.env.DefineValue(funcExpr.Params[len(funcExpr.Params)-1], runInfo.rv)
    54  			}
    55  		}
    56  
    57  		// run function statements
    58  		runInfo.runSingleStmt()
    59  		if runInfo.err != nil && runInfo.err != ErrReturn {
    60  			runInfo.err = newError(funcExpr, runInfo.err)
    61  			// return nil value and error
    62  			// need to do single reflect.ValueOf because nilValue is already reflect.Value of nil
    63  			// need to do double reflect.ValueOf of newError in order to match
    64  			return []reflect.Value{reflectValueNilValue, reflect.ValueOf(reflect.ValueOf(newError(funcExpr, runInfo.err)))}
    65  		}
    66  
    67  		// the reflect.ValueOf of rv is needed to work in the reflect.Value slice
    68  		// reflectValueErrorNilValue is already a double reflect.ValueOf
    69  		return []reflect.Value{reflect.ValueOf(runInfo.rv), reflectValueErrorNilValue}
    70  	}
    71  
    72  	// make the reflect.Value function that calls runVMFunction
    73  	runInfo.rv = reflect.MakeFunc(funcType, runVMFunction)
    74  
    75  	// if function name is not empty, define it in the env
    76  	if funcExpr.Name != "" {
    77  		runInfo.env.DefineValue(funcExpr.Name, runInfo.rv)
    78  	}
    79  }
    80  
    81  // anonCallExpr handles ast.AnonCallExpr which calls a function anonymously
    82  func (runInfo *runInfoStruct) anonCallExpr() {
    83  	anonCallExpr := runInfo.expr.(*ast.AnonCallExpr)
    84  
    85  	runInfo.expr = anonCallExpr.Expr
    86  	runInfo.expr.SetPosition(anonCallExpr.Expr.Position())
    87  	runInfo.invokeExpr()
    88  	if runInfo.err != nil {
    89  		return
    90  	}
    91  
    92  	if runInfo.rv.Kind() == reflect.Interface && !runInfo.rv.IsNil() {
    93  		runInfo.rv = runInfo.rv.Elem()
    94  	}
    95  	if runInfo.rv.Kind() != reflect.Func {
    96  		runInfo.err = newStringError(anonCallExpr, "cannot call type "+runInfo.rv.Kind().String())
    97  		runInfo.rv = nilValue
    98  		return
    99  	}
   100  
   101  	runInfo.expr = &ast.CallExpr{Func: runInfo.rv, SubExprs: anonCallExpr.SubExprs, VarArg: anonCallExpr.VarArg, Go: anonCallExpr.Go}
   102  	runInfo.expr.SetPosition(anonCallExpr.Expr.Position())
   103  	runInfo.invokeExpr()
   104  }
   105  
   106  // callExpr handles *ast.CallExpr which calls a function
   107  func (runInfo *runInfoStruct) callExpr() {
   108  	// Note that if the function type looks the same as the VM function type, the returned values will probably be wrong
   109  
   110  	callExpr := runInfo.expr.(*ast.CallExpr)
   111  
   112  	f := callExpr.Func
   113  	if !f.IsValid() {
   114  		// if function is not valid try to get by function name
   115  		f, runInfo.err = runInfo.env.GetValue(callExpr.Name)
   116  		if runInfo.err != nil {
   117  			runInfo.err = newError(callExpr, runInfo.err)
   118  			runInfo.rv = nilValue
   119  			return
   120  		}
   121  	}
   122  
   123  	if f.Kind() == reflect.Interface && !f.IsNil() {
   124  		f = f.Elem()
   125  	}
   126  	if f.Kind() != reflect.Func {
   127  		runInfo.err = newStringError(callExpr, "cannot call type "+f.Kind().String())
   128  		runInfo.rv = nilValue
   129  		return
   130  	}
   131  
   132  	var rvs []reflect.Value
   133  	var args []reflect.Value
   134  	var useCallSlice bool
   135  	fType := f.Type()
   136  	// check if this is a runVMFunction type
   137  	isRunVMFunction := checkIfRunVMFunction(fType)
   138  	// create/convert the args to the function
   139  	args, useCallSlice = runInfo.makeCallArgs(fType, isRunVMFunction, callExpr)
   140  	if runInfo.err != nil {
   141  		return
   142  	}
   143  
   144  	if !runInfo.options.Debug {
   145  		// captures panic
   146  		defer recoverFunc(runInfo)
   147  	}
   148  
   149  	runInfo.rv = nilValue
   150  
   151  	// useCallSlice lets us know to use CallSlice instead of Call because of the format of the args
   152  	if useCallSlice {
   153  		if callExpr.Go {
   154  			go f.CallSlice(args)
   155  			return
   156  		}
   157  		rvs = f.CallSlice(args)
   158  	} else {
   159  		if callExpr.Go {
   160  			go f.Call(args)
   161  			return
   162  		}
   163  		rvs = f.Call(args)
   164  	}
   165  
   166  	// TOFIX: how VM pointers/addressing work
   167  	// Until then, this is a work around to set pointers back to VM variables
   168  	// This will probably panic for some functions and/or calls that are variadic
   169  	if !isRunVMFunction {
   170  		for i, expr := range callExpr.SubExprs {
   171  			if addrExpr, ok := expr.(*ast.AddrExpr); ok {
   172  				if identExpr, ok := addrExpr.Expr.(*ast.IdentExpr); ok {
   173  					runInfo.rv = args[i].Elem()
   174  					runInfo.expr = identExpr
   175  					runInfo.invokeLetExpr()
   176  				}
   177  			}
   178  		}
   179  	}
   180  
   181  	// processCallReturnValues to get/convert return values to normal rv form
   182  	runInfo.rv, runInfo.err = processCallReturnValues(rvs, isRunVMFunction, true)
   183  }
   184  
   185  // checkIfRunVMFunction checking the number and types of the reflect.Type.
   186  // If it matches the types for a runVMFunction this will return true, otherwise false
   187  func checkIfRunVMFunction(rt reflect.Type) bool {
   188  	if rt.NumIn() < 1 || rt.NumOut() != 2 || rt.In(0) != contextType || rt.Out(0) != reflectValueType || rt.Out(1) != reflectValueType {
   189  		return false
   190  	}
   191  	if rt.NumIn() > 1 {
   192  		if rt.IsVariadic() {
   193  			if rt.In(rt.NumIn()-1) != interfaceSliceType {
   194  				return false
   195  			}
   196  		} else {
   197  			if rt.In(rt.NumIn()-1) != reflectValueType {
   198  				return false
   199  			}
   200  		}
   201  		for i := 1; i < rt.NumIn()-1; i++ {
   202  			if rt.In(i) != reflectValueType {
   203  				return false
   204  			}
   205  		}
   206  	}
   207  	return true
   208  }
   209  
   210  // makeCallArgs creates the arguments reflect.Value slice for the four different kinds of functions.
   211  // Also returns true if CallSlice should be used on the arguments, or false if Call should be used.
   212  func (runInfo *runInfoStruct) makeCallArgs(rt reflect.Type, isRunVMFunction bool, callExpr *ast.CallExpr) ([]reflect.Value, bool) {
   213  	// number of arguments
   214  	numInReal := rt.NumIn()
   215  	numIn := numInReal
   216  	if isRunVMFunction {
   217  		// for runVMFunction the first arg is context so does not count against number of SubExprs
   218  		numIn--
   219  	}
   220  	if numIn < 1 {
   221  		// no arguments needed
   222  		if isRunVMFunction {
   223  			// for runVMFunction first arg is always context
   224  			return []reflect.Value{reflect.ValueOf(runInfo.ctx)}, false
   225  		}
   226  		return []reflect.Value{}, false
   227  	}
   228  
   229  	// number of expressions
   230  	numExprs := len(callExpr.SubExprs)
   231  	// checks to short circuit wrong number of arguments
   232  	if (!rt.IsVariadic() && !callExpr.VarArg && numIn != numExprs) ||
   233  		(rt.IsVariadic() && callExpr.VarArg && (numIn < numExprs || numIn > numExprs+1)) ||
   234  		(rt.IsVariadic() && !callExpr.VarArg && numIn > numExprs+1) ||
   235  		(!rt.IsVariadic() && callExpr.VarArg && numIn < numExprs) {
   236  		runInfo.err = newStringError(callExpr, fmt.Sprintf("function wants %v arguments but received %v", numIn, numExprs))
   237  		runInfo.rv = nilValue
   238  		return nil, false
   239  	}
   240  	if rt.IsVariadic() && rt.In(numInReal-1).Kind() != reflect.Slice && rt.In(numInReal-1).Kind() != reflect.Array {
   241  		runInfo.err = newStringError(callExpr, "function is variadic but last parameter is of type "+rt.In(numInReal-1).String())
   242  		runInfo.rv = nilValue
   243  		return nil, false
   244  	}
   245  
   246  	var args []reflect.Value
   247  	indexIn := 0
   248  	indexInReal := 0
   249  	indexExpr := 0
   250  
   251  	if numInReal > numExprs {
   252  		args = make([]reflect.Value, 0, numInReal)
   253  	} else {
   254  		args = make([]reflect.Value, 0, numExprs)
   255  	}
   256  	if isRunVMFunction {
   257  		// for runVMFunction first arg is always context
   258  		args = append(args, reflect.ValueOf(runInfo.ctx))
   259  		indexInReal++
   260  	}
   261  
   262  	// create arguments except the last one
   263  	for indexInReal < numInReal-1 && indexExpr < numExprs-1 {
   264  		runInfo.expr = callExpr.SubExprs[indexExpr]
   265  		runInfo.invokeExpr()
   266  		if runInfo.err != nil {
   267  			return nil, false
   268  		}
   269  		if isRunVMFunction {
   270  			args = append(args, reflect.ValueOf(runInfo.rv))
   271  		} else {
   272  			runInfo.rv, runInfo.err = convertReflectValueToType(runInfo.rv, rt.In(indexInReal))
   273  			if runInfo.err != nil {
   274  				runInfo.err = newStringError(callExpr.SubExprs[indexExpr],
   275  					"function wants argument type "+rt.In(indexInReal).String()+" but received type "+runInfo.rv.Type().String())
   276  				runInfo.rv = nilValue
   277  				return nil, false
   278  			}
   279  			args = append(args, runInfo.rv)
   280  		}
   281  		indexIn++
   282  		indexInReal++
   283  		indexExpr++
   284  	}
   285  
   286  	if !rt.IsVariadic() && !callExpr.VarArg {
   287  		// function is not variadic and call is not variadic
   288  		// add last arguments and return
   289  		runInfo.expr = callExpr.SubExprs[indexExpr]
   290  		runInfo.invokeExpr()
   291  		if runInfo.err != nil {
   292  			return nil, false
   293  		}
   294  		if runInfo.err != nil {
   295  			return nil, false
   296  		}
   297  		if isRunVMFunction {
   298  			args = append(args, reflect.ValueOf(runInfo.rv))
   299  		} else {
   300  			runInfo.rv, runInfo.err = convertReflectValueToType(runInfo.rv, rt.In(indexInReal))
   301  			if runInfo.err != nil {
   302  				runInfo.err = newStringError(callExpr.SubExprs[indexExpr],
   303  					"function wants argument type "+rt.In(indexInReal).String()+" but received type "+runInfo.rv.Type().String())
   304  				runInfo.rv = nilValue
   305  				return nil, false
   306  			}
   307  			args = append(args, runInfo.rv)
   308  		}
   309  		return args, false
   310  	}
   311  
   312  	if !rt.IsVariadic() && callExpr.VarArg {
   313  		// function is not variadic and call is variadic
   314  		runInfo.expr = callExpr.SubExprs[indexExpr]
   315  		runInfo.invokeExpr()
   316  		if runInfo.err != nil {
   317  			return nil, false
   318  		}
   319  		if runInfo.rv.Kind() != reflect.Slice && runInfo.rv.Kind() != reflect.Array {
   320  			runInfo.err = newStringError(callExpr, "call is variadic but last parameter is of type "+runInfo.rv.Type().String())
   321  			runInfo.rv = nilValue
   322  			return nil, false
   323  		}
   324  		if runInfo.rv.Len() < numIn-indexIn {
   325  			runInfo.err = newStringError(callExpr, fmt.Sprintf("function wants %v arguments but received %v", numIn, numExprs+runInfo.rv.Len()-1))
   326  			runInfo.rv = nilValue
   327  			return nil, false
   328  		}
   329  
   330  		indexSlice := 0
   331  		for indexInReal < numInReal {
   332  			if isRunVMFunction {
   333  				args = append(args, reflect.ValueOf(runInfo.rv.Index(indexSlice)))
   334  			} else {
   335  				runInfo.rv, runInfo.err = convertReflectValueToType(runInfo.rv.Index(indexSlice), rt.In(indexInReal))
   336  				if runInfo.err != nil {
   337  					runInfo.err = newStringError(callExpr.SubExprs[indexExpr],
   338  						"function wants argument type "+rt.In(indexInReal).String()+" but received type "+runInfo.rv.Type().String())
   339  					runInfo.rv = nilValue
   340  					return nil, false
   341  				}
   342  				args = append(args, runInfo.rv)
   343  			}
   344  			indexIn++
   345  			indexInReal++
   346  			indexSlice++
   347  		}
   348  		return args, false
   349  	}
   350  
   351  	// function is variadic and call may or may not be variadic
   352  
   353  	if indexExpr == numExprs {
   354  		// no more expressions, return what we have and let reflect Call handle if call is variadic or not
   355  		return args, false
   356  	}
   357  
   358  	if numIn > numExprs {
   359  		// there are more arguments after this one, so does not matter if call is variadic or not
   360  		// add the last argument then return what we have and let reflect Call handle if call is variadic or not
   361  		runInfo.expr = callExpr.SubExprs[indexExpr]
   362  		runInfo.invokeExpr()
   363  		if runInfo.err != nil {
   364  			return nil, false
   365  		}
   366  		if isRunVMFunction {
   367  			args = append(args, reflect.ValueOf(runInfo.rv))
   368  		} else {
   369  			runInfo.rv, runInfo.err = convertReflectValueToType(runInfo.rv, rt.In(indexInReal))
   370  			if runInfo.err != nil {
   371  				runInfo.err = newStringError(callExpr.SubExprs[indexExpr],
   372  					"function wants argument type "+rt.In(indexInReal).String()+" but received type "+runInfo.rv.Type().String())
   373  				runInfo.rv = nilValue
   374  				return nil, false
   375  			}
   376  			args = append(args, runInfo.rv)
   377  		}
   378  		return args, false
   379  	}
   380  
   381  	if rt.IsVariadic() && !callExpr.VarArg {
   382  		// function is variadic and call is not variadic
   383  		sliceType := rt.In(numInReal - 1).Elem()
   384  		for indexExpr < numExprs {
   385  			runInfo.expr = callExpr.SubExprs[indexExpr]
   386  			runInfo.invokeExpr()
   387  			if runInfo.err != nil {
   388  				return nil, false
   389  			}
   390  			runInfo.rv, runInfo.err = convertReflectValueToType(runInfo.rv, sliceType)
   391  			if runInfo.err != nil {
   392  				runInfo.err = newStringError(callExpr.SubExprs[indexExpr],
   393  					"function wants argument type "+rt.In(indexInReal).String()+" but received type "+runInfo.rv.Type().String())
   394  				runInfo.rv = nilValue
   395  				return nil, false
   396  			}
   397  			args = append(args, runInfo.rv)
   398  			indexExpr++
   399  		}
   400  		return args, false
   401  
   402  	}
   403  
   404  	// function is variadic and call is variadic
   405  	// the only time we return CallSlice is true
   406  	sliceType := rt.In(numInReal - 1)
   407  	if sliceType.Kind() == reflect.Interface && !runInfo.rv.IsNil() {
   408  		sliceType = sliceType.Elem()
   409  	}
   410  	runInfo.expr = callExpr.SubExprs[indexExpr]
   411  	runInfo.invokeExpr()
   412  	if runInfo.err != nil {
   413  		return nil, false
   414  	}
   415  	runInfo.rv, runInfo.err = convertReflectValueToType(runInfo.rv, sliceType)
   416  	if runInfo.err != nil {
   417  		runInfo.err = newStringError(callExpr.SubExprs[indexExpr],
   418  			"function wants argument type "+rt.In(indexInReal).String()+" but received type "+runInfo.rv.Type().String())
   419  		runInfo.rv = nilValue
   420  		return nil, false
   421  	}
   422  	args = append(args, runInfo.rv)
   423  
   424  	return args, true
   425  }
   426  
   427  // processCallReturnValues get/converts the values returned from a function call into our normal reflect.Value, error
   428  func processCallReturnValues(rvs []reflect.Value, isRunVMFunction bool, convertToInterfaceSlice bool) (reflect.Value, error) {
   429  	// check if it is not runVMFunction
   430  	if !isRunVMFunction {
   431  		// the function was a Go function, convert to our normal reflect.Value, error
   432  		switch len(rvs) {
   433  		case 0:
   434  			// no return values so return nil reflect.Value and nil error
   435  			return nilValue, nil
   436  		case 1:
   437  			// one return value but need to add nil error
   438  			return rvs[0], nil
   439  		}
   440  		if convertToInterfaceSlice {
   441  			// need to convert from a slice of reflect.Value to slice of interface
   442  			return reflectValueSlicetoInterfaceSlice(rvs), nil
   443  		}
   444  		// need to keep as slice of reflect.Value
   445  		return reflect.ValueOf(rvs), nil
   446  	}
   447  
   448  	// is a runVMFunction, expect return in the runVMFunction format
   449  	// convertToInterfaceSlice is ignored
   450  	// some of the below checks probably can be removed because they are done in checkIfRunVMFunction
   451  
   452  	if len(rvs) != 2 {
   453  		return nilValue, fmt.Errorf("VM function did not return 2 values but returned %v values", len(rvs))
   454  	}
   455  	if rvs[0].Type() != reflectValueType {
   456  		return nilValue, fmt.Errorf("VM function value 1 did not return reflect value type but returned %v type", rvs[0].Type().String())
   457  	}
   458  	if rvs[1].Type() != reflectValueType {
   459  		return nilValue, fmt.Errorf("VM function value 2 did not return reflect value type but returned %v type", rvs[1].Type().String())
   460  	}
   461  
   462  	rvError := rvs[1].Interface().(reflect.Value)
   463  	if rvError.Type() != errorType && rvError.Type() != vmErrorType {
   464  		return nilValue, fmt.Errorf("VM function error type is %v", rvError.Type())
   465  	}
   466  
   467  	if rvError.IsNil() {
   468  		// no error, so return the normal VM reflect.Value form
   469  		return rvs[0].Interface().(reflect.Value), nil
   470  	}
   471  
   472  	// VM returns two types of errors, check to see which type
   473  	if rvError.Type() == vmErrorType {
   474  		// convert to VM *Error
   475  		return nilValue, rvError.Interface().(*Error)
   476  	}
   477  	// convert to error
   478  	return nilValue, rvError.Interface().(error)
   479  }