[gjs/wip/ptomato/mozjs31prep: 3/3] WIP - First sketch of rooted gjs_parse_call_args()



commit 2c7fecc6648a63e66fd4a9bd2a9c4a77f3163f26
Author: Philip Chimento <philip endlessm com>
Date:   Wed Oct 12 19:40:48 2016 -0700

    WIP - First sketch of rooted gjs_parse_call_args()

 gjs/jsapi-util.cpp |  340 ++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 239 insertions(+), 101 deletions(-)
---
diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp
index 440d5c4..6a92ebc 100644
--- a/gjs/jsapi-util.cpp
+++ b/gjs/jsapi-util.cpp
@@ -832,25 +832,196 @@ gjs_value_to_int64(JSContext      *context,
     return JS::ToInt64(context, val, (int64_t *) result);
 }
 
+template <typename T>
+static void
+assign(JSContext       *cx,
+       const char       c,
+       JS::HandleValue& value,
+       T&               ref)
+{
+    throw "Wrong type for c, got typeid(T).name";
+}
+
+static void
+assign(JSContext       *cx,
+       const char       c,
+       JS::HandleValue& value,
+       bool*&           ref)
+{
+    if (c != 'b')
+        throw "Wrong type for c, got bool*";
+    if (!value.isBoolean())
+        throw "Not a boolean";
+    *ref = value.toBoolean();
+}
+
+static void
+assign(JSContext               *cx,
+       const char               c,
+       JS::HandleValue&         value,
+       JS::MutableHandleObject& ref)
+{
+    if (c != 'o')
+        throw "Wrong type for c, got JS::MutableHandleObject";
+    if (!value.isObject())
+        throw "Not an object";
+    ref.set(&value.toObject());
+}
+
+// assign('s')
+// assign('F')
+
+static void
+assign(JSContext       *cx,
+       const char       c,
+       JS::HandleValue& value,
+       int32_t*&        ref)
+{
+    if (c != 'i')
+        throw "Wrong type for c, got int32_t*";
+    if (!JS::ToInt32(cx, value, ref)) {
+        /* Our error message is going to be more useful */
+        JS_ClearPendingException(cx);
+        throw "Couldn't convert to integer";
+    }
+}
+
+static void
+assign(JSContext       *cx,
+       const char       c,
+       JS::HandleValue& value,
+       uint32_t*&       ref)
+{
+    double num;
+
+    if (c != 'u')
+        throw "Wrong type for c, got uint32_t*";
+    if (!value.isNumber() || !JS::ToNumber(cx, value, &num)) {
+        /* Our error message is going to be more useful */
+        JS_ClearPendingException(cx);
+        throw "Couldn't convert to unsigned integer";
+    }
+    if (num > G_MAXUINT32 || num < 0)
+        throw "Value is out of range";
+    *ref = num;
+}
+
+static void
+assign(JSContext       *cx,
+       const char       c,
+       JS::HandleValue& value,
+       int64_t*&        ref)
+{
+    if (c != 't')
+        throw "Wrong type for c, got int64_t*";
+    if (!JS::ToInt64(cx, value, ref)) {
+        /* Our error message is going to be more useful */
+        JS_ClearPendingException(cx);
+        throw "Couldn't convert to 64-bit integer";
+    }
+}
+
+static void
+assign(JSContext       *cx,
+       const char       c,
+       JS::HandleValue& value,
+       double*&         ref)
+{
+    if (c != 'f')
+        throw "Wrong type for c, got double*";
+    if (!JS::ToNumber(cx, value, ref)) {
+        /* Our error message is going to be more useful */
+        JS_ClearPendingException(cx);
+        throw "Couldn't convert to double";
+    }
+}
+
+template<typename T>
+static void
+assign_nullable(JSContext       *cx,
+                const char       c,
+                JS::HandleValue& value,
+                T&               ref)
+{
+    throw "Wrong type for ?c, got typeid(T).name";
+}
+
+static void
+assign_nullable(JSContext               *cx,
+                const char               c,
+                JS::HandleValue&         value,
+                JS::MutableHandleObject& ref)
+{
+    if (c != 'o')
+        throw "Wrong type for ?c, got JS::MutableHandleObject";
+    if (value.isNull()) {
+        ref.set(NULL);
+        return;
+    }
+    if (!value.isObject())
+        throw "Not an object";
+    ref.set(&value.toObject());
+}
+
+// assign_nullable('s')
+// assign_nullable('F')
+
+template<typename T, typename... Args>
 static bool
-gjs_parse_args_valist (JSContext  *context,
-                       const char *function_name,
-                       const char *format,
-                       unsigned    argc,
-                       JS::Value  *argv,
-                       va_list     args)
+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)
 {
-    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;
+    bool nullable = false;
+    const char *fchar = fmt_required;
+    if (*fchar == '\0' && fmt_optional)
+        fchar = fmt_optional;
+    g_assert(((void)"Wrong number of parameters passed to gjs_parse_call_args()",
+              *fchar != '\0'));
+
+    if (*fchar == '?') {
+        nullable = true;
+        fchar++;
+        g_assert(((void)"Invalid format string, parameter required after '?'",
+                  *fchar != '\0'));
+    }
 
-    JS_BeginRequest(context);
+    try {
+        if (nullable) {
+            assign_nullable(cx, *fchar, args.get(param_ix), param_ref);
+        } else {
+            assign(cx, *fchar, args.get(param_ix), param_ref);
+        }
+    } catch (const char *message) {
+        gjs_throw(cx, "Error invoking %s, at argument %d (%s): %s",
+                  function_name, param_ix, param_name, message);
+        return false;
+    }
+
+    return parse_call_args_helper(cx, function_name, args, ignore_trailing_args,
+                                  fchar++, param_ix++, params...);
+}
+
+template<typename... Args>
+bool
+gjs_parse_call_args_t(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;
+
+    JSAutoRequest ar(cx);
 
     if (*format == '!') {
         ignore_trailing_args = true;
@@ -874,17 +1045,54 @@ gjs_parse_args_valist (JSContext  *context,
     if (n_required == 0)
         n_required = n_total;
 
-    if (argc < n_required || (argc > n_total && !ignore_trailing_args)) {
+    g_assert(((void)"Wrong number of parameters passed to gjs_parse_call_args()",
+              sizeof... Args == n_required));
+
+    // COMPAT: In future, use args.requireAtLeast()
+    if (args.length() < n_required ||
+        (args.length() > 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);
+            gjs_throw(cx, "Error invoking %s: Expected %d arguments, got %d",
+                      function_name, n_required, args.length());
         } 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);
+            gjs_throw(cx,
+                      "Error invoking %s: Expected minimum %d arguments (and %d optional), got %d",
+                      function_name, n_required, n_total - n_required,
+                      args.length());
         }
-        goto error_unwind;
+        return false;
     }
 
+    char **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_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];
+    guint consumed_args;
+
+    // JS request
+    // check for ignore_trailing_args '!' char
+    // check number of args
+
     /* We have 3 iteration variables here.
      * @i: The current integer position in fmt_args
      * @fmt_iter: A pointer to the character in fmt_args
@@ -913,6 +1121,7 @@ gjs_parse_args_valist (JSContext  *context,
 
         js_value = argv[consumed_args];
 
+        // handled '?o', still need to handle '?s' and '?F'
         if (*fmt_iter == '?') {
             fmt_iter++;
 
@@ -924,24 +1133,8 @@ gjs_parse_args_valist (JSContext  *context,
         }
 
         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;
+        // handle 'b'
+        // handle 'o'
         case 's': {
             char **arg = (char**) arg_location;
 
@@ -968,57 +1161,16 @@ gjs_parse_args_valist (JSContext  *context,
             }
         }
             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;
+        // handle 'i'
+        // handle 'u'
+        // handle 't'
+        // handle 'f'
         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;
-        }
-
+        // throw error if arg_error_message != NULL
         consumed_args++;
     }
 
@@ -1068,21 +1220,7 @@ gjs_parse_args_valist (JSContext  *context,
  * 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,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]