[gjs] arg: Automatically set all the numeric values to GIArguments



commit 6698716c91d8a8b7a0cbd99dfff1de7c9be3c305
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Thu May 14 20:37:16 2020 +0200

    arg: Automatically set all the numeric values to GIArguments
    
    Use a template-function to convert all the JS Values to the proper c++
    type and ensure they're within the valid ranges.
    
    As per this we need to use a more relaxed conversion to unsigned types using
    doubles not to have implicit conversion of negative types.
    
    Use a function override in order to keep a more generic version as
    inline implementation, while we specialize the out-of-range error
    handling per type

 gi/arg-inl.h      |  23 ++++++++++
 gi/arg.cpp        | 135 +++++++++++++++++++++++-------------------------------
 gi/js-value-inl.h |  30 ++++++++++++
 3 files changed, 111 insertions(+), 77 deletions(-)
---
diff --git a/gi/arg-inl.h b/gi/arg-inl.h
index 6d45e682..45779b62 100644
--- a/gi/arg-inl.h
+++ b/gi/arg-inl.h
@@ -17,8 +17,11 @@
 #include <girepository.h>
 #include <glib-object.h>  // for GType
 #include <glib.h>         // for gboolean
+#include <js/TypeDecls.h>  // for HandleValue
 
+#include "gi/js-value-inl.h"
 #include "gi/utils-inl.h"
+#include "gjs/macros.h"
 
 // GIArgument accessor templates
 //
@@ -205,3 +208,23 @@ gjs_arg_get_maybe_rounded(GIArgument* arg) {
 
     return static_cast<double>(val);
 }
+
+template <typename T>
+GJS_JSAPI_RETURN_CONVENTION inline bool gjs_arg_set_from_js_value(
+    JSContext* cx, const JS::HandleValue& value, GArgument* arg,
+    bool* out_of_range) {
+    if constexpr (Gjs::type_has_js_getter<T>())
+        return Gjs::js_value_to_c(cx, value, &gjs_arg_member<T>(arg));
+
+    Gjs::JsValueHolder::Relaxed<T> val;
+
+    if (!Gjs::js_value_to_c_checked<T>(cx, value, &val, out_of_range))
+        return false;
+
+    if (*out_of_range)
+        return false;
+
+    gjs_arg_set<T>(arg, val);
+
+    return true;
+}
diff --git a/gi/arg.cpp b/gi/arg.cpp
index ef13b858..a3315363 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -54,7 +54,6 @@
 #include "gi/gerror.h"
 #include "gi/gtype.h"
 #include "gi/interface.h"
-#include "gi/js-value-inl.h"
 #include "gi/object.h"
 #include "gi/param.h"
 #include "gi/union.h"
@@ -1751,6 +1750,26 @@ static bool value_to_interface_gi_argument(
     return false;
 }
 
+template <typename T>
+GJS_JSAPI_RETURN_CONVENTION inline static bool gjs_arg_set_from_js_value(
+    JSContext* cx, const JS::HandleValue& value, GArgument* arg,
+    GITypeTag type_tag, const char* arg_name, GjsArgumentType arg_type) {
+    bool out_of_range = false;
+
+    if (!gjs_arg_set_from_js_value<T>(cx, value, arg, &out_of_range)) {
+        if (out_of_range) {
+            GjsAutoChar display_name =
+                gjs_argument_display_name(arg_name, arg_type);
+            gjs_throw(cx, "value is out of range for %s (type %s)",
+                      display_name.get(), g_type_tag_to_string(type_tag));
+        }
+
+        return false;
+    }
+
+    return true;
+}
+
 bool
 gjs_value_to_g_argument(JSContext      *context,
                         JS::HandleValue value,
@@ -1770,7 +1789,6 @@ gjs_value_to_g_argument(JSContext      *context,
 
     bool nullable_type = false;
     bool wrong = false;  // return false
-    bool out_of_range = false;
     bool report_type_mismatch = false;  // wrong=true, and still need to
                                         // gjs_throw a type problem
 
@@ -1781,97 +1799,66 @@ gjs_value_to_g_argument(JSContext      *context,
         gjs_arg_unset<void*>(arg);
         break;
 
-    case GI_TYPE_TAG_INT8: {
-        gint32 i;
-        if (!JS::ToInt32(context, value, &i))
-            wrong = true;
-        if (i > G_MAXINT8 || i < G_MININT8)
-            out_of_range = true;
-        gjs_arg_set<int8_t>(arg, i);
+    case GI_TYPE_TAG_INT8:
+        if (!gjs_arg_set_from_js_value<int8_t>(context, value, arg, type_tag,
+                                               arg_name, arg_type))
+            return false;
         break;
-    }
-    case GI_TYPE_TAG_UINT8: {
-        guint32 i;
-        if (!JS::ToUint32(context, value, &i))
-            wrong = true;
-        if (i > G_MAXUINT8)
-            out_of_range = true;
-        gjs_arg_set<uint8_t>(arg, i);
+    case GI_TYPE_TAG_UINT8:
+        if (!gjs_arg_set_from_js_value<uint8_t>(context, value, arg, type_tag,
+                                                arg_name, arg_type))
+            return false;
         break;
-    }
-    case GI_TYPE_TAG_INT16: {
-        gint32 i;
-        if (!JS::ToInt32(context, value, &i))
-            wrong = true;
-        if (i > G_MAXINT16 || i < G_MININT16)
-            out_of_range = true;
-        gjs_arg_set<int16_t>(arg, i);
+    case GI_TYPE_TAG_INT16:
+        if (!gjs_arg_set_from_js_value<int16_t>(context, value, arg, type_tag,
+                                                arg_name, arg_type))
+            return false;
         break;
-    }
 
-    case GI_TYPE_TAG_UINT16: {
-        guint32 i;
-        if (!JS::ToUint32(context, value, &i))
-            wrong = true;
-        if (i > G_MAXUINT16)
-            out_of_range = true;
-        gjs_arg_set<uint16_t>(arg, i);
+    case GI_TYPE_TAG_UINT16:
+        if (!gjs_arg_set_from_js_value<uint16_t>(context, value, arg, type_tag,
+                                                 arg_name, arg_type))
+            return false;
         break;
-    }
 
     case GI_TYPE_TAG_INT32:
-        if (!JS::ToInt32(context, value, &gjs_arg_member<int32_t>(arg)))
-            wrong = true;
+        if (!gjs_arg_set_from_js_value<int32_t>(context, value, arg, type_tag,
+                                                arg_name, arg_type))
+            return false;
         break;
 
-    case GI_TYPE_TAG_UINT32: {
-        gdouble i;
-        if (!JS::ToNumber(context, value, &i))
-            wrong = true;
-        if (i > G_MAXUINT32 || i < 0)
-            out_of_range = true;
-        gjs_arg_set<uint32_t>(arg, CLAMP(i, 0, G_MAXUINT32));
+    case GI_TYPE_TAG_UINT32:
+        if (!gjs_arg_set_from_js_value<uint32_t>(context, value, arg, type_tag,
+                                                 arg_name, arg_type))
+            return false;
         break;
-    }
 
-    case GI_TYPE_TAG_INT64: {
-        double v;
-        if (!JS::ToNumber(context, value, &v))
-            wrong = true;
-        if (v > G_MAXINT64 || v < G_MININT64)
-            out_of_range = true;
-        gjs_arg_set<int64_t>(arg, v);
-    }
+    case GI_TYPE_TAG_INT64:
+        if (!gjs_arg_set_from_js_value<int64_t>(context, value, arg, type_tag,
+                                                arg_name, arg_type))
+            return false;
         break;
 
-    case GI_TYPE_TAG_UINT64: {
-        double v;
-        if (!JS::ToNumber(context, value, &v))
-            wrong = true;
-        if (v < 0)
-            out_of_range = true;
-        /* XXX we fail with values close to G_MAXUINT64 */
-        gjs_arg_set<uint64_t>(arg, MAX(v, 0));
-    }
+    case GI_TYPE_TAG_UINT64:
+        if (!gjs_arg_set_from_js_value<uint64_t>(context, value, arg, type_tag,
+                                                 arg_name, arg_type))
+            return false;
         break;
 
     case GI_TYPE_TAG_BOOLEAN:
         gjs_arg_set(arg, JS::ToBoolean(value));
         break;
 
-    case GI_TYPE_TAG_FLOAT: {
-        double v;
-        if (!JS::ToNumber(context, value, &v))
-            wrong = true;
-        if (v > G_MAXFLOAT || v < - G_MAXFLOAT)
-            out_of_range = true;
-        gjs_arg_set<float>(arg, v);
-    }
+    case GI_TYPE_TAG_FLOAT:
+        if (!gjs_arg_set_from_js_value<float>(context, value, arg, type_tag,
+                                              arg_name, arg_type))
+            return false;
         break;
 
     case GI_TYPE_TAG_DOUBLE:
-        if (!JS::ToNumber(context, value, &gjs_arg_member<double>(arg)))
-            wrong = true;
+        if (!gjs_arg_set_from_js_value<double>(context, value, arg, type_tag,
+                                               arg_name, arg_type))
+            return false;
         break;
 
     case GI_TYPE_TAG_UNICHAR:
@@ -2162,12 +2149,6 @@ _Pragma("GCC diagnostic pop")
             throw_invalid_argument(context, value, type_info, arg_name, arg_type);
         }
         return false;
-    } else if (G_UNLIKELY(out_of_range)) {
-        GjsAutoChar display_name =
-            gjs_argument_display_name(arg_name, arg_type);
-        gjs_throw(context, "value is out of range for %s (type %s)",
-                  display_name.get(), g_type_tag_to_string(type_tag));
-        return false;
     } else if (nullable_type && !gjs_arg_get<void*>(arg) && !may_be_null) {
         GjsAutoChar display_name =
             gjs_argument_display_name(arg_name, arg_type);
diff --git a/gi/js-value-inl.h b/gi/js-value-inl.h
index 32bd3749..b597a0b9 100644
--- a/gi/js-value-inl.h
+++ b/gi/js-value-inl.h
@@ -41,6 +41,20 @@ constexpr auto get_strict() {
         return int32_t{};
     else if constexpr (type_fits<T, uint32_t>())
         return uint32_t{};
+    else if constexpr (type_fits<T, double>())
+        return double{};
+    else
+        return T{};
+}
+
+template <typename T>
+constexpr auto get_relaxed() {
+    if constexpr (type_fits<T, int32_t>())
+        return int32_t{};
+    else if constexpr (type_fits<T, uint16_t>())
+        return uint32_t{};
+    else if constexpr (std::is_arithmetic_v<T>)
+        return double{};
     else
         return T{};
 }
@@ -48,8 +62,18 @@ constexpr auto get_strict() {
 template <typename T>
 using Strict = decltype(JsValueHolder::get_strict<T>());
 
+
+template <typename T>
+using Relaxed = decltype(JsValueHolder::get_relaxed<T>());
+
 }  // namespace JsValueHolder
 
+
+template <typename T, typename MODE = JsValueHolder::Relaxed<T>>
+constexpr bool type_has_js_getter() {
+    return std::is_same_v<T, MODE>;
+}
+
 /* Avoid implicit conversions */
 template <typename T>
 GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(JSContext*,
@@ -68,6 +92,12 @@ inline bool js_value_to_c(JSContext* cx, const JS::HandleValue& value,
     return JS::ToUint32(cx, value, out);
 }
 
+GJS_JSAPI_RETURN_CONVENTION
+inline bool js_value_to_c(JSContext* cx, const JS::HandleValue& value,
+                          double* out) {
+    return JS::ToNumber(cx, value, out);
+}
+
 template <typename WantedType, typename T>
 GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
     JSContext* cx, const JS::HandleValue& value, T* out, bool* out_of_range) {


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