LCOV - code coverage report
Current view: top level - gi - arg-inl.h (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 90.4 % 94 85
Test Date: 2025-05-07 12:25:00 Functions: 92.3 % 181 167
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 83.9 % 31 26

             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                 :             : #include <string.h>  // for memset
      11                 :             : 
      12                 :             : #include <cstddef>  // for nullptr_t
      13                 :             : #include <limits>
      14                 :             : #include <string>  // for to_string
      15                 :             : #include <type_traits>
      16                 :             : 
      17                 :             : #include <girepository.h>
      18                 :             : #include <glib-object.h>  // for GType
      19                 :             : #include <glib.h>         // for gboolean
      20                 :             : 
      21                 :             : #include <js/RootingAPI.h>  // for Handle
      22                 :             : #include <js/TypeDecls.h>  // for HandleValue
      23                 :             : 
      24                 :             : #include "gi/arg-types-inl.h"
      25                 :             : #include "gi/js-value-inl.h"
      26                 :             : #include "gi/utils-inl.h"
      27                 :             : #include "gjs/macros.h"
      28                 :             : 
      29                 :             : // GIArgument accessor templates
      30                 :             : //
      31                 :             : // These are intended to make access to the GIArgument union more type-safe and
      32                 :             : // reduce bugs that occur from assigning to one member and reading from another.
      33                 :             : // (These bugs often work fine on one processor architecture but crash on
      34                 :             : // another.)
      35                 :             : //
      36                 :             : // gjs_arg_member<T>(GIArgument*) - returns a reference to the appropriate union
      37                 :             : //   member that would hold the type T. Rarely used, unless as a pointer to a
      38                 :             : //   return location.
      39                 :             : // gjs_arg_get<T>(GIArgument*) - returns the value of type T from the
      40                 :             : //   appropriate union member.
      41                 :             : // gjs_arg_set(GIArgument*, T) - sets the appropriate union member for type T.
      42                 :             : // gjs_arg_unset<T>(GIArgument*) - sets the appropriate zero value in the
      43                 :             : //   appropriate union member for type T.
      44                 :             : // gjs_arg_steal<T>(GIArgument*) - sets the appropriate zero value in the
      45                 :             : //   appropriate union member for type T and returns the replaced value.
      46                 :             : 
      47                 :             : template <auto GIArgument::*member>
      48                 :      349886 : [[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) {
      49                 :      349886 :     return (arg->*member);
      50                 :             : }
      51                 :             : 
      52                 :             : template <typename TAG>
      53                 :      349886 : [[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) {
      54                 :             :     if constexpr (std::is_same_v<TAG, Gjs::Tag::GBoolean>)
      55                 :        2488 :         return gjs_arg_member<&GIArgument::v_boolean>(arg);
      56                 :             : 
      57                 :             :     if constexpr (std::is_same_v<TAG, Gjs::Tag::GType>) {
      58                 :             :         // GType is defined differently on 32-bit vs. 64-bit architectures.
      59                 :             :         if constexpr (std::is_same_v<GType, gsize>)
      60                 :        2317 :             return gjs_arg_member<&GIArgument::v_size>(arg);
      61                 :             :         else if constexpr (std::is_same_v<GType, gulong>)
      62                 :             :             return gjs_arg_member<&GIArgument::v_ulong>(arg);
      63                 :             :     }
      64                 :             : 
      65                 :             :     if constexpr (std::is_same_v<TAG, Gjs::Tag::Long>)
      66                 :           0 :         return gjs_arg_member<&GIArgument::v_long>(arg);
      67                 :             :     if constexpr (std::is_same_v<TAG, Gjs::Tag::UnsignedLong>)
      68                 :           0 :         return gjs_arg_member<&GIArgument::v_ulong>(arg);
      69                 :             : 
      70                 :             :     if constexpr (std::is_same_v<TAG, Gjs::Tag::Enum>)
      71                 :        3172 :         return gjs_arg_member<&GIArgument::v_int>(arg);
      72                 :             :     if constexpr (std::is_same_v<TAG, Gjs::Tag::UnsignedEnum>)
      73                 :        2104 :         return gjs_arg_member<&GIArgument::v_uint>(arg);
      74                 :             : 
      75                 :             :     if constexpr (std::is_same_v<TAG, bool>)
      76                 :         401 :         return gjs_arg_member<&GIArgument::v_boolean>(arg);
      77                 :             :     if constexpr (std::is_same_v<TAG, int8_t>)
      78                 :         307 :         return gjs_arg_member<&GIArgument::v_int8>(arg);
      79                 :             :     if constexpr (std::is_same_v<TAG, uint8_t>)
      80                 :          80 :         return gjs_arg_member<&GIArgument::v_uint8>(arg);
      81                 :             :     if constexpr (std::is_same_v<TAG, int16_t>)
      82                 :          84 :         return gjs_arg_member<&GIArgument::v_int16>(arg);
      83                 :             :     if constexpr (std::is_same_v<TAG, uint16_t>)
      84                 :          48 :         return gjs_arg_member<&GIArgument::v_uint16>(arg);
      85                 :             :     if constexpr (std::is_same_v<TAG, int32_t>)
      86                 :       19971 :         return gjs_arg_member<&GIArgument::v_int32>(arg);
      87                 :             :     if constexpr (std::is_same_v<TAG, uint32_t>)
      88                 :       40921 :         return gjs_arg_member<&GIArgument::v_uint32>(arg);
      89                 :             :     if constexpr (std::is_same_v<TAG, int64_t>)
      90                 :         500 :         return gjs_arg_member<&GIArgument::v_int64>(arg);
      91                 :             :     if constexpr (std::is_same_v<TAG, uint64_t>)
      92                 :        3563 :         return gjs_arg_member<&GIArgument::v_uint64>(arg);
      93                 :             : 
      94                 :             :     // gunichar is stored in v_uint32
      95                 :             :     if constexpr (std::is_same_v<TAG, char32_t>)
      96                 :           8 :         return gjs_arg_member<&GIArgument::v_uint32>(arg);
      97                 :             : 
      98                 :             :     if constexpr (std::is_same_v<TAG, float>)
      99                 :          89 :         return gjs_arg_member<&GIArgument::v_float>(arg);
     100                 :             : 
     101                 :             :     if constexpr (std::is_same_v<TAG, double>)
     102                 :         148 :         return gjs_arg_member<&GIArgument::v_double>(arg);
     103                 :             : 
     104                 :             :     if constexpr (std::is_same_v<TAG, char*>)
     105                 :        5947 :         return gjs_arg_member<&GIArgument::v_string>(arg);
     106                 :             : 
     107                 :             :     if constexpr (std::is_same_v<TAG, void*>)
     108                 :      241162 :         return gjs_arg_member<&GIArgument::v_pointer>(arg);
     109                 :             : 
     110                 :             :     if constexpr (std::is_same_v<TAG, std::nullptr_t>)
     111                 :             :         return gjs_arg_member<&GIArgument::v_pointer>(arg);
     112                 :             : 
     113                 :             :     if constexpr (std::is_pointer<TAG>()) {
     114                 :             :         using NonconstPtrT =
     115                 :             :             std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<TAG>>>;
     116                 :             :         return reinterpret_cast<NonconstPtrT&>(
     117                 :       26576 :             gjs_arg_member<&GIArgument::v_pointer>(arg));
     118                 :             :     }
     119                 :             : }
     120                 :             : 
     121                 :             : template <typename TAG, typename = std::enable_if_t<
     122                 :             :                             std::is_arithmetic_v<Gjs::Tag::RealT<TAG>>>>
     123                 :       16996 : constexpr inline void gjs_arg_set(GIArgument* arg, Gjs::Tag::RealT<TAG> v) {
     124                 :             :     if constexpr (std::is_same_v<TAG, bool> ||
     125                 :             :                   std::is_same_v<TAG, Gjs::Tag::GBoolean>)
     126                 :         340 :         v = !!v;
     127                 :             : 
     128                 :       16996 :     gjs_arg_member<TAG>(arg) = v;
     129                 :       16996 : }
     130                 :             : 
     131                 :             : // Specialization for types where TAG and RealT<TAG> are the same type, to allow
     132                 :             : // inferring template parameter
     133                 :             : template <typename T,
     134                 :             :           typename = std::enable_if_t<std::is_same_v<Gjs::Tag::RealT<T>, T> &&
     135                 :             :                                       std::is_arithmetic_v<T>>>
     136                 :         344 : constexpr inline void gjs_arg_set(GIArgument* arg, T v) {
     137                 :         344 :     gjs_arg_set<T>(arg, v);
     138                 :         344 : }
     139                 :             : 
     140                 :             : // Specialization for non-function pointers, so that you don't have to repeat
     141                 :             : // the pointer type explicitly for type deduction, and that takes care of
     142                 :             : // GIArgument not having constness
     143                 :             : template <typename T, typename = std::enable_if_t<!std::is_function_v<T>>>
     144                 :       22446 : constexpr inline void gjs_arg_set(GIArgument* arg, T* v) {
     145                 :             :     using NonconstPtrT = std::add_pointer_t<std::remove_const_t<T>>;
     146                 :       22446 :     gjs_arg_member<NonconstPtrT>(arg) = const_cast<NonconstPtrT>(v);
     147                 :       22446 : }
     148                 :             : 
     149                 :             : // Overload for nullptr since it's not handled by TAG*
     150                 :           2 : constexpr inline void gjs_arg_set(GIArgument* arg, std::nullptr_t) {
     151                 :           2 :     gjs_arg_member<void*>(arg) = nullptr;
     152                 :           2 : }
     153                 :             : 
     154                 :             : // Store function pointers as void*. It is a requirement of GLib that your
     155                 :             : // compiler can do this
     156                 :             : template <typename ReturnT, typename... Args>
     157                 :         102 : constexpr inline void gjs_arg_set(GIArgument* arg, ReturnT (*v)(Args...)) {
     158                 :         102 :     gjs_arg_member<void*>(arg) = reinterpret_cast<void*>(v);
     159                 :         102 : }
     160                 :             : 
     161                 :             : // Specifying an integer-type tag and passing a void pointer, extracts a stuffed
     162                 :             : // integer out of the pointer; otherwise just store the pointer in v_pointer
     163                 :             : template <typename TAG = void*>
     164                 :       56453 : constexpr inline void gjs_arg_set(GIArgument* arg, void* v) {
     165                 :             :     using T = Gjs::Tag::RealT<TAG>;
     166                 :             :     if constexpr (std::is_integral_v<T>)
     167                 :             :         gjs_arg_set<TAG>(arg, gjs_pointer_to_int<T>(v));
     168                 :             :     else
     169                 :       56453 :         gjs_arg_member<void*>(arg) = v;
     170                 :       56453 : }
     171                 :             : 
     172                 :             : template <typename TAG>
     173                 :      196561 : [[nodiscard]] constexpr inline Gjs::Tag::RealT<TAG> gjs_arg_get(
     174                 :             :     GIArgument* arg) {
     175                 :             :     if constexpr (std::is_same_v<TAG, bool> ||
     176                 :             :                   std::is_same_v<TAG, Gjs::Tag::GBoolean>)
     177                 :        1402 :         return Gjs::Tag::RealT<TAG>(!!gjs_arg_member<TAG>(arg));
     178                 :             : 
     179                 :      195159 :     return gjs_arg_member<TAG>(arg);
     180                 :             : }
     181                 :             : 
     182                 :             : template <typename TAG>
     183                 :             : [[nodiscard]] constexpr inline void* gjs_arg_get_as_pointer(GIArgument* arg) {
     184                 :             :     return gjs_int_to_pointer(gjs_arg_get<TAG>(arg));
     185                 :             : }
     186                 :             : 
     187                 :       31394 : constexpr inline void gjs_arg_unset(GIArgument* arg) {
     188                 :             :     // Clear all bits of the out C value. No one member is guaranteed to span
     189                 :             :     // the whole union on all architectures, so use memset() instead of
     190                 :             :     // gjs_arg_set<T>(arg, 0) for some type T.
     191                 :       31394 :     memset(arg, 0, sizeof(GIArgument));
     192                 :       31394 : }
     193                 :             : 
     194                 :             : template <typename TAG>
     195                 :       15627 : [[nodiscard]] constexpr inline Gjs::Tag::RealT<TAG> gjs_arg_steal(
     196                 :             :     GIArgument* arg) {
     197                 :       15627 :     auto val = gjs_arg_get<TAG>(arg);
     198                 :       15627 :     gjs_arg_unset(arg);
     199                 :       15627 :     return val;
     200                 :             : }
     201                 :             : 
     202                 :             : // Implementation to store rounded (u)int64_t numbers into double
     203                 :             : 
     204                 :             : template <typename BigTag>
     205                 :             : [[nodiscard]] inline constexpr std::enable_if_t<
     206                 :             :     std::is_integral_v<Gjs::Tag::RealT<BigTag>> &&
     207                 :             :         (std::numeric_limits<Gjs::Tag::RealT<BigTag>>::max() >
     208                 :             :          std::numeric_limits<int32_t>::max()),
     209                 :             :     double>
     210                 :          39 : gjs_arg_get_maybe_rounded(GIArgument* arg) {
     211                 :             :     using BigT = Gjs::Tag::RealT<BigTag>;
     212                 :          39 :     BigT val = gjs_arg_get<BigTag>(arg);
     213                 :             : 
     214   [ +  +  +  +  :          76 :     if (val < Gjs::min_safe_big_number<BigT>() ||
                   +  + ]
     215                 :          37 :         val > Gjs::max_safe_big_number<BigT>()) {
     216                 :           7 :         g_warning(
     217                 :             :             "Value %s cannot be safely stored in a JS Number "
     218                 :             :             "and may be rounded",
     219                 :             :             std::to_string(val).c_str());
     220                 :             :     }
     221                 :             : 
     222                 :          39 :     return static_cast<double>(val);
     223                 :             : }
     224                 :             : 
     225                 :             : template <typename TAG>
     226                 :       29807 : GJS_JSAPI_RETURN_CONVENTION inline bool gjs_arg_set_from_js_value(
     227                 :             :     JSContext* cx, JS::HandleValue value, GIArgument* arg, bool* out_of_range) {
     228                 :             :     if constexpr (Gjs::type_has_js_getter<TAG>())
     229                 :       14371 :         return Gjs::js_value_to_c<TAG>(cx, value, &gjs_arg_member<TAG>(arg));
     230                 :             : 
     231                 :       15436 :     Gjs::Tag::JSValuePackT<TAG> val{};
     232                 :             : 
     233                 :             :     using T = Gjs::Tag::RealT<TAG>;
     234                 :             :     using HolderTag = Gjs::Tag::JSValuePackTag<TAG>;
     235         [ +  + ]:       15436 :     if (!Gjs::js_value_to_c_checked<T, HolderTag>(cx, value, &val,
     236                 :             :                                                   out_of_range))
     237                 :          17 :         return false;
     238                 :             : 
     239         [ +  + ]:       15419 :     if (*out_of_range)
     240                 :           8 :         return false;
     241                 :             : 
     242                 :       15411 :     gjs_arg_set<TAG>(arg, val);
     243                 :             : 
     244                 :       15411 :     return true;
     245                 :             : }
     246                 :             : 
     247                 :             : // A helper function to retrieve array lengths from a GIArgument (letting the
     248                 :             : // compiler generate good instructions in case of big endian machines)
     249                 :         468 : [[nodiscard]] constexpr size_t gjs_gi_argument_get_array_length(
     250                 :             :     GITypeTag tag, GIArgument* arg) {
     251   [ -  +  -  -  :         468 :     switch (tag) {
             +  +  +  +  
                      - ]
     252                 :           0 :         case GI_TYPE_TAG_INT8:
     253                 :           0 :             return gjs_arg_get<int8_t>(arg);
     254                 :           1 :         case GI_TYPE_TAG_UINT8:
     255                 :           1 :             return gjs_arg_get<uint8_t>(arg);
     256                 :           0 :         case GI_TYPE_TAG_INT16:
     257                 :           0 :             return gjs_arg_get<int16_t>(arg);
     258                 :           0 :         case GI_TYPE_TAG_UINT16:
     259                 :           0 :             return gjs_arg_get<uint16_t>(arg);
     260                 :         123 :         case GI_TYPE_TAG_INT32:
     261                 :         123 :             return gjs_arg_get<int32_t>(arg);
     262                 :           7 :         case GI_TYPE_TAG_UINT32:
     263                 :           7 :             return gjs_arg_get<uint32_t>(arg);
     264                 :          17 :         case GI_TYPE_TAG_INT64:
     265                 :          17 :             return gjs_arg_get<int64_t>(arg);
     266                 :         320 :         case GI_TYPE_TAG_UINT64:
     267                 :         320 :             return gjs_arg_get<uint64_t>(arg);
     268                 :           0 :         default:
     269                 :             :             g_assert_not_reached();
     270                 :             :     }
     271                 :             : }
     272                 :             : 
     273                 :             : namespace Gjs {
     274                 :             : 
     275                 :       57647 : [[nodiscard]] static inline bool is_basic_type(GITypeTag tag, bool is_pointer) {
     276   [ +  +  +  - ]:       57647 :     if (tag == GI_TYPE_TAG_VOID && is_pointer)
     277                 :         276 :         return false;  // void* is not a basic type
     278   [ +  +  +  + ]:       57371 :     return GI_TYPE_TAG_IS_BASIC(tag);
     279                 :             : }
     280                 :             : 
     281                 :         105 : [[nodiscard]] static inline bool basic_type_needs_release(GITypeTag tag) {
     282                 :         105 :     g_assert(GI_TYPE_TAG_IS_BASIC(tag));
     283   [ +  +  +  + ]:         105 :     return tag == GI_TYPE_TAG_FILENAME || tag == GI_TYPE_TAG_UTF8;
     284                 :             : }
     285                 :             : 
     286                 :             : }  // namespace Gjs
        

Generated by: LCOV version 2.0-1