[gjs/wip/ptomato/mozjs31prep] WIP - Rooting-safe, typesafe gjs_parse_call_args()
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/ptomato/mozjs31prep] WIP - Rooting-safe, typesafe gjs_parse_call_args()
- Date: Sun, 16 Oct 2016 06:26:15 +0000 (UTC)
commit 567097613c8294363a742a6af311d505afe49390
Author: Philip Chimento <philip chimento gmail com>
Date: Sat Oct 15 23:25:10 2016 -0700
WIP - Rooting-safe, typesafe gjs_parse_call_args()
Still needs tests and I think the optional arguments don't work yet
Makefile-test.am | 1 +
Makefile.am | 1 +
gi/object.cpp | 37 ++--
gjs/byteArray.cpp | 5 +-
gjs/coverage.cpp | 6 +-
gjs/jsapi-util-args.h | 380 +++++++++++++++++++++++++++++++++++++
gjs/jsapi-util.cpp | 267 --------------------------
gjs/jsapi-util.h | 13 --
modules/cairo-context.cpp | 104 +++++-----
modules/cairo-gradient.cpp | 23 ++-
modules/cairo-image-surface.cpp | 13 +-
modules/cairo-linear-gradient.cpp | 11 +-
modules/cairo-pdf-surface.cpp | 9 +-
modules/cairo-ps-surface.cpp | 9 +-
modules/cairo-radial-gradient.cpp | 15 +-
modules/cairo-region.cpp | 18 +-
modules/cairo-solid-pattern.cpp | 19 +-
modules/cairo-surface-pattern.cpp | 15 +-
modules/cairo-surface.cpp | 5 +-
modules/cairo-svg-surface.cpp | 9 +-
modules/system.cpp | 19 +-
test/gjs-test-call-args.cpp | 194 +++++++++++++++++++
test/gjs-tests-add-funcs.h | 2 +
test/gjs-tests.cpp | 23 ++-
24 files changed, 757 insertions(+), 441 deletions(-)
---
diff --git a/Makefile-test.am b/Makefile-test.am
index bd6beb4..428fc8f 100644
--- a/Makefile-test.am
+++ b/Makefile-test.am
@@ -92,6 +92,7 @@ gjs_tests_LDADD = \
gjs_tests_SOURCES = \
test/gjs-tests.cpp \
test/gjs-tests-add-funcs.h \
+ test/gjs-test-call-args.cpp \
test/gjs-test-coverage.cpp \
mock-js-resources.c \
$(NULL)
diff --git a/Makefile.am b/Makefile.am
index 68e7e34..29b502d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -114,6 +114,7 @@ libgjs_la_SOURCES = \
gjs/jsapi-private.h \
gjs/jsapi-util.cpp \
gjs/jsapi-dynamic-class.cpp \
+ gjs/jsapi-util-args.h \
gjs/jsapi-util-array.cpp \
gjs/jsapi-util-error.cpp \
gjs/jsapi-util-string.cpp \
diff --git a/gi/object.cpp b/gi/object.cpp
index e6c0189..1fe93da 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -30,6 +30,7 @@
#include "object.h"
#include "gtype.h"
#include "interface.h"
+#include "gjs/jsapi-util-args.h"
#include "arg.h"
#include "repo.h"
#include "gtype.h"
@@ -2235,8 +2236,7 @@ gjs_hook_up_vfunc(JSContext *cx,
{
JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
gchar *name;
- JS::RootedObject object(cx);
- JSObject *function;
+ JS::RootedObject object(cx), function(cx);
ObjectInstance *priv;
GType gtype, info_gtype;
GIObjectInfo *info;
@@ -2244,11 +2244,10 @@ gjs_hook_up_vfunc(JSContext *cx,
gpointer implementor_vtable;
GIFieldInfo *field_info;
- if (!gjs_parse_call_args(cx, "hook_up_vfunc",
- "oso", argv,
- "object", object.address(),
- "name", &name,
- "function", &function))
+ if (!gjs_parse_call_args(cx, "hook_up_vfunc", argv, "oso",
+ "object", &object,
+ "name", &name,
+ "function", &function))
return false;
if (!do_base_typecheck(cx, object, true))
@@ -2493,9 +2492,9 @@ gjs_override_property(JSContext *cx,
GParamSpec *new_pspec;
GType gtype;
- if (!gjs_parse_call_args(cx, "override_property", "so", args,
+ if (!gjs_parse_call_args(cx, "override_property", args, "so",
"name", &name,
- "type", type.address()))
+ "type", &type))
return false;
if ((gtype = gjs_gtype_get_actual_gtype(cx, type)) == G_TYPE_INVALID) {
@@ -2750,7 +2749,8 @@ gjs_register_interface(JSContext *cx,
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
char *name = NULL;
- JSObject *constructor, *interfaces, *properties, *module;
+ JS::RootedObject interfaces(cx), properties(cx);
+ JSObject *constructor, *module;
guint32 i, n_interfaces, n_properties;
GType *iface_types;
GType interface_type;
@@ -2770,7 +2770,7 @@ gjs_register_interface(JSContext *cx,
NULL, /* instance_init */
};
- if (!gjs_parse_call_args(cx, "register_interface", "soo", args,
+ if (!gjs_parse_call_args(cx, "register_interface", args, "soo",
"name", &name,
"interfaces", &interfaces,
"properties", &properties))
@@ -2829,8 +2829,8 @@ gjs_register_type(JSContext *cx,
{
JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
gchar *name;
- JS::RootedObject parent(cx);
- JSObject *constructor, *interfaces, *properties, *module;
+ JS::RootedObject parent(cx), interfaces(cx), properties(cx);
+ JSObject *constructor, *module;
GType instance_type, parent_type;
GTypeQuery query;
GTypeModule *type_module;
@@ -2855,12 +2855,11 @@ gjs_register_type(JSContext *cx,
JS_BeginRequest(cx);
- if (!gjs_parse_call_args(cx, "register_type",
- "osoo", argv,
- "parent", parent.address(),
- "name", &name,
- "interfaces", &interfaces,
- "properties", &properties))
+ if (!gjs_parse_call_args(cx, "register_type", argv, "osoo",
+ "parent", &parent,
+ "name", &name,
+ "interfaces", &interfaces,
+ "properties", &properties))
goto out;
if (!parent)
diff --git a/gjs/byteArray.cpp b/gjs/byteArray.cpp
index 2882ab9..e8725d9 100644
--- a/gjs/byteArray.cpp
+++ b/gjs/byteArray.cpp
@@ -26,6 +26,7 @@
#include <glib.h>
#include "byteArray.h"
#include "../gi/boxed.h"
+#include "jsapi-util-args.h"
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
#include <girepository.h>
@@ -712,8 +713,8 @@ from_gbytes_func(JSContext *context,
GBytes *gbytes;
ByteArrayInstance *priv;
- if (!gjs_parse_call_args(context, "overrides_gbytes_to_array", "o", argv,
- "bytes", bytes_obj.address()))
+ if (!gjs_parse_call_args(context, "overrides_gbytes_to_array", argv, "o",
+ "bytes", &bytes_obj))
return false;
if (!gjs_typecheck_boxed(context, bytes_obj, NULL, G_TYPE_BYTES, true))
diff --git a/gjs/coverage.cpp b/gjs/coverage.cpp
index d7546bd..b7383a9 100644
--- a/gjs/coverage.cpp
+++ b/gjs/coverage.cpp
@@ -24,7 +24,7 @@
#include "gjs-module.h"
#include "coverage.h"
#include "coverage-internal.h"
-
+#include "jsapi-util-args.h"
#include "util/error.h"
struct _GjsCoveragePrivate {
@@ -1382,7 +1382,7 @@ get_filename_from_filename_as_js_string(JSContext *context,
JS::CallArgs &args) {
char *filename = NULL;
- if (!gjs_parse_call_args(context, "getFileContents", "s", args,
+ if (!gjs_parse_call_args(context, "getFileContents", args, "s",
"filename", &filename))
return NULL;
@@ -1481,7 +1481,7 @@ coverage_get_file_contents(JSContext *context,
JSString *script_jsstr;
GError *error = NULL;
- if (!gjs_parse_call_args(context, "getFileContents", "s", args,
+ if (!gjs_parse_call_args(context, "getFileContents", args, "s",
"filename", &filename))
goto out;
diff --git a/gjs/jsapi-util-args.h b/gjs/jsapi-util-args.h
new file mode 100644
index 0000000..0c63364
--- /dev/null
+++ b/gjs/jsapi-util-args.h
@@ -0,0 +1,380 @@
+#include <string>
+#include <type_traits>
+
+#include "compat.h"
+
+static void
+assign(JSContext *cx,
+ const char c,
+ bool nullable,
+ JS::HandleValue value,
+ bool *ref)
+{
+ if (c != 'b')
+ throw std::string("Wrong type for c, got bool*");
+ if (!value.isBoolean())
+ throw std::string("Not a boolean");
+ if (nullable)
+ throw std::string("Invalid format string combination ?b");
+ *ref = value.toBoolean();
+}
+
+static void
+assign(JSContext *cx,
+ const char c,
+ bool nullable,
+ JS::HandleValue value,
+ JS::MutableHandleObject ref)
+{
+ if (c != 'o')
+ throw std::string("Wrong type for c, got JS::MutableHandleObject");
+ if (nullable && value.isNull()) {
+ ref.set(NULL);
+ return;
+ }
+ if (!value.isObject())
+ throw std::string("Not an object");
+ ref.set(&value.toObject());
+}
+
+static void
+assign(JSContext *cx,
+ const char c,
+ bool nullable,
+ JS::HandleValue value,
+ char **ref)
+{
+ if (nullable && (c == 's' || c == 'F') && value.isNull()) {
+ *ref = NULL;
+ return;
+ }
+ if (c == 's') {
+ if (!gjs_string_to_utf8(cx, value, ref)) {
+ /* Our error message is going to be more useful */
+ JS_ClearPendingException(cx);
+ throw std::string("Couldn't convert to string");
+ }
+ } else if (c == 'F') {
+ if (!gjs_string_to_filename(cx, value, ref)) {
+ /* Our error message is going to be more useful */
+ JS_ClearPendingException(cx);
+ throw std::string("Couldn't convert to filename");
+ }
+ } else {
+ throw std::string("Wrong type for c, got char**");
+ }
+}
+
+static void
+assign(JSContext *cx,
+ const char c,
+ bool nullable,
+ JS::HandleValue value,
+ int32_t *ref)
+{
+ if (c != 'i')
+ throw std::string("Wrong type for c, got int32_t*");
+ if (nullable)
+ throw std::string("Invalid format string combination ?i");
+ if (!JS::ToInt32(cx, value, ref)) {
+ /* Our error message is going to be more useful */
+ JS_ClearPendingException(cx);
+ throw std::string("Couldn't convert to integer");
+ }
+}
+
+/* Special case: treat pointer-to-enum as pointer-to-int, but use enable_if to
+ * prevent instantiation for any other types besides pointer-to-enum */
+template<typename T,
+ typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+static void
+assign(JSContext *cx,
+ const char c,
+ bool nullable,
+ JS::HandleValue value,
+ T *ref)
+{
+ if (c != 'i')
+ throw std::string("Wrong type for c, got enum*");
+ assign(cx, c, nullable, value, (int32_t *)ref);
+}
+
+static void
+assign(JSContext *cx,
+ const char c,
+ bool nullable,
+ JS::HandleValue value,
+ uint32_t *ref)
+{
+ double num;
+
+ if (c != 'u')
+ throw std::string("Wrong type for c, got uint32_t*");
+ if (nullable)
+ throw std::string("Invalid format string combination ?u");
+ if (!value.isNumber() || !JS::ToNumber(cx, value, &num)) {
+ /* Our error message is going to be more useful */
+ JS_ClearPendingException(cx);
+ throw std::string("Couldn't convert to unsigned integer");
+ }
+ if (num > G_MAXUINT32 || num < 0)
+ throw std::string("Value is out of range");
+ *ref = num;
+}
+
+static void
+assign(JSContext *cx,
+ const char c,
+ bool nullable,
+ JS::HandleValue value,
+ int64_t *ref)
+{
+ if (c != 't')
+ throw std::string("Wrong type for c, got int64_t*");
+ if (nullable)
+ throw std::string("Invalid format string combination ?t");
+ if (!JS::ToInt64(cx, value, ref)) {
+ /* Our error message is going to be more useful */
+ JS_ClearPendingException(cx);
+ throw std::string("Couldn't convert to 64-bit integer");
+ }
+}
+
+static void
+assign(JSContext *cx,
+ const char c,
+ bool nullable,
+ JS::HandleValue value,
+ double *ref)
+{
+ if (c != 'f')
+ throw std::string("Wrong type for c, got double*");
+ if (nullable)
+ throw std::string("Invalid format string combination ?f");
+ if (!JS::ToNumber(cx, value, ref)) {
+ /* Our error message is going to be more useful */
+ JS_ClearPendingException(cx);
+ throw std::string("Couldn't convert to double");
+ }
+}
+
+static bool
+check_nullable(const char*& fchar,
+ const char*& fmt_string)
+{
+ if (*fchar != '?')
+ return false;
+
+ fchar++;
+ fmt_string++;
+ g_assert(((void) "Invalid format string, parameter required after '?'",
+ *fchar != '\0'));
+ return true;
+}
+
+template<typename T>
+static void
+free_if_necessary(T param_ref) {}
+
+static void
+free_if_necessary(char **param_ref)
+{
+ g_free(*param_ref);
+}
+
+template<typename T>
+static bool
+parse_call_args_helper(JSContext *cx,
+ const char *function_name,
+ JS::CallArgs& args,
+ bool ignore_trailing_args,
+ const char*& fmt_required,
+ const char*& fmt_optional,
+ unsigned param_ix,
+ const char *param_name,
+ T param_ref)
+{
+ bool nullable = false;
+ const char *fchar = fmt_required;
+
+ g_return_val_if_fail (param_name != NULL, false);
+
+ if (*fchar != '\0') {
+ nullable = check_nullable(fchar, fmt_required);
+ fmt_required++;
+ } else {
+ fchar = fmt_optional;
+ g_assert(((void) "Wrong number of parameters passed to gjs_parse_call_args()",
+ *fchar != '\0'));
+ nullable = check_nullable(fchar, fmt_optional);
+ fmt_optional++;
+ }
+
+ try {
+ assign(cx, *fchar, nullable, args.handleOrUndefinedAt(param_ix), param_ref);
+ } catch (std::string& message) {
+ JS_ReportError(cx, "Error invoking %s, at argument %d (%s): %s",
+ function_name, param_ix, param_name, message.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+template<typename T, typename... Args>
+static bool
+parse_call_args_helper(JSContext *cx,
+ const char *function_name,
+ JS::CallArgs& args,
+ bool ignore_trailing_args,
+ const char*& fmt_required,
+ const char*& fmt_optional,
+ unsigned param_ix,
+ const char *param_name,
+ T param_ref,
+ Args ...params)
+{
+ bool retval;
+
+ if (!parse_call_args_helper(cx, function_name, args, ignore_trailing_args,
+ fmt_required, fmt_optional, param_ix,
+ param_name, param_ref))
+ return false;
+
+ retval = parse_call_args_helper(cx, function_name, args,
+ ignore_trailing_args,
+ fmt_required, fmt_optional, ++param_ix,
+ params...);
+
+ /* We still own the strings in the error case, free any we converted */
+ if (!retval)
+ free_if_necessary(param_ref);
+ return retval;
+}
+
+/**
+ * gjs_parse_call_args:
+ * @context:
+ * @function_name: The name of the function being called
+ * @format: Printf-like format specifier containing the expected arguments
+ * @args: #JS::CallArgs from #JSNative function
+ * @params: for each character in @format, a pair of const char * which is the
+ * name of the argument, and a location to store the value. The type of
+ * location argument depends on the format character, as described below.
+ *
+ * This function is inspired by Python's PyArg_ParseTuple for those
+ * familiar with it. It takes a format specifier which gives the
+ * types of the expected arguments, and a list of argument names and
+ * value location pairs. The currently accepted format specifiers are:
+ *
+ * b: A boolean (pass a bool *)
+ * s: A string, converted into UTF-8 (pass a char **)
+ * F: A string, converted into "filename encoding" (i.e. active locale) (pass
+ * a char **)
+ * i: A number, will be converted to a 32-bit int (pass an int32_t *)
+ * u: A number, converted into a 32-bit unsigned int (pass a uint32_t *)
+ * t: A 64-bit number, converted into a 64-bit int (pass an int64_t *)
+ * f: A number, will be converted into a double (pass a double *)
+ * o: A JavaScript object (pass a JS::MutableHandleObject)
+ *
+ * If the first character in the format string is a '!', then JS is allowed
+ * to pass extra arguments that are ignored, to the function.
+ *
+ * The '|' character introduces optional arguments. All format specifiers
+ * after a '|' when not specified, do not cause any changes in the C
+ * value location.
+ *
+ * A prefix character '?' in front of 's', 'F', or 'o' means that the next value
+ * may be null. For 's' or 'F' a null pointer is returned, for 'o' the handle is
+ * set to null.
+ */
+template<typename... Args>
+static bool
+gjs_parse_call_args(JSContext *cx,
+ const char *function_name,
+ JS::CallArgs& args,
+ const char *format,
+ Args ...params)
+{
+ const char *fmt_iter, *fmt_required, *fmt_optional;
+ unsigned n_required = 0, n_total = 0;
+ bool ignore_trailing_args = false, retval;
+ char **parts;
+
+ if (*format == '!') {
+ ignore_trailing_args = true;
+ format++;
+ }
+
+ for (fmt_iter = format; *fmt_iter; fmt_iter++) {
+ switch (*fmt_iter) {
+ case '|':
+ n_required = n_total;
+ continue;
+ case '?':
+ continue;
+ default:
+ n_total++;
+ }
+ }
+
+ if (n_required == 0)
+ n_required = n_total;
+
+ g_assert(((void) "Wrong number of parameters passed to gjs_parse_call_args()",
+ sizeof...(Args) / 2 == n_required));
+
+ JSAutoRequest ar(cx);
+
+ // COMPAT: In future, use args.requireAtLeast()
+ if (args.length() < n_required ||
+ (args.length() > n_total && !ignore_trailing_args)) {
+ if (n_required == n_total) {
+ JS_ReportError(cx, "Error invoking %s: Expected %d arguments, got %d",
+ function_name, n_required, args.length());
+ } else {
+ JS_ReportError(cx,
+ "Error invoking %s: Expected minimum %d arguments (and %d optional), got %d",
+ function_name, n_required, n_total - n_required,
+ args.length());
+ }
+ return false;
+ }
+
+ parts = g_strsplit(format, "|", 2);
+ fmt_required = parts[0];
+ fmt_optional = parts[1]; /* may be NULL */
+
+ retval = parse_call_args_helper(cx, function_name, args, ignore_trailing_args,
+ fmt_required, fmt_optional, 0, params...);
+
+ g_strfreev(parts);
+ return retval;
+}
+
+static bool
+gjs_parse_call_args(JSContext *cx,
+ const char *function_name,
+ JS::CallArgs& args,
+ const char *format)
+{
+ bool ignore_trailing_args = false;
+
+ if (*format == '!') {
+ ignore_trailing_args = true;
+ format++;
+ }
+
+ g_assert(((void) "Wrong number of parameters passed to gjs_parse_call_args()",
+ *format == '\0'));
+
+ JSAutoRequest ar(cx);
+
+ if (!ignore_trailing_args && args.length() > 0) {
+ JS_ReportError(cx, "Error invoking %s: Expected 0 arguments, got %d",
+ function_name, args.length());
+ return false;
+ }
+
+ return true;
+}
diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp
index 8e40dc9..b04b345 100644
--- a/gjs/jsapi-util.cpp
+++ b/gjs/jsapi-util.cpp
@@ -832,273 +832,6 @@ gjs_value_to_int64(JSContext *context,
return JS::ToInt64(context, val, (int64_t *) result);
}
-static bool
-gjs_parse_args_valist (JSContext *context,
- const char *function_name,
- const char *format,
- unsigned argc,
- JS::Value *argv,
- va_list args)
-{
- guint i;
- const char *fmt_iter;
- guint n_unwind = 0;
-#define MAX_UNWIND_STRINGS 16
- gpointer unwind_strings[MAX_UNWIND_STRINGS];
- bool ignore_trailing_args = false;
- guint n_required = 0;
- guint n_total = 0;
- guint consumed_args;
-
- JS_BeginRequest(context);
-
- if (*format == '!') {
- ignore_trailing_args = true;
- format++;
- }
-
- for (fmt_iter = format; *fmt_iter; fmt_iter++) {
- switch (*fmt_iter) {
- case '|':
- n_required = n_total;
- continue;
- case '?':
- continue;
- default:
- break;
- }
-
- n_total++;
- }
-
- if (n_required == 0)
- n_required = n_total;
-
- if (argc < n_required || (argc > n_total && !ignore_trailing_args)) {
- if (n_required == n_total) {
- gjs_throw(context, "Error invoking %s: Expected %d arguments, got %d", function_name,
- n_required, argc);
- } else {
- gjs_throw(context, "Error invoking %s: Expected minimum %d arguments (and %d optional), got %d",
function_name,
- n_required, n_total - n_required, argc);
- }
- goto error_unwind;
- }
-
- /* We have 3 iteration variables here.
- * @i: The current integer position in fmt_args
- * @fmt_iter: A pointer to the character in fmt_args
- * @consumed_args: How many arguments we've taken from argv
- *
- * consumed_args can currently be different from 'i' because of the '|' character.
- */
- for (i = 0, consumed_args = 0, fmt_iter = format; *fmt_iter; fmt_iter++, i++) {
- const char *argname;
- gpointer arg_location;
- JS::Value js_value;
- const char *arg_error_message = NULL;
-
- if (*fmt_iter == '|')
- continue;
-
- if (consumed_args == argc) {
- break;
- }
-
- argname = va_arg (args, char *);
- arg_location = va_arg (args, gpointer);
-
- g_return_val_if_fail (argname != NULL, false);
- g_return_val_if_fail (arg_location != NULL, false);
-
- js_value = argv[consumed_args];
-
- if (*fmt_iter == '?') {
- fmt_iter++;
-
- if (js_value.isNull()) {
- gpointer *arg = (gpointer*) arg_location;
- *arg = NULL;
- goto got_value;
- }
- }
-
- switch (*fmt_iter) {
- case 'b': {
- if (!js_value.isBoolean()) {
- arg_error_message = "Not a boolean";
- } else {
- bool *arg = (bool *) arg_location;
- *arg = js_value.toBoolean();
- }
- }
- break;
- case 'o': {
- if (!js_value.isObject()) {
- arg_error_message = "Not an object";
- } else {
- JSObject **arg = (JSObject**) arg_location;
- *arg = &js_value.toObject();
- }
- }
- break;
- case 's': {
- char **arg = (char**) arg_location;
-
- if (gjs_string_to_utf8 (context, js_value, arg)) {
- unwind_strings[n_unwind++] = *arg;
- g_assert(n_unwind < MAX_UNWIND_STRINGS);
- } else {
- /* Our error message is going to be more useful */
- JS_ClearPendingException(context);
- arg_error_message = "Couldn't convert to string";
- }
- }
- break;
- case 'F': {
- char **arg = (char**) arg_location;
-
- if (gjs_string_to_filename (context, js_value, arg)) {
- unwind_strings[n_unwind++] = *arg;
- g_assert(n_unwind < MAX_UNWIND_STRINGS);
- } else {
- /* Our error message is going to be more useful */
- JS_ClearPendingException(context);
- arg_error_message = "Couldn't convert to filename";
- }
- }
- break;
- case 'i': {
- if (!JS::ToInt32(context, js_value, (int32_t *) arg_location)) {
- /* Our error message is going to be more useful */
- JS_ClearPendingException(context);
- arg_error_message = "Couldn't convert to integer";
- }
- }
- break;
- case 'u': {
- gdouble num;
- if (!js_value.isNumber() || !JS::ToNumber(context, js_value, &num)) {
- /* Our error message is going to be more useful */
- JS_ClearPendingException(context);
- arg_error_message = "Couldn't convert to unsigned integer";
- } else if (num > G_MAXUINT32 || num < 0) {
- arg_error_message = "Value is out of range";
- } else {
- *((guint32*) arg_location) = num;
- }
- }
- break;
- case 't': {
- if (!JS::ToInt64(context, js_value, (int64_t *) arg_location)) {
- /* Our error message is going to be more useful */
- JS_ClearPendingException(context);
- arg_error_message = "Couldn't convert to 64-bit integer";
- }
- }
- break;
- case 'f': {
- double num;
- if (!JS::ToNumber(context, js_value, &num)) {
- /* Our error message is going to be more useful */
- JS_ClearPendingException(context);
- arg_error_message = "Couldn't convert to double";
- } else {
- *((double*) arg_location) = num;
- }
- }
- break;
- default:
- g_assert_not_reached ();
- }
-
- got_value:
- if (arg_error_message != NULL) {
- gjs_throw(context, "Error invoking %s, at argument %d (%s): %s", function_name,
- consumed_args+1, argname, arg_error_message);
- goto error_unwind;
- }
-
- consumed_args++;
- }
-
- JS_EndRequest(context);
- return true;
-
- error_unwind:
- /* We still own the strings in the error case, free any we converted */
- for (i = 0; i < n_unwind; i++) {
- g_free (unwind_strings[i]);
- }
- JS_EndRequest(context);
- return false;
-}
-
-/**
- * gjs_parse_args:
- * @context:
- * @function_name: The name of the function being called
- * @format: Printf-like format specifier containing the expected arguments
- * @argc: Number of JavaScript arguments
- * @argv: JavaScript argument array
- * @Varargs: for each character in @format, a pair of a char * which is the name
- * of the argument, and a pointer to a location to store the value. The type of
- * value stored depends on the format character, as described below.
- *
- * This function is inspired by Python's PyArg_ParseTuple for those
- * familiar with it. It takes a format specifier which gives the
- * types of the expected arguments, and a list of argument names and
- * value location pairs. The currently accepted format specifiers are:
- *
- * b: A boolean
- * s: A string, converted into UTF-8
- * F: A string, converted into "filename encoding" (i.e. active locale)
- * i: A number, will be converted to a C "gint32"
- * u: A number, converted into a C "guint32"
- * t: A 64-bit number, converted into a C "gint64"
- * o: A JavaScript object, as a "JSObject *"
- *
- * If the first character in the format string is a '!', then JS is allowed
- * to pass extra arguments that are ignored, to the function.
- *
- * The '|' character introduces optional arguments. All format specifiers
- * after a '|' when not specified, do not cause any changes in the C
- * value location.
- *
- * A prefix character '?' means that the next value may be null, in
- * which case the C value %NULL is returned.
- */
-bool
-gjs_parse_args (JSContext *context,
- const char *function_name,
- const char *format,
- unsigned argc,
- JS::Value *argv,
- ...)
-{
- va_list args;
- bool ret;
- va_start (args, argv);
- ret = gjs_parse_args_valist (context, function_name, format, argc, argv, args);
- va_end (args);
- return ret;
-}
-
-bool
-gjs_parse_call_args (JSContext *context,
- const char *function_name,
- const char *format,
- JS::CallArgs &call_args,
- ...)
-{
- va_list args;
- bool ret;
- va_start (args, call_args);
- ret = gjs_parse_args_valist (context, function_name, format, call_args.length(), call_args.array(),
args);
- va_end (args);
- return ret;
-}
-
#ifdef __linux__
static void
_linux_get_self_process_size (gulong *vm_size,
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index b394fec..1839191 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -447,19 +447,6 @@ bool gjs_value_to_int64 (JSContext *context,
gint64 *result)
G_GNUC_DEPRECATED_FOR(JS::ToInt64);
-bool gjs_parse_args (JSContext *context,
- const char *function_name,
- const char *format,
- unsigned argc,
- JS::Value *argv,
- ...);
-
-bool gjs_parse_call_args (JSContext *context,
- const char *function_name,
- const char *format,
- JS::CallArgs &args,
- ...);
-
GjsRootedArray* gjs_rooted_array_new (void)
G_GNUC_DEPRECATED_FOR(JS::AutoValueVector);
void gjs_rooted_array_append (JSContext *context,
diff --git a/modules/cairo-context.cpp b/modules/cairo-context.cpp
index 70e5c71..022683b 100644
--- a/modules/cairo-context.cpp
+++ b/modules/cairo-context.cpp
@@ -25,7 +25,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
#include <gi/foreign.h>
-
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include <cairo-gobject.h>
#include "cairo-private.h"
@@ -74,8 +74,8 @@ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END
#define _GJS_CAIRO_CONTEXT_DEFINE_FUNC2FFAFF(method, cfunc, n1, n2) \
_GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \
double arg1, arg2; \
- if (!gjs_parse_call_args(context, #method, "ff", argv, \
- #n1, &arg1, #n2, &arg2)) \
+ if (!gjs_parse_call_args(context, #method, argv, "ff", \
+ #n1, &arg1, #n2, &arg2)) \
return false; \
cfunc(cr, &arg1, &arg2); \
if (cairo_status(cr) == CAIRO_STATUS_SUCCESS) { \
@@ -142,8 +142,8 @@ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END
#define _GJS_CAIRO_CONTEXT_DEFINE_FUNC1(method, cfunc, fmt, t1, n1) \
_GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \
t1 arg1; \
- if (!gjs_parse_call_args(context, #method, fmt, argv, \
- #n1, &arg1)) \
+ if (!gjs_parse_call_args(context, #method, argv, fmt, \
+ #n1, &arg1)) \
return false; \
cfunc(cr, arg1); \
argv.rval().setUndefined(); \
@@ -153,8 +153,8 @@ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_END
_GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \
t1 arg1; \
t2 arg2; \
- if (!gjs_parse_call_args(context, #method, fmt, argv, \
- #n1, &arg1, #n2, &arg2)) \
+ if (!gjs_parse_call_args(context, #method, argv, fmt, \
+ #n1, &arg1, #n2, &arg2)) \
return false; \
cfunc(cr, arg1, arg2); \
argv.rval().setUndefined(); \
@@ -165,8 +165,8 @@ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \
t1 arg1; \
t2 arg2; \
cairo_bool_t ret; \
- if (!gjs_parse_call_args(context, #method, fmt, argv, \
- #n1, &arg1, #n2, &arg2)) \
+ if (!gjs_parse_call_args(context, #method, argv, fmt, \
+ #n1, &arg1, #n2, &arg2)) \
return false; \
ret = cfunc(cr, arg1, arg2); \
argv.rval().setBoolean(ret); \
@@ -177,8 +177,8 @@ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \
t1 arg1; \
t2 arg2; \
t3 arg3; \
- if (!gjs_parse_call_args(context, #method, fmt, argv, \
- #n1, &arg1, #n2, &arg2, #n3, &arg3)) \
+ if (!gjs_parse_call_args(context, #method, argv, fmt, \
+ #n1, &arg1, #n2, &arg2, #n3, &arg3)) \
return false; \
cfunc(cr, arg1, arg2, arg3); \
argv.rval().setUndefined(); \
@@ -190,8 +190,9 @@ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \
t2 arg2; \
t3 arg3; \
t4 arg4; \
- if (!gjs_parse_call_args(context, #method, fmt, argv, \
- #n1, &arg1, #n2, &arg2, #n3, &arg3, #n4, &arg4)) \
+ if (!gjs_parse_call_args(context, #method, argv, fmt, \
+ #n1, &arg1, #n2, &arg2, \
+ #n3, &arg3, #n4, &arg4)) \
return false; \
cfunc(cr, arg1, arg2, arg3, arg4); \
_GJS_CAIRO_CONTEXT_DEFINE_FUNC_END
@@ -203,9 +204,9 @@ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \
t3 arg3; \
t4 arg4; \
t5 arg5; \
- if (!gjs_parse_call_args(context, #method, fmt, argv, \
- #n1, &arg1, #n2, &arg2, #n3, &arg3, \
- #n4, &arg4, #n5, &arg5)) \
+ if (!gjs_parse_call_args(context, #method, argv, fmt, \
+ #n1, &arg1, #n2, &arg2, #n3, &arg3, \
+ #n4, &arg4, #n5, &arg5)) \
return false; \
cfunc(cr, arg1, arg2, arg3, arg4, arg5); \
argv.rval().setUndefined(); \
@@ -219,9 +220,9 @@ _GJS_CAIRO_CONTEXT_DEFINE_FUNC_BEGIN(method) \
t4 arg4; \
t5 arg5; \
t6 arg6; \
- if (!gjs_parse_call_args(context, #method, fmt, argv, \
- #n1, &arg1, #n2, &arg2, #n3, &arg3, \
- #n4, &arg4, #n5, &arg5, #n6, &arg6)) \
+ if (!gjs_parse_call_args(context, #method, argv, fmt, \
+ #n1, &arg1, #n2, &arg2, #n3, &arg3, \
+ #n4, &arg4, #n5, &arg5, #n6, &arg6)) \
return false; \
cfunc(cr, arg1, arg2, arg3, arg4, arg5, arg6); \
argv.rval().setUndefined(); \
@@ -257,14 +258,14 @@ _gjs_cairo_context_construct_internal(JSContext *context,
GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_context)
{
GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_context)
- JSObject *surface_wrapper;
cairo_surface_t *surface;
cairo_t *cr;
GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_context);
- if (!gjs_parse_call_args(context, "Context", "o", argv,
- "surface", &surface_wrapper))
+ JS::RootedObject surface_wrapper(context);
+ if (!gjs_parse_call_args(context, "Context", argv, "o",
+ "surface", &surface_wrapper))
return false;
surface = gjs_cairo_surface_get_surface(context, surface_wrapper);
@@ -407,12 +408,12 @@ appendPath_func(JSContext *context,
JS::Value *vp)
{
GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
- JSObject *path_wrapper;
+ JS::RootedObject path_wrapper(context);
cairo_path_t *path;
cairo_t *cr = priv ? priv->cr : NULL;
- if (!gjs_parse_call_args(context, "path", "o", argv,
- "path", &path_wrapper))
+ if (!gjs_parse_call_args(context, "path", argv, "o",
+ "path", &path_wrapper))
return false;
path = gjs_cairo_path_get_path(context, path_wrapper);
@@ -435,7 +436,7 @@ copyPath_func(JSContext *context,
cairo_path_t *path;
cairo_t *cr = priv ? priv->cr : NULL;
- if (!gjs_parse_call_args(context, "", "", argv))
+ if (!gjs_parse_call_args(context, "", argv, ""))
return false;
path = cairo_copy_path(cr);
@@ -452,7 +453,7 @@ copyPathFlat_func(JSContext *context,
cairo_path_t *path;
cairo_t *cr = priv ? priv->cr : NULL;
- if (!gjs_parse_call_args(context, "", "", argv))
+ if (!gjs_parse_call_args(context, "", argv, ""))
return false;
path = cairo_copy_path_flat(cr);
@@ -466,12 +467,12 @@ mask_func(JSContext *context,
JS::Value *vp)
{
GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
- JSObject *pattern_wrapper;
+ JS::RootedObject pattern_wrapper(context);
cairo_pattern_t *pattern;
cairo_t *cr = priv ? priv->cr : NULL;
- if (!gjs_parse_call_args(context, "mask", "o", argv,
- "pattern", &pattern_wrapper))
+ if (!gjs_parse_call_args(context, "mask", argv, "o",
+ "pattern", &pattern_wrapper))
return false;
pattern = gjs_cairo_pattern_get_pattern(context, pattern_wrapper);
@@ -495,15 +496,15 @@ maskSurface_func(JSContext *context,
JS::Value *vp)
{
GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
- JSObject *surface_wrapper;
+ JS::RootedObject surface_wrapper(context);
double x, y;
cairo_surface_t *surface;
cairo_t *cr = priv ? priv->cr : NULL;
- if (!gjs_parse_call_args(context, "maskSurface", "off", argv,
- "surface", &surface_wrapper,
- "x", &x,
- "y", &y))
+ if (!gjs_parse_call_args(context, "maskSurface", argv, "off",
+ "surface", &surface_wrapper,
+ "x", &x,
+ "y", &y))
return false;
surface = gjs_cairo_surface_get_surface(context, surface_wrapper);
@@ -535,8 +536,9 @@ setDash_func(JSContext *context,
guint len;
GArray *dashes_c = NULL;
- if (!gjs_parse_call_args(context, "setDash", "of", argv,
- "dashes", dashes.address(), "offset", &offset))
+ if (!gjs_parse_call_args(context, "setDash", argv, "of",
+ "dashes", &dashes,
+ "offset", &offset))
return false;
@@ -587,12 +589,12 @@ setSource_func(JSContext *context,
JS::Value *vp)
{
GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
- JSObject *pattern_wrapper;
+ JS::RootedObject pattern_wrapper(context);
cairo_pattern_t *pattern;
cairo_t *cr = priv ? priv->cr : NULL;
- if (!gjs_parse_call_args(context, "setSource", "o", argv,
- "pattern", &pattern_wrapper))
+ if (!gjs_parse_call_args(context, "setSource", argv, "o",
+ "pattern", &pattern_wrapper))
return false;
pattern = gjs_cairo_pattern_get_pattern(context, pattern_wrapper);
@@ -617,15 +619,15 @@ setSourceSurface_func(JSContext *context,
JS::Value *vp)
{
GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
- JSObject *surface_wrapper;
+ JS::RootedObject surface_wrapper(context);
double x, y;
cairo_surface_t *surface;
cairo_t *cr = priv ? priv->cr : NULL;
- if (!gjs_parse_call_args(context, "setSourceSurface", "off", argv,
- "surface", &surface_wrapper,
- "x", &x,
- "y", &y))
+ if (!gjs_parse_call_args(context, "setSourceSurface", argv, "off",
+ "surface", &surface_wrapper,
+ "x", &x,
+ "y", &y))
return false;
surface = gjs_cairo_surface_get_surface(context, surface_wrapper);
@@ -653,8 +655,8 @@ showText_func(JSContext *context,
char *utf8;
cairo_t *cr = priv ? priv->cr : NULL;
- if (!gjs_parse_call_args(context, "showText", "s", argv,
- "utf8", &utf8))
+ if (!gjs_parse_call_args(context, "showText", argv, "s",
+ "utf8", &utf8))
return false;
cairo_show_text(cr, utf8);
@@ -679,10 +681,10 @@ selectFontFace_func(JSContext *context,
cairo_font_weight_t weight;
cairo_t *cr = priv ? priv->cr : NULL;
- if (!gjs_parse_call_args(context, "selectFontFace", "sii", argv,
- "family", &family,
- "slang", &slant,
- "weight", &weight))
+ if (!gjs_parse_call_args(context, "selectFontFace", argv, "sii",
+ "family", &family,
+ "slang", &slant,
+ "weight", &weight))
return false;
cairo_select_font_face(cr, family, slant, weight);
diff --git a/modules/cairo-gradient.cpp b/modules/cairo-gradient.cpp
index eca60dc..7dc5306 100644
--- a/modules/cairo-gradient.cpp
+++ b/modules/cairo-gradient.cpp
@@ -24,6 +24,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include "cairo-private.h"
@@ -52,11 +53,11 @@ addColorStopRGB_func(JSContext *context,
double offset, red, green, blue;
cairo_pattern_t *pattern;
- if (!gjs_parse_call_args(context, "addColorStopRGB", "ffff", argv,
- "offset", &offset,
- "red", &red,
- "green", &green,
- "blue", &blue))
+ if (!gjs_parse_call_args(context, "addColorStopRGB", argv, "ffff",
+ "offset", &offset,
+ "red", &red,
+ "green", &green,
+ "blue", &blue))
return false;
pattern = gjs_cairo_pattern_get_pattern(context, obj);
@@ -79,12 +80,12 @@ addColorStopRGBA_func(JSContext *context,
double offset, red, green, blue, alpha;
cairo_pattern_t *pattern;
- if (!gjs_parse_call_args(context, "addColorStopRGBA", "fffff", argv,
- "offset", &offset,
- "red", &red,
- "green", &green,
- "blue", &blue,
- "alpha", &alpha))
+ if (!gjs_parse_call_args(context, "addColorStopRGBA", argv, "fffff",
+ "offset", &offset,
+ "red", &red,
+ "green", &green,
+ "blue", &blue,
+ "alpha", &alpha))
return false;
pattern = gjs_cairo_pattern_get_pattern(context, obj);
diff --git a/modules/cairo-image-surface.cpp b/modules/cairo-image-surface.cpp
index 6de5682..fe2e9cb 100644
--- a/modules/cairo-image-surface.cpp
+++ b/modules/cairo-image-surface.cpp
@@ -24,6 +24,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include "cairo-private.h"
@@ -38,10 +39,10 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_image_surface)
GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_image_surface);
// create_for_data optional parameter
- if (!gjs_parse_call_args(context, "ImageSurface", "iii", argv,
- "format", &format,
- "width", &width,
- "height", &height))
+ if (!gjs_parse_call_args(context, "ImageSurface", argv, "iii",
+ "format", &format,
+ "width", &width,
+ "height", &height))
return false;
surface = cairo_image_surface_create((cairo_format_t) format, width, height);
@@ -77,8 +78,8 @@ createFromPNG_func(JSContext *context,
char *filename;
cairo_surface_t *surface;
- if (!gjs_parse_call_args(context, "createFromPNG", "s", argv,
- "filename", &filename))
+ if (!gjs_parse_call_args(context, "createFromPNG", argv, "s",
+ "filename", &filename))
return false;
surface = cairo_image_surface_create_from_png(filename);
diff --git a/modules/cairo-linear-gradient.cpp b/modules/cairo-linear-gradient.cpp
index b4ff0c6..8db98a4 100644
--- a/modules/cairo-linear-gradient.cpp
+++ b/modules/cairo-linear-gradient.cpp
@@ -24,6 +24,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include "cairo-private.h"
@@ -37,11 +38,11 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_linear_gradient)
GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_linear_gradient);
- if (!gjs_parse_call_args(context, "LinearGradient", "ffff", argv,
- "x0", &x0,
- "y0", &y0,
- "x1", &x1,
- "y1", &y1))
+ if (!gjs_parse_call_args(context, "LinearGradient", argv, "ffff",
+ "x0", &x0,
+ "y0", &y0,
+ "x1", &x1,
+ "y1", &y1))
return false;
pattern = cairo_pattern_create_linear(x0, y0, x1, y1);
diff --git a/modules/cairo-pdf-surface.cpp b/modules/cairo-pdf-surface.cpp
index cbbb415..353f9ad 100644
--- a/modules/cairo-pdf-surface.cpp
+++ b/modules/cairo-pdf-surface.cpp
@@ -24,6 +24,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include "cairo-private.h"
@@ -41,10 +42,10 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_pdf_surface)
GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_pdf_surface);
- if (!gjs_parse_call_args(context, "PDFSurface", "sff", argv,
- "filename", &filename,
- "width", &width,
- "height", &height))
+ if (!gjs_parse_call_args(context, "PDFSurface", argv, "Fff",
+ "filename", &filename,
+ "width", &width,
+ "height", &height))
return false;
surface = cairo_pdf_surface_create(filename, width, height);
diff --git a/modules/cairo-ps-surface.cpp b/modules/cairo-ps-surface.cpp
index dee145d..caac485 100644
--- a/modules/cairo-ps-surface.cpp
+++ b/modules/cairo-ps-surface.cpp
@@ -24,6 +24,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include "cairo-private.h"
@@ -41,10 +42,10 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_ps_surface)
GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_ps_surface);
- if (!gjs_parse_call_args(context, "PSSurface", "sff", argv,
- "filename", &filename,
- "width", &width,
- "height", &height))
+ if (!gjs_parse_call_args(context, "PSSurface", argv, "Fff",
+ "filename", &filename,
+ "width", &width,
+ "height", &height))
return false;
surface = cairo_ps_surface_create(filename, width, height);
diff --git a/modules/cairo-radial-gradient.cpp b/modules/cairo-radial-gradient.cpp
index ca373f0..e39225f 100644
--- a/modules/cairo-radial-gradient.cpp
+++ b/modules/cairo-radial-gradient.cpp
@@ -24,6 +24,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include "cairo-private.h"
@@ -37,13 +38,13 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_radial_gradient)
GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_radial_gradient);
- if (!gjs_parse_call_args(context, "RadialGradient", "ffffff", argv,
- "cx0", &cx0,
- "cy0", &cy0,
- "radius0", &radius0,
- "cx1", &cx1,
- "cy1", &cy1,
- "radius1", &radius1))
+ if (!gjs_parse_call_args(context, "RadialGradient", argv, "ffffff",
+ "cx0", &cx0,
+ "cy0", &cy0,
+ "radius0", &radius0,
+ "cx1", &cx1,
+ "cy1", &cy1,
+ "radius1", &radius1))
return false;
pattern = cairo_pattern_create_radial(cx0, cy0, radius0, cx1, cy1, radius1);
diff --git a/modules/cairo-region.cpp b/modules/cairo-region.cpp
index 65e33cb..d4b6059 100644
--- a/modules/cairo-region.cpp
+++ b/modules/cairo-region.cpp
@@ -25,6 +25,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
#include <gi/foreign.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include <cairo-gobject.h>
@@ -70,8 +71,8 @@ fill_rectangle(JSContext *context, JSObject *obj,
PRELUDE; \
JS::RootedObject other_obj(context); \
cairo_region_t *other_region; \
- if (!gjs_parse_call_args(context, #method, "o", argv, \
- "other_region", other_obj.address())) \
+ if (!gjs_parse_call_args(context, #method, argv, "o", \
+ "other_region", &other_obj)) \
return false; \
\
other_region = get_region(context, other_obj); \
@@ -88,10 +89,10 @@ fill_rectangle(JSContext *context, JSObject *obj,
JS::Value *vp) \
{ \
PRELUDE; \
- JSObject *rect_obj; \
+ JS::RootedObject rect_obj(context); \
cairo_rectangle_int_t rect; \
- if (!gjs_parse_call_args(context, #method, "o", argv, \
- "rect", &rect_obj)) \
+ if (!gjs_parse_call_args(context, #method, argv, "o", \
+ "rect", &rect_obj)) \
return false; \
\
if (!fill_rectangle(context, rect_obj, &rect)) \
@@ -171,7 +172,7 @@ num_rectangles_func(JSContext *context,
PRELUDE;
int n_rects;
- if (!gjs_parse_call_args(context, "num_rectangles", "", argv))
+ if (!gjs_parse_call_args(context, "num_rectangles", argv, ""))
return false;
n_rects = cairo_region_num_rectangles(this_region);
@@ -189,7 +190,8 @@ get_rectangle_func(JSContext *context,
JSObject *rect_obj;
cairo_rectangle_int_t rect;
- if (!gjs_parse_call_args(context, "get_rectangle", "i", argv, "rect", &i))
+ if (!gjs_parse_call_args(context, "get_rectangle", argv, "i",
+ "rect", &i))
return false;
cairo_region_get_rectangle(this_region, i, &rect);
@@ -243,7 +245,7 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_region)
GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_region);
- if (!gjs_parse_call_args(context, "Region", "", argv))
+ if (!gjs_parse_call_args(context, "Region", argv, ""))
return false;
region = cairo_region_create();
diff --git a/modules/cairo-solid-pattern.cpp b/modules/cairo-solid-pattern.cpp
index 5bb4a8b..a173f31 100644
--- a/modules/cairo-solid-pattern.cpp
+++ b/modules/cairo-solid-pattern.cpp
@@ -24,6 +24,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include "cairo-private.h"
@@ -50,10 +51,10 @@ createRGB_func(JSContext *context,
cairo_pattern_t *pattern;
JSObject *pattern_wrapper;
- if (!gjs_parse_call_args(context, "createRGB", "fff", argv,
- "red", &red,
- "green", &green,
- "blue", &blue))
+ if (!gjs_parse_call_args(context, "createRGB", argv, "fff",
+ "red", &red,
+ "green", &green,
+ "blue", &blue))
return false;
pattern = cairo_pattern_create_rgb(red, green, blue);
@@ -78,11 +79,11 @@ createRGBA_func(JSContext *context,
cairo_pattern_t *pattern;
JSObject *pattern_wrapper;
- if (!gjs_parse_call_args(context, "createRGBA", "ffff", argv,
- "red", &red,
- "green", &green,
- "blue", &blue,
- "alpha", &alpha))
+ if (!gjs_parse_call_args(context, "createRGBA", argv, "ffff",
+ "red", &red,
+ "green", &green,
+ "blue", &blue,
+ "alpha", &alpha))
return false;
pattern = cairo_pattern_create_rgba(red, green, blue, alpha);
diff --git a/modules/cairo-surface-pattern.cpp b/modules/cairo-surface-pattern.cpp
index 750b061..b076b02 100644
--- a/modules/cairo-surface-pattern.cpp
+++ b/modules/cairo-surface-pattern.cpp
@@ -24,6 +24,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include "cairo-private.h"
@@ -32,14 +33,14 @@ GJS_DEFINE_PROTO("CairoSurfacePattern", cairo_surface_pattern, JSCLASS_BACKGROUN
GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_surface_pattern)
{
GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_surface_pattern)
- JSObject *surface_wrapper;
cairo_surface_t *surface;
cairo_pattern_t *pattern;
GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_surface_pattern);
- if (!gjs_parse_call_args(context, "SurfacePattern", "o", argv,
- "surface", &surface_wrapper))
+ JS::RootedObject surface_wrapper(context);
+ if (!gjs_parse_call_args(context, "SurfacePattern", argv, "o",
+ "surface", &surface_wrapper))
return false;
surface = gjs_cairo_surface_get_surface(context, surface_wrapper);
@@ -83,8 +84,8 @@ setExtend_func(JSContext *context,
cairo_extend_t extend;
cairo_pattern_t *pattern;
- if (!gjs_parse_call_args(context, "setExtend", "i", argv,
- "extend", &extend))
+ if (!gjs_parse_call_args(context, "setExtend", argv, "i",
+ "extend", &extend))
return false;
pattern = gjs_cairo_pattern_get_pattern(context, obj);
@@ -131,8 +132,8 @@ setFilter_func(JSContext *context,
cairo_filter_t filter;
cairo_pattern_t *pattern;
- if (!gjs_parse_call_args(context, "setFilter", "i", argv,
- "filter", &filter))
+ if (!gjs_parse_call_args(context, "setFilter", argv, "i",
+ "filter", &filter))
return false;
pattern = gjs_cairo_pattern_get_pattern(context, obj);
diff --git a/modules/cairo-surface.cpp b/modules/cairo-surface.cpp
index 673219b..f4333a4 100644
--- a/modules/cairo-surface.cpp
+++ b/modules/cairo-surface.cpp
@@ -25,6 +25,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
#include <gi/foreign.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include <cairo-gobject.h>
#include "cairo-private.h"
@@ -66,8 +67,8 @@ writeToPNG_func(JSContext *context,
char *filename;
cairo_surface_t *surface;
- if (!gjs_parse_call_args(context, "writeToPNG", "s", argv,
- "filename", &filename))
+ if (!gjs_parse_call_args(context, "writeToPNG", argv, "F",
+ "filename", &filename))
return false;
surface = gjs_cairo_surface_get_surface(context, obj);
diff --git a/modules/cairo-svg-surface.cpp b/modules/cairo-svg-surface.cpp
index f305285..d7397ae 100644
--- a/modules/cairo-svg-surface.cpp
+++ b/modules/cairo-svg-surface.cpp
@@ -24,6 +24,7 @@
#include <gjs/gjs-module.h>
#include <gjs/compat.h>
+#include "gjs/jsapi-util-args.h"
#include <cairo.h>
#include "cairo-private.h"
@@ -41,10 +42,10 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_svg_surface)
GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_svg_surface);
- if (!gjs_parse_call_args(context, "SVGSurface", "sff", argv,
- "filename", &filename,
- "width", &width,
- "height", &height))
+ if (!gjs_parse_call_args(context, "SVGSurface", argv, "Fff",
+ "filename", &filename,
+ "width", &width,
+ "height", &height))
return false;
surface = cairo_svg_surface_create(filename, width, height);
diff --git a/modules/system.cpp b/modules/system.cpp
index 8ecf5e4..2d6175a 100644
--- a/modules/system.cpp
+++ b/modules/system.cpp
@@ -30,6 +30,7 @@
#include <gjs/gjs-module.h>
#include <gi/object.h>
+#include "gjs/jsapi-util-args.h"
#include "system.h"
static JSBool
@@ -38,14 +39,15 @@ gjs_address_of(JSContext *context,
JS::Value *vp)
{
JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
- JSObject *target_obj;
+ JS::RootedObject target_obj(context);
bool ret;
char *pointer_string;
- if (!gjs_parse_call_args(context, "addressOf", "o", argv, "object", &target_obj))
+ if (!gjs_parse_call_args(context, "addressOf", argv, "o",
+ "object", &target_obj))
return false;
- pointer_string = g_strdup_printf("%p", target_obj);
+ pointer_string = g_strdup_printf("%p", target_obj.get());
ret = gjs_string_from_utf8(context, pointer_string, -1, argv.rval());
@@ -62,8 +64,8 @@ gjs_refcount(JSContext *context,
JS::RootedObject target_obj(context);
GObject *obj;
- if (!gjs_parse_call_args(context, "refcount", "o", argv,
- "object", target_obj.address()))
+ if (!gjs_parse_call_args(context, "refcount", argv, "o",
+ "object", &target_obj))
return false;
if (!gjs_typecheck_object(context, target_obj, G_TYPE_OBJECT, true))
@@ -83,7 +85,7 @@ gjs_breakpoint(JSContext *context,
JS::Value *vp)
{
JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
- if (!gjs_parse_call_args(context, "breakpoint", "", argv))
+ if (!gjs_parse_call_args(context, "breakpoint", argv, ""))
return false;
G_BREAKPOINT();
argv.rval().setUndefined();
@@ -96,7 +98,7 @@ gjs_gc(JSContext *context,
JS::Value *vp)
{
JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
- if (!gjs_parse_call_args(context, "gc", "", argv))
+ if (!gjs_parse_call_args(context, "gc", argv, ""))
return false;
JS_GC(JS_GetRuntime(context));
argv.rval().setUndefined();
@@ -110,7 +112,8 @@ gjs_exit(JSContext *context,
{
JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
gint32 ecode;
- if (!gjs_parse_call_args(context, "exit", "i", argv, "ecode", &ecode))
+ if (!gjs_parse_call_args(context, "exit", argv, "i",
+ "ecode", &ecode))
return false;
exit(ecode);
return true;
diff --git a/test/gjs-test-call-args.cpp b/test/gjs-test-call-args.cpp
new file mode 100644
index 0000000..3e04ad9
--- /dev/null
+++ b/test/gjs-test-call-args.cpp
@@ -0,0 +1,194 @@
+#include <string.h>
+
+#include <glib.h>
+
+#include "gjs/compat.h"
+#include "gjs/jsapi-util-args.h"
+
+#define assert_match(str, pattern) \
+ G_STMT_START { \
+ const char *__s1 = (str), *__s2 = (pattern); \
+ if (!g_pattern_match_simple(__s2, __s1)) { \
+ g_printerr("**\nExpected \"%s\" to match \"%s\"\n", __s1, __s2); \
+ g_assert_not_reached(); \
+ } \
+ } G_STMT_END
+
+
+/* The class of the global object. */
+static JSClass global_class = {
+ "global",
+ JSCLASS_GLOBAL_FLAGS,
+ JS_PropertyStub,
+ JS_DeletePropertyStub,
+ JS_PropertyStub,
+ JS_StrictPropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub
+};
+
+typedef struct {
+ JSRuntime *rt;
+ JSContext *cx;
+ JSCompartment *compartment;
+ JSObject *global;
+ char *message; /* thrown exception message */
+} NativeTestFixture;
+
+static void
+unit_test_error_reporter(JSContext *cx,
+ const char *message,
+ JSErrorReport *report)
+{
+ JSRuntime *rt = JS_GetRuntime(cx);
+ NativeTestFixture *fx = static_cast<NativeTestFixture *>(JS_GetRuntimePrivate(rt));
+ g_free(fx->message);
+ fx->message = g_strdup(message);
+}
+
+#define JSNATIVE_TEST_FUNC_BEGIN(name) \
+ static JSBool \
+ name(JSContext *cx, \
+ unsigned argc, \
+ JS::Value *vp) \
+ { \
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
+ bool retval;
+
+#define JSNATIVE_TEST_FUNC_END \
+ if (retval) \
+ args.rval().setUndefined(); \
+ return retval; \
+ }
+
+JSNATIVE_TEST_FUNC_BEGIN(no_args)
+ retval = gjs_parse_call_args(cx, "noArgs", args, "");
+JSNATIVE_TEST_FUNC_END
+
+JSNATIVE_TEST_FUNC_BEGIN(no_args_ignore_trailing)
+ retval = gjs_parse_call_args(cx, "noArgsIgnoreTrailing", args, "!");
+JSNATIVE_TEST_FUNC_END
+
+JSNATIVE_TEST_FUNC_BEGIN(one_of_each_type)
+ bool boolval;
+ char *strval, *fileval;
+ int intval;
+ unsigned uintval;
+ int64_t int64val;
+ double dblval;
+ JS::RootedObject objval(cx);
+ retval = gjs_parse_call_args(cx, "oneOfEachType", args, "bsFiutfo",
+ "bool", &boolval,
+ "str", &strval,
+ "file", &fileval,
+ "int", &intval,
+ "uint", &uintval,
+ "int64", &int64val,
+ "dbl", &dblval,
+ "obj", &objval);
+JSNATIVE_TEST_FUNC_END
+
+static JSFunctionSpec native_test_funcs[] = {
+ JS_FS("noArgs", no_args, 0, 0),
+ JS_FS("noArgsIgnoreTrailing", no_args_ignore_trailing, 0, 0),
+ JS_FS("oneOfEachType", one_of_each_type, 0, 0),
+ JS_FS_END
+};
+
+static void
+setup(NativeTestFixture *fx,
+ gconstpointer unused)
+{
+ fx->rt = JS_NewRuntime(8L * 1024 * 1024, JS_USE_HELPER_THREADS);
+ g_assert_nonnull(fx->rt);
+
+ fx->cx = JS_NewContext(fx->rt, 8192);
+ g_assert_nonnull(fx->cx);
+ JS_SetRuntimePrivate(fx->rt, fx);
+ JS_SetErrorReporter(fx->cx, unit_test_error_reporter);
+
+ JS_BeginRequest(fx->cx);
+
+ fx->global = JS_NewGlobalObject(fx->cx, &global_class, NULL);
+ g_assert_nonnull(fx->global);
+ JS_AddNamedObjectRoot(fx->cx, &fx->global, "unit test global");
+ fx->compartment = JS_EnterCompartment(fx->cx, fx->global);
+
+ JS_InitStandardClasses(fx->cx, fx->global);
+
+ bool success = JS_DefineFunctions(fx->cx, fx->global, native_test_funcs);
+ g_assert_true(success);
+}
+
+static void
+teardown(NativeTestFixture *fx,
+ gconstpointer unused)
+{
+ JS_LeaveCompartment(fx->cx, fx->compartment);
+ JS_RemoveObjectRoot(fx->cx, &fx->global);
+
+ JS_EndRequest(fx->cx);
+
+ JS_DestroyContext(fx->cx);
+ JS_DestroyRuntime(fx->rt);
+ JS_ShutDown();
+}
+
+static void
+run_code(NativeTestFixture *fx,
+ gconstpointer code)
+{
+ const char *script = (const char *) code;
+ JS::CompileOptions options(fx->cx, JSVERSION_UNKNOWN);
+ options.setFileAndLine("unit test", 1);
+ bool ok = JS::Evaluate(fx->cx,
+ JS::HandleObject::fromMarkedLocation(&fx->global),
+ options, script, strlen(script), NULL);
+ if (fx->message)
+ g_printerr("**\n%s\n", fx->message);
+ g_assert_null(fx->message);
+ g_assert_true(ok);
+}
+
+static void
+run_code_expect_exception(NativeTestFixture *fx,
+ gconstpointer code)
+{
+ const char *script = (const char *) code;
+ JS::CompileOptions options(fx->cx, JSVERSION_UNKNOWN);
+ options.setFileAndLine("unit test", 1);
+ bool ok = JS::Evaluate(fx->cx,
+ JS::HandleObject::fromMarkedLocation(&fx->global),
+ options, script, strlen(script), NULL);
+ g_assert_false(ok);
+
+ /* Cheap way to shove an expected exception message into the data argument */
+ const char *expected_msg = strstr(script, "//");
+ if (expected_msg)
+ expected_msg += 2;
+ assert_match(fx->message, expected_msg);
+}
+
+void
+gjs_test_add_tests_for_parse_call_args(void)
+{
+#define ADD_CALL_ARGS_TEST_BASE(path, code, f) \
+ g_test_add("/callargs/" path, NativeTestFixture, code, setup, f, teardown)
+#define ADD_CALL_ARGS_TEST(path, code) \
+ ADD_CALL_ARGS_TEST_BASE(path, code, run_code)
+#define ADD_CALL_ARGS_TEST_XFAIL(path, code) \
+ ADD_CALL_ARGS_TEST_BASE(path, code, run_code_expect_exception)
+
+ ADD_CALL_ARGS_TEST("no-args-works", "noArgs()");
+ ADD_CALL_ARGS_TEST_XFAIL("no-args-fails-on-extra-args",
+ "noArgs(1, 2, 3)//*Expected 0 arguments, got 3");
+ ADD_CALL_ARGS_TEST("no-args-ignores-trailing",
+ "noArgsIgnoreTrailing(1, 2, 3)");
+ ADD_CALL_ARGS_TEST("one-of-each-type",
+ "oneOfEachType(true, 'foo', 'foo', 1, 1, 1, 1, {})");
+
+#undef ADD_CALL_ARGS_TEST_XFAIL
+#undef ADD_CALL_ARGS_TEST
+#undef ADD_CALL_ARGS_TEST_BASE
+}
diff --git a/test/gjs-tests-add-funcs.h b/test/gjs-tests-add-funcs.h
index a7414a6..23ae846 100644
--- a/test/gjs-tests-add-funcs.h
+++ b/test/gjs-tests-add-funcs.h
@@ -22,4 +22,6 @@
void gjs_test_add_tests_for_coverage ();
+void gjs_test_add_tests_for_parse_call_args(void);
+
#endif
diff --git a/test/gjs-tests.cpp b/test/gjs-tests.cpp
index c6afb8e..1b876bc 100644
--- a/test/gjs-tests.cpp
+++ b/test/gjs-tests.cpp
@@ -276,17 +276,18 @@ main(int argc,
g_test_init(&argc, &argv, NULL);
- g_test_add_func("/gjs/context/construct/destroy", gjstest_test_func_gjs_context_construct_destroy);
- g_test_add_func("/gjs/context/construct/eval", gjstest_test_func_gjs_context_construct_eval);
- g_test_add_func("/gjs/jsapi/util/error/throw", gjstest_test_func_gjs_jsapi_util_error_throw);
- g_test_add_func("/gjs/jsapi/util/string/js/string/utf8",
gjstest_test_func_gjs_jsapi_util_string_js_string_utf8);
- g_test_add_func("/gjs/jsutil/strip_shebang/no_shebang",
gjstest_test_strip_shebang_no_advance_for_no_shebang);
- g_test_add_func("/gjs/jsutil/strip_shebang/have_shebang",
gjstest_test_strip_shebang_advance_for_shebang);
- g_test_add_func("/gjs/jsutil/strip_shebang/only_shebang",
gjstest_test_strip_shebang_return_null_for_just_shebang);
- g_test_add_func("/util/glib/strv/concat/null", gjstest_test_func_util_glib_strv_concat_null);
- g_test_add_func("/util/glib/strv/concat/pointers", gjstest_test_func_util_glib_strv_concat_pointers);
-
- gjs_test_add_tests_for_coverage ();
+ // g_test_add_func("/gjs/context/construct/destroy", gjstest_test_func_gjs_context_construct_destroy);
+ // g_test_add_func("/gjs/context/construct/eval", gjstest_test_func_gjs_context_construct_eval);
+ // g_test_add_func("/gjs/jsapi/util/error/throw", gjstest_test_func_gjs_jsapi_util_error_throw);
+ // g_test_add_func("/gjs/jsapi/util/string/js/string/utf8",
gjstest_test_func_gjs_jsapi_util_string_js_string_utf8);
+ // g_test_add_func("/gjs/jsutil/strip_shebang/no_shebang",
gjstest_test_strip_shebang_no_advance_for_no_shebang);
+ // g_test_add_func("/gjs/jsutil/strip_shebang/have_shebang",
gjstest_test_strip_shebang_advance_for_shebang);
+ // g_test_add_func("/gjs/jsutil/strip_shebang/only_shebang",
gjstest_test_strip_shebang_return_null_for_just_shebang);
+ // g_test_add_func("/util/glib/strv/concat/null", gjstest_test_func_util_glib_strv_concat_null);
+ // g_test_add_func("/util/glib/strv/concat/pointers", gjstest_test_func_util_glib_strv_concat_pointers);
+
+ // gjs_test_add_tests_for_coverage ();
+ gjs_test_add_tests_for_parse_call_args();
g_test_run();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]