Skip to content

Checker infinit looping when using patcher/value and operator overloading #637

Closed
@rrb3942

Description

@rrb3942

I'm adding support for "github.com/shopspring/decimal" to my project that makes use of "expr/patcher/value" and ran into an issue where cpu usage spikes to 100% and expr gets stuck compiling the expression when I try to introduce operator support.

trace.txt

From the trace it seems like it is getting stuck in the checker for some reason.

Here is an example program that can recreate the problem.

package main

import (
	"fmt"
	"log"

	"net/http"
	_ "net/http/pprof"

	"github.com/expr-lang/expr"
	"github.com/expr-lang/expr/patcher/value"
	"github.com/expr-lang/expr/vm"

	"github.com/shopspring/decimal"
)

type myInt struct {
	Int int
}

func (v *myInt) AsInt() int {
	return v.Int
}

func (v *myInt) AsAny() any {
	return v.Int
}

func toDecimal(val any) (decimal.Decimal, error) {
	switch v := val.(type) {
	case int:
		return decimal.NewFromInt(int64(v)), nil
	case int32:
		return decimal.NewFromInt32(v), nil
	case int64:
		return decimal.NewFromInt(v), nil
	case float32:
		return decimal.NewFromFloat32(v), nil
	case float64:
		return decimal.NewFromFloat(v), nil
	case string:
		d, err := decimal.NewFromString(v)

		if err != nil {
			return decimal.Decimal{}, err
		}

		return d, nil
	case decimal.Decimal:
		return v, nil
	default:
		return decimal.Decimal{}, fmt.Errorf("Unhandled type for rounding (type: %T)", v)
	}
}

func ExampleAnyValuer() {
	env := make(map[string]any)
	//using a plain int here also works fine
	//env["ValueOne"] = 1
	//But value types are broken
	env["ValueOne"] = &myInt{1}
	env["ValueTwo"] = &myInt{2}

	fmt.Println("Compiling")

	//this is fine
	//program, err := expr.Compile("decimal(1) * decimal(2)",

	//these are broken
	//program, err := expr.Compile("decimal(ValueOne) * decimal(ValueTwo)",
	program, err := expr.Compile("decimal(ValueOne) * decimal(2)",
		expr.Env(env),
		value.ValueGetter,
		expr.Function(
			"decimal",
			func(params ...any) (any, error) {
				return toDecimal(params[0])
			},
			new(func(int) decimal.Decimal),
			new(func(int32) decimal.Decimal),
			new(func(int64) decimal.Decimal),
			new(func(float32) decimal.Decimal),
			new(func(float64) decimal.Decimal),
			new(func(string) decimal.Decimal),
		),
		expr.Function(
			"_decimalMul",
			func(params ...any) (any, error) {
				return params[0].(decimal.Decimal).Mul(params[1].(decimal.Decimal)), nil
			},
			new(func(decimal.Decimal, decimal.Decimal) decimal.Decimal),
		),
		expr.Operator("*", "_decimalMul"),
	)

	if err != nil {
		panic(err)
	}

	fmt.Println("Running")
	out, err := vm.Run(program, env)

	if err != nil {
		panic(err)
	}

	fmt.Println(out)
}

func main() {

	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	ExampleAnyValuer()
}

If there is any additional information that would help just let me know.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions