Skip to content

Commit 346c275

Browse files
committed
fix: panic when using pointer to map as env
Previously, passing a pointer to a map (e.g. `*map[string]any`) to `expr.Env()` would cause a panic because reflection methods were called on the pointer value instead of the underlying map. With this change the value is first deref'd in the config before any map operations are performed. Consequently, the `vm.Run()` method needs to dereference the env map. This ensures compatibility with the optimisations like `OpLoadFast` where the compiler determines the environment simply as `map[string]any`. Regression test added. Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
1 parent 593f93f commit 346c275

3 files changed

Lines changed: 30 additions & 5 deletions

File tree

conf/env.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,25 @@ func EnvWithCache(c *Cache, env any) Nature {
3030
}
3131

3232
v := reflect.ValueOf(env)
33+
d := deref.Value(v)
3334
t := v.Type()
3435

35-
switch deref.Value(v).Kind() {
36+
switch d.Kind() {
3637
case reflect.Struct:
3738
n := c.FromType(t)
3839
n.Strict = true
3940
return n
4041

4142
case reflect.Map:
42-
n := c.FromType(v.Type())
43+
n := c.FromType(d.Type())
4344
if n.TypeData == nil {
4445
n.TypeData = new(TypeData)
4546
}
4647
n.Strict = true
47-
n.Fields = make(map[string]Nature, v.Len())
48+
n.Fields = make(map[string]Nature, d.Len())
4849

49-
for _, key := range v.MapKeys() {
50-
elem := v.MapIndex(key)
50+
for _, key := range d.MapKeys() {
51+
elem := d.MapIndex(key)
5152
if !elem.IsValid() || !elem.CanInterface() {
5253
panic(fmt.Sprintf("invalid map value: %s", key))
5354
}

test/issues/825/issue_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/expr-lang/expr"
7+
"github.com/expr-lang/expr/internal/testify/require"
8+
)
9+
10+
func TestIssue825(t *testing.T) {
11+
m := map[string]any{"foo": 42}
12+
env := &m
13+
14+
prog, err := expr.Compile("foo > 0", expr.Env(env))
15+
require.NoError(t, err)
16+
17+
out, err := expr.Run(prog, env)
18+
require.NoError(t, err)
19+
require.Equal(t, true, out)
20+
}

vm/vm.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ type VM struct {
4747
}
4848

4949
func (vm *VM) Run(program *Program, env any) (_ any, err error) {
50+
if m, ok := env.(*map[string]any); ok && m != nil {
51+
env = *m
52+
}
53+
5054
defer func() {
5155
if r := recover(); r != nil {
5256
var location file.Location

0 commit comments

Comments
 (0)