[GLIB] Add API to allow creating variadic functions
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Jul 2018 05:18:20 +0000 (05:18 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Jul 2018 05:18:20 +0000 (05:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=187517

Reviewed by Michael Catanzaro.

Source/JavaScriptCore:

Add a _variadic alternate method for jsc_class_add_constructor, jsc_class_add_method and
jsc_value_new_function. In that case the callback always receives a GPtrArray of JSCValue.

* API/glib/JSCCallbackFunction.cpp:
(JSC::JSCCallbackFunction::create): Make the parameters optional.
(JSC::JSCCallbackFunction::JSCCallbackFunction): Ditto.
(JSC::JSCCallbackFunction::call): Handle the case of parameters being nullopt by creating a GPtrArray of
JSCValue for the arguments.
(JSC::JSCCallbackFunction::construct): Ditto.
* API/glib/JSCCallbackFunction.h:
* API/glib/JSCClass.cpp:
(jscClassCreateConstructor): Make the parameters optional.
(jsc_class_add_constructor_variadic): Pass nullopt as parameters to jscClassCreateConstructor.
(jscClassAddMethod): Make the parameters optional.
(jsc_class_add_method_variadic): Pass nullopt as parameters to jscClassAddMethod.
* API/glib/JSCClass.h:
* API/glib/JSCValue.cpp:
(jsc_value_object_define_property_accessor): Update now that parameters are optional.
(jscValueFunctionCreate): Make the parameters optional.
(jsc_value_new_function_variadic): Pass nullopt as parameters to jscValueFunctionCreate.
* API/glib/JSCValue.h:
* API/glib/docs/jsc-glib-4.0-sections.txt:

Tools:

Add test cases.

* TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp:
(testJSCFunction):
(fooCreateWithFooV):
(multiplyFooV):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@233760 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/API/glib/JSCCallbackFunction.cpp
Source/JavaScriptCore/API/glib/JSCCallbackFunction.h
Source/JavaScriptCore/API/glib/JSCClass.cpp
Source/JavaScriptCore/API/glib/JSCClass.h
Source/JavaScriptCore/API/glib/JSCValue.cpp
Source/JavaScriptCore/API/glib/JSCValue.h
Source/JavaScriptCore/API/glib/docs/jsc-glib-4.0-sections.txt
Source/JavaScriptCore/ChangeLog
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp

index 343d8d9..3006fca 100644 (file)
@@ -53,7 +53,7 @@ static JSObjectRef callAsConstructor(JSContextRef callerContext, JSObjectRef con
 
 const ClassInfo JSCCallbackFunction::s_info = { "CallbackFunction", &InternalFunction::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCCallbackFunction) };
 
-JSCCallbackFunction* JSCCallbackFunction::create(VM& vm, JSGlobalObject* globalObject, const String& name, Type type, JSCClass* jscClass, GRefPtr<GClosure>&& closure, GType returnType, Vector<GType>&& parameters)
+JSCCallbackFunction* JSCCallbackFunction::create(VM& vm, JSGlobalObject* globalObject, const String& name, Type type, JSCClass* jscClass, GRefPtr<GClosure>&& closure, GType returnType, std::optional<Vector<GType>>&& parameters)
 {
     Structure* structure = globalObject->glibCallbackFunctionStructure();
     JSCCallbackFunction* function = new (NotNull, allocateCell<JSCCallbackFunction>(vm.heap)) JSCCallbackFunction(vm, structure, type, jscClass, WTFMove(closure), returnType, WTFMove(parameters));
@@ -61,7 +61,7 @@ JSCCallbackFunction* JSCCallbackFunction::create(VM& vm, JSGlobalObject* globalO
     return function;
 }
 
-JSCCallbackFunction::JSCCallbackFunction(VM& vm, Structure* structure, Type type, JSCClass* jscClass, GRefPtr<GClosure>&& closure, GType returnType, Vector<GType>&& parameters)
+JSCCallbackFunction::JSCCallbackFunction(VM& vm, Structure* structure, Type type, JSCClass* jscClass, GRefPtr<GClosure>&& closure, GType returnType, std::optional<Vector<GType>>&& parameters)
     : InternalFunction(vm, structure, APICallbackFunction::call<JSCCallbackFunction>, type == Type::Constructor ? APICallbackFunction::construct<JSCCallbackFunction> : nullptr)
     , m_functionCallback(callAsFunction)
     , m_constructCallback(callAsConstructor)
@@ -99,9 +99,9 @@ JSValueRef JSCCallbackFunction::call(JSContextRef callerContext, JSObjectRef thi
     auto callbackData = jscContextPushCallback(context.get(), toRef(this), thisObject, argumentCount, arguments);
 
     // GClosure always expect to have at least the instance parameter.
-    bool addInstance = instance || m_parameters.isEmpty();
+    bool addInstance = instance || (m_parameters && m_parameters->isEmpty());
 
-    auto parameterCount = std::min(m_parameters.size(), argumentCount);
+    auto parameterCount = m_parameters ? std::min(m_parameters->size(), argumentCount) : 1;
     if (addInstance)
         parameterCount++;
     auto* values = static_cast<GValue*>(g_alloca(sizeof(GValue) * parameterCount));
@@ -113,8 +113,16 @@ JSValueRef JSCCallbackFunction::call(JSContextRef callerContext, JSObjectRef thi
         g_value_set_pointer(&values[0], instance);
         firstParameter = 1;
     }
-    for (size_t i = firstParameter; i < parameterCount && !*exception; ++i)
-        jscContextJSValueToGValue(context.get(), arguments[i - firstParameter], m_parameters[i - firstParameter], &values[i], exception);
+    if (m_parameters) {
+        for (size_t i = firstParameter; i < parameterCount && !*exception; ++i)
+            jscContextJSValueToGValue(context.get(), arguments[i - firstParameter], m_parameters.value()[i - firstParameter], &values[i], exception);
+    } else {
+        auto* parameters = g_ptr_array_new_full(argumentCount, g_object_unref);
+        for (size_t i = 0; i < argumentCount; ++i)
+            g_ptr_array_add(parameters, jscContextGetOrCreateValue(context.get(), arguments[i]).leakRef());
+        g_value_init(&values[firstParameter], G_TYPE_PTR_ARRAY);
+        g_value_take_boxed(&values[firstParameter], parameters);
+    }
 
     GValue returnValue = G_VALUE_INIT;
     if (m_returnType != G_TYPE_NONE)
@@ -155,19 +163,27 @@ JSObjectRef JSCCallbackFunction::construct(JSContextRef callerContext, size_t ar
     GValue returnValue = G_VALUE_INIT;
     g_value_init(&returnValue, m_returnType);
 
-    if (m_parameters.isEmpty()) {
+    if (m_parameters && m_parameters->isEmpty()) {
         // GClosure always expect to have at least the instance parameter.
         GValue dummyValue = G_VALUE_INIT;
         g_value_init(&dummyValue, G_TYPE_POINTER);
         g_closure_invoke(m_closure.get(), &returnValue, 1, &dummyValue, nullptr);
         g_value_unset(&dummyValue);
     } else {
-        auto parameterCount = std::min(m_parameters.size(), argumentCount);
+        auto parameterCount = m_parameters ? std::min(m_parameters->size(), argumentCount) : 1;
         auto* values = static_cast<GValue*>(g_alloca(sizeof(GValue) * parameterCount));
         memset(values, 0, sizeof(GValue) * parameterCount);
 
-        for (size_t i = 0; i < parameterCount && !*exception; ++i)
-            jscContextJSValueToGValue(context.get(), arguments[i], m_parameters[i], &values[i], exception);
+        if (m_parameters) {
+            for (size_t i = 0; i < parameterCount && !*exception; ++i)
+                jscContextJSValueToGValue(context.get(), arguments[i], m_parameters.value()[i], &values[i], exception);
+        } else {
+            auto* parameters = g_ptr_array_new_full(argumentCount, g_object_unref);
+            for (size_t i = 0; i < argumentCount; ++i)
+                g_ptr_array_add(parameters, jscContextGetOrCreateValue(context.get(), arguments[i]).leakRef());
+            g_value_init(&values[0], G_TYPE_PTR_ARRAY);
+            g_value_take_boxed(&values[0], parameters);
+        }
 
         if (!*exception)
             g_closure_invoke(m_closure.get(), &returnValue, parameterCount, values, nullptr);
index 7b16d8f..17e3210 100644 (file)
@@ -52,7 +52,7 @@ public:
         Constructor
     };
 
-    static JSCCallbackFunction* create(VM&, JSGlobalObject*, const String& name, Type, JSCClass*, GRefPtr<GClosure>&&, GType, Vector<GType>&&);
+    static JSCCallbackFunction* create(VM&, JSGlobalObject*, const String& name, Type, JSCClass*, GRefPtr<GClosure>&&, GType, std::optional<Vector<GType>>&&);
     static void destroy(JSCell*);
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
@@ -69,7 +69,7 @@ public:
 private:
     static IsoSubspace* subspaceForImpl(VM&);
     
-    JSCCallbackFunction(VM&, Structure*, Type, JSCClass*, GRefPtr<GClosure>&&, GType, Vector<GType>&&);
+    JSCCallbackFunction(VM&, Structure*, Type, JSCClass*, GRefPtr<GClosure>&&, GType, std::optional<Vector<GType>>&&);
 
     JSObjectCallAsFunctionCallback functionCallback() { return m_functionCallback; }
     JSObjectCallAsConstructorCallback constructCallback() { return m_constructCallback; }
@@ -80,7 +80,7 @@ private:
     GRefPtr<JSCClass> m_class;
     GRefPtr<GClosure> m_closure;
     GType m_returnType;
-    Vector<GType> m_parameters;
+    std::optional<Vector<GType>> m_parameters;
 };
 
 } // namespace JSC
index 3b52fea..e76fbbf 100644 (file)
@@ -521,7 +521,7 @@ JSCClass* jsc_class_get_parent(JSCClass* jscClass)
     return jscClass->priv->parentClass;
 }
 
-static GRefPtr<JSCValue> jscClassCreateConstructor(JSCClass* jscClass, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, Vector<GType>&& parameters)
+static GRefPtr<JSCValue> jscClassCreateConstructor(JSCClass* jscClass, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, std::optional<Vector<GType>>&& parameters)
 {
     JSCClassPrivate* priv = jscClass->priv;
     GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
@@ -555,7 +555,7 @@ static GRefPtr<JSCValue> jscClassCreateConstructor(JSCClass* jscClass, const cha
  * parameters and @user_data as the last parameter. When the constructor object is cleared in the #JSCClass context,
  * @destroy_notify is called with @user_data as parameter.
  *
- * This function creates the constructor, that needs to be added to an object as a property to be able to use it. Use
+ * This function creates the constructor, which needs to be added to an object as a property to be able to use it. Use
  * jsc_context_set_value() to make the constructor available in the global object.
  *
  * Returns: (transfer full): a #JSCValue representing the class constructor.
@@ -601,7 +601,7 @@ JSCValue* jsc_class_add_constructor(JSCClass* jscClass, const char* name, GCallb
  * parameters and @user_data as the last parameter. When the constructor object is cleared in the #JSCClass context,
  * @destroy_notify is called with @user_data as parameter.
  *
- * This function creates the constructor, that needs to be added to an object as a property to be able to use it. Use
+ * This function creates the constructor, which needs to be added to an object as a property to be able to use it. Use
  * jsc_context_set_value() to make the constructor available in the global object.
  *
  * Returns: (transfer full): a #JSCValue representing the class constructor.
@@ -628,7 +628,40 @@ JSCValue* jsc_class_add_constructorv(JSCClass* jscClass, const char* name, GCall
     return jscClassCreateConstructor(jscClass, name ? name : priv->name.data(), callback, userData, destroyNotify, returnType, WTFMove(parameters)).leakRef();
 }
 
-static void jscClassAddMethod(JSCClass* jscClass, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, Vector<GType>&& parameters)
+/**
+ * jsc_class_add_constructor_variadic:
+ * @jsc_class: a #JSCClass
+ * @name: (nullable): the constructor name or %NULL
+ * @callback: (scope async): a #GCallback to be called to create an instance of @jsc_class
+ * @user_data: (closure): user data to pass to @callback
+ * @destroy_notify: (nullable): destroy notifier for @user_data
+ * @return_type: the #GType of the constructor return value
+ *
+ * Add a constructor to @jsc_class. If @name is %NULL, the class name will be used. When <function>new</function>
+ * is used with the constructor or jsc_value_constructor_call() is called, @callback is invoked receiving
+ * a #GPtrArray of #JSCValue<!-- -->s as arguments and @user_data as the last parameter. When the constructor object
+ * is cleared in the #JSCClass context, @destroy_notify is called with @user_data as parameter.
+ *
+ * This function creates the constructor, which needs to be added to an object as a property to be able to use it. Use
+ * jsc_context_set_value() to make the constructor available in the global object.
+ *
+ * Returns: (transfer full): a #JSCValue representing the class constructor.
+ */
+JSCValue* jsc_class_add_constructor_variadic(JSCClass* jscClass, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType)
+{
+    g_return_val_if_fail(JSC_IS_CLASS(jscClass), nullptr);
+    g_return_val_if_fail(callback, nullptr);
+
+    JSCClassPrivate* priv = jscClass->priv;
+    g_return_val_if_fail(jscClass->priv->context, nullptr);
+
+    if (!name)
+        name = priv->name.data();
+
+    return jscClassCreateConstructor(jscClass, name ? name : priv->name.data(), callback, userData, destroyNotify, returnType, std::nullopt).leakRef();
+}
+
+static void jscClassAddMethod(JSCClass* jscClass, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, std::optional<Vector<GType>>&& parameters)
 {
     JSCClassPrivate* priv = jscClass->priv;
     GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
@@ -714,6 +747,30 @@ void jsc_class_add_methodv(JSCClass* jscClass, const char* name, GCallback callb
 }
 
 /**
+ * jsc_class_add_method_variadic:
+ * @jsc_class: a #JSCClass
+ * @name: the method name
+ * @callback: (scope async): a #GCallback to be called to invoke method @name of @jsc_class
+ * @user_data: (closure): user data to pass to @callback
+ * @destroy_notify: (nullable): destroy notifier for @user_data
+ * @return_type: the #GType of the method return value, or %G_TYPE_NONE if the method is void.
+ *
+ * Add method with @name to @jsc_class. When the method is called by JavaScript or jsc_value_object_invoke_method(),
+ * @callback is called receiving the class instance as first parameter, followed by a #GPtrArray of #JSCValue<!-- -->s
+ * with the method arguments and then @user_data as last parameter. When the method is cleared in the #JSCClass context,
+ * @destroy_notify is called with @user_data as parameter.
+ */
+void jsc_class_add_method_variadic(JSCClass* jscClass, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType)
+{
+    g_return_if_fail(JSC_IS_CLASS(jscClass));
+    g_return_if_fail(name);
+    g_return_if_fail(callback);
+    g_return_if_fail(jscClass->priv->context);
+
+    jscClassAddMethod(jscClass, name, callback, userData, destroyNotify, returnType, std::nullopt);
+}
+
+/**
  * jsc_class_add_property:
  * @jsc_class: a #JSCClass
  * @name: the property name
index 38a7fe9..8774196 100644 (file)
@@ -76,62 +76,78 @@ typedef struct {
 } JSCClassVTable;
 
 JSC_API GType
-jsc_class_get_type         (void);
+jsc_class_get_type                 (void);
 
 JSC_API const char *
-jsc_class_get_name         (JSCClass      *jsc_class);
+jsc_class_get_name                 (JSCClass      *jsc_class);
 
 JSC_API JSCClass *
-jsc_class_get_parent       (JSCClass      *jsc_class);
+jsc_class_get_parent               (JSCClass      *jsc_class);
 
 JSC_API JSCValue *
-jsc_class_add_constructor  (JSCClass      *jsc_class,
-                            const char    *name,
-                            GCallback      callback,
-                            gpointer       user_data,
-                            GDestroyNotify destroy_notify,
-                            GType          return_type,
-                            guint          n_params,
-                            ...);
+jsc_class_add_constructor          (JSCClass      *jsc_class,
+                                    const char    *name,
+                                    GCallback      callback,
+                                    gpointer       user_data,
+                                    GDestroyNotify destroy_notify,
+                                    GType          return_type,
+                                    guint          n_params,
+                                    ...);
 
 JSC_API JSCValue *
-jsc_class_add_constructorv (JSCClass      *jsc_class,
-                            const char    *name,
-                            GCallback      callback,
-                            gpointer       user_data,
-                            GDestroyNotify destroy_notify,
-                            GType          return_type,
-                            guint          n_parameters,
-                            GType         *parameter_types);
+jsc_class_add_constructorv         (JSCClass      *jsc_class,
+                                    const char    *name,
+                                    GCallback      callback,
+                                    gpointer       user_data,
+                                    GDestroyNotify destroy_notify,
+                                    GType          return_type,
+                                    guint          n_parameters,
+                                    GType         *parameter_types);
+
+JSC_API JSCValue *
+jsc_class_add_constructor_variadic (JSCClass      *jsc_class,
+                                    const char    *name,
+                                    GCallback      callback,
+                                    gpointer       user_data,
+                                    GDestroyNotify destroy_notify,
+                                    GType          return_type);
+
+JSC_API void
+jsc_class_add_method               (JSCClass      *jsc_class,
+                                    const char    *name,
+                                    GCallback      callback,
+                                    gpointer       user_data,
+                                    GDestroyNotify destroy_notify,
+                                    GType          return_type,
+                                    guint          n_params,
+                                    ...);
 
 JSC_API void
-jsc_class_add_method       (JSCClass      *jsc_class,
-                            const char    *name,
-                            GCallback      callback,
-                            gpointer       user_data,
-                            GDestroyNotify destroy_notify,
-                            GType          return_type,
-                            guint          n_params,
-                            ...);
+jsc_class_add_methodv              (JSCClass      *jsc_class,
+                                    const char    *name,
+                                    GCallback      callback,
+                                    gpointer       user_data,
+                                    GDestroyNotify destroy_notify,
+                                    GType          return_type,
+                                    guint          n_parameters,
+                                    GType         *parameter_types);
 
 JSC_API void
-jsc_class_add_methodv      (JSCClass      *jsc_class,
-                            const char    *name,
-                            GCallback      callback,
-                            gpointer       user_data,
-                            GDestroyNotify destroy_notify,
-                            GType          return_type,
-                            guint          n_parameters,
-                            GType         *parameter_types);
+jsc_class_add_method_variadic      (JSCClass      *jsc_class,
+                                    const char    *name,
+                                    GCallback      callback,
+                                    gpointer       user_data,
+                                    GDestroyNotify destroy_notify,
+                                    GType          return_type);
 
 JSC_API void
-jsc_class_add_property     (JSCClass      *jsc_class,
-                            const char    *name,
-                            GType          property_type,
-                            GCallback      getter,
-                            GCallback      setter,
-                            gpointer       user_data,
-                            GDestroyNotify destroy_notify);
+jsc_class_add_property             (JSCClass      *jsc_class,
+                                    const char    *name,
+                                    GType          property_type,
+                                    GCallback      getter,
+                                    GCallback      setter,
+                                    gpointer       user_data,
+                                    GDestroyNotify destroy_notify);
 
 G_END_DECLS
 
index 8748947..79e5316 100644 (file)
@@ -1089,14 +1089,14 @@ void jsc_value_object_define_property_accessor(JSCValue* value, const char* prop
     if (getter) {
         GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(getter, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
         auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), "get"_s,
-            JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), propertyType, { }));
+            JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), propertyType, Vector<GType> { }));
         GRefPtr<JSCValue> function = jscContextGetOrCreateValue(priv->context.get(), functionObject);
         jsc_value_object_set_property(descriptor.get(), "get", function.get());
     }
     if (setter) {
         GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(setter, userData, getter ? nullptr : reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
         auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), "set"_s,
-            JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), G_TYPE_NONE, { propertyType }));
+            JSC::JSCCallbackFunction::Type::Method, nullptr, WTFMove(closure), G_TYPE_NONE, Vector<GType> { propertyType }));
         GRefPtr<JSCValue> function = jscContextGetOrCreateValue(priv->context.get(), functionObject);
         jsc_value_object_set_property(descriptor.get(), "set", function.get());
     }
@@ -1105,7 +1105,7 @@ void jsc_value_object_define_property_accessor(JSCValue* value, const char* prop
         JSC_TYPE_VALUE, value, G_TYPE_STRING, propertyName, JSC_TYPE_VALUE, descriptor.get(), G_TYPE_NONE));
 }
 
-static GRefPtr<JSCValue> jscValueFunctionCreate(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, Vector<GType>&& parameters)
+static GRefPtr<JSCValue> jscValueFunctionCreate(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, std::optional<Vector<GType>>&& parameters)
 {
     GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
     JSC::ExecState* exec = toJS(jscContextGetJSContext(context));
@@ -1154,7 +1154,7 @@ JSCValue* jsc_value_new_function(JSCContext* context, const char* name, GCallbac
 
 /**
  * jsc_value_new_functionv: (rename-to jsc_value_new_function)
- * @context: a #JSCContext:
+ * @context: a #JSCContext
  * @name: (nullable): the function name or %NULL
  * @callback: (scope async): a #GCallback.
  * @user_data: (closure): user data to pass to @callback.
@@ -1187,6 +1187,30 @@ JSCValue* jsc_value_new_functionv(JSCContext* context, const char* name, GCallba
 }
 
 /**
+ * jsc_value_new_function_variadic:
+ * @context: a #JSCContext
+ * @name: (nullable): the function name or %NULL
+ * @callback: (scope async): a #GCallback.
+ * @user_data: (closure): user data to pass to @callback.
+ * @destroy_notify: (nullable): destroy notifier for @user_data
+ * @return_type: the #GType of the function return value, or %G_TYPE_NONE if the function is void.
+ *
+ * Create a function in @context. If @name is %NULL an anonymous function will be created.
+ * When the function is called by JavaScript or jsc_value_function_call(), @callback is called
+ * receiving an #GPtrArray of #JSCValue<!-- -->s with the arguments and then @user_data as last parameter.
+ * When the function is cleared in @context, @destroy_notify is called with @user_data as parameter.
+ *
+ * Returns: (transfer full): a #JSCValue.
+ */
+JSCValue* jsc_value_new_function_variadic(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType)
+{
+    g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
+    g_return_val_if_fail(callback, nullptr);
+
+    return jscValueFunctionCreate(context, name, callback, userData, destroyNotify, returnType, std::nullopt).leakRef();
+}
+
+/**
  * jsc_value_is_function:
  * @value: a #JSCValue
  *
index a18109e..eb5f5fe 100644 (file)
@@ -226,6 +226,14 @@ jsc_value_new_functionv                   (JSCContext           *context,
                                            guint                 n_parameters,
                                            GType                *parameter_types);
 
+JSC_API JSCValue *
+jsc_value_new_function_variadic           (JSCContext           *context,
+                                           const char           *name,
+                                           GCallback             callback,
+                                           gpointer              user_data,
+                                           GDestroyNotify        destroy_notify,
+                                           GType                 return_type);
+
 JSC_API gboolean
 jsc_value_is_function                     (JSCValue             *value);
 
index f1c4923..8d9800a 100644 (file)
@@ -99,6 +99,7 @@ jsc_value_object_define_property_data
 jsc_value_object_define_property_accessor
 jsc_value_new_function
 jsc_value_new_functionv
+jsc_value_new_function_variadic
 jsc_value_is_function
 jsc_value_function_call
 jsc_value_function_callv
@@ -178,8 +179,10 @@ jsc_class_get_name
 jsc_class_get_parent
 jsc_class_add_constructor
 jsc_class_add_constructorv
+jsc_class_add_constructor_variadic
 jsc_class_add_method
 jsc_class_add_methodv
+jsc_class_add_method_variadic
 jsc_class_add_property
 
 <SUBSECTION Standard>
index c559b1d..f64eceb 100644 (file)
@@ -1,5 +1,35 @@
 2018-07-11  Carlos Garcia Campos  <cgarcia@igalia.com>
 
+        [GLIB] Add API to allow creating variadic functions
+        https://bugs.webkit.org/show_bug.cgi?id=187517
+
+        Reviewed by Michael Catanzaro.
+
+        Add a _variadic alternate method for jsc_class_add_constructor, jsc_class_add_method and
+        jsc_value_new_function. In that case the callback always receives a GPtrArray of JSCValue.
+
+        * API/glib/JSCCallbackFunction.cpp:
+        (JSC::JSCCallbackFunction::create): Make the parameters optional.
+        (JSC::JSCCallbackFunction::JSCCallbackFunction): Ditto.
+        (JSC::JSCCallbackFunction::call): Handle the case of parameters being nullopt by creating a GPtrArray of
+        JSCValue for the arguments.
+        (JSC::JSCCallbackFunction::construct): Ditto.
+        * API/glib/JSCCallbackFunction.h:
+        * API/glib/JSCClass.cpp:
+        (jscClassCreateConstructor): Make the parameters optional.
+        (jsc_class_add_constructor_variadic): Pass nullopt as parameters to jscClassCreateConstructor.
+        (jscClassAddMethod): Make the parameters optional.
+        (jsc_class_add_method_variadic): Pass nullopt as parameters to jscClassAddMethod.
+        * API/glib/JSCClass.h:
+        * API/glib/JSCValue.cpp:
+        (jsc_value_object_define_property_accessor): Update now that parameters are optional.
+        (jscValueFunctionCreate): Make the parameters optional.
+        (jsc_value_new_function_variadic): Pass nullopt as parameters to jscValueFunctionCreate.
+        * API/glib/JSCValue.h:
+        * API/glib/docs/jsc-glib-4.0-sections.txt:
+
+2018-07-11  Carlos Garcia Campos  <cgarcia@igalia.com>
+
         [GLIB] Add jsc_context_get_global_object() to GLib API
         https://bugs.webkit.org/show_bug.cgi?id=187515
 
index 4aa199b..096ed3f 100644 (file)
@@ -1,5 +1,19 @@
 2018-07-11  Carlos Garcia Campos  <cgarcia@igalia.com>
 
+        [GLIB] Add API to allow creating variadic functions
+        https://bugs.webkit.org/show_bug.cgi?id=187517
+
+        Reviewed by Michael Catanzaro.
+
+        Add test cases.
+
+        * TestWebKitAPI/Tests/JavaScriptCore/glib/TestJSC.cpp:
+        (testJSCFunction):
+        (fooCreateWithFooV):
+        (multiplyFooV):
+
+2018-07-11  Carlos Garcia Campos  <cgarcia@igalia.com>
+
         [GLIB] Add jsc_context_get_global_object() to GLib API
         https://bugs.webkit.org/show_bug.cgi?id=187515
 
index 51c7b49..9dceaad 100644 (file)
@@ -867,6 +867,27 @@ static void testJSCFunction()
         g_assert_true(jsc_value_is_undefined(value.get()));
         g_assert_did_throw(exceptionHandler, didThrow);
     }
+
+    {
+        LeakChecker checker;
+        GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+        checker.watch(context.get());
+        ExceptionHandler exceptionHandler(context.get());
+
+        GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function_variadic(context.get(), "sumFunction", G_CALLBACK(sumFunction), nullptr, nullptr, G_TYPE_INT));
+        checker.watch(function.get());
+        jsc_context_set_value(context.get(), "sumFunction", function.get());
+
+        GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "sumFunction(2,4,6)", -1));
+        checker.watch(value.get());
+        g_assert_true(jsc_value_is_number(value.get()));
+        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12);
+
+        value = adoptGRef(jsc_context_evaluate(context.get(), "sumFunction()", -1));
+        checker.watch(value.get());
+        g_assert_true(jsc_value_is_number(value.get()));
+        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
+    }
 }
 
 static void testJSCObject()
@@ -1159,6 +1180,19 @@ static Foo* fooCreateWithFoo(int value)
     return f;
 }
 
+static Foo* fooCreateWithFooV(GPtrArray* values)
+{
+    auto* f = fooCreate();
+    g_ptr_array_foreach(values, [](gpointer data, gpointer userData) {
+        g_assert_true(JSC_IS_VALUE(data));
+        JSCValue* item = JSC_VALUE(data);
+        g_assert_true(jsc_value_is_number(item));
+        auto* foo = static_cast<Foo*>(userData);
+        foo->foo += jsc_value_to_int32(item);
+    }, f);
+    return f;
+}
+
 static void fooFree(Foo* foo)
 {
     foo->~Foo();
@@ -1190,6 +1224,17 @@ static void multiplyFoo(Foo* foo, int multiplier)
     foo->foo *= multiplier;
 }
 
+static void multiplyFooV(Foo* foo, GPtrArray* multipliers)
+{
+    g_ptr_array_foreach(multipliers, [](gpointer data, gpointer userData) {
+        g_assert_true(JSC_IS_VALUE(data));
+        JSCValue* item = JSC_VALUE(data);
+        g_assert_true(jsc_value_is_number(item));
+        auto* foo = static_cast<Foo*>(userData);
+        foo->foo *= jsc_value_to_int32(item);
+    }, foo);
+}
+
 static int fooGetProperty(Foo* foo, const char* name)
 {
     auto addResult = foo->properties.add(name, 0);
@@ -1413,6 +1458,21 @@ static void testJSCClass()
         g_assert_true(jsc_value_is_number(value.get()));
         g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 45);
 
+        jsc_class_add_method_variadic(jscClass, "multiply", G_CALLBACK(multiplyFooV), nullptr, nullptr, G_TYPE_NONE);
+        g_assert_true(jsc_value_object_has_property(foo.get(), "multiply"));
+        value = adoptGRef(jsc_context_evaluate(context.get(), "f.setFoo(1); f.multiply(1,2,3);", -1));
+        checker.watch(value.get());
+        value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE));
+        checker.watch(value.get());
+        g_assert_true(jsc_value_is_number(value.get()));
+        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 6);
+        value = adoptGRef(jsc_context_evaluate(context.get(), "f.multiply()", -1));
+        checker.watch(value.get());
+        value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE));
+        checker.watch(value.get());
+        g_assert_true(jsc_value_is_number(value.get()));
+        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 6);
+
         GRefPtr<JSCValue> constructor2 = adoptGRef(jsc_class_add_constructorv(jscClass, "CreateWithFoo", G_CALLBACK(fooCreateWithFoo), nullptr, nullptr, G_TYPE_POINTER, 1, parameterTypes));
         checker.watch(constructor2.get());
         g_assert_true(jsc_value_is_constructor(constructor2.get()));
@@ -1450,6 +1510,29 @@ static void testJSCClass()
         g_assert_true(jsc_value_is_number(value.get()));
         g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 52);
 
+        GRefPtr<JSCValue> constructorV = adoptGRef(jsc_class_add_constructor_variadic(jscClass, "CreateWithFoo", G_CALLBACK(fooCreateWithFooV), nullptr, nullptr, G_TYPE_POINTER));
+        checker.watch(constructorV.get());
+        g_assert_true(jsc_value_is_constructor(constructorV.get()));
+        jsc_value_object_set_property(constructor.get(), "CreateWithFooV", constructorV.get());
+
+        GRefPtr<JSCValue> foo3 = adoptGRef(jsc_context_evaluate(context.get(), "f3 = new Foo.CreateWithFooV(10,20,30,40);", -1));
+        checker.watch(foo3.get());
+        g_assert_true(jsc_value_is_object(foo3.get()));
+        g_assert_true(jsc_value_object_is_instance_of(foo3.get(), jsc_class_get_name(jscClass)));
+        value = adoptGRef(jsc_context_evaluate(context.get(), "f3.foo", -1));
+        checker.watch(value.get());
+        g_assert_true(jsc_value_is_number(value.get()));
+        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 100);
+
+        GRefPtr<JSCValue> foo4 = adoptGRef(jsc_context_evaluate(context.get(), "f4 = new Foo.CreateWithFooV();", -1));
+        checker.watch(foo4.get());
+        g_assert_true(jsc_value_is_object(foo4.get()));
+        g_assert_true(jsc_value_object_is_instance_of(foo3.get(), jsc_class_get_name(jscClass)));
+        value = adoptGRef(jsc_context_evaluate(context.get(), "f4.foo", -1));
+        checker.watch(value.get());
+        g_assert_true(jsc_value_is_number(value.get()));
+        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
+
         JSCClass* otherClass = jsc_context_register_class(context.get(), "Baz", nullptr, nullptr, g_free);
         checker.watch(otherClass);
         g_assert_false(jsc_class_get_parent(otherClass));