diff --git a/test/issues/817/issue_test.go b/test/issues/817/issue_test.go new file mode 100644 index 000000000..f397f9d1b --- /dev/null +++ b/test/issues/817/issue_test.go @@ -0,0 +1,33 @@ +package issue_test + +import ( + "fmt" + "testing" + + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/internal/testify/require" +) + +func TestIssue817_1(t *testing.T) { + out, err := expr.Eval( + `sprintf("result: %v %v", 1, nil)`, + map[string]any{ + "sprintf": fmt.Sprintf, + }, + ) + require.NoError(t, err) + require.Equal(t, "result: 1 ", out) +} + +func TestIssue817_2(t *testing.T) { + out, err := expr.Eval( + `thing(nil)`, + map[string]any{ + "thing": func(arg ...any) string { + return fmt.Sprintf("result: (%T) %v", arg[0], arg[0]) + }, + }, + ) + require.NoError(t, err) + require.Equal(t, "result: () ", out) +} diff --git a/vm/vm.go b/vm/vm.go index ed61d2f90..960009f0a 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -330,13 +330,29 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) { vm.push(runtime.Slice(node, from, to)) case OpCall: - fn := reflect.ValueOf(vm.pop()) + v := vm.pop() + if v == nil { + panic("invalid operation: cannot call nil") + } + fn := reflect.ValueOf(v) + if fn.Kind() != reflect.Func { + panic(fmt.Sprintf("invalid operation: cannot call non-function of type %T", v)) + } + fnType := fn.Type() size := arg in := make([]reflect.Value, size) + isVariadic := fnType.IsVariadic() + numIn := fnType.NumIn() for i := int(size) - 1; i >= 0; i-- { param := vm.pop() if param == nil { - in[i] = reflect.Zero(fn.Type().In(i)) + var inType reflect.Type + if isVariadic && i >= numIn-1 { + inType = fnType.In(numIn - 1).Elem() + } else { + inType = fnType.In(i) + } + in[i] = reflect.Zero(inType) } else { in[i] = reflect.ValueOf(param) }