From 346c275c158b94218db7159fe968a2c8551232f5 Mon Sep 17 00:00:00 2001 From: Ville Vesilehto Date: Fri, 5 Dec 2025 20:56:28 +0200 Subject: [PATCH] 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 --- conf/env.go | 11 ++++++----- test/issues/825/issue_test.go | 20 ++++++++++++++++++++ vm/vm.go | 4 ++++ 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 test/issues/825/issue_test.go diff --git a/conf/env.go b/conf/env.go index 0acd4457..ab9b56f6 100644 --- a/conf/env.go +++ b/conf/env.go @@ -30,24 +30,25 @@ func EnvWithCache(c *Cache, env any) Nature { } v := reflect.ValueOf(env) + d := deref.Value(v) t := v.Type() - switch deref.Value(v).Kind() { + switch d.Kind() { case reflect.Struct: n := c.FromType(t) n.Strict = true return n case reflect.Map: - n := c.FromType(v.Type()) + n := c.FromType(d.Type()) if n.TypeData == nil { n.TypeData = new(TypeData) } n.Strict = true - n.Fields = make(map[string]Nature, v.Len()) + n.Fields = make(map[string]Nature, d.Len()) - for _, key := range v.MapKeys() { - elem := v.MapIndex(key) + for _, key := range d.MapKeys() { + elem := d.MapIndex(key) if !elem.IsValid() || !elem.CanInterface() { panic(fmt.Sprintf("invalid map value: %s", key)) } diff --git a/test/issues/825/issue_test.go b/test/issues/825/issue_test.go new file mode 100644 index 00000000..5e1d4b38 --- /dev/null +++ b/test/issues/825/issue_test.go @@ -0,0 +1,20 @@ +package main + +import ( + "testing" + + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/internal/testify/require" +) + +func TestIssue825(t *testing.T) { + m := map[string]any{"foo": 42} + env := &m + + prog, err := expr.Compile("foo > 0", expr.Env(env)) + require.NoError(t, err) + + out, err := expr.Run(prog, env) + require.NoError(t, err) + require.Equal(t, true, out) +} diff --git a/vm/vm.go b/vm/vm.go index 8e95d4ea..916406eb 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -47,6 +47,10 @@ type VM struct { } func (vm *VM) Run(program *Program, env any) (_ any, err error) { + if m, ok := env.(*map[string]any); ok && m != nil { + env = *m + } + defer func() { if r := recover(); r != nil { var location file.Location