Title: [231693] trunk
Revision
231693
Author
carlo...@webkit.org
Date
2018-05-10 23:56:57 -0700 (Thu, 10 May 2018)

Log Message

[JSC][GLIB] Add introspectable alternatives to functions using vargars
https://bugs.webkit.org/show_bug.cgi?id=185508

Reviewed by Michael Catanzaro.

Source/_javascript_Core:

* API/glib/JSCClass.cpp:
(jscClassCreateConstructor):
(jsc_class_add_constructor):
(jsc_class_add_constructorv):
(jscClassAddMethod):
(jsc_class_add_method):
(jsc_class_add_methodv):
* API/glib/JSCClass.h:
* API/glib/JSCValue.cpp:
(jsObjectCall):
(jscValueCallFunction):
(jsc_value_object_invoke_methodv):
(jscValueFunctionCreate):
(jsc_value_new_function):
(jsc_value_new_functionv):
(jsc_value_function_callv):
(jsc_value_constructor_callv):
* API/glib/JSCValue.h:
* API/glib/docs/jsc-glib-4.0-sections.txt:

Tools:

Add test cases for the new API.

* TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp:
(testJSCFunction):
(testJSCObject):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/API/glib/JSCClass.cpp (231692 => 231693)


--- trunk/Source/_javascript_Core/API/glib/JSCClass.cpp	2018-05-11 05:34:20 UTC (rev 231692)
+++ trunk/Source/_javascript_Core/API/glib/JSCClass.cpp	2018-05-11 06:56:57 UTC (rev 231693)
@@ -521,8 +521,26 @@
     return jscClass->priv->parentClass;
 }
 
+static GRefPtr<JSCValue> jscClassCreateConstructor(JSCClass* jscClass, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, Vector<GType>&& parameters)
+{
+    JSCClassPrivate* priv = jscClass->priv;
+    GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
+    JSC::ExecState* exec = toJS(jscContextGetJSContext(priv->context));
+    JSC::VM& vm = exec->vm();
+    JSC::JSLockHolder locker(vm);
+    auto* functionObject = JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), String::fromUTF8(name),
+        JSC::JSCCallbackFunction::Type::Constructor, jscClass, WTFMove(closure), returnType, WTFMove(parameters));
+    auto constructor = jscContextGetOrCreateValue(priv->context, toRef(functionObject));
+    GRefPtr<JSCValue> prototype = jscContextGetOrCreateValue(priv->context, toRef(priv->prototype.get()));
+    auto nonEnumerable = static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_WRITABLE);
+    jsc_value_object_define_property_data(constructor.get(), "prototype", nonEnumerable, prototype.get());
+    jsc_value_object_define_property_data(prototype.get(), "constructor", nonEnumerable, constructor.get());
+    priv->constructors.set(name, functionObject);
+    return constructor;
+}
+
 /**
- * jsc_class_add_constructor:
+ * jsc_class_add_constructor: (skip)
  * @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
@@ -563,23 +581,70 @@
     }
     va_end(args);
 
+    return jscClassCreateConstructor(jscClass, name ? name : priv->name.data(), callback, userData, destroyNotify, returnType, WTFMove(parameters)).leakRef();
+
+}
+
+/**
+ * jsc_class_add_constructorv: (rename-to jsc_class_add_constructor)
+ * @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
+ * @n_parameters: the number of parameters
+ * @parameter_types: (nullable) (array length=n_parameters) (element-type GType): a list of #GType<!-- -->s, one for each parameter, or %NULL
+ *
+ * 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 the
+ * 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
+ * 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_constructorv(JSCClass* jscClass, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, unsigned parametersCount, GType* parameterTypes)
+{
+    g_return_val_if_fail(JSC_IS_CLASS(jscClass), nullptr);
+    g_return_val_if_fail(callback, nullptr);
+    g_return_val_if_fail(!parametersCount || parameterTypes, nullptr);
+
+    JSCClassPrivate* priv = jscClass->priv;
+    g_return_val_if_fail(priv->context, nullptr);
+
+    if (!name)
+        name = priv->name.data();
+
+    Vector<GType> parameters;
+    if (parametersCount) {
+        parameters.reserveInitialCapacity(parametersCount);
+        for (unsigned i = 0; i < parametersCount; ++i)
+            parameters.uncheckedAppend(parameterTypes[i]);
+    }
+
+    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)
+{
+    JSCClassPrivate* priv = jscClass->priv;
     GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
     JSC::ExecState* exec = toJS(jscContextGetJSContext(priv->context));
     JSC::VM& vm = exec->vm();
     JSC::JSLockHolder locker(vm);
-    auto* functionObject = JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), String::fromUTF8(name),
-        JSC::JSCCallbackFunction::Type::Constructor, jscClass, WTFMove(closure), returnType, WTFMove(parameters));
-    auto constructor = jscContextGetOrCreateValue(priv->context, toRef(functionObject));
+    auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), String::fromUTF8(name),
+        JSC::JSCCallbackFunction::Type::Method, jscClass, WTFMove(closure), returnType, WTFMove(parameters)));
+    auto method = jscContextGetOrCreateValue(priv->context, functionObject);
     GRefPtr<JSCValue> prototype = jscContextGetOrCreateValue(priv->context, toRef(priv->prototype.get()));
     auto nonEnumerable = static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_WRITABLE);
-    jsc_value_object_define_property_data(constructor.get(), "prototype", nonEnumerable, prototype.get());
-    jsc_value_object_define_property_data(prototype.get(), "constructor", nonEnumerable, constructor.get());
-    priv->constructors.set(name, functionObject);
-    return constructor.leakRef();
+    jsc_value_object_define_property_data(prototype.get(), name, nonEnumerable, method.get());
 }
 
 /**
- * jsc_class_add_method:
+ * jsc_class_add_method: (skip)
  * @jsc_class: a #JSCClass
  * @name: the method name
  * @callback: (scope async): a #GCallback to be called to invoke method @name of @jsc_class
@@ -599,10 +664,8 @@
     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);
 
-    JSCClassPrivate* priv = jscClass->priv;
-    g_return_if_fail(priv->context);
-
     va_list args;
     va_start(args, paramCount);
     Vector<GType> parameters;
@@ -613,19 +676,44 @@
     }
     va_end(args);
 
-    GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
-    JSC::ExecState* exec = toJS(jscContextGetJSContext(priv->context));
-    JSC::VM& vm = exec->vm();
-    JSC::JSLockHolder locker(vm);
-    auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), String::fromUTF8(name),
-        JSC::JSCCallbackFunction::Type::Method, jscClass, WTFMove(closure), returnType, WTFMove(parameters)));
-    auto method = jscContextGetOrCreateValue(priv->context, functionObject);
-    GRefPtr<JSCValue> prototype = jscContextGetOrCreateValue(priv->context, toRef(priv->prototype.get()));
-    auto nonEnumerable = static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_WRITABLE);
-    jsc_value_object_define_property_data(prototype.get(), name, nonEnumerable, method.get());
+    jscClassAddMethod(jscClass, name, callback, userData, destroyNotify, returnType, WTFMove(parameters));
 }
 
 /**
+ * jsc_class_add_methodv: (rename-to jsc_class_add_method)
+ * @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.
+ * @n_parameters: the number of parameter types to follow or 0 if the method doesn't receive parameters.
+ * @parameter_types: (nullable) (array length=n_parameters) (element-type GType): a list of #GType<!-- -->s, one for each parameter, or %NULL
+ *
+ * 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 the method parameters 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_methodv(JSCClass* jscClass, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, unsigned parametersCount, GType *parameterTypes)
+{
+    g_return_if_fail(JSC_IS_CLASS(jscClass));
+    g_return_if_fail(name);
+    g_return_if_fail(callback);
+    g_return_if_fail(!parametersCount || parameterTypes);
+    g_return_if_fail(jscClass->priv->context);
+
+    Vector<GType> parameters;
+    if (parametersCount) {
+        parameters.reserveInitialCapacity(parametersCount);
+        for (unsigned i = 0; i < parametersCount; ++i)
+            parameters.uncheckedAppend(parameterTypes[i]);
+    }
+
+    jscClassAddMethod(jscClass, name, callback, userData, destroyNotify, returnType, WTFMove(parameters));
+}
+
+/**
  * jsc_class_add_property:
  * @jsc_class: a #JSCClass
  * @name: the property name

Modified: trunk/Source/_javascript_Core/API/glib/JSCClass.h (231692 => 231693)


--- trunk/Source/_javascript_Core/API/glib/JSCClass.h	2018-05-11 05:34:20 UTC (rev 231692)
+++ trunk/Source/_javascript_Core/API/glib/JSCClass.h	2018-05-11 06:56:57 UTC (rev 231693)
@@ -76,43 +76,63 @@
 } 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,
-                           unsigned       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_API void
-jsc_class_add_method      (JSCClass      *jsc_class,
-                           const char    *name,
-                           GCallback      callback,
-                           gpointer       user_data,
-                           GDestroyNotify destroy_notify,
-                           GType          return_type,
-                           unsigned       n_params,
-                           ...);
+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_property    (JSCClass      *jsc_class,
-                           const char    *name,
-                           GType          property_type,
-                           GCallback      getter,
-                           GCallback      setter,
-                           gpointer       user_data,
-                           GDestroyNotify destroy_notify);
+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_property     (JSCClass      *jsc_class,
+                            const char    *name,
+                            GType          property_type,
+                            GCallback      getter,
+                            GCallback      setter,
+                            gpointer       user_data,
+                            GDestroyNotify destroy_notify);
+
 G_END_DECLS
 
 #endif /* JSCClass_h */

Modified: trunk/Source/_javascript_Core/API/glib/JSCValue.cpp (231692 => 231693)


--- trunk/Source/_javascript_Core/API/glib/JSCValue.cpp	2018-05-11 05:34:20 UTC (rev 231692)
+++ trunk/Source/_javascript_Core/API/glib/JSCValue.cpp	2018-05-11 06:56:57 UTC (rev 231693)
@@ -457,7 +457,7 @@
 }
 
 /**
- * jsc_value_new_array:
+ * jsc_value_new_array: (skip)
  * @context: a #JSCContext
  * @first_item_type: #GType of first item, or %G_TYPE_NONE
  * @...: value of the first item, followed optionally by more type/value pairs, followed by %G_TYPE_NONE.
@@ -831,6 +831,22 @@
     return result;
 }
 
+static JSValueRef jsObjectCall(JSGlobalContextRef jsContext, JSObjectRef function, JSC::JSCCallbackFunction::Type functionType, JSObjectRef thisObject, const Vector<JSValueRef>& arguments, JSValueRef* exception)
+{
+    switch (functionType) {
+    case JSC::JSCCallbackFunction::Type::Constructor:
+        return JSObjectCallAsConstructor(jsContext, function, arguments.size(), arguments.data(), exception);
+        break;
+    case JSC::JSCCallbackFunction::Type::Method:
+        ASSERT(thisObject);
+        FALLTHROUGH;
+    case JSC::JSCCallbackFunction::Type::Function:
+        return JSObjectCallAsFunction(jsContext, function, thisObject, arguments.size(), arguments.data(), exception);
+        break;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
 static GRefPtr<JSCValue> jscValueCallFunction(JSCValue* value, JSObjectRef function, JSC::JSCCallbackFunction::Type functionType, JSObjectRef thisObject, GType firstParameterType, va_list args)
 {
     JSCValuePrivate* priv = value->priv;
@@ -858,18 +874,7 @@
         parameterType = va_arg(args, GType);
     }
 
-    JSValueRef result;
-    switch (functionType) {
-    case JSC::JSCCallbackFunction::Type::Constructor:
-        result = JSObjectCallAsConstructor(jsContext, function, arguments.size(), arguments.data(), &exception);
-        break;
-    case JSC::JSCCallbackFunction::Type::Method:
-        ASSERT(thisObject);
-        FALLTHROUGH;
-    case JSC::JSCCallbackFunction::Type::Function:
-        result = JSObjectCallAsFunction(jsContext, function, thisObject, arguments.size(), arguments.data(), &exception);
-        break;
-    }
+    auto result = jsObjectCall(jsContext, function, functionType, thisObject, arguments, &exception);
     if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
         return adoptGRef(jsc_value_new_undefined(priv->context.get()));
 
@@ -877,7 +882,7 @@
 }
 
 /**
- * jsc_value_object_invoke_method:
+ * jsc_value_object_invoke_method: (skip)
  * @value: a #JSCValue
  * @name: the method name
  * @first_parameter_type: #GType of first parameter, or %G_TYPE_NONE
@@ -924,6 +929,60 @@
 }
 
 /**
+ * jsc_value_object_invoke_methodv: (rename-to jsc_value_object_invoke_method)
+ * @value: a #JSCValue
+ * @name: the method name
+ * @n_parameters: the number of parameters
+ * @parameters: (nullable) (array length=n_parameters) (element-type JSCValue): the #JSCValue<!-- -->s to pass as parameters to the method, or %NULL
+ *
+ * Invoke method with @name on object referenced by @value, passing the given @parameters. If
+ * @n_parameters is 0 no parameters will be passed to the method.
+ * The object instance will be handled automatically even when the method is a custom one
+ * registered with jsc_class_add_method(), so it should never be passed explicitly as parameter
+ * of this function.
+ *
+ * This function always returns a #JSCValue, in case of void methods a #JSCValue referencing
+ * <function>undefined</function> is returned.
+ *
+ * Returns: (transfer full): a #JSCValue with the return value of the method.
+ */
+JSCValue* jsc_value_object_invoke_methodv(JSCValue* value, const char* name, unsigned parametersCount, JSCValue** parameters)
+{
+    g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
+    g_return_val_if_fail(name, nullptr);
+    g_return_val_if_fail(!parametersCount || parameters, nullptr);
+
+    JSCValuePrivate* priv = value->priv;
+    auto* jsContext = jscContextGetJSContext(priv->context.get());
+    JSValueRef exception = nullptr;
+    JSObjectRef object = JSValueToObject(jsContext, priv->jsValue, &exception);
+    if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+        return jsc_value_new_undefined(priv->context.get());
+
+    JSRetainPtr<JSStringRef> methodName(Adopt, JSStringCreateWithUTF8CString(name));
+    JSValueRef functionValue = JSObjectGetProperty(jsContext, object, methodName.get(), &exception);
+    if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+        return jsc_value_new_undefined(priv->context.get());
+
+    JSObjectRef function = JSValueToObject(jsContext, functionValue, &exception);
+    if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+        return jsc_value_new_undefined(priv->context.get());
+
+    Vector<JSValueRef> arguments;
+    if (parametersCount) {
+        arguments.reserveInitialCapacity(parametersCount);
+        for (unsigned i = 0; i < parametersCount; ++i)
+            arguments.uncheckedAppend(jscValueGetJSValue(parameters[i]));
+    }
+
+    auto result = jsObjectCall(jsContext, function, JSC::JSCCallbackFunction::Type::Method, object, arguments, &exception);
+    if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+        jsc_value_new_undefined(priv->context.get());
+
+    return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
+}
+
+/**
  * JSCValuePropertyFlags:
  * @JSC_VALUE_PROPERTY_CONFIGURABLE: the type of the property descriptor may be changed and the
  *  property may be deleted from the corresponding object.
@@ -1021,8 +1080,19 @@
         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)
+{
+    GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
+    JSC::ExecState* exec = toJS(jscContextGetJSContext(context));
+    JSC::VM& vm = exec->vm();
+    JSC::JSLockHolder locker(vm);
+    auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), name ? String::fromUTF8(name) : ASCIILiteral("anonymous"),
+        JSC::JSCCallbackFunction::Type::Function, nullptr, WTFMove(closure), returnType, WTFMove(parameters)));
+    return jscContextGetOrCreateValue(context, functionObject);
+}
+
 /**
- * jsc_value_new_function:
+ * jsc_value_new_function: (skip)
  * @context: a #JSCContext:
  * @name: (nullable): the function name or %NULL
  * @callback: (scope async): a #GCallback.
@@ -1054,16 +1124,44 @@
     }
     va_end(args);
 
-    GRefPtr<GClosure> closure = adoptGRef(g_cclosure_new(callback, userData, reinterpret_cast<GClosureNotify>(reinterpret_cast<GCallback>(destroyNotify))));
-    JSC::ExecState* exec = toJS(jscContextGetJSContext(context));
-    JSC::VM& vm = exec->vm();
-    JSC::JSLockHolder locker(vm);
-    auto* functionObject = toRef(JSC::JSCCallbackFunction::create(vm, exec->lexicalGlobalObject(), name ? String::fromUTF8(name) : ASCIILiteral("anonymous"),
-        JSC::JSCCallbackFunction::Type::Function, nullptr, WTFMove(closure), returnType, WTFMove(parameters)));
-    return jscContextGetOrCreateValue(context, functionObject).leakRef();
+    return jscValueFunctionCreate(context, name, callback, userData, destroyNotify, returnType, WTFMove(parameters)).leakRef();
 }
 
 /**
+ * jsc_value_new_functionv: (rename-to jsc_value_new_function)
+ * @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.
+ * @n_parameters: the number of parameters
+ * @parameter_types: (nullable) (array length=n_parameters) (element-type GType): a list of #GType<!-- -->s, one for each parameter, or %NULL
+ *
+ * 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 the function parameters 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_functionv(JSCContext* context, const char* name, GCallback callback, gpointer userData, GDestroyNotify destroyNotify, GType returnType, unsigned parametersCount, GType *parameterTypes)
+{
+    g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
+    g_return_val_if_fail(callback, nullptr);
+    g_return_val_if_fail(!parametersCount || parameterTypes, nullptr);
+
+    Vector<GType> parameters;
+    if (parametersCount) {
+        parameters.reserveInitialCapacity(parametersCount);
+        for (unsigned i = 0; i < parametersCount; ++i)
+            parameters.uncheckedAppend(parameterTypes[i]);
+    }
+
+    return jscValueFunctionCreate(context, name, callback, userData, destroyNotify, returnType, WTFMove(parameters)).leakRef();
+}
+
+/**
  * jsc_value_is_function:
  * @value: a #JSCValue
  *
@@ -1083,7 +1181,7 @@
 }
 
 /**
- * jsc_value_function_call:
+ * jsc_value_function_call: (skip)
  * @value: a #JSCValue
  * @first_parameter_type: #GType of first parameter, or %G_TYPE_NONE
  * @...: value of the first parameter, followed optionally by more type/value pairs, followed by %G_TYPE_NONE
@@ -1116,6 +1214,46 @@
 }
 
 /**
+ * jsc_value_function_callv: (rename-to jsc_value_function_call)
+ * @value: a #JSCValue
+ * @n_parameters: the number of parameters
+ * @parameters: (nullable) (array length=n_parameters) (element-type JSCValue): the #JSCValue<!-- -->s to pass as parameters to the function, or %NULL
+ *
+ * Call function referenced by @value, passing the given @parameters. If @n_parameters
+ * is 0 no parameters will be passed to the function.
+ *
+ * This function always returns a #JSCValue, in case of void functions a #JSCValue referencing
+ * <function>undefined</function> is returned
+ *
+ * Returns: (transfer full): a #JSCValue with the return value of the function.
+ */
+JSCValue* jsc_value_function_callv(JSCValue* value, unsigned parametersCount, JSCValue** parameters)
+{
+    g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
+    g_return_val_if_fail(!parametersCount || parameters, nullptr);
+
+    JSCValuePrivate* priv = value->priv;
+    auto* jsContext = jscContextGetJSContext(priv->context.get());
+    JSValueRef exception = nullptr;
+    JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
+    if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+        return jsc_value_new_undefined(priv->context.get());
+
+    Vector<JSValueRef> arguments;
+    if (parametersCount) {
+        arguments.reserveInitialCapacity(parametersCount);
+        for (unsigned i = 0; i < parametersCount; ++i)
+            arguments.uncheckedAppend(jscValueGetJSValue(parameters[i]));
+    }
+
+    auto result = jsObjectCall(jsContext, function, JSC::JSCCallbackFunction::Type::Function, nullptr, arguments, &exception);
+    if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+        return jsc_value_new_undefined(priv->context.get());
+
+    return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
+}
+
+/**
  * jsc_value_is_constructor:
  * @value: a #JSCValue
  *
@@ -1135,7 +1273,7 @@
 }
 
 /**
- * jsc_value_constructor_call:
+ * jsc_value_constructor_call: (skip)
  * @value: a #JSCValue
  * @first_parameter_type: #GType of first parameter, or %G_TYPE_NONE
  * @...: value of the first parameter, followed optionally by more type/value pairs, followed by %G_TYPE_NONE
@@ -1163,3 +1301,40 @@
 
     return result.leakRef();
 }
+
+/**
+ * jsc_value_constructor_callv: (rename-to jsc_value_constructor_call)
+ * @value: a #JSCValue
+ * @n_parameters: the number of parameters
+ * @parameters: (nullable) (array length=n_parameters) (element-type JSCValue): the #JSCValue<!-- -->s to pass as parameters to the constructor, or %NULL
+ *
+ * Invoke <function>new</function> with constructor referenced by @value. If @n_parameters
+ * is 0 no parameters will be passed to the constructor.
+ *
+ * Returns: (transfer full): a #JSCValue referencing the newly created object instance.
+ */
+JSCValue* jsc_value_constructor_callv(JSCValue* value, unsigned parametersCount, JSCValue** parameters)
+{
+    g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
+    g_return_val_if_fail(!parametersCount || parameters, nullptr);
+
+    JSCValuePrivate* priv = value->priv;
+    auto* jsContext = jscContextGetJSContext(priv->context.get());
+    JSValueRef exception = nullptr;
+    JSObjectRef function = JSValueToObject(jsContext, priv->jsValue, &exception);
+    if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+        return jsc_value_new_undefined(priv->context.get());
+
+    Vector<JSValueRef> arguments;
+    if (parametersCount) {
+        arguments.reserveInitialCapacity(parametersCount);
+        for (unsigned i = 0; i < parametersCount; ++i)
+            arguments.uncheckedAppend(jscValueGetJSValue(parameters[i]));
+    }
+
+    auto result = jsObjectCall(jsContext, function, JSC::JSCCallbackFunction::Type::Constructor, nullptr, arguments, &exception);
+    if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+        return jsc_value_new_undefined(priv->context.get());
+
+    return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
+}

Modified: trunk/Source/_javascript_Core/API/glib/JSCValue.h (231692 => 231693)


--- trunk/Source/_javascript_Core/API/glib/JSCValue.h	2018-05-11 05:34:20 UTC (rev 231692)
+++ trunk/Source/_javascript_Core/API/glib/JSCValue.h	2018-05-11 06:56:57 UTC (rev 231693)
@@ -180,6 +180,12 @@
                                            GType                 first_parameter_type,
                                            ...);
 
+JSC_API JSCValue *
+jsc_value_object_invoke_methodv           (JSCValue             *value,
+                                           const char           *name,
+                                           guint                 n_parameters,
+                                           JSCValue            **parameters);
+
 JSC_API void
 jsc_value_object_define_property_data     (JSCValue             *value,
                                            const char           *property_name,
@@ -206,6 +212,16 @@
                                            guint                 n_params,
                                            ...);
 
+JSC_API JSCValue *
+jsc_value_new_functionv                   (JSCContext           *context,
+                                           const char           *name,
+                                           GCallback             callback,
+                                           gpointer              user_data,
+                                           GDestroyNotify        destroy_notify,
+                                           GType                 return_type,
+                                           guint                 n_parameters,
+                                           GType                *parameter_types);
+
 JSC_API gboolean
 jsc_value_is_function                     (JSCValue             *value);
 
@@ -214,6 +230,11 @@
                                            GType                 first_parameter_type,
                                            ...);
 
+JSC_API JSCValue *
+jsc_value_function_callv                  (JSCValue             *value,
+                                           guint                 n_parameters,
+                                           JSCValue            **parameters);
+
 JSC_API gboolean
 jsc_value_is_constructor                  (JSCValue             *value);
 
@@ -222,6 +243,11 @@
                                            GType                 first_parameter_type,
                                            ...);
 
+JSC_API JSCValue *
+jsc_value_constructor_callv               (JSCValue             *value,
+                                           guint                 n_parameters,
+                                           JSCValue            **parameters);
+
 G_END_DECLS
 
 #endif /* JSCValue_h */

Modified: trunk/Source/_javascript_Core/API/glib/docs/jsc-glib-4.0-sections.txt (231692 => 231693)


--- trunk/Source/_javascript_Core/API/glib/docs/jsc-glib-4.0-sections.txt	2018-05-11 05:34:20 UTC (rev 231692)
+++ trunk/Source/_javascript_Core/API/glib/docs/jsc-glib-4.0-sections.txt	2018-05-11 06:56:57 UTC (rev 231693)
@@ -92,13 +92,17 @@
 jsc_value_object_delete_property
 jsc_value_object_enumerate_properties
 jsc_value_object_invoke_method
+jsc_value_object_invoke_methodv
 jsc_value_object_define_property_data
 jsc_value_object_define_property_accessor
 jsc_value_new_function
+jsc_value_new_functionv
 jsc_value_is_function
 jsc_value_function_call
+jsc_value_function_callv
 jsc_value_is_constructor
 jsc_value_constructor_call
+jsc_value_constructor_callv
 
 <SUBSECTION Standard>
 JSCValueClass
@@ -171,7 +175,9 @@
 jsc_class_get_name
 jsc_class_get_parent
 jsc_class_add_constructor
+jsc_class_add_constructorv
 jsc_class_add_method
+jsc_class_add_methodv
 jsc_class_add_property
 
 <SUBSECTION Standard>

Modified: trunk/Source/_javascript_Core/ChangeLog (231692 => 231693)


--- trunk/Source/_javascript_Core/ChangeLog	2018-05-11 05:34:20 UTC (rev 231692)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-05-11 06:56:57 UTC (rev 231693)
@@ -1,3 +1,30 @@
+2018-05-10  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [JSC][GLIB] Add introspectable alternatives to functions using vargars
+        https://bugs.webkit.org/show_bug.cgi?id=185508
+
+        Reviewed by Michael Catanzaro.
+
+        * API/glib/JSCClass.cpp:
+        (jscClassCreateConstructor):
+        (jsc_class_add_constructor):
+        (jsc_class_add_constructorv):
+        (jscClassAddMethod):
+        (jsc_class_add_method):
+        (jsc_class_add_methodv):
+        * API/glib/JSCClass.h:
+        * API/glib/JSCValue.cpp:
+        (jsObjectCall):
+        (jscValueCallFunction):
+        (jsc_value_object_invoke_methodv):
+        (jscValueFunctionCreate):
+        (jsc_value_new_function):
+        (jsc_value_new_functionv):
+        (jsc_value_function_callv):
+        (jsc_value_constructor_callv):
+        * API/glib/JSCValue.h:
+        * API/glib/docs/jsc-glib-4.0-sections.txt:
+
 2018-05-10  Yusuke Suzuki  <utatane....@gmail.com>
 
         [JSC] Make return types of construction functions tight

Modified: trunk/Tools/ChangeLog (231692 => 231693)


--- trunk/Tools/ChangeLog	2018-05-11 05:34:20 UTC (rev 231692)
+++ trunk/Tools/ChangeLog	2018-05-11 06:56:57 UTC (rev 231693)
@@ -1,3 +1,16 @@
+2018-05-10  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        [JSC][GLIB] Add introspectable alternatives to functions using vargars
+        https://bugs.webkit.org/show_bug.cgi?id=185508
+
+        Reviewed by Michael Catanzaro.
+
+        Add test cases for the new API.
+
+        * TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp:
+        (testJSCFunction):
+        (testJSCObject):
+
 2018-05-10  Leo Balter  <leonardo.bal...@gmail.com>
 
         [Build Bot] Restore configuration options to the Test262 Runner

Modified: trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp (231692 => 231693)


--- trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp	2018-05-11 05:34:20 UTC (rev 231692)
+++ trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp	2018-05-11 06:56:57 UTC (rev 231693)
@@ -581,6 +581,14 @@
         GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_INT, 200, G_TYPE_NONE));
         checker.watch(value.get());
         g_assert_true(value.get() == result.get());
+
+        GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
+        auto* parameter = jsc_value_new_number(context.get(), 200);
+        checker.watch(parameter);
+        g_ptr_array_add(parameters.get(), parameter);
+        value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
+        checker.watch(value.get());
+        g_assert_true(value.get() == result.get());
     }
 
     {
@@ -589,6 +597,34 @@
         checker.watch(context.get());
         ExceptionHandler exceptionHandler(context.get());
 
+        GType parameterTypes[] = { G_TYPE_INT };
+        GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_functionv(context.get(), "foo", G_CALLBACK(foo), nullptr, nullptr,  G_TYPE_INT, 1, parameterTypes));
+        checker.watch(function.get());
+        jsc_context_set_value(context.get(), "foo", function.get());
+        GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo(200)", -1));
+        checker.watch(result.get());
+        g_assert_true(jsc_value_is_number(result.get()));
+        g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
+
+        GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_INT, 200, G_TYPE_NONE));
+        checker.watch(value.get());
+        g_assert_true(value.get() == result.get());
+
+        GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
+        auto* parameter = jsc_value_new_number(context.get(), 200);
+        checker.watch(parameter);
+        g_ptr_array_add(parameters.get(), parameter);
+        value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
+        checker.watch(value.get());
+        g_assert_true(value.get() == result.get());
+    }
+
+    {
+        LeakChecker checker;
+        GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+        checker.watch(context.get());
+        ExceptionHandler exceptionHandler(context.get());
+
         GRefPtr<JSCValue> function = adoptGRef(jsc_context_evaluate(context.get(), "foo = function(n) { return n * 2; }", -1));
         checker.watch(function.get());
 
@@ -600,6 +636,14 @@
         GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_INT, 200, G_TYPE_NONE));
         checker.watch(value.get());
         g_assert_true(value.get() == result.get());
+
+        GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
+        auto* parameter = jsc_value_new_number(context.get(), 200);
+        checker.watch(parameter);
+        g_ptr_array_add(parameters.get(), parameter);
+        value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
+        checker.watch(value.get());
+        g_assert_true(value.get() == result.get());
     }
 
     {
@@ -631,6 +675,15 @@
         checker.watch(result.get());
         g_assert_true(jsc_value_is_number(result.get()));
         g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
+
+        GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new());
+        g_ptr_array_add(parameters.get(), dbl.get());
+        value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
+        checker.watch(value.get());
+        result = adoptGRef(jsc_context_evaluate(context.get(), "result", -1));
+        checker.watch(result.get());
+        g_assert_true(jsc_value_is_number(result.get()));
+        g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
     }
 
     {
@@ -715,6 +768,15 @@
         g_assert_true(jsc_value_is_number(result.get()));
         g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
 
+        GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
+        auto* parameter = jsc_value_new_number(context.get(), 200);
+        checker.watch(parameter);
+        g_ptr_array_add(parameters.get(), parameter);
+        result = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "foo", parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
+        checker.watch(result.get());
+        g_assert_true(jsc_value_is_number(result.get()));
+        g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400);
+
         g_assert_false(jsc_value_object_has_property(foo.get(), "bar"));
         bool didThrow = false;
         g_assert_throw_begin(exceptionHandler, didThrow);
@@ -723,6 +785,12 @@
         g_assert_true(jsc_value_is_undefined(result.get()));
         g_assert_did_throw(exceptionHandler, didThrow);
 
+        g_assert_throw_begin(exceptionHandler, didThrow);
+        result = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "bar", parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
+        checker.watch(result.get());
+        g_assert_true(jsc_value_is_undefined(result.get()));
+        g_assert_did_throw(exceptionHandler, didThrow);
+
         GRefPtr<JSCValue> constructor = adoptGRef(jsc_context_evaluate(context.get(), "Foo", -1));
         checker.watch(constructor.get());
         g_assert_true(jsc_value_is_constructor(constructor.get()));
@@ -747,6 +815,12 @@
         checker.watch(result.get());
         g_assert_true(jsc_value_is_number(result.get()));
         g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 1000);
+
+        GRefPtr<JSCValue> foo2 = adoptGRef(jsc_value_constructor_callv(constructor.get(), 0, nullptr));
+        checker.watch(foo2.get());
+        g_assert_true(jsc_value_is_object(foo2.get()));
+        g_assert_true(jsc_value_object_is_instance_of(foo2.get(), "Foo"));
+        g_assert_false(foo.get() == foo2.get());
     }
 
     {
@@ -1182,12 +1256,18 @@
         g_assert_true(jsc_value_is_number(value.get()));
         g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
 
+        value = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "getFoo", 0, nullptr));
+        checker.watch(value.get());
+        g_assert_true(jsc_value_is_number(value.get()));
+        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0);
+
         GRefPtr<JSCValue> value2 = adoptGRef(jsc_context_evaluate(context.get(), "f.getFoo()", -1));
         checker.watch(value2.get());
         g_assert_true(value.get() == value2.get());
 
         g_assert_false(jsc_value_object_has_property(foo.get(), "setFoo"));
-        jsc_class_add_method(jscClass, "setFoo", G_CALLBACK(setFoo), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_INT);
+        GType parameterTypes[] = { G_TYPE_INT };
+        jsc_class_add_methodv(jscClass, "setFoo", G_CALLBACK(setFoo), nullptr, nullptr, G_TYPE_NONE, 1, parameterTypes);
         g_assert_true(jsc_value_object_has_property(foo.get(), "setFoo"));
         properties.reset(jsc_value_object_enumerate_properties(foo.get()));
         g_assert_null(properties.get());
@@ -1205,7 +1285,12 @@
         g_assert_true(jsc_value_is_number(value.get()));
         g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 45);
 
-        GRefPtr<JSCValue> constructor2 = adoptGRef(jsc_class_add_constructor(jscClass, "CreateWithFoo", G_CALLBACK(fooCreateWithFoo), nullptr, nullptr, G_TYPE_POINTER, 1, G_TYPE_INT));
+        value = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "getFoo", 0, nullptr));
+        checker.watch(value.get());
+        g_assert_true(jsc_value_is_number(value.get()));
+        g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 45);
+
+        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()));
         jsc_value_object_set_property(constructor.get(), "CreateWithFoo", constructor2.get());
@@ -1311,7 +1396,7 @@
         JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree));
         checker.watch(jscClass);
 
-        GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE));
+        GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructorv(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, nullptr));
         checker.watch(constructor.get());
         g_assert_true(jsc_value_is_constructor(constructor.get()));
         jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get());
@@ -1554,7 +1639,12 @@
         g_assert_true(jsc_value_is_number(result.get()));
         g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42);
 
-        GRefPtr<JSCValue> foo3 = adoptGRef(jsc_value_constructor_call(constructor2.get(), G_TYPE_INT, 62, G_TYPE_NONE));
+        GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref));
+        auto* parameter = jsc_value_new_number(context.get(), 62);
+        checker.watch(parameter);
+        g_ptr_array_add(parameters.get(), parameter);
+
+        GRefPtr<JSCValue> foo3 = adoptGRef(jsc_value_constructor_callv(constructor2.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata)));
         checker.watch(foo3.get());
         g_assert_true(jsc_value_is_object(foo3.get()));
         g_assert_true(jsc_value_object_is_instance_of(foo3.get(), "wk.Foo"));
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to