LCOV - code coverage report
Current view: top level - gi - value.cpp (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 72.1 % 659 475
Test Date: 2024-03-26 02:45:07 Functions: 100.0 % 13 13
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 68.7 % 611 420

             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: 2008 litl, LLC
       4                 :             : 
       5                 :             : #include <config.h>
       6                 :             : 
       7                 :             : #include <stddef.h>  // for NULL
       8                 :             : #include <stdint.h>
       9                 :             : 
      10                 :             : #include <sstream>
      11                 :             : #include <string>
      12                 :             : 
      13                 :             : #include <girepository.h>
      14                 :             : #include <glib-object.h>
      15                 :             : #include <glib.h>
      16                 :             : 
      17                 :             : #include <js/Array.h>
      18                 :             : #include <js/BigInt.h>
      19                 :             : #include <js/CharacterEncoding.h>
      20                 :             : #include <js/Conversions.h>
      21                 :             : #include <js/Exception.h>
      22                 :             : #include <js/GCVector.h>  // for RootedVector
      23                 :             : #include <js/Realm.h>
      24                 :             : #include <js/RootingAPI.h>
      25                 :             : #include <js/TypeDecls.h>
      26                 :             : #include <js/Utility.h>  // for UniqueChars
      27                 :             : #include <js/Value.h>
      28                 :             : #include <js/ValueArray.h>
      29                 :             : #include <js/experimental/TypedData.h>
      30                 :             : #include <jsapi.h>  // for InformalValueTypeName, JS_Get...
      31                 :             : #include <jsfriendapi.h>  // for JS_GetObjectFunction
      32                 :             : 
      33                 :             : #include "gi/arg-inl.h"
      34                 :             : #include "gi/arg.h"
      35                 :             : #include "gi/boxed.h"
      36                 :             : #include "gi/closure.h"
      37                 :             : #include "gi/foreign.h"
      38                 :             : #include "gi/fundamental.h"
      39                 :             : #include "gi/gerror.h"
      40                 :             : #include "gi/gtype.h"
      41                 :             : #include "gi/js-value-inl.h"
      42                 :             : #include "gi/object.h"
      43                 :             : #include "gi/param.h"
      44                 :             : #include "gi/union.h"
      45                 :             : #include "gi/value.h"
      46                 :             : #include "gi/wrapperutils.h"
      47                 :             : #include "gjs/byteArray.h"
      48                 :             : #include "gjs/context-private.h"
      49                 :             : #include "gjs/jsapi-util.h"
      50                 :             : #include "gjs/macros.h"
      51                 :             : #include "gjs/objectbox.h"
      52                 :             : #include "util/log.h"
      53                 :             : 
      54                 :             : GJS_JSAPI_RETURN_CONVENTION
      55                 :             : static bool gjs_value_from_g_value_internal(JSContext*, JS::MutableHandleValue,
      56                 :             :                                             const GValue*, bool no_copy = false,
      57                 :             :                                             bool is_introspected_signal = false,
      58                 :             :                                             GIArgInfo* = nullptr,
      59                 :             :                                             GITypeInfo* = nullptr);
      60                 :             : 
      61                 :             : GJS_JSAPI_RETURN_CONVENTION
      62                 :           7 : static bool gjs_arg_set_from_gvalue(JSContext* cx, GIArgument* arg,
      63                 :             :                                     const GValue* value) {
      64   [ -  -  -  -  :           7 :     switch (G_VALUE_TYPE(value)) {
          -  -  -  -  -  
          -  -  -  -  -  
                      + ]
      65                 :           0 :         case G_TYPE_CHAR:
      66                 :           0 :             gjs_arg_set(arg, g_value_get_schar(value));
      67                 :           0 :             return true;
      68                 :           0 :         case G_TYPE_UCHAR:
      69                 :           0 :             gjs_arg_set(arg, g_value_get_uchar(value));
      70                 :           0 :             return true;
      71                 :           0 :         case G_TYPE_BOOLEAN:
      72                 :           0 :             gjs_arg_set(arg, g_value_get_boolean(value));
      73                 :           0 :             return true;
      74                 :           0 :         case G_TYPE_INT:
      75                 :           0 :             gjs_arg_set(arg, g_value_get_int(value));
      76                 :           0 :             return true;
      77                 :           0 :         case G_TYPE_UINT:
      78                 :           0 :             gjs_arg_set(arg, g_value_get_uint(value));
      79                 :           0 :             return true;
      80                 :           0 :         case G_TYPE_LONG:
      81                 :           0 :             gjs_arg_set<long, GJS_TYPE_TAG_LONG>(  // NOLINT(runtime/int)
      82                 :             :                 arg, g_value_get_long(value));
      83                 :           0 :             return true;
      84                 :           0 :         case G_TYPE_ULONG:
      85                 :             :             gjs_arg_set<unsigned long,  // NOLINT(runtime/int)
      86                 :           0 :                         GJS_TYPE_TAG_LONG>(arg, g_value_get_ulong(value));
      87                 :           0 :             return true;
      88                 :           0 :         case G_TYPE_INT64:
      89                 :           0 :             gjs_arg_set(arg, int64_t{g_value_get_int64(value)});
      90                 :           0 :             return true;
      91                 :           0 :         case G_TYPE_UINT64:
      92                 :           0 :             gjs_arg_set(arg, uint64_t{g_value_get_uint64(value)});
      93                 :           0 :             return true;
      94                 :           0 :         case G_TYPE_FLOAT:
      95                 :           0 :             gjs_arg_set(arg, g_value_get_float(value));
      96                 :           0 :             return true;
      97                 :           0 :         case G_TYPE_DOUBLE:
      98                 :           0 :             gjs_arg_set(arg, g_value_get_double(value));
      99                 :           0 :             return true;
     100                 :           0 :         case G_TYPE_STRING:
     101                 :           0 :             gjs_arg_set(arg, g_value_get_string(value));
     102                 :           0 :             return true;
     103                 :           0 :         case G_TYPE_POINTER:
     104                 :           0 :             gjs_arg_set(arg, g_value_get_pointer(value));
     105                 :           0 :             return true;
     106                 :           0 :         case G_TYPE_VARIANT:
     107                 :           0 :             gjs_arg_set(arg, g_value_get_variant(value));
     108                 :           0 :             return true;
     109                 :           7 :         default: {
     110         [ +  - ]:           7 :             if (g_value_fits_pointer(value)) {
     111                 :           7 :                 gjs_arg_set(arg, g_value_peek_pointer(value));
     112                 :           7 :                 return true;
     113                 :             :             }
     114                 :             : 
     115                 :           0 :             GType gtype = G_VALUE_TYPE(value);
     116                 :             : 
     117   [ #  #  #  #  :           0 :             if (g_type_is_a(gtype, G_TYPE_FLAGS)) {
                   #  # ]
     118                 :           0 :                 gjs_arg_set(arg, g_value_get_flags(value));
     119                 :           0 :                 return true;
     120                 :             :             }
     121                 :             : 
     122   [ #  #  #  #  :           0 :             if (g_type_is_a(gtype, G_TYPE_ENUM)) {
                   #  # ]
     123                 :           0 :                 gjs_arg_set(arg, g_value_get_enum(value));
     124                 :           0 :                 return true;
     125                 :             :             }
     126                 :             : 
     127   [ #  #  #  #  :           0 :             if (g_type_is_a(gtype, G_TYPE_GTYPE)) {
                   #  # ]
     128                 :           0 :                 gjs_arg_set<GType, GI_TYPE_TAG_GTYPE>(arg,
     129                 :             :                                                       g_value_get_gtype(value));
     130                 :           0 :                 return true;
     131                 :             :             }
     132                 :             : 
     133   [ #  #  #  #  :           0 :             if (g_type_is_a(gtype, G_TYPE_PARAM)) {
                   #  # ]
     134                 :           0 :                 gjs_arg_set(arg, g_value_get_param(value));
     135                 :           0 :                 return true;
     136                 :             :             }
     137                 :             :         }
     138                 :             :     }
     139                 :             : 
     140                 :           0 :     gjs_throw(cx, "No know GArgument conversion for %s",
     141                 :             :               G_VALUE_TYPE_NAME(value));
     142                 :           0 :     return false;
     143                 :             : }
     144                 :             : 
     145                 :             : GJS_JSAPI_RETURN_CONVENTION
     146                 :           7 : static bool maybe_release_signal_value(JSContext* cx,
     147                 :             :                                        GjsAutoArgInfo const& arg_info,
     148                 :             :                                        GITypeInfo* type_info,
     149                 :             :                                        const GValue* gvalue,
     150                 :             :                                        GITransfer transfer) {
     151         [ -  + ]:           7 :     if (transfer == GI_TRANSFER_NOTHING)
     152                 :           0 :         return true;
     153                 :             : 
     154                 :             :     GIArgument arg;
     155         [ -  + ]:           7 :     if (!gjs_arg_set_from_gvalue(cx, &arg, gvalue))
     156                 :           0 :         return false;
     157                 :             : 
     158         [ -  + ]:           7 :     if (!gjs_gi_argument_release(cx, transfer, type_info,
     159                 :             :                                  GjsArgumentFlags::ARG_OUT, &arg)) {
     160                 :           0 :         gjs_throw(cx, "Cannot release argument %s value, we're gonna leak!",
     161                 :             :                   arg_info.name());
     162                 :           0 :         return false;
     163                 :             :     }
     164                 :             : 
     165                 :           7 :     return true;
     166                 :             : }
     167                 :             : 
     168                 :             : /*
     169                 :             :  * Gets signal introspection info about closure, or NULL if not found. Currently
     170                 :             :  * only works for signals on introspected GObjects, not signals on GJS-defined
     171                 :             :  * GObjects nor standalone closures. The return value must be unreffed.
     172                 :             :  */
     173                 :        8417 : [[nodiscard]] static GjsAutoSignalInfo get_signal_info_if_available(
     174                 :             :     GSignalQuery* signal_query) {
     175         [ +  + ]:        8417 :     if (!signal_query->itype)
     176                 :        8046 :         return nullptr;
     177                 :             : 
     178                 :             :     GjsAutoBaseInfo obj =
     179                 :         371 :         g_irepository_find_by_gtype(nullptr, signal_query->itype);
     180         [ +  + ]:         371 :     if (!obj)
     181                 :          66 :         return nullptr;
     182                 :             : 
     183                 :         305 :     GIInfoType info_type = obj.type();
     184         [ +  + ]:         305 :     if (info_type == GI_INFO_TYPE_OBJECT)
     185                 :         304 :         return g_object_info_find_signal(obj, signal_query->signal_name);
     186         [ +  - ]:           1 :     else if (info_type == GI_INFO_TYPE_INTERFACE)
     187                 :           1 :         return g_interface_info_find_signal(obj, signal_query->signal_name);
     188                 :             : 
     189                 :           0 :     return nullptr;
     190                 :         371 : }
     191                 :             : 
     192                 :             : /*
     193                 :             :  * Fill in value_p with a JS array, converted from a C array stored as a pointer
     194                 :             :  * in array_value, with its length stored in array_length_value.
     195                 :             :  */
     196                 :             : GJS_JSAPI_RETURN_CONVENTION
     197                 :           1 : static bool gjs_value_from_array_and_length_values(
     198                 :             :     JSContext* context, JS::MutableHandleValue value_p,
     199                 :             :     GITypeInfo* array_type_info, const GValue* array_value,
     200                 :             :     GIArgInfo* array_length_arg_info, GITypeInfo* array_length_type_info,
     201                 :             :     const GValue* array_length_value, bool no_copy,
     202                 :             :     bool is_introspected_signal) {
     203                 :           1 :     JS::RootedValue array_length(context);
     204                 :             : 
     205                 :           1 :     g_assert(G_VALUE_HOLDS_POINTER(array_value));
     206                 :           1 :     g_assert(G_VALUE_HOLDS_INT(array_length_value));
     207                 :             : 
     208         [ -  + ]:           1 :     if (!gjs_value_from_g_value_internal(
     209                 :             :             context, &array_length, array_length_value, no_copy, is_introspected_signal,
     210                 :             :             array_length_arg_info, array_length_type_info))
     211                 :           0 :         return false;
     212                 :             : 
     213                 :             :     GIArgument array_arg;
     214                 :           1 :     gjs_arg_set(&array_arg, g_value_get_pointer(array_value));
     215                 :             : 
     216         [ -  + ]:           1 :     return gjs_value_from_explicit_array(
     217                 :             :         context, value_p, array_type_info,
     218                 :             :         no_copy ? GI_TRANSFER_NOTHING : GI_TRANSFER_EVERYTHING, &array_arg,
     219                 :           1 :         array_length.toInt32());
     220                 :           1 : }
     221                 :             : 
     222                 :             : // FIXME(3v1n0): Move into closure.cpp one day...
     223                 :        8417 : void Gjs::Closure::marshal(GValue* return_value, unsigned n_param_values,
     224                 :             :                            const GValue* param_values, void* invocation_hint,
     225                 :             :                            void* marshal_data) {
     226                 :             :     JSContext *context;
     227                 :             :     unsigned i;
     228                 :        8417 :     GSignalQuery signal_query = { 0, };
     229                 :             : 
     230                 :             :     gjs_debug_marshal(GJS_DEBUG_GCLOSURE, "Marshal closure %p", this);
     231                 :             : 
     232         [ -  + ]:        8417 :     if (!is_valid()) {
     233                 :             :         /* We were destroyed; become a no-op */
     234                 :           8 :         return;
     235                 :             :     }
     236                 :             : 
     237                 :        8417 :     context = m_cx;
     238                 :        8417 :     GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
     239         [ -  + ]:        8417 :     if (G_UNLIKELY(gjs->sweeping())) {
     240                 :           0 :         GSignalInvocationHint *hint = (GSignalInvocationHint*) invocation_hint;
     241                 :           0 :         std::ostringstream message;
     242                 :             : 
     243                 :             :         message << "Attempting to call back into JSAPI during the sweeping "
     244                 :             :                    "phase of GC. This is most likely caused by not destroying "
     245                 :             :                    "a Clutter actor or Gtk+ widget with ::destroy signals "
     246                 :             :                    "connected, but can also be caused by using the destroy(), "
     247                 :             :                    "dispose(), or remove() vfuncs. Because it would crash the "
     248                 :             :                    "application, it has been blocked and the JS callback not "
     249                 :           0 :                    "invoked.";
     250         [ #  # ]:           0 :         if (hint) {
     251                 :             :             gpointer instance;
     252                 :           0 :             g_signal_query(hint->signal_id, &signal_query);
     253                 :             : 
     254                 :           0 :             instance = g_value_peek_pointer(&param_values[0]);
     255                 :             :             message << "\nThe offending signal was " << signal_query.signal_name
     256                 :           0 :                     << " on " << g_type_name(G_TYPE_FROM_INSTANCE(instance))
     257                 :           0 :                     << " " << instance << ".";
     258                 :             :         }
     259                 :           0 :         message << "\n" << gjs_dumpstack_string();
     260                 :           0 :         g_critical("%s", message.str().c_str());
     261                 :           0 :         return;
     262                 :           0 :     }
     263                 :             : 
     264                 :        8417 :     JSAutoRealm ar(context, callable());
     265                 :             : 
     266         [ +  + ]:        8417 :     if (marshal_data) {
     267                 :             :         /* we are used for a signal handler */
     268                 :             :         guint signal_id;
     269                 :             : 
     270                 :         371 :         signal_id = GPOINTER_TO_UINT(marshal_data);
     271                 :             : 
     272                 :         371 :         g_signal_query(signal_id, &signal_query);
     273                 :             : 
     274         [ -  + ]:         371 :         if (!signal_query.signal_id) {
     275                 :           0 :             gjs_debug(GJS_DEBUG_GCLOSURE,
     276                 :             :                       "Signal handler being called on invalid signal");
     277                 :           0 :             return;
     278                 :             :         }
     279                 :             : 
     280         [ -  + ]:         371 :         if (signal_query.n_params + 1 != n_param_values) {
     281                 :           0 :             gjs_debug(GJS_DEBUG_GCLOSURE,
     282                 :             :                       "Signal handler being called with wrong number of parameters");
     283                 :           0 :             return;
     284                 :             :         }
     285                 :             :     }
     286                 :             : 
     287                 :             :     /* Check if any parameters, such as array lengths, need to be eliminated
     288                 :             :      * before we invoke the closure.
     289                 :             :      */
     290                 :             :     struct ArgumentDetails {
     291                 :             :         int array_len_index_for = -1;
     292                 :             :         bool skip = false;
     293                 :             :         GITypeInfo type_info;
     294                 :             :         GjsAutoArgInfo arg_info;
     295                 :             :     };
     296                 :        8417 :     std::vector<ArgumentDetails> args_details(n_param_values);
     297                 :        8417 :     bool needs_cleanup = false;
     298                 :             : 
     299                 :        8417 :     GjsAutoSignalInfo signal_info = get_signal_info_if_available(&signal_query);
     300         [ +  + ]:        8417 :     if (signal_info) {
     301                 :             :         /* Start at argument 1, skip the instance parameter */
     302         [ +  + ]:         693 :         for (i = 1; i < n_param_values; ++i) {
     303                 :             :             int array_len_pos;
     304                 :             : 
     305                 :         388 :             ArgumentDetails& arg_details = args_details[i];
     306                 :         388 :             arg_details.arg_info = g_callable_info_get_arg(signal_info, i - 1);
     307                 :         388 :             g_arg_info_load_type(arg_details.arg_info, &arg_details.type_info);
     308                 :             : 
     309                 :             :             array_len_pos =
     310                 :         388 :                 g_type_info_get_array_length(&arg_details.type_info);
     311         [ +  + ]:         388 :             if (array_len_pos != -1) {
     312                 :           1 :                 args_details[array_len_pos + 1].skip = true;
     313                 :           1 :                 arg_details.array_len_index_for = array_len_pos + 1;
     314                 :             :             }
     315                 :             : 
     316   [ +  -  +  +  :         776 :             if (!needs_cleanup &&
                   +  + ]
     317                 :         388 :                 g_arg_info_get_ownership_transfer(arg_details.arg_info) !=
     318                 :             :                     GI_TRANSFER_NOTHING)
     319                 :           7 :                 needs_cleanup = true;
     320                 :             :         }
     321                 :             :     }
     322                 :             : 
     323                 :        8417 :     JS::RootedValueVector argv(context);
     324                 :             :     /* May end up being less */
     325         [ -  + ]:        8417 :     if (!argv.reserve(n_param_values))
     326                 :           0 :         g_error("Unable to reserve space");
     327                 :        8417 :     JS::RootedValue argv_to_append(context);
     328                 :        8417 :     bool is_introspected_signal = !!signal_info;
     329         [ +  + ]:        9288 :     for (i = 0; i < n_param_values; ++i) {
     330                 :         871 :         const GValue* gval = &param_values[i];
     331                 :         871 :         ArgumentDetails& arg_details = args_details[i];
     332                 :             :         bool no_copy;
     333                 :             :         bool res;
     334                 :             : 
     335         [ +  + ]:         871 :         if (arg_details.skip)
     336                 :           1 :             continue;
     337                 :             : 
     338                 :         870 :         no_copy = false;
     339                 :             : 
     340   [ +  +  +  + ]:         870 :         if (i >= 1 && signal_query.signal_id) {
     341                 :         436 :             no_copy = (signal_query.param_types[i - 1] & G_SIGNAL_TYPE_STATIC_SCOPE) != 0;
     342                 :             :         }
     343                 :             : 
     344         [ +  + ]:         870 :         if (arg_details.array_len_index_for != -1) {
     345                 :           1 :             const GValue* array_len_gval =
     346                 :           1 :                 &param_values[arg_details.array_len_index_for];
     347                 :             :             ArgumentDetails& array_len_details =
     348                 :           1 :                 args_details[arg_details.array_len_index_for];
     349                 :           1 :             res = gjs_value_from_array_and_length_values(
     350                 :             :                 context, &argv_to_append, &arg_details.type_info, gval,
     351                 :             :                 array_len_details.arg_info, &array_len_details.type_info,
     352                 :             :                 array_len_gval, no_copy, is_introspected_signal);
     353                 :             :         } else {
     354                 :         869 :             res = gjs_value_from_g_value_internal(
     355                 :             :                 context, &argv_to_append, gval, no_copy, is_introspected_signal,
     356                 :             :                 arg_details.arg_info, &arg_details.type_info);
     357                 :             :         }
     358                 :             : 
     359         [ -  + ]:         870 :         if (!res) {
     360                 :           0 :             gjs_debug(GJS_DEBUG_GCLOSURE,
     361                 :             :                       "Unable to convert arg %d in order to invoke closure",
     362                 :             :                       i);
     363                 :           0 :             gjs_log_exception(context);
     364                 :           0 :             return;
     365                 :             :         }
     366                 :             : 
     367                 :         870 :         argv.infallibleAppend(argv_to_append);
     368                 :             :     }
     369                 :             : 
     370                 :        8417 :     JS::RootedValue rval(context);
     371                 :             : 
     372         [ +  + ]:        8417 :     if (!invoke(nullptr, argv, &rval)) {
     373         [ -  + ]:           1 :         if (JS_IsExceptionPending(context)) {
     374                 :           0 :             gjs_log_exception_uncaught(context);
     375                 :             :         } else {
     376                 :             :             // "Uncatchable" exception thrown, we have to exit. This
     377                 :             :             // matches the closure exit handling in function.cpp
     378                 :             :             uint8_t code;
     379         [ +  - ]:           1 :             if (gjs->should_exit(&code))
     380                 :           1 :                 gjs->exit_immediately(code);
     381                 :             : 
     382                 :             :             // Some other uncatchable exception, e.g. out of memory
     383                 :           0 :             JSFunction* fn = JS_GetObjectFunction(callable());
     384                 :             :             std::string descr =
     385                 :           0 :                 fn ? "function " + gjs_debug_string(JS_GetFunctionDisplayId(fn))
     386   [ #  #  #  #  :           0 :                    : "callable object " + gjs_debug_object(callable());
                   #  # ]
     387                 :           0 :             g_error("Call to %s terminated with uncatchable exception",
     388                 :             :                     descr.c_str());
     389                 :             :         }
     390                 :             :     }
     391                 :             : 
     392         [ +  + ]:        8416 :     if (needs_cleanup) {
     393         [ +  + ]:          21 :         for (i = 0; i < n_param_values; ++i) {
     394                 :          14 :             ArgumentDetails& arg_details = args_details[i];
     395         [ +  + ]:          14 :             if (!arg_details.arg_info)
     396                 :           7 :                 continue;
     397                 :             : 
     398                 :             :             GITransfer transfer =
     399                 :           7 :                 g_arg_info_get_ownership_transfer(arg_details.arg_info);
     400                 :             : 
     401         [ -  + ]:           7 :             if (transfer == GI_TRANSFER_NOTHING)
     402                 :           0 :                 continue;
     403                 :             : 
     404         [ -  + ]:           7 :             if (!maybe_release_signal_value(context, arg_details.arg_info,
     405                 :             :                                             &arg_details.type_info,
     406                 :           7 :                                             &param_values[i], transfer)) {
     407                 :           0 :                 gjs_log_exception(context);
     408                 :           0 :                 return;
     409                 :             :             }
     410                 :             :         }
     411                 :             :     }
     412                 :             : 
     413                 :             :     // null return_value means the closure wasn't expected to return a value.
     414                 :             :     // Discard the JS function's return value in that case.
     415         [ +  + ]:        8416 :     if (return_value != NULL) {
     416         [ +  + ]:        8022 :         if (rval.isUndefined()) {
     417                 :             :             // Either an exception was thrown and logged, or the JS function
     418                 :             :             // returned undefined. Leave the GValue uninitialized.
     419                 :             :             // FIXME: not sure what happens on the other side with an
     420                 :             :             // uninitialized GValue!
     421                 :           4 :             return;
     422                 :             :         }
     423                 :             : 
     424         [ +  + ]:        8018 :         if (!gjs_value_to_g_value(context, rval, return_value)) {
     425                 :           4 :             gjs_debug(GJS_DEBUG_GCLOSURE,
     426                 :             :                       "Unable to convert return value when invoking closure");
     427                 :           4 :             gjs_log_exception(context);
     428                 :           4 :             return;
     429                 :             :         }
     430                 :             :     }
     431   [ +  +  +  +  :        8456 : }
          +  +  +  +  +  
                +  +  + ]
     432                 :             : 
     433                 :             : GJS_JSAPI_RETURN_CONVENTION
     434                 :          76 : static bool gjs_value_guess_g_type(JSContext* context, JS::Value value,
     435                 :             :                                    GType* gtype_out) {
     436                 :          76 :     g_assert(gtype_out && "Invalid return location");
     437                 :             : 
     438         [ +  + ]:          76 :     if (value.isNull()) {
     439                 :           1 :         *gtype_out = G_TYPE_POINTER;
     440                 :           1 :         return true;
     441                 :             :     }
     442         [ +  + ]:          75 :     if (value.isString()) {
     443                 :           6 :         *gtype_out = G_TYPE_STRING;
     444                 :           6 :         return true;
     445                 :             :     }
     446         [ +  + ]:          69 :     if (value.isInt32()) {
     447                 :          22 :         *gtype_out = G_TYPE_INT;
     448                 :          22 :         return true;
     449                 :             :     }
     450         [ +  + ]:          47 :     if (value.isDouble()) {
     451                 :           5 :         *gtype_out = G_TYPE_DOUBLE;
     452                 :           5 :         return true;
     453                 :             :     }
     454         [ +  + ]:          42 :     if (value.isBoolean()) {
     455                 :           2 :         *gtype_out = G_TYPE_BOOLEAN;
     456                 :           2 :         return true;
     457                 :             :     }
     458         [ +  + ]:          40 :     if (value.isBigInt()) {
     459                 :             :         // Assume that if the value is negative or within the int64_t limit,
     460                 :             :         // then we're handling a signed integer, otherwise unsigned.
     461                 :             :         int64_t ignored;
     462   [ +  +  +  +  :           5 :         if (JS::BigIntIsNegative(value.toBigInt()) ||
                   +  + ]
     463                 :           2 :             JS::BigIntFits(value.toBigInt(), &ignored))
     464                 :           2 :             *gtype_out = G_TYPE_INT64;
     465                 :             :         else
     466                 :           1 :             *gtype_out = G_TYPE_UINT64;
     467                 :           3 :         return true;
     468                 :             :     }
     469         [ +  - ]:          37 :     if (value.isObject()) {
     470                 :          37 :         JS::RootedObject obj(context, &value.toObject());
     471                 :          37 :         return gjs_gtype_get_actual_gtype(context, obj, gtype_out);
     472                 :          37 :     }
     473                 :             : 
     474                 :           0 :     *gtype_out = G_TYPE_INVALID;
     475                 :           0 :     return true;
     476                 :             : }
     477                 :             : 
     478                 :          27 : static bool throw_expect_type(JSContext* cx, JS::HandleValue value,
     479                 :             :                               const char* expected_type, GType gtype = 0,
     480                 :             :                               bool out_of_range = false) {
     481                 :          27 :     JS::UniqueChars val_str;
     482   [ +  +  +  - ]:          27 :     out_of_range = (out_of_range && value.isNumeric());
     483                 :             : 
     484         [ +  + ]:          27 :     if (out_of_range) {
     485                 :           7 :         JS::RootedString str(cx, JS::ToString(cx, value));
     486         [ +  - ]:           7 :         if (str)
     487                 :           7 :             val_str = JS_EncodeStringToUTF8(cx, str);
     488                 :           7 :     }
     489                 :             : 
     490   [ +  +  +  +  :          52 :     gjs_throw(cx, "Wrong type %s; %s%s%s expected%s%s",
             +  +  +  + ]
     491                 :             :               JS::InformalValueTypeName(value), expected_type, gtype ? " " : "",
     492                 :          18 :               gtype ? g_type_name(gtype) : "",
     493                 :             :               out_of_range ? ". But it's out of range: " : "",
     494                 :           7 :               out_of_range ? val_str.get() : "");
     495                 :          54 :     return false;  /* for convenience */
     496                 :          27 : }
     497                 :             : 
     498                 :             : GJS_JSAPI_RETURN_CONVENTION
     499                 :             : static bool
     500                 :        8966 : gjs_value_to_g_value_internal(JSContext      *context,
     501                 :             :                               JS::HandleValue value,
     502                 :             :                               GValue         *gvalue,
     503                 :             :                               bool            no_copy)
     504                 :             : {
     505                 :             :     GType gtype;
     506                 :        8966 :     bool out_of_range = false;
     507                 :             : 
     508                 :        8966 :     gtype = G_VALUE_TYPE(gvalue);
     509                 :             : 
     510         [ +  + ]:        8966 :     if (value.isObject()) {
     511                 :         230 :         JS::RootedObject obj(context, &value.toObject());
     512                 :             :         GType boxed_gtype;
     513                 :             : 
     514         [ -  + ]:         230 :         if (!gjs_gtype_get_actual_gtype(context, obj, &boxed_gtype))
     515                 :           0 :             return false;
     516                 :             : 
     517                 :             :         // Don't unbox GValue if the GValue's gtype is GObject.Value
     518   [ +  +  -  +  :         230 :         if (g_type_is_a(boxed_gtype, G_TYPE_VALUE) && gtype != G_TYPE_VALUE) {
             +  +  +  + ]
     519         [ -  + ]:           4 :             if (no_copy) {
     520                 :           0 :                 gjs_throw(
     521                 :             :                     context,
     522                 :             :                     "Cannot convert GObject.Value object without copying.");
     523                 :           0 :                 return false;
     524                 :             :             }
     525                 :             : 
     526                 :           4 :             GValue* source = BoxedBase::to_c_ptr<GValue>(context, obj);
     527                 :             :             // Only initialize the value if it doesn't have a type
     528                 :             :             // and our source GValue has been initialized
     529                 :           4 :             GType source_gtype = G_VALUE_TYPE(source);
     530         [ +  - ]:           4 :             if (gtype == 0) {
     531         [ +  + ]:           4 :                 if (source_gtype == 0) {
     532                 :           1 :                     gjs_throw(context,
     533                 :             :                               "GObject.Value is not initialized with a type");
     534                 :           1 :                     return false;
     535                 :             :                 }
     536                 :           3 :                 g_value_init(gvalue, source_gtype);
     537                 :             :             }
     538                 :             : 
     539                 :           3 :             GType dest_gtype = G_VALUE_TYPE(gvalue);
     540         [ -  + ]:           3 :             if (!g_value_type_compatible(source_gtype, dest_gtype)) {
     541                 :           0 :                 gjs_throw(context, "GObject.Value expected GType %s, found %s",
     542                 :             :                           g_type_name(dest_gtype), g_type_name(source_gtype));
     543                 :           0 :                 return false;
     544                 :             :             }
     545                 :             : 
     546                 :           3 :             g_value_copy(source, gvalue);
     547                 :           3 :             return true;
     548                 :             :         }
     549         [ +  + ]:         230 :     }
     550                 :             : 
     551         [ +  + ]:        8962 :     if (gtype == 0) {
     552         [ -  + ]:          66 :         if (!gjs_value_guess_g_type(context, value, &gtype))
     553                 :           0 :             return false;
     554                 :             : 
     555         [ -  + ]:          66 :         if (gtype == G_TYPE_INVALID) {
     556                 :           0 :             gjs_throw(context, "Could not guess unspecified GValue type");
     557                 :           0 :             return false;
     558                 :             :         }
     559                 :             : 
     560                 :             :         gjs_debug_marshal(GJS_DEBUG_GCLOSURE,
     561                 :             :                           "Guessed GValue type %s from JS Value",
     562                 :             :                           g_type_name(gtype));
     563                 :             : 
     564                 :          66 :         g_value_init(gvalue, gtype);
     565                 :             :     }
     566                 :             : 
     567                 :             :     gjs_debug_marshal(GJS_DEBUG_GCLOSURE,
     568                 :             :                       "Converting JS::Value to gtype %s",
     569                 :             :                       g_type_name(gtype));
     570                 :             : 
     571                 :             : 
     572         [ +  + ]:        8962 :     if (gtype == G_TYPE_STRING) {
     573                 :             :         /* Don't use ValueToString since we don't want to just toString()
     574                 :             :          * everything automatically
     575                 :             :          */
     576         [ +  + ]:         109 :         if (value.isNull()) {
     577                 :           3 :             g_value_set_string(gvalue, NULL);
     578         [ +  + ]:         106 :         } else if (value.isString()) {
     579                 :         104 :             JS::RootedString str(context, value.toString());
     580                 :         104 :             JS::UniqueChars utf8_string(JS_EncodeStringToUTF8(context, str));
     581         [ -  + ]:         104 :             if (!utf8_string)
     582                 :           0 :                 return false;
     583                 :             : 
     584                 :         104 :             g_value_set_string(gvalue, utf8_string.get());
     585   [ +  -  +  - ]:         104 :         } else {
     586                 :           2 :             return throw_expect_type(context, value, "string");
     587                 :             :         }
     588         [ +  + ]:        8853 :     } else if (gtype == G_TYPE_CHAR) {
     589                 :             :         int32_t i;
     590                 :          16 :         if (Gjs::js_value_to_c_checked<signed char>(context, value, &i,
     591   [ +  -  +  - ]:          32 :                                                     &out_of_range) &&
     592         [ +  - ]:          16 :             !out_of_range) {
     593                 :          16 :             g_value_set_schar(gvalue, static_cast<signed char>(i));
     594                 :             :         } else {
     595                 :           0 :             return throw_expect_type(context, value, "char", 0, out_of_range);
     596                 :             :         }
     597         [ +  + ]:        8837 :     } else if (gtype == G_TYPE_UCHAR) {
     598                 :             :         uint32_t i;
     599                 :          16 :         if (Gjs::js_value_to_c_checked<unsigned char>(context, value, &i,
     600   [ +  -  +  - ]:          32 :                                                       &out_of_range) &&
     601         [ +  - ]:          16 :             !out_of_range) {
     602                 :          16 :             g_value_set_uchar(gvalue, (unsigned char)i);
     603                 :             :         } else {
     604                 :           0 :             return throw_expect_type(context, value, "unsigned char", 0,
     605                 :           0 :                                      out_of_range);
     606                 :             :         }
     607         [ +  + ]:        8821 :     } else if (gtype == G_TYPE_INT) {
     608                 :             :         gint32 i;
     609         [ +  - ]:         178 :         if (Gjs::js_value_to_c(context, value, &i)) {
     610                 :         178 :             g_value_set_int(gvalue, i);
     611                 :             :         } else {
     612                 :           0 :             return throw_expect_type(context, value, "integer");
     613                 :             :         }
     614         [ +  + ]:        8643 :     } else if (gtype == G_TYPE_INT64) {
     615                 :             :         int64_t i;
     616                 :          35 :         if (Gjs::js_value_to_c_checked<int64_t>(context, value, &i,
     617   [ +  -  +  + ]:          70 :                                                 &out_of_range) &&
     618         [ +  + ]:          35 :             !out_of_range) {
     619                 :          30 :             g_value_set_int64(gvalue, i);
     620                 :             :         } else {
     621                 :           5 :             return throw_expect_type(context, value, "64-bit integer", 0,
     622                 :           5 :                                      out_of_range);
     623                 :             :         }
     624         [ +  + ]:        8608 :     } else if (gtype == G_TYPE_DOUBLE) {
     625                 :             :         gdouble d;
     626         [ +  - ]:          84 :         if (Gjs::js_value_to_c(context, value, &d)) {
     627                 :          84 :             g_value_set_double(gvalue, d);
     628                 :             :         } else {
     629                 :           0 :             return throw_expect_type(context, value, "double");
     630                 :             :         }
     631         [ +  + ]:        8524 :     } else if (gtype == G_TYPE_FLOAT) {
     632                 :             :         gdouble d;
     633                 :          79 :         if (Gjs::js_value_to_c_checked<float>(context, value, &d,
     634   [ +  -  +  - ]:         158 :                                               &out_of_range) &&
     635         [ +  - ]:          79 :             !out_of_range) {
     636                 :          79 :             g_value_set_float(gvalue, d);
     637                 :             :         } else {
     638                 :           0 :             return throw_expect_type(context, value, "float", 0, out_of_range);
     639                 :             :         }
     640         [ +  + ]:        8445 :     } else if (gtype == G_TYPE_UINT) {
     641                 :             :         guint32 i;
     642         [ +  - ]:          17 :         if (Gjs::js_value_to_c(context, value, &i)) {
     643                 :          17 :             g_value_set_uint(gvalue, i);
     644                 :             :         } else {
     645                 :           0 :             return throw_expect_type(context, value, "unsigned integer");
     646                 :             :         }
     647         [ +  + ]:        8428 :     } else if (gtype == G_TYPE_UINT64) {
     648                 :             :         uint64_t i;
     649                 :          22 :         if (Gjs::js_value_to_c_checked<uint64_t>(context, value, &i,
     650   [ +  -  +  + ]:          44 :                                                  &out_of_range) &&
     651         [ +  + ]:          22 :             !out_of_range) {
     652                 :          20 :             g_value_set_uint64(gvalue, i);
     653                 :             :         } else {
     654                 :           2 :             return throw_expect_type(context, value, "unsigned 64-bit integer",
     655                 :           2 :                                      0, out_of_range);
     656                 :             :         }
     657         [ +  + ]:        8406 :     } else if (gtype == G_TYPE_BOOLEAN) {
     658                 :             :         /* JS::ToBoolean() can't fail */
     659                 :        8017 :         g_value_set_boolean(gvalue, JS::ToBoolean(value));
     660   [ +  +  +  +  :         730 :     } else if (g_type_is_a(gtype, G_TYPE_OBJECT) ||
                   +  + ]
     661   [ +  -  -  + ]:         341 :                g_type_is_a(gtype, G_TYPE_INTERFACE)) {
     662                 :             :         GObject *gobj;
     663                 :             : 
     664                 :          48 :         gobj = NULL;
     665         [ +  + ]:          48 :         if (value.isNull()) {
     666                 :             :             /* nothing to do */
     667         [ +  - ]:          45 :         } else if (value.isObject()) {
     668                 :          45 :             JS::RootedObject obj(context, &value.toObject());
     669         [ +  - ]:          90 :             if (!ObjectBase::typecheck(context, obj, nullptr, gtype) ||
     670   [ -  +  -  + ]:          90 :                 !ObjectBase::to_c_ptr(context, obj, &gobj))
     671                 :           0 :                 return false;
     672         [ -  + ]:          45 :             if (!gobj)
     673                 :           0 :                 return true;  // treat disposed object as if value.isNull()
     674         [ +  - ]:          45 :         } else {
     675                 :           0 :             return throw_expect_type(context, value, "object", gtype);
     676                 :             :         }
     677                 :             : 
     678                 :          48 :         g_value_set_object(gvalue, gobj);
     679         [ +  + ]:         341 :     } else if (gtype == G_TYPE_STRV) {
     680         [ -  + ]:           9 :         if (value.isNull())
     681                 :           0 :             return true;
     682                 :             : 
     683                 :             :         bool is_array;
     684         [ -  + ]:           9 :         if (!JS::IsArrayObject(context, value, &is_array))
     685                 :           0 :             return false;
     686         [ -  + ]:           9 :         if (!is_array)
     687                 :           0 :             return throw_expect_type(context, value, "strv");
     688                 :             : 
     689                 :           9 :         JS::RootedObject array_obj(context, &value.toObject());
     690                 :             :         uint32_t length;
     691         [ -  + ]:           9 :         if (!JS::GetArrayLength(context, array_obj, &length))
     692                 :           0 :             return throw_expect_type(context, value, "strv");
     693                 :             : 
     694                 :             :         void* result;
     695         [ -  + ]:           9 :         if (!gjs_array_to_strv(context, value, length, &result))
     696                 :           0 :             return false;
     697                 :             : 
     698                 :           9 :         g_value_take_boxed(gvalue, static_cast<char**>(result));
     699   [ +  -  +  -  :         341 :     } else if (g_type_is_a(gtype, G_TYPE_BOXED)) {
             +  +  +  + ]
     700                 :             :         void *gboxed;
     701                 :             : 
     702                 :         149 :         gboxed = NULL;
     703         [ +  + ]:         149 :         if (value.isNull())
     704                 :          23 :             return true;
     705                 :             : 
     706                 :             :         /* special case GValue */
     707         [ +  + ]:         126 :         if (gtype == G_TYPE_VALUE) {
     708                 :             :             /* explicitly handle values that are already GValues
     709                 :             :                to avoid infinite recursion */
     710         [ +  + ]:          18 :             if (value.isObject()) {
     711                 :          10 :                 JS::RootedObject obj(context, &value.toObject());
     712                 :             :                 GType guessed_gtype;
     713                 :             : 
     714         [ -  + ]:          10 :                 if (!gjs_value_guess_g_type(context, value, &guessed_gtype))
     715                 :           0 :                     return false;
     716                 :             : 
     717         [ +  - ]:          10 :                 if (guessed_gtype == G_TYPE_VALUE) {
     718                 :          10 :                     gboxed = BoxedBase::to_c_ptr<GValue>(context, obj);
     719                 :          10 :                     g_value_set_boxed(gvalue, gboxed);
     720                 :          10 :                     return true;
     721                 :             :                 }
     722         [ -  + ]:          10 :             }
     723                 :             : 
     724                 :           8 :             Gjs::AutoGValue nested_gvalue;
     725         [ -  + ]:           8 :             if (!gjs_value_to_g_value(context, value, &nested_gvalue))
     726                 :           0 :                 return false;
     727                 :             : 
     728                 :           8 :             g_value_set_boxed(gvalue, &nested_gvalue);
     729                 :           8 :             return true;
     730                 :           8 :         }
     731                 :             : 
     732         [ +  + ]:         108 :         if (value.isObject()) {
     733                 :          90 :             JS::RootedObject obj(context, &value.toObject());
     734                 :             : 
     735         [ +  + ]:          90 :             if (gtype == ObjectBox::gtype()) {
     736                 :          35 :                 g_value_set_boxed(gvalue, ObjectBox::boxed(context, obj).get());
     737                 :          35 :                 return true;
     738         [ +  + ]:          55 :             } else if (gtype == G_TYPE_ERROR) {
     739                 :             :                 /* special case GError */
     740                 :           1 :                 gboxed = ErrorBase::to_c_ptr(context, obj);
     741         [ -  + ]:           1 :                 if (!gboxed)
     742                 :           0 :                     return false;
     743         [ +  + ]:          54 :             } else if (gtype == G_TYPE_BYTE_ARRAY) {
     744                 :             :                 /* special case GByteArray */
     745                 :           9 :                 JS::RootedObject obj(context, &value.toObject());
     746         [ +  - ]:           9 :                 if (JS_IsUint8Array(obj)) {
     747                 :           9 :                     g_value_take_boxed(gvalue,
     748                 :           9 :                                        gjs_byte_array_get_byte_array(obj));
     749                 :           9 :                     return true;
     750                 :             :                 }
     751   [ -  +  -  + ]:          54 :             } else if (gtype == G_TYPE_ARRAY) {
     752                 :           0 :                 gjs_throw(context, "Converting %s to GArray is not supported",
     753                 :             :                           JS::InformalValueTypeName(value));
     754                 :           0 :                 return false;
     755         [ -  + ]:          45 :             } else if (gtype == G_TYPE_PTR_ARRAY) {
     756                 :           0 :                 gjs_throw(context, "Converting %s to GArray is not supported",
     757                 :             :                           JS::InformalValueTypeName(value));
     758                 :           0 :                 return false;
     759         [ -  + ]:          45 :             } else if (gtype == G_TYPE_HASH_TABLE) {
     760                 :           0 :                 gjs_throw(context,
     761                 :             :                           "Converting %s to GHashTable is not supported",
     762                 :             :                           JS::InformalValueTypeName(value));
     763                 :           0 :                 return false;
     764                 :             :             } else {
     765                 :             :                 GjsAutoBaseInfo registered =
     766                 :          45 :                     g_irepository_find_by_gtype(nullptr, gtype);
     767                 :             : 
     768                 :             :                 /* We don't necessarily have the typelib loaded when
     769                 :             :                    we first see the structure... */
     770         [ +  - ]:          45 :                 if (registered) {
     771                 :          45 :                     GIInfoType info_type = registered.type();
     772                 :             : 
     773   [ +  +  +  +  :          89 :                     if (info_type == GI_INFO_TYPE_STRUCT &&
                   +  + ]
     774                 :          44 :                         g_struct_info_is_foreign(
     775                 :             :                             registered.as<GIStructInfo>())) {
     776                 :             :                         GIArgument arg;
     777                 :             : 
     778         [ -  + ]:           2 :                         if (!gjs_struct_foreign_convert_to_gi_argument(
     779                 :             :                                 context, value, registered, nullptr,
     780                 :             :                                 GJS_ARGUMENT_ARGUMENT, GI_TRANSFER_NOTHING,
     781                 :             :                                 GjsArgumentFlags::MAY_BE_NULL, &arg))
     782                 :           0 :                             return false;
     783                 :             : 
     784                 :           2 :                         gboxed = gjs_arg_get<void*>(&arg);
     785                 :             :                     }
     786                 :             :                 }
     787                 :             : 
     788                 :             :                 /* First try a union, if that fails,
     789                 :             :                    assume a boxed struct. Distinguishing
     790                 :             :                    which one is expected would require checking
     791                 :             :                    the associated GIBaseInfo, which is not necessary
     792                 :             :                    possible, if e.g. we see the GType without
     793                 :             :                    loading the typelib.
     794                 :             :                 */
     795         [ +  + ]:          45 :                 if (!gboxed) {
     796         [ +  + ]:          43 :                     if (UnionBase::typecheck(context, obj, nullptr, gtype,
     797                 :             :                                              GjsTypecheckNoThrow())) {
     798                 :           1 :                         gboxed = UnionBase::to_c_ptr(context, obj);
     799                 :             :                     } else {
     800         [ -  + ]:          42 :                         if (!BoxedBase::typecheck(context, obj, nullptr, gtype))
     801                 :           0 :                             return false;
     802                 :             : 
     803                 :          42 :                         gboxed = BoxedBase::to_c_ptr(context, obj);
     804                 :             :                     }
     805         [ -  + ]:          43 :                     if (!gboxed)
     806                 :           0 :                         return false;
     807                 :             :                 }
     808         [ +  - ]:          45 :             }
     809         [ +  + ]:          90 :         } else {
     810                 :          18 :             return throw_expect_type(context, value, "boxed type", gtype);
     811                 :             :         }
     812                 :             : 
     813         [ +  + ]:          46 :         if (no_copy)
     814                 :           2 :             g_value_set_static_boxed(gvalue, gboxed);
     815                 :             :         else
     816                 :          44 :             g_value_set_boxed(gvalue, gboxed);
     817         [ +  + ]:         183 :     } else if (gtype == G_TYPE_VARIANT) {
     818                 :          45 :         GVariant *variant = NULL;
     819                 :             : 
     820         [ +  + ]:          45 :         if (value.isNull()) {
     821                 :             :             /* nothing to do */
     822         [ +  - ]:          43 :         } else if (value.isObject()) {
     823                 :          43 :             JS::RootedObject obj(context, &value.toObject());
     824                 :             : 
     825         [ -  + ]:          43 :             if (!BoxedBase::typecheck(context, obj, nullptr, G_TYPE_VARIANT))
     826                 :           0 :                 return false;
     827                 :             : 
     828                 :          43 :             variant = BoxedBase::to_c_ptr<GVariant>(context, obj);
     829         [ -  + ]:          43 :             if (!variant)
     830                 :           0 :                 return false;
     831         [ +  - ]:          43 :         } else {
     832                 :           0 :             return throw_expect_type(context, value, "boxed type", gtype);
     833                 :             :         }
     834                 :             : 
     835                 :          45 :         g_value_set_variant (gvalue, variant);
     836   [ +  -  +  +  :         138 :     } else if (g_type_is_a(gtype, G_TYPE_ENUM)) {
                   +  + ]
     837                 :             :         int64_t value_int64;
     838                 :             : 
     839         [ +  - ]:          59 :         if (Gjs::js_value_to_c(context, value, &value_int64)) {
     840                 :             :             GEnumValue *v;
     841                 :          59 :             GjsAutoTypeClass<GEnumClass> enum_class(gtype);
     842                 :             : 
     843                 :             :             /* See arg.c:_gjs_enum_to_int() */
     844                 :          59 :             v = g_enum_get_value(enum_class, (int)value_int64);
     845         [ -  + ]:          59 :             if (v == NULL) {
     846                 :           0 :                 gjs_throw(context,
     847                 :             :                           "%d is not a valid value for enumeration %s",
     848                 :             :                           value.toInt32(), g_type_name(gtype));
     849                 :           0 :                 return false;
     850                 :             :             }
     851                 :             : 
     852                 :          59 :             g_value_set_enum(gvalue, v->value);
     853         [ +  - ]:          59 :         } else {
     854                 :           0 :             return throw_expect_type(context, value, "enum", gtype);
     855                 :             :         }
     856   [ +  -  +  +  :          79 :     } else if (g_type_is_a(gtype, G_TYPE_FLAGS)) {
                   +  + ]
     857                 :             :         int64_t value_int64;
     858                 :             : 
     859         [ +  - ]:          13 :         if (Gjs::js_value_to_c(context, value, &value_int64)) {
     860         [ -  + ]:          13 :             if (!_gjs_flags_value_is_valid(context, gtype, value_int64))
     861                 :           0 :                 return false;
     862                 :             : 
     863                 :             :             /* See arg.c:_gjs_enum_to_int() */
     864                 :          13 :             g_value_set_flags(gvalue, (int)value_int64);
     865                 :             :         } else {
     866                 :           0 :             return throw_expect_type(context, value, "flags", gtype);
     867                 :             :         }
     868   [ +  +  -  +  :          66 :     } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
                   +  + ]
     869                 :             :         void *gparam;
     870                 :             : 
     871                 :           2 :         gparam = NULL;
     872         [ +  - ]:           2 :         if (value.isNull()) {
     873                 :             :             /* nothing to do */
     874         [ +  - ]:           2 :         } else if (value.isObject()) {
     875                 :           2 :             JS::RootedObject obj(context, &value.toObject());
     876                 :             : 
     877         [ -  + ]:           2 :             if (!gjs_typecheck_param(context, obj, gtype, true))
     878                 :           0 :                 return false;
     879                 :             : 
     880                 :           2 :             gparam = gjs_g_param_from_param(context, obj);
     881         [ +  - ]:           2 :         } else {
     882                 :           0 :             return throw_expect_type(context, value, "param type", gtype);
     883                 :             :         }
     884                 :             : 
     885                 :           2 :         g_value_set_param(gvalue, (GParamSpec*) gparam);
     886         [ +  + ]:          64 :     } else if (gtype == G_TYPE_GTYPE) {
     887                 :             :         GType type;
     888                 :             : 
     889         [ -  + ]:          10 :         if (!value.isObject())
     890                 :           0 :             return throw_expect_type(context, value, "GType object");
     891                 :             : 
     892                 :          10 :         JS::RootedObject obj(context, &value.toObject());
     893         [ -  + ]:          10 :         if (!gjs_gtype_get_actual_gtype(context, obj, &type))
     894                 :           0 :             return false;
     895                 :          10 :         g_value_set_gtype(gvalue, type);
     896   [ +  -  +  +  :          64 :     } else if (g_type_is_a(gtype, G_TYPE_POINTER)) {
             -  +  +  + ]
     897         [ -  + ]:          11 :         if (value.isNull()) {
     898                 :             :             /* Nothing to do */
     899                 :             :         } else {
     900                 :           0 :             gjs_throw(context,
     901                 :             :                       "Cannot convert non-null JS value to G_POINTER");
     902                 :           0 :             return false;
     903                 :             :         }
     904   [ +  +  +  -  :          75 :     } else if (value.isNumber() &&
                   +  + ]
     905                 :          32 :                g_value_type_transformable(G_TYPE_INT, gtype)) {
     906                 :             :         /* Only do this crazy gvalue transform stuff after we've
     907                 :             :          * exhausted everything else. Adding this for
     908                 :             :          * e.g. ClutterUnit.
     909                 :             :          */
     910                 :             :         gint32 i;
     911         [ +  - ]:          32 :         if (Gjs::js_value_to_c(context, value, &i)) {
     912                 :          32 :             GValue int_value = { 0, };
     913                 :          32 :             g_value_init(&int_value, G_TYPE_INT);
     914                 :          32 :             g_value_set_int(&int_value, i);
     915                 :          32 :             g_value_transform(&int_value, gvalue);
     916                 :             :         } else {
     917                 :           0 :             return throw_expect_type(context, value, "integer");
     918                 :             :         }
     919         [ +  - ]:          11 :     } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
     920                 :             :         // The gtype is none of the above, it should be derived from a custom
     921                 :             :         // fundamental type.
     922         [ -  + ]:          11 :         if (!value.isObject())
     923                 :           2 :             return throw_expect_type(context, value, "object", gtype);
     924                 :             : 
     925                 :          11 :         JS::RootedObject fundamental_object(context, &value.toObject());
     926         [ +  + ]:          11 :         if (!FundamentalBase::to_gvalue(context, fundamental_object, gvalue))
     927                 :           2 :             return false;
     928         [ +  + ]:          11 :     } else {
     929                 :           0 :         gjs_debug(GJS_DEBUG_GCLOSURE, "JS::Value is number %d gtype fundamental %d transformable to int %d from int %d",
     930                 :           0 :                   value.isNumber(),
     931                 :             :                   G_TYPE_IS_FUNDAMENTAL(gtype),
     932                 :             :                   g_value_type_transformable(gtype, G_TYPE_INT),
     933                 :             :                   g_value_type_transformable(G_TYPE_INT, gtype));
     934                 :             : 
     935                 :           0 :         gjs_throw(context,
     936                 :             :                   "Don't know how to convert JavaScript object to GType %s",
     937                 :             :                   g_type_name(gtype));
     938                 :           0 :         return false;
     939                 :             :     }
     940                 :             : 
     941                 :        8848 :     return true;
     942                 :             : }
     943                 :             : 
     944                 :             : bool
     945                 :        8960 : gjs_value_to_g_value(JSContext      *context,
     946                 :             :                      JS::HandleValue value,
     947                 :             :                      GValue         *gvalue)
     948                 :             : {
     949                 :        8960 :     return gjs_value_to_g_value_internal(context, value, gvalue, false);
     950                 :             : }
     951                 :             : 
     952                 :             : bool
     953                 :           6 : gjs_value_to_g_value_no_copy(JSContext      *context,
     954                 :             :                              JS::HandleValue value,
     955                 :             :                              GValue         *gvalue)
     956                 :             : {
     957                 :           6 :     return gjs_value_to_g_value_internal(context, value, gvalue, true);
     958                 :             : }
     959                 :             : 
     960                 :           9 : [[nodiscard]] static JS::Value convert_int_to_enum(GType gtype, int v) {
     961                 :             :     double v_double;
     962                 :             : 
     963   [ +  +  +  - ]:           9 :     if (v > 0 && v < G_MAXINT) {
     964                 :             :         /* Optimize the unambiguous case */
     965                 :           8 :         v_double = v;
     966                 :             :     } else {
     967                 :             :         /* Need to distinguish between negative integers and unsigned integers */
     968                 :           1 :         GjsAutoEnumInfo info = g_irepository_find_by_gtype(nullptr, gtype);
     969                 :             : 
     970                 :             :         // Native enums don't have type info, assume
     971                 :             :         // they are signed to avoid crashing when
     972                 :             :         // they are exposed to JS.
     973         [ +  - ]:           1 :         if (!info) {
     974                 :           1 :             v_double = int64_t(v);
     975                 :             :         } else {
     976                 :           0 :             v_double = _gjs_enum_from_int(info, v);
     977                 :             :         }
     978                 :           1 :     }
     979                 :             : 
     980                 :           9 :     return JS::NumberValue(v_double);
     981                 :             : }
     982                 :             : 
     983                 :             : GJS_JSAPI_RETURN_CONVENTION
     984                 :        1718 : static bool gjs_value_from_g_value_internal(JSContext* context,
     985                 :             :                                             JS::MutableHandleValue value_p,
     986                 :             :                                             const GValue* gvalue, bool no_copy,
     987                 :             :                                             bool is_introspected_signal,
     988                 :             :                                             GIArgInfo* arg_info,
     989                 :             :                                             GITypeInfo* type_info) {
     990                 :             :     GType gtype;
     991                 :             : 
     992                 :        1718 :     gtype = G_VALUE_TYPE(gvalue);
     993                 :             : 
     994                 :             :     gjs_debug_marshal(GJS_DEBUG_GCLOSURE,
     995                 :             :                       "Converting gtype %s to JS::Value",
     996                 :             :                       g_type_name(gtype));
     997                 :             : 
     998   [ +  +  +  +  :        3035 :     if (gtype != G_TYPE_STRV && g_value_fits_pointer(gvalue) &&
             +  +  +  + ]
     999                 :        1317 :         g_value_peek_pointer(gvalue) == nullptr) {
    1000                 :             :         // In theory here we should throw if !g_arg_info_may_be_null(arg_info)
    1001                 :             :         // however most signals don't explicitly mark themselves as nullable,
    1002                 :             :         // so better to avoid this.
    1003                 :             :         gjs_debug_marshal(GJS_DEBUG_GCLOSURE,
    1004                 :             :                           "Converting NULL %s to JS::NullValue()",
    1005                 :             :                           g_type_name(gtype));
    1006                 :          93 :         value_p.setNull();
    1007                 :          93 :         return true;
    1008                 :             :     }
    1009                 :             : 
    1010         [ +  + ]:        1625 :     if (gtype == G_TYPE_STRING) {
    1011                 :         282 :         return gjs_string_from_utf8(context, g_value_get_string(gvalue),
    1012                 :         282 :                                     value_p);
    1013         [ +  + ]:        1343 :     } else if (gtype == G_TYPE_CHAR) {
    1014                 :             :         signed char v;
    1015                 :          20 :         v = g_value_get_schar(gvalue);
    1016                 :          20 :         value_p.setInt32(v);
    1017         [ +  + ]:        1323 :     } else if (gtype == G_TYPE_UCHAR) {
    1018                 :             :         unsigned char v;
    1019                 :          14 :         v = g_value_get_uchar(gvalue);
    1020                 :          14 :         value_p.setInt32(v);
    1021         [ +  + ]:        1309 :     } else if (gtype == G_TYPE_INT) {
    1022                 :             :         int v;
    1023                 :         161 :         v = g_value_get_int(gvalue);
    1024                 :         161 :         value_p.set(JS::NumberValue(v));
    1025         [ +  + ]:        1148 :     } else if (gtype == G_TYPE_UINT) {
    1026                 :             :         guint v;
    1027                 :          17 :         v = g_value_get_uint(gvalue);
    1028                 :          17 :         value_p.setNumber(v);
    1029         [ +  + ]:        1131 :     } else if (gtype == G_TYPE_DOUBLE) {
    1030                 :             :         double d;
    1031                 :          17 :         d = g_value_get_double(gvalue);
    1032                 :          17 :         value_p.setNumber(JS::CanonicalizeNaN(d));
    1033         [ +  + ]:        1114 :     } else if (gtype == G_TYPE_FLOAT) {
    1034                 :             :         double d;
    1035                 :          15 :         d = g_value_get_float(gvalue);
    1036                 :          15 :         value_p.setNumber(JS::CanonicalizeNaN(d));
    1037         [ +  + ]:        1099 :     } else if (gtype == G_TYPE_BOOLEAN) {
    1038                 :             :         bool v;
    1039                 :          40 :         v = g_value_get_boolean(gvalue);
    1040                 :          40 :         value_p.setBoolean(!!v);
    1041   [ +  +  +  +  :        1059 :     } else if (g_type_is_a(gtype, G_TYPE_OBJECT) || g_type_is_a(gtype, G_TYPE_INTERFACE)) {
          +  -  -  +  +  
                      + ]
    1042                 :         525 :         return ObjectInstance::set_value_from_gobject(
    1043                 :         525 :             context, static_cast<GObject*>(g_value_get_object(gvalue)),
    1044                 :         525 :             value_p);
    1045         [ +  + ]:         534 :     } else if (gtype == G_TYPE_STRV) {
    1046         [ -  + ]:          15 :         if (!gjs_array_from_strv (context,
    1047                 :             :                                   value_p,
    1048                 :          15 :                                   (const char**) g_value_get_boxed (gvalue))) {
    1049                 :           0 :             gjs_throw(context, "Failed to convert strv to array");
    1050                 :           0 :             return false;
    1051                 :             :         }
    1052   [ +  -  +  + ]:         519 :     } else if (gtype == G_TYPE_ARRAY || gtype == G_TYPE_BYTE_ARRAY ||
    1053         [ +  + ]:         510 :                gtype == G_TYPE_PTR_ARRAY) {
    1054         [ +  + ]:          15 :         if (gtype == G_TYPE_BYTE_ARRAY) {
    1055                 :           9 :             auto* byte_array = static_cast<GByteArray*>(g_value_get_boxed(gvalue));
    1056                 :             :             JSObject* array =
    1057                 :           9 :                 gjs_byte_array_from_byte_array(context, byte_array);
    1058         [ -  + ]:           9 :             if (!array) {
    1059                 :           0 :                 gjs_throw(context,
    1060                 :             :                           "Couldn't convert GByteArray to a Uint8Array");
    1061                 :           9 :                 return false;
    1062                 :             :             }
    1063                 :           9 :             value_p.setObject(*array);
    1064                 :           9 :             return true;
    1065                 :             :         }
    1066                 :             : 
    1067   [ +  -  -  + ]:           6 :         if (!is_introspected_signal || !arg_info) {
    1068                 :           0 :             gjs_throw(context, "Unknown signal");
    1069                 :           0 :             return false;
    1070                 :             :         }
    1071                 :             : 
    1072                 :           6 :         GITransfer transfer = g_arg_info_get_ownership_transfer(arg_info);
    1073                 :           6 :         GjsAutoTypeInfo element_info = g_type_info_get_param_type(type_info, 0);
    1074         [ -  + ]:           6 :         if (!gjs_array_from_g_value_array(context, value_p, element_info,
    1075                 :             :                                           transfer, gvalue)) {
    1076                 :           0 :             gjs_throw(context, "Failed to convert array");
    1077                 :           0 :             return false;
    1078                 :             :         }
    1079   [ +  -  +  + ]:         516 :     } else if (gtype == G_TYPE_HASH_TABLE) {
    1080         [ -  + ]:           3 :         if (!arg_info) {
    1081                 :           0 :             gjs_throw(context, "Failed to get GValue from Hash Table without"
    1082                 :             :                       "signal information");
    1083                 :           0 :             return false;
    1084                 :             :         }
    1085                 :           3 :         GjsAutoTypeInfo key_info = g_type_info_get_param_type(type_info, 0);
    1086                 :           3 :         GjsAutoTypeInfo value_info = g_type_info_get_param_type(type_info, 1);
    1087                 :           3 :         GITransfer transfer = g_arg_info_get_ownership_transfer(arg_info);
    1088                 :             : 
    1089         [ -  + ]:           3 :         if (!gjs_object_from_g_hash(
    1090                 :             :                 context, value_p, key_info, value_info, transfer,
    1091                 :           3 :                 static_cast<GHashTable*>(g_value_get_boxed(gvalue)))) {
    1092                 :           0 :             gjs_throw(context, "Failed to convert Hash Table");
    1093                 :           0 :             return false;
    1094                 :             :         }
    1095   [ +  -  +  -  :         504 :     } else if (g_type_is_a(gtype, G_TYPE_BOXED) || gtype == G_TYPE_VARIANT) {
          +  -  +  +  +  
                +  +  + ]
    1096                 :             :         void *gboxed;
    1097                 :             :         JSObject *obj;
    1098                 :             : 
    1099   [ +  -  +  +  :         205 :         if (g_type_is_a(gtype, G_TYPE_BOXED))
                   +  + ]
    1100                 :         121 :             gboxed = g_value_get_boxed(gvalue);
    1101                 :             :         else
    1102                 :          84 :             gboxed = g_value_get_variant(gvalue);
    1103                 :             : 
    1104         [ +  + ]:         205 :         if (gtype == ObjectBox::gtype()) {
    1105                 :          30 :             obj = ObjectBox::object_for_c_ptr(context,
    1106                 :             :                                               static_cast<ObjectBox*>(gboxed));
    1107         [ -  + ]:          30 :             if (!obj)
    1108                 :          41 :                 return false;
    1109                 :          30 :             value_p.setObject(*obj);
    1110                 :          30 :             return true;
    1111                 :             :         }
    1112                 :             : 
    1113                 :             :         /* special case GError */
    1114         [ +  + ]:         175 :         if (gtype == G_TYPE_ERROR) {
    1115                 :           2 :             obj = ErrorInstance::object_for_c_ptr(context,
    1116                 :             :                                                   static_cast<GError*>(gboxed));
    1117         [ -  + ]:           2 :             if (!obj)
    1118                 :           0 :                 return false;
    1119                 :           2 :             value_p.setObject(*obj);
    1120                 :           2 :             return true;
    1121                 :             :         }
    1122                 :             : 
    1123                 :             :         /* special case GValue */
    1124         [ +  + ]:         173 :         if (gtype == G_TYPE_VALUE) {
    1125                 :           8 :             return gjs_value_from_g_value(context, value_p,
    1126                 :           8 :                                           static_cast<GValue *>(gboxed));
    1127                 :             :         }
    1128                 :             : 
    1129                 :             :         /* The only way to differentiate unions and structs is from
    1130                 :             :          * their g-i info as both GBoxed */
    1131                 :         165 :         GjsAutoBaseInfo info = g_irepository_find_by_gtype(nullptr, gtype);
    1132         [ -  + ]:         165 :         if (!info) {
    1133                 :           0 :             gjs_throw(context,
    1134                 :             :                       "No introspection information found for %s",
    1135                 :             :                       g_type_name(gtype));
    1136                 :           0 :             return false;
    1137                 :             :         }
    1138                 :             : 
    1139   [ +  -  +  +  :         330 :         if (info.type() == GI_INFO_TYPE_STRUCT &&
                   +  + ]
    1140                 :         165 :             g_struct_info_is_foreign(info)) {
    1141                 :             :             GIArgument arg;
    1142                 :           1 :             gjs_arg_set(&arg, gboxed);
    1143                 :           1 :             return gjs_struct_foreign_convert_from_gi_argument(context, value_p,
    1144                 :           1 :                                                                info, &arg);
    1145                 :             :         }
    1146                 :             : 
    1147                 :         164 :         GIInfoType type = info.type();
    1148   [ +  -  +  - ]:         164 :         if (type == GI_INFO_TYPE_BOXED || type == GI_INFO_TYPE_STRUCT) {
    1149         [ +  + ]:         164 :             if (no_copy)
    1150                 :           1 :                 obj = BoxedInstance::new_for_c_struct(context, info, gboxed,
    1151                 :             :                                                       BoxedInstance::NoCopy());
    1152                 :             :             else
    1153                 :         163 :                 obj = BoxedInstance::new_for_c_struct(context, info, gboxed);
    1154         [ #  # ]:           0 :         } else if (type == GI_INFO_TYPE_UNION) {
    1155                 :           0 :             obj = UnionInstance::new_for_c_union(context, info, gboxed);
    1156                 :             :         } else {
    1157                 :           0 :             gjs_throw(context, "Unexpected introspection type %d for %s",
    1158                 :           0 :                       info.type(), g_type_name(gtype));
    1159                 :           0 :             return false;
    1160                 :             :         }
    1161                 :             : 
    1162                 :         164 :         value_p.setObjectOrNull(obj);
    1163   [ +  +  +  +  :         461 :     } else if (g_type_is_a(gtype, G_TYPE_ENUM)) {
             +  +  +  + ]
    1164                 :           9 :         value_p.set(convert_int_to_enum(gtype, g_value_get_enum(gvalue)));
    1165   [ +  +  -  +  :         287 :     } else if (g_type_is_a(gtype, G_TYPE_PARAM)) {
                   +  + ]
    1166                 :             :         GParamSpec *gparam;
    1167                 :             :         JSObject *obj;
    1168                 :             : 
    1169                 :         179 :         gparam = g_value_get_param(gvalue);
    1170                 :             : 
    1171                 :         179 :         obj = gjs_param_from_g_param(context, gparam);
    1172                 :         179 :         value_p.setObjectOrNull(obj);
    1173   [ -  +  -  -  :         108 :     } else if (is_introspected_signal && g_type_is_a(gtype, G_TYPE_POINTER)) {
             -  -  -  + ]
    1174         [ #  # ]:           0 :         if (!arg_info) {
    1175                 :           0 :             gjs_throw(context, "Unknown signal.");
    1176                 :           0 :             return false;
    1177                 :             :         }
    1178                 :             : 
    1179                 :           0 :         g_assert(((void)"Check gjs_value_from_array_and_length_values() before"
    1180                 :             :                         " calling gjs_value_from_g_value_internal()",
    1181                 :             :                   g_type_info_get_array_length(type_info) == -1));
    1182                 :             : 
    1183                 :             :         GIArgument arg;
    1184                 :           0 :         gjs_arg_set(&arg, g_value_get_pointer(gvalue));
    1185                 :             : 
    1186                 :           0 :         return gjs_value_from_gi_argument(context, value_p, type_info, &arg,
    1187                 :           0 :                                           true);
    1188         [ +  + ]:         108 :     } else if (gtype == G_TYPE_GTYPE) {
    1189                 :           3 :         GType gvalue_gtype = g_value_get_gtype(gvalue);
    1190                 :             : 
    1191         [ -  + ]:           3 :         if (gvalue_gtype == 0) {
    1192                 :           0 :             value_p.setNull();
    1193                 :           0 :             return true;
    1194                 :             :         }
    1195                 :             : 
    1196                 :             :         JS::RootedObject obj(
    1197                 :           3 :             context, gjs_gtype_create_gtype_wrapper(context, gvalue_gtype));
    1198         [ -  + ]:           3 :         if (!obj)
    1199                 :           0 :             return false;
    1200                 :             : 
    1201                 :           3 :         value_p.setObject(*obj);
    1202   [ +  -  +  -  :         108 :     } else if (g_type_is_a(gtype, G_TYPE_POINTER)) {
             -  +  -  + ]
    1203         [ #  # ]:           0 :         if (g_value_get_pointer(gvalue) != nullptr) {
    1204                 :           0 :             gjs_throw(context,
    1205                 :             :                       "Can't convert non-null pointer to JS value");
    1206                 :           0 :             return false;
    1207                 :             :         }
    1208         [ +  + ]:         105 :     } else if (g_value_type_transformable(gtype, G_TYPE_DOUBLE)) {
    1209                 :          84 :         GValue double_value = { 0, };
    1210                 :             :         double v;
    1211                 :          84 :         g_value_init(&double_value, G_TYPE_DOUBLE);
    1212                 :          84 :         g_value_transform(gvalue, &double_value);
    1213                 :          84 :         v = g_value_get_double(&double_value);
    1214                 :          84 :         value_p.setNumber(v);
    1215         [ +  + ]:          21 :     } else if (g_value_type_transformable(gtype, G_TYPE_INT)) {
    1216                 :           9 :         GValue int_value = { 0, };
    1217                 :             :         int v;
    1218                 :           9 :         g_value_init(&int_value, G_TYPE_INT);
    1219                 :           9 :         g_value_transform(gvalue, &int_value);
    1220                 :           9 :         v = g_value_get_int(&int_value);
    1221                 :           9 :         value_p.set(JS::NumberValue(v));
    1222         [ +  - ]:          12 :     } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
    1223                 :             :         /* The gtype is none of the above, it should be a custom
    1224                 :             :            fundamental type. */
    1225                 :          12 :         JS::RootedObject obj(context);
    1226         [ -  + ]:          12 :         if (!FundamentalInstance::object_for_gvalue(context, gvalue, gtype,
    1227                 :             :                                                     &obj))
    1228                 :           0 :             return false;
    1229                 :             : 
    1230                 :          12 :         value_p.setObjectOrNull(obj);
    1231         [ +  - ]:          12 :     } else {
    1232                 :           0 :         gjs_throw(context,
    1233                 :             :                   "Don't know how to convert GType %s to JavaScript object",
    1234                 :             :                   g_type_name(gtype));
    1235                 :           0 :         return false;
    1236                 :             :     }
    1237                 :             : 
    1238                 :         768 :     return true;
    1239                 :             : }
    1240                 :             : 
    1241                 :             : bool
    1242                 :         848 : gjs_value_from_g_value(JSContext             *context,
    1243                 :             :                        JS::MutableHandleValue value_p,
    1244                 :             :                        const GValue          *gvalue)
    1245                 :             : {
    1246                 :         848 :     return gjs_value_from_g_value_internal(context, value_p, gvalue, false);
    1247                 :             : }
        

Generated by: LCOV version 2.0-1