LCOV - code coverage report
Current view: top level - gi - value.cpp (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 72.0 % 668 481
Test Date: 2025-05-07 12:25:00 Functions: 100.0 % 13 13
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 66.8 % 618 413

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

Generated by: LCOV version 2.0-1