LCOV - code coverage report
Current view: top level - gi - js-value-inl.h (source / functions) Hit Total Coverage
Test: gjs- Code Coverage Lines: 83 91 91.2 %
Date: 2023-09-17 02:39:54 Functions: 29 30 96.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 34 42 81.0 %

           Branch data     Line data    Source code
       1                 :            : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
       2                 :            : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
       3                 :            : // SPDX-FileCopyrightText: 2020 Marco Trevisan <marco.trevisan@canonical.com>
       4                 :            : 
       5                 :            : #pragma once
       6                 :            : 
       7                 :            : #include <config.h>
       8                 :            : 
       9                 :            : #include <stdint.h>
      10                 :            : 
      11                 :            : #include <cmath>  // for isnan
      12                 :            : #include <limits>
      13                 :            : 
      14                 :            : #include <girepository.h>
      15                 :            : #include <glib-object.h>
      16                 :            : #include <glib.h>
      17                 :            : 
      18                 :            : #include <js/BigInt.h>
      19                 :            : #include <js/Conversions.h>
      20                 :            : #include <js/RootingAPI.h>
      21                 :            : #include <js/TypeDecls.h>
      22                 :            : #include <js/Utility.h>  // for UniqueChars
      23                 :            : 
      24                 :            : #include "gi/gtype.h"
      25                 :            : #include "gi/value.h"
      26                 :            : #include "gjs/jsapi-util.h"
      27                 :            : #include "gjs/macros.h"
      28                 :            : 
      29                 :            : namespace Gjs {
      30                 :            : 
      31                 :            : template <typename T>
      32                 :            : struct TypeWrapper {
      33                 :            :     constexpr TypeWrapper() : m_value(0) {}
      34                 :       1404 :     explicit constexpr TypeWrapper(T v) : m_value(v) {}
      35                 :            :     constexpr operator T() const { return m_value; }
      36                 :       1400 :     constexpr operator T() { return m_value; }
      37                 :            : 
      38                 :            :  private:
      39                 :            :     T m_value;
      40                 :            : };
      41                 :            : 
      42                 :            : namespace JsValueHolder {
      43                 :            : 
      44                 :            : template <typename T1, typename T2>
      45                 :            : constexpr bool comparable_types() {
      46                 :            :     return std::is_arithmetic_v<T1> == std::is_arithmetic_v<T2> &&
      47                 :            :            std::is_signed_v<T1> == std::is_signed_v<T2>;
      48                 :            : }
      49                 :            : 
      50                 :            : template <typename T, typename Container>
      51                 :            : constexpr bool type_fits() {
      52                 :            :     if constexpr (comparable_types<T, Container>()) {
      53                 :            :         return (std::is_integral_v<T> == std::is_integral_v<Container> &&
      54                 :            :                 std::numeric_limits<T>::max() <=
      55                 :            :                     std::numeric_limits<Container>::max() &&
      56                 :            :                 std::numeric_limits<T>::lowest() >=
      57                 :            :                     std::numeric_limits<Container>::lowest());
      58                 :            :     }
      59                 :            : 
      60                 :            :     return false;
      61                 :            : }
      62                 :            : 
      63                 :            : /* The tag is needed to disambiguate types such as gboolean and GType
      64                 :            :  * which are in fact typedef's of other generic types.
      65                 :            :  * Setting a tag for a type allows to perform proper specialization. */
      66                 :            : template <typename T, GITypeTag TAG>
      67                 :            : constexpr auto get_strict() {
      68                 :            :     if constexpr (TAG != GI_TYPE_TAG_VOID) {
      69                 :            :         if constexpr (std::is_same_v<T, GType> && TAG == GI_TYPE_TAG_GTYPE)
      70                 :            :             return GType{};
      71                 :            :         else if constexpr (std::is_same_v<T, gboolean> &&
      72                 :            :                            TAG == GI_TYPE_TAG_BOOLEAN)
      73                 :            :             return gboolean{};
      74                 :            :         else
      75                 :            :             return;
      76                 :            :     } else {
      77                 :            :         if constexpr (std::is_same_v<T, char32_t>)
      78                 :            :             return char32_t{};
      79                 :            :         else if constexpr (type_fits<T, int32_t>())
      80                 :            :             return int32_t{};
      81                 :            :         else if constexpr (type_fits<T, uint32_t>())
      82                 :            :             return uint32_t{};
      83                 :            :         else if constexpr (type_fits<T, int64_t>())
      84                 :            :             return int64_t{};
      85                 :            :         else if constexpr (type_fits<T, uint64_t>())
      86                 :            :             return uint64_t{};
      87                 :            :         else if constexpr (type_fits<T, double>())
      88                 :            :             return double{};
      89                 :            :         else
      90                 :            :             return T{};
      91                 :            :     }
      92                 :            : }
      93                 :            : 
      94                 :            : template <typename T>
      95                 :            : constexpr auto get_relaxed() {
      96                 :            :     if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>)
      97                 :            :         return TypeWrapper<T>{};
      98                 :            :     else if constexpr (type_fits<T, int32_t>())
      99                 :            :         return int32_t{};
     100                 :            :     else if constexpr (type_fits<T, uint16_t>())
     101                 :            :         return uint32_t{};
     102                 :            :     else if constexpr (std::is_arithmetic_v<T>)
     103                 :            :         return double{};
     104                 :            :     else
     105                 :            :         return T{};
     106                 :            : }
     107                 :            : 
     108                 :            : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
     109                 :            : using Strict = decltype(JsValueHolder::get_strict<T, TAG>());
     110                 :            : 
     111                 :            : template <typename T>
     112                 :            : using Relaxed = decltype(JsValueHolder::get_relaxed<T>());
     113                 :            : 
     114                 :            : }  // namespace JsValueHolder
     115                 :            : 
     116                 :            : 
     117                 :            : template <typename T, typename MODE = JsValueHolder::Relaxed<T>>
     118                 :            : constexpr bool type_has_js_getter() {
     119                 :            :     return std::is_same_v<T, MODE>;
     120                 :            : }
     121                 :            : 
     122                 :            : /* Avoid implicit conversions */
     123                 :            : template <GITypeTag TAG = GI_TYPE_TAG_VOID, typename T>
     124                 :            : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(JSContext*,
     125                 :            :                                                       const JS::HandleValue&,
     126                 :            :                                                       T*) = delete;
     127                 :            : 
     128                 :            : template <>
     129                 :      12179 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
     130                 :            :     JSContext* cx, const JS::HandleValue& value, int32_t* out) {
     131                 :      12179 :     return JS::ToInt32(cx, value, out);
     132                 :            : }
     133                 :            : 
     134                 :            : template <>
     135                 :        128 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
     136                 :            :     JSContext* cx, const JS::HandleValue& value, uint32_t* out) {
     137                 :        128 :     return JS::ToUint32(cx, value, out);
     138                 :            : }
     139                 :            : 
     140                 :            : template <>
     141                 :         24 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
     142                 :            :     JSContext* cx, const JS::HandleValue& value, char32_t* out) {
     143                 :            :     uint32_t tmp;
     144                 :         24 :     bool retval = JS::ToUint32(cx, value, &tmp);
     145                 :         24 :     *out = tmp;
     146                 :         24 :     return retval;
     147                 :            : }
     148                 :            : 
     149                 :            : template <>
     150                 :        537 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
     151                 :            :     JSContext* cx, const JS::HandleValue& value, int64_t* out) {
     152         [ -  + ]:        537 :     if (value.isBigInt()) {
     153                 :          0 :         *out = JS::ToBigInt64(value.toBigInt());
     154                 :          0 :         return true;
     155                 :            :     }
     156                 :        537 :     return JS::ToInt64(cx, value, out);
     157                 :            : }
     158                 :            : 
     159                 :            : template <>
     160                 :          7 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
     161                 :            :     JSContext* cx, const JS::HandleValue& value, uint64_t* out) {
     162         [ -  + ]:          7 :     if (value.isBigInt()) {
     163                 :          0 :         *out = JS::ToBigUint64(value.toBigInt());
     164                 :          0 :         return true;
     165                 :            :     }
     166                 :          7 :     return JS::ToUint64(cx, value, out);
     167                 :            : }
     168                 :            : 
     169                 :            : template <>
     170                 :      11460 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
     171                 :            :     JSContext* cx, const JS::HandleValue& value, double* out) {
     172                 :      11460 :     return JS::ToNumber(cx, value, out);
     173                 :            : }
     174                 :            : 
     175                 :            : template <>
     176                 :         12 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c<GI_TYPE_TAG_BOOLEAN>(
     177                 :            :     JSContext*, const JS::HandleValue& value, gboolean* out) {
     178                 :         12 :     *out = !!JS::ToBoolean(value);
     179                 :         12 :     return true;
     180                 :            : }
     181                 :            : 
     182                 :            : template <>
     183                 :          5 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c<GI_TYPE_TAG_GTYPE>(
     184                 :            :     JSContext* cx, const JS::HandleValue& value, GType* out) {
     185         [ +  + ]:          5 :     if (!value.isObject())
     186                 :          2 :         return false;
     187                 :            : 
     188                 :          3 :     JS::RootedObject elem_obj(cx);
     189                 :          3 :     elem_obj = &value.toObject();
     190                 :            : 
     191         [ -  + ]:          3 :     if (!gjs_gtype_get_actual_gtype(cx, elem_obj, out))
     192                 :          0 :         return false;
     193                 :            : 
     194         [ -  + ]:          3 :     if (*out == G_TYPE_INVALID)
     195                 :          0 :         return false;
     196                 :            : 
     197                 :          3 :     return true;
     198                 :          3 : }
     199                 :            : 
     200                 :            : template <>
     201                 :         10 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
     202                 :            :     JSContext* cx, const JS::HandleValue& value, GValue* out) {
     203                 :         10 :     *out = G_VALUE_INIT;
     204                 :         10 :     return gjs_value_to_g_value(cx, value, out);
     205                 :            : }
     206                 :            : 
     207                 :            : template <>
     208                 :         91 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c(
     209                 :            :     JSContext* cx, const JS::HandleValue& value, char** out) {
     210                 :         91 :     JS::UniqueChars tmp_result = gjs_string_to_utf8(cx, value);
     211                 :            : 
     212         [ +  + ]:         91 :     if (!tmp_result)
     213                 :          1 :         return false;
     214                 :            : 
     215                 :         90 :     *out = g_strdup(tmp_result.get());
     216                 :         90 :     return true;
     217                 :         91 : }
     218                 :            : 
     219                 :            : template <typename BigT>
     220                 :       1264 : [[nodiscard]] inline constexpr BigT max_safe_big_number() {
     221                 :       1264 :     return (BigT(1) << std::numeric_limits<double>::digits) - 1;
     222                 :            : }
     223                 :            : 
     224                 :            : template <typename BigT>
     225                 :       1152 : [[nodiscard]] inline constexpr BigT min_safe_big_number() {
     226                 :            :     if constexpr (std::is_signed_v<BigT>)
     227                 :        124 :         return -(max_safe_big_number<BigT>());
     228                 :            : 
     229                 :       1028 :     return std::numeric_limits<BigT>::lowest();
     230                 :            : }
     231                 :            : 
     232                 :            : template <typename WantedType, typename T>
     233                 :      12983 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
     234                 :            :     JSContext* cx, const JS::HandleValue& value, T* out, bool* out_of_range) {
     235                 :            :     static_assert(std::numeric_limits<T>::max() >=
     236                 :            :                           std::numeric_limits<WantedType>::max() &&
     237                 :            :                       std::numeric_limits<T>::lowest() <=
     238                 :            :                           std::numeric_limits<WantedType>::lowest(),
     239                 :            :                   "Container can't contain wanted type");
     240                 :            : 
     241                 :            :     if constexpr (std::is_same_v<WantedType, uint64_t> ||
     242                 :            :                   std::is_same_v<WantedType, int64_t>) {
     243         [ +  - ]:       1461 :         if (out_of_range) {
     244                 :       1461 :             JS::BigInt* bi = nullptr;
     245                 :       1461 :             *out_of_range = false;
     246                 :            : 
     247         [ +  + ]:       1461 :             if (value.isBigInt()) {
     248                 :         60 :                 bi = value.toBigInt();
     249         [ +  + ]:       1401 :             } else if (value.isNumber()) {
     250                 :       1395 :                 double number = value.toNumber();
     251         [ +  + ]:       1395 :                 if (!std::isfinite(number)) {
     252                 :         15 :                     *out = 0;
     253                 :         15 :                     return true;
     254                 :            :                 }
     255                 :       1380 :                 number = std::trunc(number);
     256                 :       1380 :                 bi = JS::NumberToBigInt(cx, number);
     257         [ -  + ]:       1380 :                 if (!bi)
     258                 :          0 :                     return false;
     259                 :            :             }
     260                 :            : 
     261         [ +  + ]:       1446 :             if (bi) {
     262                 :       1440 :                 *out_of_range = Gjs::bigint_is_out_of_range(bi, out);
     263                 :       1440 :                 return true;
     264                 :            :             }
     265                 :            :         }
     266                 :            :     }
     267                 :            : 
     268                 :            :     if constexpr (std::is_same_v<WantedType, T>)
     269                 :         14 :         return js_value_to_c(cx, value, out);
     270                 :            : 
     271                 :            :     // JS::ToIntNN() converts undefined, NaN, infinity to 0
     272                 :            :     if constexpr (std::is_integral_v<WantedType>) {
     273   [ +  +  +  +  :      22839 :         if (value.isUndefined() ||
                   +  + ]
     274         [ +  + ]:      11433 :             (value.isDouble() && !std::isfinite(value.toDouble()))) {
     275                 :         27 :             *out = 0;
     276                 :         27 :             return true;
     277                 :            :         }
     278                 :            :     }
     279                 :            : 
     280                 :            :     if constexpr (std::is_arithmetic_v<T>) {
     281                 :      11487 :         bool ret = js_value_to_c(cx, value, out);
     282         [ +  - ]:      11487 :         if (out_of_range) {
     283                 :            :             // Infinity and NaN preserved between floating point types
     284                 :            :             if constexpr (std::is_floating_point_v<WantedType> &&
     285                 :            :                           std::is_floating_point_v<T>) {
     286         [ +  + ]:        108 :                 if (!std::isfinite(*out)) {
     287                 :          5 :                     *out_of_range = false;
     288                 :          5 :                     return ret;
     289                 :            :                 }
     290                 :            :             }
     291                 :            : 
     292                 :      11482 :             *out_of_range =
     293                 :      11482 :                 (*out >
     294         [ +  + ]:      22962 :                      static_cast<T>(std::numeric_limits<WantedType>::max()) ||
     295                 :      11480 :                  *out <
     296         [ +  + ]:      11480 :                      static_cast<T>(std::numeric_limits<WantedType>::lowest()));
     297                 :            : 
     298                 :            :             if constexpr (std::is_integral_v<WantedType> &&
     299                 :            :                           std::is_floating_point_v<T>)
     300                 :      11219 :                 *out_of_range |= std::isnan(*out);
     301                 :            :         }
     302                 :      11482 :         return ret;
     303                 :            :     }
     304                 :            : }
     305                 :            : 
     306                 :            : template <typename WantedType>
     307                 :       1404 : GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
     308                 :            :     JSContext* cx, const JS::HandleValue& value, TypeWrapper<WantedType>* out,
     309                 :            :     bool* out_of_range) {
     310                 :            :     static_assert(std::is_integral_v<WantedType>);
     311                 :            : 
     312                 :            :     WantedType wanted_out;
     313         [ -  + ]:       1404 :     if (!js_value_to_c_checked<WantedType>(cx, value, &wanted_out,
     314                 :            :                                            out_of_range))
     315                 :          0 :         return false;
     316                 :            : 
     317                 :       1404 :     *out = TypeWrapper<WantedType>{wanted_out};
     318                 :            : 
     319                 :       1404 :     return true;
     320                 :            : }
     321                 :            : 
     322                 :            : }  // namespace Gjs

Generated by: LCOV version 1.14