Skip to content

Commit

Permalink
Removed special treatment of the last argument passed to a variadic G…
Browse files Browse the repository at this point in the history
…o function. Do not treat callables (functions) as array-like when exporting to slices. Fixes #369.
  • Loading branch information
dop251 committed Jan 24, 2022
1 parent 61453c1 commit cfb079c
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 31 deletions.
5 changes: 4 additions & 1 deletion object.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
26 changes: 3 additions & 23 deletions runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 {
Expand All @@ -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
}
Expand Down Expand Up @@ -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.
//
Expand Down
30 changes: 23 additions & 7 deletions runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
`
Expand Down

0 comments on commit cfb079c

Please sign in to comment.