From e53172aadd5062b3aeefebe22441c07dc195a8e7 Mon Sep 17 00:00:00 2001 From: Solaris-star <820622658@qq.com> Date: Wed, 22 Jul 2026 05:17:17 +0800 Subject: [PATCH] fix: support pointer-to-map env without panic expr.Env(*map[string]any) dereferenced only for Kind checks but still called Len/MapKeys on the pointer value, panicking. Use the deref'd map for field inspection, and unwrap *map[string]any in vm.Run so OpLoadFast works at runtime. Fixes #825 --- conf/env.go | 11 ++++++----- test/issues/825/issue_test.go | 20 ++++++++++++++++++++ vm/vm.go | 6 ++++++ 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 test/issues/825/issue_test.go diff --git a/conf/env.go b/conf/env.go index 0acd44570..ab9b56f63 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 000000000..76849435a --- /dev/null +++ b/test/issues/825/issue_test.go @@ -0,0 +1,20 @@ +package issue_test + +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 ba3b53863..42ae2602e 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -52,6 +52,12 @@ type VM struct { } func (vm *VM) Run(program *Program, env any) (_ any, err error) { + // Allow pointer-to-map envs (e.g. *map[string]any) so OpLoadFast and + // map field access see a concrete map value (#825). + if m, ok := env.(*map[string]any); ok && m != nil { + env = *m + } + defer func() { if r := recover(); r != nil { var location file.Location