Go plugin: keep float parameters typed (#33162)

This commit is contained in:
Dennis Klipp 2026-08-25 14:23:53 +02:00 • committed by GitHub
parent 022171ffde
commit 9beca9b22f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 86 additions and 1 deletions

View file

@ -202,11 +202,25 @@ func (p *Go) evaluate(vm *interp.Interpreter) (res any, err error) {
func (p *Go) setParam(vm *interp.Interpreter) func(param string, val any) error { func (p *Go) setParam(vm *interp.Interpreter) func(param string, val any) error {
return func(param string, val any) error { return func(param string, val any) error {
_, err := vm.Eval(fmt.Sprintf("%s := %#v;", param, val)) _, err := vm.Eval(fmt.Sprintf("%s := %s;", param, goLiteral(val)))
return err return err
} }
} }
// goLiteral renders val as Go source. Floats need their type spelled out:
// %#v prints float64(100) as `100`, which the interpreter infers as int. Any
// arithmetic mixing the parameter with a float constant then fails to compile.
func goLiteral(val any) string {
switch v := val.(type) {
case float32:
return fmt.Sprintf("float32(%v)", v)
case float64:
return fmt.Sprintf("float64(%v)", v)
default:
return fmt.Sprintf("%#v", val)
}
}
var _ IntSetter = (*Go)(nil) var _ IntSetter = (*Go)(nil)
// IntSetter sends int request // IntSetter sends int request

71
plugin/go_test.go Normal file
View file

@ -0,0 +1,71 @@
package plugin
import (
"testing"
"github.com/stretchr/testify/assert"
)
// A float parameter must keep its type inside the interpreter even when the
// value is a whole number. Otherwise arithmetic mixing it with a float constant
// fails with "invalid operation: mismatched types int and untyped float".
func TestGoFloatParam(t *testing.T) {
for _, tc := range []struct {
value float64
want int64
}{
{11000, 73},
{15000, 100},
{0, 0},
{11000.5, 73},
} {
p, err := NewGoPluginFromConfig(t.Context(), map[string]any{
"in": []map[string]any{{
"name": "limit",
"type": "float",
"config": map[string]any{"source": "const", "value": tc.value},
}},
"script": "int(limit*100/15000 + 0.5)",
})
assert.NoError(t, err)
g, err := p.(IntGetter).IntGetter()
assert.NoError(t, err)
v, err := g()
assert.NoError(t, err)
assert.Equal(t, tc.want, v, tc.value)
}
}
// int parameters keep integer semantics
func TestGoIntParam(t *testing.T) {
p, err := NewGoPluginFromConfig(t.Context(), map[string]any{
"in": []map[string]any{{
"name": "mode",
"type": "int",
"config": map[string]any{"source": "const", "value": 3},
}},
"script": "mode & 1",
})
assert.NoError(t, err)
g, err := p.(IntGetter).IntGetter()
assert.NoError(t, err)
v, err := g()
assert.NoError(t, err)
assert.Equal(t, int64(1), v)
}
// the value handed to a setter is a parameter, too
func TestGoFloatSetter(t *testing.T) {
p, err := NewGoPluginFromConfig(t.Context(), map[string]any{
"script": "int(limit*100/15000 + 0.5)",
})
assert.NoError(t, err)
s, err := p.(FloatSetter).FloatSetter("limit")
assert.NoError(t, err)
assert.NoError(t, s(11000))
}