LCOV - code coverage report
Current view: top level - gi - arg-cache.cpp (source / functions) Hit Total Coverage
Test: gjs- Code Coverage Lines: 858 1015 84.5 %
Date: 2023-09-17 02:39:54 Functions: 305 371 82.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 366 476 76.9 %

           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: 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
       4                 :            : // SPDX-FileCopyrightText: 2020 Marco Trevisan <marco.trevisan@canonical.com>
       5                 :            : 
       6                 :            : #include <config.h>
       7                 :            : 
       8                 :            : #include <inttypes.h>
       9                 :            : #include <stddef.h>  // for size_t
      10                 :            : #include <stdint.h>
      11                 :            : #include <string.h>
      12                 :            : 
      13                 :            : #include <limits>
      14                 :            : #include <tuple>
      15                 :            : #include <type_traits>
      16                 :            : #include <unordered_set>  // for unordered_set::erase(), insert()
      17                 :            : #include <utility>
      18                 :            : 
      19                 :            : #include <ffi.h>
      20                 :            : #include <girepository.h>
      21                 :            : #include <glib.h>
      22                 :            : 
      23                 :            : #include <js/Conversions.h>
      24                 :            : #include <js/RootingAPI.h>
      25                 :            : #include <js/TypeDecls.h>
      26                 :            : #include <js/Utility.h>  // for UniqueChars
      27                 :            : #include <js/Value.h>
      28                 :            : #include <js/experimental/TypedData.h>
      29                 :            : #include <jsapi.h>        // for InformalValueTypeName, JS_TypeOfValue
      30                 :            : #include <jspubtd.h>      // for JSTYPE_FUNCTION
      31                 :            : 
      32                 :            : #include "gi/arg-cache.h"
      33                 :            : #include "gi/arg-inl.h"
      34                 :            : #include "gi/arg-types-inl.h"
      35                 :            : #include "gi/arg.h"
      36                 :            : #include "gi/boxed.h"
      37                 :            : #include "gi/closure.h"
      38                 :            : #include "gi/foreign.h"
      39                 :            : #include "gi/function.h"
      40                 :            : #include "gi/fundamental.h"
      41                 :            : #include "gi/gerror.h"
      42                 :            : #include "gi/gtype.h"
      43                 :            : #include "gi/js-value-inl.h"
      44                 :            : #include "gi/object.h"
      45                 :            : #include "gi/param.h"
      46                 :            : #include "gi/union.h"
      47                 :            : #include "gi/value.h"
      48                 :            : #include "gi/wrapperutils.h"  // for GjsTypecheckNoThrow
      49                 :            : #include "gjs/byteArray.h"
      50                 :            : #include "gjs/enum-utils.h"  // for operator&, operator|=, operator|
      51                 :            : #include "gjs/jsapi-util.h"
      52                 :            : #include "gjs/macros.h"
      53                 :            : #include "util/log.h"
      54                 :            : 
      55                 :            : enum ExpectedType {
      56                 :            :     OBJECT,
      57                 :            :     FUNCTION,
      58                 :            :     STRING,
      59                 :            :     LAST,
      60                 :            : };
      61                 :            : 
      62                 :            : static const char* expected_type_names[] = {"object", "function", "string"};
      63                 :            : static_assert(G_N_ELEMENTS(expected_type_names) == ExpectedType::LAST,
      64                 :            :               "Names must match the values in ExpectedType");
      65                 :            : 
      66                 :        328 : static constexpr void gjs_g_argument_set_array_length(GITypeTag tag,
      67                 :            :                                                       GIArgument* arg,
      68                 :            :                                                       size_t value) {
      69   [ -  +  -  -  :        328 :     switch (tag) {
             +  +  +  +  
                      - ]
      70                 :          0 :         case GI_TYPE_TAG_INT8:
      71                 :          0 :             gjs_arg_set<int8_t>(arg, value);
      72                 :          0 :             break;
      73                 :          1 :         case GI_TYPE_TAG_UINT8:
      74                 :          1 :             gjs_arg_set<uint8_t>(arg, value);
      75                 :          1 :             break;
      76                 :          0 :         case GI_TYPE_TAG_INT16:
      77                 :          0 :             gjs_arg_set<int16_t>(arg, value);
      78                 :          0 :             break;
      79                 :          0 :         case GI_TYPE_TAG_UINT16:
      80                 :          0 :             gjs_arg_set<uint16_t>(arg, value);
      81                 :          0 :             break;
      82                 :         66 :         case GI_TYPE_TAG_INT32:
      83                 :         66 :             gjs_arg_set<int32_t>(arg, value);
      84                 :         66 :             break;
      85                 :          4 :         case GI_TYPE_TAG_UINT32:
      86                 :          4 :             gjs_arg_set<uint32_t>(arg, value);
      87                 :          4 :             break;
      88                 :         14 :         case GI_TYPE_TAG_INT64:
      89                 :         14 :             gjs_arg_set<int64_t>(arg, value);
      90                 :         14 :             break;
      91                 :        243 :         case GI_TYPE_TAG_UINT64:
      92                 :        243 :             gjs_arg_set<uint64_t>(arg, value);
      93                 :        243 :             break;
      94                 :          0 :         default:
      95                 :            :             g_assert_not_reached();
      96                 :            :     }
      97                 :        328 : }
      98                 :            : 
      99                 :            : GJS_JSAPI_RETURN_CONVENTION
     100                 :          0 : static bool report_typeof_mismatch(JSContext* cx, const char* arg_name,
     101                 :            :                                    JS::HandleValue value,
     102                 :            :                                    ExpectedType expected) {
     103                 :          0 :     gjs_throw(cx, "Expected type %s for argument '%s' but got type %s",
     104                 :          0 :               expected_type_names[expected], arg_name,
     105                 :            :               JS::InformalValueTypeName(value));
     106                 :          0 :     return false;
     107                 :            : }
     108                 :            : 
     109                 :            : GJS_JSAPI_RETURN_CONVENTION
     110                 :          0 : static bool report_gtype_mismatch(JSContext* cx, const char* arg_name,
     111                 :            :                                   JS::Value value, GType expected) {
     112                 :          0 :     gjs_throw(
     113                 :            :         cx, "Expected an object of type %s for argument '%s' but got type %s",
     114                 :            :         g_type_name(expected), arg_name, JS::InformalValueTypeName(value));
     115                 :          0 :     return false;
     116                 :            : }
     117                 :            : 
     118                 :            : GJS_JSAPI_RETURN_CONVENTION
     119                 :          2 : static bool report_invalid_null(JSContext* cx, const char* arg_name) {
     120                 :          2 :     gjs_throw(cx, "Argument %s may not be null", arg_name);
     121                 :          2 :     return false;
     122                 :            : }
     123                 :            : 
     124                 :            : // Overload operator| so that Visual Studio won't complain
     125                 :            : // when converting unsigned char to GjsArgumentFlags
     126                 :      22307 : GjsArgumentFlags operator|(
     127                 :            :     GjsArgumentFlags const& v1, GjsArgumentFlags const& v2) {
     128                 :      22307 :     return static_cast<GjsArgumentFlags>(std::underlying_type<GjsArgumentFlags>::type(v1) |
     129                 :      22307 :                                          std::underlying_type<GjsArgumentFlags>::type(v2));
     130                 :            : }
     131                 :            : 
     132                 :            : namespace Gjs {
     133                 :            : namespace Arg {
     134                 :            : 
     135                 :            : // Arguments Interfaces:
     136                 :            : //
     137                 :            : // Each of these types are meant to be used to extend each Gjs::Argument
     138                 :            : // implementation, taking advantage of the C++ multiple inheritance.
     139                 :            : 
     140                 :            : struct BasicType {
     141                 :        918 :     constexpr BasicType() : m_tag(GI_TYPE_TAG_VOID) {}
     142                 :       3114 :     constexpr explicit BasicType(GITypeTag tag) : m_tag(tag) {}
     143                 :            :     constexpr operator GITypeTag() const { return m_tag; }
     144                 :            : 
     145                 :            :     GITypeTag m_tag : 5;
     146                 :            : };
     147                 :            : 
     148                 :            : struct String {
     149                 :       4997 :     constexpr String() : m_filename(false) {}
     150                 :            :     bool m_filename : 1;
     151                 :            : };
     152                 :            : 
     153                 :            : struct TypeInfo {
     154                 :      33891 :     constexpr GITypeInfo* type_info() const {
     155                 :            :         // Should be const GITypeInfo*, but G-I APIs won't accept that
     156                 :      33891 :         return const_cast<GITypeInfo*>(&m_type_info);
     157                 :            :     }
     158                 :            : 
     159                 :            :     GITypeInfo m_type_info;
     160                 :            : };
     161                 :            : 
     162                 :            : struct Transferable {
     163                 :      15101 :     constexpr Transferable() : m_transfer(GI_TRANSFER_NOTHING) {}
     164                 :            :     constexpr explicit Transferable(GITransfer transfer)
     165                 :            :         : m_transfer(transfer) {}
     166                 :            :     GITransfer m_transfer : 2;
     167                 :            : };
     168                 :            : 
     169                 :            : struct Nullable {
     170                 :      15337 :     constexpr Nullable() : m_nullable(false) {}
     171                 :            :     bool m_nullable : 1;
     172                 :            : 
     173                 :            :     bool handle_nullable(JSContext* cx, GIArgument* arg, const char* arg_name);
     174                 :            : 
     175                 :        511 :     constexpr GjsArgumentFlags flags() const {
     176         [ +  + ]:        511 :         return m_nullable ? GjsArgumentFlags::MAY_BE_NULL
     177                 :        511 :                           : GjsArgumentFlags::NONE;
     178                 :            :     }
     179                 :            : };
     180                 :            : 
     181                 :            : struct Positioned {
     182                 :       1304 :     void set_arg_pos(int pos) {
     183                 :       1304 :         g_assert(pos <= Argument::MAX_ARGS &&
     184                 :            :                  "No more than 253 arguments allowed");
     185                 :       1304 :         m_arg_pos = pos;
     186                 :       1304 :     }
     187                 :            : 
     188                 :        656 :     constexpr bool set_out_parameter(GjsFunctionCallState* state,
     189                 :            :                                      GIArgument* arg) {
     190                 :        656 :         gjs_arg_unset<void*>(&state->out_cvalue(m_arg_pos));
     191                 :        656 :         gjs_arg_set(arg, &gjs_arg_member<void*>(&state->out_cvalue(m_arg_pos)));
     192                 :        656 :         return true;
     193                 :            :     }
     194                 :            : 
     195                 :            :     uint8_t m_arg_pos = 0;
     196                 :            : };
     197                 :            : 
     198                 :            : struct Array : BasicType {
     199                 :            :     uint8_t m_length_pos = 0;
     200                 :            :     GIDirection m_length_direction : 2;
     201                 :            : 
     202                 :        918 :     Array() : BasicType(), m_length_direction(GI_DIRECTION_IN) {}
     203                 :            : 
     204                 :        918 :     void set_array_length(int pos, GITypeTag tag, GIDirection direction) {
     205                 :        918 :         g_assert(pos >= 0 && pos <= Argument::MAX_ARGS &&
     206                 :            :                  "No more than 253 arguments allowed");
     207                 :        918 :         m_length_pos = pos;
     208                 :        918 :         m_length_direction = direction;
     209                 :        918 :         m_tag = tag;
     210                 :        918 :     }
     211                 :            : };
     212                 :            : 
     213                 :            : struct BaseInfo {
     214                 :       1124 :     constexpr explicit BaseInfo(GIBaseInfo* info,
     215                 :            :                                 const GjsAutoTakeOwnership& add_ref)
     216                 :       1124 :         : m_info(info, add_ref) {}
     217                 :            :     constexpr explicit BaseInfo(GIBaseInfo* info) : m_info(info) {}
     218                 :            : 
     219                 :            :     GjsAutoBaseInfo m_info;
     220                 :            : };
     221                 :            : 
     222                 :            : // boxed / union / GObject
     223                 :            : struct GTypedType {
     224                 :       6059 :     explicit GTypedType(GType gtype) : m_gtype(gtype) {}
     225                 :      91178 :     constexpr GType gtype() const { return m_gtype; }
     226                 :            : 
     227                 :            :  protected:
     228                 :            :     GType m_gtype;
     229                 :            : };
     230                 :            : 
     231                 :            : struct RegisteredType : GTypedType {
     232                 :          7 :     RegisteredType(GType gtype, GIInfoType info_type)
     233                 :          7 :         : GTypedType(gtype), m_info_type(info_type) {}
     234                 :       5573 :     explicit RegisteredType(GIBaseInfo* info)
     235                 :       5573 :         : GTypedType(g_registered_type_info_get_g_type(info)),
     236                 :       5573 :           m_info_type(g_base_info_get_type(info)) {
     237                 :       5573 :         g_assert(m_gtype != G_TYPE_NONE &&
     238                 :            :                  "Use RegisteredInterface for this type");
     239                 :       5573 :     }
     240                 :            : 
     241                 :            :     GIInfoType m_info_type : 5;
     242                 :            : };
     243                 :            : 
     244                 :            : struct RegisteredInterface : BaseInfo, GTypedType {
     245                 :        473 :     explicit RegisteredInterface(GIBaseInfo* info)
     246                 :        946 :         : BaseInfo(info, GjsAutoTakeOwnership{}),
     247                 :        473 :           GTypedType(g_registered_type_info_get_g_type(m_info)) {}
     248                 :            : };
     249                 :            : 
     250                 :            : struct Callback : Nullable, BaseInfo {
     251                 :        644 :     explicit Callback(GIInterfaceInfo* info)
     252                 :       1288 :         : BaseInfo(info, GjsAutoTakeOwnership{}),
     253                 :        644 :           m_scope(GI_SCOPE_TYPE_INVALID) {}
     254                 :            : 
     255                 :        643 :     inline void set_callback_destroy_pos(int pos) {
     256                 :        643 :         g_assert(pos <= Argument::MAX_ARGS &&
     257                 :            :                  "No more than 253 arguments allowed");
     258         [ +  + ]:        643 :         m_destroy_pos = pos < 0 ? Argument::ABSENT : pos;
     259                 :        643 :     }
     260                 :            : 
     261                 :        506 :     [[nodiscard]] constexpr bool has_callback_destroy() {
     262                 :        506 :         return m_destroy_pos != Argument::ABSENT;
     263                 :            :     }
     264                 :            : 
     265                 :        643 :     inline void set_callback_closure_pos(int pos) {
     266                 :        643 :         g_assert(pos <= Argument::MAX_ARGS &&
     267                 :            :                  "No more than 253 arguments allowed");
     268         [ +  + ]:        643 :         m_closure_pos = pos < 0 ? Argument::ABSENT : pos;
     269                 :        643 :     }
     270                 :            : 
     271                 :        253 :     [[nodiscard]] constexpr bool has_callback_closure() {
     272                 :        253 :         return m_closure_pos != Argument::ABSENT;
     273                 :            :     }
     274                 :            : 
     275                 :            :     uint8_t m_closure_pos = Argument::ABSENT;
     276                 :            :     uint8_t m_destroy_pos = Argument::ABSENT;
     277                 :            :     GIScopeType m_scope : 3;
     278                 :            : };
     279                 :            : 
     280                 :            : struct Enum {
     281                 :            :     explicit Enum(GIEnumInfo*);
     282                 :            :     bool m_unsigned : 1;
     283                 :            :     uint32_t m_min = 0;
     284                 :            :     uint32_t m_max = 0;
     285                 :            : };
     286                 :            : 
     287                 :            : struct Flags {
     288                 :            :     explicit Flags(GIEnumInfo*);
     289                 :            :     unsigned m_mask = 0;
     290                 :            : };
     291                 :            : 
     292                 :            : struct CallerAllocates {
     293                 :            :     size_t m_allocates_size = 0;
     294                 :            : };
     295                 :            : 
     296                 :            : // Gjs::Arguments:
     297                 :            : //
     298                 :            : // Each argument, irrespective of the direction, is processed in three phases:
     299                 :            : // - before calling the function [in]
     300                 :            : // - after calling it, when converting the return value and out arguments [out]
     301                 :            : // - at the end of the invocation, to release any allocated memory [release]
     302                 :            : //
     303                 :            : // Some types don't have direction (for example, caller_allocates is only out,
     304                 :            : // and callback is only in), in which case it is implied.
     305                 :            : 
     306                 :            : struct SkipAll : Argument {
     307                 :        666 :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     308                 :            :             JS::HandleValue) override {
     309                 :        666 :         return skip();
     310                 :            :     }
     311                 :            : 
     312                 :      30638 :     bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
     313                 :            :              JS::MutableHandleValue) override {
     314                 :      30638 :         return skip();
     315                 :            :     }
     316                 :            : 
     317                 :      25648 :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     318                 :            :                  GIArgument*) override {
     319                 :      25648 :         return skip();
     320                 :            :     }
     321                 :            : 
     322                 :            :  protected:
     323                 :     152430 :     constexpr bool skip() { return true; }
     324                 :            : };
     325                 :            : 
     326                 :            : struct Generic : SkipAll, Transferable, TypeInfo {};
     327                 :            : 
     328                 :            : struct GenericIn : Generic {
     329                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     330                 :            :             JS::HandleValue) override;
     331                 :          0 :     bool out(JSContext* cx, GjsFunctionCallState*, GIArgument*,
     332                 :            :              JS::MutableHandleValue) override {
     333                 :          0 :         return invalid(cx, G_STRFUNC);
     334                 :            :     }
     335                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     336                 :            :                  GIArgument*) override;
     337                 :            : };
     338                 :            : 
     339                 :            : struct GenericInOut : GenericIn, Positioned {
     340                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     341                 :            :             JS::HandleValue) override;
     342                 :            :     bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
     343                 :            :              JS::MutableHandleValue) override;
     344                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     345                 :            :                  GIArgument*) override;
     346                 :            : };
     347                 :            : 
     348                 :            : struct GenericOut : GenericInOut {
     349                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     350                 :            :             JS::HandleValue) override;
     351                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     352                 :            :                  GIArgument*) override;
     353                 :            : 
     354                 :      33891 :     const ReturnValue* as_return_value() const override { return this; }
     355                 :            : };
     356                 :            : 
     357                 :            : struct GenericReturn : ReturnValue {
     358                 :            :     // No in!
     359                 :          0 :     bool in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
     360                 :            :             JS::HandleValue) override {
     361                 :          0 :         return invalid(cx, G_STRFUNC);
     362                 :            :     }
     363                 :            : };
     364                 :            : 
     365                 :            : struct SimpleOut : SkipAll, Positioned {
     366                 :         21 :     bool in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
     367                 :            :             JS::HandleValue) override {
     368                 :         21 :         return set_out_parameter(state, arg);
     369                 :            :     };
     370                 :            : };
     371                 :            : 
     372                 :            : struct ExplicitArray : GenericOut, Array, Nullable {
     373                 :        333 :     GjsArgumentFlags flags() const override {
     374                 :        333 :         return Argument::flags() | Nullable::flags();
     375                 :            :     }
     376                 :            : };
     377                 :            : 
     378                 :            : struct ExplicitArrayIn : ExplicitArray {
     379                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     380                 :            :             JS::HandleValue) override;
     381                 :        312 :     bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
     382                 :            :              JS::MutableHandleValue) override {
     383                 :        312 :         return skip();
     384                 :            :     };
     385                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     386                 :            :                  GIArgument*) override;
     387                 :            : };
     388                 :            : 
     389                 :            : struct ExplicitArrayInOut : ExplicitArrayIn {
     390                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     391                 :            :             JS::HandleValue) override;
     392                 :            :     bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
     393                 :            :              JS::MutableHandleValue) override;
     394                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     395                 :            :                  GIArgument*) override;
     396                 :            : };
     397                 :            : 
     398                 :            : struct ExplicitArrayOut : ExplicitArrayInOut {
     399                 :          0 :     bool in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
     400                 :            :             JS::HandleValue) override {
     401                 :          0 :         return invalid(cx, G_STRFUNC);
     402                 :            :     }
     403                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     404                 :            :                  GIArgument*) override;
     405                 :            : };
     406                 :            : 
     407                 :            : struct ReturnArray : ExplicitArrayOut {
     408                 :         14 :     bool in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
     409                 :            :             JS::HandleValue value) override {
     410         [ -  + ]:         14 :         if (m_length_direction != GI_DIRECTION_OUT) {
     411                 :          0 :             gjs_throw(cx,
     412                 :            :                       "Using different length argument direction for array %s"
     413                 :            :                       "is not supported for out arrays",
     414                 :            :                       m_arg_name);
     415                 :          0 :             return false;
     416                 :            :         }
     417                 :         14 :         return GenericOut::in(cx, state, arg, value);
     418                 :            :     };
     419                 :            : };
     420                 :            : 
     421                 :            : using ArrayLengthOut = SimpleOut;
     422                 :            : 
     423                 :            : struct FallbackIn : GenericIn, Nullable {
     424                 :         35 :     bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
     425                 :            :              JS::MutableHandleValue) override {
     426                 :         35 :         return skip();
     427                 :            :     }
     428                 :            : 
     429                 :         35 :     GjsArgumentFlags flags() const override {
     430                 :         35 :         return Argument::flags() | Nullable::flags();
     431                 :            :     }
     432                 :            : };
     433                 :            : 
     434                 :            : using FallbackInOut = GenericInOut;
     435                 :            : using FallbackOut = GenericOut;
     436                 :            : 
     437                 :            : struct NotIntrospectable : GenericIn {
     438                 :         67 :     explicit NotIntrospectable(NotIntrospectableReason reason)
     439                 :         67 :         : m_reason(reason) {}
     440                 :            :     NotIntrospectableReason m_reason;
     441                 :            : 
     442                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     443                 :            :             JS::HandleValue) override;
     444                 :            : };
     445                 :            : 
     446                 :            : struct NullableIn : SkipAll, Nullable {
     447                 :      11461 :     inline bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
     448                 :            :                    JS::HandleValue) override {
     449                 :      11461 :         return handle_nullable(cx, arg, m_arg_name);
     450                 :            :     }
     451                 :            : 
     452                 :        129 :     GjsArgumentFlags flags() const override {
     453                 :        129 :         return Argument::flags() | Nullable::flags();
     454                 :            :     }
     455                 :            : };
     456                 :            : 
     457                 :            : struct Instance : NullableIn {
     458                 :            :     //  Some calls accept null for the instance (thus we inherit from
     459                 :            :     //  NullableIn), but generally in an object oriented language it's wrong to
     460                 :            :     //  call a method on null.
     461                 :            :     //  As per this we actually default to SkipAll methods.
     462                 :            : 
     463                 :          0 :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     464                 :            :             JS::HandleValue) override {
     465                 :          0 :         return skip();
     466                 :            :     }
     467                 :      36306 :     bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
     468                 :            :              JS::MutableHandleValue) override {
     469                 :      36306 :         return skip();
     470                 :            :     }
     471                 :       2870 :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     472                 :            :                  GIArgument*) override {
     473                 :       2870 :         return skip();
     474                 :            :     }
     475                 :            : 
     476                 :      33718 :     const Instance* as_instance() const override { return this; }
     477                 :            : 
     478                 :            :     //  The instance GType can be useful only in few cases such as GObjects and
     479                 :            :     //  GInterfaces, so we don't store it by default, unless needed.
     480                 :            :     //  See Function's code to see where this is relevant.
     481                 :        251 :     virtual GType gtype() const { return G_TYPE_NONE; }
     482                 :            : };
     483                 :            : 
     484                 :            : struct EnumIn : Instance, Enum {
     485                 :            :     using Enum::Enum;
     486                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     487                 :            :             JS::HandleValue) override;
     488                 :            : };
     489                 :            : 
     490                 :            : struct FlagsIn : Instance, Flags {
     491                 :            :     using Flags::Flags;
     492                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     493                 :            :             JS::HandleValue) override;
     494                 :            : };
     495                 :            : 
     496                 :            : struct RegisteredIn : Instance, RegisteredType, Transferable {
     497                 :            :     using RegisteredType::RegisteredType;
     498                 :            : 
     499                 :      33467 :     GType gtype() const override { return RegisteredType::gtype(); }
     500                 :            : };
     501                 :            : 
     502                 :            : struct RegisteredInterfaceIn : Instance, RegisteredInterface, Transferable {
     503                 :            :     using RegisteredInterface::RegisteredInterface;
     504                 :            : 
     505                 :          0 :     GType gtype() const override { return RegisteredInterface::gtype(); }
     506                 :            : };
     507                 :            : 
     508                 :            : struct ForeignStructInstanceIn : RegisteredInterfaceIn {
     509                 :            :     using RegisteredInterfaceIn::RegisteredInterfaceIn;
     510                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     511                 :            :             JS::HandleValue) override;
     512                 :            : };
     513                 :            : 
     514                 :            : struct ForeignStructIn : ForeignStructInstanceIn {
     515                 :            :     using ForeignStructInstanceIn::ForeignStructInstanceIn;
     516                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     517                 :            :                  GIArgument*) override;
     518                 :            : };
     519                 :            : 
     520                 :            : struct FallbackInterfaceIn : RegisteredInterfaceIn, TypeInfo {
     521                 :            :     using RegisteredInterfaceIn::RegisteredInterfaceIn;
     522                 :            : 
     523                 :        127 :     bool in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
     524                 :            :             JS::HandleValue value) override {
     525                 :        254 :         return gjs_value_to_g_argument(cx, value, &m_type_info, m_arg_name,
     526                 :        127 :                                        GJS_ARGUMENT_ARGUMENT, m_transfer,
     527                 :        254 :                                        flags(), arg);
     528                 :            :     }
     529                 :            : };
     530                 :            : 
     531                 :            : struct BoxedInTransferNone : RegisteredIn {
     532                 :            :     using RegisteredIn::RegisteredIn;
     533                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     534                 :            :             JS::HandleValue) override;
     535                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     536                 :            :                  GIArgument*) override;
     537                 :            : 
     538                 :      44777 :     virtual GIBaseInfo* info() const { return nullptr; }
     539                 :            : };
     540                 :            : 
     541                 :            : struct BoxedIn : BoxedInTransferNone {
     542                 :            :     using BoxedInTransferNone::BoxedInTransferNone;
     543                 :            :     // This is a smart argument, no release needed
     544                 :      55940 :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     545                 :            :                  GIArgument*) override {
     546                 :      55940 :         return skip();
     547                 :            :     }
     548                 :            : };
     549                 :            : 
     550                 :            : struct UnregisteredBoxedIn : BoxedIn, BaseInfo {
     551                 :          7 :     explicit UnregisteredBoxedIn(GIInterfaceInfo* info)
     552                 :          7 :         : BoxedIn(g_registered_type_info_get_g_type(info),
     553                 :            :                   g_base_info_get_type(info)),
     554                 :          7 :           BaseInfo(info, GjsAutoTakeOwnership{}) {}
     555                 :            :     // This is a smart argument, no release needed
     556                 :          8 :     GIBaseInfo* info() const override { return m_info; }
     557                 :            : };
     558                 :            : 
     559                 :            : struct GValueIn : BoxedIn {
     560                 :            :     using BoxedIn::BoxedIn;
     561                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     562                 :            :             JS::HandleValue) override;
     563                 :            : };
     564                 :            : 
     565                 :            : struct GValueInTransferNone : GValueIn {
     566                 :            :     using GValueIn::GValueIn;
     567                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     568                 :            :                  GIArgument*) override;
     569                 :            : };
     570                 :            : 
     571                 :            : struct GClosureInTransferNone : BoxedInTransferNone {
     572                 :            :     using BoxedInTransferNone::BoxedInTransferNone;
     573                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     574                 :            :             JS::HandleValue) override;
     575                 :            : };
     576                 :            : 
     577                 :            : struct GClosureIn : GClosureInTransferNone {
     578                 :            :     using GClosureInTransferNone::GClosureInTransferNone;
     579                 :          0 :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     580                 :            :                  GIArgument*) override {
     581                 :          0 :         return skip();
     582                 :            :     }
     583                 :            : };
     584                 :            : 
     585                 :            : struct GBytesIn : BoxedIn {
     586                 :            :     using BoxedIn::BoxedIn;
     587                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     588                 :            :             JS::HandleValue) override;
     589                 :            :     bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
     590                 :            :                  GIArgument* out_arg) override;
     591                 :            : };
     592                 :            : 
     593                 :            : struct GBytesInTransferNone : GBytesIn {
     594                 :            :     using GBytesIn::GBytesIn;
     595                 :         33 :     bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
     596                 :            :                  GIArgument* out_arg) override {
     597                 :         33 :         return BoxedInTransferNone::release(cx, state, in_arg, out_arg);
     598                 :            :     }
     599                 :            : };
     600                 :            : 
     601                 :            : struct ObjectIn : RegisteredIn {
     602                 :            :     using RegisteredIn::RegisteredIn;
     603                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     604                 :            :             JS::HandleValue) override;
     605                 :            :     // This is a smart argument, no release needed
     606                 :            : };
     607                 :            : 
     608                 :            : struct InterfaceIn : RegisteredIn {
     609                 :            :     using RegisteredIn::RegisteredIn;
     610                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     611                 :            :             JS::HandleValue) override;
     612                 :            :     // This is a smart argument, no release needed
     613                 :            : };
     614                 :            : 
     615                 :            : struct FundamentalIn : RegisteredIn {
     616                 :            :     using RegisteredIn::RegisteredIn;
     617                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     618                 :            :             JS::HandleValue) override;
     619                 :            :     // This is a smart argument, no release needed
     620                 :            : };
     621                 :            : 
     622                 :            : struct UnionIn : RegisteredIn {
     623                 :            :     using RegisteredIn::RegisteredIn;
     624                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     625                 :            :             JS::HandleValue) override;
     626                 :            :     // This is a smart argument, no release needed
     627                 :            : };
     628                 :            : 
     629                 :            : struct NullIn : NullableIn {
     630                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     631                 :            :             JS::HandleValue) override;
     632                 :            : };
     633                 :            : 
     634                 :            : struct BooleanIn : SkipAll {
     635                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     636                 :            :             JS::HandleValue) override;
     637                 :            : };
     638                 :            : 
     639                 :            : struct NumericIn : SkipAll, BasicType {
     640                 :       3114 :     explicit NumericIn(GITypeTag tag) : BasicType(tag) {}
     641                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     642                 :            :             JS::HandleValue) override;
     643                 :            : };
     644                 :            : 
     645                 :            : struct UnicharIn : SkipAll {
     646                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     647                 :            :             JS::HandleValue) override;
     648                 :            : };
     649                 :            : 
     650                 :            : struct GTypeIn : SkipAll {
     651                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     652                 :            :             JS::HandleValue) override;
     653                 :            : };
     654                 :            : 
     655                 :            : struct StringInTransferNone : NullableIn, String {
     656                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     657                 :            :             JS::HandleValue) override;
     658                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     659                 :            :                  GIArgument*) override;
     660                 :            : };
     661                 :            : 
     662                 :            : struct StringIn : StringInTransferNone {
     663                 :          0 :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     664                 :            :                  GIArgument*) override {
     665                 :          0 :         return skip();
     666                 :            :     }
     667                 :            : };
     668                 :            : 
     669                 :            : struct FilenameInTransferNone : StringInTransferNone {
     670                 :        216 :     FilenameInTransferNone() { m_filename = true; }
     671                 :            : };
     672                 :            : 
     673                 :            : struct FilenameIn : FilenameInTransferNone {
     674                 :          0 :     FilenameIn() { m_filename = true; }
     675                 :          0 :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     676                 :            :                  GIArgument*) override {
     677                 :          0 :         return skip();
     678                 :            :     }
     679                 :            : };
     680                 :            : 
     681                 :            : // .out is ignored for the instance parameter
     682                 :            : struct GTypeStructInstanceIn : Instance {
     683                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     684                 :            :             JS::HandleValue) override;
     685                 :            :     // no out
     686                 :          0 :     bool out(JSContext* cx, GjsFunctionCallState*, GIArgument*,
     687                 :            :              JS::MutableHandleValue) override {
     688                 :          0 :         return invalid(cx, G_STRFUNC);
     689                 :            :     };
     690                 :            : };
     691                 :            : 
     692                 :            : struct ParamInstanceIn : Instance, Transferable {
     693                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     694                 :            :             JS::HandleValue) override;
     695                 :            :     // no out
     696                 :          0 :     bool out(JSContext* cx, GjsFunctionCallState*, GIArgument*,
     697                 :            :              JS::MutableHandleValue) override {
     698                 :          0 :         return invalid(cx, G_STRFUNC);
     699                 :            :     };
     700                 :            : };
     701                 :            : 
     702                 :            : struct CallbackIn : SkipAll, Callback {
     703                 :            :     using Callback::Callback;
     704                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     705                 :            :             JS::HandleValue) override;
     706                 :            : 
     707                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     708                 :            :                  GIArgument*) override;
     709                 :            :  private:
     710                 :            :     ffi_closure *m_ffi_closure;
     711                 :            : };
     712                 :            : 
     713                 :            : using CArrayIn = ExplicitArrayIn;
     714                 :            : 
     715                 :            : using CArrayInOut = ExplicitArrayInOut;
     716                 :            : 
     717                 :            : using CArrayOut = ReturnArray;
     718                 :            : 
     719                 :            : struct CallerAllocatesOut : GenericOut, CallerAllocates {
     720                 :            :     bool in(JSContext*, GjsFunctionCallState*, GIArgument*,
     721                 :            :             JS::HandleValue) override;
     722                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     723                 :            :                  GIArgument*) override;
     724                 :            : 
     725                 :          0 :     GjsArgumentFlags flags() const override {
     726                 :          0 :         return GenericOut::flags() | GjsArgumentFlags::CALLER_ALLOCATES;
     727                 :            :     }
     728                 :            : };
     729                 :            : 
     730                 :            : struct BoxedCallerAllocatesOut : CallerAllocatesOut, GTypedType {
     731                 :            :     using GTypedType::GTypedType;
     732                 :            :     bool release(JSContext*, GjsFunctionCallState*, GIArgument*,
     733                 :            :                  GIArgument*) override;
     734                 :            : };
     735                 :            : 
     736                 :            : struct ZeroTerminatedArrayInOut : GenericInOut {
     737                 :          2 :     bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument*,
     738                 :            :                  GIArgument* out_arg) override {
     739                 :            :         GITransfer transfer =
     740         [ +  - ]:          2 :             state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
     741                 :          2 :         GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
     742         [ -  + ]:          2 :         if (!gjs_g_argument_release_in_array(cx, transfer, &m_type_info,
     743                 :            :                                              original_out_arg))
     744                 :          0 :             return false;
     745                 :            : 
     746                 :          2 :         transfer =
     747         [ +  - ]:          2 :             state->call_completed() ? m_transfer : GI_TRANSFER_EVERYTHING;
     748                 :          2 :         return gjs_g_argument_release_out_array(cx, transfer, &m_type_info,
     749                 :          2 :                                                 out_arg);
     750                 :            :     }
     751                 :            : };
     752                 :            : 
     753                 :            : struct ZeroTerminatedArrayIn : GenericIn, Nullable {
     754                 :         10 :     bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
     755                 :            :              JS::MutableHandleValue) override {
     756                 :         10 :         return skip();
     757                 :            :     }
     758                 :            : 
     759                 :         10 :     bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
     760                 :            :                  GIArgument*) override {
     761                 :            :         GITransfer transfer =
     762         [ +  + ]:         10 :             state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
     763                 :            : 
     764                 :         10 :         return gjs_g_argument_release_in_array(cx, transfer, &m_type_info,
     765                 :         10 :                                                in_arg);
     766                 :            :     }
     767                 :            : 
     768                 :         14 :     GjsArgumentFlags flags() const override {
     769                 :         14 :         return Argument::flags() | Nullable::flags();
     770                 :            :     }
     771                 :            : };
     772                 :            : 
     773                 :            : struct FixedSizeArrayIn : GenericIn {
     774                 :          5 :     bool out(JSContext*, GjsFunctionCallState*, GIArgument*,
     775                 :            :              JS::MutableHandleValue) override {
     776                 :          5 :         return skip();
     777                 :            :     }
     778                 :            : 
     779                 :          5 :     bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument* in_arg,
     780                 :            :                  GIArgument*) override {
     781                 :            :         GITransfer transfer =
     782         [ +  - ]:          5 :             state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
     783                 :            : 
     784                 :          5 :         int size = g_type_info_get_array_fixed_size(&m_type_info);
     785                 :          5 :         return gjs_g_argument_release_in_array(cx, transfer, &m_type_info, size,
     786                 :          5 :                                                in_arg);
     787                 :            :     }
     788                 :            : };
     789                 :            : 
     790                 :            : struct FixedSizeArrayInOut : GenericInOut {
     791                 :          1 :     bool release(JSContext* cx, GjsFunctionCallState* state, GIArgument*,
     792                 :            :                  GIArgument* out_arg) override {
     793                 :            :         GITransfer transfer =
     794         [ +  - ]:          1 :             state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
     795                 :          1 :         GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
     796                 :          1 :         int size = g_type_info_get_array_fixed_size(&m_type_info);
     797         [ -  + ]:          1 :         if (!gjs_g_argument_release_in_array(cx, transfer, &m_type_info, size,
     798                 :            :                                              original_out_arg))
     799                 :          0 :             return false;
     800                 :            : 
     801                 :          1 :         transfer =
     802         [ +  - ]:          1 :             state->call_completed() ? m_transfer : GI_TRANSFER_EVERYTHING;
     803                 :          1 :         return gjs_g_argument_release_out_array(cx, transfer, &m_type_info,
     804                 :          1 :                                                 size, out_arg);
     805                 :            :     }
     806                 :            : };
     807                 :            : 
     808                 :            : GJS_JSAPI_RETURN_CONVENTION
     809                 :          3 : bool NotIntrospectable::in(JSContext* cx, GjsFunctionCallState* state,
     810                 :            :                            GIArgument*, JS::HandleValue) {
     811                 :          3 :     const char* reason_string = "invalid introspection";
     812                 :            : 
     813   [ -  +  +  -  :          3 :     switch (m_reason) {
          +  -  -  -  -  
                      - ]
     814                 :          0 :         case CALLBACK_OUT:
     815                 :          0 :             reason_string = "callback out-argument";
     816                 :          0 :             break;
     817                 :          1 :         case DESTROY_NOTIFY_NO_CALLBACK:
     818                 :          1 :             reason_string = "DestroyNotify argument with no callback";
     819                 :          1 :             break;
     820                 :          1 :         case DESTROY_NOTIFY_NO_USER_DATA:
     821                 :          1 :             reason_string = "DestroyNotify argument with no user data";
     822                 :          1 :             break;
     823                 :          0 :         case INTERFACE_TRANSFER_CONTAINER:
     824                 :          0 :             reason_string = "type not supported for (transfer container)";
     825                 :          0 :             break;
     826                 :          1 :         case OUT_CALLER_ALLOCATES_NON_STRUCT:
     827                 :          1 :             reason_string = "type not supported for (out caller-allocates)";
     828                 :          1 :             break;
     829                 :          0 :         case UNREGISTERED_BOXED_WITH_TRANSFER:
     830                 :          0 :             reason_string =
     831                 :            :                 "boxed type with transfer not registered as a GType";
     832                 :          0 :             break;
     833                 :          0 :         case UNREGISTERED_UNION:
     834                 :          0 :             reason_string = "union type not registered as a GType";
     835                 :          0 :             break;
     836                 :          0 :         case UNSUPPORTED_TYPE:
     837                 :          0 :             reason_string = "type not supported by introspection";
     838                 :          0 :             break;
     839                 :          0 :         case LAST_REASON:
     840                 :            :             g_assert_not_reached();
     841                 :            :     }
     842                 :            : 
     843                 :          6 :     gjs_throw(cx,
     844                 :            :               "Function %s() cannot be called: argument '%s' with type %s is "
     845                 :            :               "not introspectable because it has a %s",
     846                 :          6 :               state->display_name().get(), m_arg_name,
     847                 :            :               g_type_tag_to_string(g_type_info_get_tag(&m_type_info)),
     848                 :            :               reason_string);
     849                 :          3 :     return false;
     850                 :            : }
     851                 :            : 
     852                 :            : GJS_JSAPI_RETURN_CONVENTION
     853                 :         97 : bool GenericIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
     854                 :            :                    JS::HandleValue value) {
     855                 :        194 :     return gjs_value_to_g_argument(cx, value, &m_type_info, m_arg_name,
     856                 :         97 :                                    GJS_ARGUMENT_ARGUMENT, m_transfer, flags(),
     857                 :         97 :                                    arg);
     858                 :            : }
     859                 :            : 
     860                 :            : GJS_JSAPI_RETURN_CONVENTION
     861                 :         43 : bool GenericInOut::in(JSContext* cx, GjsFunctionCallState* state,
     862                 :            :                       GIArgument* arg, JS::HandleValue value) {
     863         [ -  + ]:         43 :     if (!GenericIn::in(cx, state, arg, value))
     864                 :          0 :         return false;
     865                 :            : 
     866                 :         43 :     state->out_cvalue(m_arg_pos) = state->inout_original_cvalue(m_arg_pos) =
     867                 :            :         *arg;
     868                 :         43 :     gjs_arg_set(arg, &state->out_cvalue(m_arg_pos));
     869                 :         43 :     return true;
     870                 :            : }
     871                 :            : 
     872                 :            : GJS_JSAPI_RETURN_CONVENTION
     873                 :        333 : bool ExplicitArrayIn::in(JSContext* cx, GjsFunctionCallState* state,
     874                 :            :                          GArgument* arg, JS::HandleValue value) {
     875                 :            :     void* data;
     876                 :            :     size_t length;
     877                 :            : 
     878         [ +  + ]:        333 :     if (m_length_direction != GI_DIRECTION_INOUT &&
     879         [ -  + ]:        318 :         m_length_direction != GI_DIRECTION_IN) {
     880                 :          0 :         gjs_throw(cx, "Using different length argument direction for array %s"
     881                 :            :                   "is not supported for in arrays", m_arg_name);
     882                 :          0 :         return false;
     883                 :            :     }
     884                 :            : 
     885         [ +  + ]:        333 :     if (!gjs_array_to_explicit_array(cx, value, &m_type_info, m_arg_name,
     886                 :        333 :                                      GJS_ARGUMENT_ARGUMENT, m_transfer, flags(),
     887                 :            :                                      &data, &length))
     888                 :          5 :         return false;
     889                 :            : 
     890                 :        328 :     gjs_g_argument_set_array_length(m_tag, &state->in_cvalue(m_length_pos),
     891                 :            :                                     length);
     892                 :        328 :     gjs_arg_set(arg, data);
     893                 :        328 :     return true;
     894                 :            : }
     895                 :            : 
     896                 :            : GJS_JSAPI_RETURN_CONVENTION
     897                 :         15 : bool ExplicitArrayInOut::in(JSContext* cx, GjsFunctionCallState* state,
     898                 :            :                             GIArgument* arg, JS::HandleValue value) {
     899         [ -  + ]:         15 :     if (!ExplicitArrayIn::in(cx, state, arg, value))
     900                 :          0 :         return false;
     901                 :            : 
     902                 :         15 :     uint8_t length_pos = m_length_pos;
     903                 :         15 :     uint8_t ix = m_arg_pos;
     904                 :            : 
     905         [ +  + ]:         15 :     if (!gjs_arg_get<void*>(arg)) {
     906                 :            :         // Special case where we were given JS null to also pass null for
     907                 :            :         // length, and not a pointer to an integer that derefs to 0.
     908                 :          9 :         gjs_arg_unset<void*>(&state->in_cvalue(length_pos));
     909                 :          9 :         gjs_arg_unset<int>(&state->out_cvalue(length_pos));
     910                 :          9 :         gjs_arg_unset<int>(&state->inout_original_cvalue(length_pos));
     911                 :            : 
     912                 :          9 :         gjs_arg_unset<void*>(&state->out_cvalue(ix));
     913                 :          9 :         gjs_arg_unset<void*>(&state->inout_original_cvalue(ix));
     914                 :            :     } else {
     915         [ +  - ]:          6 :         if G_LIKELY (m_length_direction == GI_DIRECTION_INOUT) {
     916                 :          6 :             state->out_cvalue(length_pos) =
     917                 :          6 :                 state->inout_original_cvalue(length_pos) =
     918                 :          6 :                     state->in_cvalue(length_pos);
     919                 :          6 :             gjs_arg_set(&state->in_cvalue(length_pos),
     920                 :          6 :                         &state->out_cvalue(length_pos));
     921                 :            :         }
     922                 :            : 
     923                 :          6 :         state->out_cvalue(ix) = state->inout_original_cvalue(ix) = *arg;
     924                 :          6 :         gjs_arg_set(arg, &state->out_cvalue(ix));
     925                 :            :     }
     926                 :            : 
     927                 :         15 :     return true;
     928                 :            : }
     929                 :            : 
     930                 :            : GJS_JSAPI_RETURN_CONVENTION
     931                 :        255 : bool CallbackIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
     932                 :            :                     JS::HandleValue value) {
     933                 :            :     GjsCallbackTrampoline* trampoline;
     934                 :            :     void* closure;
     935                 :            : 
     936   [ +  +  +  +  :        255 :     if (value.isNull() && m_nullable) {
                   +  + ]
     937                 :          8 :         closure = nullptr;
     938                 :          8 :         trampoline = nullptr;
     939                 :          8 :         m_ffi_closure = nullptr;
     940                 :            :     } else {
     941         [ +  + ]:        247 :         if (JS_TypeOfValue(cx, value) != JSTYPE_FUNCTION) {
     942                 :          2 :             gjs_throw(cx, "Expected function for callback argument %s, got %s",
     943                 :            :                       m_arg_name, JS::InformalValueTypeName(value));
     944                 :          2 :             return false;
     945                 :            :         }
     946                 :            : 
     947                 :        245 :         JS::RootedObject callable(cx, &value.toObject());
     948                 :        245 :         bool is_object_method = !!state->instance_object;
     949                 :        245 :         trampoline = GjsCallbackTrampoline::create(
     950                 :        245 :             cx, callable, m_info, m_scope, is_object_method, false);
     951         [ -  + ]:        245 :         if (!trampoline)
     952                 :          0 :             return false;
     953   [ +  +  -  + ]:        245 :         if (m_scope == GI_SCOPE_TYPE_NOTIFIED && is_object_method) {
     954                 :          0 :             auto* priv = ObjectInstance::for_js(cx, state->instance_object);
     955         [ #  # ]:          0 :             if (!priv) {
     956                 :          0 :                 gjs_throw(cx, "Signal connected to wrong type of object");
     957                 :          0 :                 return false;
     958                 :            :             }
     959                 :            : 
     960         [ #  # ]:          0 :             if (!priv->associate_closure(cx, trampoline))
     961                 :          0 :                 return false;
     962                 :            :         }
     963                 :        245 :         closure = trampoline->closure();
     964                 :        245 :         m_ffi_closure = trampoline->get_ffi_closure();
     965         [ +  - ]:        245 :     }
     966                 :            : 
     967         [ +  + ]:        253 :     if (has_callback_destroy()) {
     968                 :        106 :         GDestroyNotify destroy_notify = nullptr;
     969         [ +  + ]:        106 :         if (trampoline) {
     970                 :            :             /* Adding another reference and a DestroyNotify that unsets it */
     971                 :        102 :             g_closure_ref(trampoline);
     972                 :        102 :             destroy_notify = [](void* data) {
     973                 :         90 :                 g_assert(data);
     974                 :         90 :                 g_closure_unref(static_cast<GClosure*>(data));
     975                 :         90 :             };
     976                 :            :         }
     977                 :        106 :         gjs_arg_set(&state->in_cvalue(m_destroy_pos), destroy_notify);
     978                 :            :     }
     979         [ +  + ]:        253 :     if (has_callback_closure())
     980                 :        229 :         gjs_arg_set(&state->in_cvalue(m_closure_pos), trampoline);
     981                 :            : 
     982   [ +  +  +  + ]:        253 :     if (trampoline && m_scope == GI_SCOPE_TYPE_ASYNC) {
     983                 :            :         // Add an extra reference that will be cleared when garbage collecting
     984                 :            :         // async calls
     985                 :        111 :         g_closure_ref(trampoline);
     986                 :            :     }
     987                 :            : 
     988                 :            :     bool keep_forever =
     989         [ +  + ]:        400 :         !has_callback_destroy() && (
     990                 :            : #if GI_CHECK_VERSION(1, 72, 0)
     991         [ +  - ]:        147 :                                        m_scope == GI_SCOPE_TYPE_FOREVER ||
     992                 :            : #endif
     993         [ +  + ]:        147 :                                        m_scope == GI_SCOPE_TYPE_NOTIFIED);
     994                 :            : 
     995   [ +  +  +  + ]:        253 :     if (trampoline && keep_forever) {
     996                 :          1 :         trampoline->mark_forever();
     997                 :            :     }
     998                 :        253 :     gjs_arg_set(arg, closure);
     999                 :            : 
    1000                 :        253 :     return true;
    1001                 :            : }
    1002                 :            : 
    1003                 :            : GJS_JSAPI_RETURN_CONVENTION
    1004                 :        635 : bool GenericOut::in(JSContext*, GjsFunctionCallState* state, GIArgument* arg,
    1005                 :            :                     JS::HandleValue) {
    1006                 :            :     // Default value in case a broken C function doesn't fill in the pointer
    1007                 :        635 :     return set_out_parameter(state, arg);
    1008                 :            : }
    1009                 :            : 
    1010                 :            : GJS_JSAPI_RETURN_CONVENTION
    1011                 :          9 : bool CallerAllocatesOut::in(JSContext*, GjsFunctionCallState* state,
    1012                 :            :                             GIArgument* arg, JS::HandleValue) {
    1013                 :          9 :     void* blob = g_malloc0(m_allocates_size);
    1014                 :          9 :     gjs_arg_set(arg, blob);
    1015                 :          9 :     gjs_arg_set(&state->out_cvalue(m_arg_pos), blob);
    1016                 :          9 :     return true;
    1017                 :            : }
    1018                 :            : 
    1019                 :            : GJS_JSAPI_RETURN_CONVENTION
    1020                 :          7 : bool NullIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
    1021                 :            :                 JS::HandleValue) {
    1022                 :          7 :     return handle_nullable(cx, arg, m_arg_name);
    1023                 :            : }
    1024                 :            : 
    1025                 :            : GJS_JSAPI_RETURN_CONVENTION
    1026                 :        224 : bool BooleanIn::in(JSContext*, GjsFunctionCallState*, GIArgument* arg,
    1027                 :            :                    JS::HandleValue value) {
    1028                 :        224 :     gjs_arg_set(arg, JS::ToBoolean(value));
    1029                 :        224 :     return true;
    1030                 :            : }
    1031                 :            : 
    1032                 :            : template <typename T>
    1033                 :      24304 : GJS_JSAPI_RETURN_CONVENTION inline static bool gjs_arg_set_from_js_value(
    1034                 :            :     JSContext* cx, const JS::HandleValue& value, GArgument* arg,
    1035                 :            :     Argument* gjs_arg) {
    1036                 :      24304 :     bool out_of_range = false;
    1037                 :            : 
    1038         [ +  + ]:      24304 :     if (!gjs_arg_set_from_js_value<T>(cx, value, arg, &out_of_range)) {
    1039         [ +  + ]:         33 :         if (out_of_range) {
    1040                 :          8 :             gjs_throw(cx, "Argument %s: value is out of range for %s",
    1041                 :            :                       gjs_arg->arg_name(), Gjs::static_type_name<T>());
    1042                 :            :         }
    1043                 :            : 
    1044                 :         33 :         return false;
    1045                 :            :     }
    1046                 :            : 
    1047                 :            :     gjs_debug_marshal(
    1048                 :            :         GJS_DEBUG_GFUNCTION, "%s set to value %s (type %s)",
    1049                 :            :         GjsAutoChar(gjs_argument_display_name(gjs_arg->arg_name(),
    1050                 :            :                                               GJS_ARGUMENT_ARGUMENT))
    1051                 :            :             .get(),
    1052                 :            :         std::to_string(gjs_arg_get<T>(arg)).c_str(),
    1053                 :            :         Gjs::static_type_name<T>());
    1054                 :            : 
    1055                 :      24271 :     return true;
    1056                 :            : }
    1057                 :            : 
    1058                 :            : GJS_JSAPI_RETURN_CONVENTION
    1059                 :      24304 : bool NumericIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
    1060                 :            :                    JS::HandleValue value) {
    1061   [ +  +  +  +  :      24304 :     switch (m_tag) {
          +  +  +  +  +  
                   +  - ]
    1062                 :         30 :         case GI_TYPE_TAG_INT8:
    1063                 :         30 :             return gjs_arg_set_from_js_value<int8_t>(cx, value, arg, this);
    1064                 :         25 :         case GI_TYPE_TAG_UINT8:
    1065                 :         25 :             return gjs_arg_set_from_js_value<uint8_t>(cx, value, arg, this);
    1066                 :         24 :         case GI_TYPE_TAG_INT16:
    1067                 :         24 :             return gjs_arg_set_from_js_value<int16_t>(cx, value, arg, this);
    1068                 :         16 :         case GI_TYPE_TAG_UINT16:
    1069                 :         16 :             return gjs_arg_set_from_js_value<uint16_t>(cx, value, arg, this);
    1070                 :      11615 :         case GI_TYPE_TAG_INT32:
    1071                 :      11615 :             return gjs_arg_set_from_js_value<int32_t>(cx, value, arg, this);
    1072                 :         24 :         case GI_TYPE_TAG_DOUBLE:
    1073                 :         24 :             return gjs_arg_set_from_js_value<double>(cx, value, arg, this);
    1074                 :         15 :         case GI_TYPE_TAG_FLOAT:
    1075                 :         15 :             return gjs_arg_set_from_js_value<float>(cx, value, arg, this);
    1076                 :        116 :         case GI_TYPE_TAG_INT64:
    1077                 :        116 :             return gjs_arg_set_from_js_value<int64_t>(cx, value, arg, this);
    1078                 :       1221 :         case GI_TYPE_TAG_UINT64:
    1079                 :       1221 :             return gjs_arg_set_from_js_value<uint64_t>(cx, value, arg, this);
    1080                 :      11218 :         case GI_TYPE_TAG_UINT32:
    1081                 :      11218 :             return gjs_arg_set_from_js_value<uint32_t>(cx, value, arg, this);
    1082                 :          0 :         default:
    1083                 :            :             g_assert_not_reached();
    1084                 :            :     }
    1085                 :            : }
    1086                 :            : 
    1087                 :            : GJS_JSAPI_RETURN_CONVENTION
    1088                 :          3 : bool UnicharIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
    1089                 :            :                    JS::HandleValue value) {
    1090         [ -  + ]:          3 :     if (!value.isString())
    1091                 :          0 :         return report_typeof_mismatch(cx, m_arg_name, value,
    1092                 :          0 :                                       ExpectedType::STRING);
    1093                 :            : 
    1094                 :          3 :     return gjs_unichar_from_string(cx, value, &gjs_arg_member<char32_t>(arg));
    1095                 :            : }
    1096                 :            : 
    1097                 :            : GJS_JSAPI_RETURN_CONVENTION
    1098                 :        462 : bool GTypeIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
    1099                 :            :                  JS::HandleValue value) {
    1100         [ -  + ]:        462 :     if (value.isNull())
    1101                 :          0 :         return report_invalid_null(cx, m_arg_name);
    1102         [ -  + ]:        462 :     if (!value.isObject())
    1103                 :          0 :         return report_typeof_mismatch(cx, m_arg_name, value,
    1104                 :          0 :                                       ExpectedType::OBJECT);
    1105                 :            : 
    1106                 :        462 :     JS::RootedObject gtype_obj(cx, &value.toObject());
    1107                 :        462 :     return gjs_gtype_get_actual_gtype(
    1108                 :        924 :         cx, gtype_obj, &gjs_arg_member<GType, GI_TYPE_TAG_GTYPE>(arg));
    1109                 :        462 : }
    1110                 :            : 
    1111                 :            : // Common code for most types that are pointers on the C side
    1112                 :      11468 : bool Nullable::handle_nullable(JSContext* cx, GIArgument* arg,
    1113                 :            :                                const char* arg_name) {
    1114         [ +  + ]:      11468 :     if (!m_nullable)
    1115                 :          2 :         return report_invalid_null(cx, arg_name);
    1116                 :      11466 :     gjs_arg_unset<void*>(arg);
    1117                 :      11466 :     return true;
    1118                 :            : }
    1119                 :            : 
    1120                 :            : GJS_JSAPI_RETURN_CONVENTION
    1121                 :       4755 : bool StringInTransferNone::in(JSContext* cx, GjsFunctionCallState* state,
    1122                 :            :                               GIArgument* arg, JS::HandleValue value) {
    1123         [ +  + ]:       4755 :     if (value.isNull())
    1124                 :         30 :         return NullableIn::in(cx, state, arg, value);
    1125                 :            : 
    1126         [ -  + ]:       4725 :     if (!value.isString())
    1127                 :          0 :         return report_typeof_mismatch(cx, m_arg_name, value,
    1128                 :          0 :                                       ExpectedType::STRING);
    1129                 :            : 
    1130         [ +  + ]:       4725 :     if (m_filename) {
    1131                 :        163 :         GjsAutoChar str;
    1132         [ -  + ]:        163 :         if (!gjs_string_to_filename(cx, value, &str))
    1133                 :          0 :             return false;
    1134                 :        163 :         gjs_arg_set(arg, str.release());
    1135                 :        163 :         return true;
    1136                 :        163 :     }
    1137                 :            : 
    1138                 :       4562 :     JS::UniqueChars str = gjs_string_to_utf8(cx, value);
    1139         [ -  + ]:       4562 :     if (!str)
    1140                 :          0 :         return false;
    1141                 :       9124 :     gjs_arg_set(arg, g_strdup(str.get()));
    1142                 :       4562 :     return true;
    1143                 :       4562 : }
    1144                 :            : 
    1145                 :            : GJS_JSAPI_RETURN_CONVENTION
    1146                 :         21 : bool EnumIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
    1147                 :            :                 JS::HandleValue value) {
    1148                 :            :     int64_t number;
    1149         [ -  + ]:         21 :     if (!Gjs::js_value_to_c(cx, value, &number))
    1150                 :          0 :         return false;
    1151                 :            : 
    1152                 :            :     // Unpack the values from their uint32_t bitfield. See note in
    1153                 :            :     // Enum::Enum().
    1154                 :            :     int64_t min, max;
    1155         [ +  + ]:         21 :     if (m_unsigned) {
    1156                 :          2 :         min = m_min;
    1157                 :          2 :         max = m_max;
    1158                 :            :     } else {
    1159                 :         19 :         min = static_cast<int32_t>(m_min);
    1160                 :         19 :         max = static_cast<int32_t>(m_max);
    1161                 :            :     }
    1162                 :            : 
    1163   [ +  -  -  + ]:         21 :     if (number > max || number < min) {
    1164                 :          0 :         gjs_throw(cx, "%" PRId64 " is not a valid value for enum argument %s",
    1165                 :            :                   number, m_arg_name);
    1166                 :          0 :         return false;
    1167                 :            :     }
    1168                 :            : 
    1169         [ +  + ]:         21 :     if (m_unsigned)
    1170                 :          2 :         gjs_arg_set<unsigned, GI_TYPE_TAG_INTERFACE>(arg, number);
    1171                 :            :     else
    1172                 :         19 :         gjs_arg_set<int, GI_TYPE_TAG_INTERFACE>(arg, number);
    1173                 :            : 
    1174                 :         21 :     return true;
    1175                 :            : }
    1176                 :            : 
    1177                 :            : GJS_JSAPI_RETURN_CONVENTION
    1178                 :        433 : bool FlagsIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
    1179                 :            :                  JS::HandleValue value) {
    1180                 :            :     int64_t number;
    1181         [ -  + ]:        433 :     if (!Gjs::js_value_to_c(cx, value, &number))
    1182                 :          0 :         return false;
    1183                 :            : 
    1184         [ -  + ]:        433 :     if ((uint64_t(number) & m_mask) != uint64_t(number)) {
    1185                 :          0 :         gjs_throw(cx,
    1186                 :            :                   "0x%" G_GINT64_MODIFIER
    1187                 :            :                   "x is not a valid value for flags argument %s",
    1188                 :            :                   number, m_arg_name);
    1189                 :          0 :         return false;
    1190                 :            :     }
    1191                 :            : 
    1192                 :            :     // We cast to unsigned because that's what makes sense, but then we
    1193                 :            :     // put it in the v_int slot because that's what we use to unmarshal
    1194                 :            :     // flags types at the moment.
    1195                 :        433 :     gjs_arg_set<int, GI_TYPE_TAG_INTERFACE>(arg, static_cast<unsigned>(number));
    1196                 :        433 :     return true;
    1197                 :            : }
    1198                 :            : 
    1199                 :            : GJS_JSAPI_RETURN_CONVENTION
    1200                 :          2 : bool ForeignStructInstanceIn::in(JSContext* cx, GjsFunctionCallState*,
    1201                 :            :                                  GIArgument* arg, JS::HandleValue value) {
    1202                 :          2 :     return gjs_struct_foreign_convert_to_g_argument(
    1203                 :          2 :         cx, value, m_info, m_arg_name, GJS_ARGUMENT_ARGUMENT, m_transfer,
    1204                 :          4 :         flags(), arg);
    1205                 :            : }
    1206                 :            : 
    1207                 :            : GJS_JSAPI_RETURN_CONVENTION
    1208                 :        111 : bool GValueIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
    1209                 :            :                   JS::HandleValue value) {
    1210         [ +  + ]:        111 :     if (value.isObject()) {
    1211                 :         87 :         JS::RootedObject obj(cx, &value.toObject());
    1212                 :            :         GType gtype;
    1213                 :            : 
    1214         [ -  + ]:         87 :         if (!gjs_gtype_get_actual_gtype(cx, obj, &gtype))
    1215                 :          0 :             return false;
    1216                 :            : 
    1217         [ +  + ]:         87 :         if (gtype == G_TYPE_VALUE) {
    1218                 :         60 :             gjs_arg_set(arg, BoxedBase::to_c_ptr<GValue>(cx, obj));
    1219                 :         60 :             state->ignore_release.insert(arg);
    1220                 :         60 :             return true;
    1221                 :            :         }
    1222         [ +  + ]:         87 :     }
    1223                 :            : 
    1224                 :         51 :     Gjs::AutoGValue gvalue;
    1225                 :            : 
    1226         [ -  + ]:         51 :     if (!gjs_value_to_g_value(cx, value, &gvalue))
    1227                 :          0 :         return false;
    1228                 :            : 
    1229                 :         51 :     gjs_arg_set(arg, g_boxed_copy(G_TYPE_VALUE, &gvalue));
    1230                 :            : 
    1231                 :         51 :     return true;
    1232                 :         51 : }
    1233                 :            : 
    1234                 :            : GJS_JSAPI_RETURN_CONVENTION
    1235                 :      55946 : bool BoxedInTransferNone::in(JSContext* cx, GjsFunctionCallState* state,
    1236                 :            :                              GIArgument* arg, JS::HandleValue value) {
    1237         [ +  + ]:      55946 :     if (value.isNull())
    1238                 :      11155 :         return NullableIn::in(cx, state, arg, value);
    1239                 :            : 
    1240                 :      44791 :     GType gtype = RegisteredType::gtype();
    1241                 :            : 
    1242         [ -  + ]:      44791 :     if (!value.isObject())
    1243                 :          0 :         return report_gtype_mismatch(cx, m_arg_name, value, gtype);
    1244                 :            : 
    1245                 :      44791 :     JS::RootedObject object(cx, &value.toObject());
    1246         [ +  + ]:      44791 :     if (gtype == G_TYPE_ERROR) {
    1247                 :         12 :         return ErrorBase::transfer_to_gi_argument(cx, object, arg,
    1248                 :         12 :                                                   GI_DIRECTION_IN, m_transfer);
    1249                 :            :     }
    1250                 :            : 
    1251                 :      89570 :     return BoxedBase::transfer_to_gi_argument(cx, object, arg, GI_DIRECTION_IN,
    1252                 :      89570 :                                               m_transfer, gtype, info());
    1253                 :      44791 : }
    1254                 :            : 
    1255                 :            : // Unions include ClutterEvent and GdkEvent, which occur fairly often in an
    1256                 :            : // interactive application, so they're worth a special case in a different
    1257                 :            : // virtual function.
    1258                 :            : GJS_JSAPI_RETURN_CONVENTION
    1259                 :          2 : bool UnionIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
    1260                 :            :                  JS::HandleValue value) {
    1261         [ -  + ]:          2 :     if (value.isNull())
    1262                 :          0 :         return NullableIn::in(cx, state, arg, value);
    1263                 :            : 
    1264                 :          2 :     GType gtype = RegisteredType::gtype();
    1265                 :            : 
    1266         [ -  + ]:          2 :     if (!value.isObject())
    1267                 :          0 :         return report_gtype_mismatch(cx, m_arg_name, value, gtype);
    1268                 :            : 
    1269                 :          2 :     JS::RootedObject object(cx, &value.toObject());
    1270                 :          2 :     return UnionBase::transfer_to_gi_argument(cx, object, arg, GI_DIRECTION_IN,
    1271                 :          4 :                                               m_transfer, gtype);
    1272                 :          2 : }
    1273                 :            : 
    1274                 :            : GJS_JSAPI_RETURN_CONVENTION
    1275                 :      11031 : bool GClosureInTransferNone::in(JSContext* cx, GjsFunctionCallState* state,
    1276                 :            :                                 GIArgument* arg, JS::HandleValue value) {
    1277         [ -  + ]:      11031 :     if (value.isNull())
    1278                 :          0 :         return NullableIn::in(cx, state, arg, value);
    1279                 :            : 
    1280         [ -  + ]:      11031 :     if (!(JS_TypeOfValue(cx, value) == JSTYPE_FUNCTION))
    1281                 :          0 :         return report_typeof_mismatch(cx, m_arg_name, value,
    1282                 :          0 :                                       ExpectedType::FUNCTION);
    1283                 :            : 
    1284                 :      11031 :     JS::RootedObject callable(cx, &value.toObject());
    1285                 :      11031 :     GClosure* closure = Gjs::Closure::create_marshaled(cx, callable, "boxed");
    1286                 :      11031 :     gjs_arg_set(arg, closure);
    1287                 :      11031 :     g_closure_ref(closure);
    1288                 :      11031 :     g_closure_sink(closure);
    1289                 :            : 
    1290                 :      11031 :     return true;
    1291                 :      11031 : }
    1292                 :            : 
    1293                 :            : GJS_JSAPI_RETURN_CONVENTION
    1294                 :         68 : bool GBytesIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
    1295                 :            :                   JS::HandleValue value) {
    1296         [ -  + ]:         68 :     if (value.isNull())
    1297                 :          0 :         return NullableIn::in(cx, state, arg, value);
    1298                 :            : 
    1299         [ -  + ]:         68 :     if (!value.isObject())
    1300                 :          0 :         return report_gtype_mismatch(cx, m_arg_name, value, G_TYPE_BYTES);
    1301                 :            : 
    1302                 :         68 :     JS::RootedObject object(cx, &value.toObject());
    1303         [ +  + ]:         68 :     if (JS_IsUint8Array(object)) {
    1304                 :         17 :         state->ignore_release.insert(arg);
    1305                 :         17 :         gjs_arg_set(arg, gjs_byte_array_get_bytes(object));
    1306                 :         17 :         return true;
    1307                 :            :     }
    1308                 :            : 
    1309                 :            :     // The bytearray path is taking an extra ref irrespective of transfer
    1310                 :            :     // ownership, so we need to do the same here.
    1311                 :        102 :     return BoxedBase::transfer_to_gi_argument(
    1312                 :         51 :         cx, object, arg, GI_DIRECTION_IN, GI_TRANSFER_EVERYTHING, G_TYPE_BYTES);
    1313                 :         68 : }
    1314                 :            : 
    1315                 :            : GJS_JSAPI_RETURN_CONVENTION
    1316                 :         35 : bool GBytesIn::release(JSContext* cx, GjsFunctionCallState* state,
    1317                 :            :                        GIArgument* in_arg, GIArgument* out_arg) {
    1318         [ -  + ]:         35 :     if (state->ignore_release.erase(in_arg))
    1319                 :          0 :         return BoxedIn::release(cx, state, in_arg, out_arg);
    1320                 :            : 
    1321                 :         35 :     return BoxedInTransferNone::release(cx, state, in_arg, out_arg);
    1322                 :            : }
    1323                 :            : 
    1324                 :            : GJS_JSAPI_RETURN_CONVENTION
    1325                 :          4 : bool InterfaceIn::in(JSContext* cx, GjsFunctionCallState* state,
    1326                 :            :                      GIArgument* arg, JS::HandleValue value) {
    1327         [ -  + ]:          4 :     if (value.isNull())
    1328                 :          0 :         return NullableIn::in(cx, state, arg, value);
    1329                 :            : 
    1330                 :          4 :     GType gtype = RegisteredType::gtype();
    1331                 :            : 
    1332         [ -  + ]:          4 :     if (!value.isObject())
    1333                 :          0 :         return report_gtype_mismatch(cx, m_arg_name, value, gtype);
    1334                 :            : 
    1335                 :          4 :     JS::RootedObject object(cx, &value.toObject());
    1336                 :            : 
    1337                 :            :     // Could be a GObject interface that's missing a prerequisite,
    1338                 :            :     // or could be a fundamental
    1339         [ +  - ]:          4 :     if (ObjectBase::typecheck(cx, object, nullptr, gtype,
    1340                 :            :                               GjsTypecheckNoThrow())) {
    1341                 :          8 :         return ObjectBase::transfer_to_gi_argument(
    1342                 :          8 :             cx, object, arg, GI_DIRECTION_IN, m_transfer, gtype);
    1343                 :            :     }
    1344                 :            : 
    1345                 :            :     // If this typecheck fails, then it's neither an object nor a
    1346                 :            :     // fundamental
    1347                 :          0 :     return FundamentalBase::transfer_to_gi_argument(
    1348                 :          0 :         cx, object, arg, GI_DIRECTION_IN, m_transfer, gtype);
    1349                 :          4 : }
    1350                 :            : 
    1351                 :            : GJS_JSAPI_RETURN_CONVENTION
    1352                 :       2037 : bool ObjectIn::in(JSContext* cx, GjsFunctionCallState* state, GIArgument* arg,
    1353                 :            :                   JS::HandleValue value) {
    1354         [ +  + ]:       2037 :     if (value.isNull())
    1355                 :        276 :         return NullableIn::in(cx, state, arg, value);
    1356                 :            : 
    1357                 :       1761 :     GType gtype = RegisteredType::gtype();
    1358                 :            : 
    1359         [ -  + ]:       1761 :     if (!value.isObject())
    1360                 :          0 :         return report_gtype_mismatch(cx, m_arg_name, value, gtype);
    1361                 :            : 
    1362                 :       1761 :     JS::RootedObject object(cx, &value.toObject());
    1363                 :       1761 :     return ObjectBase::transfer_to_gi_argument(cx, object, arg, GI_DIRECTION_IN,
    1364                 :       3522 :                                                m_transfer, gtype);
    1365                 :       1761 : }
    1366                 :            : 
    1367                 :            : GJS_JSAPI_RETURN_CONVENTION
    1368                 :          3 : bool FundamentalIn::in(JSContext* cx, GjsFunctionCallState* state,
    1369                 :            :                        GIArgument* arg, JS::HandleValue value) {
    1370         [ -  + ]:          3 :     if (value.isNull())
    1371                 :          0 :         return NullableIn::in(cx, state, arg, value);
    1372                 :            : 
    1373                 :          3 :     GType gtype = RegisteredType::gtype();
    1374                 :            : 
    1375         [ -  + ]:          3 :     if (!value.isObject())
    1376                 :          0 :         return report_gtype_mismatch(cx, m_arg_name, value, gtype);
    1377                 :            : 
    1378                 :          3 :     JS::RootedObject object(cx, &value.toObject());
    1379                 :          3 :     return FundamentalBase::transfer_to_gi_argument(
    1380                 :          6 :         cx, object, arg, GI_DIRECTION_IN, m_transfer, gtype);
    1381                 :          3 : }
    1382                 :            : 
    1383                 :            : GJS_JSAPI_RETURN_CONVENTION
    1384                 :         90 : bool GTypeStructInstanceIn::in(JSContext* cx, GjsFunctionCallState*,
    1385                 :            :                                GIArgument* arg, JS::HandleValue value) {
    1386                 :            :     // Instance parameter is never nullable
    1387         [ -  + ]:         90 :     if (!value.isObject())
    1388                 :          0 :         return report_typeof_mismatch(cx, m_arg_name, value,
    1389                 :          0 :                                       ExpectedType::OBJECT);
    1390                 :            : 
    1391                 :         90 :     JS::RootedObject obj(cx, &value.toObject());
    1392                 :            :     GType actual_gtype;
    1393         [ -  + ]:         90 :     if (!gjs_gtype_get_actual_gtype(cx, obj, &actual_gtype))
    1394                 :          0 :         return false;
    1395                 :            : 
    1396         [ -  + ]:         90 :     if (actual_gtype == G_TYPE_NONE) {
    1397                 :          0 :         gjs_throw(cx, "Invalid GType class passed for instance parameter");
    1398                 :          0 :         return false;
    1399                 :            :     }
    1400                 :            : 
    1401                 :            :     // We use peek here to simplify reference counting (we just ignore transfer
    1402                 :            :     // annotation, as GType classes are never really freed.) We know that the
    1403                 :            :     // GType class is referenced at least once when the JS constructor is
    1404                 :            :     // initialized.
    1405   [ +  -  -  +  :         90 :     if (g_type_is_a(actual_gtype, G_TYPE_INTERFACE))
                   -  + ]
    1406                 :          0 :         gjs_arg_set(arg, g_type_default_interface_peek(actual_gtype));
    1407                 :            :     else
    1408                 :         90 :         gjs_arg_set(arg, g_type_class_peek(actual_gtype));
    1409                 :            : 
    1410                 :         90 :     return true;
    1411                 :         90 : }
    1412                 :            : 
    1413                 :            : GJS_JSAPI_RETURN_CONVENTION
    1414                 :        161 : bool ParamInstanceIn::in(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
    1415                 :            :                          JS::HandleValue value) {
    1416                 :            :     // Instance parameter is never nullable
    1417         [ -  + ]:        161 :     if (!value.isObject())
    1418                 :          0 :         return report_typeof_mismatch(cx, m_arg_name, value,
    1419                 :          0 :                                       ExpectedType::OBJECT);
    1420                 :            : 
    1421                 :        161 :     JS::RootedObject obj(cx, &value.toObject());
    1422         [ -  + ]:        161 :     if (!gjs_typecheck_param(cx, obj, G_TYPE_PARAM, true))
    1423                 :          0 :         return false;
    1424                 :        161 :     gjs_arg_set(arg, gjs_g_param_from_param(cx, obj));
    1425                 :            : 
    1426         [ -  + ]:        161 :     if (m_transfer == GI_TRANSFER_EVERYTHING)
    1427                 :          0 :         g_param_spec_ref(gjs_arg_get<GParamSpec*>(arg));
    1428                 :            : 
    1429                 :        161 :     return true;
    1430                 :        161 : }
    1431                 :            : 
    1432                 :            : GJS_JSAPI_RETURN_CONVENTION
    1433                 :      34542 : bool GenericInOut::out(JSContext* cx, GjsFunctionCallState*, GIArgument* arg,
    1434                 :            :                        JS::MutableHandleValue value) {
    1435                 :      34542 :     return gjs_value_from_g_argument(cx, value, &m_type_info, arg, true);
    1436                 :            : }
    1437                 :            : 
    1438                 :            : GJS_JSAPI_RETURN_CONVENTION
    1439                 :         36 : bool ExplicitArrayInOut::out(JSContext* cx, GjsFunctionCallState* state,
    1440                 :            :                              GIArgument* arg, JS::MutableHandleValue value) {
    1441                 :            :     GIArgument* length_arg;
    1442                 :            : 
    1443         [ +  - ]:         36 :     if (m_length_direction != GI_DIRECTION_IN)
    1444                 :         36 :         length_arg = &(state->out_cvalue(m_length_pos));
    1445                 :            :     else
    1446                 :          0 :         length_arg = &(state->in_cvalue(m_length_pos));
    1447                 :            : 
    1448                 :         36 :     size_t length = gjs_g_argument_get_array_length(m_tag, length_arg);
    1449                 :            : 
    1450                 :         36 :     return gjs_value_from_explicit_array(cx, value, &m_type_info, m_transfer,
    1451                 :         36 :                                          arg, length);
    1452                 :            : }
    1453                 :            : 
    1454                 :            : GJS_JSAPI_RETURN_CONVENTION
    1455                 :         35 : bool GenericIn::release(JSContext* cx, GjsFunctionCallState* state,
    1456                 :            :                         GIArgument* in_arg, GIArgument*) {
    1457                 :            :     GITransfer transfer =
    1458         [ +  - ]:         35 :         state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
    1459                 :         35 :     return gjs_g_argument_release_in_arg(cx, transfer, &m_type_info, in_arg);
    1460                 :            : }
    1461                 :            : 
    1462                 :            : GJS_JSAPI_RETURN_CONVENTION
    1463                 :      34490 : bool GenericOut::release(JSContext* cx, GjsFunctionCallState*,
    1464                 :            :                          GIArgument* in_arg [[maybe_unused]],
    1465                 :            :                          GIArgument* out_arg) {
    1466                 :      34490 :     return gjs_g_argument_release(cx, m_transfer, &m_type_info, out_arg);
    1467                 :            : }
    1468                 :            : 
    1469                 :            : GJS_JSAPI_RETURN_CONVENTION
    1470                 :         40 : bool GenericInOut::release(JSContext* cx, GjsFunctionCallState* state,
    1471                 :            :                            GIArgument*, GIArgument* out_arg) {
    1472                 :            :     // For inout, transfer refers to what we get back from the function; for
    1473                 :            :     // the temporary C value we allocated, clearly we're responsible for
    1474                 :            :     // freeing it.
    1475                 :            : 
    1476                 :         40 :     GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
    1477         [ -  + ]:         40 :     if (!gjs_g_argument_release_in_arg(cx, GI_TRANSFER_NOTHING, &m_type_info,
    1478                 :            :                                        original_out_arg))
    1479                 :          0 :         return false;
    1480                 :            : 
    1481                 :         40 :     return gjs_g_argument_release(cx, m_transfer, &m_type_info, out_arg);
    1482                 :            : }
    1483                 :            : 
    1484                 :            : GJS_JSAPI_RETURN_CONVENTION
    1485                 :         21 : bool ExplicitArrayOut::release(JSContext* cx, GjsFunctionCallState* state,
    1486                 :            :                                GIArgument* in_arg [[maybe_unused]],
    1487                 :            :                                GIArgument* out_arg) {
    1488                 :         21 :     GIArgument* length_arg = &state->out_cvalue(m_length_pos);
    1489                 :         21 :     size_t length = gjs_g_argument_get_array_length(m_tag, length_arg);
    1490                 :            : 
    1491                 :         21 :     return gjs_g_argument_release_out_array(cx, m_transfer, &m_type_info,
    1492                 :         21 :                                             length, out_arg);
    1493                 :            : }
    1494                 :            : 
    1495                 :            : GJS_JSAPI_RETURN_CONVENTION
    1496                 :        313 : bool ExplicitArrayIn::release(JSContext* cx, GjsFunctionCallState* state,
    1497                 :            :                               GIArgument* in_arg,
    1498                 :            :                               GIArgument* out_arg [[maybe_unused]]) {
    1499                 :        313 :     GIArgument* length_arg = &state->in_cvalue(m_length_pos);
    1500                 :        313 :     size_t length = gjs_g_argument_get_array_length(m_tag, length_arg);
    1501                 :            : 
    1502                 :            :     GITransfer transfer =
    1503         [ +  + ]:        313 :         state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
    1504                 :            : 
    1505                 :        313 :     return gjs_g_argument_release_in_array(cx, transfer, &m_type_info, length,
    1506                 :        313 :                                            in_arg);
    1507                 :            : }
    1508                 :            : 
    1509                 :            : GJS_JSAPI_RETURN_CONVENTION
    1510                 :         15 : bool ExplicitArrayInOut::release(JSContext* cx, GjsFunctionCallState* state,
    1511                 :            :                                  GIArgument* in_arg [[maybe_unused]],
    1512                 :            :                                  GIArgument* out_arg) {
    1513                 :         15 :     GIArgument* length_arg = &state->out_cvalue(m_length_pos);
    1514                 :         15 :     size_t length = gjs_g_argument_get_array_length(m_tag, length_arg);
    1515                 :            : 
    1516                 :            :     // For inout, transfer refers to what we get back from the function; for
    1517                 :            :     // the temporary C value we allocated, clearly we're responsible for
    1518                 :            :     // freeing it.
    1519                 :            : 
    1520                 :         15 :     GIArgument* original_out_arg = &state->inout_original_cvalue(m_arg_pos);
    1521                 :            :     // Due to https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/192
    1522                 :            :     // Here we've to guess what to do, but in general is "better" to leak than
    1523                 :            :     // crash, so let's assume that in/out transfer is matching.
    1524         [ +  + ]:         15 :     if (gjs_arg_get<void*>(original_out_arg) != gjs_arg_get<void*>(out_arg)) {
    1525                 :            :         GITransfer transfer =
    1526         [ +  - ]:          4 :             state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
    1527         [ -  + ]:          4 :         if (!gjs_g_argument_release_in_array(cx, transfer, &m_type_info, length,
    1528                 :            :                                              original_out_arg))
    1529                 :          0 :             return false;
    1530                 :            :     }
    1531                 :            : 
    1532                 :            :     GITransfer transfer =
    1533         [ +  - ]:         15 :         state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
    1534                 :         15 :     return gjs_g_argument_release_out_array(cx, transfer, &m_type_info,
    1535                 :         15 :                                             length, out_arg);
    1536                 :            : }
    1537                 :            : 
    1538                 :            : GJS_JSAPI_RETURN_CONVENTION
    1539                 :          6 : bool CallerAllocatesOut::release(JSContext*, GjsFunctionCallState*,
    1540                 :            :                                  GIArgument* in_arg,
    1541                 :            :                                  GIArgument* out_arg [[maybe_unused]]) {
    1542                 :          6 :     g_free(gjs_arg_steal<void*>(in_arg));
    1543                 :          6 :     return true;
    1544                 :            : }
    1545                 :            : 
    1546                 :            : GJS_JSAPI_RETURN_CONVENTION
    1547                 :          3 : bool BoxedCallerAllocatesOut::release(JSContext*, GjsFunctionCallState*,
    1548                 :            :                                       GIArgument* in_arg, GIArgument*) {
    1549                 :          3 :     g_boxed_free(m_gtype, gjs_arg_steal<void*>(in_arg));
    1550                 :          3 :     return true;
    1551                 :            : }
    1552                 :            : 
    1553                 :            : GJS_JSAPI_RETURN_CONVENTION
    1554                 :        253 : bool CallbackIn::release(JSContext*, GjsFunctionCallState*, GIArgument* in_arg,
    1555                 :            :                          GIArgument* out_arg [[maybe_unused]]) {
    1556                 :        253 :     ffi_closure *closure = m_ffi_closure;
    1557         [ +  + ]:        253 :     if (!closure)
    1558                 :          8 :         return true;
    1559                 :            : 
    1560                 :        245 :     g_closure_unref(static_cast<GClosure*>(closure->user_data));
    1561                 :            :     // CallbackTrampolines are refcounted because for notified/async closures
    1562                 :            :     // it is possible to destroy it while in call, and therefore we cannot
    1563                 :            :     // check its scope at this point
    1564                 :        245 :     gjs_arg_unset<void*>(in_arg);
    1565                 :        245 :     return true;
    1566                 :            : }
    1567                 :            : 
    1568                 :            : GJS_JSAPI_RETURN_CONVENTION
    1569                 :       4754 : bool StringInTransferNone::release(JSContext*, GjsFunctionCallState*,
    1570                 :            :                                    GIArgument* in_arg,
    1571                 :            :                                    GIArgument* out_arg [[maybe_unused]]) {
    1572                 :       4754 :     g_free(gjs_arg_get<void*>(in_arg));
    1573                 :       4754 :     return true;
    1574                 :            : }
    1575                 :            : 
    1576                 :            : GJS_JSAPI_RETURN_CONVENTION
    1577                 :          2 : bool ForeignStructIn::release(JSContext* cx, GjsFunctionCallState* state,
    1578                 :            :                               GIArgument* in_arg,
    1579                 :            :                               GIArgument* out_arg [[maybe_unused]]) {
    1580                 :            :     GITransfer transfer =
    1581         [ +  - ]:          2 :         state->call_completed() ? m_transfer : GI_TRANSFER_NOTHING;
    1582                 :            : 
    1583         [ +  - ]:          2 :     if (transfer == GI_TRANSFER_NOTHING)
    1584                 :          2 :         return gjs_struct_foreign_release_g_argument(cx, m_transfer, m_info,
    1585                 :          2 :                                                      in_arg);
    1586                 :            : 
    1587                 :          0 :     return true;
    1588                 :            : }
    1589                 :            : 
    1590                 :            : GJS_JSAPI_RETURN_CONVENTION
    1591                 :      11150 : bool BoxedInTransferNone::release(JSContext*, GjsFunctionCallState*,
    1592                 :            :                                   GIArgument* in_arg,
    1593                 :            :                                   GIArgument* out_arg [[maybe_unused]]) {
    1594                 :      11150 :     GType gtype = RegisteredType::gtype();
    1595                 :      11150 :     g_assert(g_type_is_a(gtype, G_TYPE_BOXED));
    1596                 :            : 
    1597         [ -  + ]:      11150 :     if (!gjs_arg_get<void*>(in_arg))
    1598                 :          0 :         return true;
    1599                 :            : 
    1600                 :      11150 :     g_boxed_free(gtype, gjs_arg_get<void*>(in_arg));
    1601                 :      11150 :     return true;
    1602                 :            : }
    1603                 :            : 
    1604                 :            : GJS_JSAPI_RETURN_CONVENTION
    1605                 :        111 : bool GValueInTransferNone::release(JSContext* cx, GjsFunctionCallState* state,
    1606                 :            :                                    GIArgument* in_arg, GIArgument* out_arg) {
    1607         [ +  + ]:        111 :     if (state->ignore_release.erase(in_arg))
    1608                 :         60 :         return true;
    1609                 :            : 
    1610                 :         51 :     return BoxedInTransferNone::release(cx, state, in_arg, out_arg);
    1611                 :            : }
    1612                 :            : 
    1613                 :            : }  // namespace Arg
    1614                 :            : 
    1615                 :          0 : bool Argument::invalid(JSContext* cx, const char* func) const {
    1616         [ #  # ]:          0 :     gjs_throw(cx, "%s not implemented", func ? func : "Function");
    1617                 :          0 :     return false;
    1618                 :            : }
    1619                 :            : 
    1620                 :          0 : bool Argument::in(JSContext* cx, GjsFunctionCallState*, GIArgument*,
    1621                 :            :                   JS::HandleValue) {
    1622                 :          0 :     return invalid(cx, G_STRFUNC);
    1623                 :            : }
    1624                 :            : 
    1625                 :          0 : bool Argument::out(JSContext* cx, GjsFunctionCallState*, GIArgument*,
    1626                 :            :                    JS::MutableHandleValue) {
    1627                 :          0 :     return invalid(cx, G_STRFUNC);
    1628                 :            : }
    1629                 :            : 
    1630                 :          0 : bool Argument::release(JSContext*, GjsFunctionCallState*, GIArgument*,
    1631                 :            :                        GIArgument*) {
    1632                 :          0 :     return true;
    1633                 :            : }
    1634                 :            : 
    1635                 :            : // This is a trick to print out the sizes of the structs at compile time, in
    1636                 :            : // an error message:
    1637                 :            : // template <int s> struct Measure;
    1638                 :            : // Measure<sizeof(Arg::)> arg_size;
    1639                 :            : 
    1640                 :            : #ifdef GJS_DO_ARGUMENTS_SIZE_CHECK
    1641                 :            : template <typename T>
    1642                 :            : constexpr size_t argument_maximum_size() {
    1643                 :            :     if constexpr (std::is_same_v<T, Arg::NumericIn>)
    1644                 :            :         return 24;
    1645                 :            :     if constexpr (std::is_same_v<T, Arg::ObjectIn> ||
    1646                 :            :                   std::is_same_v<T, Arg::BoxedIn>)
    1647                 :            :         return 40;
    1648                 :            :     else
    1649                 :            :         return 120;
    1650                 :            : }
    1651                 :            : #endif
    1652                 :            : 
    1653                 :            : template <typename T, Arg::Kind ArgKind, typename... Args>
    1654                 :      28497 : GjsAutoCppPointer<T> Argument::make(uint8_t index, const char* name,
    1655                 :            :                                     GITypeInfo* type_info, GITransfer transfer,
    1656                 :            :                                     GjsArgumentFlags flags, Args&&... args) {
    1657                 :            : #ifdef GJS_DO_ARGUMENTS_SIZE_CHECK
    1658                 :            :     static_assert(
    1659                 :            :         sizeof(T) <= argument_maximum_size<T>(),
    1660                 :            :         "Think very hard before increasing the size of Gjs::Arguments. "
    1661                 :            :         "One is allocated for every argument to every introspected function.");
    1662                 :            : #endif
    1663                 :      28497 :     auto arg = new T(args...);
    1664                 :            : 
    1665                 :            :     if constexpr (ArgKind == Arg::Kind::INSTANCE) {
    1666                 :       2659 :         g_assert(index == Argument::ABSENT &&
    1667                 :            :                  "index was ignored in INSTANCE parameter");
    1668                 :       2659 :         g_assert(name == nullptr && "name was ignored in INSTANCE parameter");
    1669                 :       2659 :         arg->set_instance_parameter();
    1670                 :            :     } else if constexpr (ArgKind == Arg::Kind::RETURN_VALUE) {
    1671                 :       7503 :         g_assert(index == Argument::ABSENT &&
    1672                 :            :                  "index was ignored in RETURN_VALUE parameter");
    1673                 :       7503 :         g_assert(name == nullptr &&
    1674                 :            :                  "name was ignored in RETURN_VALUE parameter");
    1675                 :       7503 :         arg->set_return_value();
    1676                 :            :     } else {
    1677                 :            :         if constexpr (std::is_base_of_v<Arg::Positioned, T>)
    1678                 :       1304 :             arg->set_arg_pos(index);
    1679                 :      18335 :         arg->m_arg_name = name;
    1680                 :            :     }
    1681                 :            : 
    1682                 :      28497 :     arg->m_skip_in = (flags & GjsArgumentFlags::SKIP_IN);
    1683                 :      28497 :     arg->m_skip_out = (flags & GjsArgumentFlags::SKIP_OUT);
    1684                 :            : 
    1685                 :            :     if constexpr (std::is_base_of_v<Arg::Nullable, T>)
    1686                 :      15337 :         arg->m_nullable = (flags & GjsArgumentFlags::MAY_BE_NULL);
    1687                 :            : 
    1688                 :            :     if constexpr (std::is_base_of_v<Arg::Transferable, T>)
    1689                 :      15101 :         arg->m_transfer = transfer;
    1690                 :            : 
    1691                 :            :     if constexpr (std::is_base_of_v<Arg::TypeInfo, T> &&
    1692                 :            :                   ArgKind != Arg::Kind::INSTANCE) {
    1693                 :       9378 :         arg->m_type_info = std::move(*type_info);
    1694                 :            :     }
    1695                 :            : 
    1696                 :      28497 :     return arg;
    1697                 :            : }
    1698                 :            : 
    1699                 :       9618 : bool ArgsCache::initialize(JSContext* cx, GICallableInfo* callable) {
    1700         [ -  + ]:       9618 :     if (!callable) {
    1701                 :          0 :         gjs_throw(cx, "Invalid callable provided");
    1702                 :          0 :         return false;
    1703                 :            :     }
    1704                 :            : 
    1705         [ -  + ]:       9618 :     if (m_args) {
    1706                 :          0 :         gjs_throw(cx, "Arguments cache already initialized!");
    1707                 :          0 :         return false;
    1708                 :            :     }
    1709                 :            : 
    1710                 :            :     GITypeInfo type_info;
    1711                 :       9618 :     g_callable_info_load_return_type(callable, &type_info);
    1712                 :            : 
    1713   [ +  +  +  + ]:      11757 :     m_has_return = g_type_info_get_tag(&type_info) != GI_TYPE_TAG_VOID ||
    1714                 :       2139 :                    g_type_info_is_pointer(&type_info);
    1715                 :       9618 :     m_is_method = !!g_callable_info_is_method(callable);
    1716                 :            : 
    1717                 :       9618 :     int size = g_callable_info_get_n_args(callable);
    1718         [ +  + ]:       9618 :     size += (m_is_method ? 1 : 0);
    1719         [ +  + ]:       9618 :     size += (m_has_return ? 1 : 0);
    1720                 :            : 
    1721         [ -  + ]:       9618 :     if (size > Argument::MAX_ARGS) {
    1722                 :          0 :         gjs_throw(cx,
    1723                 :            :                   "Too many arguments, only %u are supported, while %d are "
    1724                 :            :                   "provided!",
    1725                 :            :                   Argument::MAX_ARGS, size);
    1726                 :          0 :         return false;
    1727                 :            :     }
    1728                 :            : 
    1729   [ +  -  +  + ]:      37946 :     m_args = new ArgumentPtr[size]{};
    1730                 :       9618 :     return true;
    1731                 :            : }
    1732                 :            : 
    1733                 :            : template <typename T, Arg::Kind ArgKind, typename... Args>
    1734                 :      28497 : constexpr T* ArgsCache::set_argument(uint8_t index, const char* name,
    1735                 :            :                                      GITypeInfo* type_info, GITransfer transfer,
    1736                 :            :                                      GjsArgumentFlags flags, Args&&... args) {
    1737                 :      28497 :     GjsAutoCppPointer<T> arg = Argument::make<T, ArgKind>(
    1738                 :            :         index, name, type_info, transfer, flags, args...);
    1739                 :      28497 :     arg_get<ArgKind>(index) = arg.release();
    1740                 :      28497 :     return static_cast<T*>(arg_get<ArgKind>(index).get());
    1741                 :      28497 : }
    1742                 :            : 
    1743                 :            : template <typename T, Arg::Kind ArgKind, typename... Args>
    1744                 :       2100 : constexpr T* ArgsCache::set_argument(uint8_t index, const char* name,
    1745                 :            :                                      GITransfer transfer,
    1746                 :            :                                      GjsArgumentFlags flags, Args&&... args) {
    1747                 :       2100 :     return set_argument<T, ArgKind>(index, name, nullptr, transfer, flags,
    1748                 :       2100 :                                     args...);
    1749                 :            : }
    1750                 :            : 
    1751                 :            : template <typename T, Arg::Kind ArgKind, typename... Args>
    1752                 :            : constexpr T* ArgsCache::set_argument_auto(Args&&... args) {
    1753                 :            :     return set_argument<T, ArgKind>(std::forward<Args>(args)...);
    1754                 :            : }
    1755                 :            : 
    1756                 :            : template <typename T, Arg::Kind ArgKind, typename Tuple, typename... Args>
    1757                 :      18734 : constexpr T* ArgsCache::set_argument_auto(Tuple&& tuple, Args&&... args) {
    1758                 :            :     // TODO(3v1n0): Would be nice to have a simple way to check we're handling a
    1759                 :            :     // tuple
    1760                 :      18734 :     return std::apply(
    1761                 :      56202 :         [&](auto&&... largs) {
    1762                 :            :             return set_argument<T, ArgKind>(largs...,
    1763                 :      18734 :                                             std::forward<Args>(args)...);
    1764                 :            :         },
    1765                 :      18734 :         tuple);
    1766                 :            : }
    1767                 :            : 
    1768                 :            : template <typename T>
    1769                 :       7503 : constexpr T* ArgsCache::set_return(GITypeInfo* type_info, GITransfer transfer,
    1770                 :            :                                    GjsArgumentFlags flags) {
    1771                 :       7503 :     return set_argument<T, Arg::Kind::RETURN_VALUE>(Argument::ABSENT, nullptr,
    1772                 :       7503 :                                                     type_info, transfer, flags);
    1773                 :            : }
    1774                 :            : 
    1775                 :            : template <typename T>
    1776                 :        584 : constexpr T* ArgsCache::set_instance(GITransfer transfer,
    1777                 :            :                                      GjsArgumentFlags flags) {
    1778                 :        584 :     return set_argument<T, Arg::Kind::INSTANCE>(Argument::ABSENT, nullptr,
    1779                 :        584 :                                                 transfer, flags);
    1780                 :            : }
    1781                 :            : 
    1782                 :      33718 : GType ArgsCache::instance_type() const {
    1783         [ -  + ]:      33718 :     if (!m_is_method)
    1784                 :          0 :         return G_TYPE_NONE;
    1785                 :            : 
    1786                 :      33718 :     return instance()->as_instance()->gtype();
    1787                 :            : }
    1788                 :            : 
    1789                 :      61341 : GITypeInfo* ArgsCache::return_type() const {
    1790                 :      61341 :     Argument* rval = return_value();
    1791         [ +  + ]:      61341 :     if (!rval)
    1792                 :      27450 :         return nullptr;
    1793                 :            : 
    1794                 :      33891 :     return const_cast<GITypeInfo*>(rval->as_return_value()->type_info());
    1795                 :            : }
    1796                 :            : 
    1797                 :       1516 : constexpr void ArgsCache::set_skip_all(uint8_t index, const char* name) {
    1798                 :       1516 :     set_argument<Arg::SkipAll>(index, name, GI_TRANSFER_NOTHING,
    1799                 :            :                                GjsArgumentFlags::SKIP_ALL);
    1800                 :       1516 : }
    1801                 :            : 
    1802                 :            : template <Arg::Kind ArgKind>
    1803                 :        918 : void ArgsCache::set_array_argument(GICallableInfo* callable, uint8_t gi_index,
    1804                 :            :                                    GITypeInfo* type_info, GIDirection direction,
    1805                 :            :                                    GIArgInfo* arg, GjsArgumentFlags flags,
    1806                 :            :                                    int length_pos) {
    1807                 :            :     Arg::ExplicitArray* array;
    1808                 :            :     GIArgInfo length_arg;
    1809                 :        918 :     g_callable_info_load_arg(callable, length_pos, &length_arg);
    1810                 :            :     GITypeInfo length_type;
    1811                 :        918 :     g_arg_info_load_type(&length_arg, &length_type);
    1812                 :            : 
    1813                 :            :     if constexpr (ArgKind == Arg::Kind::RETURN_VALUE) {
    1814                 :        140 :         GITransfer transfer = g_callable_info_get_caller_owns(callable);
    1815                 :        140 :         array = set_return<Arg::ReturnArray>(type_info, transfer,
    1816                 :            :                                              GjsArgumentFlags::NONE);
    1817                 :            :     } else {
    1818                 :        778 :         const char* arg_name = g_base_info_get_name(arg);
    1819                 :        778 :         GITransfer transfer = g_arg_info_get_ownership_transfer(arg);
    1820                 :        778 :         auto common_args =
    1821                 :            :             std::make_tuple(gi_index, arg_name, type_info, transfer, flags);
    1822                 :            : 
    1823         [ +  + ]:        778 :         if (direction == GI_DIRECTION_IN) {
    1824                 :        745 :             array = set_argument_auto<Arg::CArrayIn>(common_args);
    1825                 :        745 :             set_skip_all(length_pos, g_base_info_get_name(&length_arg));
    1826         [ +  + ]:         33 :         } else if (direction == GI_DIRECTION_INOUT) {
    1827                 :         13 :             array = set_argument_auto<Arg::CArrayInOut>(common_args);
    1828                 :         13 :             set_skip_all(length_pos, g_base_info_get_name(&length_arg));
    1829                 :            :         } else {
    1830                 :         20 :             array = set_argument_auto<Arg::CArrayOut>(common_args);
    1831                 :            :         }
    1832                 :            :     }
    1833                 :            : 
    1834         [ +  + ]:        778 :     if (ArgKind == Arg::Kind::RETURN_VALUE || direction == GI_DIRECTION_OUT) {
    1835                 :            :         // Even if we skip the length argument most of time, we need to
    1836                 :            :         // do some basic initialization here.
    1837                 :        160 :         set_argument<Arg::ArrayLengthOut>(
    1838                 :            :             length_pos, g_base_info_get_name(&length_arg), &length_type,
    1839                 :            :             GI_TRANSFER_NOTHING,
    1840                 :        180 :             static_cast<GjsArgumentFlags>(flags | GjsArgumentFlags::SKIP_ALL));
    1841                 :            :     }
    1842                 :            : 
    1843                 :        918 :     array->set_array_length(length_pos, g_type_info_get_tag(&length_type),
    1844                 :            :                             g_arg_info_get_direction(&length_arg));
    1845                 :        918 : }
    1846                 :            : 
    1847                 :       9618 : void ArgsCache::build_return(GICallableInfo* callable, bool* inc_counter_out) {
    1848                 :       9618 :     g_assert(inc_counter_out && "forgot out parameter");
    1849                 :            : 
    1850         [ +  + ]:       9618 :     if (!m_has_return) {
    1851                 :       2115 :         *inc_counter_out = false;
    1852                 :       2255 :         return;
    1853                 :            :     }
    1854                 :            : 
    1855                 :            :     GITypeInfo type_info;
    1856                 :       7503 :     g_callable_info_load_return_type(callable, &type_info);
    1857                 :       7503 :     GITransfer transfer = g_callable_info_get_caller_owns(callable);
    1858                 :       7503 :     GITypeTag tag = g_type_info_get_tag(&type_info);
    1859                 :            : 
    1860                 :       7503 :     *inc_counter_out = true;
    1861                 :       7503 :     GjsArgumentFlags flags = GjsArgumentFlags::SKIP_IN;
    1862                 :            : 
    1863         [ +  + ]:       7503 :     if (tag == GI_TYPE_TAG_ARRAY) {
    1864                 :        283 :         int length_pos = g_type_info_get_array_length(&type_info);
    1865         [ +  + ]:        283 :         if (length_pos >= 0) {
    1866                 :        140 :             set_array_argument<Arg::Kind::RETURN_VALUE>(
    1867                 :            :                 callable, 0, &type_info, GI_DIRECTION_OUT, nullptr, flags,
    1868                 :            :                 length_pos);
    1869                 :        140 :             return;
    1870                 :            :         }
    1871                 :            :     }
    1872                 :            : 
    1873                 :            :     // in() is ignored for the return value, but skip_in is not (it is used
    1874                 :            :     // in the failure release path)
    1875                 :       7363 :     set_return<Arg::GenericReturn>(&type_info, transfer, flags);
    1876                 :            : }
    1877                 :            : 
    1878                 :            : namespace Arg {
    1879                 :            : 
    1880                 :        225 : Enum::Enum(GIEnumInfo* enum_info) {
    1881                 :        225 :     int64_t min = std::numeric_limits<int64_t>::max();
    1882                 :        225 :     int64_t max = std::numeric_limits<int64_t>::min();
    1883                 :        225 :     int n = g_enum_info_get_n_values(enum_info);
    1884         [ +  + ]:       2914 :     for (int i = 0; i < n; i++) {
    1885                 :       2689 :         GjsAutoValueInfo value_info = g_enum_info_get_value(enum_info, i);
    1886                 :       2689 :         int64_t value = g_value_info_get_value(value_info);
    1887                 :            : 
    1888         [ +  + ]:       2689 :         if (value > max)
    1889                 :       2663 :             max = value;
    1890         [ +  + ]:       2689 :         if (value < min)
    1891                 :        235 :             min = value;
    1892                 :       2689 :     }
    1893                 :            : 
    1894                 :            :     // From the docs for g_value_info_get_value(): "This will always be
    1895                 :            :     // representable as a 32-bit signed or unsigned value. The use of gint64 as
    1896                 :            :     // the return type is to allow both."
    1897                 :            :     // We stuff them both into unsigned 32-bit fields, and use a flag to tell
    1898                 :            :     // whether we have to compare them as signed.
    1899                 :        225 :     m_min = static_cast<uint32_t>(min);
    1900                 :        225 :     m_max = static_cast<uint32_t>(max);
    1901                 :            : 
    1902   [ +  +  +  + ]:        225 :     m_unsigned = (min >= 0 && max > std::numeric_limits<int32_t>::max());
    1903                 :        225 : }
    1904                 :            : 
    1905                 :        846 : Flags::Flags(GIEnumInfo* enum_info) {
    1906                 :        846 :     uint64_t mask = 0;
    1907                 :        846 :     int n = g_enum_info_get_n_values(enum_info);
    1908         [ +  + ]:       5880 :     for (int i = 0; i < n; i++) {
    1909                 :       5034 :         GjsAutoValueInfo value_info = g_enum_info_get_value(enum_info, i);
    1910                 :       5034 :         int64_t value = g_value_info_get_value(value_info);
    1911                 :            :         // From the docs for g_value_info_get_value(): "This will always be
    1912                 :            :         // representable as a 32-bit signed or unsigned value. The use of
    1913                 :            :         // gint64 as the return type is to allow both."
    1914                 :            :         // We stuff both into an unsigned, int-sized field, matching the
    1915                 :            :         // internal representation of flags in GLib (which uses guint).
    1916                 :       5034 :         mask |= static_cast<unsigned>(value);
    1917                 :       5034 :     }
    1918                 :            : 
    1919                 :        846 :     m_mask = mask;
    1920                 :        846 : }
    1921                 :            : 
    1922                 :            : }  // namespace Arg
    1923                 :            : 
    1924                 :            : namespace arg_cache {
    1925                 :       5646 : [[nodiscard]] static inline bool is_gdk_atom(GIBaseInfo* info) {
    1926         [ -  + ]:       5646 :     return strcmp("Atom", g_base_info_get_name(info)) == 0 &&
    1927         [ -  - ]:       5646 :            strcmp("Gdk", g_base_info_get_namespace(info)) == 0;
    1928                 :            : }
    1929                 :            : }  // namespace arg_cache
    1930                 :            : 
    1931                 :            : template <Arg::Kind ArgKind>
    1932                 :       7124 : void ArgsCache::build_interface_in_arg(uint8_t gi_index, GITypeInfo* type_info,
    1933                 :            :                                        GIBaseInfo* interface_info,
    1934                 :            :                                        GITransfer transfer, const char* name,
    1935                 :            :                                        GjsArgumentFlags flags) {
    1936                 :       7124 :     GIInfoType interface_type = g_base_info_get_type(interface_info);
    1937                 :            : 
    1938                 :       7124 :     auto base_args =
    1939                 :            :         std::make_tuple(gi_index, name, type_info, transfer, flags);
    1940                 :       7124 :     auto common_args =
    1941                 :       2075 :         std::tuple_cat(base_args, std::make_tuple(interface_info));
    1942                 :            : 
    1943                 :            :     // We do some transfer magic later, so let's ensure we don't mess up.
    1944                 :            :     // Should not happen in practice.
    1945         [ -  + ]:       7124 :     if (G_UNLIKELY(transfer == GI_TRANSFER_CONTAINER)) {
    1946                 :          0 :         set_argument_auto<Arg::NotIntrospectable>(base_args,
    1947                 :          0 :                                                   INTERFACE_TRANSFER_CONTAINER);
    1948                 :       7124 :         return;
    1949                 :            :     }
    1950                 :            : 
    1951   [ +  +  +  +  :       7124 :     switch (interface_type) {
                      - ]
    1952                 :        225 :         case GI_INFO_TYPE_ENUM:
    1953                 :        225 :             set_argument_auto<Arg::EnumIn, ArgKind>(common_args);
    1954                 :        225 :             return;
    1955                 :            : 
    1956                 :        846 :         case GI_INFO_TYPE_FLAGS:
    1957                 :        846 :             set_argument_auto<Arg::FlagsIn, ArgKind>(common_args);
    1958                 :        846 :             return;
    1959                 :            : 
    1960                 :       2785 :         case GI_INFO_TYPE_STRUCT:
    1961         [ +  + ]:       2785 :             if (g_struct_info_is_foreign(interface_info)) {
    1962                 :            :                 if constexpr (ArgKind == Arg::Kind::INSTANCE)
    1963                 :          0 :                     set_argument_auto<Arg::ForeignStructInstanceIn, ArgKind>(
    1964                 :            :                         common_args);
    1965                 :            :                 else
    1966                 :         15 :                     set_argument_auto<Arg::ForeignStructIn, ArgKind>(
    1967                 :            :                         common_args);
    1968                 :         15 :                 return;
    1969                 :            :             }
    1970                 :            :             [[fallthrough]];
    1971                 :            :         case GI_INFO_TYPE_BOXED:
    1972                 :            :         case GI_INFO_TYPE_OBJECT:
    1973                 :            :         case GI_INFO_TYPE_INTERFACE:
    1974                 :            :         case GI_INFO_TYPE_UNION: {
    1975                 :       6038 :             GType gtype = g_registered_type_info_get_g_type(interface_info);
    1976                 :            : 
    1977   [ +  +  +  +  :       6358 :             if (interface_type == GI_INFO_TYPE_STRUCT && gtype == G_TYPE_NONE &&
             +  -  +  + ]
    1978                 :        320 :                 !g_struct_info_is_gtype_struct(interface_info)) {
    1979                 :            :                 if constexpr (ArgKind != Arg::Kind::INSTANCE) {
    1980                 :            :                     // This covers cases such as GTypeInstance
    1981                 :        313 :                     set_argument_auto<Arg::FallbackInterfaceIn, ArgKind>(
    1982                 :            :                         common_args);
    1983                 :        313 :                     return;
    1984                 :            :                 }
    1985                 :            :             }
    1986                 :            : 
    1987                 :            :             // Transfer handling is a bit complex here, because some of our in()
    1988                 :            :             // arguments know not to copy stuff if we don't need to.
    1989                 :            : 
    1990         [ +  + ]:       5725 :             if (gtype == G_TYPE_VALUE) {
    1991                 :            :                 if constexpr (ArgKind == Arg::Kind::INSTANCE)
    1992                 :         55 :                     set_argument_auto<Arg::BoxedIn, ArgKind>(common_args);
    1993         [ +  - ]:         24 :                 else if (transfer == GI_TRANSFER_NOTHING)
    1994                 :         24 :                     set_argument_auto<Arg::GValueInTransferNone, ArgKind>(
    1995                 :            :                         common_args);
    1996                 :            :                 else
    1997                 :          0 :                     set_argument_auto<Arg::GValueIn, ArgKind>(common_args);
    1998                 :         79 :                 return;
    1999                 :            :             }
    2000                 :            : 
    2001         [ -  + ]:       5646 :             if (arg_cache::is_gdk_atom(interface_info)) {
    2002                 :            :                 // Fall back to the generic marshaller
    2003                 :          0 :                 set_argument_auto<Arg::FallbackInterfaceIn, ArgKind>(
    2004                 :            :                     common_args);
    2005                 :          0 :                 return;
    2006                 :            :             }
    2007                 :            : 
    2008         [ +  + ]:       5646 :             if (gtype == G_TYPE_CLOSURE) {
    2009         [ +  - ]:        707 :                 if (transfer == GI_TRANSFER_NOTHING &&
    2010                 :            :                     ArgKind != Arg::Kind::INSTANCE)
    2011                 :        707 :                     set_argument_auto<Arg::GClosureInTransferNone, ArgKind>(
    2012                 :            :                         common_args);
    2013                 :            :                 else
    2014                 :          0 :                     set_argument_auto<Arg::GClosureIn, ArgKind>(common_args);
    2015                 :        707 :                 return;
    2016                 :            :             }
    2017                 :            : 
    2018         [ +  + ]:       4939 :             if (gtype == G_TYPE_BYTES) {
    2019         [ +  - ]:         77 :                 if (transfer == GI_TRANSFER_NOTHING &&
    2020                 :            :                     ArgKind != Arg::Kind::INSTANCE)
    2021                 :         77 :                     set_argument_auto<Arg::GBytesInTransferNone, ArgKind>(
    2022                 :            :                         common_args);
    2023                 :            :                 else
    2024                 :          2 :                     set_argument_auto<Arg::GBytesIn, ArgKind>(common_args);
    2025                 :         79 :                 return;
    2026                 :            :             }
    2027                 :            : 
    2028   [ +  +  +  +  :       4860 :             if (g_type_is_a(gtype, G_TYPE_OBJECT)) {
                   +  + ]
    2029                 :       3110 :                 set_argument_auto<Arg::ObjectIn, ArgKind>(common_args);
    2030                 :       3110 :                 return;
    2031                 :            :             }
    2032                 :            : 
    2033   [ +  +  -  +  :       1750 :             if (g_type_is_a(gtype, G_TYPE_PARAM)) {
                   +  + ]
    2034                 :        145 :                 set_argument_auto<Arg::FallbackInterfaceIn, ArgKind>(
    2035                 :            :                     common_args);
    2036                 :        145 :                 return;
    2037                 :            :             }
    2038                 :            : 
    2039         [ +  + ]:       1605 :             if (interface_type == GI_INFO_TYPE_UNION) {
    2040         [ -  + ]:          8 :                 if (gtype == G_TYPE_NONE) {
    2041                 :            :                     // Can't handle unions without a GType
    2042                 :          0 :                     set_argument_auto<Arg::NotIntrospectable>(
    2043                 :          0 :                         base_args, UNREGISTERED_UNION);
    2044                 :          0 :                     return;
    2045                 :            :                 }
    2046                 :            : 
    2047                 :          8 :                 set_argument_auto<Arg::UnionIn, ArgKind>(common_args);
    2048                 :          8 :                 return;
    2049                 :            :             }
    2050                 :            : 
    2051         [ +  + ]:       1597 :             if (G_TYPE_IS_INSTANTIATABLE(gtype)) {
    2052                 :          1 :                 set_argument_auto<Arg::FundamentalIn, ArgKind>(common_args);
    2053                 :          1 :                 return;
    2054                 :            :             }
    2055                 :            : 
    2056   [ +  -  +  +  :       1596 :             if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
                   +  + ]
    2057                 :          4 :                 set_argument_auto<Arg::InterfaceIn, ArgKind>(common_args);
    2058                 :          4 :                 return;
    2059                 :            :             }
    2060                 :            : 
    2061                 :            :             // generic boxed type
    2062         [ +  + ]:       1592 :             if (gtype == G_TYPE_NONE) {
    2063         [ -  + ]:          7 :                 if (transfer != GI_TRANSFER_NOTHING) {
    2064                 :            :                     // Can't transfer ownership of a structure type not
    2065                 :            :                     // registered as a boxed
    2066                 :          0 :                     set_argument_auto<Arg::NotIntrospectable>(
    2067                 :          0 :                         base_args, UNREGISTERED_BOXED_WITH_TRANSFER);
    2068                 :          0 :                     return;
    2069                 :            :                 }
    2070                 :            : 
    2071                 :          7 :                 set_argument_auto<Arg::UnregisteredBoxedIn, ArgKind>(
    2072                 :            :                     common_args);
    2073                 :          7 :                 return;
    2074                 :            :             }
    2075                 :       1585 :             set_argument_auto<Arg::BoxedIn, ArgKind>(common_args);
    2076                 :       1585 :             return;
    2077                 :            :         } break;
    2078                 :            : 
    2079                 :          0 :         case GI_INFO_TYPE_INVALID:
    2080                 :            :         case GI_INFO_TYPE_FUNCTION:
    2081                 :            :         case GI_INFO_TYPE_CALLBACK:
    2082                 :            :         case GI_INFO_TYPE_CONSTANT:
    2083                 :            :         case GI_INFO_TYPE_INVALID_0:
    2084                 :            :         case GI_INFO_TYPE_VALUE:
    2085                 :            :         case GI_INFO_TYPE_SIGNAL:
    2086                 :            :         case GI_INFO_TYPE_VFUNC:
    2087                 :            :         case GI_INFO_TYPE_PROPERTY:
    2088                 :            :         case GI_INFO_TYPE_FIELD:
    2089                 :            :         case GI_INFO_TYPE_ARG:
    2090                 :            :         case GI_INFO_TYPE_TYPE:
    2091                 :            :         case GI_INFO_TYPE_UNRESOLVED:
    2092                 :            :         default:
    2093                 :            :             // Don't know how to handle this interface type (should not happen
    2094                 :            :             // in practice, for typelibs emitted by g-ir-compiler)
    2095                 :          0 :             set_argument_auto<Arg::NotIntrospectable>(base_args,
    2096                 :          0 :                                                       UNSUPPORTED_TYPE);
    2097                 :            :     }
    2098                 :            : }
    2099                 :            : 
    2100                 :      14705 : void ArgsCache::build_normal_in_arg(uint8_t gi_index, GITypeInfo* type_info,
    2101                 :            :                                     GIArgInfo* arg, GjsArgumentFlags flags) {
    2102                 :            :     // "Normal" in arguments are those arguments that don't require special
    2103                 :            :     // processing, and don't touch other arguments.
    2104                 :            :     // Main categories are:
    2105                 :            :     // - void*
    2106                 :            :     // - small numbers (fit in 32bit)
    2107                 :            :     // - big numbers (need a double)
    2108                 :            :     // - strings
    2109                 :            :     // - enums/flags (different from numbers in the way they're exposed in GI)
    2110                 :            :     // - objects (GObjects, boxed, unions, etc.)
    2111                 :            :     // - hashes
    2112                 :            :     // - sequences (null-terminated arrays, lists, etc.)
    2113                 :            : 
    2114                 :      14705 :     const char* name = g_base_info_get_name(arg);
    2115                 :      14705 :     GITransfer transfer = g_arg_info_get_ownership_transfer(arg);
    2116                 :            :     auto common_args =
    2117                 :      14705 :         std::make_tuple(gi_index, name, type_info, transfer, flags);
    2118                 :      14705 :     GITypeTag tag = g_type_info_get_tag(type_info);
    2119                 :            : 
    2120   [ +  +  +  +  :      14705 :     switch (tag) {
             +  +  +  +  
                      + ]
    2121                 :        869 :         case GI_TYPE_TAG_VOID:
    2122                 :        869 :             set_argument_auto<Arg::NullIn>(common_args);
    2123                 :        869 :             break;
    2124                 :            : 
    2125                 :        377 :         case GI_TYPE_TAG_BOOLEAN:
    2126                 :        377 :             set_argument_auto<Arg::BooleanIn>(common_args);
    2127                 :        377 :             break;
    2128                 :            : 
    2129                 :       3114 :         case GI_TYPE_TAG_INT8:
    2130                 :            :         case GI_TYPE_TAG_INT16:
    2131                 :            :         case GI_TYPE_TAG_INT32:
    2132                 :            :         case GI_TYPE_TAG_UINT8:
    2133                 :            :         case GI_TYPE_TAG_UINT16:
    2134                 :            :         case GI_TYPE_TAG_UINT32:
    2135                 :            :         case GI_TYPE_TAG_INT64:
    2136                 :            :         case GI_TYPE_TAG_UINT64:
    2137                 :            :         case GI_TYPE_TAG_FLOAT:
    2138                 :            :         case GI_TYPE_TAG_DOUBLE:
    2139                 :       3114 :             set_argument_auto<Arg::NumericIn>(common_args, tag);
    2140                 :       3114 :             break;
    2141                 :            : 
    2142                 :          1 :         case GI_TYPE_TAG_UNICHAR:
    2143                 :          1 :             set_argument_auto<Arg::UnicharIn>(common_args);
    2144                 :          1 :             break;
    2145                 :            : 
    2146                 :        191 :         case GI_TYPE_TAG_GTYPE:
    2147                 :        191 :             set_argument_auto<Arg::GTypeIn>(common_args);
    2148                 :        191 :             break;
    2149                 :            : 
    2150                 :        216 :         case GI_TYPE_TAG_FILENAME:
    2151         [ +  - ]:        216 :             if (transfer == GI_TRANSFER_NOTHING)
    2152                 :        216 :                 set_argument_auto<Arg::FilenameInTransferNone>(common_args);
    2153                 :            :             else
    2154                 :          0 :                 set_argument_auto<Arg::FilenameIn>(common_args);
    2155                 :        216 :             break;
    2156                 :            : 
    2157                 :       4781 :         case GI_TYPE_TAG_UTF8:
    2158         [ +  - ]:       4781 :             if (transfer == GI_TRANSFER_NOTHING)
    2159                 :       4781 :                 set_argument_auto<Arg::StringInTransferNone>(common_args);
    2160                 :            :             else
    2161                 :          0 :                 set_argument_auto<Arg::StringIn>(common_args);
    2162                 :       4781 :             break;
    2163                 :            : 
    2164                 :       5049 :         case GI_TYPE_TAG_INTERFACE: {
    2165                 :            :             GjsAutoBaseInfo interface_info =
    2166                 :       5049 :                 g_type_info_get_interface(type_info);
    2167                 :       5049 :             build_interface_in_arg(gi_index, type_info, interface_info,
    2168                 :            :                                    transfer, name, flags);
    2169                 :       5049 :             return;
    2170                 :       5049 :         }
    2171                 :            : 
    2172                 :        107 :         case GI_TYPE_TAG_ARRAY:
    2173                 :            :         case GI_TYPE_TAG_GLIST:
    2174                 :            :         case GI_TYPE_TAG_GSLIST:
    2175                 :            :         case GI_TYPE_TAG_GHASH:
    2176                 :            :         case GI_TYPE_TAG_ERROR:
    2177                 :            :         default:
    2178                 :            :             // FIXME: Falling back to the generic marshaller
    2179                 :        107 :             set_argument_auto<Arg::FallbackIn>(common_args);
    2180                 :            :     }
    2181                 :            : }
    2182                 :            : 
    2183                 :       9618 : void ArgsCache::build_instance(GICallableInfo* callable) {
    2184         [ +  + ]:       9618 :     if (!m_is_method)
    2185                 :       6959 :         return;
    2186                 :            : 
    2187                 :       2659 :     GIBaseInfo* interface_info = g_base_info_get_container(callable);  // !owned
    2188                 :            : 
    2189                 :            :     GITransfer transfer =
    2190                 :       2659 :         g_callable_info_get_instance_ownership_transfer(callable);
    2191                 :            : 
    2192                 :            :     // These cases could be covered by the generic marshaller, except that
    2193                 :            :     // there's no way to get GITypeInfo for a method's instance parameter.
    2194                 :            :     // Instead, special-case the arguments here that would otherwise go through
    2195                 :            :     // the generic marshaller.
    2196                 :            :     // See: https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/334
    2197                 :       2659 :     GIInfoType info_type = g_base_info_get_type(interface_info);
    2198   [ +  +  +  +  :       3682 :     if (info_type == GI_INFO_TYPE_STRUCT &&
                   +  + ]
    2199                 :       1023 :         g_struct_info_is_gtype_struct(interface_info)) {
    2200                 :        456 :         set_instance<Arg::GTypeStructInstanceIn>(transfer);
    2201                 :        456 :         return;
    2202                 :            :     }
    2203         [ +  + ]:       2203 :     if (info_type == GI_INFO_TYPE_OBJECT) {
    2204                 :       1326 :         GType gtype = g_registered_type_info_get_g_type(interface_info);
    2205                 :            : 
    2206   [ +  +  -  +  :       1326 :         if (g_type_is_a(gtype, G_TYPE_PARAM)) {
                   +  + ]
    2207                 :        128 :             set_instance<Arg::ParamInstanceIn>(transfer);
    2208                 :        128 :             return;
    2209                 :            :         }
    2210                 :            :     }
    2211                 :            : 
    2212                 :       2075 :     build_interface_in_arg<Arg::Kind::INSTANCE>(
    2213                 :            :         Argument::ABSENT, nullptr, interface_info, transfer, nullptr,
    2214                 :            :         GjsArgumentFlags::NONE);
    2215                 :            : }
    2216                 :            : 
    2217                 :         11 : static constexpr bool type_tag_is_scalar(GITypeTag tag) {
    2218   [ +  -  +  -  :         11 :     return GI_TYPE_TAG_IS_NUMERIC(tag) || tag == GI_TYPE_TAG_BOOLEAN ||
             +  -  -  + ]
    2219                 :         11 :            tag == GI_TYPE_TAG_GTYPE;
    2220                 :            : }
    2221                 :            : 
    2222                 :      16658 : void ArgsCache::build_arg(uint8_t gi_index, GIDirection direction,
    2223                 :            :                           GIArgInfo* arg, GICallableInfo* callable,
    2224                 :            :                           bool* inc_counter_out) {
    2225                 :      16658 :     g_assert(inc_counter_out && "forgot out parameter");
    2226                 :            :     GITypeInfo type_info;
    2227                 :            : 
    2228                 :      16658 :     const char* arg_name = g_base_info_get_name(arg);
    2229                 :      16658 :     g_arg_info_load_type(arg, &type_info);
    2230                 :      16658 :     GITransfer transfer = g_arg_info_get_ownership_transfer(arg);
    2231                 :            : 
    2232                 :      16658 :     GjsArgumentFlags flags = GjsArgumentFlags::NONE;
    2233         [ +  + ]:      16658 :     if (g_arg_info_may_be_null(arg))
    2234                 :       3911 :         flags |= GjsArgumentFlags::MAY_BE_NULL;
    2235         [ +  + ]:      16658 :     if (g_arg_info_is_caller_allocates(arg))
    2236                 :         14 :         flags |= GjsArgumentFlags::CALLER_ALLOCATES;
    2237                 :            : 
    2238         [ +  + ]:      16658 :     if (direction == GI_DIRECTION_IN)
    2239                 :      16257 :         flags |= GjsArgumentFlags::SKIP_OUT;
    2240         [ +  + ]:        401 :     else if (direction == GI_DIRECTION_OUT)
    2241                 :        330 :         flags |= GjsArgumentFlags::SKIP_IN;
    2242                 :      16658 :     *inc_counter_out = true;
    2243                 :            : 
    2244                 :            :     auto common_args =
    2245                 :      16658 :         std::make_tuple(gi_index, arg_name, &type_info, transfer, flags);
    2246                 :            : 
    2247                 :      16658 :     GITypeTag type_tag = g_type_info_get_tag(&type_info);
    2248   [ +  +  +  + ]:      16988 :     if (direction == GI_DIRECTION_OUT &&
    2249         [ +  + ]:      16988 :         (flags & GjsArgumentFlags::CALLER_ALLOCATES)) {
    2250                 :         14 :         size_t size = 0;
    2251                 :            : 
    2252         [ +  + ]:         14 :         if (type_tag == GI_TYPE_TAG_ARRAY) {
    2253                 :          3 :             GIArrayType array_type = g_type_info_get_array_type(&type_info);
    2254                 :            : 
    2255         [ +  + ]:          3 :             switch (array_type) {
    2256                 :          2 :                 case GI_ARRAY_TYPE_C: {
    2257                 :          2 :                     GjsAutoTypeInfo param_info;
    2258                 :            :                     int n_elements =
    2259                 :          2 :                         g_type_info_get_array_fixed_size(&type_info);
    2260                 :            : 
    2261         [ -  + ]:          2 :                     if (n_elements <= 0)
    2262                 :          0 :                         break;
    2263                 :            : 
    2264                 :          2 :                     param_info = g_type_info_get_param_type(&type_info, 0);
    2265                 :          2 :                     GITypeTag param_tag = g_type_info_get_tag(param_info);
    2266                 :            : 
    2267                 :          2 :                     size = gjs_type_get_element_size(param_tag, param_info);
    2268                 :          2 :                     size *= n_elements;
    2269                 :          2 :                     break;
    2270                 :          2 :                 }
    2271                 :          1 :                 default:
    2272                 :          1 :                     break;
    2273                 :            :             }
    2274   [ +  -  +  +  :         22 :         } else if (!type_tag_is_scalar(type_tag) &&
                   +  + ]
    2275                 :         11 :                    !g_type_info_is_pointer(&type_info)) {
    2276                 :            :             // Scalar out parameters should not be annotated with
    2277                 :            :             // caller-allocates, which is for structured types that need to be
    2278                 :            :             // allocated in order for the function to fill them in.
    2279                 :         10 :             size = gjs_type_get_element_size(type_tag, &type_info);
    2280                 :            :         }
    2281                 :            : 
    2282         [ +  + ]:         14 :         if (!size) {
    2283                 :          2 :             set_argument_auto<Arg::NotIntrospectable>(
    2284                 :          2 :                 common_args, OUT_CALLER_ALLOCATES_NON_STRUCT);
    2285                 :          2 :             return;
    2286                 :            :         }
    2287                 :            : 
    2288         [ +  + ]:         12 :         if (type_tag == GI_TYPE_TAG_INTERFACE) {
    2289                 :            :             GjsAutoBaseInfo interface_info =
    2290                 :         10 :                 g_type_info_get_interface(&type_info);
    2291                 :         10 :             GType gtype = g_registered_type_info_get_g_type(interface_info);
    2292   [ +  -  +  +  :         10 :             if (g_type_is_a(gtype, G_TYPE_BOXED)) {
                   +  + ]
    2293                 :          6 :                 auto* gjs_arg = set_argument_auto<Arg::BoxedCallerAllocatesOut>(
    2294                 :            :                     common_args, gtype);
    2295                 :          6 :                 gjs_arg->m_allocates_size = size;
    2296                 :          6 :                 return;
    2297                 :            :             }
    2298         [ +  + ]:         10 :         }
    2299                 :            : 
    2300                 :          6 :         auto* gjs_arg = set_argument_auto<Arg::CallerAllocatesOut>(common_args);
    2301                 :          6 :         gjs_arg->m_allocates_size = size;
    2302                 :            : 
    2303                 :          6 :         return;
    2304                 :            :     }
    2305                 :            : 
    2306         [ +  + ]:      16644 :     if (type_tag == GI_TYPE_TAG_INTERFACE) {
    2307                 :       5843 :         GjsAutoBaseInfo interface_info = g_type_info_get_interface(&type_info);
    2308         [ +  + ]:       5843 :         if (interface_info.type() == GI_INFO_TYPE_CALLBACK) {
    2309         [ -  + ]:        708 :             if (direction != GI_DIRECTION_IN) {
    2310                 :            :                 // Can't do callbacks for out or inout
    2311                 :          0 :                 set_argument_auto<Arg::NotIntrospectable>(common_args,
    2312                 :          0 :                                                           CALLBACK_OUT);
    2313                 :          0 :                 return;
    2314                 :            :             }
    2315                 :            : 
    2316   [ +  +  +  + ]:        772 :             if (strcmp(interface_info.name(), "DestroyNotify") == 0 &&
    2317         [ +  - ]:         64 :                 strcmp(interface_info.ns(), "GLib") == 0) {
    2318                 :            :                 // We don't know (yet) what to do with GDestroyNotify appearing
    2319                 :            :                 // before a callback. If the callback comes later in the
    2320                 :            :                 // argument list, then the invalid argument will be
    2321                 :            :                 // overwritten with the 'skipped' one. If no callback follows,
    2322                 :            :                 // then this is probably an unsupported function, so the
    2323                 :            :                 // function invocation code will check this and throw.
    2324                 :         64 :                 set_argument_auto<Arg::NotIntrospectable>(
    2325                 :         64 :                     common_args, DESTROY_NOTIFY_NO_CALLBACK);
    2326                 :         64 :                 *inc_counter_out = false;
    2327                 :            :             } else {
    2328                 :        644 :                 auto* gjs_arg = set_argument_auto<Arg::CallbackIn>(
    2329                 :            :                     common_args, interface_info);
    2330                 :            : 
    2331                 :        644 :                 int destroy_pos = g_arg_info_get_destroy(arg);
    2332                 :        644 :                 int closure_pos = g_arg_info_get_closure(arg);
    2333                 :            : 
    2334         [ +  + ]:        644 :                 if (destroy_pos >= 0)
    2335                 :        146 :                     set_skip_all(destroy_pos);
    2336                 :            : 
    2337         [ +  + ]:        644 :                 if (closure_pos >= 0)
    2338                 :        612 :                     set_skip_all(closure_pos);
    2339                 :            : 
    2340   [ +  +  +  + ]:        644 :                 if (destroy_pos >= 0 && closure_pos < 0) {
    2341                 :          1 :                     set_argument_auto<Arg::NotIntrospectable>(
    2342                 :          1 :                         common_args, DESTROY_NOTIFY_NO_USER_DATA);
    2343                 :          1 :                     return;
    2344                 :            :                 }
    2345                 :            : 
    2346                 :        643 :                 gjs_arg->m_scope = g_arg_info_get_scope(arg);
    2347                 :        643 :                 gjs_arg->set_callback_destroy_pos(destroy_pos);
    2348                 :        643 :                 gjs_arg->set_callback_closure_pos(closure_pos);
    2349                 :            :             }
    2350                 :            : 
    2351                 :        707 :             return;
    2352                 :            :         }
    2353         [ +  + ]:       5843 :     }
    2354                 :            : 
    2355   [ +  +  +  +  :      16842 :     if (type_tag == GI_TYPE_TAG_ARRAY &&
                   +  + ]
    2356                 :        906 :         g_type_info_get_array_type(&type_info) == GI_ARRAY_TYPE_C) {
    2357                 :        891 :         int length_pos = g_type_info_get_array_length(&type_info);
    2358                 :            : 
    2359         [ +  + ]:        891 :         if (length_pos >= 0) {
    2360                 :        778 :             Argument* cached_length = argument(length_pos);
    2361   [ +  +  +  + ]:        781 :             bool skip_length = cached_length && !(cached_length->skip_in() &&
    2362         [ +  + ]:          3 :                                                   cached_length->skip_out());
    2363                 :            : 
    2364                 :        778 :             set_array_argument(callable, gi_index, &type_info, direction, arg,
    2365                 :            :                                flags, length_pos);
    2366                 :            : 
    2367   [ +  +  +  + ]:        778 :             if (length_pos < gi_index && skip_length) {
    2368                 :            :                 // we already collected length_pos, remove it
    2369                 :        167 :                 *inc_counter_out = false;
    2370                 :            :             }
    2371                 :            : 
    2372                 :        778 :             return;
    2373         [ +  + ]:        113 :         } else if (g_type_info_is_zero_terminated(&type_info)) {
    2374         [ +  + ]:        101 :             if (direction == GI_DIRECTION_IN) {
    2375                 :         94 :                 set_argument_auto<Arg::ZeroTerminatedArrayIn>(common_args);
    2376                 :         94 :                 return;
    2377         [ +  + ]:          7 :             } else if (direction == GI_DIRECTION_INOUT) {
    2378                 :          2 :                 set_argument_auto<Arg::ZeroTerminatedArrayInOut>(common_args);
    2379                 :          2 :                 return;
    2380                 :            :             }
    2381         [ +  + ]:         12 :         } else if (g_type_info_get_array_fixed_size(&type_info) >= 0) {
    2382         [ +  + ]:         11 :             if (direction == GI_DIRECTION_IN) {
    2383                 :          5 :                 set_argument_auto<Arg::FixedSizeArrayIn>(common_args);
    2384                 :          5 :                 return;
    2385         [ +  + ]:          6 :             } else if (direction == GI_DIRECTION_INOUT) {
    2386                 :          1 :                 set_argument_auto<Arg::FixedSizeArrayInOut>(common_args);
    2387                 :          1 :                 return;
    2388                 :            :             }
    2389                 :            :         }
    2390                 :            :     }
    2391                 :            : 
    2392         [ +  + ]:      15056 :     if (direction == GI_DIRECTION_IN)
    2393                 :      14705 :         build_normal_in_arg(gi_index, &type_info, arg, flags);
    2394         [ +  + ]:        351 :     else if (direction == GI_DIRECTION_INOUT)
    2395                 :         55 :         set_argument_auto<Arg::FallbackInOut>(common_args);
    2396                 :            :     else
    2397                 :        296 :         set_argument_auto<Arg::FallbackOut>(common_args);
    2398                 :            : 
    2399                 :      15056 :     return;
    2400                 :            : }
    2401                 :            : 
    2402                 :            : }  // namespace Gjs

Generated by: LCOV version 1.14