Browse Source

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.

Dmitry Panov 3 years ago
parent
commit
cfb079cdc7
3 changed files with 30 additions and 31 deletions
  1. 4 1
      object.go
  2. 3 23
      runtime.go
  3. 23 7
      runtime_test.go

+ 4 - 1
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)
 		}

+ 3 - 23
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.
 //

+ 23 - 7
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);
 	}
 
 	`