Skip to content

Commit 431bcfa

Browse files
committed
fix(vm): handle nil arguments in variadic funcs
Update OpCall to properly handle nil values passed to variadic functions. Previously, passing nil as the last argument caused an index out of range panic, and a single nil argument was incorrectly treated as a nil slice. Added regression tests. Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
1 parent ad49544 commit 431bcfa

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

test/issues/817/issue_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package issue_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/expr-lang/expr"
8+
"github.com/expr-lang/expr/internal/testify/require"
9+
)
10+
11+
func TestIssue817_1(t *testing.T) {
12+
out, err := expr.Eval(
13+
`sprintf("result: %v %v", 1, nil)`,
14+
map[string]any{
15+
"sprintf": fmt.Sprintf,
16+
},
17+
)
18+
require.NoError(t, err)
19+
require.Equal(t, "result: 1 <nil>", out)
20+
}
21+
22+
func TestIssue817_2(t *testing.T) {
23+
out, err := expr.Eval(
24+
`thing(nil)`,
25+
map[string]any{
26+
"thing": func(arg ...any) string {
27+
return fmt.Sprintf("result: (%T) %v", arg[0], arg[0])
28+
},
29+
},
30+
)
31+
require.NoError(t, err)
32+
require.Equal(t, "result: (<nil>) <nil>", out)
33+
}

vm/vm.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,13 +330,29 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
330330
vm.push(runtime.Slice(node, from, to))
331331

332332
case OpCall:
333-
fn := reflect.ValueOf(vm.pop())
333+
v := vm.pop()
334+
if v == nil {
335+
panic("invalid operation: cannot call nil")
336+
}
337+
fn := reflect.ValueOf(v)
338+
if fn.Kind() != reflect.Func {
339+
panic(fmt.Sprintf("invalid operation: cannot call non-function of type %T", v))
340+
}
341+
fnType := fn.Type()
334342
size := arg
335343
in := make([]reflect.Value, size)
344+
isVariadic := fnType.IsVariadic()
345+
numIn := fnType.NumIn()
336346
for i := int(size) - 1; i >= 0; i-- {
337347
param := vm.pop()
338348
if param == nil {
339-
in[i] = reflect.Zero(fn.Type().In(i))
349+
var inType reflect.Type
350+
if isVariadic && i >= numIn-1 {
351+
inType = fnType.In(numIn - 1).Elem()
352+
} else {
353+
inType = fnType.In(i)
354+
}
355+
in[i] = reflect.Zero(inType)
340356
} else {
341357
in[i] = reflect.ValueOf(param)
342358
}

0 commit comments

Comments
 (0)