From cfb079cdc7b42fe2e4fba673316c7bdb9e8af1df Mon Sep 17 00:00:00 2001 From: Dmitry Panov Date: Mon, 24 Jan 2022 17:10:16 +0000 Subject: [PATCH] Removed special treatment of the last argument passed to a variadic Go function. Do not treat callables (functions) as array-like when exporting to slices. Fixes #369. --- object.go | 5 ++++- runtime.go | 26 +++----------------------- runtime_test.go | 30 +++++++++++++++++++++++------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/object.go b/object.go index 4fb72d63..5426733c 100644 --- a/object.go +++ b/object.go @@ -1044,7 +1044,10 @@ func genericExportToArrayOrSlice(o *Object, dst reflect.Value, typ reflect.Type, } } else { // array-like - lp := o.self.getStr("length", nil) + var lp Value + if _, ok := o.self.assertCallable(); !ok { + lp = o.self.getStr("length", nil) + } if lp == nil { return fmt.Errorf("cannot convert %v to %v: not an array or iterable", o, typ) } diff --git a/runtime.go b/runtime.go index 27c1bec1..86a16f13 100644 --- a/runtime.go +++ b/runtime.go @@ -1844,7 +1844,6 @@ func (r *Runtime) wrapReflectFunc(value reflect.Value) func(FunctionCall) Value in = make([]reflect.Value, l) } - callSlice := false for i, a := range call.Arguments { var t reflect.Type @@ -1861,19 +1860,6 @@ func (r *Runtime) wrapReflectFunc(value reflect.Value) func(FunctionCall) Value t = typ.In(n) } - // if this is a variadic Go function, and the caller has supplied - // exactly the number of JavaScript arguments required, and this - // is the last JavaScript argument, try treating it as the - // actual set of variadic Go arguments. if that succeeds, break - // out of the loop. - if typ.IsVariadic() && len(call.Arguments) == nargs && i == nargs-1 { - v := reflect.New(typ.In(n)).Elem() - if err := r.toReflectValue(a, v, &objectExportCtx{}); err == nil { - in[i] = v - callSlice = true - break - } - } v := reflect.New(t).Elem() err := r.toReflectValue(a, v, &objectExportCtx{}) if err != nil { @@ -1882,13 +1868,7 @@ func (r *Runtime) wrapReflectFunc(value reflect.Value) func(FunctionCall) Value in[i] = v } - var out []reflect.Value - if callSlice { - out = value.CallSlice(in) - } else { - out = value.Call(in) - } - + out := value.Call(in) if len(out) == 0 { return _undefined } @@ -2192,8 +2172,8 @@ func (r *Runtime) wrapJSFunc(fn Callable, typ reflect.Type) func(args []reflect. // // Array is treated as iterable (i.e. overwriting Symbol.iterator affects the result). // -// If an object has a 'length' property it is treated as array-like. The resulting slice will contain -// obj[0], ... obj[length-1]. +// If an object has a 'length' property and is not a function it is treated as array-like. The resulting slice +// will contain obj[0], ... obj[length-1]. // // For any other Object an error is returned. // diff --git a/runtime_test.go b/runtime_test.go index 2700fa68..29bd293c 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -320,6 +320,27 @@ func TestSetFuncVariadic(t *testing.T) { } } +func TestSetFuncVariadicFuncArg(t *testing.T) { + vm := New() + vm.Set("f", func(s string, g ...Value) { + if f, ok := AssertFunction(g[0]); ok { + v, err := f(nil) + if err != nil { + t.Fatal(err) + } + if v != valueTrue { + t.Fatal(v) + } + } + }) + _, err := vm.RunString(` + f("something", () => true) + `) + if err != nil { + t.Fatal(err) + } +} + func TestArgsKeys(t *testing.T) { const SCRIPT = ` function testArgs2(x, y, z) { @@ -1319,19 +1340,14 @@ func TestReflectCallVariadic(t *testing.T) { throw new Error("test 1 has failed: " + r); } - r = f("Hello %s, %d", ["test", 42]); - if (r !== "Hello test, 42") { - throw new Error("test 2 has failed: " + r); - } - r = f("Hello %s, %s", "test"); if (r !== "Hello test, %!s(MISSING)") { - throw new Error("test 3 has failed: " + r); + throw new Error("test 2 has failed: " + r); } r = f(); if (r !== "") { - throw new Error("test 4 has failed: " + r); + throw new Error("test 3 has failed: " + r); } `