[gjs] arg: Automatically figure out the proper container type for a JS value



commit 8e9d037693dc72cf1683e22ac115141995b12229
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Tue May 12 23:35:00 2020 +0200

    arg: Automatically figure out the proper container type for a JS value
    
    Add template functions to get the JS value to a variable that is big
    enough to fit the requested type, doing also checks in case the
    resulting value is out of bounding for the requested type.
    
    This allows to redefine hashtable_int_key() so that there's no type
    checks and will allow to get values per type.
    
    Move the functions to a different header not to bother arg with such
    stuff that might be re-used also by other code paths

 gi/arg.cpp        | 55 +++----------------------------
 gi/js-value-inl.h | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 101 insertions(+), 50 deletions(-)
---
diff --git a/gi/arg.cpp b/gi/arg.cpp
index 756234ec..ef13b858 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -26,10 +26,8 @@
 
 #include <string.h>  // for strcmp, strlen, memcpy
 
-#include <limits>  // for numeric_limits
 #include <memory>  // for operator!=, unique_ptr
 #include <string>
-#include <type_traits>
 
 #include <girepository.h>
 #include <glib-object.h>
@@ -56,6 +54,7 @@
 #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"
@@ -462,65 +461,21 @@ gjs_array_to_g_list(JSContext   *context,
     return g_hash_table_new(NULL, NULL);
 }
 
-template <typename T>
-GJS_JSAPI_RETURN_CONVENTION static inline std::enable_if_t<
-    std::is_integral_v<T> && std::is_signed_v<T>, bool>
-js_value_convert(JSContext* cx, const JS::HandleValue& value, T* out) {
-    return JS::ToInt32(cx, value, out);
-}
-
-template <typename T>
-GJS_JSAPI_RETURN_CONVENTION static inline std::enable_if_t<
-    std::is_integral_v<T> && std::is_unsigned_v<T>, bool>
-js_value_convert(JSContext* cx, const JS::HandleValue& value, T* out) {
-    return JS::ToUint32(cx, value, out);
-}
-
-template <typename IntType, typename Container>
+template <typename IntType>
 GJS_JSAPI_RETURN_CONVENTION static bool hashtable_int_key(
     JSContext* cx, const JS::HandleValue& value, bool* out_of_range,
     void** pointer_out) {
-    Container i;
-
     static_assert(std::is_integral_v<IntType>, "Need an integer");
-    static_assert(std::numeric_limits<Container>::max() >=
-                  std::numeric_limits<IntType>::max(),
-                  "Max possible Container value must be at least the max possible IntType value");
-    static_assert(std::numeric_limits<Container>::min() <=
-                  std::numeric_limits<IntType>::min(),
-                  "Min possible Container value must be at most the min possible IntType value");
-
-    if (!js_value_convert<Container>(cx, value, &i))
-        return false;
 
-    if (out_of_range &&
-        (i > static_cast<Container>(std::numeric_limits<IntType>::max()) ||
-         i < static_cast<Container>(std::numeric_limits<IntType>::min())))
-        *out_of_range = true;
+    Gjs::JsValueHolder::Strict<IntType> i;
+    if (!Gjs::js_value_to_c_checked<IntType>(cx, value, &i, out_of_range))
+        return false;
 
     *pointer_out = gjs_int_to_pointer<IntType>(i);
 
     return true;
 }
 
-template <typename IntType>
-GJS_JSAPI_RETURN_CONVENTION static inline std::enable_if_t<
-    std::is_signed_v<IntType>, bool>
-hashtable_int_key(JSContext* cx, const JS::HandleValue& value,
-                  bool* out_of_range, void** pointer_out) {
-    return hashtable_int_key<IntType, int32_t>(cx, value, out_of_range,
-                                               pointer_out);
-}
-
-template <typename IntType>
-GJS_JSAPI_RETURN_CONVENTION static inline std::enable_if_t<
-    std::is_unsigned_v<IntType>, bool>
-hashtable_int_key(JSContext* cx, const JS::HandleValue& value,
-                  bool* out_of_range, void** pointer_out) {
-    return hashtable_int_key<IntType, uint32_t>(cx, value, out_of_range,
-                                                pointer_out);
-}
-
 /* Converts a JS::Value to a GHashTable key, stuffing it into @pointer_out if
  * possible, otherwise giving the location of an allocated key in @pointer_out.
  */
diff --git a/gi/js-value-inl.h b/gi/js-value-inl.h
new file mode 100644
index 00000000..32bd3749
--- /dev/null
+++ b/gi/js-value-inl.h
@@ -0,0 +1,96 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2020 Marco Trevisan <marco trevisan canonical com>
+
+#pragma once
+
+#include <stdint.h>
+#include <limits>
+#include <type_traits>
+
+#include <js/Conversions.h>
+
+#include "gjs/macros.h"
+
+namespace Gjs {
+
+namespace JsValueHolder {
+
+template <typename T1, typename T2>
+constexpr bool comparable_types() {
+    return std::is_arithmetic_v<T1> == std::is_arithmetic_v<T2> &&
+           std::is_signed_v<T1> == std::is_signed_v<T2>;
+}
+
+template <typename T, typename Container>
+constexpr bool type_fits() {
+    if constexpr (comparable_types<T, Container>()) {
+        return (std::is_integral_v<T> == std::is_integral_v<Container> &&
+                std::numeric_limits<T>::max() <=
+                    std::numeric_limits<Container>::max() &&
+                std::numeric_limits<T>::lowest() >=
+                    std::numeric_limits<Container>::lowest());
+    }
+
+    return false;
+}
+
+template <typename T>
+constexpr auto get_strict() {
+    if constexpr (type_fits<T, int32_t>())
+        return int32_t{};
+    else if constexpr (type_fits<T, uint32_t>())
+        return uint32_t{};
+    else
+        return T{};
+}
+
+template <typename T>
+using Strict = decltype(JsValueHolder::get_strict<T>());
+
+}  // namespace JsValueHolder
+
+/* Avoid implicit conversions */
+template <typename T>
+GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(JSContext*,
+                                                      const JS::HandleValue&,
+                                                      T*) = delete;
+
+GJS_JSAPI_RETURN_CONVENTION
+inline bool js_value_to_c(JSContext* cx, const JS::HandleValue& value,
+                          int32_t* out) {
+    return JS::ToInt32(cx, value, out);
+}
+
+GJS_JSAPI_RETURN_CONVENTION
+inline bool js_value_to_c(JSContext* cx, const JS::HandleValue& value,
+                          uint32_t* out) {
+    return JS::ToUint32(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) {
+    static_assert(std::numeric_limits<T>::max() >=
+                          std::numeric_limits<WantedType>::max() &&
+                      std::numeric_limits<T>::lowest() <=
+                          std::numeric_limits<WantedType>::lowest(),
+                  "Container can't contain wanted type");
+
+    if constexpr (std::is_same_v<WantedType, T>)
+        return js_value_to_c(cx, value, out);
+
+    if constexpr (std::is_arithmetic_v<T>) {
+        bool ret = js_value_to_c(cx, value, out);
+        if (out_of_range) {
+            *out_of_range =
+                (*out >
+                     static_cast<T>(std::numeric_limits<WantedType>::max()) ||
+                 *out <
+                     static_cast<T>(std::numeric_limits<WantedType>::lowest()));
+        }
+        return ret;
+    }
+}
+
+}  // namespace Gjs


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