From f4806faefb67fd3d6bca647fc3f7e2849c0f06f0 Mon Sep 17 00:00:00 2001 From: Diego Augusto Molina Date: Mon, 23 Jun 2025 13:51:00 -0300 Subject: [PATCH 1/3] fix runtime error not allowing to slice unaddressable array type --- vm/runtime/regression_test.go | 24 ++++++++++++++++++++++++ vm/runtime/runtime.go | 14 +++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 vm/runtime/regression_test.go diff --git a/vm/runtime/regression_test.go b/vm/runtime/regression_test.go new file mode 100644 index 000000000..4631f4e8c --- /dev/null +++ b/vm/runtime/regression_test.go @@ -0,0 +1,24 @@ +package runtime_test + +import ( + "testing" + + "github.com/expr-lang/expr" +) + +func TestCanSliceNonAddressableArrayType(t *testing.T) { + prog, err := expr.Compile(`arr[1:2][0]`) + if err != nil { + t.Fatalf("error compiling program: %v", err) + } + val, err := expr.Run(prog, map[string]any{ + "arr": [5]int{0, 1, 2, 3, 4}, + }) + if err != nil { + t.Fatalf("error running program: %v", err) + } + valInt, ok := val.(int) + if !ok || valInt != 1 { + t.Fatalf("invalid result, expected 1, got %v", val) + } +} diff --git a/vm/runtime/runtime.go b/vm/runtime/runtime.go index cd48a280d..575e2f899 100644 --- a/vm/runtime/runtime.go +++ b/vm/runtime/runtime.go @@ -137,6 +137,8 @@ func FetchMethod(from any, method *Method) any { panic(fmt.Sprintf("cannot fetch %v from %T", method.Name, from)) } +var reflectTypeSliceOfAny = reflect.SliceOf(reflect.TypeOf(new(any)).Elem()) + func Slice(array, from, to any) any { v := reflect.ValueOf(array) @@ -162,7 +164,17 @@ func Slice(array, from, to any) any { if a > b { a = b } - value := v.Slice(a, b) + var value reflect.Value + if v.Kind() == reflect.Array && !v.CanAddr() { + totalElems := b - a + value = reflect.MakeSlice(reflectTypeSliceOfAny, 0, totalElems) + for i := 0; i < totalElems; i++ { + elem := v.Index(a + i) + value = reflect.Append(value, elem) + } + } else { + value = v.Slice(a, b) + } if value.IsValid() { return value.Interface() } From b38da5cefeaff261c57a70ddcd38137423b3dcbe Mon Sep 17 00:00:00 2001 From: Diego Augusto Molina Date: Fri, 27 Jun 2025 11:32:14 -0300 Subject: [PATCH 2/3] simplify logic and move test --- expr_test.go | 17 +++++++++++++++++ vm/runtime/regression_test.go | 24 ------------------------ vm/runtime/runtime.go | 13 ++++--------- 3 files changed, 21 insertions(+), 33 deletions(-) delete mode 100644 vm/runtime/regression_test.go diff --git a/expr_test.go b/expr_test.go index 57d2cfbbb..8214b954c 100644 --- a/expr_test.go +++ b/expr_test.go @@ -2765,3 +2765,20 @@ func TestMemoryBudget(t *testing.T) { }) } } + +func TestIssue802(t *testing.T) { + prog, err := expr.Compile(`arr[1:2][0]`) + if err != nil { + t.Fatalf("error compiling program: %v", err) + } + val, err := expr.Run(prog, map[string]any{ + "arr": [5]int{0, 1, 2, 3, 4}, + }) + if err != nil { + t.Fatalf("error running program: %v", err) + } + valInt, ok := val.(int) + if !ok || valInt != 1 { + t.Fatalf("invalid result, expected 1, got %v", val) + } +} diff --git a/vm/runtime/regression_test.go b/vm/runtime/regression_test.go deleted file mode 100644 index 4631f4e8c..000000000 --- a/vm/runtime/regression_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package runtime_test - -import ( - "testing" - - "github.com/expr-lang/expr" -) - -func TestCanSliceNonAddressableArrayType(t *testing.T) { - prog, err := expr.Compile(`arr[1:2][0]`) - if err != nil { - t.Fatalf("error compiling program: %v", err) - } - val, err := expr.Run(prog, map[string]any{ - "arr": [5]int{0, 1, 2, 3, 4}, - }) - if err != nil { - t.Fatalf("error running program: %v", err) - } - valInt, ok := val.(int) - if !ok || valInt != 1 { - t.Fatalf("invalid result, expected 1, got %v", val) - } -} diff --git a/vm/runtime/runtime.go b/vm/runtime/runtime.go index 575e2f899..6781f8150 100644 --- a/vm/runtime/runtime.go +++ b/vm/runtime/runtime.go @@ -164,17 +164,12 @@ func Slice(array, from, to any) any { if a > b { a = b } - var value reflect.Value if v.Kind() == reflect.Array && !v.CanAddr() { - totalElems := b - a - value = reflect.MakeSlice(reflectTypeSliceOfAny, 0, totalElems) - for i := 0; i < totalElems; i++ { - elem := v.Index(a + i) - value = reflect.Append(value, elem) - } - } else { - value = v.Slice(a, b) + newValue := reflect.New(v.Type()).Elem() + newValue.Set(v) + v = newValue } + value := v.Slice(a, b) if value.IsValid() { return value.Interface() } From c7ab5cc51ad7853132dae9aa0bdfe47399433677 Mon Sep 17 00:00:00 2001 From: Diego Augusto Molina Date: Fri, 27 Jun 2025 11:33:01 -0300 Subject: [PATCH 3/3] remove dead code --- vm/runtime/runtime.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/vm/runtime/runtime.go b/vm/runtime/runtime.go index 6781f8150..ccd499e0e 100644 --- a/vm/runtime/runtime.go +++ b/vm/runtime/runtime.go @@ -137,8 +137,6 @@ func FetchMethod(from any, method *Method) any { panic(fmt.Sprintf("cannot fetch %v from %T", method.Name, from)) } -var reflectTypeSliceOfAny = reflect.SliceOf(reflect.TypeOf(new(any)).Elem()) - func Slice(array, from, to any) any { v := reflect.ValueOf(array)